본문 바로가기

자바 캡슐화 완벽 가이드: 접근 제어자와 데이터 보호 실전 예제

@L.Carol2022. 7. 17. 15:55
반응형

캡슐화란?

**캡슐화(Encapsulation)**는 객체지향 프로그래밍의 핵심 원칙 중 하나로, 데이터와 그 데이터를 조작하는 메서드를 하나의 단위로 감싸고, 외부에서의 직접적인 접근을 제한하는 것입니다.

캡슐화의 목적

  1. 데이터 보호: 잘못된 값으로부터 객체의 상태를 보호
  2. 정보 은닉: 내부 구현 세부사항을 외부로부터 숨김
  3. 유지보수성 향상: 내부 구현 변경 시 외부 코드에 미치는 영향 최소화
  4. 코드 재사용성: 잘 설계된 캡슐화는 다른 프로젝트에서도 안전하게 재사용 가능

문제가 있는 코드

public class Time {
    public int hour;
    public int minute;
    public int second;
}

문제점:

  • 시간 값의 유효성 검사 불가능 (hour > 23, minute > 59 등)
  • 외부에서 임의로 값 변경 가능
  • 데이터 무결성 보장 불가
// 이런 위험한 상황이 발생할 수 있음
Time time = new Time();
time.hour = -5;    // 음수 시간!
time.minute = 70;  // 70분!

캡슐화를 적용한 개선된 코드

public class Time {
    private int hour;    // 0~23 사이의 값
    private int minute;  // 0~59 사이의 값
    private int second;  // 0~59 사이의 값
    
    // 생성자
    public Time() {
        this(0, 0, 0);
    }
    
    public Time(int hour, int minute, int second) {
        setHour(hour);
        setMinute(minute);
        setSecond(second);
    }
    
    // Getter 메서드들
    public int getHour() { 
        return hour; 
    }
    
    public int getMinute() { 
        return minute; 
    }
    
    public int getSecond() { 
        return second; 
    }
    
    // Setter 메서드들 (유효성 검사 포함)
    public boolean setHour(int hour) {
        if (isValidHour(hour)) {
            this.hour = hour;
            return true;
        }
        return false; // 설정 실패를 알림
    }
    
    public boolean setMinute(int minute) {
        if (isValidMinute(minute)) {
            this.minute = minute;
            return true;
        }
        return false;
    }
    
    public boolean setSecond(int second) {
        if (isValidSecond(second)) {
            this.second = second;
            return true;
        }
        return false;
    }
    
    // 전체 시간을 한 번에 설정하는 메서드
    public boolean setTime(int hour, int minute, int second) {
        boolean hourSet = setHour(hour);
        boolean minuteSet = setMinute(minute);
        boolean secondSet = setSecond(second);
        
        return hourSet && minuteSet && secondSet;
    }
    
    // 유효성 검사 메서드들 (private)
    private boolean isValidHour(int hour) {
        return hour >= 0 && hour <= 23;
    }
    
    private boolean isValidMinute(int minute) {
        return minute >= 0 && minute <= 59;
    }
    
    private boolean isValidSecond(int second) {
        return second >= 0 && second <= 59;
    }
    
    // 시간을 문자열로 표현하는 메서드
    @Override
    public String toString() {
        return String.format("%02d:%02d:%02d", hour, minute, second);
    }
    
    // 시간 비교 메서드
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        
        Time time = (Time) obj;
        return hour == time.hour && 
               minute == time.minute && 
               second == time.second;
    }
}

사용 예제

public class TimeTest {
    public static void main(String[] args) {
        // 객체 생성 및 초기화
        Time time = new Time(14, 30, 45);
        System.out.println("현재 시간: " + time.toString()); // 14:30:45
        
        // 안전한 값 설정
        if (time.setHour(23)) {
            System.out.println("시간 설정 성공: " + time.getHour() + "시");
        }
        
        // 잘못된 값 설정 시도
        if (!time.setHour(25)) {
            System.out.println("잘못된 시간 값입니다. 현재 시간: " + time.getHour() + "시");
        }
        
        // 전체 시간 설정
        if (time.setTime(12, 0, 0)) {
            System.out.println("정오로 설정: " + time);
        }
        
        // 객체 비교
        Time noon = new Time(12, 0, 0);
        if (time.equals(noon)) {
            System.out.println("두 시간이 같습니다.");
        }
    }
}

접근 제어자의 종류와 범위

접근 제어자 같은 클래스 같은 패키지 다른 패키지의 하위클래스 다른 패키지

private
default
protected
public

모던 자바에서의 추가 고려사항

1. 불변 객체(Immutable Object) 사용

public final class ImmutableTime {
    private final int hour;
    private final int minute;
    private final int second;
    
    public ImmutableTime(int hour, int minute, int second) {
        if (!isValidTime(hour, minute, second)) {
            throw new IllegalArgumentException("잘못된 시간 값입니다.");
        }
        this.hour = hour;
        this.minute = minute;
        this.second = second;
    }
    
    // Getter만 제공, Setter는 없음
    public int getHour() { return hour; }
    public int getMinute() { return minute; }
    public int getSecond() { return second; }
    
    // 값을 변경하려면 새로운 객체를 반환
    public ImmutableTime withHour(int newHour) {
        return new ImmutableTime(newHour, this.minute, this.second);
    }
    
    private boolean isValidTime(int h, int m, int s) {
        return h >= 0 && h <= 23 && m >= 0 && m <= 59 && s >= 0 && s <= 59;
    }
}

2. Java 14+ Record 사용 (간단한 데이터 클래스)

public record TimeRecord(int hour, int minute, int second) {
    // 생성자에서 유효성 검사
    public TimeRecord {
        if (hour < 0 || hour > 23) {
            throw new IllegalArgumentException("시간은 0-23 사이여야 합니다.");
        }
        if (minute < 0 || minute > 59) {
            throw new IllegalArgumentException("분은 0-59 사이여야 합니다.");
        }
        if (second < 0 || second > 59) {
            throw new IllegalArgumentException("초는 0-59 사이여야 합니다.");
        }
    }
    
    @Override
    public String toString() {
        return String.format("%02d:%02d:%02d", hour, minute, second);
    }
}

 

핵심 원칙

  1. 필드는 private으로: 직접 접근을 막고 메서드를 통한 제어된 접근만 허용
  2. 유효성 검사: setter 메서드에서 항상 입력값의 유효성을 검사
  3. 적절한 반환값: 설정 성공/실패를 boolean으로 반환하거나 예외 발생
  4. 최소 권한 원칙: 필요한 최소한의 접근 권한만 부여
  5. 일관성 유지: 객체의 상태가 항상 유효하도록 보장

캡슐화는 단순히 필드를 숨기는 것이 아니라, 객체의 무결성을 보장하고 외부와의 안전한 인터페이스를 제공하는 핵심 개념입니다.

반응형

'자바의 정석' 카테고리의 다른 글

매개변수의 다형성  (0) 2022.08.22
상속  (0) 2022.05.23
인스턴스 멤버와 정적 멤버  (0) 2022.04.15
메소드 Method  (0) 2022.04.12
생성자 Constructor  (0) 2022.04.12
L.Carol
@L.Carol :: 소소한 일상

많이 배우고 있습니다.

공감하셨다면 ❤️ 구독도 환영합니다! 🤗

목차