Conversation
- QueryDSL 설정 - 테스트 초기값 생성 로직 추가
WalkthroughQueryDSL을 Gradle에 통합하고 팔로우 리스트 조회 API를 구현했습니다. JPAQueryFactory 설정, 커서 기반 페이지네이션을 지원하는 리포지토리 구현, 그리고 컨트롤러 엔드포인트를 추가했으며, 로컬 프로필용 테스트 데이터 초기화 로직을 도입했습니다. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant Controller as UserController
participant Service as FollowService
participant Repository as FollowRepositoryImpl
participant Database as JPA/Database
Client->>Controller: GET /api/v1/users/{userId}/follow?cursor=X&size=20
activate Controller
Controller->>Service: followList(userId, cursor, size)
activate Service
Service->>Repository: existsById(userId)
activate Repository
Repository-->>Service: true/false
deactivate Repository
alt User not found
Service-->>Controller: UserNotFoundException
Controller-->>Client: 404 Not Found
else User exists
Service->>Repository: findFollowingList(userId, cursor, size)
activate Repository
Repository->>Database: QueryDSL SELECT (follow.id, user.id, user.profileImage, etc.)
activate Database
Database-->>Repository: List<FollowResponse>
deactivate Database
Repository-->>Service: List<FollowResponse>
deactivate Repository
Service->>Service: compute nextCursor (last item.followId or null)
Service-->>Controller: FollowListResponse(items, nextCursor)
deactivate Service
end
Controller-->>Client: 200 OK + ApiResponse<FollowListResponse>
deactivate Controller
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 주의 깊은 검토가 필요한 영역:
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/main/java/team/wego/wegobackend/group/presentation/GroupImageController.java (1)
62-62: 명시적 value 속성은 선택적 스타일 변경입니다.47번 라인과 동일하게, 명시적
value속성 사용은 스타일 일관성을 위한 선택적 변경입니다.
🧹 Nitpick comments (7)
src/main/java/team/wego/wegobackend/group/presentation/GroupImageController.java (2)
47-47: 명시적 value 속성은 선택적 스타일 변경입니다.
@PatchMapping("/{groupId}")에서@PatchMapping(value = "/{groupId}")로의 변경은 기능적 차이가 없는 스타일 일관성을 위한 변경입니다. 코드베이스 전체에서 일관된 스타일을 유지하고자 한다면 의미가 있지만, 필수는 아닙니다.
35-45: 사전 업로드 단계이므로userDetails매개변수의 필요성을 확인하세요.이 메소드는 2단계 이미지 업로드 프로세스의 첫 단계입니다. API 문서에 따르면
uploadImages는 S3에 이미지를 먼저 업로드하고 URL을 반환하는 "사전 업로드" 단계이며,updateGroupImages에서 실제로 그룹과 연결될 때 HOST 권한을 검증합니다.uploadGroupImages서비스 메소드가 userDetails를 받지 않는 것은 의도된 설계입니다. 다만 컨트롤러의@AuthenticationPrincipal CustomUserDetails userDetails매개변수가 실제로 사용되지 않으므로, 향후 기능(예: 사용자별 업로드 할당량 제한, 감사 로깅)이 필요하지 않다면 제거를 고려하세요.src/main/java/team/wego/wegobackend/common/config/QuerydslConfig.java (1)
12-17: 생성자 주입 방식 고려현재
@PersistenceContext필드 주입을 사용하고 있습니다. Spring의 권장 사항에 따라 생성자 주입 방식을 고려해보세요.다음과 같이 리팩터링할 수 있습니다:
@Configuration public class QuerydslConfig { - @PersistenceContext - private EntityManager entityManager; + private final EntityManager entityManager; + + public QuerydslConfig(@PersistenceContext EntityManager entityManager) { + this.entityManager = entityManager; + } @Bean public JPAQueryFactory jpaQueryFactory() { return new JPAQueryFactory(entityManager); } }src/main/java/team/wego/wegobackend/user/application/FollowService.java (1)
75-86: 페이지네이션 로직 개선 고려현재 구현은 리스트가 비어있지 않으면 항상 마지막 아이템의
followId를nextCursor로 반환합니다. 이는 실제로 더 많은 데이터가 있는지 확인하지 않아, 클라이언트가 빈 결과를 반환받는 불필요한 추가 요청을 할 수 있습니다.더 나은 사용자 경험을 위해
size+1개의 아이템을 조회하고,(size+1)번째 아이템이 존재하는지 확인한 후size개로 자르는 방식을 고려해보세요.다음과 같이 개선할 수 있습니다:
public FollowListResponse followList(Long userId, Long cursor, Integer size) { if(!userRepository.existsById(userId)) { throw new UserNotFoundException(); } - List<FollowResponse> list = followRepository.findFollowingList(userId, cursor, size); + List<FollowResponse> list = followRepository.findFollowingList(userId, cursor, size + 1); - Long nextCursor = list.isEmpty() ? null : list.getLast().getFollowId(); + Long nextCursor = null; + if (list.size() > size) { + nextCursor = list.get(size - 1).getFollowId(); + list = list.subList(0, size); + } return new FollowListResponse(list, nextCursor); }src/main/java/team/wego/wegobackend/common/support/LocalDataInitializer.java (2)
4-4:jakarta.transaction.Transactional대신 Spring의@Transactional사용 권장
jakarta.transaction.Transactional은 Spring의 트랜잭션 관리와 완전히 통합되지 않을 수 있습니다. Spring 환경에서는org.springframework.transaction.annotation.Transactional을 사용하는 것이 더 안전합니다.-import jakarta.transaction.Transactional; +import org.springframework.transaction.annotation.Transactional;
16-18:@Configuration대신@Component사용 권장
@Configuration은 주로@Bean메서드를 포함하는 설정 클래스에 사용됩니다.ApplicationRunner구현체는@Component를 사용하는 것이 의미적으로 더 적합합니다.-@Configuration +@Component @Profile("local") public class LocalDataInitializer implements ApplicationRunner {src/main/java/team/wego/wegobackend/user/repository/FollowRepositoryImpl.java (1)
46-50: 메서드 이름 컨벤션 수정 권장Java 메서드 이름은 소문자로 시작해야 합니다.
ItCursor를ltCursor또는lessThanCursor로 변경하세요.- private BooleanExpression ItCursor(Long cursorFollowId) { + private BooleanExpression ltCursor(Long cursorFollowId) { return cursorFollowId == null ? null : QFollow.follow.id.lt(cursorFollowId); }Line 39의 호출부도 함께 수정해야 합니다:
- ItCursor(cursorFollowId) + ltCursor(cursorFollowId)
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
build.gradle(1 hunks)src/main/java/team/wego/wegobackend/common/config/QuerydslConfig.java(1 hunks)src/main/java/team/wego/wegobackend/common/support/LocalDataInitializer.java(1 hunks)src/main/java/team/wego/wegobackend/group/presentation/GroupImageController.java(4 hunks)src/main/java/team/wego/wegobackend/user/application/FollowService.java(2 hunks)src/main/java/team/wego/wegobackend/user/application/dto/response/FollowListResponse.java(1 hunks)src/main/java/team/wego/wegobackend/user/application/dto/response/FollowResponse.java(1 hunks)src/main/java/team/wego/wegobackend/user/presentation/UserController.java(4 hunks)src/main/java/team/wego/wegobackend/user/presentation/UserControllerDocs.java(3 hunks)src/main/java/team/wego/wegobackend/user/repository/FollowRepository.java(1 hunks)src/main/java/team/wego/wegobackend/user/repository/FollowRepositoryCustom.java(1 hunks)src/main/java/team/wego/wegobackend/user/repository/FollowRepositoryImpl.java(1 hunks)src/test/http/user/user-api.http(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/main/java/team/wego/wegobackend/user/application/FollowService.java (2)
src/main/java/team/wego/wegobackend/user/repository/FollowRepositoryImpl.java (1)
RequiredArgsConstructor(12-51)src/main/java/team/wego/wegobackend/user/presentation/UserController.java (1)
Slf4j(34-184)
src/main/java/team/wego/wegobackend/user/presentation/UserController.java (2)
src/main/java/team/wego/wegobackend/user/repository/FollowRepositoryImpl.java (1)
RequiredArgsConstructor(12-51)src/main/java/team/wego/wegobackend/user/application/FollowService.java (1)
Slf4j(20-87)
src/main/java/team/wego/wegobackend/common/config/QuerydslConfig.java (1)
src/main/java/team/wego/wegobackend/common/support/LocalDataInitializer.java (1)
Configuration(16-48)
src/main/java/team/wego/wegobackend/common/support/LocalDataInitializer.java (1)
src/main/java/team/wego/wegobackend/common/config/QuerydslConfig.java (1)
Configuration(9-19)
🔇 Additional comments (12)
src/main/java/team/wego/wegobackend/group/presentation/GroupImageController.java (1)
34-34: 멀티파트 요청 명시 추가를 승인합니다.
consumes = MediaType.MULTIPART_FORM_DATA_VALUE속성 추가는 API 계약을 명확히 하고, 잘못된 Content-Type 헤더를 가진 요청을 거부하도록 합니다. 멀티파트 엔드포인트에 대한 모범 사례입니다.src/main/java/team/wego/wegobackend/user/application/dto/response/FollowResponse.java (1)
1-29: 잘 구현된 DTO입니다불변 객체로 설계되었고 QueryDSL 프로젝션을 위한
@QueryProjection어노테이션이 적절히 사용되었습니다. 커서 페이지네이션을 위한followId필드도 명확하게 표시되어 있습니다.src/test/http/user/user-api.http (1)
80-92: 효과적인 테스트 케이스 추가커서 기반 페이지네이션의 초기 요청과 다음 페이지 요청을 모두 테스트하는 케이스가 잘 작성되었습니다.
nextCursor변수를 활용한 연속 요청 테스트도 적절합니다.src/main/java/team/wego/wegobackend/user/repository/FollowRepository.java (1)
7-7: 표준 패턴에 따른 확장
FollowRepositoryCustom인터페이스 확장을 통해 커스텀 쿼리 메서드를 추가하는 표준적인 접근 방식입니다.src/main/java/team/wego/wegobackend/user/application/dto/response/FollowListResponse.java (1)
1-7: 간결하고 명확한 응답 구조Java record를 사용한 불변 응답 DTO 설계가 적절합니다. 페이지네이션에 필요한
items와nextCursor필드가 명확하게 정의되어 있습니다.src/main/java/team/wego/wegobackend/user/presentation/UserControllerDocs.java (1)
59-64: 잘 작성된 API 문서파라미터 설명과 검증 제약조건이 명확하게 문서화되어 있습니다. 크기 제한(1-100)과 기본값(20) 설정도 적절합니다.
src/main/java/team/wego/wegobackend/user/application/FollowService.java (1)
77-79: 사용자 존재 확인은 적절합니다
existsById체크를 통해 명확한 에러 메시지를 제공하는 것은 좋은 접근입니다. 추가 쿼리 비용이 발생하지만, 사용자 경험 측면에서 가치가 있습니다.src/main/java/team/wego/wegobackend/user/repository/FollowRepositoryCustom.java (1)
6-13: LGTM!커서 기반 페이지네이션을 위한 커스텀 레포지토리 인터페이스가 잘 정의되어 있습니다. Spring Data JPA의
Impl접미사 규칙과 결합하여 사용될 수 있도록 설계되어 있습니다.src/main/java/team/wego/wegobackend/common/support/LocalDataInitializer.java (1)
30-38: 평문 비밀번호 사용 확인 필요비밀번호가 평문으로 저장되고 있습니다. 실제 로그인 테스트 시
PasswordEncoder로 인코딩된 비밀번호가 필요하다면, 여기서도 인코딩된 값을 사용해야 합니다. 로컬 테스트에서 인증 기능을 사용할 계획이라면 확인이 필요합니다.src/main/java/team/wego/wegobackend/user/repository/FollowRepositoryImpl.java (1)
17-44: LGTM!QueryDSL을 사용한 커서 기반 페이지네이션 구현이 잘 되어 있습니다.
where절에서null반환 시 조건이 무시되는 QueryDSL의 동작을 적절히 활용하고 있습니다.src/main/java/team/wego/wegobackend/user/presentation/UserController.java (2)
60-60: LGTM!
consumes = MediaType.MULTIPART_FORM_DATA_VALUE추가는 파일 업로드 엔드포인트에 적절한 명시적 선언입니다.
138-153: 팔로우 리스트 조회 API 구현 적절합니다.커서 기반 페이지네이션 파라미터와 기본값 설정이 잘 되어 있습니다.
userId를 PathVariable로 받아 다른 사용자의 팔로우 리스트도 조회할 수 있는 설계가 좋습니다.
| //QueryDSL | ||
| implementation 'com.querydsl:querydsl-jpa:5.1.0:jakarta' | ||
| annotationProcessor 'com.querydsl:querydsl-apt:5.1.0:jakarta' | ||
| annotationProcessor 'jakarta.annotation:jakarta.annotation-api' | ||
| annotationProcessor 'jakarta.persistence:jakarta.persistence-api' | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# QueryDSL 최신 버전 확인
echo "=== QueryDSL 최신 버전 확인 ==="
curl -s "https://search.maven.org/solrsearch/select?q=g:com.querydsl+AND+a:querydsl-jpa&rows=5&wt=json" | jq -r '.response.docs[] | "\(.v)"' | head -5
echo ""
echo "=== Maven Central에서 5.1.0 검색 ==="
curl -s "https://search.maven.org/solrsearch/select?q=g:com.querydsl+AND+a:querydsl-jpa+AND+v:5.1.0&wt=json" | jq -r '.response.docs[] | "Version: \(.v), Timestamp: \(.timestamp)"'Repository: WeGo-Together/WeGo_BackEnd
Length of output: 175
🏁 Script executed:
#!/bin/bash
# Maven Central에서 QueryDSL 정보 더 자세히 확인
echo "=== Maven Central API로 최신 버전 확인 ==="
curl -s "https://search.maven.org/solrsearch/select?q=g:com.querydsl+AND+a:querydsl-jpa&rows=20&wt=json&sort=timestamp+desc" | jq '.response.docs[0:5] | .[] | {version: .v, timestamp: .timestamp}'
echo ""
echo "=== QueryDSL 보안 정보 확인 ==="
# NVD에서 QueryDSL 취약점 확인
curl -s "https://services.nvd.nist.gov/rest/json/cves/2.0?keywordSearch=querydsl" | jq '.vulnerabilities[]? | {id: .cve.id, summary: .cve.descriptions[0].value}' | head -20Repository: WeGo-Together/WeGo_BackEnd
Length of output: 497
🏁 Script executed:
#!/bin/bash
# QueryDSL 5.1.x 및 6.x 버전 확인
echo "=== QueryDSL 5.1.x 패치 버전 확인 ==="
curl -s "https://search.maven.org/solrsearch/select?q=g:com.querydsl+AND+a:querydsl-jpa+AND+v:5.1.*&rows=10&wt=json" | jq '.response.docs[] | {version: .v, timestamp: .timestamp}' | sort
echo ""
echo "=== QueryDSL 최신 안정 버전 (6.x) 확인 ==="
curl -s "https://search.maven.org/solrsearch/select?q=g:com.querydsl+AND+a:querydsl-jpa&rows=30&wt=json" | jq '.response.docs | sort_by(.timestamp) | reverse | .[0:3] | .[] | {version: .v, timestamp: .timestamp}'
echo ""
echo "=== CVE-2024-49203 상세 정보 ==="
curl -s "https://services.nvd.nist.gov/rest/json/cves/2.0?keywordSearch=CVE-2024-49203" | jq '.vulnerabilities[0] | {id: .cve.id, published: .cve.published, modified: .cve.lastModified, severity: .containers.cna.metrics[0].cvssV3_1.baseScore}'Repository: WeGo-Together/WeGo_BackEnd
Length of output: 406
QueryDSL 5.1.0에 SQL/HQL 인젝션 취약점(CVE-2024-49203) 발견
현재 사용 중인 QueryDSL 5.1.0에 CVE-2024-49203 취약점이 있습니다(2024년 11월 공개). 이는 JPAQuery의 orderBy에서 SQL/HQL 인젝션을 허용하는 취약점입니다. 신뢰할 수 없는 입력값을 orderBy에 직접 사용하지 않는다면 즉시적인 위험은 낮지만, 5.1.0은 패치 버전이 없으므로 최신 버전으로 업그레이드하는 것을 권장합니다.
🤖 Prompt for AI Agents
In build.gradle around lines 51 to 56, QueryDSL 5.1.0 is used which is
vulnerable to CVE-2024-49203; replace the QueryDSL coordinates (implementation
and both annotationProcessor entries) with a patched QueryDSL release (upgrade
5.1.0 → the latest patched version that addresses CVE-2024-49203), update the
version consistently for all three entries, refresh Gradle dependencies (e.g.,
./gradlew --refresh-dependencies), rebuild and run tests, and audit any use of
JPAQuery.orderBy to ensure untrusted input is not directly passed (sanitize or
whitelist ordering fields if needed).
| @GetMapping("/{userId}/follow") | ||
| public ResponseEntity<ApiResponse<FollowListResponse>> followList( | ||
| @PathVariable Long userId, //다른 유저 조회를 위한 파라메터 | ||
| @RequestParam(required = false) Long cursor, | ||
| @RequestParam(defaultValue = "20") @Min(1) @Max(100) Integer size | ||
| ) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n src/main/java/team/wego/wegobackend/user/presentation/UserController.java | head -20Repository: WeGo-Together/WeGo_BackEnd
Length of output: 1201
🏁 Script executed:
rg -n "@Validated|@RestController|@RequestMapping" src/main/java/team/wego/wegobackend/user/presentation/UserController.java | head -20Repository: WeGo-Together/WeGo_BackEnd
Length of output: 125
🏁 Script executed:
sed -n '30,45p' src/main/java/team/wego/wegobackend/user/presentation/UserController.javaRepository: WeGo-Together/WeGo_BackEnd
Length of output: 587
클래스에 @Validated 어노테이션 추가 필요
@RequestParam의 @Min(1)과 @Max(100) 검증이 작동하려면 컨트롤러 클래스에 @Validated 어노테이션이 필수입니다. 현재 클래스 선언부에 해당 어노테이션이 없습니다.
클래스 선언부에 @Validated 추가:
+import org.springframework.validation.annotation.Validated;
+
@Slf4j
@RestController
@RequiredArgsConstructor
+@Validated
@RequestMapping("/api/v1/users")
public class UserController implements UserControllerDocs {Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/user/presentation/UserController.java
around lines 141 to 146, the @Min/@Max constraints on the @RequestParam 'size'
won't be enforced because the controller class is missing the Spring Validation
trigger; add the @Validated annotation to the controller class declaration
(importing org.springframework.validation.annotation.Validated if needed) so
that parameter-level JSF validation annotations are activated by Spring MVC.
📝 Pull Request
📌 PR 종류
해당하는 항목에 체크해주세요.
✨ 변경 내용
팔로우 리스트를 조회할 수 있는 API 개발을 진행하였습니다.
이번 PR로 인해 변경되는 점은 아래와 같습니다.
팔로우 리스트 조회를 위한 QueryDSL 설정을 진행했습니다. (Gradle 의존성 추가, config 파일 추가)
테스트를 위한 초기값 세팅을 ApplicationRunner 인터페이스를 활용하여 추가해두었습니다. (
local프로필에서만 실행되도록 설정)팔로우 관련 레포지토리가 추가되었습니다. (
FollowRepositoryCustom,FollowRepositoryImpl)🔍 관련 이슈
해당 PR이 해결하는 이슈가 있다면 연결해주세요.
Closes #101
🧪 테스트
변경된 기능에 대한 테스트 범위 또는 테스트 결과를 작성해주세요.
초기값 생성 쿼리 로그 (User 1 ~ 100)
초기값 생성 쿼리 로그 (Follow 1번 -> 2~50)
.http 테스트 결과 (초기 조회, cursor = null)
.http 테스트 결과 (cursor != null)
.http 테스트 결과 (cursor is last)
🚨 확인해야 할 사항 (Checklist)
PR을 제출하기 전에 아래 항목들을 확인해주세요.
🙋 기타 참고 사항
팔로우 리스트 조회에 사용되는 cursor값은 Follow Entity의 id값으로 활용하였습니다.
또한 nextCursor의 값은 조회된 마지막 데이터의 id값을 주는 것으로 설정해두었습니다.
Summary by CodeRabbit
릴리스 노트
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.