5장: 코드 구조화

프로그램이 커질수록 코드를 체계적으로 구조화하는 것이 중요해집니다. Rust는 구조체, 열거형, 모듈 등 강력한 조직화 도구를 제공합니다. 이 장에서는 깨끗하고 유지보수 가능한 코드를 작성하는 방법을 배웁니다.

5.1 구조체 (Structs) - 데이터 묶기

구조체는 관련된 데이터를 하나의 의미 있는 그룹으로 묶습니다. 객체지향 프로그래밍의 클래스와 비슷하지만, 상속 없이 구성(composition)에 집중합니다.

기본 구조체 정의

// 사용자 정보를 담는 구조체
struct User {
    username: String,
    email: String,
    active: bool,
    sign_in_count: u64,
}

fn main() {
    // 구조체 인스턴스 생성
    let user1 = User {
        email: String::from("user@example.com"),
        username: String::from("rustacean"),
        active: true,
        sign_in_count: 1,
    };

    println!("환영합니다, {}!", user1.username);
    println!("이메일: {}", user1.email);
}

가변 구조체

구조체의 필드를 수정하려면 전체 인스턴스를 mut로 선언해야 합니다. Rust는 일부 필드만 가변으로 만들 수 없습니다.

fn main() {
    let mut user = User {
        email: String::from("user@example.com"),
        username: String::from("rustacean"),
        active: true,
        sign_in_count: 1,
    };

    // 필드 수정
    user.email = String::from("new_email@example.com");
    user.sign_in_count += 1;

    println!("로그인 횟수: {}", user.sign_in_count);
}

구조체 업데이트 문법

기존 인스턴스의 대부분 값을 재사용하면서 일부만 변경할 수 있습니다.

fn main() {
    let user1 = User {
        email: String::from("user1@example.com"),
        username: String::from("user1"),
        active: true,
        sign_in_count: 1,
    };

    // user1의 대부분을 복사하되 email만 변경
    let user2 = User {
        email: String::from("user2@example.com"),
        ..user1  // 나머지는 user1에서 가져옴
    };

    // 주의: user1의 String 필드가 이동되었음!
    // println!("{}", user1.username); // 에러!
    println!("{}", user2.username); // OK
}

튜플 구조체

필드명 없이 타입만으로 구조체를 정의할 수 있습니다.

// RGB 색상을 나타내는 튜플 구조체
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);

    // Color와 Point는 다른 타입!
    println!("Red: {}", black.0);
    println!("X: {}", origin.0);
}

유닛 구조체

필드가 없는 구조체로, 트레이트를 구현할 때 유용합니다.

struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;
    // 트레이트 구현에 사용
}

5.2 메서드와 impl 블록

impl 블록을 사용해 구조체에 동작(behavior)을 추가합니다.

메서드 정의

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 메서드: 첫 번째 매개변수가 &self
    fn area(&self) -> u32 {
        self.width * self.height
    }

    // 가변 참조를 받는 메서드
    fn scale(&mut self, factor: u32) {
        self.width *= factor;
        self.height *= factor;
    }

    // 소유권을 가져가는 메서드 (드물게 사용)
    fn into_square(self) -> Rectangle {
        let size = self.width.max(self.height);
        Rectangle { width: size, height: size }
    }

    // 다른 인스턴스를 받는 메서드
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
    println!("넓이: {}", rect1.area());

    let mut rect2 = Rectangle { width: 10, height: 20 };
    rect2.scale(2);
    println!("2배 확대: {}x{}", rect2.width, rect2.height);

    println!("rect1이 rect2를 포함? {}", rect1.can_hold(&rect2));
}

연관 함수 (Associated Functions)

self를 받지 않는 함수로, 주로 생성자로 사용됩니다.

impl Rectangle {
    // 연관 함수 - 생성자
    fn new(width: u32, height: u32) -> Rectangle {
        Rectangle { width, height }
    }

    // 정사각형 생성자
    fn square(size: u32) -> Rectangle {
        Rectangle { width: size, height: size }
    }
}

fn main() {
    let rect = Rectangle::new(30, 50);
    let square = Rectangle::square(10);

    println!("사각형 넓이: {}", rect.area());
    println!("정사각형 넓이: {}", square.area());
}

여러 impl 블록

하나의 구조체에 여러 impl 블록을 가질 수 있습니다.

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

impl Rectangle {
    fn perimeter(&self) -> u32 {
        2 * (self.width + self.height)
    }
}

fn main() {
    let rect = Rectangle::new(30, 50);
    println!("넓이: {}, 둘레: {}", rect.area(), rect.perimeter());
}

5.3 열거형 (Enums) - 여러 가능성 표현

열거형은 하나의 값이 여러 가능한 변형(variant) 중 하나일 수 있음을 표현합니다.

기본 열거형

enum IpAddrKind {
    V4,
    V6,
}

fn main() {
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;

    route(four);
    route(six);
}

fn route(ip_kind: IpAddrKind) {
    // IP 주소 라우팅 로직
}

데이터를 포함하는 열거형

각 변형이 서로 다른 타입과 양의 데이터를 가질 수 있습니다.

enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

fn main() {
    let home = IpAddr::V4(127, 0, 0, 1);
    let loopback = IpAddr::V6(String::from("::1"));
}

복잡한 열거형

enum Message {
    Quit,                       // 데이터 없음
    Move { x: i32, y: i32 },    // 익명 구조체
    Write(String),              // 하나의 String
    ChangeColor(i32, i32, i32), // 세 개의 i32
}

impl Message {
    fn call(&self) {
        match self {
            Message::Quit => println!("종료"),
            Message::Move { x, y } => println!("({}, {})로 이동", x, y),
            Message::Write(text) => println!("텍스트: {}", text),
            Message::ChangeColor(r, g, b) => println!("색상: ({}, {}, {})", r, g, b),
        }
    }
}

fn main() {
    let msg1 = Message::Write(String::from("안녕하세요"));
    let msg2 = Message::Move { x: 10, y: 20 };

    msg1.call();
    msg2.call();
}
Option<T> - Null 없는 세상

Rust에는 null이 없습니다. 대신 Option<T> 열거형으로 값의 존재/부재를 표현합니다.

enum Option<T> {
    Some(T),
    None,
}

fn main() {
    let some_number = Some(5);
    let some_string = Some("문자열");

    // 타입을 명시해야 함
    let absent_number: Option<i32> = None;

    // Option을 사용하는 함수
    let x: i32 = 5;
    let y: Option<i32> = Some(5);

    // let sum = x + y; // 에러! i32와 Option<i32>는 다른 타입

    // Option을 처리해야 함
    let sum = match y {
        Some(value) => x + value,
        None => x,
    };

    println!("합계: {}", sum);
}

Result<T, E> - 에러 처리

성공 또는 실패를 표현하는 열거형입니다.

enum Result<T, E> {
    Ok(T),
    Err(E),
}

use std::fs::File;

fn main() {
    let file_result = File::open("hello.txt");

    let file = match file_result {
        Ok(file) => file,
        Err(error) => {
            println!("파일 열기 실패: {:?}", error);
            return;
        }
    };
}

5.4 패턴 매칭

match는 Rust의 강력한 제어 흐름 연산자로, 패턴에 따라 코드를 실행합니다.

기본 매칭

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => {
            println!("행운의 페니!");
            1
        }
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

fn main() {
    let coin = Coin::Penny;
    println!("가치: {}센트", value_in_cents(coin));
}

값을 바인딩하는 패턴

enum UsState {
    Alabama,
    Alaska,
    // ...
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("주 쿼터 from {:?}!", state);
            25
        }
    }
}

Option 매칭

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

fn main() {
    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);

    match six {
        Some(value) => println!("값: {}", value),
        None => println!("값 없음"),
    }
}

_ 플레이스홀더

나머지 모든 경우를 처리하는 패턴입니다.

fn main() {
    let dice_roll = 9;

    match dice_roll {
        3 => add_fancy_hat(),
        7 => remove_fancy_hat(),
        _ => reroll(),  // 그 외 모든 값
    }
}

fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn reroll() {}

if let - 간결한 매칭

하나의 패턴만 관심 있을 때 사용합니다.

fn main() {
    let config_max = Some(3u8);

    // match 사용
    match config_max {
        Some(max) => println!("최댓값: {}", max),
        _ => (),
    }

    // if let 사용 (더 간결)
    if let Some(max) = config_max {
        println!("최댓값: {}", max);
    }
}

5.5 모듈 시스템

Rust의 모듈 시스템은 코드를 조직화하고 재사용 가능하게 만듭니다.

기본 모듈

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {
            println!("손님을 대기 명단에 추가");
        }

        fn seat_at_table() {
            println!("자리에 안내");
        }
    }

    mod serving {
        fn take_order() {}
        fn serve_order() {}
        fn take_payment() {}
    }
}

pub fn eat_at_restaurant() {
    // 절대 경로
    crate::front_of_house::hosting::add_to_waitlist();

    // 상대 경로
    front_of_house::hosting::add_to_waitlist();
}

fn main() {
    eat_at_restaurant();
}

파일로 분리하기

모듈을 별도 파일로 분리할 수 있습니다.

// src/lib.rs
mod front_of_house;  // front_of_house.rs 파일을 찾음

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

// src/front_of_house.rs
pub mod hosting {
    pub fn add_to_waitlist() {}
}

// src/front_of_house/hosting.rs (대안)
pub fn add_to_waitlist() {}

use 키워드

경로를 스코프로 가져와 더 짧게 만듭니다.

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert("key", "value");
}

// as로 이름 변경
use std::io::Result as IoResult;

// 여러 항목 가져오기
use std::io::{self, Write};

// glob 연산자
use std::collections::*;
모듈 베스트 프랙티스

5.6 WIA 표준 구조 예제

WIA 표준을 Rust로 구현하는 실전 예제입니다.

// WIA-HOME 장치 모델
mod wia_home {
    pub enum DeviceType {
        Light,
        Thermostat,
        Camera,
        Lock,
    }

    pub struct Device {
        id: String,
        device_type: DeviceType,
        status: DeviceStatus,
    }

    pub enum DeviceStatus {
        Online,
        Offline,
        Error(String),
    }

    impl Device {
        pub fn new(id: String, device_type: DeviceType) -> Self {
            Device {
                id,
                device_type,
                status: DeviceStatus::Offline,
            }
        }

        pub fn connect(&mut self) -> Result<(), String> {
            match self.status {
                DeviceStatus::Offline => {
                    self.status = DeviceStatus::Online;
                    Ok(())
                }
                DeviceStatus::Online => {
                    Err("이미 연결됨".to_string())
                }
                DeviceStatus::Error(ref msg) => {
                    Err(format!("에러 상태: {}", msg))
                }
            }
        }

        pub fn get_status(&self) -> &DeviceStatus {
            &self.status
        }
    }
}

fn main() {
    use wia_home::{Device, DeviceType};

    let mut light = Device::new(
        String::from("light-001"),
        DeviceType::Light
    );

    match light.connect() {
        Ok(_) => println!("장치 연결 성공"),
        Err(e) => println!("연결 실패: {}", e),
    }
}

5.7 실전 팁

구조체 vs 열거형 선택 기준
일반적인 실수

5.8 고급 패턴

빌더 패턴

복잡한 구조체를 단계적으로 생성하는 패턴입니다.

struct Server {
    host: String,
    port: u16,
    timeout: u64,
    max_connections: u32,
}

struct ServerBuilder {
    host: Option<String>,
    port: Option<u16>,
    timeout: Option<u64>,
    max_connections: Option<u32>,
}

impl ServerBuilder {
    fn new() -> Self {
        ServerBuilder {
            host: None,
            port: None,
            timeout: None,
            max_connections: None,
        }
    }

    fn host(mut self, host: &str) -> Self {
        self.host = Some(host.to_string());
        self
    }

    fn port(mut self, port: u16) -> Self {
        self.port = Some(port);
        self
    }

    fn timeout(mut self, timeout: u64) -> Self {
        self.timeout = Some(timeout);
        self
    }

    fn max_connections(mut self, max: u32) -> Self {
        self.max_connections = Some(max);
        self
    }

    fn build(self) -> Result<Server, String> {
        Ok(Server {
            host: self.host.ok_or("host is required")?,
            port: self.port.unwrap_or(8080),
            timeout: self.timeout.unwrap_or(30),
            max_connections: self.max_connections.unwrap_or(100),
        })
    }
}

fn main() {
    let server = ServerBuilder::new()
        .host("localhost")
        .port(3000)
        .timeout(60)
        .max_connections(500)
        .build()
        .unwrap();

    println!("서버: {}:{}", server.host, server.port);
}

상태 패턴

열거형으로 상태 머신을 구현할 수 있습니다.

enum ConnectionState {
    Disconnected,
    Connecting,
    Connected { session_id: String },
    Error { message: String },
}

struct Connection {
    state: ConnectionState,
    address: String,
}

impl Connection {
    fn new(address: String) -> Self {
        Connection {
            state: ConnectionState::Disconnected,
            address,
        }
    }

    fn connect(&mut self) -> Result<(), String> {
        match self.state {
            ConnectionState::Disconnected => {
                self.state = ConnectionState::Connecting;
                // 연결 시뮬레이션
                self.state = ConnectionState::Connected {
                    session_id: "abc123".to_string(),
                };
                Ok(())
            }
            ConnectionState::Connected { .. } => {
                Err("이미 연결됨".to_string())
            }
            _ => Err("연결할 수 없는 상태".to_string()),
        }
    }

    fn get_status(&self) -> String {
        match &self.state {
            ConnectionState::Disconnected => "연결 끊김".to_string(),
            ConnectionState::Connecting => "연결 중...".to_string(),
            ConnectionState::Connected { session_id } => {
                format!("연결됨 (세션: {})", session_id)
            }
            ConnectionState::Error { message } => {
                format!("에러: {}", message)
            }
        }
    }
}

fn main() {
    let mut conn = Connection::new("localhost:8080".to_string());
    println!("상태: {}", conn.get_status());

    conn.connect().unwrap();
    println!("상태: {}", conn.get_status());
}

5.9 연습 문제

도전 과제

다음 기능을 구현해보세요:

  1. 도서관 시스템: 책(Book), 회원(Member), 대출(Loan) 구조체를 만들고 대출/반납 기능 구현
  2. 계산기: 사칙연산을 열거형으로 정의하고 계산 함수 작성
  3. 색상 믹서: RGB와 HSL 간 변환을 구조체와 메서드로 구현
  4. 파일 탐색기: 파일과 폴더를 열거형으로 표현하고 트리 구조 순회
  5. 주문 시스템: 주문 상태를 열거형으로 관리하고 상태 전이 구현
학습 체크리스트

한국 일반 인프라 매핑 (제5장)

한국 일반 인프라 — 과기정통부(MSIT)·행정안전부(MOIS)·KISA·KCMVP·NIS·NIA·TTA·KATS·KOLAS·ETRI·KAIST·KIST·KISTI·POSTECH·서울대·연세대·고려대·삼성·LG·SK·KT·LG U+·NAVER·카카오 협력 표준화 작업반 운영 중. 「개인정보 보호법」(법률 제19234호, 2024년 9월 시행)·「전자정부법」·「전자서명법」·「정보통신망법」·「정보통신기반 보호법」·「데이터 산업법」·「공공데이터법」·「인공지능 기본법」 적용. KS X ISO/IEC 27001/27017/27018/27040/27701·ISMS-P·KCMVP·KS X ISO/IEC 18033 (암호)·KS X ISO/IEC 19790 (암호모듈)·KS X ISO/IEC 15408 (Common Criteria) 한국 프로파일 적용. NIA「ICT 표준화 추진체계 운영」·KISA「개인정보보호 종합 포털」·MSIT「K-디지털 2030」 로드맵 운영 중.

한국 표준화 인프라 종합 매핑

한국의 산업·기술 표준화는 다음 협력 체계를 통해 운영된다. 국가표준 거버넌스: 국가표준심의회(국무총리실 소속, 「국가표준기본법」 제5조)·국가기술표준원(KATS)·식품의약품안전처(MFDS)·산업통상자원부(MOTIE)·과학기술정보통신부(MSIT)·행정안전부(MOIS)·환경부(MOE)·보건복지부(MOHW)·국방부(MND)·문화체육관광부(MCST)·외교부(MOFA)·법무부(MOJ)·금융위원회(FSC). 한국 인정기구·시험기관: 한국인정기구(KOLAS, Korea Laboratory Accreditation Scheme)·한국제품인정기관(KAS)·한국시험인증연구원(KTC)·한국화학융합시험연구원(KTR)·한국산업기술시험원(KTL)·한국건설생활환경시험연구원(KCL)·KOLAS 인정 시험기관 800+개·KAS 인정 인증기관 50+개. 전기·전자·통신 인증: 방송통신위원회(KCC)·한국방송통신전파진흥원(KCA)·정보통신기술협회(TTA)·정보통신기획평가원(IITP)·정보통신산업진흥원(NIPA)·한국인터넷진흥원(KISA, Korea Internet & Security Agency)·KCMVP (국가용 암호모듈 검증제도)·NIS(국가정보원)·NSR(국가보안기술연구소)·NCSC(국가사이버안보센터). 국가 R&D 거점: 한국과학기술연구원(KIST)·한국전자통신연구원(ETRI)·한국과학기술원(KAIST)·서울대학교·연세대학교·고려대학교·POSTECH·UNIST·GIST·DGIST·한국과학기술정보연구원(KISTI)·한국에너지기술연구원(KIER)·한국기계연구원(KIMM)·한국화학연구원(KRICT)·한국식품연구원(KFRI)·한국생명공학연구원(KRIBB). 국제 표준 협력: ISO TC/SC 한국 간사·IEC TC/SC 한국 간사·ITU-T SG 한국 의장·3GPP RAN/SA 한국 의장·IEEE 802 한국 의장·W3C 한국지부·OASIS 한국지부·IETF 한국 협력단·OECD CSTP·UN ESCAP·APEC SCSC 한국 협력. 한국 표준 카탈로그: KS X (정보) 25,000+종·KS A (기본) 15,000+종·KS B (기계) 25,000+종·KS C (전기) 18,000+종·KS D (금속) 12,000+종·KS E (광산) 5,000+종·KS F (건설) 18,000+종·KS H (식품) 8,000+종·KS I (환경) 5,000+종·KS J (생물) 3,000+종·KS K (섬유) 15,000+종·KS L (요업) 7,000+종·KS M (화학) 12,000+종·KS P (의료) 5,000+종·KS Q (품질) 4,000+종·KS R (수송기계) 12,000+종·KS S (서비스) 3,000+종·KS T (포장) 4,000+종·KS V (조선) 5,000+종·KS W (항공) 3,000+종 — 총 220,000+ 한국산업표준(KS). 「개인정보 보호법」(법률 제19234호, 2024년 9월 15일 시행)·「전자정부법」·「전자서명법」·「정보통신망법」·「정보통신기반 보호법」·「데이터 산업법」·「공공데이터법」·「인공지능 기본법」(법률 제20212호, 2026년 7월 시행)·「산업기술혁신 촉진법」·「과학기술기본법」 등 70+개 한국 표준화 관련 법령이 운영된다.

한국 디지털 전환·표준화 상세 매핑

한국의 디지털 전환과 표준화는 다음 협력 체계로 운영된다. 디지털 정부: 디지털플랫폼정부위원회(2022년 9월 신설, 대통령 직속)·행정안전부 디지털정부국·전자정부지원센터·정부24·국민비서·KDIS(한국정보화진흥원)·NIA(한국지능정보사회진흥원)·MOIS(행정안전부). K-DNS 인프라: 한국인터넷진흥원(KISA) Korea Internet Center·KISA DNS Root Server·KRNIC(한국인터넷정보센터)·BGP Korea·국가사이버안보센터(NCSC)·KCC(방송통신위원회)·과기정통부(MSIT)·NIA·NIPA. 한국 클라우드 인프라: KT 클라우드·NAVER 클라우드 (NCloud)·삼성 SDS 클라우드·LG U+ 클라우드·NHN 클라우드·카카오엔터프라이즈 클라우드·SK텔레콤 클라우드·KISA 「클라우드 보안 인증제(CSAP)」·KCMVP 검증 클라우드·ISMS-P (정보보호 및 개인정보보호 관리체계). 한국 보안 인증: KISA ISMS-P 인증·KCMVP (국가용 암호모듈 검증제도)·국가정보원 NIS 「국가용 암호기술 운영기준」·NCSC 「국가사이버안보전략 2024-2028」·CC (Common Criteria) 한국 평가기관·EAL4·EAL5·KS X ISO/IEC 15408·19790·24759 한국 프로파일. 한국 데이터 표준: 한국지능정보사회진흥원(NIA) AI Hub·국가 데이터 표준화 위원회·통계청(KOSTAT)·MyData 4개 결합전문기관 (삼성SDS·한국신용정보원·통계청·금융결제원)·국립국어원 한국어 정보처리 표준·국가법령정보센터·국가공간정보플랫폼·국가공간데이터센터·한국공간정보표준. 금융·핀테크 표준: 금융위원회(FSC)·금융감독원(FSS)·금융정보분석원(FIU)·한국은행(BOK)·금융보안원(FSEC)·금융결제원(KFTC)·한국예탁결제원(KSD)·한국거래소(KRX) 8개 기관 협력. 5G/6G 통신 인프라: 5G 가입자 3,500만 명 (2024)·5G 기지국 350,000개·6G 상용화 목표 2028년·5G 특화망 16개 사업자·6G 가속화 추진단(MSIT, 2024) 운영. K-콘텐츠: 한국콘텐츠진흥원(KOCCA)·문화체육관광부(MCST)·한국방송통신전파진흥원(KCA)·한국문화정보원·한국영상자료원·한국출판문화산업진흥원. 「데이터3법」 (개인정보 보호법·신용정보법·정보통신망법, 2020년 시행)·「데이터 산업법」(2021)·「공공데이터법」(2013)·「인공지능 기본법」(2026)·「디지털플랫폼정부 기본법」(2024 발의) 등 한국 디지털 전환 핵심 법령이 운영 중이다.