파이썬/기초 프로그래밍

파이썬 컨텍스트 매니저(Context Manager) 완전 정리

Data Jun 2025. 9. 13. 16:15

리소스를 다루다 보면 “열고 → 사용하고 → 닫는” 과정이 필요합니다.
대표적인 예시는 파일 입출력입니다.

만약 파일을 열고 닫는 작업을 직접 관리하면, 코드가 길어지고 예외 상황에서 리소스가 제대로 해제되지 않을 수 있습니다.
이때 유용한 것이 바로 컨텍스트 매니저(Context Manager) 입니다.

 

1. 전통적인 방식 (try-finally)

file = open('./testfile.txt', 'w')
try:
    file.write('Context Manager Test\n Contextlib Test!')
finally:
    file.close()
  • open() 으로 파일을 열고
  • finally 블록에서 반드시 닫아줘야 합니다.
  • 예외가 발생하더라도 file.close() 가 실행됩니다.

 

2. 컨텍스트 매니저 사용 (with)

with open('./testfile2.txt','w') as f:
    f.write('Context Manager Test2\n Contextlib Test!')
  • with 구문을 사용하면 __enter__ 시점에 파일을 열고,
  • 블록이 끝날 때 자동으로 __exit__ 가 호출되어 파일을 닫습니다.
  • try-finally 패턴을 간단하게 표현할 수 있죠.

 

3. 사용자 정의 컨텍스트 매니저 만들기

컨텍스트 매니저는 내부적으로 __enter____exit__ 메서드를 이용합니다.

class MyFileWriter():
    def __init__(self, file_name, method):
        print('MyFileWriter started : __init__'
        # 파일 열기 (리소스 할당))
        self.file_obj = open(file_name, method)

    def __enter__(self):
		# with 블록 진입 시점에 실행됨
        # 여기서 return 되는 객체가 as f 로 전달됨  
        print('MyFileWriter started : __enter__')
        return self.file_obj

    def __exit__(self, exc_type, value, trace_back):
   		# with 블록을 빠져나올 때 실행됨
        # 정상 종료든, 예외 발생이든 무조건 호출됨
        print('MyFileWriter started : __exit__')
        
        # exc_type: 발생한 예외의 클래스 (예: ValueError, TypeError 등)
        # value: 예외 메시지 객체
        # trace_back: 예외가 발생한 위치와 콜스택 정보
        if exc_type:  
            print('Logging exception {}'.format((exc_type, value, trace_back)))
        # 마지막에 리소스 해제
        self.file_obj.close()

# 사용
with MyFileWriter('./testfile3.txt', 'w') as f:
    f.write('Context Manager Test3\n Contextlib Test!')

 

출력 흐름:

MyFileWriter started : __init__
MyFileWriter started : __enter__
MyFileWriter started : __exit__​
  • __enter__ : with 블록에 진입 시점에 실행됩니다.
    • 여기서 반환한 값이 as f 에 바인딩됩니다.
  • __exit__ : with 블록을 빠져나올 때 무조건 실행됩니다.
    • 예외가 발생하지 않았다면 exc_type, value, trace_back 는 모두 None.
    • 예외가 발생했다면:
      • exc_type → 예외의 타입 (예: <class 'ValueError'>)
      • value → 예외 메시지 (예: "division by zero")
      • trace_back → 예외가 발생한 코드의 실행 흐름 정보

즉, exc_type 이 있다는 건, with 블록 안에서 예외가 발생했다는 뜻입니다

 

 

4. ASCII 도식으로 보는 흐름

1️⃣ 전통적인 try-finally 구조

 ┌───────────────┐
 │ open()        │
 └──────┬────────┘
        │
        ▼
  리소스 사용 (write 등)
        │
        ▼
 ┌──────┴────────┐
 │ finally: close│
 └───────────────┘

2️⃣  컨텍스트 매니저 (with)

with MyFileWriter(...) as f:
    ┌───────────────┐
    │ __enter__     │  ← 리소스 할당 (open)
    └──────┬────────┘
           │
           ▼
     리소스 사용
           │
           ▼
    ┌──────┴────────┐
    │ __exit__      │  ← 리소스 반환 (close)
    └───────────────┘

 

5. 예외 상황 처리까지 자동화

컨텍스트 매니저의 장점은 예외가 발생해도 안전하게 리소스를 반환한다는 점입니다.

with MyFileWriter('./testfile3.txt', 'w') as f:
    raise ValueError("테스트 에러")

출력:

MyFileWriter started : __init__
MyFileWriter started : __enter__
Logging exception (<class 'ValueError'>, ValueError('테스트 에러'), <traceback object>)
MyFileWriter started : __exit__

 

 

 

정리하면

  • 컨텍스트 매니저는 __enter__ 와 __exit__ 메서드로 동작한다.
  • 파일, DB 연결, 소켓 같은 리소스를 안전하게 관리할 수 있다.
  • with 구문을 쓰면 코드가 간결해지고 예외 상황에도 안정적이다.
  • 필요하다면 __enter__, __exit__ 를 구현해 사용자 정의 컨텍스트 매니저도 만들 수 있다.