30. Python에서의 캡슐화 – 정보 은닉을 위한 접근 지정

안녕하세요. 언휴예요.

OOP의 주요 특징인 캡슐화는 여러 가지 멤버를 하나의 형식으로 묶는 과정을 말하며 클래스를 통해 구현합니다. 특히 클래스에 멤버를 정의할 때 어느 영역에서 해당 멤버에 접근할 수 있는지를 지정할 수 있게 하여 주요 멤버에 외부에서 접근하여 잘못된 상태가 만들어지지 않게 만들 수 있게 지원합니다. 이러한 특징은 접근 지정을 통한 정보 은닉이라고 부릅니다.

예를 들어 유닛의 멤버 필드 중에 체력이 0에서 100 사이를 유지한다고 가정합시다. 그리고 운동하면 체력이 1 증가하고 술 마시면 1 감소하게 작성했고요. 그런데 유닛을 사용하는 부분에서 유닛의 체력 멤버 필드에 직접 접근하여 값을 변경한다면 이상한 값(0보다 작거나 100보다 큰 값)을 갖을 수도 있습니다. 이러한 논리적 버그를 방지하려면 유닛을 정의할 때 외부에서 체력 멤버 필드에 직접 접근하는 것을 허용하지 말아야 합니다.

이러한 이유로 많은 OOP 언어에서는 멤버의 접근 지정을 설정할 수 있는 키워드(흔히 public, private, protected)를 제공하고 있어요.

하지만 Python에서는 멤버의 접근 지정을 설정하는 키워드를 제공하고 있지 않습니다. Python에서 클래스를 정의할 때 멤버 이름에 따라 접근 지정을 설정할 수 있어요.

멤버 이름 앞에 언더바(_)가 두 개 붙으면 해당 형식 내부에서만 접근이 가능합니다. 이는 다른 OOP 언어의 private 접근 지정과 같습니다.

멤버 이름 앞에 언더바(_)가 한 개 붙으면 해당 형식과 파생 형식에서 접근이 가능합니다. 이는 다른 OOP 언어의 protected 접근 지정과 같습니다. 이 부분은 상속을 다루면서 설명할게요.

멤버 이름 앞에 언더바가 없다면 외부에서도 접근이 가능합니다. 이는 다른 OOP 언어의 public 접근 지정과 같습니다.

유닛 클래스를 정의하면서 접근 지정에 관해 살펴봅시다.

#접근 지정

class Unit:

초기화 메서드는 이미 이름이 __init__으로 정해졌으며 개체를 생성할 때 수행합니다. 여기에서는 이름을 입력 인자로 전달받아 이름 멤버 필드에 설정하고 체력 멤버 필드는 100으로 설정하기로 합시다. 메서드를 정의할 때 첫번째 인자로 self 키워드를 명시하면 해당 메서드는 멤버 메서드입니다. 그리고 self는 개체 자신을 의미하며 개체의 멤버에 접근할 때 self 키워드를 이용합니다. 이름 멤버 필드와 체력 멤버 필드는 Unit 클래스 내부에서만 접근할 수 없게 멤버 이름의 시작 부분에 언더바(_)를 명시할게요.

    def __init__(self,name):
        self.__name = name
        self.__hp=100

만약 def __init__(self, name): 처럼 초기화 메서드를 지정하면 호출할 때는 name부분만 전달하며 self부분은 전달하는 것이 아니라 개체 자신을 의미합니다. 다음은 이름이 “홍길동”인 Unit 개체를 생성하는 코드입니다.

unit = Unit("홍길동")

만약 외부에서 멤버 필드에 접근할 필요가 있다면 접근자 메서드를 제공하세요. 외부에서 멤버 필드를 설정할 필요가 있다면 설정자 메서드를 제공하세요. 여기에서는 유닛의 체력 멤버 필드와 이름 멤버 필드에 접근하는 접근자는 외부에서 접근할 수 있게 제공하세요.

    def GetHP(self):
        return self.__hp
    def GetName(self):
        return self.__name

유닛의 체력은 운동하면 늘어나고 술 마시면 줄어들게 할거예요. 이 때 체력은 0에서 100 사이에 있게 조절해 주어야 하는데 체력이 바뀌는 곳마다 이를 조절하는 코드를 작성하는 것은 귀찮을 뿐만 아니라 개발자의 실수로 조절하는 부분을 넣지 않아 버그가 만들어질 수 있어요.

되도록이면 멤버 필드 값을 설정하는 부분은 설정자 메서드를 정의하고 멤버 필드 값을 변경하길 원한다면 설정자 메서드를 호출하여 변경하게 구현하세요.

    def __SetHP(self, hp):
        if(hp<0):
            hp = 0
        if(hp>100):
            hp = 100
        self.__hp = hp
    def Play(self,hour):
        print(hour,"시간 운동하다.")
        self.__SetHP(self.__hp+hour)
    def Drink(self, cups):
        print(cups,"잔 마시다.")
        self.__SetHP(self.__hp-cups)

다음은 접근 지정을 설명하기 위해 정의한 Unit 클래스와 이를 사용하는 코드입니다.

#접근 지정

class Unit:
    def __init__(self,name):
        self.__name = name
        self.__hp=100

    def GetHP(self):
        return self.__hp

    def GetName(self):
        return self.__name

    def __SetHP(self, hp):
        if(hp<0):
            hp = 0
        if(hp>100):
            hp = 100
        self.__hp = hp

    def Play(self,hour):
        print(hour,"시간 운동하다.")
        self.__SetHP(self.__hp+hour)

    def Drink(self, cups):
        print(cups,"잔 마시다.")
        self.__SetHP(self.__hp-cups)


unit = Unit("홍길동")
print("유닛 이름:{0} 체력:{1}".format(unit.GetName(), unit.GetHP()))
unit.Drink(3)
print("유닛 이름:{0} 체력:{1}".format(unit.GetName(), unit.GetHP()))
unit.Play(5)
print("유닛 이름:{0} 체력:{1}".format(unit.GetName(), unit.GetHP()))

개발 도구에서 Unit 클래스 내부에서는 모든 멤버가 보입니다. 하지만 Unit 클래스 외부에서는 언더바(_)로 시작하는 멤버는 보이지 않습니다.

[그림 1] 접근 지정 확인
[그림 1] 접근 지정 확인