본문 바로가기
Dev/Python

파이썬 중급: 클래스 정의부터 파이썬다운 객체지향 특징까지 완벽 정리

by DevGyu0511 2026. 1. 15.
반응형

개요

복잡한 프로그램을 설계할 때 데이터와 기능을 체계적으로 묶어 관리하는 객체지향 프로그래밍(OOP)은 필수적인 요소이다. 파이썬은 모든 것이 객체로 이루어진 언어이며, Java나 C++ 같은 정적 타입 언어와 차별화되는 유연한 클래스 구조를 지니고 있다. 본 포스팅에서는 클래스의 기본 정의와 원형을 먼저 살펴보고, 파이썬만의 고유한 특징과 올바른 설계 주의점을 코드 예제와 함께 상세히 다룬다.


1. 클래스의 정의와 기본 원형

클래스는 객체를 생성하기 위한 '설계도'이다. 파이썬에서는 'class' 키워드를 사용하며, 객체의 탄생과 소멸을 관리하는 라이프사이클 콜백(생성자와 소멸자)을 지원한다.

클래스 기본 구조 및 라이프사이클

class Smartphone:
    # [생성자] 객체 생성 시 자동 호출 (Android의 onCreate와 유사)
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model
        print(f"{self.model} 인스턴스가 생성되었다.")

    # [일반 메서드] 객체의 동작 정의
    def info(self):
        return f"이 기기는 {self.brand}의 {self.model}이다."

    # [소멸자] 객체 소멸 시 호출 (Android의 onDestroy와 유사)
    def __del__(self):
        print(f"{self.model} 인스턴스가 메모리에서 해제되었다.")

# 인스턴스화
my_phone = Smartphone("Apple", "iPhone 15")
print(my_phone.info())

2. 파이썬 클래스만의 고유 특징

파이썬 클래스의 진가는 정적 언어의 틀을 깨는 유연함에 있다. 대표적인 두 가지 특징인 '동적 속성 바인딩'과 '덕 타이핑'을 살펴보자.

2.1. 동적 속성 바인딩 (Dynamic Binding)

파이썬은 런타임에 언제든 클래스 구조를 변경할 수 있다. 미리 정의되지 않은 속성을 즉석에서 추가하는 것이 가능하다.

class Developer:
    def __init__(self, name):
        self.name = name

dev = Developer("태식")

# 클래스 설계도에 없던 'language' 속성을 런타임에 추가
dev.language = "Python"
print(f"{dev.name}의 주력 언어는 {dev.language}이다.")

2.2. 덕 타이핑 (Duck Typing)

파이썬은 상속 관계나 특정 인터페이스의 구현 여부보다 '객체가 어떤 행동을 할 수 있는가'를 중시한다. "오리처럼 걷고 꽥꽥거린다면 오리라고 본다"는 철학이다.

[Image of Duck Typing concept in programming: if it walks like a duck and quacks like a duck, it is a duck]

class Robot:
    def move(self): print("로봇이 전진한다.")

class Person:
    def move(self): print("사람이 걷는다.")

# 상속 관계가 전혀 없어도 move() 메서드만 있다면 실행 가능하다.
def start_walking(obj):
    obj.move()

start_walking(Robot())
start_walking(Person())

3. 파이썬다운 설계 시 주의점 및 실전 예제

파이썬은 자유도가 높은 언어이다. 하지만 그 자유가 '스파게티 코드'가 되지 않도록 돕는 파이썬다운 설계 관례들을 지켜야 한다.

3.1. 명시적인 self 활용

파이썬은 메서드 정의 시 첫 번째 인자로 'self'를 반드시 명시해야 한다. 이는 현재 객체의 네임스페이스에 접근하기 위한 명확한 통로가 된다.

class Counter:
    def __init__(self):
        self.count = 0

    def increment(self):
        # self를 통해 인스턴스 변수에 명시적으로 접근한다.
        self.count += 1

3.2. 캡슐화 (Encapsulation): Consenting Adults와 Name Mangling

파이썬에는 강제적인 'private' 키워드가 없다. 대신 두 가지 수준의 속성 보호 방식을 사용한다.

  • Consenting Adults (싱글 언더바): 기술적으로 접근은 가능하나, 외부에서 건드리지 말라는 신뢰 기반의 신호이다.
  • Name Mangling (더블 언더바): 이름을 강제로 변형시켜 외부 접근을 어렵게 만든다.
class BankAccount:
    def __init__(self, balance, password):
        self._balance = balance      # Consenting Adults: 접근은 가능하나 자제 권고
        self.__password = password    # Name Mangling: 이름이 내부적으로 변형됨

account = BankAccount(1000, "1234")

# [참고] account._balance 에 직접 접근은 가능하나(에러 없음), 
# 이는 파이썬의 관례를 어기는 행위이며 유지보수성을 해친다.
print(account._balance) 

# 더블 언더바는 원래 이름으로 접근 시 AttributeError가 발생한다.
# print(account.__password) # AttributeError

3.3. 라이프사이클 관리와 자원 해제의 한계

객체의 소멸자( __del__ )는 가비지 컬렉터(GC)에 의해 호출 시점이 결정되므로, 자원 해제 시 주의가 필요하다.

class FileHandler:
    def __init__(self, filename):
        self.file = open(filename, 'w')

    def __del__(self):
        # GC 호출 시점이 불분명하여 자원 해제가 늦어질 수 있다.
        self.file.close()

# 권장 방식: with 구문(Context Manager)을 통한 명시적 자원 해제
with open("test.txt", "w") as f:
    f.write("명시적인 자원 관리가 파이썬다운 방식이다.")

3.4. 유연함 속의 절제: 단일 책임의 원칙 (SRP)

파이썬 클래스는 매우 유연하여 한 클래스에 너무 많은 기능을 담고 싶은 유혹에 빠지기 쉽다. 하지만 좋은 파이썬다운 코드는 '클래스가 오직 하나의 책임만 지도록' 분리하는 절제에서 시작된다. 이는 클린 코드의 핵심 원칙이자, 파이썬의 유연함을 유지보수성으로 승화시키는 비결이다.

# 역할을 분리하여 설계하는 것이 좋은코드이며, 파이썬 클래스가 확장이 유연하다고 하지만, 
# 과하면 클린코드에 어긋나므로 주의하자.
class User:
    def __init__(self, name):
        self.name = name

class UserRepository:
    def save(self, user):
        print(f"{user.name} 정보를 DB에 저장한다.")

Conclusion

파이썬의 클래스는 기초적인 객체 생성을 넘어 동적 바인딩과 덕 타이핑이라는 강력한 유연성을 제공한다. 캡슐화조차 강제보다는 개발자 간의 신뢰(Consenting Adults)를 중시하는 파이썬의 철학을 이해한다면, 더욱 견고하고 확장성 있는 프로그램을 구축할 수 있다.

오늘 학습한 클래스 설계 원칙라이프사이클 콜백을 프로젝트에 적용하여,
코드의 간결성안정성, 그리고 객체지향적 완성도까지 챙기는 개발자가 되어보자.

반응형