Java : Getter/Setter

최혜린·2025년 3월 13일

1. Getter/Setter란?

GetterSetter는 객체의 필드 값에 접근하는 메서드이다.

  • Getter : 객체의 필드 값을 외부에서 읽을 때 사용 ( return 필요)
  • Setter : 객체의 필드 값을 외부에서 변경할 때 사용 (매개변수 필요)
  • 사용 이유: private 필드에 직접 접근할 수 없으므로, getter/setter를 통해 안전하게 값을 읽고 수정할 수 있도록 한다 (캡슐화 원칙 적용).
class Person {
    private String name;
    
    // Getter: 필드 값 조회
    public String getName() {
        return name;
    }

    // Setter: 필드 값 변경
    public void setName(String name) {
        this.name = name;
    }
}

public class Main {
    public static void main(String[] args) {
        Person p = new Person();
        p.setName("hyerin");  // Setter 사용
        System.out.println(p.getName());  // Getter 사용 (출력: hyerin)
    }
}

2. Getter/Setter를 왜 사용할까?

1. 정보은닉 & 캡슐화

  • private 필드를 외부에서 직접 수정하지 못하도록 보호.

  • 외부에서는 Getter/Setter를 통해서만 값을 수정할 수 있다.

2. 데이터 무결성 유지

  • Setter를 통해 데이터 유효성을 검증하여 잘못된 값이 들어가는 것을 방지할 수 있다.
public void setAge(int age) {
    if (age > 0) {
        this.age = age;
    } else {
        System.out.println("나이는 0보다 커야 합니다.");
    }
}

3. 유지보수성 향상

  • 필드를 직접 수정하면 코드가 많아질 수 있지만, 메서드로 관리하면 유지보수가 쉬워진다.

3. Getter / Setter를 지양해야 한다?

🚨 Setter의 문제점

🔴 Setter로 값을 변경하는 경우 이유가 명확하지 않다.

// ❌ 잔액이 왜 변경되었는지 알 수 없음
Account myAccount = new Account(500);
myAccount.setBalance(1000);  
  • setBalance(1000); 하면, 잔액을 그냥 바꿔버릴 수 있다.
  • 이게 "입금" 때문인지, "출금" 때문인지 알 수 없음..
  • 의도를 알 수 없으니 유지보수가 어려워진다.

🟢 해결방법

class Account {
    private long balance;

    public void deposit(long amount) { // 입금
        if (amount > 0) {
            balance += amount;
        }
    }

    public void withdraw(long amount) { // 출금
        if (amount > balance) {
            throw new IllegalArgumentException("잔액 부족");
        }
        balance -= amount;
    }

    public long getBalance() {
        return balance;
    }
}
  • Setter를 없애고 "의도가 명확한 메서드"를 사용한다.
  • deposit(1000), withdraw(500)처럼 "이 행동이 왜 필요한지"를 명확하게 표현

🚨 Getter의 문제점

🔴 Getter로 데이터를 꺼내와서 로직을 처리하면 비즈니스 로직이 분산된다

public void withdraw(long id, long amount) {
    Account account = accountRepository.findById(id).orElseThrow();
    long newBalance = account.getBalance() - amount;
    
    if (newBalance < 0) {
        throw new IllegalArgumentException("잔액 부족");
    }
    
    account.setBalance(newBalance);
}
  • account.getBalance()로 잔액을 가져와서, withdraw()에서 직접 계산함.
  • 즉, 출금 로직이 Account가 아니라 withdraw()를 호출하는 곳에 있다.
  • 만약 출금 로직이 바뀌면? → 출금 기능을 쓰는 모든 코드를 수정해야 한다!

🟢 해결방안

class Account {
    private long balance;

    public void withdraw(long amount) {
        if (amount > balance) {
            throw new IllegalArgumentException("잔액 부족");
        }
        balance -= amount;
    }
}
  • "잔액을 직접 꺼내서(get) 출금 로직을 만들지 않고, 계좌 객체에게 요청하도록 수정"
  • account.withdraw(amount); 한 줄이면 끝!
  • 비즈니스 로직을 Account 객체 내부에 유지해서, 변경이 필요할 때 한 곳만 수정하면 된다.

4. 그럼 Getter / Setter를 아예 쓰면 안 될까?

No!
Getter/Setter를 무조건 피하는 게 아니라, "적절하게 사용"하는 것이 중요하다.

Getter/Setter가 필요한 경우

1. DTO (Data Transfer Object)

데이터를 전달할 때는 getter/setter가 필요하다.
예를 들어, JSON 응답을 만들 때는 getter가 없으면 데이터 변환이 안 된다.

2. 단순한 객체 조회 (조회만 할 때는 괜찮음)

예를 들어, getName(), getAge() 같은 단순한 정보 조회는 문제 없다.

3. "변경 메서드"가 필요 없을 때

모든 값을 변경할 필요는 없지만, 읽기는 해야 할 경우 getter를 사용할 수 있다.

✅ 결론: Getter/Setter를 "적절하게" 사용해야 함

  • Getter/Setter를 무조건 사용하지 않는 것이 아니라, 상황에 맞게 적절히 사용해야 함.

  • Setter 대신 도메인의 의미가 명확한 메서드를 사용하는 것이 유지보수에 유리함.

  • DTO에서는 Getter/Setter가 필요하지만, 비즈니스 로직이 포함된 객체에서는 최소한으로 사용해야 함.

profile
산으로 가는 코딩.. 등산 중..🌄

0개의 댓글