덕 타이핑(Duck Typing) (260522)

WonTerry·2026년 5월 22일

Python

목록 보기
10/20

"파이썬에서는 어떤 객체가 fly라는 메서드를 가지고 있다면, 그 객체가 새(Bird) 클래스인지 비행기(Airplane) 클래스인지 상관하지 않고 일단 실행해 봅니다."


덕 타이핑(Duck Typing)은 객체의 실제 타입(클래스)이 무엇인지보다, "그 객체가 어떤 메서드나 행동을 할 수 있는가"를 기준으로 타입을 검사하는 동적 타이핑 언어(파이썬, 자바스크립트 등)의 철학입니다.

이 개념은 아래의 유명한 구절에서 유래되었습니다.

"만약 어떤 새가 오리처럼 걷고, 오리처럼 깩깩거리며 운다면, 나는 그 새를 오리라고 부를 것이다."
(If it walks like a duck and quacks like a duck, I call it a duck.)


쉽게 이해하는 비유

  • 일반적인 타입 검사 (정적 타이핑): "주민등록증을 보여주세요. 신분증에 '오리'라고 적혀 있어야만 입장할 수 있습니다." (객체의 혈통이나 클래스 이름을 따짐)
  • 덕 타이핑 (동적 타이핑): "신분증은 필요 없고, 제 앞에서 오리처럼 뒤뚱뒤뚱 걷고 깩깩 울어보세요. 할 줄 안다면 당신은 오리입니다." (객체의 실제 능력을 따짐)

코드 예시로 보는 덕 타이핑

파이썬에서는 어떤 객체가 fly라는 메서드를 가지고 있다면, 그 객체가 새(Bird) 클래스인지 비행기(Airplane) 클래스인지 상관하지 않고 일단 실행해 봅니다.

class Duck:
    def quack(self):
        print("깩깩!")

class Person:
    def quack(self):
        print("인간이 오리 소리를 냅니다: 깩깩!")

# 객체의 타입을 검사하지 않고, 오직 'quack' 메서드가 있는지만 보고 실행합니다.
def make_it_quack(animal):
    animal.quack()

duck = Duck()
person = Person()

make_it_quack(duck)    # 출력: 깩깩!
make_it_quack(person)  # 출력: 인간이 오리 소리를 냅니다: 깩깩!

make_it_quack 함수는 입력받은 값이 Duck 클래스의 인스턴스인지 검사하지 않습니다. 오직 quack()이라는 행동을 할 수 있는가?만 중요하게 여깁니다. Person 객체도 quack() 메서드를 가지고 있으므로 이 함수 안에서는 오리처럼 취급됩니다.


앞서 배운 Mapping과의 연결

우리가 처음에 다룬 isinstance(data, Mapping)도 파이썬이 덕 타이핑을 안전하게 쓰기 위해 도입한 방식(구조적 서브타이핑)입니다.

어떤 객체가 실제 dict 클래스를 상속받지 않았더라도, 내부적으로 getitem(대괄호 조회), iter(반복), len(길이)이라는 메서드를 가지고 있어서 딕셔너리처럼 행동할 수 있다면 파이썬은 이를 Mapping으로 인정해 줍니다.


실무에서 가장 흔하게 마주치는 '결제 시스템(Payment System)'을 예로 들어보겠습니다. 왜 덕 타이핑이 강력하고 실무에서 유용하게 쓰이는지 확실히 와닿으실 겁니다.


💡 실무 상황 가정

여러분의 서비스에서 원래 토스페이(Toss)로만 결제를 처리하고 있었습니다. 그런데 회사가 성장하면서 카카오페이(Kakao)와 페이코(Payco) 결제 수단을 급하게 추가해야 하는 상황이 왔습니다.

❌ 덕 타이핑을 안 썼을 때 (경직된 코드)

덕 타이핑을 모르면 함수나 클래스 내부에서 toss_client, kakao_client 처럼 특정 결제사 타입인지 일일이 검사(Type Checking)해야 합니다.

class TossPay:
    def process_toss(self, amount):
        print(f"토스페이로 {amount}원 결제 완료")

class KakaoPay:
    def process_kakao(self, amount):
        print(f"카카오페이로 {amount}원 결제 완료")

# [문제의 결제 처리 함수] 새로운 결제사가 추가될 때마다 함수 내부가 엉망이 됩니다.
def pay_service(payment_method, amount):
    if isinstance(payment_method, TossPay):
        payment_method.process_toss(amount)
    elif isinstance(payment_method, KakaoPay):
        payment_method.process_kakao(amount)
    else:
        raise TypeError("지원하지 않는 결제 수단입니다.")

이 방식의 문제점: 네이버페이가 추가되면 또 elif isinstance(payment_method, NaverPay): 코드를 추가해야 합니다. 결제 처리 로직을 담은 핵심 코드가 계속 수정되어 버그가 날 확률이 높아집니다.


⭕ 덕 타이핑을 활용했을 때 (유연하고 실무적인 코드)

덕 타이핑을 활용하면 객체의 실제 타입(토스냐, 카카오냐)은 전혀 신경 쓰지 않습니다. 오직 "결제 기능을 하는 메서드(pay)가 있는가?"만 봅니다.

각 결제사 클래스의 메서드 이름을 pay로 통일해 줍니다.

class TossPay:
    def pay(self, amount):
        print(f"토스페이로 {amount}원 결제")

class KakaoPay:
    def pay(self, amount):
        print(f"카카오페이로 {amount}원 결제")

class Payco:
    def pay(self, amount):
        print(f"페이코로 {amount}원 결제")

# [덕 타이핑을 적용한 결제 처리 함수]
def pay_service(payment_method, amount):
    # payment_method가 어떤 클래스인지 검사하지 않습니다.
    # "오리처럼 결제(pay)할 수 있으면 오리(결제 수단)다!"
    payment_method.pay(amount)

이제 새로운 결제 수단인 NaverPay나 ApplePay가 추가되더라도, pay_service 함수는 단 한 줄도 수정할 필요가 없습니다. 그저 pay(amount) 메서드를 가진 클래스를 새로 만들어 전달하기만 하면 알아서 척척 돌아갑니다.

# 새로 추가된 네이버페이
class NaverPay:
    def pay(self, amount):
        print(f"네이버페이로 {amount}원 결제")

# 기존 결제 함수를 그대로 사용 가능! (확장성 폭발)
pay_service(NaverPay(), 50000)
# 출력: 네이버페이로 50000원 결제

🛠️ 실제 파이썬 내장 기능에서의 덕 타이핑 예시

실무에서 파일에 데이터를 쓰는 코드를 짤 때도 우리는 자신도 모르게 덕 타이핑을 쓰고 있습니다.

파이썬의 print() 함수나 어떤 데이터를 저장하는 함수들은 입력받은 객체가 "진짜 하드디스크에 있는 파일 객체(_io.TextIOWrapper)"인지 검사하지 않습니다. 오직 .write() 메서드를 가지고 있는가?만 봅니다.

# 1. 진짜 파일 객체 (write 메서드가 있음)
with open("log.txt", "w") as f:
    f.write("로그 데이터")

# 2. 파일이 아니라 메모리 공간 (하지만 write 메서드가 있음)
import io
memory_buffer = io.StringIO()
memory_buffer.write("메모리 데이터") 

# 3. 네트워크 전송 소켓 (하지만 write 메서드가 있게 커스텀 가능)

이 덕분에 우리는 데이터를 파일에 저장하는 기능을 만들었다가, 코드를 거의 안 고치고 그대로 메모리나 네트워크(네이버 클라우드 등)로 전송 방향을 바꿀 수 있습니다. 인터페이스(.write()나 .pay())만 맞춰주면 파이썬은 군말 없이 실행해 주기 때문입니다.


3줄 요약

  1. 덕 타이핑 전: "너 토스 클래스 맞지? 그럼 토스 결제 메서드 실행할게." (수정이 어렵고 빽빽함)
  2. 덕 타이핑 후: "니가 토스든 카카오든 상관없어. pay() 메서드 가지고 있지? 그럼 실행한다." (유연하고 확장에 열려있음)
  3. 결론: 객체의 정체(Type)보다 객체의 행동(Method)을 중심에 두고 코드를 짜는 기법입니다.
profile
Hello, I'm Terry! 👋 Enjoy every moment of your life! 🌱 My current interests are Signal processing, Machine learning, Python, Database, LLM & RAG, MCP & ADK, Multi-Agents, Physical AI, ROS2...

0개의 댓글