덕 타이핑
Duck Typing
"오리처럼 걷고 꽥꽥거리면 오리다" - 객체의 타입보다 메서드와 속성으로 판단하는 동적 타이핑.
Duck Typing
"오리처럼 걷고 꽥꽥거리면 오리다" - 객체의 타입보다 메서드와 속성으로 판단하는 동적 타이핑.
덕 타이핑(Duck Typing)은 "오리처럼 걷고 오리처럼 꽥꽥거리면, 그것은 오리다(If it walks like a duck and quacks like a duck, it's a duck)"라는 격언에서 유래한 프로그래밍 개념입니다. 객체의 실제 타입보다 그 객체가 어떤 메서드와 속성을 가지고 있는지로 적합성을 판단합니다.
정적 타이핑 언어(Java, C++)에서는 명시적 인터페이스나 상속을 통해 타입을 정의하지만, 덕 타이핑을 지원하는 언어(Python, JavaScript, Ruby)에서는 필요한 메서드만 구현되어 있으면 어떤 객체든 사용할 수 있습니다. 이는 유연성을 크게 높여주지만, 런타임 에러의 위험도 있습니다.
Python에서 덕 타이핑은 핵심 철학입니다. `for` 문은 `__iter__` 메서드를 가진 모든 객체를 순회할 수 있고, `len()` 함수는 `__len__`을 구현한 모든 객체에 작동합니다. 이런 "프로토콜" 기반 접근은 상속 없이도 다형성을 구현할 수 있게 해줍니다.
TypeScript의 구조적 타이핑(Structural Typing)은 덕 타이핑의 정적 타입 버전으로 볼 수 있습니다. 명시적으로 인터페이스를 구현하지 않아도 필요한 속성과 메서드를 가지면 해당 타입으로 인정됩니다. 이는 Java의 명목적 타이핑(Nominal Typing)과 대조됩니다.
# Python에서의 덕 타이핑 예제
class Duck:
def walk(self):
return "뒤뚱뒤뚱"
def quack(self):
return "꽥꽥!"
class Person:
def walk(self):
return "걷는다"
def quack(self):
return "꽥꽥 흉내"
class Robot:
def walk(self):
return "삐빅 이동"
def quack(self):
return "QUACK 출력"
# 덕 타이핑: 타입이 아닌 메서드 존재 여부로 판단
def make_it_quack(duck_like):
"""walk와 quack 메서드만 있으면 어떤 객체든 OK"""
print(f"걷기: {duck_like.walk()}")
print(f"울음: {duck_like.quack()}")
# 모두 작동! 타입 상속 없이도 다형성 구현
for obj in [Duck(), Person(), Robot()]:
make_it_quack(obj)
print("---")
# 프로토콜 기반 덕 타이핑 (Python 표준)
class FilelikeObject:
"""read() 메서드만 있으면 파일처럼 사용 가능"""
def __init__(self, data):
self.data = data
self.position = 0
def read(self, size=-1):
if size == -1:
result = self.data[self.position:]
self.position = len(self.data)
else:
result = self.data[self.position:self.position + size]
self.position += size
return result
# 실제 파일이 아니어도 read()가 있으면 동작
import json
fake_file = FilelikeObject('{"name": "duck", "type": "bird"}')
data = json.load(fake_file) # 파일처럼 사용됨
print(data) # {'name': 'duck', 'type': 'bird'}
"이 함수가 리스트만 받을 필요 없어요. `__iter__`만 있으면 되니까 타입 힌트를 `Iterable[str]`로 바꾸면 제너레이터나 튜플도 받을 수 있습니다. 덕 타이핑의 장점을 살리면 더 유연한 API가 됩니다."
"TypeScript의 구조적 타이핑 덕분에 기존 JavaScript 코드를 점진적으로 마이그레이션할 수 있어요. 인터페이스를 implements로 명시 안 해도 구조만 맞으면 타입 체크가 통과해서, 레거시 코드와의 호환성이 좋습니다."
"목(Mock) 객체 만들 때 덕 타이핑이 편해요. 전체 클래스를 상속받지 않고 테스트에 필요한 메서드만 가진 객체를 만들면 됩니다. Python의 MagicMock이 이걸 잘 활용하고 있죠."
| 방식 | 언어 | 특징 | 체크 시점 |
|---|---|---|---|
| 덕 타이핑 | Python, Ruby, JS | 메서드 존재 여부로 판단 | 런타임 |
| 구조적 타이핑 | TypeScript, Go | 구조(shape)로 호환성 판단 | 컴파일 타임 |
| 명목적 타이핑 | Java, C#, C++ | 명시적 상속/구현 필요 | 컴파일 타임 |
💡 실무 팁: Python에서 타입 안전성이 필요하면 Protocol (PEP 544)을 사용하세요. 런타임 덕 타이핑의 유연성과 정적 타입 체크의 안전성을 모두 얻을 수 있습니다.
덕 타이핑은 컴파일 타임에 타입 오류를 잡지 못합니다. 필요한 메서드가 없는 객체를 전달하면 런타임에서야 AttributeError가 발생합니다. 중요한 코드에는 타입 힌트와 런타임 체크를 함께 사용하세요.
덕 타이핑에서 "어떤 메서드가 필요한지"가 암묵적이 되기 쉽습니다. Protocol 타입 힌트나 docstring으로 기대하는 인터페이스를 명확히 문서화하세요. 그렇지 않으면 유지보수가 어려워집니다.
같은 이름의 메서드가 다른 의미로 사용될 수 있습니다. `close()`가 파일 닫기인지, UI 창 닫기인지, 연결 종료인지 문맥에 따라 다릅니다. 의미적 호환성까지 고려해야 합니다.