Rust 핵심: 열거형과 매칭
안녕하세요! 여러분의 코딩 구원자, 재준봇입니다!
자, 여러분. 여기까지 오시느라 정말 고생 많으셨습니다. 이제 드디어 Rust의 꽃, 아니 Rust의 정수라고 할 수 있는 열거형(Enum)과 매칭(Match) 단계에 진입하셨네요.
솔직히 말씀드릴게요. 이 파트 제대로 이해 못 하면 Rust 공부하는 의미가 없습니다. 왜냐고요? Rust가 다른 언어보다 훨씬 안전하고 강력하다고 소문난 이유가 바로 여기서 나오거든요. 하지만 걱정 마세요. 제가 여러분의 눈높이에서, 아주 찰떡같은 비유로 머릿속에 때려 박아 드리겠습니다.
준비되셨나요? 그럼 바로 시작하겠습니다!
15강: Rust 핵심: 열거형과 매칭
1. 열거형(Enum)이 도대체 뭔가요?
코딩을 처음 접하는 분들은 열거형이라는 단어만 들어도 “어우, 무슨 사전 읽는 것 같아”라고 생각하실 겁니다. 아주 쉽게 설명해 드릴게요.
여러분, 카페에 갔다고 상상해 보세요. 메뉴판에 커피, 에이드, 티가 있습니다. 여기서 여러분은 선택을 해야 합니다. 중요한 건, 여러분이 지금 이 순간에 주문하는 메뉴는 커피이거나, 에이드이거나, 티 중 딱 하나여야 한다는 겁니다. 커피이면서 동시에 티일 수는 없죠.
이렇게 “여러 가지 선택지 중 딱 하나만 가질 수 있는 타입”을 만드는 것이 바로 열거형입니다.
왜 그냥 문자열로 처리 안 하나요?
“재준봇님, 그냥 String으로 ‘커피’라고 쓰면 안 되나요?”라고 물으실 수 있습니다. 하지만 그러면 대참사가 일어납니다. 누군가는 ‘커피’라고 쓰고, 누군가는 ‘Coffee’라고 쓰고, 누군가는 오타가 나서 ‘커펴’라고 쓴다면? 프로그램은 멘붕에 빠지고 결국 터져버리겠죠.
열거형은 이런 오타와 실수 가능성을 원천 봉쇄해서, 컴파일러가 “야! 너 여기 메뉴판에 없는 거 주문했어!”라고 미리 알려주게 만드는 아주 똑똑한 장치입니다.
2. 기본 열거형 만들기: 단순한 선택지
가장 기초적인 형태부터 보겠습니다. 날씨를 표현하는 열거형을 만들어 보죠.
// 날씨를 나타내는 열거형을 정의합니다.
enum Weather {
Sunny, // 맑음
Cloudy, // 흐림
Rainy, // 비
Snowy, // 눈
}
fn main() {
// 현재 날씨 변수를 생성하고 '맑음' 상태를 할당합니다.
let current_weather = Weather::Sunny;
// 여기서 주의할 점은 Weather::Sunny 처럼 '타입::값' 형태로 사용한다는 것입니다.
println!("오늘의 날씨 상태가 결정되었습니다!");
}
코드 뜯어보기
enum Weather { ... }: 이제Weather라는 새로운 타입을 만들었습니다. 이 타입은 오직Sunny,Cloudy,Rainy,Snowy네 가지 값만 가질 수 있습니다.Weather::Sunny: 이것을 ‘연관된 값’이라고 합니다.Weather라는 상자 안에 들어있는Sunny라는 항목을 꺼내 쓴다고 생각하세요.
3. Rust 열거형의 진짜 마법: 데이터 품기
자, 이제부터가 진짜입니다. 다른 언어(C, Java 등)의 열거형은 단순히 이름만 나열하는 경우가 많습니다. 하지만 Rust의 열거형은 각 선택지가 서로 다른 종류의 데이터를 가질 수 있습니다.
이거 진짜 신기한 기능입니다. 비유를 들어볼게요. 여러분 집에 ‘수납함’이 있는데, 첫 번째 칸에는 ‘책’을 넣고, 두 번째 칸에는 ‘옷’을 넣고, 세 번째 칸에는 ‘약’을 넣는다고 생각하세요. 칸마다 들어가는 물건의 종류와 크기가 다 다르죠? Rust의 열거형이 바로 이 수납함입니다.
웹 메시지를 처리하는 예제로 확인해 보겠습니다.
// 메시지 타입을 정의하는데, 각 타입마다 담기는 데이터가 다릅니다.
enum WebMessage {
Quit, // 데이터가 없는 단순한 종료 신호
Move { x: i32, y: i32 }, // 좌표 정보가 필요한 이동 신호 (구조체 형태)
Write(String), // 메시지 내용이 필요한 쓰기 신호 (튜플 형태)
ChangeColor(i32, i32, i32), // RGB 값이 필요한 색상 변경 신호 (튜플 형태)
}
fn main() {
// 1. 단순 종료 메시지
let m1 = WebMessage::Quit;
// 2. 좌표 이동 메시지 (x: 10, y: 20)
let m2 = WebMessage::Move { x: 10, y: 20 };
// 3. 텍스트 쓰기 메시지
let m3 = WebMessage::Write(String::from("안녕하세요 재준봇님!"));
// 4. 색상 변경 메시지 (빨강, 초록, 파랑)
let m4 = WebMessage::ChangeColor(255, 0, 0);
println!("다양한 형태의 메시지들이 생성되었습니다!");
}
코드 뜯어보기
Quit: 데이터가 없습니다. 그냥 “끝내!”라는 신호만 줍니다.Move { x: i32, y: i32 }: 이건 열거형 안에 이름 있는 필드(구조체)를 넣은 겁니다. x좌표와 y좌표라는 구체적인 정보가 필요하니까요.Write(String): 이건 튜플 형태입니다. 그냥 문자열 하나만 툭 던져주는 방식이죠.ChangeColor(i32, i32, i32): 역시 튜플 형태입니다. 숫자 3개를 순서대로 넣어서 RGB 값을 표현합니다.
이렇게 하나의 타입(WebMessage)이 상황에 따라 전혀 다른 형태의 데이터를 가질 수 있다는 점, 이게 바로 Rust의 강력함입니다!
4. 매칭(Match): 열거형의 짝꿍
열거형을 만들었으면 이제 꺼내 써야겠죠? 이때 사용하는 것이 바로 match 문입니다. match는 마치 “초정밀 분류기”와 같습니다. 들어온 값이 무엇인지 확인하고, 그 값에 딱 맞는 처리 방법을 실행합니다.
여기서 중요한 점! Rust의 match는 모든 경우의 수를 다 적어야 합니다. 하나라도 빼먹으면 컴파일러가 “야! 너 비 올 때 어떻게 할지 안 적었어! 당장 적어!”라고 화를 냅니다. 이게 바로 Rust가 안전한 이유입니다.
방법 1: 완전한 match 문 (가장 정석적인 방법)
enum WebMessage {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn process_message(msg: WebMessage) {
match msg {
WebMessage::Quit => {
println!("프로그램을 종료합니다.");
}
WebMessage::Move { x, y } => {
println!("좌표 ({}, {})로 이동합니다.", x, y);
}
WebMessage::Write(text) => {
println!("메시지 작성: {}", text);
}
WebMessage::ChangeColor(r, g, b) => {
println!("색상을 RGB({}, {}, {})로 변경합니다.", r, g, b);
}
}
}
fn main() {
let msg = WebMessage::Write(String::from("Rust는 정말 재밌어!"));
process_message(msg);
}
코드 뜯어보기
match msg { ... }:msg라는 변수의 값이 무엇인지 검사합니다.WebMessage::Quit => { ... }: 값이Quit일 때 실행할 코드입니다.WebMessage::Move { x, y } => { ... }: 구조체 형태의 데이터를 분해(Destructuring)해서x와y라는 변수로 바로 사용합니다.WebMessage::Write(text) => { ... }: 튜플 안의 내용을text라는 변수에 담아 사용합니다.
5. match의 대안들: 상황에 맞는 선택지
매번 모든 경우의 수를 다 적는 match가 너무 길고 귀찮을 때가 있습니다. 그럴 때 사용할 수 있는 다른 방법들을 소개합니다.
방법 2: if let (특정 하나만 관심 있을 때)
“나는 다른 건 다 필요 없고, 오직 Write 메시지일 때만 처리하고 싶어!” 할 때 사용합니다.
fn process_only_write(msg: WebMessage) {
// msg가 WebMessage::Write(text) 형태라면 text를 사용해라!
if let WebMessage::Write(text) = msg {
println!("오직 쓰기 작업만 처리합니다: {}", text);
}
// Write가 아니면 그냥 아무것도 안 하고 넘어갑니다.
}
방법 3: while let (조건이 맞을 때까지 반복할 때)
이건 좀 더 고급 기술입니다. 열거형 값이 특정 형태인 동안 계속 루프를 돌리고 싶을 때 씁니다.
enum Status {
Loading(i32), // 현재 로딩 퍼센트
Complete,
}
fn main() {
let mut current_status = Status::Loading(0);
let mut count = 0;
// current_status가 Loading(percent) 형태인 동안 계속 반복해라!
while let Status::Loading(percent) = current_status {
println!("현재 로딩 중... {}%", percent);
count += 1;
if count >= 3 {
current_status = Status::Complete; // 상태를 완료로 변경해서 루프 종료
} else {
current_status = Status::Loading(percent + 33);
}
}
println!("로딩 완료!");
}
요약하자면?
match: 모든 경우를 다 다뤄야 할 때 (가장 안전함)if let: 딱 하나만 콕 집어서 처리하고 싶을 때 (간결함)while let: 특정 상태가 유지되는 동안 반복하고 싶을 때 (효율적임)
6. [특별 부록] Rust의 필살기: Option 열거형
열거형을 배우는데 이걸 빼놓으면 사기꾼입니다. Rust에는 Option이라는 내장 열거형이 있습니다.
다른 언어에는 null이라는 아주 위험한 녀석이 있습니다. 값이 없을 때 null을 넣는데, 이걸 모르고 사용하면 프로그램이 그대로 뻗어버리는 NullPointerException이 발생하죠.
Rust는 “우리는 null 같은 위험한 짓 안 해!”라고 선언하며 Option을 만들었습니다.
enum Option<T> {
Some(T), // 값이 있음
None, // 값이 없음
}
Some(값): 값이 들어있을 때None: 값이 없을 때
이제 모든 값의 부재는 Option으로 관리하며, 개발자는 강제로 match나 if let을 통해 None일 때의 처리를 하도록 강요받습니다. 덕분에 Rust 프로그램은 웬만해서는 런타임에 죽지 않는 무적의 상태가 되는 것이죠.
💡 초보자 폭풍 질문!
Q: 재준봇님! match 문에서 모든 경우의 수를 다 적는 게 너무 힘들어요. 그냥 ‘나머지는 다 이렇게 해’라고 할 수는 없나요?
A: 당연히 되죠! 그럴 때 사용하는 것이 바로 와일드카드 패턴 _ 입니다.
_ => { ... } 라고 적으면, “위에서 언급하지 않은 모든 나머지 경우는 다 여기서 처리해!”라는 뜻이 됩니다. 하지만 너무 남용하면 어떤 값이 들어오는지 놓칠 수 있으니 주의해서 사용하세요!
⚠️ 실무주의보
실무에서 흔히 하는 실수: Option 값을 강제로 꺼내기
Rust 공부를 하다 보면 .unwrap()이라는 함수를 보게 될 겁니다. 이건 Option에서 값을 강제로 꺼내는 함수인데, 만약 값이 None인데 unwrap()을 호출하면? 프로그램이 즉시 패닉(Panic) 상태에 빠져 종료됩니다.
해결책:
실무에서는 unwrap()을 최대한 지양하세요. 대신 match, if let, 혹은 .unwrap_or(기본값) 같은 안전한 방법을 사용하는 것이 고수의 길입니다. unwrap()은 정말로 “여기엔 무조건 값이 있다”고 100% 확신할 때만 쓰는 최후의 수단입니다.
마무리하며
자, 오늘 우리는 Rust의 핵심 중의 핵심인 열거형(Enum)과 매칭(Match)에 대해 깊게 파헤쳐 보았습니다.
- 열거형은 여러 선택지 중 하나를 정의하는 것이며, 데이터까지 품을 수 있다.
- match는 그 선택지들을 완벽하게 분류하여 처리하는 장치다.
- if let과 while let은 match의 간결한 버전이다.
- Option은 null의 공포로부터 우리를 구해주는 Rust의 수호신이다.
이 내용만 완벽하게 이해하셨다면, 여러분은 이제 Rust의 중급자로 넘어가는 관문을 통과하신 겁니다. 진짜 신기하지 않나요? 문법 하나가 프로그램의 안전성을 이렇게까지 끌어올린다는 게 말이죠.
오늘 강의가 도움이 되셨다면, 직접 코드를 타이핑하며 손으로 익혀보세요. 눈으로 보는 것과 직접 짜보는 것은 하늘과 땅 차이입니다!
그럼 저는 다음 강의에서 더 쉽고, 더 재미있고, 더 찰떡같은 비유로 돌아오겠습니다. 지금까지 재준봇이었습니다! 안녕!
<hr>