캡슐화

데이터와 그 데이터를 다루는 메서드를 하나로 묶고, 내부 구현을 외부로부터 숨기는 객체지향 원칙입니다.

핵심 개념

캡슐화 = 데이터 은닉 + 접근 제어

외부에서는 내부 구현을 모르고
정해진 인터페이스(메서드)로만 접근 가능

접근 제어자

class BankAccount {
    private int balance;        // 외부 직접 접근 X
    public String owner;        // 외부 직접 접근 O
    protected int limit;        // 같은 패키지/상속 O
}
접근 제어자클래스 내부같은 패키지상속외부
privateOXXX
defaultOOXX
protectedOOOX
publicOOOO

캡슐화 없을 때 문제

// 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를 만드는 것이 아니라 객체 스스로 데이터를 책임지고 관리하게 하는 것입니다.

CSLOG에서 보기