Spring, Spring Boot) 65번째 회고
1. @PostConstruct
@PostConstruct는 Java EE와 Spring Framework에서 사용되는 어노테이션으로, 빈의 생성과 의존성 주입이
완료된 후 실행되어야 하는 메소드를 지정한다. 이 어노테이션이 붙은 메소드는 객체 생성 후 자동으로 호출되어
초기화 작업을 수행한다.
💡 주요 특징
- 의존성 주입 완료 후 실행
- 클래스당 한 번만 호출
- 트랜잭션 처리 불가
📌 속성과 설명
메소드/속성 | 설명 |
@PostConstruct | 초기화 메소드 지정 |
💻 예시 코드
@Component
public class MyService {
private final DatabaseClient databaseClient;
@Autowired
public MyService(DatabaseClient databaseClient) {
this.databaseClient = databaseClient;
}
@PostConstruct
public void init() {
databaseClient.connect();
System.out.println("Database connection initialized");
}
}
Spring Boot에서 @PostConstruct 알아보자!
spring boot : 2.6.1의존성 주입이 완료된 후에 실행되어야 하는 method에 사용해당 어노테이션은 다른 리소스에서 호출되지 않아도 수행생성자 보다 늦게 호출된다.호출 순서생성자 호출의존성 주입
velog.io
- 참고 : Karim.log
2. @Temporal(TemporalType.DATE) : JPA에서 사용
@Temporal은 JPA에서 날짜 타입 (java.util.Date, java.util.Calendar)을 매핑할 때 사용하는 어노테이션이다.
TemporalType.DATE는 날짜 정보만 데이터베이스에 저장함을 지정한다.
📌 속성과 설명
속성 | 설명 |
TemporalType.DATE | 날짜만 저장 |
TemporalType.TIME | 시간만 저장 |
TemporalType.TIMESTAMP | 날짜와 시간 모두 저장 |
💻 예시 코드
@Entity
public class Event {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Temporal(TemporalType.DATE)
private Date eventDate;
// getters and setters
}
[JPA] 엔티티 매핑 - @Temporal, @Enumerated, @Lob, @Transient
[JPA] 엔티티 매핑 - @Entity, @Table, @Column 엔티티매니저에 의해 생성된 엔티티는 DB의 테이블과 매핑된다. 매핑 작업은 JPA프레임워크가 담당한다. 개발자가 해야할 일은 어떤 엔티티가 어떤 테이블
lordofkangs.tistory.com
- 참고 : lordofkangs
3. Fech Join
Fetch Join은 JPA에서 연관된 엔티티나 컬렉션을 한 번의 쿼리로 함께 조회하는 기능이다.
N+1 문제를 해결하고 성능을 최적화하는 데 사용된다.
💡 주요 특징
- 연관 엔티티를 즉시 로딩
- JPQL에서 'JOIN FETCH' 키워드 사용
- 페이징 처리에 제한이 있음
🚀 N+1문제란?
ORM(Object-Relational Mapping) 프레임워크를 사용할 때 자주 발생하는 성능 문제이다.
이 문제는 주로 연관된 엔티티를 조회할 때 발생하며, 데이터베이스 쿼리 수가 불필요하게 많아지는 현상을 말한다.
💡 N+1 문제의 원인
- 최초 쿼리로 N개의 엔티티를 조회
- 각 엔티티의 연관된 엔티티를 조회하기 위해 N번의 추가 쿼리 실행
결과적으로 1(최초 쿼리) + N(연관 엔티티 조회) 번의 쿼리가 실행된다.
💻 예시 상황 코드
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
private List<Comment> comments;
// getters and setters
}
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String content;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
// getters and setters
}
❗ N+1 문제 발생 코드
List<Post> posts = postRepository.findAll();
for (Post post : posts) {
System.out.println(post.getComments().size()); // 각 포스트의 댓글 수 출력
}
이 코드는 다음과 같은 쿼리를 실행한다.
- 모든 POST 조회 (1번의 쿼리)
- 각 POST의 Commit 조회 (N번의 쿼리)
💡 N+1 문제의 해결 방법
1️⃣ Fetch Join 사용
@Query("SELECT p FROM Post p JOIN FETCH p.comments")
List<Post> findAllWithComments();
2️⃣ EntityGraph 사용
@EntityGraph(attributePaths = {"comments"})
List<Post> findAll();
3️⃣ BatchSize 설정
@Entity
public class Post {
// ...
@OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
@BatchSize(size = 100)
private List<Comment> comments;
// ...
}
4️⃣ @Fetch (FetchMode.SUBSELECT) 사용
@Entity
public class Post {
// ...
@OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
@Fetch(FetchMode.SUBSELECT)
private List<Comment> comments;
// ...
}
5️⃣ DTO 프로젝션 사용
@Query("SELECT new com.example.PostDTO(p.id, p.title, SIZE(p.comments)) FROM Post p")
List<PostDTO> findAllPostsWithCommentCount();
📌 각 해결 방법의 장단점
해결 방법 | 장점 | 단점 |
Fetch Join | 한 번의 쿼리로 모든 데이터 조회 | 카테시안 곱으로 인한 데이터 중복, 페이징 불가 |
EntityGraph | 유연한 설정 가능 | 복잡한 관계에서 사용이 어려움 |
BatchSize | 여러 쿼리를 하나로 묶음 | 최적의 사이즈 설정 필요 |
SUBSELECT | 컬렉션 조회 시 효율적 | 항상 서브쿼리 사용으로 인한 성능 저하 가능 |
DTO 프로젝션 | 필요한 데이터만 정확히 조 | 추가 매핑 작업 필요 |
🚀 Fetch Type이란?
JPA에서 연관 엔티티를 불러오는 시점을 결정하는 속성이다. 주로 @OneToMany, @ManyToOne 등의 연관관계 매핑 어노테이션과 함께 사용되며, 성능과 데이터 일관성에 중요한 영향을 미친다.
💡 JPA에서 제공하는 두 가지 Fetch Type
Fetch Type | 설명 | 장점 | 단점 |
EAGER | 연관 엔티티를 즉시 함께 조회 |
연관 데이터를 항상 사용할 때 유용 |
불필요한 데이터도 항상 조회하여 성능 저하 가능 |
LAZY | 연관 엔티티를 실제 사용 시점에 조회 |
필요 시점에만 데이터 로드하여 성능 향상 |
N+1 문제 발생 가능, 세션 종료 후 사용 시 문제 발생 |
💻 예시 코드
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
private List<Comment> comments;
// getters and setters
}
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String content;
@ManyToOne(fetch = FetchType.EAGER)
private Post post;
// getters and setters
}
📌 Fetch Type 사용 시 주의사항
- @OneToMany, @ManyToMany
기본 값이 LAZY - @OneToOne, @ManyToOne
기본 값이 EAGER
💡 Fetch Type 선택 기준
- 연관 엔티티를 자주 사용하는 경우 → EAGER 고려
- 연관 엔티티를 가끔 사용하는 경우 → LAZY 사용
- 성능 최적화가 필요한 경우 → 상황에 따라 동적으로 결정 (JPQL의 Fetch Join 등 활용)
페치 조인
페치 조인(fetch join) 페치 조인이란 SQL 조인의 종류가 아니며, JPQL에서 성능 최적화를 위해서 제공해주는 기능이다. 페치 조인은 엔티티를 조회할 때 연관된 엔티티나 컬렉션을 한 번의 SQL로 함께
kimtaesoo99.tistory.com
-참고 : kimtaesoo99
[Spring] 패치조인(fetch Join)이란?
ㅁ 들어가며ㅇ [Spring] JDBC와 MyBatis와 JPA 비교, 시대적 흐름에서 장단점 분석에서 JDBC, MyBatis, JPA의 시대적 흐름에 따른 장단점을 정리하였다.ㅇ JPA의 장단점을 요약적으로 정리하고 fetch Join이 왜
peterica.tistory.com
- 참고 : peterica
[JPA] 일반 Join과 Fetch Join의 차이
JPA를 사용하다 보면 바로 N+1의 문제에 마주치고 바로 Fetch Join을 접하게 됩니다. 처음 Fetch Join을 접했을 때 왜 일반 Join으로 해결하면 안되는지에 대해 명확히 정리가 안된 채로 Fetch Join을 사용했
cobbybb.tistory.com
- 참고 : cobbybb
[JPA] Fetch - Eager, Lazy
Fetch란? Fetch의 사전적 의미는 '(어디를 가서) ~을 가지고 오다' 라는 뜻이다. JPA에서도 이 의미는 일맥상통한다. JPA에서 Fetch는 엔티티의 필드에 DB에서 실제 값을 가져오는 것이고 가져오는 방법
somuchthings.tistory.com
- 참고 : somuchthings
JPA의 LazyInitializationException
JPA를 사용하는 경우 잊을 만하면 만나는 LazyInitializationException 을 해결 하는 방법 정리
velog.io
- 참고 : iamcoder.log
4. Entity DTO 분리, 차이점
Entity는 데이터베이스 테이블과 매핑되는 객체이고, DTO는 계층 간 데이터 전송을 위한 객체이다.
이들을 분리하여 사용하면 각 객체의 책임을 명확히 하고, 데이터베이스 구조 변경의 영향을 최소화할 수 있다.
📌 주요 차이점
특성 | Entity | DTO |
목적 | 데이터베이스 매핑 | 데이터 전송 |
영속성 | JPA 관리 | 일반 Java 객체 |
변경 | 신중히 다룸 | 자유롭게 변경 가능 |
💻 예시 코드
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
// getters and setters
}
public class UserDTO {
private String username;
// getter and setter
}
📚 Dto와 Entity를 분리하는 이유, 분리하는 방법
작년에 프로젝트를 하면서 entity를 view에 그대로 반환했다가 Dto로 변환해야겠다는 생각이 들어서 바꿨었는데, Entity와 dto에 대한 내용을 블로그에 정리하면 좋을 거 같아 글을 쓰게 되었다. (일기
velog.io
- 참고 : 0sunset0.log
역할 분리를 위한 Entity, DTO 개념과 차이점
'Entity, DTO 개념과 차이점 (+ VO)' - Entity Entity 클래스는 실제 DB 테이블과 매핑되는 핵심 클래스로, 데이터베이스의 테이블에 존재하는 컬럼들을 필드로 가지는 객체입니다. (DB의 테이블과 1:1로 매
wildeveloperetrain.tistory.com
- 참고 : wildeveloperetrain
Entity와 DTO 분리
* Entity와 DTO 분리 *Entity는 데이터베이스와 연관이 있으며 데이터베이스의 영속성을 관리하기 위한 역할을 한다. DTO는 클라이언트와 서버 간의 데이터 전송을 관리하고, 필요한 데이터를 포함하
cafe.daum.net
- 출처 : 데이터 과학자 + 프로그래머 세상 다음카페
5. Spring Model
Spring Model은 컨트롤러에서 뷰로 데이터를 전달하는 데 사용되는 인터페이스이다.
주로 뷰에서 표시할 데이터를 담아 전달하는 역할을 한다.
💡 주요 메소드
메소드 | 설명 |
addAttribute(String name, Object value) | 모델에 속성 추가 |
getAttribute(String name) | 속성 값 조회 |
asMap() | 모델의 모든 속성을 Map으로 반환 |
💻 예시 코드
@Controller
public class UserController {
@GetMapping("/user/{id}")
public String getUser(@PathVariable Long id, Model model) {
User user = userService.findById(id);
model.addAttribute("user", user);
return "user-details";
}
}
스프링에서 Model
Model은 어디서 오는?어디에서 만들어지는?생명주기는?사용 방법은?실생활에서도 자주 쓰여지는지?
velog.io
- 참고 : jungnoeun.log