파이썬으로 프로그램을 만들다 보면, 어떤 변수는 특정 코드 블록에서만 작동하고 어떤 변수는 프로그램 전체에서 작동하는 것을 보게 됩니다. 이러한 변수의 유효 범위를 이해하는 것은 예상치 못한 버그를 방지하고 깔끔한 코드를 작성하는 데 중요합니다. 이번 절에서는 파이썬의 지역 변수와 전역 변수 개념을 명확히 알아보고, 변수 스코프의 원리를 파악해 보겠습니다.
변수 스코프란 무엇인가요?
변수의 유효 범위, 즉 스코프는 코드 내에서 변수에 접근할 수 있는 영역을 의미합니다. 제가 현업에서 코드를 작성할 때, 스코프를 제대로 이해하지 못해 엉뚱한 변수가 수정되거나 접근할 수 없는 변수 때문에 오류가 발생하는 경우를 종종 겪습니다. 파이썬은 변수가 선언된 위치에 따라 접근 가능한 범위가 달라지며, 이는 프로그램의 동작을 예측하는 데 결정적인 역할을 합니다. 마치 회사의 부서별 역할과 권한처럼, 변수도 자신만의 활동 영역이 있는 것이지요.
지역 변수(Local Variable)의 개념과 활용
지역 변수는 함수 내에서 선언되며, 해당 함수 안에서만 유효하고 함수 실행이 끝나면 사라집니다. 예를 들어, 함수를 정의하고 호출할 때 함수 내부에서 선언한 변수는 오직 그 함수 내부에서만 사용 가능합니다. 이는 함수가 독립적인 작업을 수행하고 외부 코드에 영향을 주지 않도록 하여 코드의 재사용성과 안정성을 높이는 장점이 있습니다. 함수가 종료되면 해당 지역 변수에 할당된 메모리는 자동으로 해제됩니다.
지역 변수 코드 예제
다음 코드를 통해 지역 변수의 동작 방식을 살펴보겠습니다.
def calculate_sum(a, b):
result = a + b # result는 calculate_sum 함수의 지역 변수
print(f"함수 내부: {result}")
calculate_sum(10, 20)
# print(result) # 에러 발생: NameError: name 'result' is not defined
이 코드에서 `result` 변수는 `calculate_sum` 함수 내부에서 정의되었으므로, 함수 바깥에서는 접근할 수 없습니다. 함수 호출 후 `print(result)`를 실행하면 `NameError`가 발생하는 것을 확인할 수 있습니다. 저도 처음 파이썬을 배울 때 이 부분에서 혼란을 겪으며 디버깅 시간을 많이 썼던 기억이 납니다.
전역 변수(Global Variable)의 개념과 활용
전역 변수는 어떤 함수 밖에서 선언되며, 프로그램의 모든 부분에서 접근하고 읽을 수 있습니다. 즉, 전역 스코프에 정의된 변수는 어느 함수에서든 참조할 수 있어, 여러 함수에서 공유해야 하는 데이터를 다룰 때 유용합니다. 하지만 전역 변수를 남용하면 코드의 복잡성이 증가하고, 예상치 못한 부작용을 일으킬 수 있으므로 신중하게 사용해야 합니다. 특히 여러 함수가 동시에 전역 변수를 수정할 경우, 데이터의 흐름을 추적하기 어려워지는 문제가 발생할 수 있습니다.
전역 변수 코드 예제
전역 변수의 예시를 살펴보겠습니다.
global_message = "안녕하세요, 저는 전역 변수입니다." # 전역 변수
def greet():
print(f"함수 내부에서 접근: {global_message}")
greet()
print(f"함수 외부에서 접근: {global_message}")
이 예제에서 `global_message`는 함수 밖에서 선언된 전역 변수입니다. `greet` 함수 내부와 외부 모두에서 성공적으로 접근하여 출력하는 것을 볼 수 있습니다. 이처럼 전역 변수는 프로그램의 넓은 영역에서 사용될 수 있습니다.
함수 내에서 전역 변수 수정하기: `global` 키워드
함수 내부에서 전역 변수의 값을 변경하려면 `global` 키워드를 사용하여 해당 변수가 전역 변수임을 명시해야 합니다. 파이썬은 기본적으로 함수 내부에서 변수에 값을 할당하면 그 변수를 지역 변수로 간주합니다. 이 때문에 함수 안에서 전역 변수와 이름이 같은 변수에 값을 할당하면, 새로운 지역 변수가 생성되어 전역 변수에는 영향을 미치지 않습니다. 전역 변수를 직접 수정하고자 할 때 이 `global` 키워드가 필요합니다.
`global` 키워드 사용 예제
`global` 키워드의 중요성을 보여주는 두 가지 상황을 비교해 보겠습니다.
# 상황 1: global 키워드 없이 전역 변수와 이름이 같은 지역 변수 생성
count = 0 # 전역 변수
def increment_wrong():
count = 1 # 새로운 지역 변수 count 생성, 전역 변수에 영향 없음
print(f"increment_wrong 함수 내부: {count}")
increment_wrong()
print(f"increment_wrong 함수 호출 후 전역 count: {count}") # 여전히 0
# 상황 2: global 키워드를 사용하여 전역 변수 수정
count_global = 0 # 전역 변수
def increment_correct():
global count_global # count_global이 전역 변수임을 명시
count_global += 1 # 전역 변수 count_global 수정
print(f"increment_correct 함수 내부: {count_global}")
increment_correct()
increment_correct()
print(f"increment_correct 함수 호출 후 전역 count_global: {count_global}") # 2
첫 번째 상황에서는 함수 내부의 `count = 1`이 새로운 지역 변수를 만들었기 때문에 전역 `count`는 0으로 유지됩니다. 하지만 두 번째 상황에서는 `global count_global`을 사용하여 함수 내부에서도 전역 `count_global` 변수를 직접 수정할 수 있었습니다. 제가 자동화 스크립트를 만들 때, 설정 값을 전역 변수로 두고 함수에서 이를 업데이트해야 할 때 `global`을 사용하곤 했습니다. 하지만 저는 이런 경우에도 매개변수와 인자를 사용하는 방식을 더 선호합니다.
스코프 해결 순서: LEGB 규칙
파이썬은 변수를 찾을 때 지역(Local) -> 둘러싼 함수(Enclosing) -> 전역(Global) -> 내장(Built-in) 순서로 스코프를 탐색합니다. 이를 LEGB 규칙이라고 부르며, 파이썬이 어떤 변수에 접근해야 할지 결정하는 기본 원칙입니다. 변수를 찾을 때 이 순서를 따르기 때문에, 이름이 같은 변수가 여러 스코프에 존재할 경우 어떤 변수가 사용될지 예측할 수 있게 됩니다.
- L (Local): 현재 함수 내의 스코프입니다. 가장 먼저 탐색합니다.
- E (Enclosing function locals): 중첩 함수가 있을 때, 바깥 함수의 스코프입니다. (이 책의 수준을 고려하여 자세한 설명은 생략합니다.)
- G (Global): 모듈 전체의 스코프입니다. 파일의 최상위에 정의된 변수들이 여기에 해당합니다.
- B (Built-in): 파이썬이 기본으로 제공하는 내장 함수나 상수의 스코프입니다. `print`, `len`, `True` 등이 여기에 속합니다.
x = 10 # 전역 (Global)
def outer_function():
x = 20 # 둘러싼 함수 (Enclosing)의 지역
def inner_function():
# x = 30 # 만약 이 줄이 있다면 Local
print(f"inner_function 안의 x: {x}") # Enclosing의 x (20)를 참조
inner_function()
print(f"outer_function 안의 x: {x}") # Enclosing의 x (20)를 참조
outer_function()
print(f"전역 x: {x}") # Global의 x (10)를 참조
위 예시에서 `inner_function`은 `x`를 직접 정의하지 않았으므로, 한 단계 위인 `outer_function`의 지역 변수 `x` (값 20)를 참조하게 됩니다. 이처럼 LEGB 규칙은 파이썬이 변수를 어떻게 찾아내고 사용하는지를 명확하게 보여줍니다.
지역 변수와 전역 변수, 현명하게 사용하는 팁
전역 변수를 과도하게 사용하면 코드의 예측 가능성이 떨어지고 유지보수가 어려워지므로, 함수 매개변수를 통해 데이터를 전달하는 것이 바람직합니다. 제 경험상, 프로그램의 규모가 커질수록 전역 변수는 잠재적인 버그의 원인이 될 수 있습니다. 저는 가능한 한 변수의 스코프를 좁게 유지하려고 노력합니다. 함수 간에 데이터를 주고받을 때는 변수를 선언하고 할당하는 기본 원칙에 따라 매개변수를 사용하여 전달하고, 반환값을 통해 결과를 돌려받는 것이 가장 좋습니다. 이는 각 함수가 독립적으로 작동하고, 어떤 데이터에 접근하고 변경하는지 명확하게 보여주어 코드의 가독성과 테스트 용이성을 높여줍니다. 전역 변수는 정말로 프로그램 전반에 걸쳐 상수처럼 사용되거나, 특정 초기 설정값처럼 변경될 가능성이 적은 경우에 한정하여 사용하는 것이 좋습니다. 예를 들어, 제가 GUI 앱을 개발할 때 애플리케이션의 메인 윈도우 객체처럼 항상 접근해야 하는 객체는 전역적으로 관리할 때도 있지만, 최소한으로 사용합니다.
마무리하며
지금까지 파이썬의 지역 변수와 전역 변수, 그리고 변수 스코프의 개념에 대해 자세히 살펴보았습니다. 변수의 스코프를 이해하는 것은 파이썬 프로그래밍의 기본이자, 오류 없는 견고한 코드를 작성하기 위한 필수적인 지식입니다. 여러분의 파이썬 학습 여정에 이 지식이 큰 도움이 되기를 바랍니다. 다음 절에서는 파이썬의 핵심 기능 중 하나인 람다 함수에 대해 알아보겠습니다.
FAQ
Q1: 지역 변수와 전역 변수가 같은 이름을 가질 수 있나요?
네, 가능합니다. 하지만 함수 내부에서는 지역 변수가 전역 변수보다 우선권을 가집니다. 즉, 함수 내에서 이름이 같은 지역 변수를 선언하면, 그 함수 안에서는 지역 변수가 사용되고 전역 변수는 가려집니다.
Q2: `global` 키워드를 자주 사용하는 것이 좋은가요?
가능하면 `global` 키워드의 사용을 최소화하는 것이 좋습니다. `global` 키워드를 남용하면 프로그램의 데이터 흐름을 예측하기 어려워지고 디버깅이 복잡해질 수 있습니다. 함수는 매개변수로 데이터를 받고 반환값으로 결과를 돌려주는 방식으로 설계하는 것이 코드의 응집도를 높이고 독립성을 유지하는 데 유리합니다.
Q3: 지역 변수는 언제 사라지나요?
지역 변수는 해당 변수를 포함하는 함수가 실행을 마칠 때 사라집니다. 함수 호출이 끝나면 지역 변수에 할당된 메모리가 해제되어 더 이상 접근할 수 없게 됩니다.
참고 자료
- [1] Python 공식 튜토리얼 — 제어 흐름 도구와 함수 정의 — https://docs.python.org/ko/3/tutorial/controlflow.html
- [2] Python 용어집 — https://docs.python.org/ko/3/glossary.html
