
파이썬은 동적 타이핑 언어로서의 유연함을 자랑하지만, 대규모 프로젝트나 엄격한 엔터프라이즈 환경에서는 그 유연함이 때로는 독이 되기도 합니다. 특히 클래스 내부에서 변수의 의도를 명확히 하고, 수정되어서는 안 될 값을 보호하는 것은 코드의 안정성을 결정짓는 핵심 요소입니다. 본 포스팅에서는 Python 3.8 이상에서 도입된 typing.Final과 클래스 네임스페이스 관리를 위한 typing.ClassVar의 깊이 있는 차이점을 분석하고, 런타임 강제성이 없는 파이썬 환경에서 이를 어떻게 실무적으로 활용하여 설계 결함을 해결할 수 있는지 다룹니다.
1. 상수와 클래스 변수의 설계 철학
전통적인 Java나 C++와 달리 파이썬은 언어 차원에서 private이나 constant를 물리적으로 강제하지 않습니다. 관례적으로 언더스코어(_)를 사용해 접근 제한을 표시해 왔으나, typing 모듈의 발전은 이를 정적 분석(Static Analysis) 단계에서 검증할 수 있게 만들었습니다.
Final: 재할당 금지의 선언
Final은 이름 그대로 해당 변수가 한 번 초기화되면 다시는 재할당될 수 없음을 명시합니다. 이는 클래스 속성뿐만 아니라 지역 변수, 전역 변수에도 적용될 수 있습니다.
ClassVar: 인스턴스와의 격리
ClassVar는 특정 변수가 개별 인스턴스(Instance)의 소유가 아닌, 클래스 자체의 소유임을 명확히 합니다. 이는 메모리 절약과 공유 상태 관리에 필수적입니다.
2. Final과 ClassVar의 핵심 차이 및 제약 사항 비교
두 타입 힌트는 목적 자체가 다르지만, 클래스 내부에서 상수를 정의할 때 혼동하기 쉽습니다. 아래 표를 통해 3가지 관점에서 차이를 정리했습니다.
| 비교 항목 | typing.Final | typing.ClassVar |
|---|---|---|
| 주요 목적 | 값의 재할당 방지 (상수화) | 인스턴스 변수와 클래스 변수 구분 |
| 적용 범위 | 변수, 메서드, 클래스 전체 | 클래스 속성 (Class Attribute) 한정 |
| 정적 검사 (Mypy) | 재할당 시 에러 발생 | 인스턴스를 통한 수정 시 경고 |
| 런타임 강제성 | 없음 (값 변경 가능) | 없음 (값 변경 가능) |
3. 실무적 제약 사항과 해결 방법
파이썬 타입 힌트 시스템에는 입문자가 반드시 알아야 할 '강제성의 부재'라는 제약이 있습니다. Final로 선언했더라도 런타임에서 obj.FINAL_VAR = new_value를 수행하면 파이썬 인터프리터는 아무런 에러를 내뱉지 않습니다.
해결책: Mypy와 프로퍼티의 조합
런타임에서도 값을 보호해야 한다면 @property 데코레이터를 사용하여 setter를 구현하지 않는 방식을 병행해야 합니다. Final은 개발 단계에서의 린팅(Linting)을 담당하고, 프로퍼티는 실제 실행 환경에서의 무결성을 담당하는 2중 방어 체계를 구축하는 것이 모범 사례입니다.
4. Sample Example: 실제 프로젝트 적용 사례
다음은 데이터베이스 연결 설정을 관리하는 클래스에서 두 기능을 혼합하여 사용하는 예시 코드입니다.
from typing import Final, ClassVar
class DatabaseConfig:
# 모든 인스턴스가 공유하는 클래스 변수
CONNECTION_COUNT: ClassVar[int] = 0
# 변경되어서는 안 되는 설정값 (Final)
TIMEOUT: Final[int] = 30
def __init__(self, db_name: str):
self.db_name: Final[str] = db_name # 인스턴스별로 한 번 설정 후 고정
DatabaseConfig.CONNECTION_COUNT += 1
# 올바른 사용
config = DatabaseConfig("Production_DB")
# Mypy에서 에러로 잡아내는 케이스
# config.TIMEOUT = 60 # Error: Cannot assign to final attribute
# config.db_name = "Dev_DB" # Error: Cannot assign to final attribute
5. 결론: 언제 무엇을 써야 하는가?
1. 설정값, 수학적 상수, 고정된 ID 등을 정의할 때는 Final을 사용하십시오.
2. 카운터, 공유 캐시, 설정 템플릿 등 모든 인스턴스가 동일한 주소를 참조해야 하는 데이터에는 ClassVar를 사용하십시오.
3. 만약 클래스 레벨에서 절대 변하지 않는 상수를 만들고 싶다면 ClassVar[Final[int]]와 같이 중첩하여 선언하는 것이 가장 완벽한 의도 전달 방법입니다.
출처 및 참고 문헌
- Python Software Foundation. (2024). PEP 591 – Adding a final qualifier to typing.
- Python Software Foundation. (2024). PEP 526 – Syntax for Variable Annotations.
- Mypy Documentation. "Final names, methods and classes".
- Real Python. "Python Type Checking (Guide)".
'Artificial Intelligence > 60. Python' 카테고리의 다른 글
| [PYTHON] 모든 객체의 뿌리, PyObject 헤더 구조의 2가지 핵심 요소와 메모리 관리 방법 (0) | 2026.03.15 |
|---|---|
| [PYTHON] 파이썬 id() 함수가 반환하는 메모리 주소의 3가지 비밀과 객체 식별 방법 (0) | 2026.03.15 |
| [PYTHON] Multipledispatch를 이용한 함수 오버로딩 구현 방법 3가지와 정적 언어와의 차이 해결 (0) | 2026.03.14 |
| [PYTHON] 클래스 변수와 인스턴스 변수의 3가지 차이점과 가려짐(Shadowing) 문제 해결 방법 (0) | 2026.03.14 |
| [PYTHON] 가변 인자(*args, **kwargs) 성능 오버헤드 3가지 측정 방법과 해결 전략 (0) | 2026.03.14 |