객체지향 프로그래밍(OOP)의 핵심 특징 중 하나는 캡슐화(Encapsulation)다. 캡슐화는 연관된 데이터와 메서드를 하나의 단위로 묶고, 외부로부터 데이터를 보호하는 기법을 의미한다.
Java에서는 이러한 캡슐화를 구현하고 객체의 내부 데이터에 대한 접근 범위를 지정하기 위해 접근 제어자(Access Modifier)를 제공한다.
캡슐화는 알약 캡슐 안에 여러 성분을 넣어 내부를 보호하고 외부에는 알약 자체만 노출하는 것과 유사하다. 객체지향에서 캡슐화가 제공하는 주요 이점은 두 가지다.
만약 데이터 보호가 이루어지지 않는다면 외부에서 잘못된 데이터를 주입하여 객체의 상태가 오염될 수 있다.
// 캡슐화가 적용되지 않은 예시
class BankAccount {
public int balance; // 외부에서 직접 접근 가능
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.balance = -10000; // 음수 잔액이라는 치명적인 오류 발생 가능
}
}
Java는 클래스, 필드, 메서드, 생성자의 접근 범위를 지정하기 위해 4가지 접근 제어자를 제공한다.
| 접근 제어자 | 같은 클래스 | 같은 패키지 | 자식 클래스 (다른 패키지) | 전체 (다른 패키지) |
|---|---|---|---|---|
| private | O | X | X | X |
| (default) | O | O | X | X |
| protected | O | O | O | X |
| public | O | O | O | O |
캡슐화를 적용할 때는 멤버 변수를 private으로 선언하여 외부 직접 접근을 차단하고, Getter와 Setter 메서드를 통해 간접적으로 접근하도록 설계한다.
class BankAccount {
// 1. 멤버 변수를 private으로 보호
private int balance;
// 2. Setter를 통해 유효성 검증 후 데이터 수정
public void deposit(int amount) {
if (amount <= 0) {
System.out.println("입금 금액은 0원보다 커야 합니다.");
return;
}
this.balance += amount;
}
// 3. Getter를 통해 데이터 조회
public int getBalance() {
return this.balance;
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.deposit(-5000); // 유효성 검증 실패: "입금 금액은 0원보다 커야 합니다."
account.deposit(10000);
System.out.println("현재 잔액: " + account.getBalance()); // 10000
}
}
이처럼 Setter 내부에서 조건문 등을 통해 유효하지 않은 값이 입력되는 것을 사전 방지함으로써 객체 데이터의 무결성을 지킬 수 있다.
private, default, protected, public)를 사용하여 클래스 요소의 접근 권한을 명확히 제어한다.private으로 설정하고, 필요한 경우 Getter/Setter 메서드를 제공하는 것이 올바른 객체지향 설계의 기본이다.