본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] 내부 동작의 핵심 : Frame Object와 실행 컨텍스트의 3가지 밀접한 관계와 구조적 차이 해결

by Papa Martino V 2026. 3. 1.
728x90

Frame Object
프레임 오브젝트 (Frame Object)

 

파이썬(Python)은 표면적으로는 매우 쉬운 언어처럼 보이지만, 그 내부(Internal)로 들어가면 CPython 인터프리터가 코드를 실행하기 위해 복잡한 관리 체계를 가동하고 있음을 알 수 있습니다. 개발자가 함수를 호출하거나 루프를 돌릴 때, 메모리 상에서는 어떤 일이 벌어질까요? 단순히 변수가 저장되는 것을 넘어, 실행 흐름을 제어하는 실행 컨텍스트(Execution Context)와 이를 물리적으로 구현한 프레임 오브젝트(Frame Object)의 메커니즘을 이해하는 것이 고성능 최적화와 디버깅의 핵심입니다. 본 포스팅에서는 파이썬 인터프리터 레벨에서 코드의 생명주기를 결정짓는 프레임 오브젝트의 구조와 실행 컨텍스트와의 상관관계를 심층 분석합니다.


1. 실행 컨텍스트(Execution Context)란 무엇인가?

실행 컨텍스트는 코드가 실행되기 위해 필요한 '환경 정보'의 집합입니다. 파이썬 인터프리터는 코드를 실행할 때 현재 어떤 변수들이 유효한지(Scope), 어떤 함수가 호출되었는지, 그리고 다음에 실행할 바이트코드는 무엇인지를 추적해야 합니다. 이 추상적인 개념을 실행 컨텍스트라고 부릅니다.

파이썬에서 컨텍스트는 크게 세 가지 수준으로 나뉩니다:

  • Global Context: 모듈 수준에서 실행되는 코드의 환경.
  • Function Context: 함수가 호출될 때마다 생성되는 독립적인 환경.
  • Eval/Exec Context: 동적으로 실행되는 코드의 환경.

2. 프레임 오브젝트(Frame Object, PyFrameObject)의 실체

실행 컨텍스트가 논리적인 개념이라면, 프레임 오브젝트(Frame Object)는 이를 파이썬 메모리 상에 구현한 실제 C 구조체(PyFrameObject)입니다. 파이썬의 각 함수 호출은 하나의 '프레임'을 생성하며, 이 프레임들은 스택(Stack) 구조로 쌓이게 됩니다.

프레임 오브젝트의 주요 구성 요소

  1. f_back: 이전 프레임(호출자)에 대한 참조입니다. 이를 통해 콜 스택(Call Stack)이 형성됩니다.
  2. f_code: 실행될 실제 바이트코드가 담긴 코드 객체(Code Object)입니다.
  3. f_locals: 지역 변수들이 저장되는 딕셔너리입니다.
  4. f_globals: 전역 변수들이 저장되는 딕셔너리입니다.
  5. f_lasti: 마지막으로 실행된 바이트코드의 인덱스(Instruction Pointer)입니다.

3. 프레임 오브젝트와 실행 컨텍스트의 3가지 핵심 관계 및 차이 해결

많은 개발자들이 이 두 개념을 혼용하지만, 엄밀히 말하면 '관리 주체'와 '데이터 구조'라는 차이가 있습니다. 아래 표를 통해 그 구조적 차이를 명확히 정리합니다.

[비교] 실행 컨텍스트 vs 프레임 오브젝트

구분 실행 컨텍스트 (Execution Context) 프레임 오브젝트 (Frame Object)
개념적 성격 논리적 추상화 (실행 환경 그 자체) 물리적 구현체 (PyFrameObject 구조체)
생성 시점 코드 블록(함수 등)이 진입될 때 인터프리터가 스택 프레임을 할당할 때
주요 역할 식별자 해결(Scope Resolution) 및 흐름 제어 메모리 할당, 로컬 변수 저장, 상태 보존
가시성 언어 사양(Spec)상의 개념 sys._getframe() 등을 통해 직접 접근 가능

이들의 관계를 요약하자면, "프레임 오브젝트는 실행 컨텍스트를 담는 물리적인 그릇"이라고 정의할 수 있습니다. 함수가 호출되면 인터프리터는 새로운 실행 컨텍스트를 설정하기 위해 메모리에 프레임 오브젝트를 'Push'하고, 함수가 종료되면 해당 프레임을 'Pop'하여 컨텍스트를 이전 상태로 복구합니다.


4. Sample Example: sys 모듈을 활용한 프레임 추적

파이썬의 sys 모듈을 사용하면 현재 실행 중인 컨텍스트의 프레임 오브젝트에 직접 접근하여 내부 정보를 들여다볼 수 있습니다. 이는 고성능 로깅 시스템이나 디버거를 설계할 때 필수적인 방법입니다.


import sys

def secondary_function(a, b):
    # 현재 실행 중인 컨텍스트의 프레임 오브젝트 획득
    current_frame = sys._getframe()
    
    print(f"--- [Inside {current_frame.f_code.co_name}] ---")
    print(f"현재 프레임 로컬 변수: {current_frame.f_locals}")
    
    # 상위 컨텍스트(caller)의 프레임 접근
    caller_frame = current_frame.f_back
    print(f"호출한 함수 이름: {caller_frame.f_code.co_name}")
    print(f"호출한 곳의 전역 변수 개수: {len(caller_frame.f_globals)}")

def primary_function():
    x = 10
    y = 20
    secondary_function(x, y)

if __name__ == "__main__":
    primary_function()

코드 해설

위 예제에서 secondary_function이 실행되는 순간, 메모리에는 module -> primary_function -> secondary_function 순서로 프레임 오브젝트가 스택에 쌓입니다. f_back 속성을 활용하면 상위 실행 컨텍스트로 거슬러 올라가 호출자의 상태를 확인할 수 있으며, 이는 파이썬이 예외 발생 시 트레이스백(Traceback)을 생성하는 기본 원리가 됩니다.


5. 프레임 오브젝트 활용 시 주의사항: 메모리 누수 해결 방법

프레임 오브젝트는 강력하지만 주의해서 다뤄야 합니다. 프레임 오브젝트는 실행 중인 컨텍스트의 모든 로컬 변수를 참조(Reference)하고 있습니다. 만약 예외 객체나 전역 변수에 프레임 오브젝트를 저장하고 명시적으로 삭제하지 않으면, 가비지 컬렉터(GC)가 해당 컨텍스트의 변수들을 해제하지 못해 메모리 누수(Memory Leak)가 발생할 수 있습니다.

해결 방법: sys._getframe()이나 inspect 모듈로 얻은 프레임 객체는 사용 직후 del frame_obj를 통해 참조를 제거하거나, 함수 범위 내에서만 일시적으로 사용하는 것이 권장됩니다.


6. 결론: 왜 프레임과 컨텍스트를 알아야 하는가?

파이썬 개발자가 중급에서 고급으로 넘어가는 분기점은 '코드의 동작'을 넘어 '인터프리터의 관리 방식'을 이해하는 시점입니다. 프레임 오브젝트와 실행 컨텍스트의 관계를 이해하면 다음과 같은 이점이 있습니다.

  • 재귀 함수 최적화: 스택 깊이(Recursion Limit)와 프레임 생성 비용을 고려한 설계가 가능해집니다.
  • 고급 디버깅: 단순한 Print 출력을 넘어, 런타임에 호출 스택을 분석하여 복잡한 버그의 근원을 찾을 수 있습니다.
  • 메타 프로그래밍: 데코레이터나 컨텍스트 매니저를 만들 때, 상위 스코프의 변수를 안전하게 제어할 수 있습니다.

결국 파이썬의 모든 실행은 프레임이라는 단위로 쪼개어 관리되며, 우리가 작성하는 모든 코드 한 줄은 이 거대한 실행 컨텍스트의 파편임을 인지해야 합니다.


참고 문헌 (Sources)

  • Python Software Foundation. "Python Data Model - Frame Objects." Python 3.x Documentation.
  • Guido van Rossum. "The Python Standard Library - sys module."
  • Real Python. "Python Behind the Scenes: How the Python Interpreter Works."
  • CPython Source Code (Include/frameobject.h, Objects/frameobject.c).
728x90