[IT-정보] Rust 데이터 타입 개념 핵심 가이드 – 스칼라와 복합 타입의 차이점과 실전 활용법

Rust 데이터 타입 개념를 설명하는 대표 이미지

왜 Rust 데이터 타입부터 제대로 잡아야 할까요?

코드 한 줄을 적었는데 컴파일러가 빨간 줄을 그으며 소리치고 있나요? 분명히 숫자를 넣었는데 타입이 맞지 않는다며 거절당하는 경험을 했다면, 그건 실력이 부족해서가 아니라 Rust의 데이터 타입 규칙을 아직 몸으로 익히지 못했기 때문이에요.

다른 언어를 배우다 온 분들이 가장 당황하는 지점이 바로 여기예요. 파이썬처럼 대충 숫자를 넣으면 알아서 처리해 주는 게 아니라, Rust는 아주 깐깐하게 “이 숫자가 8비트인지, 64비트인지, 아니면 소수점이 포함되어 있는지”를 따져 물어요. 이 까다로운 규칙이 처음에는 벽처럼 느껴지겠지만, 사실 이 덕분에 우리는 프로그램이 실행 중에 갑자기 멈춰버리는 끔찍한 사고를 미리 막을 수 있어요.

데이터 타입을 제대로 이해하지 못한 채로 다음 단계인 소유권(Ownership)이나 빌림(Borrowing)으로 넘어가면, 코드 전체가 미로처럼 느껴질 거예요. 기초 공사가 부실한 건물은 금방 무너지듯, 데이터 타입이라는 기초를 탄탄히 다져야 비로소 Rust라는 강력한 도구를 자유자재로 다룰 수 있게 돼요.

오늘 이 글을 읽고 나면 다음과 같은 능력을 갖추게 돼요.

  • 스칼라 타입과 복합 타입이 무엇인지 명확하게 구분할 수 있어요.
  • 상황에 맞는 정수형과 부동 소수점 타입을 골라 쓸 수 있어요.
  • 튜플과 배열을 활용해 여러 데이터를 효율적으로 묶는 법을 배워요.
  • 컴파일러의 타입 오류 메시지를 보고 스스로 수정할 수 있는 힘이 생겨요.

시작 전 반드시 짚고 넘어가야 할 기본 개념

Rust의 데이터 타입을 공부하기 전에 우리가 먼저 머릿속에 그려야 할 그림이 있어요. 바로 데이터가 메모리의 어디에, 어떤 방식으로 저장되는지에 대한 이해예요. Rust는 성능을 극한으로 끌어올리기 위해 데이터의 크기를 아주 세밀하게 관리하거든요.

우리가 다룰 데이터 타입은 크게 두 가지 큰 줄기로 나뉘어요. 단 하나의 값을 담는 스칼라(Scalar) 타입과 여러 값을 하나로 묶는 복합(Compound) 타입이에요. 이 둘을 구분하는 것은 마치 요리를 할 때 소금 한 꼬집(스칼라)을 쓸지, 아니면 샐러드 한 접시(복합)를 준비할지 결정하는 것과 같아요.

💡 알아두기
Rust는 정적 타입 언어예요. 즉, 변수를 선언할 때 그 변수가 어떤 타입인지가 결정되면 프로그램이 실행되는 동안에는 그 타입이 절대로 변하지 않아요. 이 점이 유연한 파이썬과 가장 큰 차이점이에요.

본격적으로 타입을 배우기 전에, 내가 어떤 데이터를 다룰지 결정하는 기준을 미리 세워두면 좋아요. 아래 표를 통해 스칼라 타입과 복합 타입이 어떤 특징을 가졌는지 가볍게 훑어보세요.

구분 기준 스칼라 타입 (Scalar) 복합 타입 (Compound)
데이터 구성 단 하나의 값만 포함 여러 개의 값을 그룹화
주요 사례 정수, 실수, 불리언, 문자 튜플, 배열
메모리 특징 고정된 크기를 가짐 내부 요소들의 합으로 결정
사용 목적 기본적인 수치나 논리 값 저장 관련 있는 데이터들을 묶어서 관리

이 개념들을 머릿속에 넣었다면 이제 진짜 실전으로 들어갈 준비가 된 거예요. 단순히 외우는 것이 아니라, 메모리 공간을 어떻게 효율적으로 나눠 쓸 것인가라는 관점으로 접근하면 훨씬 이해하기 쉬워요.

Rust 데이터 타입의 핵심 구조 파헤치기

이제 본격적으로 Rust의 데이터 세계를 여행해 볼까요? 먼저 가장 기본이 되는 스칼라 타입부터 하나씩 뜯어볼게요. 이 과정은 마치 건물을 짓기 위해 벽돌 하나하나의 강도와 크기를 확인하는 과정과 같아요.

STEP 1. 단일 값을 담는 스칼라 타입 완벽 이해

스칼라 타입은 크게 정수, 부동 소수점, 불리언, 그리고 문자로 나뉩ers. 각 타입은 용도가 명확하게 정해져 있어요.

먼저 정수형(Integer)이에요. Rust는 정수를 양수만 표현하는 부호 없는 정수(unsigned)와 음수도 표현하는 부호 있는 정수(signed)로 엄격히 구분해요. 이름 앞에 ‘u’가 붙으면 unsigned, ‘i’가 붙으면 signed라고 생각하면 쉬워요. 크기에 따라 8비트부터 128비트까지 아주 세분화되어 있어요.

  • i8, u8: 아주 작은 숫자를 다룰 때 써요. 예를 들어 0~255 사이의 밝기 값 같은 데이터를 처리할 때 메모리를 아끼기 위해 사용하죠.
  • i32, u32: 가장 대중적인 타입이에요. 웬만한 숫자는 이 범위 안에서 해결돼요.
  • i64, u64: 엄청나게 큰 숫자를 다뤄야 할 때, 예를 들어 전 세계 인구수나 우주의 거리 같은 데이터를 다룰 때 필요해요.

다음은 부동 소수점(Floating-point) 타입이에요. 소수점이 있는 실수를 표현할 때 사용해요. Rust에는 f32와 f64 두 가지가 있는데, 특별한 이유가 없다면 기본적으로 f64를 사용하는 것이 좋아요. f64가 더 정밀하기 때문에 계산 오차를 줄이는 데 유리하거든요.

그다음은 참과 거짓을 나타내는 불리언(Boolean)이에요. true와 false 두 가지만 존재하죠. 조건문에서 프로그램의 흐름을 결정하는 아주 중요한 역할을 해요. 마지막으로 문자(Character) 타입인 char가 있어요. 여기서 주의할 점은 Rust의 char는 단순히 ASCII 문자가 아니라, 유니코드(Unicode) 코드 포인트를 나타낸다는 거예요. 그래서 한글이나 이모지도 단 하나의 char에 담을 수 있어요.

STEP 2. 여러 데이터를 묶는 복합 타입 활용하기

단일 값만으로는 복잡한 현실 세계를 표현하기 부족해요. 예를 들어 사람의 이름, 나이, 키를 한꺼번에 다뤄야 한다면 어떻게 할까요? 이때 등장하는 것이 복합 타입이에요.

첫 번째는 튜플(Tuple)이에요. 튜플은 서로 다른 타입의 값들을 하나로 묶을 수 있어요. 예를 들어 좌표를 표현할 때 (위도, 경도)처럼 실수와 실수를 묶거나, (이름, 나이)처럼 문자열과 정수를 묶을 수 있죠. 튜플은 한 번 만들면 크기를 늘리거나 줄일 수 없는 고정된 크기를 가진다는 특징이 있어요.

두 번째는 배열(Array)이에요. 배열은 튜플과 달리 모든 요소의 타입이 같아야 해요. 모든 값이 정수라면 모두 정수여야 하죠. 배열 역시 생성할 때 정한 크기를 바꿀 수 없어요. 만약 데이터가 계속 늘어나야 하는 상황이라면 나중에 배울 Vector를 써야 하겠지만, 데이터의 개수가 확실히 정해져 있는 경우에는 배열이 메모리 효율 측면에서 훨씬 뛰어나요.

STEP 3. 데이터 타입 선택을 위한 실전 시나리오

실제로 개발할 때 어떤 타입을 골라야 할지 막막할 때가 있죠? 간단한 예시를 통해 감을 잡아볼게요.

💡 알아두기
데이터 타입을 결정할 때는 ‘사용 가능한 범위’와 ‘메모리 사용량’ 사이의 균형을 생각해야 해요. 너무 작은 타입을 쓰면 숫자가 넘쳐버리고(Overflow), 너무 큰 타입을 쓰면 메모리가 낭비되니까요.

시나리오: 게임 캐릭터의 정보 시스템 만들기

  1. 캐릭터의 레벨: 0부터 시작해서 100을 넘지 않아요. 👉 u8 (0~255 범위로 충분해요)
  2. 캐릭터의 골드 보유량: 최대 1,000,000까지 가능해요. 👉 u32 (약 42억까지 가능해요)
  3. 캐릭터의 현재 좌표: (x, y) 좌표값이에요. 👉 (f64, f64) 튜플
  4. 캐릭터의 장착 아이템 슬롯(5개): 아이템 ID 번호들을 담아요. 👉 [u32; 5] 배열

이렇게 상황을 분석하면 어떤 타입을 써야 할지 명확해져요. 단순히 코드를 짜는 게 아니라, 데이터의 성격에 맞춰 그릇을 고르는 과정이 바로 프로그래밍의 핵심이에요.

STEP 4. 메모리 관점에서의 깊이 있는 분석

왜 Rust는 이렇게 타입을 엄격하게 나눌까요? 그 이유는 바로 스택(Stack)과 힙(Heap) 메모리 관리 때문이에요. 스칼라 타입과 고정 크기 배열은 대부분 스택에 저장돼요. 스택은 데이터의 크기가 컴파일 타임에 미리 결정되어 있어야 하며, 접근 속도가 굉장히 빨라요.

만약 우리가 데이터의 크기를 미리 알 수 없다면 어떻게 할까요? 그때는 데이터를 힙(Heap) 영역에 저장해야 해요. 하지만 힙에 저장하면 스택에 저장할 때보다 관리 비용이 들고 속도가 조금 느려질 수 있어요. 그래서 Rust 개발자들은 가능한 한 스칼라 타입이나 배열처럼 스택에서 빠르게 처리할 수 있는 타입을 적절히 활용하려고 노력해요.

결국 타입 시스템을 잘 이해한다는 것은, 프로그램이 메모리를 어떻게 사용하는지 통제하고 있다는 뜻과 같아요. 이는 곧 시스템의 성능과 안정성으로 직결되죠.

자주 하는 실수와 해결법

공부하다 보면 누구나 겪는 시행착오가 있어요. 컴파일러가 보내는 에러 메시지에 당황하지 않도록, 자주 발생하는 실수 5가지를 정리해 봤어요.

  • 정수와 실수를 그냥 더하려고 할 때
    왜 발생하는가: Rust는 서로 다른 타입 간의 암시적 형 변환을 허용하지 않아요. i32와 f64를 더하려고 하면 타입 불일치 에러가 떠요.
    ✅ 해결법: as 키워드를 사용하여 명시적으로 타입을 변환해 주세요. 예: let sum = my_int as f64 + my_float;
  • 정수 오버플로(Overflow) 발생
    왜 발생하는가: u8 타입 변수에 255를 넣은 상태에서 1을 더하면, 범위를 벗어나 에러가 나거나 예상치 못한 값이 나와요.
    ✅ 해결법: 다룰 숫자의 범위를 미리 예측하고, 충분히 큰 타입(예: i32나 i64)을 선택하세요.
  • 배열 인덱스 범위를 벗어남
    왜 발생하는가: 길이가 5인 배열에서 5번 인덱스(실제로는 6번째)를 찾으려고 하면 프로그램이 중단돼요.
    ✅ 해결법: 인덱스는 항상 0부터 시작한다는 점을 명심하고, 배열의 길이를 확인하는 습관을 가지세요.
  • 튜플 요소 접근 방식 실수
    왜 발생하는가: 튜플의 값에 접근할 때 괄호를 잘못 쓰거나 잘못된 방식으로 접근하려고 할 때 발생해요.
    ✅ 해결법: 튜플은 점(.) 표기법을 사용해요. 예: tuple.0, tuple.1 처럼 숫자를 사용해 접근하세요.
  • char 타입에 문자열을 넣으려고 할 때
    왜 발생하는가: char는 단 하나의 유니코드 문자를 의미하지만, 문자열은 여러 문자의 집합이에요.
    ✅ 해결법: 단일 문자는 'A'(작은따옴표)로, 문자열은 "Hello"(큰따옴표)로 명확히 구분해서 사용하세요.

자주 묻는 질문

Q. i32와 i64 중 어떤 것을 기본으로 써야 하나요?

특별히 엄청나게 큰 숫자가 필요하거나 메모리를 극도로 아껴야 하는 상황이 아니라면, 일반적으로 i32를 사용하는 것이 가장 표준적이고 효율적이에요. 대부분의 현대적 CPU에서 i32 연산은 매우 최적화되어 있거든요.

Q. 배열(Array)과 벡터(Vector)의 차이가 정확히 무엇인가요?

가장 큰 차이는 크기의 고정 여부예요. 배열은 한 번 정해진 크기를 바꿀 수 없고 스택에 저장되지만, 벡터는 실행 중에 크기를 늘리거나 줄일 수 있고 데이터가 힙에 저장돼요. 데이터 양이 유동적이라면 벡터를 써야 해요.

Q. 튜플을 언제 사용하면 좋을까요?

서로 다른 타입의 데이터들을 잠시 하나로 묶어서 함수에 전달하거나 반환하고 싶을 때 가장 유용해요. 예를 들어, 함수의 결과로 성공 여부(bool)와 결과값(i32)을 동시에 돌려주고 싶을 때 (bool, i32) 형태의 튜플을 쓰면 아주 깔끔해요.

Q. 실수(Float) 계산을 할 때 오차가 생기는 건 왜인가요?

컴퓨터는 2진수로 숫자를 처리하기 때문에, 0.1 같은 10진수 소수를 완벽하게 표현하지 못할 때가 있어요. 정밀한 금융 계산이 필요하다면 실수 타입 대신 정수를 사용해 최소 단위를 계산하는 방식을 권장해요.

오늘 배운 내용을 내 것으로 만드는 법

Rust의 데이터 타입은 처음에는 복잡해 보이지만, 원리를 이해하면 이보다 더 든든한 지원군이 없어요. 타입이 엄격하다는 것은, 그만큼 우리가 쓴 코드가 예측 가능하다는 뜻이니까요.

✅ 핵심 요약

  • 스칼라 타입(정수, 실수, 불리언, 문자)은 단 하나의 값을 저장해요.
  • 복합 타입(튜플, 배열)은 여러 데이터를 하나로 묶어서 관리해요.
  • 정수는 부호 유무(i/u)와 크기(8~128비트)에 따라 세분화되어 있어요.
  • 배열은 모든 요소의 타입이 같아야 하며 크기가 고정되어 있어요.
  • 타입 간 연산 시에는 반드시 명시적인 형 변환(as)이 필요해요.

자, 이제 이론은 충분해요. 직접 코드를 작성해 볼 시간이에요!

🚀 오늘 바로 실천해 볼 과제

  • Rust Playground에서 다양한 정수형 타입에 값을 넣고 출력해 보세요.
  • 튜플을 만들어 내 이름과 나이를 저장하고, 각 요소에 접근해 보세요.
  • 배열의 크기를 넘어서는 인덱스에 접근했을 때 컴파일러가 어떤 에러를 내는지 직접 확인해 보세요.

어렵게 느껴졌던 Rust 데이터 타입, 오늘 확실히 정리해 보셨나요? 이 기초가 쌓이면 나중에 만날 소유권 개념도 훨씬 수월하게 이해할 수 있을 거예요. 꾸준히 하나씩 정복해 나가시길 응원할게요!

함께 읽으면 좋은 글: Rust 입문자를 위한 환경 설정 가이드, Rust 소유권과 빌림 개념 완벽 정리

댓글 남기기