강제하지 않고 설명만 하는 어노테이션
타입 힌트는 이름(주로 함수의 매개변수)에 붙이는 일종의 메모입니다. "여긴 int가 와야 해", "이 함수는 문자열 리스트를 돌려줘" 같은 정보를 적어두는 거죠. 파이썬은 실행 시점에 이걸 검사하지 않습니다. int로 표시한 자리에 문자열을 넘겨도 에러가 나지 않아요. 대신 에디터나 외부 도구(mypy, pyright, VS Code의 Pylance를 통한 Pyright 등)가 힌트를 읽고 코드 실행 전에 경고를 띄워줍니다.
가장 간단한 예시부터 보죠:
name: str은 매개변수에, -> str은 반환값에 타입을 명시해 주는 표기예요. 두 호출 모두 실행은 잘 됩니다. 두 번째는 잘못된 코드라서 정적 타입 검사기라면 경고를 띄우겠지만, 파이썬 자체는 42가 f"{...}" 포매팅을 지원하니까 아무 불평 없이 돌아가죠.
여기서 꼭 잡아 둬야 할 감각이 있어요. 타입 힌트는 기계가 읽을 수 있는 문서일 뿐이라는 점입니다. 런타임 동작은 전혀 바뀌지 않아요.
굳이 왜 써야 할까?
효과가 빨리 체감되는 순서로 세 가지를 꼽아 볼게요.
- 에디터가 훨씬 똑똑해져요. 자동완성이 정확한 메서드를 보여주고, 이름 바꾸기(rename)도 제대로 전파되며, 변수에 마우스를 올리면 타입이 바로 뜹니다.
- 함수 시그니처가 문서 역할을 해요.
def fetch(url: str, timeout: float = 5.0) -> dict:만 봐도 뭘 넘겨야 하고 뭐가 돌아오는지 한눈에 들어오죠. 함수 본문을 뒤져볼 필요가 없어요. - 타입 검사기가 실행 전에 버그를 잡아줘요. 프로젝트 루트에서
mypy .만 돌려도 유닛 테스트로는 놓치기 쉬운 버그 — 값이 와야 할 자리에None이 반환된다거나, 리스트가 와야 할 곳에 딕셔너리가 쓰인다거나 — 가 줄줄이 드러납니다.
오늘 하루 나만 쓰고 버릴 일회용 스크립트라면 굳이 안 써도 돼요. 하지만 나중에 다시 열어볼 코드이거나 누군가와 공유할 코드라면, 힌트 다는 데 드는 15초가 한 시간 안에 본전을 뽑습니다.
기본 내장 타입
아래 타입들은 전부 import 없이 바로 쓸 수 있어요.
변수 어노테이션(name: str = "Rosa")은 사실 거의 쓸 일이 없습니다. 오른쪽 값만 보면 파이썬이 알아서 타입을 추론해 주니까요. 어노테이션은 함수 매개변수와 반환 타입에 남겨 두고, 추론된 타입이 애매할 때만 예외적으로 붙이는 게 좋습니다.
아무것도 반환하지 않는 함수에는 -> None을 씁니다:
리스트, 딕셔너리, 튜플, 세트
컨테이너 타입은 한 가지 정보가 더 필요합니다. 바로 무엇을 담고 있는지죠. 최신 버전의 Python에서는 내장 타입에 직접 대괄호를 붙여 타입을 지정할 수 있습니다:
소리 내어 읽어 보면 이런 느낌입니다:
list[float]— float 값들의 리스트.dict[str, int]— 문자열 키와 정수 값을 가진 딕셔너리.tuple[float, float]— 정확히 두 개의 float로 이루어진 튜플.set[str]— 문자열들의 집합.
list[...], dict[...]처럼 대괄호를 붙이는 표기법은 Python 3.9 이상에서 쓸 수 있습니다. 그 이전 코드에서는 typing 모듈에서 List, Dict, Tuple을 임포트해서 쓰던 걸 볼 수 있는데, 의미는 똑같고 표기법만 구버전일 뿐입니다.
Optional: None일 수도 있는 값
"None일 수도 있음"은 자주 등장하는 패턴입니다. 표기 방법이 두 가지 있는데 둘 다 동일한 의미이고, 최근 문법 쪽이 읽기 더 깔끔합니다:
str | None은 "문자열 또는 None"을 뜻합니다. | 문법은 Python 3.10부터 사용할 수 있어요. 그 이전 버전의 코드에서는 typing 모듈의 Optional[str]을 보게 되는데, 의미는 똑같습니다.
함수 시그니처에서 -> str | None을 본 호출자는 결과를 쓰기 전에 None 여부를 확인해야 한다는 걸 바로 알 수 있죠. 타입 힌트를 붙이는 이유가 바로 여기에 있습니다.
유니온 타입: 이것 아니면 저것
값이 여러 타입 중 하나일 수 있을 때는 |를 사용하면 됩니다.
두 개 이상의 타입을 union으로 묶을 수도 있습니다. int | str | float는 "이 셋 중 어느 것이든 된다"는 뜻입니다.
함수 내부 변수에 타입 힌트 달기
대부분의 경우 파이썬은 초기화 값만 보고도 지역 변수의 타입을 알아서 추론합니다. 그래서 타입 힌트를 직접 달아야 하는 상황은 다음과 같이 제한적입니다:
- 컨테이너가 비어 있는 상태로 시작해서 타입 체커가 내용물을 추론할 수 없을 때
- 값이 여러 타입이 될 수 있는데, 하나로 확정하고 싶을 때
- 사람이 읽었을 때 의도를 명확히 드러내고 싶을 때
typing.Any는 일종의 탈출구입니다. "여기는 굳이 정확히 타입을 달고 싶지 않다"고 선언하는 셈이죠. 꼭 필요할 때만 쓰세요. Any를 남발하면 나머지 타입 힌트까지 전부 무의미해집니다.
클래스에 타입 힌트 달기
클래스 속성과 메서드 시그니처에 타입을 지정하는 방법도 일반 함수와 똑같습니다:
Dataclass는 타입 어노테이션이 필수입니다. @dataclass 데코레이터가 어노테이션을 읽어서 __init__과 __repr__을 자동으로 만들어 주거든요. 어노테이션이 런타임 동작에 실제로 영향을 주는 거의 유일한 경우죠.
튜플과 "길이 상관없음" 케이스
tuple[...]에는 초보자를 헷갈리게 하는 두 가지 형태가 있습니다:
tuple[float, float]— float 두 개짜리 튜플을 의미합니다.tuple[int, ...]— 개수 제한 없는 int 튜플입니다. 여기서...은 "기타 등등"이라는 뜻으로, 타입 시스템에서 실제로 쓰이는 문법 요소입니다.
Callable과 타입 별칭(Type Alias)
함수가 다른 함수를 인자로 받거나 반환할 때는 Callable을 사용합니다:
Callable[[int], int]은 "int 하나를 받아서 int를 반환하는 함수"라는 뜻이에요.
타입 어노테이션이 반복돼서 지저분해지면, 이렇게 이름을 붙여주면 됩니다:
별칭은 그냥 평범한 파이썬 대입문입니다. 원래의 긴 이름을 쓸 수 있는 자리라면 어디서든 짧은 별칭도 그대로 동작합니다.
타입 체커 실행하기
파이썬 인터프리터 자체는 타입 힌트를 무시합니다. 실제로 타입을 검사 하려면 별도의 타입 체커를 설치해야 합니다. mypy가 원조 격이고, pyright(VS Code의 Pylance가 내부적으로 쓰는 엔진)는 속도가 더 빠릅니다.
pip install mypy
mypy your_project/
첫 실행에서 미처 생각지 못했던 곳에서 오류가 줄줄이 드러날 겁니다. 한 번에 다 잡으려 하지 말고 하나씩 처리하세요. 일단 넘어가야 할 때는 # type: ignore로 해당 줄만 무시하게 할 수 있습니다.
요즘 IDE는 코드를 작성하는 동안 타입 검사를 실시간으로 돌려주기 때문에, 저장하기도 전에 대부분의 피드백을 받아볼 수 있습니다.
타입 힌트가 어울리지 않는 경우
- 잠깐 써보는 탐색용 스크립트. 한 시간이면 수명이 끝나는 코드에 어노테이션을 다는 건 오히려 짐이 됩니다.
- 동적 성격이 강한 코드. 메타프로그래밍이나 플러그인 시스템처럼 타입 시스템이 표현하기 어려운 패턴이 많습니다. 외부에 노출되는 API만 타입을 달고, 내부는 느슨하게 두는 게 현실적입니다.
- 타입 정보가 없는 서드파티 라이브러리. 가져다 쓰는 라이브러리에 타입 정보가 없다면
Any가 자연스럽게 섞여 들어옵니다. 내 코드도 아니니 어쩔 수 없죠.
이런 경우를 빼면, 타입 힌트는 작은 습관으로 큰 이득을 얻는 도구입니다. 비용이라고 해봐야 함수 시그니처에 몇 글자 더 치는 정도. 대신 버그가 줄어들고, 리팩터링이 수월해지고, 코드 자체가 문서 역할을 해줍니다.
다음 주제: 모듈과 import
이제 함수 단위에서 쓰는 도구들 — 인자, 데코레이터, 타입 힌트 — 은 모두 살펴봤습니다. 다음에는 파이썬이 여러 파일에 걸쳐 코드를 어떻게 정리하는지, 즉 모듈과 패키지, 그리고 import 시스템을 다뤄보겠습니다.
자주 묻는 질문
파이썬의 타입 힌트가 정확히 뭔가요?
타입 힌트는 변수, 함수 매개변수, 반환값이 어떤 타입이어야 하는지 코드에 명시해 두는 어노테이션입니다. 파이썬 인터프리터는 런타임에 이를 강제하지 않고, mypy나 pyright 같은 타입 체커, IDE, 그리고 코드를 읽는 사람을 위한 힌트로만 사용됩니다.
타입 힌트를 붙이면 파이썬이 더 빨라지나요?
아니요. 파이썬 인터프리터는 런타임에 타입 힌트를 그냥 무시합니다. 속도가 빨라지는 건 실행 속도가 아니라 개발 속도예요. 에디터가 오타를 잡아주고, 함수 시그니처가 명확해지며, 리팩토링도 훨씬 안전해집니다.
타입 힌트는 언제 붙이는 게 좋나요?
가장 먼저 공개 함수의 매개변수와 반환 타입에 붙이세요. 함수 내부 변수에는 타입이 한눈에 보이지 않을 때만 제한적으로 쓰는 게 좋습니다. 혼자 쓰는 일회성 스크립트라면 없어도 상관없지만, 팀에서 공유하는 코드나 라이브러리에서는 투자 대비 효과가 아주 큽니다.