RUST · 심층 가이드
Rust 패턴 완전 정리
빌더와 전략, 옵저버, 상태 패턴부터 저장소·미들웨어 체인·의존성 주입까지 Rust에서 20가지 설계 패턴을 어떻게 바꿔 쓰는지 정리했습니다.
고전 디자인 패턴 상당수는 클래스 상속을 전제로 씌어졌는데 Rust에는 상속이 없습니다. 그래서 패턴을 그대로 옮기는 대신 트레이트와 합성으로 다시 쓰게 됩니다. 이 과정에서 어떤 패턴은 훨씬 단순해지고, 옵저버처럼 상호 참조가 필요한 패턴은 오히려 까다로워집니다. 패턴 이름보다 무엇을 분리하려는 패턴인가를 먼저 보는 편이 유용합니다.
빌더 패턴과 설정 객체는 Rust에서 가장 자연스럽게 자리 잡은 조합이라 여기서 시작하는 편이 좋습니다. 전략 패턴과 어댑터는 트레이트 하나로 정리되는 반면, 옵저버는 여러 곳에서 같은 대상을 참조해야 해서 Rc와 RefCell이 등장합니다. 상태 패턴은 열거형으로 표현하는 길과 타입 자체로 상태를 강제하는 길이 갈리므로 두 방식을 비교하며 읽으십시오.
정적 디스패치와 동적 디스패치의 선택이 조용한 갈림길입니다. 제네릭은 타입마다 코드를 찍어 내 빠르지만 컬렉션에 서로 다른 구현을 섞어 담을 수 없고, dyn Trait은 섞어 담을 수 있는 대신 트레이트가 객체 안전 조건을 만족해야 합니다. 제네릭 메서드나 Self를 반환하는 메서드가 있으면 그 트레이트는 dyn으로 쓸 수 없습니다.
01설정 객체
Rust에서 설정 객체 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 설정 객체
trait Discount { fn apply(&self, value: i32) -> i32; }
struct Percent;
impl Discount for Percent { fn apply(&self, value: i32) -> i32 { value * 9 / 10 } }
fn main() {
println!("{}", Percent.apply(100));
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
02전략 패턴
Rust에서 전략 패턴 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 전략 패턴
use std::collections::HashMap;
fn main() {
let mut cache = HashMap::new();
cache.entry(2).or_insert("user");
println!("{}", cache[&2]);
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
03옵저버
Rust에서 옵저버 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 옵저버
trait Discount { fn apply(&self, value: i32) -> i32; }
struct Percent;
impl Discount for Percent { fn apply(&self, value: i32) -> i32 { value * 9 / 10 } }
fn main() {
println!("{}", Percent.apply(100));
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
04데코레이터
Rust에서 데코레이터 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 데코레이터
use std::collections::HashMap;
fn main() {
let mut cache = HashMap::new();
cache.entry(4).or_insert("user");
println!("{}", cache[&4]);
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
05어댑터
Rust에서 어댑터 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 어댑터
trait Discount { fn apply(&self, value: i32) -> i32; }
struct Percent;
impl Discount for Percent { fn apply(&self, value: i32) -> i32 { value * 9 / 10 } }
fn main() {
println!("{}", Percent.apply(100));
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
06커맨드
Rust에서 커맨드 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 커맨드
use std::collections::HashMap;
fn main() {
let mut cache = HashMap::new();
cache.entry(6).or_insert("user");
println!("{}", cache[&6]);
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
07상태 패턴
Rust에서 상태 패턴 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 상태 패턴
trait Discount { fn apply(&self, value: i32) -> i32; }
struct Percent;
impl Discount for Percent { fn apply(&self, value: i32) -> i32 { value * 9 / 10 } }
fn main() {
println!("{}", Percent.apply(100));
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
08팩터리 메서드
Rust에서 팩터리 메서드 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 팩터리 메서드
use std::collections::HashMap;
fn main() {
let mut cache = HashMap::new();
cache.entry(8).or_insert("user");
println!("{}", cache[&8]);
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
09빌더 패턴
Rust에서 빌더 패턴 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 빌더 패턴
trait Discount { fn apply(&self, value: i32) -> i32; }
struct Percent;
impl Discount for Percent { fn apply(&self, value: i32) -> i32 { value * 9 / 10 } }
fn main() {
println!("{}", Percent.apply(100));
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
10캐시 래퍼
Rust에서 캐시 래퍼 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 캐시 래퍼
use std::collections::HashMap;
fn main() {
let mut cache = HashMap::new();
cache.entry(10).or_insert("user");
println!("{}", cache[&10]);
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
11retry 유틸
Rust에서 retry 유틸 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// retry 유틸
trait Discount { fn apply(&self, value: i32) -> i32; }
struct Percent;
impl Discount for Percent { fn apply(&self, value: i32) -> i32 { value * 9 / 10 } }
fn main() {
println!("{}", Percent.apply(100));
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
12미들웨어 체인
Rust에서 미들웨어 체인 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 미들웨어 체인
use std::collections::HashMap;
fn main() {
let mut cache = HashMap::new();
cache.entry(12).or_insert("user");
println!("{}", cache[&12]);
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
13검증기 조합
Rust에서 검증기 조합 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 검증기 조합
trait Discount { fn apply(&self, value: i32) -> i32; }
struct Percent;
impl Discount for Percent { fn apply(&self, value: i32) -> i32 { value * 9 / 10 } }
fn main() {
println!("{}", Percent.apply(100));
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
14포매터 파이프
Rust에서 포매터 파이프 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 포매터 파이프
use std::collections::HashMap;
fn main() {
let mut cache = HashMap::new();
cache.entry(14).or_insert("user");
println!("{}", cache[&14]);
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
15페이지네이션
Rust에서 페이지네이션 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 페이지네이션
trait Discount { fn apply(&self, value: i32) -> i32; }
struct Percent;
impl Discount for Percent { fn apply(&self, value: i32) -> i32 { value * 9 / 10 } }
fn main() {
println!("{}", Percent.apply(100));
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
16데이터 매퍼
Rust에서 데이터 매퍼 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 데이터 매퍼
use std::collections::HashMap;
fn main() {
let mut cache = HashMap::new();
cache.entry(16).or_insert("user");
println!("{}", cache[&16]);
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
17저장소 패턴
Rust에서 저장소 패턴 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 저장소 패턴
trait Discount { fn apply(&self, value: i32) -> i32; }
struct Percent;
impl Discount for Percent { fn apply(&self, value: i32) -> i32 { value * 9 / 10 } }
fn main() {
println!("{}", Percent.apply(100));
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
18의존성 주입 컨테이너
Rust에서 의존성 주입 컨테이너 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 의존성 주입 컨테이너
use std::collections::HashMap;
fn main() {
let mut cache = HashMap::new();
cache.entry(18).or_insert("user");
println!("{}", cache[&18]);
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
19템플릿 메서드
Rust에서 템플릿 메서드 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 템플릿 메서드
trait Discount { fn apply(&self, value: i32) -> i32; }
struct Percent;
impl Discount for Percent { fn apply(&self, value: i32) -> i32 { value * 9 / 10 } }
fn main() {
println!("{}", Percent.apply(100));
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
20파사드
Rust에서 파사드 개념을 코드로 빠르게 확인하는 패턴 예제입니다.
Rust code
// 파사드
use std::collections::HashMap;
fn main() {
let mut cache = HashMap::new();
cache.entry(20).or_insert("user");
println!("{}", cache[&20]);
}패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.
패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.
정리하며
- 상속이 없으므로 패턴은 트레이트와 합성으로 다시 씁니다
- 전략·어댑터는 트레이트 하나로 단순해지지만 옵저버는 Rc와 RefCell을 부릅니다
- 상태는 열거형으로 표현하거나 타입으로 강제하는 두 갈래가 있습니다
- dyn Trait은 객체 안전 조건을 만족해야 하며 제네릭 메서드는 그것을 깹니다
더 깊이 들어가고 싶다면 Rust 학습 라이브러리에서 다른 주제 가이드를 이어서 보거나, 언어 비교에서 같은 개념이 다른 언어에서 어떻게 표현되는지 확인해 보세요.