[JPA] N+1 문제란 무엇일까?
·
Back-End/Spring
Spring Boot 프로젝트를 진행하다 보면 JPA의 연관관계를 자주 사용하게 된다.처음에는 @ManyToOne, @OneToMany 같은 연관관계를 단순히 Entity끼리 연결하는 기능으로 사용했지만, 실제 조회 로직을 작성하다 보면 예상보다 많은 SQL이 실행되는 상황을 마주할 수 있다.이때 자주 언급되는 문제가 바로 N+1 문제다.N+1 문제란?N+1 문제는 하나의 조회 쿼리를 실행한 뒤, 조회된 N개의 데이터와 연관된 데이터를 가져오기 위해 추가 쿼리가 N번 더 실행되는 문제를 말한다.예를 들어 상품 목록을 조회했는데, 각 상품의 판매자 정보를 가져오기 위해 상품 개수만큼 추가 쿼리가 실행되는 상황이다.상품 목록 조회 쿼리 1번+ 각 상품의 판매자 조회 쿼리 N번= 총 N+1번의 쿼리 실행즉, ..
@RequestParam, @ModelAttribute, @RequestBody는 언제 사용할까?
·
Back-End/Spring
Spring MVC에서 컨트롤러를 작성하다 보면 요청 데이터를 여러 방식으로 받을 수 있다.대표적으로 자주 사용하는 방식은 다음 세 가지다.@RequestParam@ModelAttribute@RequestBody처음에는 모두 클라이언트가 보낸 값을 컨트롤러에서 받기 위한 어노테이션처럼 보여서 헷갈릴 수 있다.특히 @ModelAttribute와 @RequestBody는 둘 다 DTO 클래스를 만들어 값을 받을 수 있기 때문에 비슷해 보인다.하지만 핵심 차이는 DTO를 사용하느냐가 아니라, 요청 데이터가 어디에 담겨 오느냐이다.간단히 정리하면@RequestParam- Query String이나 Form Parameter의 단일 값을 받을 때 사용한다.@ModelAttribute- Query String이나 ..
[JPA] @ManyToOne, @OneToMany 연관관계 정리
·
Back-End/Spring
Spring Boot 프로젝트를 진행하다 보면 JPA Entity 간의 관계를 설정하기 위해 @ManyToOne, @OneToMany 같은 어노테이션을 자주 사용하게 된다. 처음에는 단순히 Entity끼리 연결하기 위해 사용했지만, 프로젝트를 진행할수록 연관관계의 방향, 주인, mappedBy, 지연 로딩 같은 개념을 한 번 정리할 필요가 있다고 느꼈다.이번 글에서는 JPA 연관관계 중 가장 자주 사용되는 @ManyToOne과 @OneToMany를 중심으로 정리해보려 한다.연관관계란?JPA에서 연관관계는 Entity와 Entity 사이의 관계를 객체로 표현하는 방식이다.예를 들어 하나의 회원이 여러 개의 상품을 등록할 수 있다고 해보자.회원 1명 → 상품 여러 개DB 테이블에서는 보통 product 테이..
[SpringBoot] Entity를 DTO로 변환해야 하는 이유
·
Back-End/Spring
Spring Boot 프로젝트를 진행하다 보면 Entity, DTO, VO라는 용어를 자주 사용하게 된다.처음에는 각각의 객체를 단순히 역할에 맞게 사용했지만, 프로젝트를 진행할수록 이 개념들을 한 번 정리할 필요가 있다고 느꼈다.특히 API 응답을 만들 때 Entity를 그대로 반환하지 않고 DTO로 변환해서 내보내는 이유를 설명하지 못하고 있었다. @GetMapping("/products/{id}")public Product getProduct(@PathVariable Long id) { return productService.findById(id);}위 코드처럼 Entity를 그대로 반환하면 간단해 보인다.하지만 실제 API에서는 Entity를 그대로 외부에 노출하기보다, 응답용 DTO로 변환..
ID값을 UUID v4에서 UUID v7로 변경한 이유
·
Back-End/Spring
이전 글에서는 API 테스트 과정에서 UUID만으로는 도메인 데이터를 식별하기 어렵다는 문제를 해결하기 위해 도메인 prefix + UUID 형태의 code를 생성하도록 개선한 과정을 정리했다. 이번 글에서는 그 이후 code에 사용하던 UUID를 v4에서 v7로 변경한 이유를 정리해보려고 한다.기존 방식처음에는 Java에서 기본으로 제공하는 UUID.randomUUID()를 사용했다.private String createUuid() { return UUID.randomUUID().toString();}이 방식은 간단하고 고유한 값을 생성하기에 충분했다.생성되는 code는 다음과 같은 형태였다.product-69a29997-2c97-433b-a3a5-1656721a4efc하지만 PR 리뷰 과정에서 ..
UUID만으로 구분하기 어려운 데이터를 @PrePersist로 개선해보기
·
Back-End/Spring
프로젝트에서 여러 도메인 엔티티를 설계하면서 공통 필드를 관리하기 위해 BaseEntity를 사용하고 있었다.각 엔티티는 공통적으로 id, code, createdAt, updatedAt과 같은 필드를 가지고 있었고, 이를 반복해서 선언하지 않기 위해 공통 상위 클래스로 분리했다.하지만 API 테스트를 진행하면서 한 가지 불편함이 생겼다.문제 상황초기에는 code 값을 UUID만으로 생성하고 있었다.UUID는 고유성을 보장하기에는 적합하지만, 값만 보고 어떤 도메인의 데이터인지 파악하기는 어려웠다.예를 들어 여러 도메인의 데이터가 모두 UUID 형태로 생성되면 다음과 같이 보인다.69a29997-2c97-433b-a3a5-1656721a4efc7b421a11-8e45-4b62-a9d7-4214378812..