실무에서 바로 쓰는 타입힌트 치트시트입니다. 기본 컨테이너 타입, Optional/Union(|), Callable, Generic/TypeVar, Literal/Final, TypedDict/Protocol까지 코드 가독성과 안정성을 높이는 패턴을 정리합니다.
타입힌트는 코드의 의도를 명확히 하고 리팩토링 안정성을 높입니다. 이 치트시트는 기본 타입부터 Generic, TypeVar, Literal까지 실무에서 바로 쓰는 타입힌트 패턴을 정리합니다.
언제 이 치트시트를 보나?
“이 함수는 뭘 받는지/뭘 리턴하는지"가 불분명할 때
리팩토링/코드리뷰에서 안정성을 올리고 싶을 때
Generic 클래스/함수를 정의해야 할 때
핵심 패턴
컨테이너: list[str], dict[str, int] (Py3.9+)
Optional: str | None (Py3.10+) 또는 Optional[str]
Generic: TypeVar로 타입 파라미터 정의
제한된 값: Literal["a", "b"]
상수: Final[int]
기본 타입
1
2
3
4
5
6
7
8
9
# 기본 타입 (Py3.9+)defgreet(name:str)->str:returnf"Hello, {name}"defadd(a:int,b:int)->int:returna+bdefis_valid(value:float)->bool:returnvalue>0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 컨테이너 타입 (Py3.9+ 내장 타입 직접 사용)deftotal(nums:list[int])->int:returnsum(nums)defget_config()->dict[str,str]:return{"host":"localhost","port":"8080"}defunique_items(items:list[str])->set[str]:returnset(items)# 튜플 (고정 길이 + 타입)defget_point()->tuple[int,int]:return(10,20)# 가변 길이 튜플defget_scores()->tuple[int,...]:return(90,85,88)
Optional / Union
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Optional - None일 수 있는 타입deffind_user(user_id:int)->str|None:# Py3.10+ifuser_id==1:return"Alice"returnNone# Py3.9 이하fromtypingimportOptionaldeffind_user_old(user_id:int)->Optional[str]:...# Union - 여러 타입 중 하나defprocess(value:int|str)->str:# Py3.10+returnstr(value)# Py3.9 이하fromtypingimportUniondefprocess_old(value:Union[int,str])->str:returnstr(value)
Callable - 함수 타입
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fromcollections.abcimportCallable# 함수를 인자로 받기defapply(func:Callable[[int,int],int],a:int,b:int)->int:returnfunc(a,b)result=apply(lambdax,y:x+y,3,4)# 7# 콜백 패턴defon_complete(callback:Callable[[str],None])->None:callback("done")# 임의 인자 함수deflogger(func:Callable[...,None])->None:func()
Generic / TypeVar - 제네릭 타입
1
2
3
4
5
6
7
8
9
10
11
fromtypingimportTypeVar,GenericT=TypeVar("T")# 타입 변수 선언# 제네릭 함수deffirst(items:list[T])->T|None:returnitems[0]ifitemselseNone# 사용first([1,2,3])# int 반환first(["a","b"])# str 반환
# 제한된 TypeVarfromtypingimportTypeVarNumber=TypeVar("Number",int,float)# int 또는 float만defdouble(x:Number)->Number:returnx*2# bound 사용 - 특정 타입의 서브타입만fromtypingimportTypeVarclassAnimal:defspeak(self)->str:return"..."classDog(Animal):defspeak(self)->str:return"Woof!"A=TypeVar("A",bound=Animal)defmake_speak(animal:A)->str:returnanimal.speak()
fromtypingimportLiteral# 특정 값만 허용defset_mode(mode:Literal["read","write","append"])->None:print(f"Mode: {mode}")set_mode("read")# OK# set_mode("delete") # Type error!# 불리언 대신 명시적 값defget_status()->Literal["success","failure","pending"]:return"success"# 숫자 리터럴defset_priority(level:Literal[1,2,3])->None:pass
Final / ClassVar - 상수와 클래스 변수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fromtypingimportFinal,ClassVar# 상수 (재할당 금지)MAX_SIZE:Final[int]=100API_URL:Final="https://api.example.com"# 타입 추론# 클래스에서 사용classConfig:DEBUG:Final[bool]=False# 인스턴스에서 재할당 불가# 클래스 변수 (인스턴스 변수와 구분)instance_count:ClassVar[int]=0def__init__(self)->None:Config.instance_count+=1
fromtypingimportProtocol# "이 메서드가 있으면 됨" - 덕 타이핑의 타입 버전classReadable(Protocol):defread(self)->str:...classFile:defread(self)->str:return"file content"classStringIO:defread(self)->str:return"string content"defprocess(source:Readable)->str:returnsource.read()# File과 StringIO 모두 Readable을 "암시적으로" 구현process(File())process(StringIO())
추상 컬렉션 타입 (유연한 API)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fromcollections.abcimportIterable,Sequence,Mapping# Iterable - for문 가능한 모든 것defsum_all(items:Iterable[int])->int:returnsum(items)sum_all([1,2,3])# listsum_all((1,2,3))# tuplesum_all({1,2,3})# setsum_all(range(4))# range# Sequence - 인덱싱 + 길이defget_middle(items:Sequence[str])->str:returnitems[len(items)//2]# Mapping - 키-값 접근defget_name(data:Mapping[str,str])->str:returndata.get("name","unknown")