Programming Languages/Python

캡슐화(Encapsulation)

newclass 2025. 3. 26. 04:05

캡슐화(Encapsulation)

1. 캡슐화란?

캡슐화는 객체의 속성과 메서드를 하나로 묶고, 실제 구현 내용의 일부를 외부에 감추는 것을 말합니다. 이를 통해 객체 내부 구현의 안정성을 높일 수 있습니다.

2. 접근 제어자

파이썬에서는 진정한 의미의 private 속성이나 메서드는 없지만, 네이밍 컨벤션을 통해 이를 표현합니다:

  • _변수명: 관례적으로 protected 멤버 (클래스 내부와 자식 클래스에서만 접근)
  • __변수명: 이름 맹글링(name mangling)을 통한 private 멤버 (클래스 내부에서만 접근)
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner        # 공개 속성
        self._balance = balance   # protected 속성
        self.__account_number = self.__generate_account_number()  # private 속성
    
    def __generate_account_number(self):  # private 메서드
        import random
        return random.randint(10000000, 99999999)
    
    def deposit(self, amount):
        if amount > 0:
            self._balance += amount
            return True
        return False
    
    def withdraw(self, amount):
        if 0 < amount <= self._balance:
            self._balance -= amount
            return True
        return False
    
    def get_balance(self):
        return self._balance
    
    def get_account_info(self):
        # private 속성은 클래스 내부에서 접근 가능
        return f"Owner: {self.owner}, Balance: {self._balance}, Account: ***{str(self.__account_number)[-4:]}"

# 인스턴스 생성
account = BankAccount("홍길동", 10000)

# 공개 메서드를 통한 상호작용
print(account.get_account_info())
account.deposit(5000)
print(f"Balance: {account.get_balance()}")

# protected 속성 접근 (가능하지만 권장하지 않음)
print(account._balance)  # 출력: 15000

# private 속성 직접 접근 시도 (오류 발생)
try:
    print(account.__account_number)
except AttributeError as e:
    print(f"오류 발생: {e}")

3. 프로퍼티(Property)

@property 데코레이터를 사용하면 메서드를 속성처럼 접근할 수 있습니다. 이를 통해 getter와 setter 함수를 정의할 수 있습니다.

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age
    
    @property  # getter
    def age(self):
        return self._age
    
    @age.setter  # setter
    def age(self, value):
        if value < 0:
            raise ValueError("나이는 음수가 될 수 없습니다.")
        self._age = value
    
    @property
    def name(self):
        return self._name

# 인스턴스 생성
person = Person("홍길동", 30)

# 프로퍼티를 통한 접근 (메서드이지만 속성처럼 사용)
print(person.age)  # getter 호출, 출력: 30

# 프로퍼티를 통한 설정 (메서드이지만 속성처럼 설정)
person.age = 35    # setter 호출
print(person.age)  # 출력: 35

# 유효성 검사
try:
    person.age = -5  # ValueError 발생
except ValueError as e:
    print(f"오류 발생: {e}")

# setter가 없는 프로퍼티는 읽기 전용
print(person.name)  # 출력: 홍길동
try:
    person.name = "김철수"  # AttributeError 발생
except AttributeError as e:
    print(f"오류 발생: {e}")