객체지향 프로그래밍에서 클래스를 정의하는 중요하게 적용되는 특징 4가지가 있다. 캡슐화(Encapsulation), 정보은닉(Information Hiding), 상속(Inheritance), 그리고 다형성(Polymorphism) 이다.

 

이번 장에서는 두번째 특징인 정보은닉 개념과 클래스에서 정보은닉이 필요한 의미를 이해한다. 또한, 캡슐화된 클래스에서 정보를 보호하는 코드를 파이썬에서 구현한다. 

정보은닉(Information Hiding) 개념

정보은닉(Information Hiding)은 정보 보호라고도 번역되어 사용되는데, 클래스가 가지고 있는 정보 즉, 속성데이타나 메서드 구현의 세부 정보를 클래스 외부에서 접근하지 못하도록 숨긴다는 의미입니다. 여기서 접근하지 못한다는 의미는 속성값을 클래스 외부 코드에서 임의로 수정하지 못한다는 의미입니다.

 

예를 들어 고양이 클래스의 고양이 품종 속성(cat_breed)를 정보은닉을 하지 않은 경우와 한 경우를 비교해보면 아래의 두가지 case와 같습니다. 

 

 

 

 

Case 1)을 코드로 구현하면 아래와 같은데, Cat 클래스의 인스턴스 Nabi, Momo, Coco는 cat_breed를 "Domestic Short Hair", "Korean Short Hair" 그리고 줄여서 만든 "KoShort"을 규칙에 상관없이 모두 쓸 수 있습니다. 이는 Cat 클래스의 cat_breed의 데이타 규칙이 무너짐을 의미합니다. 

class Cat:
    def __init__(self, name):
        self.name = name
        self.cat_breed = None 
        
# 공개(public) 속성 (직접 접근 가능)
my_cat1 = Cat("Nabi")
my_cat2 = Cat("Momo")
my_cat3 = Cat("Coco")

# 속성에 직접 접근하여 값 변경
my_cat1.cat_breed = "Domestic Short Hair"
my_cat2.cat_breed = "Korean Short Hair"
my_cat3.cat_breed = "KoShort"

# 검증 없이 그대로 저장됨
print("my_cat1 품종:", my_cat1.cat_breed)
print("my_cat2 품종:", my_cat2.cat_breed)
print("my_cat3 품종:", my_cat3.cat_breed)

 

Case 2)를 코드로 구현하면 아래와 같습니다. _allowed_breeds라는 클래스 수준의 변수에 품종명을 저장하고, cat_breed 속성을 update를 하기 위한 setter를 set_cat_breed() 로 정의합니다. Cat 클래스 외부에서 Cat 클래스의 인스턴스 Nabi, Momo, Coco를 만들어 각 인스턴스의 품종을 수정하려면, set_cat_breed( ) 함수 (setter)를 호출해 매개변수를 통해 품종을 입력합니다.

 

그러면,  set_cat_breed( ) 함수에서 입력된 품종값(breed_type)dl _allowed_breeds에 정의된 품종인지를 확인하여 update처리 하거나 fail을 리턴합니다. 이러한 방법으로 Cat 클래스의 품종 속성은 그 데이타가 일관되게 관리될 수 있습니다.

class Cat:
    # 허용 가능한 품종 목록 (클래스 상수처럼 사용)
    _allowed_breeds = ["Domestic Short Hair", "Persian", "Siamese", "Maine Coon"]
    
    def __init__(self, name):
        self.name = name
        self._cat_breed = None # 직접 접근 방지를 위해 _ 사용
    
    def set_cat_breed(self, breed_type):
        # 품종을 규칙에 맞게 설정
        if breed_type in Cat._allowed_breeds:
            self._cat_breed = breed_type
            print(f"[SUCCESS] {self.name} 품종이 '{breed_type}'로 설정되었습니다.")
            return True
        else:
            print(f"[FAIL] '{breed_type}'는 허용되지 않는 품종입니다.")
            return False

my_cat1 = Cat("Nabi")
my_cat2 = Cat("Momo")
my_cat3 = Cat("Coco")

my_cat1.set_cat_breed("Domestic Short Hair")
my_cat2.set_cat_breed("Korean Short Hair")  # 실패 케이스
my_cat3.set_cat_breed("KoShort")            # 실패 케이스

print("my_cat1 품종:", my_cat1._cat_breed)
print("my_cat2 품종:", my_cat2._cat_breed)
print("my_cat3 품종:", my_cat3._cat_breed)

 

 

접근 지정자 (Access Modifier) 

객체지향 설계에서는 클래스의 정보 보호와 무결성 유지를 위해 접근 지정자(Access Modifier)를 정의고, 접근 범위를 명시함으로써 데이터의 성격과 설계 의도에 따라 공개 정보와 내부 보호 정보를 구분합니다. 

 

접근 지정자는 크게 public, protected, private으로 정의하며 특징은 아래와 같습니다.

종류 설명
public 어디서든 접근 가능  (클래스 내부/외부)
protected 해당 클래스서브클래스에서 접근 가능
private 해당 클래스 내부에서 접근 가능

 

객체지향 언어에서는 일반적으로 접근지정자를 제공하여 접근 지정자의 종류에 따라 프로그래밍 언어에서 규칙에 맞지 않게 사용하는 경우 에러메시지를 출력하여 제재를 합니다. 하지만 파이썬의 경우는 이러한 제제가 없고, 개발자에게 자유도를 더 줍니다. 

 

아래는 객체지향 프로그래밍 언어의 종류에 따른 접근지정자의 사용 예 입니다. C++과 JAVA에서는 private, public 키워드를 정의하고 이에 맞춰 속성과 메서드를 정의한 후 private을 클래스 외부에서 사용한 경우 컴파일러에서 에러메시지를 출력합니다. 

하지만 파이썬의 경우 강제적인 제재가 없습니다. 

 

 

 

 

파이썬에서의 접근 지정자 사용

public, protected, private으로 정의된 접근지정자의 종류에 따라 파이썬에서 코드를 작성하는 언어적인 제약은 없는 (오류체크를 하지 않는) 관례가 있다. 

종류 명명 방법 설명
public 일반 이름 name 어디서든 접근 가능 (클래스 내부/외부)
protected _(언더스코어 하나) _name 같은 클래스와 서브클래스에서 접근 가능하다고 관례적으로 표현
private __ (언더스코어 두개) __name - 해당 클래스 내부에서 접근 가능
- 내부적으로 이름이 맹글링되어 cat.__name으로 사용 할 수 없다. 맹글링 규칙은 _클래스명+속성명으로 _Cat__name을 사용하면 접근할 수 있다.  

 

파이썬에서 private의 경우 __(double underscore or dunder)를 사용할 수 있으나, 속성명을 직접 접근하기 보다 아래와 같이 함수를 사용하여 접근하기를 권장한다. 

 

방법 1. @property getter/setter 정의 ->  파이썬 스타일(권장)

@property는 파이썬에서 제공하는 데코레이터(기존 함수(또는 메서드)에 새로운 기능을 추가하거나 동작 방식을 변경하는 기능을 제공하는 문법) 중 하나로 메서드를 속성(attribute)처럼 사용할 수 있도록 한다. 아래 코드의 예에서 def name(self)는 name이라는 이름의 메서드를 정의해서 클래스의 __name 속성을 리턴하는 메서드 인데 @property라는 데코레이터로 선언하여 실제 코드에서는 my_cat.name으로 속성명 처럼 사용할 수 있다. 

방법 2. getter/setter를 직접 정의 -> 전통적인 방식(it’s ok)
두번째 방법은 일반적인 객체지향에서 사용하는 속성값을 return 하거나 속성정보를 수정하기 위한 getter/setter 함수를 정의하는 것이다. 아래 코드의 def get_name() 함수는 name값을 리턴하도록 구현되어 있다. getter의 한 종류가 된다. 
방법 3. 맹글링 된 이름 직접 사용 ->  추천X
방법 3은 맹글링 된 이름을 직접 사용하는 방법으로 데이타 보호를 위해서는 추천하지 않는다.

 

class Cat:
    def __init__(self, name):
        self.__name = name  # private (name mangling)

    @property              
    def name(self):          # 방법 1. @property로 getter/setter 정의 ->  파이썬 스타일(권장)
        return self.__name
    def get_name(self):     # 방법 2. getter/setter를 직접 정의 -> 전통적인 방식(it’s ok)
        return self.__name

# 객체 생성
my_cat = Cat(name="Cat1")

# __name 접근
print(f"case 1. @property 사용: my_cat.name -> {my_cat.name}")
print(f"case 2. getter method 사용: my_cat.get_name() -> {my_cat.get_name()}")
print(f"case 3. Mangled name 사용: my_cat._Cat__name -> {my_cat._Cat__name}")  #방법 3. 맹글링 된 이름 직접 사용 ->  추천X

 

[참고] 데코레이터, getter/setter

방법 1과 2의 get/set을 위한 방법을 같이 정리하면 아래와 같다. 

 
데코레이터
getter/setter
설명
- 파이썬에서 정의
- @property
- @
attributes_name.setter
- 객체지향의 일반적인 언어에서 사용하는 용어
- 속성값을 return하는 메소드를 getter
- 속성값을 update하는 메소드를 setter
예제코드

   

 

Book 클래스에서의 예 

클래스의 캡슐화 과정과 정보은닉을 통한 속성 정의 과정을 아래와 같이 표현할 수 있습니다. 도서관 시스템에서의 요구사항을 통해 Book 클래스라 모델수준에서 도출이 되고, 속성과 메소드가 정의가 되면 이를 파이썬 코드로 변경 할 수 있습니다. 

 

이때, 객체지향에서는 객체의 속성 정보를 private으로 정의하고 클래스 내부에서 관리하며 외부에서는 메소드를 통해 접근하도록 정의합니다. 이를 위해 파이썬 코드에서는 @property 데코레이터로 정의하여 속성을 접근합니다. 

 

 

 

+ Recent posts