파이썬(Python) 대용량 데이터 MemoryError: 제너레이터(Generator)로 해결하기

파이썬(Python) 대용량 데이터 MemoryError: 제너레이터(Generator)로 해결하기

"몇 년간 쌓인 20GB짜리 시스템 로그 파일을 분석하기 위해 파이썬 코드를 짰는데, 실행하자마자 컴퓨터 팬이 미친 듯이 돌더니 MemoryError를 뿜으며 스크립트가 죽어버렸습니다."

데이터 엔지니어링이나 백엔드 실무에서 빅데이터를 다루기 시작할 때 100% 확률로 마주하는 물리적 장벽입니다. 초보자들은 코드의 로직을 의심하기보다 당장 AWS EC2 인스턴스의 RAM(메모리) 용량을 올리는 스케일업(Scale-up)에 비용을 낭비합니다.

하지만 데이터는 항상 하드웨어의 발전 속도보다 빠르게 증식합니다. 메모리를 64GB로 늘리면 100GB 파일 앞에서 또다시 무너질 것입니다. 이 문제의 근본 원인은 데이터를 처리하는 파이썬의 '탐욕스러운(Eager)' 평가 방식에 있습니다. 본 가이드에서는 메모리 피크를 원천 차단하는 제너레이터(Generator) 아키텍처를 단호하게 제시합니다.

📌 이 글의 핵심 포인트

  • MemoryError의 본질: 모든 데이터를 한 번에 RAM에 올려버리는 리스트(List)의 한계
  • 지연 평가(Lazy Evaluation)의 개념: 필요할 때 필요한 만큼만 데이터를 가져오는 구조
  • yield 키워드를 활용한 순수 파이썬 제너레이터 기반 메모리 최적화 기법

1단계: 왜 메모리가 터지는가? (객체 오버헤드)

하드디스크에 저장된 20GB의 텍스트 파일은 텍스트 에디터로 열리지도 않을 만큼 큽니다. 이를 파이썬에서 readlines()나 리스트 컴프리헨션(List Comprehension)을 사용해 변수에 담는 순간, 파이썬은 이 텍스트들을 전부 '객체(Object)'로 변환합니다.

파이썬의 객체는 데이터 자체의 크기뿐만 아니라 참조 카운트(Reference Count), 타입 정보 등 부가적인 메타데이터를 포함하므로, 메모리에 올라오는 순간 파일 용량의 2~3배로 팽창합니다. 16GB 램을 가진 컴퓨터라면 순식간에 메모리가 고갈(OOM, Out Of Memory)되어 OS가 파이썬 프로세스를 강제 킬(Kill)해 버립니다.

2단계: 완벽한 트러블슈팅 - 제너레이터(Generator) 도입

가장 우아한 해결책은 데이터를 한 번에 메모리에 올리지 않고, 수도꼭지를 틀어 물을 한 컵씩 받아 마시듯 한 줄씩만 메모리에 올리고 버리는 것입니다. 이를 가능하게 하는 것이 파이썬의 yield 키워드입니다.

// ❌ 최악의 방식 (메모리 폭발): 리스트를 만들어 모든 라인을 한 번에 저장
def read_all_logs(file_path):
    with open(file_path, 'r') as file:
        # 파일 전체를 읽어 메모리에 거대한 배열 생성
        return [line.strip() for line in file]

// ✅ 제너레이터 방식 (메모리 0.01% 사용): 한 줄씩만 내보내고 상태를 일시정지
def generate_logs(file_path):
    with open(file_path, 'r') as file:
        for line in file:
            # 값을 반환하고 함수의 실행을 일시 정지(Lazy Evaluation)
            yield line.strip()

# 실제 사용 시: for문을 돌 때만 메모리에 한 줄씩 올라감
for log_line in generate_logs('massive_20gb_log.csv'):
    if "ERROR" in log_line:
        print("에러 발견:", log_line)

return 대신 yield를 사용하면 함수는 제너레이터 객체를 반환합니다. 이 객체는 데이터의 '흐름'만 제어할 뿐, 데이터를 미리 메모리에 적재하지 않으므로 20GB가 아니라 1TB의 데이터라도 램 1GB짜리 초소형 서버에서 무결점으로 처리할 수 있습니다.

🙋‍♂️ 자주 묻는 질문 (FAQ)

Q. 제너레이터를 쓰면 처리 속도가 느려지지 않나요?
A. 메모리에 한 번에 올리는 것보다는 디스크 I/O가 반복되어 미세하게 느릴 수 있습니다. 하지만 OOM 에러로 스크립트가 죽어버리는 것과, 메모리를 적게 쓰면서 끝까지 완주하는 것 중 선택해야 합니다. 실무에서는 무조건 후자가 정답입니다.

Q. Pandas로 대용량 CSV를 다룰 때도 제너레이터를 쓸 수 있나요?
A. Pandas에서는 read_csv() 함수에 chunksize=10000 옵션을 주면 제너레이터와 동일하게 데이터를 1만 줄씩 잘라서 읽어오는 이터레이터를 반환합니다.

💡 핵심 정리 및 마무리

빅데이터 시대에 모든 데이터를 메모리에 우겨넣으려는 시도는 물리적 한계에 부딪힙니다. 파이썬이 제공하는 지연 평가(Lazy Evaluation)의 철학을 코드로 구현하십시오. 제너레이터의 흐름을 이해하는 순간, 열악한 인프라 환경에서도 무한대에 가까운 데이터를 지배하는 강력한 무기를 얻게 될 것입니다.

댓글

이 블로그의 인기 게시물

Zapier & Make.com 자동화의 덫: 무한 루프(Infinite Loop) 에러 완벽 방어 아키텍처

엑셀 보고서의 종말: 구글 스프레드시트와 루커 스튜디오(Looker Studio)로 실시간 대시보드 구축하기

Docker OOMKilled (Exit Code 137) 에러의 진실: 컨테이너 메모 누수 방어 및 리소스 최적화 아키텍처