원클릭으로
design-patterns-rust
Rust 디자인 패턴 8종 - Builder, Newtype, Type State, Strategy, Command, Observer, RAII, Extension Trait
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Rust 디자인 패턴 8종 - Builder, Newtype, Type State, Strategy, Command, Observer, RAII, Extension Trait
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Spring Security 5.5.x + jjwt 0.10.7 레거시 JWT 인증 - WebSecurityConfigurerAdapter, OncePerRequestFilter, javax.servlet 환경
Spring Boot 3.x + Spring Security 6.x + jjwt 0.12.x 기반 모던 JWT 인증 패턴. SecurityFilterChain Bean, 람다 DSL, jakarta.servlet, Virtual Threads 적용
Unity 6 LTS 2D 모바일 게임용 uGUI 시스템 전문 스킬. Canvas/RectTransform/TextMeshPro, 모바일 UI 패턴(팝업·무한 스크롤·광고·IAP), 성능 최적화, UI Toolkit과의 선택 기준 포함.
아크라시아(akrasia, 자제력 없음) 학술 논쟁의 핵심 구도와 주요 연구자·문헌을 빠르게 파악할 수 있는 도메인 지식 스킬. 도덕윤리교육 전공 대학원생(석/박사)이 학위논문·KCI 투고·세미나 준비 시 고대–현대–한국 학계–도덕심리학 흐름을 한 번에 짚도록 구성. <example>사용자: "아리스토텔레스의 propeteia와 astheneia 구분을 인용하려는데 출처를 알려줘"</example> <example>사용자: "데이비슨이 의지박약을 어떻게 가능하다고 봤는지 핵심 논증을 정리해줘"</example> <example>사용자: "한국 도덕교육 학계에서 아크라시아 다룬 논문 있어?"</example>
아리스토텔레스 『니코마코스 윤리학』에서 akrasia(자제력없음)와 akolasia(무절제)의 5축 차이를 정밀하게 정리한 학위논문 자료 스킬. NE VII.4 1147b20-1148b14, VII.8 1150b29-1151a28, III.10-12 1117b23-1119b18 절별 분해와 표준 학자 해석(Bostock, Broadie-Rowe, Pakaluk, Hursthouse, Charles 등)을 포함. 도덕교육 적용을 위한 두 상태 차이의 함의 및 한국어 번역어 처리 권장안 제공. <example>사용자: "akrates와 akolastos를 prohairesis 측면에서 어떻게 구분해야 하나요?"</example> <example>사용자: "NE VII.4의 ἁπλῶς akrasia가 akolasia와 어떻게 갈라지는지 절별 분해해주세요"</example> <example>사용자: "Hursthouse의 연속체 모델을 도덕교육 적용 절에서 어떻게 활용할 수 있나요?"</example>
한국 위기 대응 자원(자살·자해·정신건강·여성·청소년·노인·다문화) 핫라인과 앱·챗봇 안전 가드 응답 패턴 종합. 꿈 해몽·정신건강 앱 등 자가 진단/감정 콘텐츠 도메인에서 위험 신호 포착 시 안전한 자원 안내 문구를 작성할 때 참조. <example>사용자: "꿈 해몽 앱에 위기 안내 문구를 어떻게 넣을까?"</example> <example>사용자: "한국에서 자살예방 핫라인 번호가 어떻게 바뀌었지?"</example> <example>사용자: "정신건강 챗봇 안전 가드 응답 템플릿을 짜줘"</example>
| name | design-patterns-rust |
| description | Rust 디자인 패턴 8종 - Builder, Newtype, Type State, Strategy, Command, Observer, RAII, Extension Trait |
소스: https://rust-unofficial.github.io/patterns/ | https://doc.rust-lang.org/book/ | https://docs.rs/tokio/latest/tokio/sync/ | https://docs.rs/derive_builder/latest/derive_builder/ 검증일: 2026-06-20
주의: 코드 예제는 Rust 1.75+ stable 기준입니다. derive_builder 버전은 0.20.x 기준이며, API가 변경될 수 있습니다.
구조체 필드가 많거나 선택적 필드가 있을 때 가독성 높은 생성 인터페이스를 제공한다.
pub struct Request {
url: String,
method: String,
headers: Vec<(String, String)>,
body: Option<String>,
}
pub struct RequestBuilder {
url: String,
method: String,
headers: Vec<(String, String)>,
body: Option<String>,
}
impl RequestBuilder {
pub fn new(url: impl Into<String>) -> Self {
Self {
url: url.into(),
method: "GET".to_string(),
headers: Vec::new(),
body: None,
}
}
pub fn method(mut self, method: impl Into<String>) -> Self {
self.method = method.into();
self
}
pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.headers.push((key.into(), value.into()));
self
}
pub fn body(mut self, body: impl Into<String>) -> Self {
self.body = Some(body.into());
self
}
pub fn build(self) -> Request {
Request {
url: self.url,
method: self.method,
headers: self.headers,
body: self.body,
}
}
}
// 사용
let req = RequestBuilder::new("https://api.example.com")
.method("POST")
.header("Content-Type", "application/json")
.body(r#"{"key": "value"}"#)
.build();
핵심 규칙:
self를 소비하는 방식(consuming)과 &mut self 방식(non-consuming) 모두 관용적으로 사용된다
Builder::new().a().b().build())derive_builder의 기본 방식주의: consuming self 방식이 "더 일반적"이라고 단정할 수 없다. 대형 크레이트(reqwest, hyper)는
&mut self방식을 선호한다.
new()에, 선택 파라미터는 메서드 체이닝으로build()에서 유효성 검사 후 Result<T, E> 반환 가능// Cargo.toml: derive_builder = "0.20"
use derive_builder::Builder;
#[derive(Builder, Debug)]
#[builder(setter(into))]
pub struct ServerConfig {
host: String,
port: u16,
#[builder(default = "4")]
max_connections: usize,
#[builder(default)]
tls_enabled: bool,
#[builder(setter(strip_option), default)]
cert_path: Option<String>,
}
// 사용
let config = ServerConfigBuilder::default()
.host("0.0.0.0")
.port(8080)
.max_connections(16)
.cert_path("/path/to/cert.pem")
.build()
.unwrap(); // Result<ServerConfig, ServerConfigBuilderError>
derive_builder 주요 속성:
#[builder(setter(into))]: setter에 impl Into<T> 적용#[builder(default)]: Default::default() 사용#[builder(default = "expr")]: 커스텀 기본값#[builder(setter(strip_option))]: Option<T> 필드에 T만 받기build()는 항상 Result를 반환 (필수 필드 누락 시 에러)기본 타입을 감싸서 의미를 부여하고, 잘못된 타입 대입을 컴파일 타임에 방지한다.
// 문제: user_id와 post_id 모두 i64라 실수로 바꿔 넣을 수 있음
// fn get_post(user_id: i64, post_id: i64) -> Post
// Newtype으로 해결
pub struct UserId(pub i64);
pub struct PostId(pub i64);
fn get_post(user_id: UserId, post_id: PostId) -> Post {
// user_id.0, post_id.0 으로 내부 값 접근
todo!()
}
// 컴파일 에러: UserId와 PostId를 바꿔 넣으면 타입 불일치
// get_post(PostId(1), UserId(2)); // ERROR
use std::ops::Deref;
pub struct Email(String);
impl Email {
pub fn new(value: impl Into<String>) -> Result<Self, &'static str> {
let s = value.into();
if s.contains('@') {
Ok(Self(s))
} else {
Err("invalid email")
}
}
}
impl Deref for Email {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
// email.len(), email.contains("@") 등 &str 메서드 직접 사용 가능
// 외부 크레이트의 타입에 외부 trait을 직접 구현할 수 없음 (orphan rule)
// Newtype으로 감싸면 내 타입이 되므로 구현 가능
pub struct AppVec(pub Vec<String>);
impl std::fmt::Display for AppVec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct UserId(pub i64);
// JSON에서 i64로 자동 직렬화/역직렬화
// {"user_id": 42} → UserId(42)
제네릭 타입 파라미터로 상태를 표현하여, 잘못된 상태 전이를 컴파일 타임에 차단한다.
use std::marker::PhantomData;
// 상태를 나타내는 마커 타입 (ZST: Zero-Sized Type)
pub struct Draft;
pub struct Published;
pub struct Article<State> {
title: String,
content: String,
_state: PhantomData<State>,
}
// Draft 상태에서만 가능한 메서드
impl Article<Draft> {
pub fn new(title: impl Into<String>) -> Self {
Self {
title: title.into(),
content: String::new(),
_state: PhantomData,
}
}
pub fn set_content(mut self, content: impl Into<String>) -> Self {
self.content = content.into();
self
}
// 상태 전이: Draft → Published
pub fn publish(self) -> Article<Published> {
Article {
title: self.title,
content: self.content,
_state: PhantomData,
}
}
}
// Published 상태에서만 가능한 메서드
impl Article<Published> {
pub fn title(&self) -> &str {
&self.title
}
pub fn content(&self) -> &str {
&self.content
}
}
// 사용
let article = Article::<Draft>::new("Hello")
.set_content("World")
.publish(); // Draft → Published
// article.set_content("change"); // 컴파일 에러: Published에는 set_content 없음
println!("{}", article.title());
PhantomData 역할:
trait을 인터페이스로 사용해 런타임에 알고리즘을 교체한다.
pub trait Compressor: Send + Sync {
fn compress(&self, data: &[u8]) -> Vec<u8>;
fn name(&self) -> &str;
}
pub struct GzipCompressor;
pub struct ZstdCompressor;
impl Compressor for GzipCompressor {
fn compress(&self, data: &[u8]) -> Vec<u8> {
// gzip 압축 로직
todo!()
}
fn name(&self) -> &str { "gzip" }
}
impl Compressor for ZstdCompressor {
fn compress(&self, data: &[u8]) -> Vec<u8> {
// zstd 압축 로직
todo!()
}
fn name(&self) -> &str { "zstd" }
}
pub struct FileProcessor {
compressor: Box<dyn Compressor>,
}
impl FileProcessor {
pub fn new(compressor: Box<dyn Compressor>) -> Self {
Self { compressor }
}
pub fn process(&self, data: &[u8]) -> Vec<u8> {
println!("Using {} compressor", self.compressor.name());
self.compressor.compress(data)
}
}
// 런타임에 전략 선택
let processor = match format {
"gzip" => FileProcessor::new(Box::new(GzipCompressor)),
"zstd" => FileProcessor::new(Box::new(ZstdCompressor)),
_ => panic!("unknown format"),
};
pub struct FileProcessor<C: Compressor> {
compressor: C,
}
impl<C: Compressor> FileProcessor<C> {
pub fn new(compressor: C) -> Self {
Self { compressor }
}
pub fn process(&self, data: &[u8]) -> Vec<u8> {
self.compressor.compress(data)
}
}
// 컴파일 타임에 전략 결정 -- 인라이닝 가능, 제로 비용
let processor = FileProcessor::new(GzipCompressor);
판단 기준:
Box<dyn Trait> (동적)Arc<dyn Trait> (공유 + 동적)상세 레퍼런스 (예제·고급 패턴·흔한 실수) →
references/REFERENCE.md