[IT-정보] Rust 변수와 가변성 개념 완벽 정리 – 안전한 코딩을 위한 첫걸음

Rust 변수와 가변성 개념를 설명하는 대표 이미지

Rust 변수와 가변성 한눈에 이해하기

다른 언어로 프로그래밍을 하다가 러스트(Rust)의 세계에 처음 발을 들여놓으면 가장 먼저 마주하는 당혹스러운 순간이 있어요. 분명히 값을 변경하려고 코드를 작성했는데, 컴파일러가 마치 엄격한 선생님처럼 나타나서 “이 변수는 바꿀 수 없어요!”라고 빨간 줄을 그으며 거절하는 상황이죠. 변수에 새로운 값을 넣었을 뿐인데 왜 프로그램이 실행조차 되지 않는지 이해되지 않아 답답함을 느꼈을 거예요.

이런 경험은 러스트를 배우는 입문자라면 누구나 거치는 통과의례와 같아요. 러스트는 우리가 흔히 사용하는 다른 언어들과는 철학부터가 완전히 다르거든요. 대부분의 언어는 변수를 언제든 바꿀 수 있는 ‘그릇’으로 취급하지만, 러스트는 기본적으로 한 번 담으면 바꿀 수 없는 고정된 상자로 간주해요. 이것이 바로 러스트가 자랑하는 메모리 안전성의 핵심 원리인 가변성 제어의 시작이에요.

우리가 왜 이 개념을 반드시 이해해야 할까요? 단순히 문법을 외우기 위해서가 아니에요. 변수의 값이 언제 바뀌고 언제 유지되는지를 명확히 통제함으로써, 프로그램이 예상치 못한 방식으로 데이터를 수정하는 사고를 원천 차단하기 위해서예요. 이는 나중에 멀티스레딩 환경에서 데이터가 꼬이는 심각한 오류를 방지하는 아주 강력한 무기가 된답니다.

오늘 글에서는 러스트를 처음 시작하는 여러분이 변수와 가변성 개념을 완벽하게 정복할 수 있도록 아래 내용들을 차근차근 다룰 예정이에요.

  • 러스트가 기본적으로 불변성을 선택한 이유와 그 이점
  • 가변 변수를 선언하는 올바른 방법과 주의사항
  • 상수와 변수의 결정적인 차이점 분석
  • 섀도잉(Shadowing)이라는 독특한 기술 활용법

사전 준비 — 기본 이해와 체크리스트

본격적으로 코드를 작성하기 전에, 러스트가 변수를 다루는 방식에 담긴 논리적 구조를 먼저 머릿속에 그려두어야 해요. 러스트는 개발자가 실수할 수 있는 여지를 아예 문법 단계에서부터 줄여버리려는 성향이 강해요. 그래서 우리는 변수를 선언할 때 이 데이터가 앞으로 변할 것인지, 아니면 끝까지 유지될 것인지를 명확히 결정해야 하죠.

먼저 가장 중요한 개념인 불변성(Immutability)가변성(Mutability)을 구분해야 해요. 불변성은 변수에 값을 한 번 할당하면 그 이후로는 절대로 수정할 수 없는 상태를 말해요. 반대로 가변성은 필요에 따라 값을 다시 써넣을 수 있는 상태를 의미하죠. 러스트에서는 변수를 만들 때 아무런 표시를 하지 않으면 무조건 불변 상태가 된답니다.

💡 알아두기
러스트의 컴파일러는 매우 똑똑해요. 여러분이 변수를 가변으로 선언하지 않았는데 값을 바꾸려 시도하면, 단순히 에러를 내는 것에 그치지 않고 어떻게 수정하면 좋을지 친절하게 가이드까지 제공해 준답니다.

변수를 설계할 때 어떤 방식을 선택할지는 프로젝트의 성격과 데이터의 흐름에 따라 달라져요. 아래 표를 통해 상황에 맞는 적절한 선택 기준을 확인해 보세요.

구분 방식 추천 상황 장점 단점
불변 변수 (let) 값이 변하지 않는 설정값, 상수 성격의 데이터 데이터 안정성 극대화, 코드 읽기 쉬움 값 변경 시 에러 발생
가변 변수 (let mut) 반복문 카운터, 누적 합계, 상태 변경 데이터 유연한 데이터 조작 가능 의도치 않은 수정 위험 존재
상수 (const) 프로그램 전체에서 공유되는 절대적 고정값 컴파일 타임에 결정되어 매우 빠름 런타임 계산 불가, 타입 명시 필수

단순히 “바꿀 수 있냐 없냐”의 문제를 넘어, 어떤 데이터가 안전하게 보호되어야 하는가를 고민하는 것이 러스트 개발자의 올바른 태도예요. 처음에는 불변성을 기본으로 두고, 정말로 값이 변해야만 하는 순간에만 가변성을 부여하는 습관을 들이는 것이 가장 좋습니다.

핵심 본문 — 단계별 실행

이제 이론을 넘어 실제 러스트 코드가 어떻게 작동하는지 단계별로 깊이 있게 파헤쳐 볼게요. 이 과정을 따라오다 보면 러스트의 변수 관리 철학이 왜 그렇게 강력한지 몸소 느끼게 될 거예요.

STEP 1. 불변성의 원칙과 기본 선언 이해하기

러스트에서 변수를 만드는 가장 기본적인 방법은 let 키워드를 사용하는 것이에요. 흥미로운 점은 let x = 5;라고 선언하는 순간, x는 절대로 바꿀 수 없는 존재가 된다는 사실이에요.

이게 왜 중요할까요? 만약 우리가 어떤 함수에 데이터를 넘겨줄 때, 그 데이터가 함수 내부에서 멋대로 바뀌지 않을 것이라는 보장이 있다면 얼마나 안심이 될까요? 러스트는 이 보장을 언어 차원에서 강제해요. 프로그래머가 실수로 값을 수정하려고 하면 컴파일러가 즉시 차단하죠. 이는 코드의 예측 가능성을 높이고, 복잡한 로직에서도 데이터의 흐름을 추적하기 쉽게 만들어줘요.

💡 알아두기
불변 변수는 메모리 최적화에도 도움을 줘요. 컴파일러가 이 값이 변하지 않는다는 것을 알면, 더 효율적인 기계어를 생성할 수 있거든요.

STEP 2. 가변성(mut)을 통한 유연한 데이터 제어

하지만 세상의 모든 데이터가 고정되어 있지는 않죠. 사용자의 점수를 올리거나, 반복문에서 숫자를 하나씩 늘려가는 작업은 반드시 필요해요. 이때 사용하는 것이 바로 mut 키워드예요. let mut score = 0;처럼 선언하면, 이제 score는 자유롭게 값을 바꿀 수 있는 상태가 돼요.

가변성을 사용할 때는 반드시 최소한의 범위에만 적용해야 한다는 규칙을 기억하세요. 모든 변수를 가변으로 만들어 버리면 러스트를 사용하는 의미가 퇴색돼요. 값이 변해야 하는 지점을 정확히 파악하고, 그 부분에만 mut를 붙이는 것이 고수의 방법이에요. 이렇게 하면 코드를 읽는 동료 개발자도 “아, 이 변수는 중간에 값이 바뀌는구나”라고 즉시 인지할 수 있어요.

STEP 3. 상수(const)와 변수(let)의 명확한 구분

초보자들이 가장 많이 헷갈려 하는 지점이 바로 constlet의 차이에요. 둘 다 값을 고정한다는 점에서는 비슷해 보이지만, 작동 방식은 완전히 달라요.

  • 상수(const): 컴파일 단계에서 이미 값이 결정되어 있어야 해요. 즉, 프로그램이 실행되는 도중에 계산해서 값을 정할 수 없어요. 또한 반드시 데이터 타입을 명시해야 하며, 프로그램 전체에서 어디서든 사용할 수 있는 전역적인 성격이 강해요.
  • 불변 변수(let): 프로그램이 실행되는 도중에(Runtime) 계산된 결과를 담을 수 있어요. 예를 들어 사용자가 입력한 숫자를 변수에 저장하는 것은 상수가 아닌 불변 변수로 해야 해요.

정리하자면, 수학 공식의 $\pi$ 값처럼 프로그램이 돌아가는 내내 절대 변하지 않는 기준값은 const를 쓰고, 실행 중에 결정되는 값은 let을 사용하는 것이 올바른 설계예요.

STEP 4. 섀도잉(Shadowing) 기술 활용하기

러스트에는 아주 독특하고 강력한 기능이 하나 있는데, 바로 섀도잉(Shadowing)이에요. 이는 동일한 이름의 변수를 다시 선언함으로써, 이전 변수를 가리고 새로운 변수를 만드는 기술을 말해요.

예를 들어, 사용자로부터 입력받은 문자열 데이터를 숫자로 바꾸고 싶을 때가 있어요. 이때 가변 변수(let mut)를 사용해 타입을 바꾸는 것은 불가능해요. 하지만 섀도잉을 사용하면 가능하죠. let spaces = " ";라고 선언한 뒤, 다시 let spaces = spaces.len();라고 선언할 수 있어요. 이것은 기존 변수를 수정한 것이 아니라, 같은 이름의 새로운 변수를 만든 것이에요.

이 기술을 사용하면 데이터의 타입을 변환하면서도 변수 이름을 계속 일관되게 유지할 수 있어 코드가 훨씬 깔끔해져요. 다만, 섀도잉을 남용하면 어떤 값이 어디서 왔는지 헷갈릴 수 있으니 주의가 필요해요.

STEP 5. 실전 적용 시나리오: 점수 관리 시스템

이 모든 개념을 종합해서 간단한 게임 점수 관리 로직을 상상해 볼게요. 아래 흐름은 러스트의 변수 원칙을 어떻게 적용하는지 잘 보여줍니다.

  1. 상수 선언: 게임의 최고 점수를 const MAX_SCORE: i32 = 100;로 정해요.
  2. 가변 변수 선언: 플레이어의 현재 점수를 let mut current_score = 0;로 시작해요.
  3. 데이터 변경: 아이템을 먹으면 current_score += 10;처럼 값을 업데이트해요.
  4. 섀도잉 활용: 점수를 문자열로 출력하기 위해 let score_str = current_score.to_string();를 통해 타입을 변환하여 관리해요.

이렇게 각 변수의 성격에 맞춰 적절한 도구를 선택하는 것이 러스트 프로그래밍의 핵심이에요.

자주 하는 실수와 해결법

러스트를 처음 다룰 때 누구나 한 번쯤은 겪게 되는 시행착오들이 있어요. 에러 메시지를 보고 당황하기 전에, 미리 아래 사례들을 체크해 보세요.

  • 불변 변수의 값을 수정하려고 함 → 왜 발생하는가: let으로 선언된 변수는 기본적으로 수정이 불가능해요. → ✅ 해결법: 변수 선언 시 mut 키워드를 추가하세요.
  • 상수(const)에 실행 중 계산된 값을 넣으려 함 → 왜 발생하는가: 상수는 컴파일 타임에 값이 결정되어야 하기 때문이에요. → ✅ 해결법: 실행 중에 결정되는 값은 let을 사용하세요.
  • 섀도잉을 쓰지 않고 가변 변수의 타입만 바꾸려 함 → 왜 발생하는가: 러스트는 한 번 정해진 변수의 타입을 중간에 바꿀 수 없어요. → ✅ 해결법: let을 다시 사용하여 섀도잉을 적용하세요.
  • 가변 변수를 너무 남발하여 코드 흐름을 놓침 → 왜 발생하는가: 어디서 값이 바뀌는지 추적하기 어려워져요. → ✅ 해결법: 기본은 불변으로 두고, 반드시 필요한 순간에만 mut를 사용하세요.
  • 스코프(Scope) 밖에서 변수에 접근하려고 함 → 왜 발생하는가: 변수는 중괄호 ⚠️ 주의
    가변 변수를 사용할 때는 해당 변수가 공유되는 위치를 항상 확인하세요. 여러 곳에서 동시에 값을 바꾸려 하면 러스트의 소유권 시스템과 충돌하여 더 복잡한 에러를 만날 수 있어요.

자주 묻는 질문

Q. 왜 러스트는 기본을 불변으로 만들었나요?

데이터가 변하지 않는다는 확신은 프로그래머에게 엄청난 안전성을 줘요. 특히 여러 작업이 동시에 일어나는 환경에서 데이터가 예기치 않게 바뀌는 것을 막아주기 때문에, 더 안전하고 빠른 코드를 짤 수 있게 도와준답니다.

Q. 상수는 타입 명시가 왜 꼭 필요한가요?

상수는 컴파일러가 코드를 읽기 전에 이미 그 값을 알고 있어야 해요. 타입을 명시해주어야 컴파일러가 메모리 공간을 어떻게 할당할지 명확하게 판단할 수 있기 때문이에요.

Q. 섀도잉을 쓰면 메모리가 더 많이 소비되나요?

아니요, 그렇지 않아요. 섀도잉은 기존 변수를 수정한 게 아니라 새로운 변수를 만드는 것이지만, 이전 변수의 메모리 사용은 스코프에 따라 적절히 관리되므로 효율적이에요.

Q. 가변 변수를 많이 쓰면 성능이 떨어지나요?
직접적인 성능 저하보다는 코드의 가독성과 유지보수성이 떨어지는 것이 더 큰 문제입니다. 러스트의 철학을 따르려면 가변성을 최소화하는 습관이 중요해요.

Q. let mut와 const의 차이를 한 문장으로 요약한다면?
let mut는 실행 중에 바꿀 수 있는 유연한 그릇이고, const는 프로그램 시작부터 끝까지 변하지 않는 단단한 바위와 같아요.

핵심 요약과 다음 단계

러스트의 변수와 가변성 개념은 처음엔 까다롭게 느껴질 수 있지만, 익숙해지면 이보다 든든한 조력자가 없어요. 오늘 배운 내용을 바탕으로 안전한 코딩의 기초를 다져보세요.

✅ 핵심 요약

  • 기본적으로 모든 변수는 불변(Immutable) 상태로 선언돼요.
  • 값을 바꾸려면 반드시 mut 키워드를 사용해야 해요.
  • 상수(const)는 컴파일 타임에 결정되며 타입 명시가 필수예요.
  • 섀도잉을 활용하면 동일한 이름으로 타입을 안전하게 변환할 수 있어요.
  • 가변성은 꼭 필요한 곳에만 최소한으로 사용하는 것이 러스트다운 방식이에요.

오늘 공부를 마쳤다면 바로 다음 단계를 실행해 보세요. 이론만 아는 것과 직접 에러를 마주하며 고치는 것은 하늘과 땅 차이랍니다.

  • 오늘 할 일: 러스트 컴파일러를 켜고, 의도적으로 불변 변수의 값을 바꿔보며 에러 메시지 읽어보기
  • 이번 주 할 일: 간단한 계산기 프로그램을 만들며 let, let mut, const를 적재적소에 사용해 보기
  • 실행 직전 할 일: 섀도잉을 이용해 문자열을 숫자로 바꾸는 연습 코드를 작성해 보기

어렵게 느껴졌던 러스트 변수와 가변성, 오늘 확실히 정리해 보셨나요? 이 개념이 탄탄해야 앞으로 배울 소유권(Ownership)과 빌림(Borrowing)이라는 거대한 산도 즐겁게 넘을 수 있어요. 여러분의 러스트 여정을 응원해요!

더 깊이 있는 학습을 원하신다면, 러스트 입문 학습 가이드러스트 소유권 개념 정리 글도 함께 읽어보시는 것을 추천해 드려요.

댓글 남기기