티스토리 뷰
✏️ 들어가며
Spring Boot와 JPA를 활용한 백엔드 개발은 매우 강력하면서도 효율적인 방법입니다.
그러나 Entity 클래스를 설계할 때 Setter를 자칫 잘못 사용하면 코드 품질과 유지보수성에 큰 영향을 줄 수 있습니다.
Java의 전통적인 개발 방식에서 습관적으로 getter와 setter를 생성하지만, JPA 기반의 도메인 설계에서는 setter를 자제하는 것이 권장됩니다. 이 글에서는 그 이유와 대안에 대해서 설명하고자 합니다.
✏️ Entity 클래스란 무엇인가
Entity는 JPA에서 데이터베이스의 테이블과 매핑되는 클래스입니다.
Entity는 단순한 데이터 그릇이 아니라, 도메인 중심 설계를 위한 핵심 모델 객체입니다.
또한 단순히 데이터를 저장하는 역할을 넘어, 비즈니스 규칙을 담고 객체의 상태를 관리하는 책임을 가지기 때문에 무분별한 접근보다는 엄격한 상태 관리가 필요합니다.
예를 들어, 다음과 같은 Product 클래스는 데이터베이스의 Product 테이블과 1:1로 매핑됩니다.
@Entity
public class Product {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
✏️ Setter 메서드란 무엇인가?
Setter는 객체의 필드 값을 외부에서 변경할 수 있도록 제공하는 public 메서드입니다.
직관적이고 코드 작성이 쉬워서 자주 사용되지만, 객체의 캡슐화 원칙을 깨뜨릴 수 있는 위험 요소이기도 합니다.
보통 IDE에서 자동 생성되는 경우가 많으며, 다음과 같은 형태를 가집니다.
public void setPrice(int price) {
this.price = price;
}
✏️ 왜 Setter를 사용하면 안 되는가?
1) 객체의 일관성(Consistency)이 무너질 수 있음
Setter를 통해 외부에서 필드 값을 마음대로 변경할 수 있으면, 객체의 상태가 의도치 않게 망가질 수 있습니다.
2) 비즈니스 규칙이 사라짐
도메인 객체는 단순 데이터 저장소가 아니라 비즈니스 규칙을 반영하는 책임이 있습니다.
setter를 통해 직접 값을 수정하면, 유효성 검증이나 도메인 로직이 무시될 수 있습니다.
3) 변경 추적(Dirty Checking)에 혼란을 줌
JPA는 트랜잭션 안에서 Entity의 상태 변화를 추적해 자동으로 UPDATE 쿼리를 발생시킵니다.
setter가 남용되면 변경이 어디서 일어났는지 파악하기 어려워지고, 의도치 않은 변경이 발생할 수 있습니다.
4) 디버깅 및 유지보수가 어려워짐
다양한 객체가 동일한 Entity에 setter를 호출하면, 추적이 어려워지고 버그 발생 시 원인을 찾기 힘듭니다.
✏️ 대안은?
1) Lombok 어노테이션으로 생성자 관리
Lombok을 사용하면 반복적인 생성자 코드를 줄이면서도 setter 없이 객체를 안전하게 생성할 수 있습니다.
이렇게 하면 외부에서 필드에 직접 접근하거나 setter 없어도 객체 생성이 가능합니다.
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Getter
public class Product {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
// 또는 Builder 패턴
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
public class Product {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
// 사용
Product product = Product.builder()
.name("키보드")
.price(30000)
.build();
2) 의미 있는 필드 수정 메서드 작성 (updateXXX 방식)
setter대신 명확한 목적을 가진 메서드로 객체의 상태를 변경하도록 합니다.
이렇게 하면 비즈니스 규칙을 내부에서 처리할 수 있어 유지보수가 쉬워집니다.
public void updatePrice(int newPrice) {
if (newPrice < 0) {
throw new IllegalArgumentException("가격은 0 이상이어야 합니다.");
}
this.price = newPrice;
}
public void updateName(String name) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("상품명은 비어 있을 수 없습니다.");
}
this.name = name;
}
// 사용
product.updatePrice(25000);
product.updateName("무소음 키보드");
✏️ 정리하며
Spring Boot와 JPA에서 Entity에 setter를 사용하는 것은 편리할 수 있지만, 객체 지향 설계 원칙과 JPA 동작 방식 모두에 부합하지 않는 경우가 많습니다. Entity는 데이터를 담는 그릇이 아닌, 도메인의 규칙과 의미를 표현하는 객체이므로 생성자, 도메인 메서드를 통해 책임을 명확히 분리하고 설계하는 것이 바람직합니다.
'Back-End' 카테고리의 다른 글
| [Spring/스프링] - Spring Boot에서의 MVC 흐름 (4) | 2025.08.21 |
|---|---|
| [Spring/스프링] - JPA에서 N+1 문제와 해결 방법 (2) | 2025.08.20 |
| [Spring/스프링] - Spring Boot + JWT 로그인 구현하기 (7) | 2025.08.18 |
| [Spring/스프링] - 스프링 부트에서 계층관점으로 바라본 아키텍처 설계: DDD vs 레이어드 아키텍처 (6) | 2025.08.17 |
| [FastAPI/Pyhton] - FastAPI Github Actions 자동 배포하기 #2 (15) | 2025.08.16 |
- Total
- Today
- Yesterday
- Do it!
- 유니온 파인드
- 알고리즘 공부
- CSS
- 스택
- 자바스크립트
- java
- 백준
- 우선순위 큐
- 투 포인터
- 스프링 부트 crud 게시판 구현
- 에라토스테네스의 체
- c++ string
- BFS
- 유클리드 호제법
- js
- html
- DFS
- 반복문
- 자료구조
- 이분 매칭
- C++ Stack
- 알고리즘
- 세그먼트 트리
- DP
- 자바
- HTML5
- C++
- 카운팅 정렬
- 백준 풀이
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
