
Go 언어의 엄격한 자료형 때문에 당황하셨나요?
새로 Go 언어를 배우기 시작했을 때, 가장 먼저 마주치는 벽이 바로 타입 시스템이에요. 다른 언어에서는 숫자끼리 더하는 것이 너무나 당연하게 느껴지는데, Go에서는 갑자기 “int와 int64는 서로 다른 타입이라 계산할 수 없어요”라는 무시무시한 에러 메시지를 던지며 코드를 멈춰 세우곤 하죠.
이런 경험을 한 번이라도 해보셨다면, 여러분은 지극히 정상적인 입문 과정을 겪고 있는 거예요. Go는 성능과 안정성을 위해 자료형을 매우 엄격하게 관리하는 언어거든요. 이 엄격함이 처음에는 귀찮게 느껴질 수 있지만, 익숙해지면 프로그램이 실행 중에 갑자기 죽어버리는 대참사를 막아주는 든든한 방패가 되어준답니다.
오늘 이 글에서는 단순히 자료형의 종류를 나열하는 것을 넘어, 실무에서 개발자들이 가장 자주 묻고 실수하는 질문들을 중심으로 깊이 있게 다뤄보려고 해요. 이 가이드를 끝까지 읽고 나면, 더 이상 타입 에러 때문에 머리를 싸매지 않고 자신 있게 코드를 작성할 수 있게 될 거예요.
- 정수형과 실수형의 미묘한 차이와 선택 기준
- 문자열과 바이트, 룬(rune)의 복잡한 관계 정리
- 실무에서 빈번한 타입 불일치 오류 해결 방법
- 입문자가 반드시 알아야 할 자료형 FAQ
자료형을 배우기 전 꼭 알아야 할 기본 원칙
Go의 자료형을 제대로 활용하려면, 먼저 이 언어가 어떤 철학을 가지고 설계되었는지 이해해야 해요. Go는 정적 타이핑(Static Typing) 언어예요. 즉, 변수를 선언하는 시점에 이미 그 변수가 어떤 종류의 데이터를 담을지 결정되어야 하고, 한 번 결정된 타입은 중간에 마음대로 바꿀 수 없다는 뜻이에요.
이것이 왜 중요할까요? 바로 컴파일 단계에서 오류를 미리 잡아낼 수 있기 때문이에요. 만약 우리가 실수로 숫자가 들어가야 할 자리에 문자열을 넣었다면, 프로그램이 실제로 돌아가다가 터지는 게 아니라 코드를 실행하기 전인 컴파일 단계에서 즉시 경고를 해줘요. 덕분에 대규모 프로젝트를 운영할 때도 안정성을 유지할 수 있는 것이죠.
또한, Go에는 제로 값(Zero Value)이라는 개념이 있어요. 변수를 선언만 하고 값을 할당하지 않았을 때, Go는 자동으로 각 타입에 맞는 기본값을 채워넣어 줘요. 예를 들어 숫자는 0, 불리언은 false, 문자열은 빈 문자열(
실전에서 바로 쓰는 Go 자료형 핵심 가이드
이제 본격적으로 Go 자료형의 심장부로 들어가 볼게요. 단순히 종류를 아는 것을 넘어, 어떤 상황에서 어떤 타입을 꺼내 써야 하는지 구체적인 단계별 가이드로 설명해 드릴게요.
STEP 1. 정수형(Integer) 마스터하기
Go의 정수형은 크게 부호가 있는 정수(signed)와 부호가 없는 정수(unsigned)로 나뉘어요. `int8`, `int16`, `int32`, `int64`처럼 숫자가 붙은 것은 해당 타입이 차지하는 메모리 비트 수를 의미해요. 숫자가 클수록 더 넓은 범위의 데이터를 담을 수 있지만, 그만큼 메모리도 더 많이 사용하게 되죠.
많은 입문자가 실수하는 부분 중 하나가 바로 `int`와 `int64`를 혼동하는 거예요. 64비트 컴퓨터 환경에서 `int`는 보통 64비트지만, Go 언어 체계에서는 이 둘을 서로 다른 타입으로 취급해요. 따라서 두 값을 더하려면 반드시 하나를 다른 하나로 변환해 주어야 해요.
정수형 변환 예시
a := int32(10)
b := int64(20)
result := int64(a) + b // a를 int64로 변환해야 함부호가 없는 정수인 `uint` 시리즈는 음수를 표현할 필요가 없는 데이터(예: 메모리 주소, 데이터의 크기 등)를 다룰 때 사용해요. 하지만 값이 0인 상태에서 1을 빼면 예상치 못한 거대한 양수로 변하는 언더플로우(Underflow) 현상이 발생할 수 있으니 주의가 필요해요.
STEP 2. 실수형(Floating-point)과 정밀도의 함정
소수점을 다루는 실수형은 `float32`와 `float64`가 있어요. 현대적인 프로그래밍에서는 대부분 float64를 사용해요. `float32`는 메모리는 적게 쓰지만 정밀도가 낮아 계산을 반복하다 보면 오차가 눈덩이처럼 불어날 수 있거든요.
여기서 정말 중요한 주의사항이 있어요. 컴퓨터는 십진수 소수를 이진수로 변환하여 저장하기 때문에, 우리가 흔히 쓰는 0.1 같은 숫자도 내부적으로는 무한 소수로 표현될 때가 많아요. 그래서 “0.1 + 0.2 == 0.3″이라는 식은 거짓(false)이 될 수 있어요.
만약 금융 관련 앱처럼 소수점 한 자리까지 정확해야 하는 서비스를 만든다면, `float` 타입을 쓰면 안 돼요. 대신 정수로 환산해서 계산하거나(예: 1.23달러를 123센트로 변환), 정밀도 계산을 지원하는 별도의 패키지를 사용하는 것이 프로페셔널한 방법이에요.
STEP 3. 문자열(String)과 바이트(Byte)의 비밀
Go에서 문자열은 단순히 글자의 집합이 아니라, 읽기 전용 바이트 슬라이스(Read-only byte slice)예요. 이 사실을 모르면 한글을 다룰 때 큰 코를 다칠 수 있어요. 영어는 한 글자가 1바이트지만, 한글은 UTF-8 인코딩 기준으로 한 글자당 보통 3바이트를 차지하거든요.
예를 들어, `len(“한글”)`을 실행하면 결과값으로 2가 아닌 6이 나와요. 글자 수가 아니라 사용된 바이트 수를 알려주기 때문이죠. 글자 수를 정확히 세고 싶다면 문자열을 rune(룬) 타입으로 변환해서 다뤄야 해요.
여기서 rune이란 무엇일까요? 바로 하나의 유니코드 코드 포인트를 나타내는 타입이에요. 즉, 한글 한 글자를 하나의 단위로 인식하게 해주는 마법 같은 타입이라고 생각하면 쉬워요. 문자열을 한 글자씩 쪼개서 분석해야 하는 로직을 짤 때는 반드시 for range 문을 사용하세요. 이 문법은 바이트 단위가 아닌 룬 단위로 안전하게 순회해 주거든요.
STEP 4. 불리언(Boolean)과 논리 연산
가장 단순해 보이지만 의외로 엄격한 타입이 바로 `bool`이에요. Go에서는 숫자를 불리언처럼 쓸 수 없어요. C언어 같은 곳에서는 0이면 false, 그 외에는 true라고 치지만, Go에서는 숫자 0이나 1을 boolean 변수에 대입하려고 하면 즉시 컴파일 에러가 발생해요.
이 규칙 덕분에 코드는 더 명확해져요. `if x { … }` 대신 `if x != 0 { … }`라고 명시적으로 써야 하므로, 읽는 사람이 코드를 오해할 여지를 없애주죠. 논리 연산자 `&&`(AND), `||`(OR), `!`(NOT)을 사용할 때도 오직 boolean 타입끼리만 연산이 가능하다는 점을 기억하세요.
실전 활용 시나리오: 사용자 정보 처리하기
위에서 배운 내용을 종합해서, 간단한 사용자 정보를 담는 구조체를 설계해 볼게요. 어떤 타입을 선택해야 가장 효율적이고 안전할까요?
- 사용자 나이: 0~150 사이의 작은 정수이므로 int나 uint8이 적당해요.
- 계좌 잔액: 소수점이 생길 수 있고 매우 큰 금액을 다룰 수 있으므로 float64보다는 정수형으로 센트(cent) 단위로 관리하거나 전문 라이브러리를 권장해요.
- 사용자 이름: 다양한 언어를 지원해야 하므로 string을 사용해요.
- 계정 활성화 여부: 참/거짓이므로 bool을 사용해요.
자주 하는 실수와 해결법
코드를 짜다 보면 누구나 겪는 상황들이 있어요. 가장 흔한 5가지 케이스를 정리했으니, 비슷한 에러를 만난다면 여기서 답을 찾아보세요.
❌ 실수: 서로 다른 정수 타입 간의 직접 연산
왜 발생하는가: `int32` 변수와 `int64` 변수를 더하려고 할 때 Go는 타입을 자동으로 맞춰주지 않아요.
✅ 해결법: 한쪽 타입을 명시적으로 형변환하세요. `int64(a) + b`와 같이 작성하면 됩니다.
❌ 실수: 문자열 인덱스로 한글 접근하기
왜 발생하는가: 한글은 여러 바이트로 이루어져 있는데, 인덱스로 접근하면 글자가 아닌 바이트 조각을 가져오게 되어 글자가 깨집니다.
✅ 해결법: 문자열을 `[]rune`으로 변환한 뒤 인덱싱하거나, `for range` 문을 사용하여 안전하게 순회하세요.
❌ 실수: float 타입 간의 동등 비교
왜 발생하는가: 부동 소수점 오차 때문에 `0.1 + 0.2`는 정확히 `0.3`이 아닐 수 있어요.
✅ 해결법: 두 값의 차이가 아주 작은 값(epsilon)보다 작은지 확인하는 방식으로 비교하세요.
❌ 실수: 정수 오버플로우 방치
왜 발생하는가: `int8` 범위(-128~127)를 넘어가는 값을 넣으면 갑자기 음수가 될 수 있어요.
✅ 해결법: 다루려는 데이터의 최대 범위를 미리 계산해 보고, 안전하게 `int64`나 `int`를 사용하세요.
❌ 실수: 제로 값(Zero Value)에 대한 오해
왜 발생하는가: 포인터 타입 변수를 선언만 하고 초기화하지 않으면 `nil`이 들어가 있는데, 이를 일반 변수처럼 사용하려 하면 프로그램이 종료됩니다.
✅ 해결법: 포인터나 슬라이스, 맵을 사용할 때는 반드시 초기화 여부를 확인하세요.
자주 묻는 질문
Q. 변수를 선언할 때 `var`를 쓰는 것과 `:=`를 쓰는 것의 차이는 무엇인가요?
`var`는 타입을 명시적으로 지정하거나, 값이 없는 초기 상태(제로 값)로 변수를 만들 때 주로 사용해요. 반면 `:=`는 타입을 추론해 주는 단축 선언 방식이에요. 함수 내부에서 짧고 빠르게 변수를 만들 때 아주 유용하지만, 전역 변수로는 사용할 수 없다는 차이점이 있어요.
Q. Go에는 왜 `double` 타입이 없나요?
다른 언어에서는 `float`와 `double`을 구분하지만, Go는 혼란을 줄이기 위해 `float32`와 `float64`라는 명확한 이름을 사용해요. `float64`가 사실상 다른 언어의 `double` 역할을 수행한다고 보시면 됩니다.
Q. `int`의 크기는 항상 일정하나요?
아니요, 실행되는 환경에 따라 달라져요. 32비트 시스템에서는 32비트, 64비트 시스템에서는 64비트 크기를 가져요. 따라서 아주 큰 숫자를 다뤄야 한다면 환경에 의존하지 않는 `int64`를 명시적으로 쓰는 것이 훨씬 안전해요.
Q. `rune`은 왜 별도로 존재해야 하나요?
컴퓨터는 기본적으로 바이트 단위로 데이터를 읽지만, 사람은 글자 단위로 생각하죠. `rune`은 유니코드의 한 글자(코드 포인트)를 하나의 정수 값으로 매핑해 주기 때문에, 다국어 처리가 필수적인 현대 프로그래밍에서 글자를 정확히 다루기 위한 필수 장치예요.
Q. 문자열을 숫자로 바꾸는 가장 좋은 방법은 무엇인가요?
`strconv` 패키지의 `Atoi` 함수를 사용하세요. 예를 들어 `strconv.Atoi(“123”)`를 호출하면 문자열을 정수 `int`로 아주 간단하게 바꿀 수 있어요.
자료형 정복을 위한 마지막 체크리스트
Go의 자료형은 처음에는 까다롭지만, 그 원리를 이해하면 매우 강력한 도구가 됩니다. 오늘 배운 내용을 바탕으로 코드를 작성할 때 다음 사항들을 꼭 점검해 보세요.
- 정수 연산 시 타입이 일치하는지 반드시 확인하기
- 실수 연산 시에는 `float64`를 쓰고 오차를 주의하기
- 한글 등 다국어 처리 시에는 `rune`과 `for range` 활용하기
- 변수를 선언할 때 적절한 제로 값(Zero Value) 이해하기
- 시스템 환경에 의존적인 `int` 대신 명확한 크기의 타입을 고려하기
자료형에 대한 기초가 탄탄해졌다면, 이제 여러분은 더 복잡한 데이터 구조로 나아갈 준비가 된 거예요. 오늘 바로 여러분의 코드를 열어보고, 혹시 타입 변환이 누락되었거나 부적절한 자료형을 사용하고 있지는 않은지 한 번 살펴보는 건 어떨까요?
이번 주 할 일: 기존에 작성했던 코드에서 `int`를 `int64`로 바꾸어 보며 타입 변환이 어떻게 일어나는지 직접 경험해 보세요.
실행 직전 할 일: `strconv` 패키지를 활용해 문자열을 숫자로 바꾸는 간단한 예제 코드를 작성해 보세요.
Go의 자료형을 넘어서, 슬라이스와 맵 같은 더 깊은 데이터 구조가 궁금하다면 다음 편을 놓치지 마세요. 더 심화된 내용으로 돌아올 테니 구독하고 기다려 주세요!
관련해서 더 자세한 내용이 궁금하다면 Go 자료형 완전 정복 가이드 글을 함께 읽어보시는 것을 추천해요.