
Rust 데이터 타입 빠른 시작을 위한 완벽 가이드
Rust 컴파일러를 처음 마주했을 때, 빨간색 에러 메시지가 화면을 가득 채우는 경험을 해보셨나요? 분명히 숫자를 넣었는데 왜 타입이 맞지 않는다는 경고가 뜨는지, 왜 배열의 크기를 마음대로 바꿀 수 없는지 당황스러울 때가 많아요. 이러한 문제는 대부분 Rust의 엄격한 타입 시스템을 아직 충분히 이해하지 못했기 때문에 발생해요.
Rust는 다른 언어보다 훨씬 깐깐하게 데이터를 관리해요. 하지만 이 깐깐함이야말로 메모리 안전성과 압도적인 성능을 보장하는 Rust만의 강력한 무기예요. 데이터가 메모리의 어느 위치에 어떻게 저장되는지, 그 크기가 얼마인지를 명확히 알아야만 컴파일러와 싸우지 않고 협력할 수 있어요.
이 글을 끝까지 읽고 나면, 단순히 코드를 타이핑하는 수준을 넘어 데이터가 메모리에서 어떻게 움직이는지 이해하는 눈을 갖게 될 거예요. 복잡한 문법 이론 대신, 실무에서 즉시 활용할 수 있는 핵심 원리와 예제 위주로 차근차근 설명해 드릴게요.
- 데이터의 최소 단위인 스칼라(Scalar) 타입의 모든 종류
- 여러 데이터를 묶어 관리하는 복합(Compound) 타입의 활용법
- 메모리 효율을 높이는 타입 선택 기준과 실전 예제
- 입문자가 가장 많이 저지르는 타입 관련 실수와 해결책
사전 준비 — 데이터 타입의 기본 개념 이해하기
Rust의 데이터 타입을 배우기 전에, 우리가 다룰 데이터가 크게 두 가지 줄기로 나뉜다는 사실을 먼저 머릿속에 그려두어야 해요. 하나는 단 하나의 값만을 담는 스칼라 타입이고, 다른 하나는 여러 값을 하나로 묶어 관리하는 복합 타입이에요.
이 차이를 이해하는 것이 중요한 이유는 메모리 관리 방식이 완전히 다르기 때문이에요. 스칼라 타입은 크기가 고정되어 있어 메모리의 스택(Stack) 영역에 매우 빠르게 저장되지만, 복합 타입은 구조에 따라 데이터를 다루는 방식이 훨씬 복잡해질 수 있어요. 따라서 무작정 코드를 작성하기보다, 내가 쓰려는 데이터가 어떤 성격인지 판단하는 기준을 먼저 세워야 해요.
타입 분류와 선택 기준 비교
어떤 타입을 사용할지 고민될 때는 아래 표를 참고해서 결정해 보세요. 상황에 맞는 타입을 선택하는 것만으로도 코드의 가독성과 성능을 동시에 잡을 수 있어요.
| 구분 | 타입 종류 | 주요 용도 | 특징 |
|---|---|---|---|
| 스칼라 | 정수(Integer) | 카운트, 인덱스, ID | 부호 유무와 크기 선택 가능 |
| 스칼라 | 실수(Float) | 측정값, 확률, 좌표 | 소수점 아래 정밀도 결정 |
| 스칼라 | 불리언(Boolean) | 조건문, 상태 체크 | true 또는 false 값만 보유 |
| 복합 | 튜플(Tuple) | 서로 다른 타입의 묶음 | 데이터의 개수가 고정됨 |
| 복합 | 배열(Array) | 동일 타입의 목록 | 길이를 변경할 수 없음 |
위의 기준을 바탕으로, 우리가 다음에 수행할 실습에서는 각 타입이 실제로 코드에서 어떻게 선언되고, 메모리에서 어떤 크기를 차지하는지 구체적으로 살펴볼 예정이에요.
타입을 선택할 때 단순히 편한 것을 고르기보다, 다룰 데이터의 예상 범위를 먼저 고려하세요. 너무 작은 정수 타입을 선택하면 데이터가 넘쳐버리는(Overflow) 사고가 발생할 수 있어요.
핵심 본문 — 실전에서 사용하는 Rust 데이터 타입 완벽 마스터
이제 본격적으로 Rust의 데이터 타입을 하나씩 뜯어볼게요. 이론적인 설명보다는 실제 개발할 때 어떤 상황에서 어떤 타입을 꺼내 써야 하는지에 집중해서 설명해 드릴게요.
STEP 1. 단 하나의 값을 다루는 스칼라 타입(Scalar Types)
스칼라 타입은 가장 기본적인 구성 요소예요. 크게 정수, 실수, 불리언, 그리고 문자로 나눌 수 있어요.
1) 정수 타입: 상황에 맞는 크기 선택하기
정수는 양수와 음수를 모두 포함할 수 있는 부호 있는 정수(signed)와, 0과 양수만을 다루는 부호 없는 정수(unsigned)로 구분돼요. Rust에서는 이를 `i`(signed)와 `u`(unsigned)라는 접두사로 구분해요.
- i8, u8: 아주 작은 숫자나 바이트 데이터를 다룰 때 사용해요. (u8은 0~255까지 표현 가능)
- i32, u32: 가장 일반적인 정수 타입이에요. 특별한 이유가 없다면 기본값으로 많이 사용해요.
- i64, u64: 매우 큰 숫자나 전 세계 인구수, 금융 데이터 등을 다룰 때 적합해요.
- isize, usize: 현재 시스템의 아키텍처(32비트 또는 64비트)에 따라 크기가 결정돼요. 특히 배열의 인덱스나 컬렉션의 길이를 나타낼 때는 반드시 usize를 사용해야 해요.
정수 타입을 선택할 때는 다룰 숫자의 최댓값을 미리 계산해 보세요. 만약 255를 넘을 가능성이 있는 `u8`을 사용 중이라면, 프로그램이 갑자기 잘못된 값을 출력하는 대참사가 일어날 수 있어요.
2) 실수 타입: 정밀도의 미학
소수점이 포함된 숫자는 두 가지 타입만 기억하면 돼요. `f32`와 `f64`예요. `f32`는 단정밀도, `f64`는 배정밀도라고 불러요. 현대적인 컴퓨터 환경에서는 정밀도가 더 높은 f64가 기본값으로 권장돼요. 과학적 계산이나 정교한 그래픽 작업을 한다면 `f64`를 사용해 오차를 최소화하는 것이 좋아요.
3) 불리언과 문자 타입
불리언(`bool`)은 `true`와 `false` 두 가지 값만 가져요. 조건문의 핵심이죠. 문자 타입(`char`)은 다른 언어와 조금 달라요. Rust의 `char`는 단순히 ASCII 문자가 아니라 4바이트 크기의 유니코드(Unicode) 문자예요. 따라서 한글, 이모지(Emoji), 특수 기호도 하나의 `char`로 안전하게 다룰 수 있어요.
STEP 2. 여러 데이터를 묶는 복합 타입(Compound Types)
단일 값이 아니라 여러 데이터를 논리적으로 묶어야 할 때 복합 타입을 사용해요. 여기에는 튜플과 배열이 있어요.
1) 튜플(Tuple): 서로 다른 형제의 만남
튜플은 서로 다른 타입의 값들을 하나로 묶어주는 아주 유용한 도구예요. 예를 들어, 어떤 함수의 결과로 좌표값 (x, y)를 반환하거나, 이름과 나이를 한꺼번에 전달하고 싶을 때 유용해요. 튜플은 생성할 때 정해진 크기를 절대 바꿀 수 없다는 특징이 있어요.
let person: (&str, i32) = ("Alice", 30); 와 같이 선언하며, 값을 꺼낼 때는 점(.) 연산자를 사용해 각 위치에 접근해요.
2) 배열(Array): 같은 형제의 집합
배열은 튜플과 달리 모든 요소의 타입이 반드시 같아야 해요. 정수 배열에는 정수만, 문자열 배열에는 문자열만 들어갈 수 있어요. 배열의 가장 큰 특징은 길이가 고정되어 있다는 점이에요. 처음에 크기를 5로 정했다면, 나중에 6번째 요소를 추가하는 것은 불가능해요. 이는 메모리 예측 가능성을 높여 성능 최적화에 큰 도움을 돼요.
STEP 3. 실무 적용 시나리오: 데이터 타입 활용하기
실제 코드를 작성할 때 어떻게 이 타입들을 조합하는지 예시를 통해 살펴볼까요? 아래는 간단한 사용자 정보를 관리하는 시나리오예요.
// 1. 스칼라 타입 활용
let user_id: u64 = 1029384756;
let user_score: f64 = 95.5;
let is_active: bool = true;
// 2. 튜플을 이용한 좌표 정보 묶기
let location: (f64, f64) = (37.5665, 126.9780);
// 3. 배열을 이용한 최근 점수 기록
let recent_scores: [i32; 5] = [90, 85, 100, 78, 92];
위 예시에서 `user_id`는 0 이상의 큰 숫자이므로 `u64`를 사용했고, `location`은 정밀한 위경도 좌표를 위해 `f64` 튜플로 묶었어요. 점수 기록은 같은 종류의 데이터이므로 `i32` 배열로 관리하는 것이 가장 효율적이에요.
STEP 4. 주의해야 할 메모리 구조: 스택과 힙
이 모든 타입이 어디에 저장되는지 아는 것이 고수의 지름길이에요. 스칼라 타입과 고정 크기 복합 타입(튜플, 배열)은 대부분 스택(Stack)이라는 아주 빠른 메모리 공간에 저장돼요. 반면, 나중에 배울 벡터(Vector)나 문자열(String)처럼 크기가 변하는 데이터는 힙(Heap)이라는 공간을 사용해요. 스칼라 타입의 크기를 정확히 아는 것은 스택을 얼마나 효율적으로 사용할 수 있는지를 결정하는 핵심 요소예요.
자주 하는 실수와 해결법
Rust 학습 과정에서 입문자들이 가장 흔하게 겪는 시행착오들을 모아봤어요. 이 패턴들만 익혀두어도 에러 해결 시간이 절반으로 줄어들 거예요.
- ❌ 정수 오버플로우(Overflow) 발생
왜 발생하는가: `u8` 타입에 255보다 큰 값을 넣으려고 할 때 발생해요.
✅ 해결법: 다룰 데이터의 범위를 확인하고 `u16`, `u32` 등 더 큰 타입으로 변경하세요. - ❌ 타입 불일치(Type Mismatch) 에러
왜 발생하는가: `i32` 타입 변수와 `u32` 타입 변수를 더하려고 하면 Rust는 이를 허용하지 않아요.
✅ 해결법: `as` 키워드를 사용해 타입을 명시적으로 변환하거나(`x as u32`), 애초에 처음부터 타입을 통일하세요. - ❌ 배열 인덱스 타입 오류
왜 발생하는가: 배열의 인덱스로 `i32`를 넣으면 컴파일러가 거절해요.
✅ 해결법: 인덱스 자리에는 반드시 usize 타입을 사용해야 해요. - ❌ 문자열과 문자의 혼동
왜 발생하는가: 단일 문자 `char`를 문자열 `&str`처럼 다루려 할 때 발생해요.
✅ 해결법: 문자는 작은따옴표(`’a’`)를, 문자열은 큰따옴표(`”abc”`)를 사용한다는 규칙을 지키세요. - ❌ 튜플 요소 접근 실수
왜 발생하는가: 튜플의 인덱스는 0부터 시작하는데, 1부터 시작하는 실수를 자주 해요.
✅ 해결법: 프로그래밍의 기본인 ‘Zero-based indexing’을 항상 기억하세요.
자주 묻는 질문
Q. 정수 타입에서 i32를 기본으로 쓰는 특별한 이유가 있나요?
A. 특별한 기술적 이유보다는, 컴퓨터 아키텍처에서 가장 효율적으로 처리할 수 있는 크기이면서 대부분의 일상적인 숫자를 충분히 담을 수 있는 균형 잡힌 타입이기 때문이에요. 굳이 큰 타입을 써서 메모리를 낭비할 필요가 없다면 `i32`가 가장 무난해요.
Q. 배열과 벡터(Vector)의 차이점이 무엇인가요?
A. 가장 큰 차이는 크기의 고정 여부예요. 배열은 한 번 정하면 크기를 바꿀 수 없지만, 벡터는 실행 중에 요소를 추가하거나 삭제하며 크기를 자유롭게 조절할 수 있어요.
Q. 실수형(f32, f64) 계산을 하면 왜 아주 미세한 오차가 생기나요?
A. 컴퓨터는 숫자를 2진수로 저장하기 때문에, 0.1과 같은 10진수 소수를 무한히 정확하게 표현하지 못하는 경우가 있어요. 금융 계산처럼 아주 정밀한 데이터가 필요하다면 정수형을 활용해 최소 단위를 관리하는 방식을 권장해요.
Q. usize는 왜 꼭 인덱스로만 써야 하나요?
Q. Rust의 char는 왜 4바이트나 차지하나요?
A. `char`가 4바이트인 이유는 전 세계의 모든 언어와 이모지를 하나의 타입으로 완벽하게 지원하기 위해서예요. `usize`는 메모리 주소를 다루는 데 최적화된 타입이라 시스템의 메모리 크기와 직결되기 때문에 인덱싱에 특화되어 있어요.
핵심 요약과 다음 단계
오늘 우리는 Rust의 근간이 되는 데이터 타입들을 함께 살펴보았어요. 처음에는 복잡해 보일 수 있지만, 타입 시스템이 주는 안정성을 경험하기 시작하면 Rust의 매력에 푹 빠지게 될 거예요.
- 스칼라 타입은 단일 값(정수, 실수, 불리언, 문자)을 다루며 스택에 저장돼요.
- 복합 타입은 묶음 데이터(튜플, 배열)이며 데이터의 성격에 따라 선택해요.
- 정수 타입은 부호 유무(i/u)와 크기(8~128)를 신중히 결정해야 해요.
- 배열은 크기가 고정되며, 모든 요소의 타입이 동일해야 해요.
- 배열 인덱싱에는 반드시 usize 타입을 사용해야 에러가 없어요.
- 정밀한 소수점 계산이 필요하다면 기본값인 `f64`를 활용하세요.
오늘 배운 내용을 바탕으로 지금 바로 작은 실습을 해보시는 건 어떨까요? 단순히 눈으로 읽는 것보다, 직접 코드를 입력하며 컴파일러의 에러 메시지를 마주해 보는 것이 가장 빠르게 성장하는 길이에요.
🚀 다음 단계로 나아가기
- 오늘 할 일: Rust 설치를 완료하고 `println!`으로 각 타입의 값을 출력해 보세요.
- 이번 주 할 일: 서로 다른 타입의 변수를 선언하고, `as` 키워드를 이용해 타입을 변환하는 연습을 해보세요.
- 실행 직전 할 일: 튜플을 사용하여 함수의 반환값을 여러 개 만들어 보는 연습을 해보세요.
딱 10분만 투자해서 Rust 데이터 타입을 직접 코드로 구현해 보세요. 여러분의 러스트 여정이 훨씬 더 견고해질 거예요!
함께 읽으면 좋은 글: Rust 입문자를 위한 환경 설정 가이드, Rust 소유권(Ownership) 개념 완벽 정리