주문 객체의 필드를 private으로 바꿨지만 setter로 음수 금액이나 잘못된 상태를 넣을 수 있습니다. 캡슐화가 보호해야 할 규칙은 무엇이며 API를 어떻게 바꾸나요?
잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?
답변과 점검 포인트 펼치기답변 접기
구두 답변
정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.
캡슐화는 필드를 private으로 바꾸는 데서 끝나지 않고, 객체가 항상 지켜야 하는 규칙인 불변식을 외부의 임의 변경으로부터 보호하는 설계입니다. 따라서 주문의 상태나 금액을 감추고, 아무 값이나 넣는 setter 대신 결제 승인이나 취소처럼 규칙을 검사하는 동작을 객체가 제공하게 하겠습니다.
예를 들어 주문 금액은 0보다 커야 하고 결제된 주문은 다시 대기 상태가 될 수 없다고 합시다. 필드를 숨겼더라도 public setter가 -1을 그대로 넣도록 허용하면 상태의 유효성은 보호되지 않습니다. changeAmount(newAmount)에서 미결제 상태와 양수 조건을 함께 검사하거나, 생성 시 검증하고 이후 금액을 바꾸지 않는 값 객체로 만들면 객체가 관찰되는 모든 시점에 규칙을 유지할 수 있습니다.
다만 모든 데이터를 무조건 숨기고 getter와 setter를 늘리는 것이 좋은 캡슐화는 아닙니다. 호출자가 여러 필드를 읽어 조합해 규칙을 판단하면 불변식이 서비스 곳곳으로 흩어지고, 내부 표현을 바꾸기 어려워집니다. 반대로 단순한 전송용 데이터처럼 규칙이 없는 구조는 공개 필드나 접근자를 써도 비용이 작습니다. 핵심은 정보 은닉을 목적이 아니라 변경 권한과 규칙의 소유권을 정하는 수단으로 선택하는 것입니다.
변경 명령이 상태 규칙을 소유합니다
setAmount(-1)와 setStatus(PAID) 같은 setter는 호출 순서를 외부에 맡겨 결제 후 금액 변경이나 취소 후 재승인을 허용할 수 있습니다. 생성 팩터리에서 금액·통화·반올림을 검증하고, 이후에는 approve, cancel, changeAmount가 현재 상태와 입력을 함께 검사하도록 합니다. 실패한 명령은 필드 일부만 바꾸지 않아야 합니다. 컬렉션은 내부 목록 대신 불변 복사본이나 읽기 전용 순회를 반환하고, 원소까지 가변인지 확인합니다.
객체와 저장소의 계약을 잇습니다
직렬화 프레임워크가 생성자 검증을 우회해 잘못된 주문을 만들지 않게 복원 전용 경계를 둡니다. 객체의 불변식이 맞아도 두 요청이 같은 주문 버전을 저장하면 갱신이 유실될 수 있으므로 버전 조건이나 DB 제약을 함께 사용합니다. 규칙 없는 DTO까지 무거운 캡슐화로 감싸지 않고, 주문에 허용된 상태 전이와 금지 전이, 예외 뒤 상태 보존, 내부 컬렉션 변경 시도를 테스트하겠습니다.
구체적인 경계 사례
계좌 A에서 B로 이체할 때 각 객체가 음수 잔액을 금지해도 A 차감 후 B 증가가 실패하면 전체 합계가 깨질 수 있습니다. 여러 객체가 함께 지켜야 할 규칙은 하나의 aggregate나 서비스 트랜잭션 경계로 보호해야 합니다. 각 객체의 private 필드만으로 두 저장소의 원자성이 생기지는 않습니다.
읽기 API도 내부 표현보다 의미를 반환하게 합니다. 주문의 상태 코드를 꺼내 호출자가 여러 if문으로 취소 가능성을 계산하기보다 canCancel이나 cancel의 명확한 결과를 제공할 수 있습니다. 다만 canCancel 조회 후 cancel 호출 사이 상태가 바뀔 수 있으므로 실제 변경 명령에서 다시 조건을 검사합니다. 확인용 조회는 사용자 안내이고 명령의 내부 검증이 최종 책임이라는 구분이 필요합니다.
득점 포인트
이해가 드러나는 지점을 확인해 보세요.
- private 필드가 아니라 객체의 불변식과 허용된 명령을 캡슐화한다.
- 금액 값 객체·상태 전이·컬렉션 보호와 실패 시 부분 변경 방지를 설명한다.
- 객체 불변식과 DB 동시성·직렬화 복원 경계를 함께 검증한다.
감점 포인트
놓치기 쉬운 조건과 오해를 짚어 보세요.
- private과 setter만 있으면 음수 금액과 잘못된 상태가 막힌다고 한다.
- getter로 꺼낸 필드를 외부 서비스가 조합해도 캡슐화라고 본다.
- 객체 보호만으로 동시 저장 충돌과 DB 제약도 해결된다고 과장한다.
더 파고들 거리
하나 더 질문하며 이해의 범위를 넓혀 보세요.
- 컬렉션을 반환할 때 내부 목록을 직접 노출하지 않으려면 어떤 복사 또는 읽기 전용 뷰가 필요한가요?
- 서로 다른 객체 두 개가 함께 지켜야 할 규칙이 있습니다. aggregate·서비스 transaction의 경계를 어떻게 정하나요?
- 캡슐화로 내부 표현을 바꿔도 클라이언트가 깨지지 않는지 어떤 테스트로 확인할까요?
득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.
원리부터 공부하고 싶다면
답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.