본문 바로가기

Programming/Python

[Python] Context Manager

파이썬 로고

Python에서의 리소스 관리

프로그래밍에서 말하는 리소스는 CPU, 메모리, 파일 핸들, 네트워크 소켓, DB connection등 한정 된 시스템 자원을 의마합니다. 파이썬에서는 가비지컬렉터가 동작하여 대부분의 객체는 사용이 종료되면 자동으로 메모리가 반환되고 있습니다.

하지만 파일 핸들링, 네트워크 연결, lock등은 명시적으로 리소스를 해제하지 않으면 시스템에 누수가 발생합니다.

이런 명시적 리소스 해제를 쉽게 처리하기 위해 파이썬에서는 Context manager를 사용합니다

Context Manager

Context Manager는 Python에서 리소스를 열고 닫는 과정을 자동화하는 패턴입니다. with 키워드를 이용하여 구현ㄴ되며, 리소스를 할당하고 해제하는 작업을 자동으로 처리해줍니다.

f = open("file.txt",'w')
try :
	f.write("Hello World")
finally:
	f.close()

파일에 텍스트를 쓰는 예제입니다. 위와 같은 기능을 구현한다고 했을 때 개발자가 신경을 쓰지 않으면 f.close()를 빠드리는 경우가 있고, 이럴 경우에 프로그램이 리소스를 계속 점유하고 있는 상태가 됩니다. 이러한 문제점을 해결하기 위해 파이썬에서는 아래와 같이 사용할 수 있습니다.

with open('file.txt','w') as f:
	f.write("Hello Wolrd")

with 문법을 사용하면 블록을 벗어났을 때 내부 동작을 통해 리소스가 무조건 해제됩니다.

개발자의 실수를 줄일 수 있고, 간단하게 리소스를 관리할 수 있습니다.

Context Manager 원리

Context Manager는 두 가지 메소드를 갖는 객체입니다.

  • __enter__ : with 블록 진입 시 실행 ( 리소스 획득 )
  • __exit__: 블록 종료 시 실행 ( 리소스 해제 )

동작 순서는

1) __enter__ 실행을 통해 리소스 획득

2) 블록 내부 코드 실행 

3) 블록 종료 ( 정상 동작 완료 or Exception 발생 )

4) __exit__ 호출을 통해 리소스 해제

Context Manager 직접 구현

위에서 설명한 __enter__와 __exit__ 매직 메소드를 이용하여 Context Manager를 직접 구현할 수 있습니다.

class FileManager:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
        self.file = None  # 나중에 open()으로 열릴 핸들을 저장할 변수

    def __enter__(self):
        print(f"[ENTER] 파일 {self.filename} 열기")
        self.file = open(self.filename, self.mode)
        return self.file  # with 블록의 as 변수로 전달됨

    def __exit__(self, exc_type, exc_value, traceback):
        print(f"[EXIT] 파일 {self.filename} 닫기")
        if self.file:
            self.file.close()
        # False를 반환하면 예외를 그대로 밖으로 전달함
        # True를 반환하면 예외를 무시
        return False

# 사용 예
with FileManager('test.txt', 'w') as f:
    f.write("Hello, custom context manager!\n")
    print("파일에 데이터 쓰기 완료!")
    
# [ENTER] 파일 test.txt 열기
# 파일에 데이터 쓰기 완료!
# [EXIT] 파일 test.txt 닫기

FileManager 클래스를 정의하고 with 구문을 통해 호출해보았습니다.

1.  __init__ 에서는 파일의 이름과 실행 모드를 매개변수로 받아 FileManager 객체가 생성됩니다.

2. with 블록에 진입하면서 __enter__함수가 호출되고, print문 출력 후에 파일을 입력받은 이름과 실행모드로 open합니다. 

3. with 블록 내부에서 파일 핸들로 파일에 쓰기 작업을 실행합니다.

4. __exit__ 는 블록이 끝날 때 호출되고, print문 출력 후에 file을 닫습니다.

위와 같이 매직메소드를 통해 Context Manger를 직접 구현하는 것도 가능합니다.

 

다양한 Context Manager 사용 예제

1. 파일 핸들 관리

# 텍스트 파일 쓰기
with open('example.txt', 'w') as f:
    f.write("Hello, Context Manager!\n")

# 텍스트 파일 읽기
with open('example.txt', 'r') as f:
    data = f.read()
    print(data)

 

2. 네트워크 세션

import requests

with requests.Session() as session:
    response = session.get('https://httpbin.org/get')
    print(response.status_code)

 

3. 데이터베이스 연결과 커서 관리

import sqlite3

# SQLite 예제
conn = sqlite3.connect(':memory:')

with conn:
    conn.execute('CREATE TABLE demo (id INTEGER PRIMARY KEY, name TEXT)')
    conn.execute('INSERT INTO demo (name) VALUES (?)', ('Alice',))

# 커서도 with로 안전하게 사용 가능
with conn:
    with conn.cursor() as cur:  # 일부 DB 라이브러리는 지원 (sqlite3 커서는 X)
        cur.execute('SELECT * FROM demo')
        print(cur.fetchall())

conn.close()

 

4. 쓰레드 락

import threading

lock = threading.Lock()

with lock:
    print("락 획득 후 안전한 작업 수행")

 

5. 직접 만든 사용자 정의 Context Manager

class MyResource:
    def __enter__(self):
        print("리소스 연결!")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("리소스 해제!")

with MyResource():
    print("작업 수행 중")

'Programming > Python' 카테고리의 다른 글

[Python] 메타클래스(Meta Class) 이해하기  (7) 2025.07.05
[Python] Async I/O 정리  (13) 2025.06.21
[Python] Future 이해하기  (2) 2025.06.20
[Python] 코루틴(Coroutine) 이해하기  (13) 2025.06.19
[Python] 클로저 (Closure)  (0) 2025.06.17