이 속성은 컴파일러에게 특정 트레이트(trait) 구현을 자동으로 생성하도록 지시합니다.
derive 속성을 사용하면 반복적이고 상용구 코드(boilerplate code)를 직접 작성하지 않아도 됩니다. 컴파일러가 자동으로 해당 트레이트의 구현을 생성해 줍니다.
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
struct User {
id: u64,
name: String,
}
이 코드에서 derive 속성은 User 구조체에 대해 아래 트레이트들의 구현을 자동 생성합니다.
- Debug: 디버깅을 위한 출력 형식을 제공합니다. println!("{:?}", user)와 같이 사용할 수 있습니다.
- Deserialize: Serde를 통해 외부 데이터 형식(JSON 등)에서 러스트 구조체로 변환하는 기능을 제공합니다.
- Serialize: 러스트 구조체를 외부 데이터 형식으로 변환하는 기능을 제공합니다.
- Clone: 객체의 깊은 복사본을 만들 수 있게 합니다. let user_copy = user.clone();와 같이 사용됩니다.
- PartialEq: 동등성 비교(==, !=)를 가능하게 합니다. if user1 == user2 { ... }와 같이 사용됩니다.
예시 코드
더보기
러스트의 derive 트레이트 예시
각 트레이트의 실제 사용 예시를 코드와 함께 보여드리겠습니다.
Debug 트레이트 예시
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 10, y: 20 };
// 기본 디버그 출력
println!("{:?}", point); // 출력: Point { x: 10, y: 20 }
// 예쁘게 포맷팅된 디버그 출력
println!("{:#?}", point);
// 출력:
// Point {
// x: 10,
// y: 20,
// }
// debug_assert!에서도 사용 가능
debug_assert_eq!(point.x, 10);
}
Serialize와 Deserialize 트레이트 예시
use serde::{Deserialize, Serialize};
use serde_json;
#[derive(Serialize, Deserialize, Debug)]
struct Person {
name: String,
age: u8,
addresses: Vec<Address>,
}
#[derive(Serialize, Deserialize, Debug)]
struct Address {
street: String,
city: String,
}
fn main() {
// 객체 생성
let person = Person {
name: "김철수".to_string(),
age: 30,
addresses: vec![
Address {
street: "세종대로 110".to_string(),
city: "서울".to_string(),
},
Address {
street: "중앙로 123".to_string(),
city: "부산".to_string(),
},
],
};
// 직렬화: 구조체 -> JSON 문자열
let json = serde_json::to_string_pretty(&person).unwrap();
println!("직렬화된 JSON:\n{}", json);
// 역직렬화: JSON 문자열 -> 구조체
let deserialized: Person = serde_json::from_str(&json).unwrap();
println!("역직렬화된 객체: {:?}", deserialized);
// 다른 형식으로도 직렬화 가능 (YAML 예시)
// let yaml = serde_yaml::to_string(&person).unwrap();
}
Clone 트레이트 예시
#[derive(Clone, Debug)]
struct Database {
name: String,
connections: Vec<String>,
is_active: bool,
}
fn main() {
let db = Database {
name: "ProductionDB".to_string(),
connections: vec!["192.168.1.1".to_string(), "192.168.1.2".to_string()],
is_active: true,
};
// 원본 객체
println!("원본: {:?}", db);
// 복제 생성
let db_backup = db.clone();
// 원본 변경해도 복제본은 영향 없음 (깊은 복사)
let mut mutable_db = db;
mutable_db.name = "ModifiedDB".to_string();
mutable_db.is_active = false;
println!("변경된 원본: {:?}", mutable_db);
println!("영향 없는 복제본: {:?}", db_backup);
// 부분 복제도 가능
let connections_backup = db_backup.connections.clone();
}
PartialEq 트레이트 예시
#[derive(Debug, PartialEq)]
struct Product {
id: u32,
name: String,
price: f64,
}
#[derive(Debug, PartialEq)]
enum Status {
Active,
Inactive,
Pending(String),
}
fn main() {
let product1 = Product {
id: 1,
name: "노트북".to_string(),
price: 1_200_000.0,
};
let product2 = Product {
id: 1,
name: "노트북".to_string(),
price: 1_200_000.0,
};
let product3 = Product {
id: 2,
name: "스마트폰".to_string(),
price: 800_000.0,
};
// 동등성 비교
println!("product1 == product2: {}", product1 == product2); // true
println!("product1 == product3: {}", product1 == product3); // false
// 열거형(enum)에서도 사용 가능
let status1 = Status::Active;
let status2 = Status::Active;
let status3 = Status::Pending("승인 대기 중".to_string());
println!("status1 == status2: {}", status1 == status2); // true
println!("status1 == status3: {}", status1 == status3); // false
// 컬렉션에서 활용
let products = vec![product1, product3];
println!("products에 product2와 같은 제품이 있는가: {}",
products.contains(&product2)); // true
}
이 예시들은 각 트레이트가 실제로 어떻게 동작하는지 보여주며, derive 매크로가 이러한 기능들을 자동으로 구현해주기 때문에 코드를 훨씬 간결하게 작성할 수 있습니다.
이걸 그래서 왜?
- 코드 간결성: 수십, 수백 줄의 구현 코드를 몇 글자로 대체할 수 있습니다.
- 오류 감소: 직접 구현할 때 발생할 수 있는 버그를 줄일 수 있습니다.
- 유지보수성: 구조체 필드가 변경되어도 트레이트 구현이 자동으로 업데이트됩니다.
'Backend(Framework) > Rust' 카테고리의 다른 글
[Rust] 응답 객체 패턴: ResponseDto 구현 (0) | 2025.03.25 |
---|---|
[Rust] 구조체 (0) | 2025.03.25 |
러스트(Rust) 웹 개발의 핵심 라이브러리 소개 (0) | 2025.03.25 |