
왜 Rust 면접에서 변수와 가변성을 물어볼까요
코딩 테스트를 치르거나 Rust 프로젝트를 시작했을 때, 분명히 값을 바꾸려고 했는데 컴파일러가 빨간 줄을 그으며 화를 내는 경험을 해보셨나요? “cannot assign twice to immutable variable”이라는 메시지를 마주하면 당혹스러울 수밖에 없어요. 대부분의 프로그래밍 언어에서는 변수를 선언하면 언제든 값을 바꿀 수 있지만, Rust는 태생부터 다르게 설계되었기 때문이에요.
면접관들이 이 주제를 집요하게 묻는 이유는 단순합니다. 여러분이 Rust의 핵심 철학인 메모리 안전성과 데이터 경합 방지를 얼마나 깊이 이해하고 있는지 확인하고 싶어 해요. 단순히 문법을 외웠는지가 아니라, 왜 Rust가 불변성을 기본값으로 선택했는지 그 근본적인 이유를 알고 있는지가 실력을 가르는 기준이 됩니다.
이 글을 끝까지 읽고 나면 다음의 내용들을 확실히 내 것으로 만들 수 있어요.
- 불변성과 가변성의 기술적 차이와 메모리 관점의 이해
- 변수 섀도잉(Shadowing)과 가변성(Mutability)의 결정적 차이점
- 면접에서 바로 써먹을 수 있는 실전 답변과 코드 예제
- 자주 발생하는 컴파일 에러 해결 방법
지금부터 Rust 개발자로 도약하기 위한 가장 중요한 첫 단추를 함께 끼워보아요.
기본 개념 이해를 위한 사전 체크리스트
Rust의 변수 시스템을 이해하기 위해서는 먼저 불변성(Immutability)이라는 개념과 친해져야 해요. Rust는 모든 변수를 선언할 때 기본적으로 ‘한 번 정하면 바꿀 수 없는 상태’로 만듭니다. 이는 의도치 않은 값의 변경을 막아 프로그램의 예측 가능성을 높여주는 강력한 도구가 돼요.
본격적인 학습에 앞서, 여러분이 어떤 상황에서 어떤 키워드를 사용해야 할지 판단 기준을 먼저 세워두는 것이 좋습니다. 무작정 코드를 짜기보다 아래의 비교 표를 통해 기준을 명확히 잡아보세요.
| 구분 요소 | 불변 변수 (let) | 가변 변수 (let mut) |
|---|---|---|
| 기본 선언 방식 | let x = 5; | let mut x = 5; |
| 값의 재할당 | 불가능 (컴파일 에러) | 가능 |
| 데이터 타입 변경 | 불가능 (섀도잉 필요) | 불가능 |
| 주요 용도 | 상수성 유지, 안전한 데이터 전달 | 카운터, 버퍼, 상태 업데이트 |
Rust에서 가변성을 선언한다는 것은 단순히 값을 바꿀 수 있다는 뜻을 넘어, 해당 데이터에 대한 가변 참조(Mutable Reference)를 생성할 권한을 얻는다는 의미와도 연결됩니다. 이는 곧 메모리 관리 시스템인 소유권(Ownership) 개념과 밀접하게 연관되어 있어요.
이 개념들을 머릿속에 넣어두었다면, 이제 본격적으로 실무에서 어떻게 적용되고 면접에서는 어떤 식으로 파고드는지 심층적으로 들어가 볼까요?
Rust 변수와 가변성 마스터하기
이제부터는 단순한 문법을 넘어 Rust의 철학이 담긴 핵심 메커니즘을 단계별로 파헤쳐 보겠습니다. 이 과정은 면접에서 기술적 깊이를 증명하는 데 결정적인 역할을 할 거예요.
STEP 1. 불변성의 철학과 안전한 설계
왜 Rust는 처음부터 가변성을 허용하지 않을까요? 그 답은 데이터 경합(Data Race)을 원천 차단하기 위해서예요. 여러 스레드가 동시에 하나의 데이터를 수정하려고 할 때 발생하는 문제는 디버깅이 불가능할 정도로 치명적입니다.
불변 변수를 사용하면 코드를 읽는 사람이나 컴파일러가 “이 값은 절대 변하지 않는다”라고 확신할 수 있어요. 이는 함수의 부수 효과(Side Effect)를 줄여주고, 코드의 논리적 흐름을 명확하게 만듭니다. 즉, 불변성은 단순히 제약 사항이 아니라, 소프트웨어의 신뢰성을 높이는 설계 원칙인 셈이에요. 면접에서 이 질문을 받는다면, “예측 가능한 코드 작성과 멀티스레드 환경에서의 안전성을 확보하기 위함입니다”라고 답변하는 것이 베스트예요.
STEP 2. 가변성(Mutability)의 적절한 사용 시점
물론 모든 것을 불변으로 만들 수는 없어요. 프로그램의 상태를 추적해야 하는 카운터, 사용자 입력을 담는 버퍼, 혹은 반복문 내에서 누적되는 합계 값 등은 반드시 가변적이어야 합니다. 이때 우리는 let mut 키워드를 사용합니다.
가변성을 사용할 때는 항상 경계심을 가져야 해요. 가변 변수가 많아질수록 데이터가 어디서 어떻게 변했는지 추적하기 어려워지기 때문이죠. 따라서 가능한 한 좁은 범위(Scope) 내에서만 가변성을 유지하고, 값이 변경된 후에는 다시 불변 상태로 관리할 수 있는 구조를 고민하는 습관이 필요합니다.
STEP 3. 변수 섀도잉(Shadowing) vs 가변성(Mutability)
이 부분이 면접의 단골 질문이자 가장 많은 입문자가 헷갈려 하는 지점이에요. 두 개념은 겉보기에 비슷해 보이지만, 메모리와 타입 관점에서 완전히 다른 방식으로 동작합니다.
가변성(Mutability)은 기존에 선언된 메모리 공간에 들어있는 값만 바꾸는 것을 의미해요. 즉, 데이터의 타입은 절대로 바꿀 수 없습니다. 반면, 섀도잉(Shadowing)은 기존 변수와 동일한 이름을 가진 새로운 변수를 선언하는 것이에요. 이전 변수는 가려지고(Shadowed), 새로운 변수가 그 자리를 대신합니다.
섀도잉의 가장 큰 장점은 데이터 타입의 변환이 가능하다는 점이에요. 예를 들어, 사용자로부터 입력받은 문자열 데이터를 숫자로 변환할 때, 가변성을 쓰면 타입을 바꿀 수 없지만 섀도잉을 쓰면 자연스럽게 변환된 타입의 새 변수를 만들 수 있어요.
이 차이를 명확히 설명할 수 있다면, 여러분은 이미 Rust의 변수 관리 메커니즘을 깊이 이해하고 있는 것입니다.
STEP 4. 메모리 안전성과 소유권의 연결고리
Rust의 변수는 단순히 값을 담는 그릇이 아니라, 메모리의 소유권을 관리하는 주체예요. 가변 변수를 선언하면 컴파일러는 해당 데이터에 대해 ‘쓰기 권한’을 부여합니다. 하지만 Rust의 대원칙 중 하나는 “동시에 여러 곳에서 가변 참조를 가질 수 없다”는 것이에요.
만약 여러분이 어떤 데이터를 가변적으로 수정하고 있는 도중에, 다른 곳에서 그 데이터를 읽으려고 시도한다면 어떻게 될까요? Rust 컴파일러는 즉시 에러를 발생시켜 프로그램이 실행되기도 전에 문제를 잡아냅니다. 이는 런타임에 발생할 수 있는 메모리 오염이나 크래시를 컴파일 타임에 방지하는 Rust만의 강력한 방어 기제입니다.
STEP 5. 실전 코드로 익히는 차이점 시나리오
이론만으로는 부족하니, 실제 코드가 어떻게 동작하는지 시나리오를 통해 살펴보아요. 아래 예시는 면접에서 화이트보드에 직접 그려보라고 할 법한 핵심 패턴입니다.
먼저, 타입 변환이 필요한 상황에서의 섀도잉 시나리오예요. let spaces = " ";
let spaces = spaces.trim(); // 섀도잉을 통해 타입(문자열 슬라이스)과 값이 변경됨
이 경우, 기존의 spaces가 가진 타입과 완전히 다른 형태의 데이터를 다룰 수 있게 되죠.
다음은 단순한 값의 증가가 필요한 카운터 시나리오예요. let mut count = 0;
count += 1; // 가변성을 통해 기존 메모리의 값을 직접 수정함
이 시나리오에서는 새로운 변수를 만들 필요 없이, 이미 확보된 메모리 공간을 효율적으로 재사용합니다.
이처럼 상황에 따라 섀도잉을 쓸지, 가변성을 쓸지를 결정하는 능력이 바로 숙련된 Rust 개발자의 역량입니다.
자주 하는 실수와 해결법 및 FAQ
자주 하는 실수와 해결법
Rust를 배우다 보면 컴파일러와 싸우는 시간이 길어질 수 있어요. 하지만 에러 메시지를 잘 읽으면 해결책은 생각보다 명확합니다. 자주 마주치는 실수 5가지를 정리해 드릴게요.
- ❌ 불변 변수에 값을 다시 할당하려는 경우
왜 발생하는가:let으로 선언된 변수는 기본적으로 읽기 전용입니다.
✅ 해결법: 값을 변경해야 한다면 선언 시let mut를 사용하세요. - ❌ 가변 변수의 타입을 중간에 바꾸려는 경우
왜 발생하는가:let mut는 값의 변경은 허용하지만, 메모리 레이아웃이 바뀌는 타입 변경은 허용하지 않습니다.
✅ 해결법: 타입 변환이 필요하다면 섀도잉(Shadowing)을 활용하세요. - ❌ 가변 참조를 가진 상태에서 다른 참조를 생성하려는 경우
왜 발생하는가: Rust의 대원칙인 “하나의 가변 참조 혹은 여러 개의 불변 참조” 규칙을 위반했기 때문입니다.
✅ 해결법: 가변 참조의 생명주기(Scope)를 줄이거나, 참조를 해제한 뒤 새로운 참조를 만드세요. - ❌ 섀도잉을 너무 남발하여 코드 가독성을 해치는 경우
왜 발생하는가: 이름은 같은데 성격이 전혀 다른 변수들이 섞여 있으면 협업 시 혼란을 줍니다.
✅ 해결법: 섀도잉은 꼭 필요한 타입 변환 시에만 제한적으로 사용하고, 의미가 완전히 달라진다면 새로운 이름을 부여하세요. - ❌ 함수 인자로 가변 변수를 넘겼는데 함수 내부에서 수정이 안 되는 경우
왜 발생하는가: 변수 자체는 가변이어도, 함수로 전달될 때의 참조 방식이 불변 참조(&T)일 수 있습니다.
✅ 해결법: 함수 정의에서 가변 참조(&mut T)를 받도록 수정하세요.
자주 묻는 질문
Q. Rust에서 왜 모든 변수를 처음부터 가변으로 만들지 않나요?
A. 기본값이 불변이면 개발자가 의도적으로 가변성을 선언해야만 합니다. 이는 실수를 방지하고, 코드를 읽는 사람이 해당 데이터의 변화 가능성을 즉시 파악할 수 있게 하여 프로그램의 안정성을 비약적으로 높여줍니다.
Q. 섀도잉과 가변성의 결정적인 차이점 한 가지만 꼽는다면 무엇인가요?
A. 데이터 타입의 변경 가능 여부입니다. 가변성은 기존 타입의 값만 바꿀 수 있지만, 섀도잉은 새로운 타입을 가진 새로운 변수를 만드는 것과 같습니다.
Q. 가변 변수를 사용하는 것이 성능에 불리한가요?
A. 그렇지 않습니다. 오히려 섀도잉을 통해 계속 새로운 변수를 만드는 것보다, 기존의 가변 메모리를 재사용하는 것이 메모리 할당 측면에서 더 효율적일 때가 많습니다. 용도에 맞는 선택이 중요합니다.
Q. 면접에서 ‘Rust의 메모리 안전성’에 대해 질문받으면 어떻게 답해야 하나요?
A. “Rust는 소유권(Ownership)과 빌림(Borrowing) 규칙을 통해 컴파일 타임에 데이터 경합과 유효하지 않은 메모리 접근을 방지합니다. 특히 변수의 가변성을 엄격히 제어함으로써 멀티스레드 환경에서도 안전한 코드를 작성할 수 있게 돕습니다”라고 답변하세요.
마무리 정리와 다음 단계
오늘 우리는 Rust의 근간을 이루는 변수와 가변성, 그리고 면접에서 핵심이 되는 섀도잉 개념까지 깊이 있게 살펴보았습니다. 이 개념들은 단순히 문법을 넘어 Rust가 지향하는 안전한 프로그래밍의 철학을 담고 있습니다.
- 모든 Rust 변수는 기본적으로 불변(Immutable)입니다.
- 값을 바꾸려면 반드시
let mut키워드를 사용해야 합니다. - 섀도잉은 변수 이름을 재사용하여 타입을 바꿀 수 있는 기법입니다.
- 가변성은 기존 메모리 공간의 값을 바꾸고, 섀도잉은 새로운 변수를 생성합니다.
- 가변성 제어는 데이터 경합을 방지하는 Rust 안전성의 핵심입니다.
오늘 배운 내용을 잊지 않으려면 지금 바로 여러분의 에디터를 켜고 직접 코드를 작성해 보세요. 컴파일 에러가 발생했을 때 오늘 배운 개념을 떠올리며 해결해 보는 과정이 진짜 실력이 됩니다.
🚀 이번 주 실행 계획
let과let mut를 섞어 간단한 계산기 프로그램 만들기- 섀도잉을 활용하여 문자열 데이터를 숫자로 변환하는 연습하기
- 가변 참조(&&mut)를 사용하여 함수 밖의 변수 값 변경해 보기
오늘 내용이 도움이 되셨나요? Rust의 세계는 아직 넓고 배울 것이 많습니다. 다음 단계로 넘어가기 전, Rust 소유권(Ownership)과 빌림(Borrowing) 완벽 가이드를 먼저 읽어보시는 것을 강력히 추천드려요. 이 주제를 이해해야 비로소 Rust의 진정한 마법을 경험하실 수 있습니다!