spring 11

[쇼핑몰 프로젝트 - 리팩토링] 상품 목록 조회 최적화: N+1 해결 & 인덱스로 31% 개선

0. 개요기존 쇼핑몰 프로젝트의 카테고리별 도서 조회에서 N+1 문제와 조회 속도가 느리다는 문제점이 있었다.리팩토링을 통해 N+1 문제를 해결하고, 인덱스를 통해 조회 속도를 개선했다.이 글은 그 과정을 정리한 글이다.1. 기존 구조의 문제점먼저 프론트를 보면, 해당 페이지에서 필요한 데이터는 bookId, title, price, salePrice, amount 5개다. 하지만 백엔드는 아래 메서드로 엔티티(Page) 자체를 반환했다. 이러한 문제 때문에 N+1 문제가 발생했다.한 번의 리스트 쿼리로 books_categories를 가져온 뒤, 행마다 Book을 개별 조회했다.Book.publisher가 EAGER라 출판사까지 매번 조인됐다.20개의 책을 가져오는데, 결과적으로 요청 1번에 쿼리 23..

[쇼핑몰 프로젝트 - 리팩토링] 카테고리 N+1 문제 해결과 Redis 캐싱 적용

0. 개요쇼핑몰 프로젝트의 카테고리는 재귀적 계층 구조로 설계되어 있었다. 처음에는 백엔드에서 전체 카테고리 트리를 재귀적으로 내려주는 방식이었지만, 이 방식은 N+1 문제를 유발했고, 그로 인해 쿼리 수가 과도하게 늘어나고 응답 속도도 매우 느려지는 문제가 있었다. 당시에는 시간적인 여유가 없어서, 문제를 임시로 해결하기 위해 프론트 서버에서 아래와 같이 로직을 구성한 것 같다.최상위 카테고리 목록을 먼저 /topCategories API로 조회.그 중 자식이 있는 카테고리에 대해 /subCategories/{id} API를 반복 호출.이 방식은 겉보기에 구조를 분리한 것처럼 보이지만, 결국 카테고리 개수만큼 API 요청이 발생하게 되면서 N+1 문제를 DB 쿼리에서 API 요청 수준으로 옮긴 것에 불..

[쇼핑몰 프로젝트] FeignClient 에러 처리: ErrorDecoder

0. 개요쇼핑몰 프로젝트를 혼자 다시 만들면서, FeignClient 사용 중 발생하는 예외 처리 방식에 대해 고민하게 되었다. 당시 프로젝트는 MSA 구조였고, 각 API 내부에서는 상황에 맞는 적절한 예외 처리를 하고 있었다. 문제는, 서버 간 통신을 위해 FeignClient로 요청을 보낼 때 발생했다. 요청을 받은 서버는 어떤 예외가 발생했는지 알고 있었지만, 요청을 보낸 서버에서는 모든 예외가 그냥 FeignException 하나로 퉁쳐졌다. 그 결과, 실제 어떤 오류가 발생했는지를 확인하려면 요청을 받은 서버의 로그를 직접 확인해야 했고, 이는 디버깅이나 예외 추적에 있어 꽤 큰 불편함이었다. 그때는 프로젝트 마감 기한이 촉박해서 그냥 넘겼지만, 이번에 혼자 다시 프로젝트를 진행하면서 이 문제..

[쇼핑몰 프로젝트] Spring 예외 처리: ErrorCode 기반 공통 처리로 단순화하기

0. 개요쇼핑몰 프로젝트를 혼자 새로 만들면서, 예외 처리 방식을 어떻게 설계할 것인가에 대해 많은 고민을 하게 되었다.이전 팀 프로젝트에서는 예외 클래스를 도메인별로 세분화하고 상속 구조를 활용해 관리했지만, 예외 종류가 많아질수록 클래스 수가 급격히 늘어나고, 각 예외마다 @ExceptionHandler를 추가해야 하는 번거로움이 뒤따랐다. 결국 이러한 관리 부담과 중복 처리 문제를 해결하기 위해, ErrorCode Enum과 공통 예외 클래스를 활용한 단순화된 예외 처리 방식으로 전환하게 되었다. 이 글에서는 기존 방식의 한계와 그로 인한 고민 과정을 소개하고, ErrorCode 기반 예외 처리 방식으로 어떻게 개선했는지를 정리해본다.1. 기존 방식: 예외 클래스를 세분화하여 상속 구조로 관리이전 ..

[쇼핑몰 프로젝트] Spring Cloud Gateway와 OpenFeign을 같이 사용하면 발생하는 문제

0. 개요쇼핑몰 프로젝트를 혼자 다시 구현하면서, 이전과는 달리 refresh token과 블랙리스트 기능을 새롭게 추가하게 되면서 gateway 로직을 다시 짜게 되었다.Gateway는 클라이언트 요청의 Authorization 헤더에 담긴 JWT를 검증하고, 인증 API에 요청을 보내서 해당 토큰이 블랙리스트에 등록된 것인지 확인한 후, 토큰 안에 담긴 사용자 정보를 파싱해 다음을 헤더에 추가해서 백엔드 API에 전달한다.X-MEMBER-ID: 사용자 식별자X-MEMBER-ROLES: 사용자 권한처음에는 인증 서버와 통신하기 위해 Spring Cloud OpenFeign을 사용했지만, Spring Cloud Gateway는 Spring WebFlux 기반의 비동기 논블로킹 환경에서 동작하기 때문에, ..

[쇼핑몰 프로젝트] Spring JWT 재발급 로직에서 Interceptor 대신 Filter를 선택한 이유 (+Spring Cloud OpenFeign, Spring MVC)

0. 개요쇼핑몰 프로젝트를 혼자 다시 구현하면서, 이전에 구현하지 못했던 Refresh Token 기반의 재발급 로직과 블랙리스트 기능을 새롭게 추가했다. Access Token이 만료됐을 때, 쿠키에 담긴 Refresh Token을 활용해 Auth 서버가 Redis에서 해당 토큰의 존재 여부를 확인하고, 유효한 경우 Access Token을 자동으로 재발급하는 구조를 구현했다. 로그인 후 발급되는 Access/Refresh Token은 모두 HttpOnly 쿠키로 설정되며, 이후 모든 요청에 함께 전달된다. 이 점을 활용해, 로그인한 사용자의 요청을 서버에서 선처리하고, Refresh Token은 존재하지만 Access Token이 없거나 만료된 경우 Auth 서버로 재발급 요청을 보내도록 설계했다. ..

[Spring Error] Error creating bean with name 'jpaAuditingHandler':

0. 개요@WebMvcTest를 활용해서 컨트롤러에 대한 테스트 코드를 작성하고 실행 중, 아래와 같은 오류가 발생했다.이번 포스팅에서는 이 오류의 원인과 해결 방법을 공유하려고 한다.Error creating bean with name 'jpaAuditingHandler': Cannot resolve reference to bean 'jpaMappingContext' while setting constructor argument1. 문제해당 오류는 jpaAuditingHandler 빈을 생성하는 동안 jpaMappingContext 빈을 찾을 수 없다는 BeanCreationException이다. 이 문제는 Spring Data JPA에서 발생하며, 보통 @EnableJpaAuditing을 활성화하지 ..

Spring 2025.05.01

[쇼핑몰 프로젝트] Spring MVC 구조 - 단위 테스트 코드 작성하기

0. 개요쇼핑몰 프로젝트의 코드는 MVC 구조로 작성했다.Controller - Service - Repository 이렇게 크게 3가지의 구조인데, 각각의 단위 테스트를 어떻게 했는지 공유하려고 한다. 처음에는 @SpringBootTest를 사용해서 통합 테스트를 진행했지만, 애플리케이션의 설정과 모든 빈을 로드하는 과정에서 시간이 오래 걸렸다. 코드가 점점 복잡해지면서 테스트가 느려졌기 때문에, 최종적으로는 단위 테스트로 방향을 바꿔 진행하기로 했다. 테스트 코드는 when - given - then 형식으로 작성해야 한다.1. 의존성 추가: spring-boot-starter-testJUnit 5, AssertJ, Mockito, JsonPath ...와 같은 유용한 라이브러리를 제공한다. o..

[쇼핑몰 프로젝트] 분산 환경에서 데이터 캐싱 - Redis

0. 개요장바구니 로직을 설계하던 중, 사용자가 장바구니를 이용할 때마다 DB에서 매번 데이터를 넣고 가져오는 방식은 비효율적이라 생각했다. 그래서 장바구니 데이터를 세션으로 관리하고, 특정 시점(세션 종료, 로그아웃)에만 DB에 백업하여 저장하는 방식으로 설계를 진행했다.1. 문제서버 이중화 환경에서, 세션 통해 장바구니를 관리하면 세션 불일치 문제가 발생했다.서버가 이중화된 환경에서 사용자가 장바구니 세션이 없는 다른 서버로 요청이 가면 장바구니 기능이 제대로 동작하지 않는 문제가 발생했다.사용자가 많을 경우, 서버 메모리의 부담이 커진다.장바구니는 사용자가 자주 또 많이 이용하는 기능이다. 이는 서버의 메모리 사용량을 크게 증가시켜, 서버의 성능 저하로 인해 서비스의 품질에 영향이 갈 것이라 생각했..