본문 바로가기
카테고리 없음

컨텍스트 매니저와 with문 제대로 쓰기

by python note 2025. 6. 9.

파이썬 with문 사용법 - 컨텍스트 매니저로 리소스 자동 관리하기

파이썬으로 프로그래밍을 할 때, 파일을 열거나 네트워크에 연결하는 등의 작업을 자주 하게 됩니다. 이런 작업들은 사용 후에는 반드시 '닫아주는' 과정이 필요한데요. 만약 이 과정을 잊어버리면 컴퓨터에 문제가 생길 수 있습니다. 이번 시간에는 파이썬의 with 문과 컨텍스트 매니저를 통해 이런 번거로운 '뒷정리'를 파이썬이 알아서 해주도록 만드는 방법을 배워보겠습니다. 마치 정리 정돈을 잘하는 비서가 항상 여러분의 뒤를 봐주는 것과 같습니다.

with 문은 코드를 더 안전하고 간결하게 만들어주며, 여러분이 복잡한 프로그램을 만들 때 큰 도움이 될 것입니다.

with문과 컨텍스트 매니저란 무엇일까요?

컨텍스트 매니저(Context Manager)는 특정 작업을 시작할 때 필요한 준비 작업(예: 파일 열기)과 작업이 끝난 후 필요한 마무리 작업(예: 파일 닫기)을 자동으로 처리해주는 파이썬의 특별한 기능입니다. 이 컨텍스트 매니저를 사용할 때 쓰는 문법이 바로 with 문입니다.
with 문을 사용하면 코드를 더 짧고 읽기 쉽게 만들 수 있을 뿐만 아니라, 중요한 '자원(리소스)'을 빠뜨리지 않고 정리할 수 있어 프로그램이 더 안정적으로 작동하게 됩니다. 마치 놀이터에서 놀기 전에 안전모를 쓰고(준비) 놀고 난 후에는 안전모를 벗어 제자리에 두는(마무리) 것을 자동으로 해주는 것과 같습니다.

with문 사용 예시 1: 파일 자동 닫기 (가장 흔한 예시)

파이썬에서 파일을 다룰 때, 파일을 열면 반드시 닫아주어야 합니다. 만약 파일을 닫지 않으면 다른 프로그램이 그 파일을 사용하지 못하거나, 데이터가 손상될 수도 있습니다. with 문을 사용하면 파이썬이 파일을 자동으로 닫아주기 때문에 이런 걱정을 할 필요가 없습니다.

Python
# with 문 없이 파일을 다룰 경우: 반드시 close()를 호출해야 합니다. file = open("my_story.txt", "w", encoding="utf-8") # "w"는 쓰기 모드입니다. try: file.write("옛날 옛적에...\n") file.write("아주 먼 옛날에 파이썬이라는 언어가 있었습니다.") finally: file.close() # 파일을 반드시 닫아야 합니다. # with 문을 사용하여 파일을 다룰 경우: close()를 명시적으로 호출할 필요가 없습니다. with open("my_story_with_with.txt", "w", encoding="utf-8") as f: # 'f'는 파일 객체를 나타내는 이름입니다. f.write("with 문은 정말 편리해요.\n") f.write("자동으로 파일을 닫아주니까요!") # with 블록을 벗어나면 파일 f는 자동으로 닫힙니다.

위 코드를 실행하면 두 개의 텍스트 파일(my_story.txt, my_story_with_with.txt)이 생성됩니다. 중요한 점은 with 문을 사용한 경우, 개발자가 직접 close()를 호출하지 않아도 파이썬이 알아서 파일을 닫아준다는 것입니다. 오류가 발생해도 파일을 안전하게 닫아주기 때문에 프로그램이 더 튼튼해집니다.

출력 결과 (파일 내용)
my_story.txt: 옛날 옛적에... 아주 먼 옛날에 파이썬이라는 언어가 있었습니다. my_story_with_with.txt: with 문은 정말 편리해요. 자동으로 파일을 닫아주니까요!

with문 사용 예시 2: 직접 컨텍스트 매니저 만들기 (클래스)

with 문과 함께 사용되는 객체를 컨텍스트 매니저라고 부릅니다. 이런 객체는 __enter__()__exit__()라는 특별한 함수(메서드)를 가지고 있습니다. __enter__()with 문이 시작될 때 호출되고, __exit__()with 블록이 끝날 때(오류가 나든 안 나든) 호출됩니다. 이를 이용해서 나만의 컨텍스트 매니저를 만들 수 있습니다.

Python
class MyContextManager: def __init__(self, name): self.name = name print(f"[{self.name}] 컨텍스트 매니저가 준비되었습니다.") def __enter__(self): print(f"[{self.name}] 작업을 시작합니다!") return self.name # 'as' 뒤의 변수에 전달될 값 def __exit__(self, exc_type, exc_val, exc_tb): print(f"[{self.name}] 작업을 마칩니다!") if exc_type: # 오류가 발생했다면 print(f"[{self.name}] 오류가 발생했습니다: {exc_val}") return False # 오류를 다시 발생시킵니다 (True로 하면 오류를 무시). # 컨텍스트 매니저 사용 예시 print("--- 첫 번째 with 블록 (정상 종료) ---") with MyContextManager("제1작업") as manager_name: print(f"현재 작업: {manager_name} 진행 중!") # 여기에 실제 작업 코드가 들어갑니다. print("\n--- 두 번째 with 블록 (오류 발생) ---") try: with MyContextManager("제2작업") as manager_name: print(f"현재 작업: {manager_name} 진행 중!") raise ValueError("일부러 오류 발생!") # 의도적으로 오류를 발생시킵니다. except ValueError as e: print(f"외부에서 오류를 처리했습니다: {e}")

위 코드를 실행하면, with 문이 시작될 때와 끝날 때, 심지어 오류가 발생했을 때도 미리 정해둔 작업(__enter____exit__)이 자동으로 실행되는 것을 확인할 수 있습니다. 이렇게 하면 어떤 상황에서도 자원 정리가 보장됩니다.

출력 결과
[제1작업] 컨텍스트 매니저가 준비되었습니다. --- 첫 번째 with 블록 (정상 종료) --- [제1작업] 작업을 시작합니다! 현재 작업: 제1작업 진행 중! [제1작업] 작업을 마칩니다! [제2작업] 컨텍스트 매니저가 준비되었습니다. --- 두 번째 with 블록 (오류 발생) --- [제2작업] 작업을 시작합니다! 현재 작업: 제2작업 진행 중! [제2작업] 작업을 마칩니다! [제2작업] 오류가 발생했습니다: 일부러 오류 발생! 외부에서 오류를 처리했습니다: 일부러 오류 발생!

with문 사용 예시 3: 직접 컨텍스트 매니저 만들기 (함수 - contextlib)

클래스를 만들지 않고 함수를 이용해서 더 간단하게 컨텍스트 매니저를 만들 수도 있습니다. 이때는 파이썬의 contextlib 모듈에 있는 @contextmanager 데코레이터를 사용합니다. 함수 안에 yield 키워드를 기준으로 앞부분은 with 문 진입 시, 뒷부분은 with 문 탈출 시 자동으로 실행됩니다.

Python
import os from contextlib import contextmanager # 작업 디렉토리를 임시로 변경하는 컨텍스트 매니저 @contextmanager def change_directory(new_path): old_path = os.getcwd() # 현재 디렉토리 경로 저장 os.chdir(new_path) # 새로운 디렉토리로 변경 (진입 시) print(f"새로운 작업 디렉토리: {os.getcwd()}") try: yield # 이 지점에서 with 블록 안의 코드가 실행됩니다. finally: os.chdir(old_path) # 원래 디렉토리로 복원 (탈출 시) print(f"원래 작업 디렉토리로 돌아왔습니다: {os.getcwd()}") # 현재 디렉토리 확인 print(f"처음 시작 디렉토리: {os.getcwd()}") # 임시 디렉토리를 만들어 작업 if not os.path.exists("temp_dir"): os.makedirs("temp_dir") # temp_dir 폴더가 없으면 만듭니다. with change_directory("temp_dir"): print("temp_dir 안에서 작업 중입니다.") with open("temp_file.txt", "w") as f: f.write("임시 파일 내용") print("임시 파일 생성 완료.") # 여기에서 오류가 발생해도 finally 블록이 실행됩니다. print(f"최종 현재 디렉토리: {os.getcwd()}") # 임시 파일 및 디렉토리 정리 (선택 사항) import shutil if os.path.exists("temp_dir"): shutil.rmtree("temp_dir") print("temp_dir 폴더 삭제 완료.")

위 예제는 작업 디렉토리(현재 작업하는 폴더)를 임시로 바꾸었다가, 작업이 끝나면 원래 디렉토리로 자동으로 돌아오게 하는 컨텍스트 매니저입니다. @contextmanager 데코레이터와 yield 키워드를 통해 클래스보다 더 간결하게 컨텍스트 매니저를 만들 수 있습니다.

출력 결과
처음 시작 디렉토리: /path/to/your/current/directory 새로운 작업 디렉토리: /path/to/your/current/directory/temp_dir temp_dir 안에서 작업 중입니다. 임시 파일 생성 완료. 원래 작업 디렉토리로 돌아왔습니다: /path/to/your/current/directory 최종 현재 디렉토리: /path/to/your/current/directory temp_dir 폴더 삭제 완료.

중요:

with 문은 파일을 다루거나 네트워크 연결을 할 때처럼 '열고 닫는' 절차가 필요한 자원(리소스)을 안전하게 관리하는 데 사용됩니다. as 키워드 뒤에 오는 변수는 __enter__() 메서드가 반환하는 값을 받습니다.

주의:

with 문은 모든 파이썬 객체에 사용할 수 있는 것이 아닙니다. 오직 컨텍스트 매니저 프로토콜(즉, __enter____exit__ 메서드를 가진)을 따르는 객체에만 적용할 수 있습니다. 이미 파이썬에 내장된 많은 함수(예: open())나 라이브러리 객체들은 컨텍스트 매니저로 작동하도록 설계되어 있습니다.

핵심 : with 문은 컨텍스트 매니저와 함께 사용하여 리소스(파일, 네트워크 등)를 안전하고 자동으로 관리하게 해줍니다. 이를 통해 코드는 더 간결해지고, 오류 발생 시에도 뒷정리가 확실히 보장됩니다. 클래스의 __enter____exit__ 메서드, 또는 contextlib.contextmanager 데코레이터를 이용해 나만의 컨텍스트 매니저를 만들 수 있습니다.

마무리 요약

이번 시간에는 파이썬에서 with 문과 컨텍스트 매니저를 사용하는 방법을 배웠습니다. 이를 통해 파일을 다루는 등 리소스 관리의 중요성과 함께, 코드를 더 간결하고 안전하게 작성할 수 있다는 것을 알게 되었습니다. with 문은 파이썬 개발에서 매우 유용하게 쓰이는 문법이니 꼭 익혀두시기 바랍니다. 다음 강의에서는 파이썬의 오류와 예외 처리에 대해 더 깊이 배워보겠습니다.

질문이 있다면 댓글에 남겨주세요.