캡슐화
데이터와 그 데이터를 다루는 메서드를 하나로 묶고, 내부 구현을 외부로부터 숨기는 객체지향 원칙입니다.
핵심 개념
캡슐화 = 데이터 은닉 + 접근 제어
외부에서는 내부 구현을 모르고
정해진 인터페이스(메서드)로만 접근 가능
접근 제어자
class BankAccount {
private int balance; // 외부 직접 접근 X
public String owner; // 외부 직접 접근 O
protected int limit; // 같은 패키지/상속 O
}
| 접근 제어자 | 클래스 내부 | 같은 패키지 | 상속 | 외부 |
|---|---|---|---|---|
| private | O | X | X | X |
| default | O | O | X | X |
| protected | O | O | O | X |
| public | O | O | O | O |
캡슐화 없을 때 문제
// X 캡슐화 없음 - 외부에서 직접 접근
class BankAccount {
public int balance = 1000;
}
BankAccount account = new BankAccount();
account.balance = -999999; // 잔액이 음수가 되는 문제 발생!
캡슐화 적용
// O 캡슐화 적용 - 메서드로만 접근
class BankAccount {
private int balance; // 외부 직접 접근 차단
public void deposit(int amount) {
if (amount <= 0) throw new IllegalArgumentException("금액 오류");
balance += amount;
}
public void withdraw(int amount) {
if (amount > balance) throw new IllegalArgumentException("잔액 부족");
balance -= amount;
}
public int getBalance() { return balance; }
}
// 외부에서는 메서드로만 접근
account.withdraw(500); // O
account.balance = -999; // X 컴파일 에러
캡슐화의 이점
내부 구현 변경 시 외부 코드에 영향 없음
// balance를 int → long으로 변경해도
// 외부 코드(getBalance 호출부)는 수정 불필요
private long balance; // 내부만 변경
| 이점 | 설명 |
|---|---|
| 데이터 보호 | 유효하지 않은 값 차단 |
| 유지보수 | 내부 변경이 외부에 영향 없음 |
| 낮은 결합도 | 인터페이스로만 소통 |
캡슐화 vs 단순 getter/setter
// X 의미 없는 캡슐화 - getter/setter만 만든 것
class User {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
// private인데 setter로 다 열어줌 → 캡슐화 의미 없음
}
// O 의미 있는 캡슐화 - 검증 로직 포함
class User {
private String name;
public void changeName(String name) {
if (name == null || name.isBlank())
throw new IllegalArgumentException("이름 오류");
this.name = name;
}
}
캡슐화의 핵심은 getter/setter를 만드는 것이 아니라 객체 스스로 데이터를 책임지고 관리하게 하는 것입니다.