파이썬 타입 힌팅, 모르고 쓰면 손해예요
지난 글에서 제너레이터까지 보고 나면 슬슬 "고급 문법은 다 비슷한 느낌"이라는 생각이 드실 수 있어요. 그래서 이번엔 좀 분위기를 바꿔서, 문법보다는 코드가 얼마나 읽기 좋은가에 영향을 주는 도구 하나를 들고 왔습니다.
혹시 이런 경험 있으세요? 다른 사람이 짠 코드를 보는데, 함수 하나가 떡 하니 있어요.
def process(data, config):
...
data가 뭐예요? 리스트인가요, 딕셔너리인가요? config는요? 결국 함수 안으로 들어가서 한참 추적해야 알 수 있죠. 본인이 한 달 전에 짠 코드를 다시 봐도 똑같거든요. 저도 그랬어요.
이걸 한 줄로 해결해주는 게 오늘 이야기할 파이썬 타입 힌팅이에요.
파이썬 타입 힌팅이 뭐예요?
쉽게 말하면, 변수랑 함수에 라벨을 붙여주는 일이에요.
약병에 "두통약"이라고 써 붙이는 거랑 비슷합니다. 라벨이 없어도 약은 약이에요. 먹는 데 지장이 없죠. 근데 라벨이 붙어 있으면 다른 사람도 헷갈리지 않고, 본인도 두 달 뒤에 안 까먹잖아요.
타입 힌트가 딱 그 라벨이에요. "이 변수에는 정수가 들어가요", "이 함수는 문자열을 받아서 숫자를 돌려줘요" 같은 안내문인 거죠.
가장 단순한 형태부터
변수 옆에 콜론(:)을 붙이고 타입을 적어주면 끝이에요.
# 변수 이름 옆에 콜론, 그리고 타입을 적어요
name: str = "철수" # 이 자리에는 문자열이 들어와요
age: int = 30 # 이 자리에는 정수가 들어와요
height: float = 175.5 # 이 자리에는 실수가 들어와요
이게 전부입니다. 진짜 별거 없어요.
함수에 라벨 달기 (가장 많이 쓰는 형태)
사실 변수 힌트는 잘 안 써요. 진짜 자주 쓰는 건 함수에 다는 거거든요.
# 매개변수마다 : 타입, 반환값은 -> 타입
def add(a: int, b: int) -> int:
return a + b
result = add(3, 5)
print(result) # 8
a: int는 "a는 정수예요", -> int는 "반환값도 정수예요"라는 뜻이에요. 함수 시그니처만 봐도 뭐가 들어가고 뭐가 나오는지 한눈에 보이잖아요.
반환값이 없는 함수는 -> None을 써줍니다.
def print_log(msg: str) -> None: # 아무것도 안 돌려주면 None
print(msg)
리스트나 딕셔너리는 어떻게 해요?
파이썬 3.9 이상이면 그냥 소문자로 쓰면 됩니다.
scores: list[int] = [90, 85, 70] # 정수가 든 리스트
ages: dict[str, int] = {"철수": 20, "영희": 22} # 키는 문자열, 값은 정수
def average(nums: list[int]) -> float:
return sum(nums) / len(nums)
print(average(scores)) # 81.66666666666667
가끔 옛날 코드에서 from typing import List, Dict 이렇게 임포트해서 List[int] 같은 걸 쓰는 게 보일 거예요. 그건 3.8 이전 버전 호환용이에요. 요즘 환경이라면 그냥 소문자 list[int] 쓰시면 됩니다.
"값이 없을 수도 있어요"는 어떻게 표현해요?
함수가 가끔은 None을 돌려줄 때가 있잖아요. 그럴 때 쓰는 게 X | None 형태예요. (파이썬 3.10 이상에서 가능합니다.)
def find_age(name: str, ages: dict[str, int]) -> int | None:
return ages.get(name) # 없으면 None 반환
result = find_age("민수", {"철수": 20, "영희": 22})
if result is None:
print("못 찾았어요")
else:
print(f"{result}살")
int | None은 "정수 아니면 None"이라는 뜻이에요. 옛날엔 Optional[int]라고 썼는데, 요즘은 int | None이 훨씬 깔끔해서 많이 씁니다.
잠깐, 근데 강제는 아니에요
여기서 좀 충격적인 사실 하나 알려드릴게요.
age: int = "스무살" # 어... 에러 안 나요
이거 실행됩니다. 에러 안 나요. 진짜예요.
처음 이걸 알게 되면 "그럼 왜 써요?" 하는 반응이 자동으로 나오시거든요. 저도 그랬어요.
답은 이렇습니다.
- 에디터가 똑똑해져요: VS Code나 파이참이 자동완성을 훨씬 잘해주고, 잘못된 사용엔 빨간 줄을 그어줘요
- 읽는 사람이 편해요: 협업할 때 함수 시그니처만 봐도 뭐가 들어가는지 바로 알아요
- mypy로 미리 잡을 수 있어요: 실행 전에 타입 오류를 잡아주는 도구가 따로 있어요
즉, 타입 힌트는 사람과 도구를 위한 안내문이에요. 파이썬 자체는 신경 안 씁니다.
mypy로 정적 검사 살짝 맛보기
말로만 하면 와닿지 않으니까, 진짜 검사하는 방법도 짧게 보여드릴게요.
pip install mypy
mypy my_script.py
이렇게 하면 타입이 안 맞는 곳을 콘솔에서 알려줍니다. 실행하기 전에 미리 잡을 수 있다는 게 핵심이에요. 코드 양이 늘어날수록 진짜 유용해지거든요. 협업 프로젝트에선 거의 필수에 가깝습니다.
너무 자세히 적으면 오히려 독이에요
가끔 욕심내서 이런 거 적으시는 분들이 있는데요.
def handle(x: Callable[[int, str], dict[str, list[tuple[int, str]]]]) -> None:
...
이건 라벨이 아니라 암호문이에요. 읽기 더 힘들어집니다.
타입 힌트는 함수 시그니처와 핵심 변수에 우선 달고, 모든 줄에 다 붙일 필요는 없어요. 너무 복잡해지면 차라리 주석으로 풀어 쓰는 게 낫습니다.
직접 해보기
지난 글들에서 만들었던 BMI 함수에 타입 힌트를 한번 달아보세요.
# 힌트 없는 원본
def bmi(weight, height):
return weight / (height ** 2)
# 힌트 추가
def bmi(weight: float, height: float) -> float:
return weight / (height ** 2)
print(bmi(70.0, 1.75)) # 22.857142857142858
위아래를 비교해보시면, 라벨이 붙은 쪽이 "아, 실수 두 개 받아서 실수 돌려주는구나"가 바로 보이잖아요. 이게 파이썬 코드 품질을 올리는 가장 작고 확실한 습관 중 하나예요.
타입 힌팅으로 코드 읽히는 정도를 끌어올렸으니, 이제 코드가 빨라지는 다른 차원이 남아 있어요. 다음 글에선 비동기 프로그래밍, async랑 await 이야기를 해볼 거예요. 네트워크 요청 여러 개를 동시에 던지는, 좀 신기한 세상입니다.
제 글이 도움이 되셨다면 댓글 & 공감 부탁드려요 😀
'Application > Python' 카테고리의 다른 글
| [Python] 파이썬 멀티스레딩으로 작업 속도 확 줄이기 (0) | 2026.05.29 |
|---|---|
| [Python] 파이썬 비동기 프로그래밍, async와 await 기초부터 (0) | 2026.05.27 |
| [Python] 이터레이터와 제너레이터 이해하기 (파이썬 제너레이터 입문) (0) | 2026.05.21 |
| [Python] 파이썬 성적 관리 프로그램 만들기 — 중급 종합 실습 (0) | 2026.05.20 |
| [Python] 파이썬 가상환경과 pip 완벽 정리 (Windows 기준) (0) | 2026.05.19 |