정보 은닉
Coddy Python 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 64개 중 30번째.
정보 은닉은 객체 구성 요소에 대한 직접적인 접근을 제한하며, 모든 상호작용이 잘 정의된 인터페이스를 통해 이루어지도록 요구합니다. 이는 무단 접근으로부터 내부 데이터를 보호합니다.
다음은 다양한 수준의 정보 은닉을 사용하는 클래스의 예입니다:
class BankAccount:
def __init__(self, owner, initial_balance):
self.owner = owner # Public - 직접 접근 가능
self._balance = initial_balance # Protected - 내부용
self.__account_number = "ACC123456" # Private - 외부에서 숨겨짐숨겨진 데이터에 대한 제어된 액세스를 제공하는 메서드를 추가합니다:
class BankAccount:
def __init__(self, owner, initial_balance):
self.owner = owner
self._balance = initial_balance
self.__account_number = "ACC123456"
def deposit(self, amount):
if amount > 0:
self._balance += amount
return True
return False
def withdraw(self, amount):
if amount > 0 and amount <= self._balance:
self._balance -= amount
return True
return False
def get_balance(self):
return self._balance
def get_account_info(self):
# 비공개 데이터의 일부를 안전하게 표시하는 방법
return f"Owner: {self.owner}, Account: ***{self.__account_number[-4:]}"공개 인터페이스를 통해 클래스를 사용하세요:
account = BankAccount("Alice", 1000)공개 데이터에 직접 액세스합니다:
print(account.owner) # Alice - 공개 액세스 가능보호된 데이터에 대해 제어된 메서드를 사용하세요:
print(account.get_balance()) # 1000 - 제어된 액세스
account.deposit(500)
print(account.get_balance()) # 1500 - 잔액이 안전하게 변경됨숨겨진 데이터에 접근해 보세요:
print(account.get_account_info()) # 소유자: Alice, 계좌번호: ***3456
# print(account.__account_number) # AttributeError - 숨겨짐private 속성은 네임 맹글링(name-mangling)되지만 기술적으로는 여전히 접근 가능합니다:
# 이 방법은 작동하지만 정보 은닉 원칙을 위반합니다:
print(account._BankAccount__account_number) # ACC123456출력:
Alice
1000
1500
Owner: Alice, Account: ***3456
ACC123456핵심 포인트: 정보 은닉은 내부 데이터를 private 또는 protected로 설정하여 보호하고, public 메서드를 통해 제어된 접근을 제공합니다. 이는 민감한 데이터의 직접적인 조작을 방지하며, 접근 메서드 내의 유효성 검사를 통해 데이터 무결성을 보장합니다.
챌린지
중급이 챌린지에서는 솔루션을 검증하기 위한 포괄적인 테스트를 포함한 보안 메시징 시스템을 구현합니다.
정보 은닉 원칙에 따라 SecureMessenger 클래스를 구현하기 위해 securemessenger.py를 수정해야 합니다. 파일에는 구현을 안내하는 상세한 TODO 주석이 포함되어 있습니다.
주요 요구 사항은 다음과 같습니다:
- 자격 증명 및 메시지의 적절한 캡슐화
- 메시지 작업을 허용하기 전 인증
- 로그인 시도 추적을 통한 보안 모니터링
- TODO에 명시된 정확한 반환 메시지
치트 시트
정보 은닉은 접근 제어자를 통해 객체 구성 요소에 대한 직접적인 접근을 제한합니다:
public- 직접 접근 가능 (밑줄 없음)_protected- 내부용 (단일 밑줄)__private- 외부로부터 숨겨짐 (이중 밑줄)
class BankAccount:
def __init__(self, owner, initial_balance):
self.owner = owner # 공개 (Public)
self._balance = initial_balance # 보호 (Protected)
self.__account_number = "ACC123456" # 비공개 (Private)공개 메서드를 통해 제어된 접근을 제공합니다:
def get_balance(self):
return self._balance
def deposit(self, amount):
if amount > 0:
self._balance += amount
return True
return False비공개 속성은 이름 장식(name-mangling)이 적용되지만 여전히 접근 가능합니다:
# 정보 은닉을 위반하지만 작동합니다:
print(account._BankAccount__account_number)직접 해보기
from securemessenger import SecureMessenger
# 테스트 케이스 처리기
test_case = input()
if test_case == "default_test":
# 원래 문제의 표준 테스트 케이스
messenger = SecureMessenger("user1")
# 로그인 전 메시지 추가 시도
print(messenger.add_message("Hello World!"))
print(messenger.add_message("Secure Message"))
# 잘못된 비밀번호로 로그인 시도
print(messenger.login("wrong_pass"))
# 올바른 비밀번호로 로그인
print(messenger.login("secure123"))
# 로그인 성공 후 메시지 추가
print(messenger.add_message("Hello World!"))
print(messenger.add_message("Secure Message"))
# 메시지 조회
print(messenger.get_messages())
# 로그인 시도 횟수 확인
print(messenger.get_login_attempts())
elif test_case == "custom_password":
# 사용자 정의 비밀번호 테스트
messenger = SecureMessenger("admin", "admin123")
print(messenger.login("wrong_password"))
print(messenger.login("admin123"))
print(messenger.get_login_attempts())
elif test_case == "multiple_login_attempts":
# 여러 번의 로그인 시도 테스트
messenger = SecureMessenger("user2")
print(messenger.login("attempt1"))
print(messenger.login("attempt2"))
print(messenger.login("attempt3"))
print(messenger.login("secure123"))
print(messenger.get_login_attempts())
elif test_case == "message_management":
# 메시지 관리 테스트
messenger = SecureMessenger("user3")
print(messenger.login("secure123"))
# 여러 메시지 추가
messages = ["Message 1", "Message 2", "Message 3", "Message 4", "Message 5"]
for msg in messages:
print(messenger.add_message(msg))
# 모든 메시지 확인
print(messenger.get_messages())
elif test_case == "empty_messages":
# 빈 메시지 케이스 테스트
messenger = SecureMessenger("user4")
print(messenger.login("secure123"))
print(messenger.get_messages())
elif test_case == "access_without_login":
# 로그인 없이 접근 테스트
messenger = SecureMessenger("user5")
print(messenger.add_message("Unauthorized message"))
print(messenger.get_messages())
elif test_case == "attribute_privacy":
# 속성 비공개 테스트
messenger = SecureMessenger("user6")
# 공개 속성
print(f"Public username: {messenger.username}")
# 비공개 속성 - AttributeError가 발생해야 함
try:
print(messenger.__password)
except AttributeError as e:
print("Password is private: AttributeError")
try:
print(messenger.__messages)
except AttributeError as e:
print("Messages are private: AttributeError")
try:
print(messenger.__login_attempts)
except AttributeError as e:
print("Login attempts are private: AttributeError")
try:
print(messenger.__is_logged_in)
except AttributeError as e:
print("Login status is private: AttributeError")
elif test_case == "login_logout_sequence":
# 이 테스트는 로그아웃 메서드로 클래스를 확장해야 함
# 테스트 목적으로 로그아웃 기능이 있는 하위 클래스를 생성함
class ExtendedMessenger(SecureMessenger):
def logout(self):
self._SecureMessenger__is_logged_in = False
return "Logged out successfully"
messenger = ExtendedMessenger("user7")
print(messenger.login("secure123"))
print(messenger.add_message("Test message"))
print(messenger.get_messages())
print(messenger.logout())
print(messenger.get_messages())
elif test_case == "stress_test":
# 많은 메시지로 부하 테스트
messenger = SecureMessenger("user8")
print(messenger.login("secure123"))
# 100개의 메시지 추가
for i in range(1, 101):
messenger.add_message(f"Message {i}")
# 메시지 개수 확인
messages = messenger.get_messages()
newline_char = '\n' # 줄바꿈 문자를 변수에 저장
message_count = len(messages.split(newline_char))
print(f"Added 100 messages. Retrieved {message_count} messages.")이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.