편리해 보이는 @Getter / @Setter를 지양해야 한다?
지양을 해야하는 이유를 알기 전 @Getter / @Setter 애너테이션에 대해 먼저 알고가자!
@Getter / @Setter 애너테이션이란?
자바를 공부하면서 객체 지향 프로그래밍에서
객체의 데이터는 객체의 외부에서 직접적으로 접근하는 것을 막는다는 캡슐화의 원칙을 기억하고 있을 것이다.
이를 위해 접근 제어자를 private으로 두고, getter/setter를 사용하라고 배웠다.
@Getter/@Setter 애너테이션은 이(getter/setter)를 선언하는 것을 줄여주는 애너테이션이다.
즉, 아래와 같이 사용이 가능하다.
[애너테이션 사용 전 Member.java]
public class Member{
private String id;
private String name;
private String password;
private String email;
private String address;
public String getId() {return id;}
public void setId(String id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getPassword() {return password;}
public void setPassword(String password) {this.password = password;}
public String getEmail() {return email;}
public void setEmail(String email) {this.email = email;}
public String getAddress() {return address;}
public void setAddress(String address) {this.address = address;}
public List<Board> getBoardList() {return boardList;}
public void setBoardList(List<Board> boardList) {this.boardList = boardList;}
}
애너테이션을 사용하면 이 코드를 확 줄여줄 수 있는데, 아래와 같이 사용이 가능하다.
[애너테이션 사용 후 Member.java]
@Getter
@Setter
public class Member{
private String id;
private String name;
private String password;
private String email;
private String address;
}
이렇게 간편한 애너테이션 사용을 지양해야 하는걸까?
위에서 설명한 문구를 가져와보고자 한다.
객체의 데이터는 객체의 외부에서 직접적으로 접근하는 것을 막는다는 캡슐화의 원칙을 기억하고 있을 것이다.
이를 위해 접근 제어자를 private으로 두고, getter/setter를 사용하라고 배웠다.
캡슐화를 위해 getter/setter를 사용해 접근하라고 배웠는데, 사실상 getter/setter를 쓰면 캡슐화가 깨지게 된다.
캡슐화 자체가 외부에서 객체의 내부에 접근하는것을 막아 어떤 것이 있는지 알지 못하게 하는 것인데,
getter/setter를 통해 접근/수정이 가능하게 되기 때문이다.
정리해보면, 필드는 private으로 숨겨 놓고서 Getter/Setter를 public으로 열어서 사용할 경우
정보 은닉의 효과를 볼 수 없게 되는 것이다.
Setter를 지양해야 하는 이유
Setter를 사용하면 객체의 상태를 변경할 수 있다는 장점이 있지만,
단점도 존재한다.
1. 값을 바꾸는 이유를 드러내지 않아 변견 의도를 파악하기 어려움
만약 내가Account 계좌에서 입금하여 잔액이 변경되어 아래와 같이 사용했다면?
class Account{... 생략}
Account account = new Account(500);
account.setBalance(1000);
위 코드를 통해 기존 500원의 계좌의 잔액이 1000원이 된 것을 알 수 있지만,
account.setBalance(1000)만 본다면 계좌에 입금해서 1000원이 된건지, 인출해서 된 것인지 알 수 없다.
즉, 객체의 속성 값이 변경된 이유를 명확하게 알 수 없게된다.
2. 객체의 일관성 유지가 어려움
Setter는 public으로 언제든 변경이 가능한 샅애이기에 모든 곳에서 변경할 수 있는 상태가 되기 때문에 일관성을 유지하기 어려워진다.
3. 다른 객체들로 책임이 분산됨
만약 내가 출금을 하고 싶다면?
class Account{
private Integer balance;
... 생략
//(1)
public void setBalance(Integer balance){
this.balance = balance;
}
//(2)
public void withdraw(Integer money){
if(balance > 0 ){
balance += money;
}
}
}
(1)을 보면 그냥 setter이다. 이는 어떤 메서드인지 명확히 알기 어려울 뿐 아니라
Service에서 이를 사용할 때에도 출금의 기능을 모두 구현해 줘야 한다. 하지만 (2)의 경우에는 말이 다르다.
(2)는 해당 setter가 어떤 역할을 하는지가 명확하고, Service로 자신의 기능에 대한 역할을 분산하지 않는다.
그럼 Setter를 사용할 수 없다면 뭘 사용해야 하는걸까?
Setter를 대신해 사용할 수 있는 방법이 3가지 있다.
1. 생성자를 오버로딩
2. Builder 패턴 사용
3. 정적 팩토리 메서드 사용
물론 Setter를 아예 안 쓸수는 없다.
그 때에는 setter가 어떤 역할을 하는지 명확한 의도를 가진 메서드로서 사용을 해야 한다.
Getter를 지양해야 하는 이유
setter의 경우에는 지양해야 하는 이유가 Getter보다는 와닿을 것이다.
그렇다면 왜 getter 사용을 지양해야 하는지 알아보고자 한다.
1. 조회로 끝나지 않는다.
Getter는 단순히 객체의 값을 알고 싶어서 사용하는 경우가 대다수이기는 하지만, 그 이상으로 사용하게 된다.
2. 변경에 취약할 수 있다.
만약 회원 정보에 이메일이 있었는데, 정보가 변경되면서 이름과 전화번호만 가지게 된다면?
Service에서 기능이 작동할 때 정보 변경에 대해서는 감지하지 못하게 되면서 기존 기능을 그대로 작동하게 되고
이는 이메일에 대해 조회를 할 때 컴파일 에러가 발생하게 되는것이다.
그럼 Getter를 사용하면 안되는 것일까?
Getter를 사용한다고 캡슐화가 깨졌다고 말하기 어려우며, 객체의 값을 외부로 표현해야 한다면 사용을 피할 수 없다.
다만 이를 막 써서는 안된다는 것이다.
Getter를 통해 객체 내부의 값을 노출할 경우
노출된 값을 이용한 비즈니스 로직이 도메인 외부에 생성되지 않도록 유의해야 한다.
애너테이션으로 Getter/Setter를 만들어주는 것을 간단하게 할 수 있지만
Getter/Setter 자체의 사용을 지양하고 주의해야 하는 만큼
Entity 전체에 해당 애너테이션을 이용해 모든 객체들에 대한 Getter/Setter를 만드는 것 또한 주의해야 할 것이다.
* 추가적으로 Getter의 경우에는 정보에 접근만 가능한 것일 뿐 수정은 불가능하기에 보안을 제외하면 문제가 없지만
수정까지 가능한 @Setter는 최대한 지양할 필요가 있다.
'공부 자료 > Spring' 카테고리의 다른 글
[Spring] @CreatedDate&@LastModifiedDate vs @CreationTimeStamp&@UpdateTimeStamp, 둘은 뭐가 다를까? (1) | 2023.11.21 |
---|---|
[Spring JPA] 페이징 Pageable, Page, Slice / 페이지 나누기 (2) | 2023.11.21 |
[Spring] CORS란? (CORS에 대해 알고 설정하기) (2) | 2023.11.20 |
[Spring Boot] ResponseEntity란? (0) | 2023.11.20 |
[MyBatis/Spring] About @Mapper/@Repository (0) | 2023.11.13 |