Backend(Framework)/Rust / / 2025. 3. 25. 11:04

#[derive(...)] : 러스트의 매크로 속성

이 속성은 컴파일러에게 특정 트레이트(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 매크로가 이러한 기능들을 자동으로 구현해주기 때문에 코드를 훨씬 간결하게 작성할 수 있습니다.

 

이걸 그래서 왜?

  • 코드 간결성: 수십, 수백 줄의 구현 코드를 몇 글자로 대체할 수 있습니다.
  • 오류 감소: 직접 구현할 때 발생할 수 있는 버그를 줄일 수 있습니다.
  • 유지보수성: 구조체 필드가 변경되어도 트레이트 구현이 자동으로 업데이트됩니다.

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유