티스토리 뷰

Setter 를 지양하는 이유

아마 모두가 알고 있을것이다.

setter 를 사용(남용) 하다 보면 객체의 불변성, 무결성, 캡슐화가 깨지기 쉽다.

객체가 불변성을 가지면 상태가 변하지 않아 객체의 상태에 대한 예측 가능성이 높아진다. 객체가 불변하고 무결하면 여러 곳에서 동시에 접근했을때의 동시성 문제 부터로도 안전해진다.

캡슐화-> 엔티티는 비지니스 로직을 가진 도메인 객체이다. setter 를 이용하면 단순 dto 처럼 이용될 가능성이 생긴다.

 

그래서?

그래서 당시엔 프로젝트를 진행할때 쓰지 말라니깐 다른 방법들로 엔티티의 값을 업데이트 했다.

주로 사용한 방식은

1. dto에다 setter 를 두고 그 dto를 엔티티로 변경해 save 하기

2. 엔티티의 constructor 를 이용해 새로운 객체를 만들어 save하기

정도가 있었던것같다.

 

그 당시엔 배움의 열정에 그것들이 귀찮다는 생각보단 당연한 방법이라고 생각했다.

하지만 지금 코딩하며 내 스타일이 생기고 약간의 꼼수와 귀찮음이 더해져 다른 방법을 찾고 있었다.

entity에 setter 를 직접 만들긴 양심에 가책을 느껴 함수의 이름만 조금 바꾸고 안에 약간의 validation을 넣어 사용했다.

아래의 코드를

void setName(String name) {
	this.name = name;
}

 

아래처럼 바꿔서 엔티티에 두고 사용했다는 뜻이다.

void updateName(String name) {
	if(name.isEmpty()) {
		throw new RuntimeException(-);
	}
    this.name = name;
}

 

이렇게 사용하다 보니 이게 과연 좋을 방식인가? 지금까진 아무문젠 없었지만 이게 아주 배척받는 방법은 아닐까? 의문이 들었다.

 

 

무엇이 좋은 방식일까?

1.  update(newName)

+ setter 와 같은 기능을 할지언정 좀 더 명확하게 명시된 메서드임

+ jpa 를 이용시 더티체킹(변경감지)를 활용해 변경된 필드만 업데이트 할 수 있다.

+ 트랜잭션 범위 내에서 엔티티의 상태가 유지되어 성능상 유리하다.

- 객체가 불변이 아니다( 불변이 아니면 위에 setter 의 지양 이유에서 말한 이유들이 생길 수 있다)

- 변경해야할 필드가 많으면 그만큼 많은 메서드가 필요하다.

 

2. 새로운 객체 생성 후 저장 (업데이트)

+ 불변객체를 유지할 수 있다.

+ 일관성이 보장된다.

+jpa 변경감지를 감안하지 않아도 된다.

- 불필요한 쿼리가 발생할 가능성이 있다.

- 객체 전체를 새로 생성하다보니 불필요한 메모리 할당이 필요하다

- jpa 영속성 컨텍스트와의 연동이 어렵다

 

이렇게 적어놓고 보니 변경감지(dirty checking) 과 병합(merge) 방식의 차이 처럼 느껴지기도 해서

그 둘의 차이점도 한번 찾아보았다.

 

변경감지와 병합 방식의 차이

변경감지(dirty checking)

실행 흐름

  1. userRepository.findById(id) 로 엔티티 조회 → 영속 상태 유지됨
  2. user.updateName("newName") 호출 → 필드 값 변경 (setName과 유사하지만 의미 있는 메서드 제공)
  3. 트랜잭션 종료 시점에 변경 감지로 인해 UPDATE 쿼리 실행됨

실행되는 SQL

SELECT * FROM user WHERE id = ?; -- 엔티티 조회
UPDATE user SET name = ? WHERE id = ?; -- Dirty Checking을 통한 변경 감지

 

장단점

+ 변경된 필드만 업데이트 → 최적화된 쿼리 실행
+ 추가적인 insert/update 없이 최소한의 쿼리로 처리

+ JPA가 자동으로 관리 → 직관적이고 유지보수 용이

- 영속성 컨텍스트 내에서 관리되고 있어야 함

- 조회 후 변경하는 구조이므로, 기존 엔티티를 조회하는 SELECT 쿼리는 필요함

 

병합(Merge) 방식 (save()를 통한 새로운 객체 생성 후 저장)

실행 흐름

  1. new User()로 새로운 객체를 생성
  2. userRepository.save(updatedUser) 호출
  3. ID가 존재하면 SELECT로 기존 엔티티 조회 후, 필드 값 비교 → UPDATE 실행
  4. ID가 존재하지 않으면 INSERT 실행

실행되는 SQL

SELECT * FROM user WHERE id = ?; -- 병합할 기존 데이터 조회
UPDATE user SET name = ?, address = ? WHERE id = ?; -- 모든 필드 업데이트

모든 필드를 업데이트함! (변경된 필드만 반영하는 것이 아님)

장단점

+ 영속성 컨텍스트를 고려할 필요 없이 새 객체 생성 가능
+ 비영속 상태의 엔티티도 처리 가능

- 모든 필드가 업데이트됨 (불필요한 필드까지 포함될 가능성 높음)
- SELECT 후 UPDATE가 실행되므로 불필요한 쿼리 발생 가능
- 변경 감지 방식보다 성능이 떨어질 가능성이 높음

 

결론

ddd 같은 아키텍쳐를 따르고 있거나, jpa 없이 개발하는 경우엔 병합방식을 따르는게 맞지만

그것이 아니라면 굳이 setter 를 절대로 금기시 하며 더티체킹을 피하기 위해 새로운 객체를 이용해 업데이트 하는게 꼭 정답은 아닌듯하다.

gpt에 의하면 updateName 을 사용하는게 더 효율적이라고 한다.

'Java > JPA' 카테고리의 다른 글

JPA - @JoinColumn 과 @MappedBy 의 차이  (0) 2024.03.25
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함