리소스를 다루다 보면 “열고 → 사용하고 → 닫는” 과정이 필요합니다.
대표적인 예시는 파일 입출력입니다.
만약 파일을 열고 닫는 작업을 직접 관리하면, 코드가 길어지고 예외 상황에서 리소스가 제대로 해제되지 않을 수 있습니다.
이때 유용한 것이 바로 컨텍스트 매니저(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__ 를 구현해 사용자 정의 컨텍스트 매니저도 만들 수 있다.
'파이썬 > 기초 프로그래밍' 카테고리의 다른 글
| 파이썬에서 내부 자원과 외부 리소스의 차이 (0) | 2025.09.13 |
|---|---|
| 파이썬 컨텍스트 매니저로 실행 시간 측정하기 (0) | 2025.09.13 |
| Python 객체 복사: Copy, Shallow Copy, Deep Copy (0) | 2025.09.13 |
| Python 함수 재사용: lambda보다 내부 함수 구조가 좋은 경우 (0) | 2025.09.13 |
| Python map 객체와 형 변환 (0) | 2025.09.13 |