Project 22

WebMvcTest + Spring REST doc + Swagger 적용 ( 2 / 2 - Spring REST doc + Swagger )

plugins { id 'java' id 'org.springframework.boot' version '3.4.3' id 'io.spring.dependency-management' version '1.1.7' id 'com.epages.restdocs-api-spec' version '0.19.4' // ✅ OpenAPI spec 생성용}dependencies { // ✅ REST Docs testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' // ✅ OpenAPI 3 스펙 변환 라이브러리 testImplementation 'com.epages:restdocs-api-spec-mo..

Project/Boilerplate 2025.03.29

WebMvcTest + Spring REST doc 적용 ( 1 / 2 - MockMvc )

WebMvcTest + Spring REST doc 을 적용 적용 이유: 테스트와 동시에 문서화를 자동화 할 수 있기에 문서 작성의 편리함 Spring REST doc을 선택한 이유: Swagger와 달리, 프로덕트 코드에 테스트 관련 코드가 포함되지 않음. 이후 Swagger UI만 가져와서, Spring REST doc과 Swagger의 장점을 합칠 예정 RestAssured가 아니라 MockMvc를 선택한 이유:RestAssured는 @SpringBootTest를 실행해야 하기 때문에 속도가 느림.  @WebMvcTest(GreetingController.class)는Spring에서 웹 계층(Controller) 만 테스트하기 위한 전용 어노테이션입니다.속도는 빠르면서도 HTTP 요청/응답 흐름을 ..

Project/Boilerplate 2025.03.28

필터 예외 처리

문제:OAuth2 인증 인가 도입 후, 필터측에서 잡는 에러에 대해서 200Ok, OAuth2 기본 로그인 html이 response로 응답됨. 원인:Spring Security는 Spring Context 바깥 쪽의 Filter에서 처리되기 때문에 Spring Context 내에서 예외 처리하는 @RestControllerAdivce로 예외 처리할 수 없음. 가장 마지막 필터인 AuthorizationFilter에서 인증이나 인가 예외가 터지면 이전 필터인 ExceptionTranslationFilter로 해당 예외들이 넘어가고, 이 필터에서 인증과 인가 오류에 대해 로그인 페이지 리디렉션을 보내기 때문에.  해결:authenticationEntryPoint와 accessDeniedHandler를 커스..

Project/Boilerplate 2025.03.17

API 예외 처리

기존 예외 처리 문제점IllegalArugmentException, IllegalStateException 등 혼용해서 쓰다 보니 헷갈림.IllegalArgumentException(UserError.USER_NOT_FOUND.getMessage()) 와 같은 방식으로 메세지를 전달하기 때문에 코드 중복이 많아짐.RestControllerAdvice에서 전역적으로 관리할 때, 새로운 클래스의 에러가 추가될 때 마다 계속해서 추가해주어야 함. 해결:에러 코드와 메세지를 관리할 ErrorCode 인터페이스public interface ErrorCode { String name(); HttpStatus getStatus(); String getMessage();} 도메인 별 에러코드@Gette..

Project/Boilerplate 2025.03.17

@PreAuthorize를 활용한 댓글 수정/삭제 권한 관리 및 코드 개선

기존 문제점: 관리자(Admin) 권한이 있어도 댓글 수정/삭제 불가기존 코드 분석현재 @AuthenticationPrincipal을 이용하여 컨트롤러에서 userId를 추출하고, 이를 서비스 계층에서 권한 검증하고 있다.@PutMapping("/{commentId}")public ResponseEntity updateComment( @AuthenticationPrincipal JwtUserDetails userPrincipal, @PathVariable Long commentId, @Valid @RequestBody CommentRequest request) { return ResponseEntity.ok( commentService.update( ..

Project/Boilerplate 2025.03.10

Cursor 페이지네이션에서의 정렬, 필터링

현재 모든 게시글을 불러올 때, 댓글 개수를 따로 조회하고 합치는 방식으로 구현되어 있음.이 방식의 문제점과 해결 방법을 살펴보자.1. 기존 방식의 문제점현재는 게시글을 먼저 불러오고, 댓글 개수를 따로 조회하여 합치는 방식을 사용하고 있다.✅ 기존 코드@Query("SELECT p FROM Post p " + "LEFT JOIN FETCH p.user " + "WHERE (:cursor IS NULL OR p.id findAllPostsByCursor(@Param("cursor") Long cursor, @Param("limit") int limit);@Query("SELECT c.post.id, COUNT(c) FROM Comment c WHERE c.post.id IN :postIds ..

Project/Boilerplate 2025.03.10

게시글 Pagination 적용 (2 / 2 - 인피니티 스크롤형 게시판)

게시글 Pagination 적용 (1 / 2 - 리스트형 게시판) 지난 글에서는 OFFSET 기반 페이지네이션을 구현했다. 하지만 OFFSET 방식은 데이터가 많아질수록 성능이 저하되는 문제가 있다.이번에는 커서(Cursor) 기반 페이지네이션을 적용해보겠다.1. Offset vs. Cursor 페이지네이션 차이구분Offset 페이지네이션Cursor 페이지네이션쿼리 방식OFFSET 1000 LIMIT 10WHERE id 읽기 연산량1,010개 읽고 1,000개 스킵10개만 읽음속도데이터가 많아질수록 OFFSET 연산이 느려짐OFFSET 없이 빠름일관성중간에 데이터 추가/삭제 시 결과가 틀어질 가능성 있음항상 정확한 데이터 반환결론:데이터가 적다면 OFFSET 기반도 괜찮지만, 데이터가 많아질수록 Curs..

Project/Boilerplate 2025.03.10

게시글 Pagination 적용 (1 / 2 - 리스트형 게시판)

현 상황현재 데이터베이스에는 다음과 같은 데이터 존재유저(User): 50명게시글(Post): 10,000개댓글(Comment): 각 게시글당 5개씩, 총 50,000개기존 성능 문제  기존 방식으로 전체 게시글을 조회하면 총 15,791ms 소요 또한, 10,000개의 게시글 모두 불러옴 기존 쿼리 분석기존에는 LEFT JOIN FETCH를 사용하여 게시글과 모든 댓글을 한 번에 불러오는 방식@Query("SELECT DISTINCT p FROM Post p " + "LEFT JOIN FETCH p.comments c " + "LEFT JOIN FETCH c.user " + "LEFT JOIN FETCH p.user")List findAllWithComments(); 문제점모든 댓글을 ..

Project/Boilerplate 2025.03.10

Mock 객체 테스트 시 필드 주의점

문제:@BeforeEachvoid setUp() { mockUser = new User("testEmail", "testUser", "password", "testName", Role.USER); mockPost = new Post("Test Title", "Test Content", 0, mockUser); mockComment = new Comment("Test Comment", mockPost, mockUser, null);}이후, mockUser.getId()를 가져오는 것에 실패. 원인:mock 객체이기 때문에 JPA 엔티티가 아니라 Id가 존재하지 않음 해결:@BeforeEachvoid setUp() { mockUser = new User("testEmail", "test..

Project/Boilerplate 2025.03.06

Spring Boot response로 header가 추가되지 않는 현상

문제:response.addHeader(JwtProperties.HEADER_AUTHORIZATION, JwtProperties.ACCESS_TOKEN_PREFIX + newAccessToken);스프링 서버에서 응답 헤더에 추가한 헤더가 Front 서버에서 출력했을 때, undefined로 출력됨 원인:configuration.setExposedHeaders(Collections.singletonList("Set-Cookie"));configuration.setExposedHeaders(Collections.singletonList("Authorization"));configuration.setExposedHeaders(Collections.singletonList("x-reissue-token"..

Project/Boilerplate 2025.03.06