QueryDSL을 사용하여 리뷰 조회 기능에 추가 할 페이지네이션 설정 중 발생한 트러블 슈팅들

금은체리·2024년 6월 11일
0

문제 1: IllegalArgumentException 오류 발생

문제 설명

REST API를 호출할 때 다음과 같은 오류가 발생했습니다:

java.lang.IllegalArgumentException: Name for argument of type [java.lang.Long] not specified, and parameter name information not available via reflection. Ensure that the compiler uses the '-parameters' flag.
문제 원인

이 오류는 Spring이 런타임에 메서드 인자의 이름을 확인할 수 없어서 발생하였습니다. 더 자세히 말하자면, @PathVariable 또는 @RequestParam 어노테이션이 있는 메서드 인자가 명시적으로 이름이 지정되지 않았을 때 발생합니다. 자바 컴파일러가 기본적으로 메서드 인자 이름을 클래스 파일에 포함하지 않기 때문에, Spring이 해당 정보를 사용할 수 없게 되기 때문에 생긴 문제입니다.

문제 해결 과정
  1. 빌드 설정 수정: build.gradle 파일에서 JavaCompile 태스크에 -parameters 플래그를 추가하여 자바 컴파일러가 메서드 인자 이름 정보를 클래스 파일에 포함하도록 설정했습니다.

    tasks.withType(JavaCompile).configureEach {
        options.compilerArgs += ["-parameters"]
    }
  2. 컨트롤러 메서드 수정: @PathVariable@RequestParam 어노테이션에 명시적으로 이름을 지정했습니다.

    @GetMapping("/cpUsers/{cpUserId}/reviews")
    public ResponseEntity<GlobalApiResponse<UserReviewListResponseDTO>> getReviewsWithVisibility(
            @AuthenticationPrincipal UserDetailsImpl userDetails,
            @PathVariable("cpUserId") Long cpUserId,
            @RequestParam("page") int page,
            @RequestParam("size") int size) {
        // 메서드 구현
    }
    
    @PostMapping("/reviews/{reviewId}/view")
    public ResponseEntity<GlobalApiResponse<ReviewResponseDTO>> viewReview(
            @AuthenticationPrincipal UserDetailsImpl userDetails,
            @PathVariable("reviewId") Long reviewId) {
        // 메서드 구현
    }
결과

이러한 수정 후, IllegalArgumentException 오류가 발생하지 않고, API가 정상적으로 작동했습니다. 이를 통해 컴파일러 플래그와 메서드 인자 이름의 중요성을 이해하게 되었습니다.


문제 2: QueryDSL 설정 문제

문제 설명

복잡한 쿼리를 작성하기 위해 QueryDSL을 사용하려고 했으나, 설정 문제로 인해 제대로 작동하지 않았습니다.

문제 원인

QueryDSL은 JPA와 함께 사용될 때, 적절한 설정과 의존성이 필요합니다. 특히, 컴파일 타임에 QueryDSL 관련 클래스를 생성하기 위해서는 빌드 설정이 올바르게 구성되어 있어야 합니다. 초기 설정에서는 이러한 부분이 누락되어 문제가 발생했습니다.

문제 해결 과정
  1. QueryDSL 의존성 추가: build.gradle 파일에 QueryDSL 관련 의존성을 추가했습니다.

    dependencies {
        implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
        annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
        annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
        annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
    }
  2. 빌드 설정 수정: QueryDSL의 소스 생성 디렉토리를 설정하고, 컴파일러가 이를 인식하도록 빌드 설정을 수정했습니다.

    def generated = 'src/main/generated'
    
    sourceSets {
        main {
            java {
                srcDirs = ['src/main/java', generated]
            }
        }
    }
    
    tasks.named('clean') {
        delete generated
    }
    
    tasks.withType(JavaCompile).configureEach {
        options.annotationProcessorPath = configurations.querydsl
        options.generatedSourceOutputDirectory = file(generated)
    }
    
    querydsl {
        library = "com.querydsl:querydsl-apt"
        jpa = true
        querydslSourcesDir = generated
    }
    
    compileQuerydsl {
        options.annotationProcessorPath = configurations.querydsl
    }
    
    tasks.withType(JavaCompile).matching { task -> task.name != 'compileQuerydsl' }.configureEach {
        dependsOn compileQuerydsl
    }
결과

QueryDSL 설정 후, 복잡한 쿼리를 문제없이 작성하고 실행할 수 있었습니다. 이를 통해 빌드 설정의 중요성과 QueryDSL의 유용성을 다시 한 번 확인할 수 있었습니다.


문제 3: 페이지네이션 문제

문제 설명

대량의 데이터를 처리할 때, 페이지네이션이 제대로 작동하지 않는 문제를 겪었습니다. 이는 성능 문제로 이어질 수 있었습니다.

문제 원인

페이지네이션을 구현하기 위해 PageablePageRequest를 사용하지 않았거나, 잘못 사용하여 문제가 발생했습니다.

문제 해결 과정
  1. 페이지네이션 구현: 서비스 레이어에서 PageablePageRequest를 사용하여 데이터를 페이징 처리하도록 수정했습니다.

    @Service
    @RequiredArgsConstructor
    public class ReviewService {
        private final ReviewRepository reviewRepository;
        private final UserReviewStatusRepository userReviewStatusRepository;
        private final UserRepository userRepository;
    
        @Transactional(readOnly = true)
        public UserReviewListResponseDTO getReviewsWithVisibility(Long cpUserId, Long userId, int page, int size) {
            Pageable pageable = PageRequest.of(page, size);
            Page<Review> reviews = reviewRepository.findReviewsByCpUser_CpUserId(cpUserId, pageable);
            List<UserReviewStatus> statuses = userReviewStatusRepository.findByUserAndReviewIn(userRepository.findById(userId)
                    .orElseThrow(() -> new ApiException(ErrorCode.USER_NOT_FOUND)), reviews.getContent());
    
            return new UserReviewListResponseDTO(reviews, statuses);
        }
    }
  2. 컨트롤러 수정: 페이지네이션을 지원하도록 컨트롤러 메서드를 수정했습니다.

    @GetMapping("/cpUsers/{cpUserId}/reviews")
    public ResponseEntity<GlobalApiResponse<UserReviewListResponseDTO>> getReviewsWithVisibility(
            @AuthenticationPrincipal UserDetailsImpl userDetails,
            @PathVariable("cpUserId") Long cpUserId,
            @RequestParam("page") int page,
            @RequestParam("size") int size) {
        Long userId = userDetails.getUserId();
        UserReviewListResponseDTO responseDTO = reviewService.getReviewsWithVisibility(cpUserId, userId, page, size);
    
        return ResponseEntity.status(SUCCESS_CP_USER_REVIEW_READ.getHttpStatus())
                .body(
                        GlobalApiResponse.of(
                                SUCCESS_CP_USER_REVIEW_READ.getMessage(),
                                responseDTO
                        )
                );
    }
결과

페이지네이션 기능이 정상적으로 작동하여 대량의 데이터를 효율적으로 처리할 수 있었습니다. 이를 통해 페이지네이션의 중요성을 실감할 수 있었으며, 대규모 데이터를 처리할 때 발생할 수 있는 성능 문제를 해결할 수 있었습니다.

profile
전 체리 알러지가 있어요!

0개의 댓글