본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] Monkey Patching의 위험성 3가지 해결 방법과 유닛 테스트 활용의 차이

by Papa Martino V 2026. 4. 7.
728x90

Monkey Patching의 위험성
Monkey Patching의 위험성

 

파이썬은 그 유연함 덕분에 실행 시간(Runtime)에 코드의 동작을 수정할 수 있는 강력한 기능을 제공합니다. 그 중심에 있는 기법이 바로 몽키 패칭(Monkey Patching)입니다. 하지만 "큰 힘에는 큰 책임이 따른다"는 말처럼, 몽키 패칭은 적절한 전략 없이 사용할 경우 전체 시스템의 안정성을 해치고 원인을 알 수 없는 버그를 양산하는 양날의 검이 됩니다. 오늘 이 글에서는 몽키 패칭의 본질적인 위험성을 분석하고, 이를 안전하게 대체하거나 관리할 수 있는 전문적인 해결 방안을 심도 있게 다룹니다.


1. Monkey Patching이란 무엇인가?

몽키 패칭은 원래 소스 코드를 수정하지 않고 런타임에 모듈, 클래스, 또는 함수의 속성을 교체하거나 확장하는 기법을 말합니다. 주로 외부 라이브러리의 버그를 긴급히 수정해야 하거나, 테스트 환경에서 실제 네트워크 통신 대신 가짜 응답(Mocking)을 반환하게 할 때 사용됩니다. 파이썬의 동적 특성상 모든 객체는 런타임에 수정 가능하므로 이 기법이 널리 쓰이고 있습니다.


2. 몽키 패칭 vs. 표준 확장 방식의 기술적 차이 비교

코드의 유지보수성과 예측 가능성 측면에서 몽키 패칭과 일반적인 상속/데코레이터 방식은 큰 차이를 보입니다.

비교 항목 몽키 패칭 (Monkey Patching) 표준 확장 (Inheritance/Decorator)
수정 시점 런타임 (프로그램 실행 중) 디자인 타임 (코드 작성 시)
영향 범위 전역적 (해당 모듈을 사용하는 모든 곳) 국소적 (명시적으로 사용한 클래스/함수만)
가독성 및 추적 매우 낮음 (디버깅 시 소스와 실제 동작이 다름) 높음 (IDE에서 추적 가능)
적합한 사례 긴급 버그 수정, 유닛 테스트용 모킹 일반적인 기능 확장 및 비즈니스 로직

3. 몽키 패칭의 3가지 핵심 위험성

3.1. 예측 불가능한 부작용(Side Effects)

몽키 패칭은 전역 네임스페이스를 오염시킵니다. 라이브러리 A를 패칭했는데, 이를 전혀 모르는 라이브러리 B가 A를 참조할 때 예기치 못한 크래시가 발생할 수 있습니다.

3.2. 업그레이드 호환성 결여

외부 라이브러리의 내부 메서드를 패칭해 두면, 해당 라이브러리가 업데이트되어 내부 로직이 바뀔 때 패칭 코드가 작동하지 않거나 시스템을 중단시키는 원인이 됩니다.

3.3. 디버깅 지옥(Debugging Hell)

로그에는 에러가 발생했다고 찍히는데, 소스 코드상에서는 아무리 봐도 논리적 결함이 없는 경우가 발생합니다. 런타임에 누군가 함수를 바꿔치기했기 때문입니다.


4. 올바른 대안 및 해결 방법

4.1. 유닛 테스트 시 unittest.mock 활용

가장 흔한 몽키 패칭 사례는 테스트입니다. 이때 수동으로 함수를 바꾸는 대신, 파이썬 표준 라이브러리의 patch 데코레이터를 사용하여 테스트 종료 후 자동으로 원상복구 되도록 설계해야 합니다.

4.2. 어댑터(Adapter) 패턴 도입

외부 라이브러리의 동작이 마음에 들지 않는다면 몽키 패칭 대신, 해당 라이브러리를 감싸는 어댑터 클래스를 만들어 원하는 동작을 구현하십시오. 이것이 의존성 역전 원칙(DIP)을 준수하는 방법입니다.


5. Sample Example: 안전한 패칭과 위험한 패칭의 차이

아래 예제는 전역 상태를 망가뜨리는 방식과 테스트 환경에서 안전하게 활용하는 방식의 대조를 보여줍니다.

[위험한 사례] 전역 상태 직접 수정


import requests

# 1. requests.get을 통째로 바꿔치기 (매우 위험)
def fake_get(*args, **kwargs):
    print("Monkey Patched!")
    return "Fake Data"

requests.get = fake_get 

# 이후 시스템 전체에서 requests.get은 정상 동작하지 않음

[해결 사례] unittest.mock을 이용한 일시적 패칭


from unittest.mock import patch
import requests

def get_status(url):
    response = requests.get(url)
    return response.status_code

# context manager를 사용하여 해당 블록 내에서만 패칭 수행
with patch('requests.get') as mock_get:
    mock_get.return_value.status_code = 200
    
    status = get_status("https://example.com")
    print(f"Status: {status}") # 결과: 200

# 블록을 나가면 requests.get은 다시 원래 상태로 복구됨

6. 결론: 몽키 패칭은 최후의 수단이어야 합니다

몽키 패칭은 고성능의 도구이지만, 그만큼 시스템을 불투명하게 만듭니다. 대규모 프로젝트일수록 런타임 수정보다는 구조적 아키텍처 개선을 통해 문제를 해결해야 합니다. 꼭 사용해야 한다면 그 범위를 최소화하고, 충분한 문서화와 주석을 통해 다른 개발자가 혼란에 빠지지 않도록 배려하는 설계 전략이 필수적입니다.


내용 출처 및 참고 문헌

  • Python Documentation: "unittest.mock — mock object library" (2026)
  • Stack Overflow: "What is monkey patching?" - Community Discussion
  • Fluent Python by Luciano Ramalho: "Dynamic Attributes and Properties"
728x90