[feat] 웨이블존 추천, 검색 응답 필드에 장애 시설 정보 추가#65
Conversation
|
""" Walkthrough웨이블존 추천 및 검색 응답에 장애 시설 정보가 통합되었습니다. 이를 위해 도메인, DTO, Elasticsearch 매핑, 컨트롤러, 서비스, 테스트 코드 등 여러 계층에 시설 관련 필드와 객체가 추가 및 확장되었습니다. 또한, 테스트에서는 JWT 인증과 시설 정보 검증이 강화되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller
participant Service
participant Repository
participant ES as Elasticsearch
Client->>Controller: GET /explore/maps (with JWT)
Controller->>Service: findByCondition(...)
Service->>Repository: search(...)
Repository->>ES: Query with facility fields
ES-->>Repository: Documents with facility data
Repository-->>Service: List<WaybleZoneDocument>
Service-->>Controller: List<WaybleZoneSearchResponseDto> (facility 포함)
Controller-->>Client: 응답 (facility 정보 포함)
Estimated code review effort4 (~75분) Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (6)
src/main/java/com/wayble/server/explore/repository/recommend/WaybleZoneQueryRecommendRepository.java (1)
142-142: 시설 정보 추가가 올바르게 구현되었습니다.
WaybleZoneRecommendResponseDto에 시설 정보를 포함하는 로직이 적절히 구현되었습니다.FacilityResponseDto.from()메서드를 사용하여 엔티티를 응답 DTO로 변환하는 방식도 일관성 있게 적용되었습니다.가독성 향상을 위해 전체 패키지명 대신 import 구문 사용을 고려해보세요:
+import com.wayble.server.explore.dto.FacilityResponseDto; - .facility(com.wayble.server.explore.dto.FacilityResponseDto.from(zone.getFacility())) + .facility(FacilityResponseDto.from(zone.getFacility()))src/main/java/com/wayble/server/explore/entity/EsWaybleZoneFacility.java (1)
20-33: null 처리 방식 개선 고려현재
facility가 null일 때 null을 반환하고 있는데, 이는 null 값이 전파될 수 있습니다. 기본값을 가진 객체를 반환하는 것을 고려해보세요.public static EsWaybleZoneFacility from(WaybleZoneFacility facility) { if (facility == null) { - return null; + return EsWaybleZoneFacility.builder() + .hasSlope(false) + .hasNoDoorStep(false) + .hasElevator(false) + .hasTableSeat(false) + .hasDisabledToilet(false) + .floorInfo("") + .build(); }src/main/java/com/wayble/server/explore/dto/FacilityResponseDto.java (1)
15-28: getter 메서드 네이밍 확인 필요
facility.isHasSlope()같은 getter 호출이 다소 어색합니다.EsWaybleZoneFacility엔티티의 boolean 필드 getter가isHasSlope()형태로 생성되는 것 같은데, 일반적으로 boolean 필드의 getter는hasSlope()또는isSlope()형태가 더 자연스럽습니다.엔티티 클래스의 필드명이나 getter 생성 방식을 확인해보시기 바랍니다.
src/main/java/com/wayble/server/explore/entity/WaybleZoneDocument.java (1)
48-48: TODO 주석 처리 필요이미지 경로 관련 TODO가 남아있습니다. 이미지 URL 처리 로직이 완성되었다면 TODO를 제거하거나, 추가 작업이 필요하다면 구체적인 내용으로 업데이트해주세요.
이미지 경로 처리와 관련된 추가 구현이 필요하신가요? 새로운 이슈를 생성해드릴까요?
src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java (2)
410-434: 테스트 간 코드 중복
generateRandomBirthDate()와createRandomFacility()메서드가WaybleZoneRecommendApiIntegrationTest와 동일합니다.공통 테스트 유틸리티 클래스로 추출하여 코드 중복을 제거하는 것을 고려해보세요.
+// 새로운 테스트 유틸리티 클래스 생성 +public class WaybleZoneTestUtils { + public static LocalDate generateRandomBirthDate() { + // 현재 구현 이동 + } + + public static WaybleZoneFacility createRandomFacility(int seed) { + // 현재 구현 이동 + } +}
94-94: 테스트 데이터 샘플 크기 불일치추천 API 테스트는 100개, 검색 API 테스트는 1000개의 샘플을 사용합니다. 테스트 일관성을 위해 동일한 크기를 사용하거나, 차이가 있는 이유를 문서화하는 것이 좋겠습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
build.gradle(1 hunks)src/main/java/com/wayble/server/common/entity/BaseEntity.java(1 hunks)src/main/java/com/wayble/server/explore/controller/WaybleZoneSearchController.java(1 hunks)src/main/java/com/wayble/server/explore/dto/FacilityResponseDto.java(1 hunks)src/main/java/com/wayble/server/explore/dto/recommend/WaybleZoneRecommendResponseDto.java(3 hunks)src/main/java/com/wayble/server/explore/dto/search/WaybleZoneDocumentRegisterDto.java(2 hunks)src/main/java/com/wayble/server/explore/dto/search/WaybleZoneSearchResponseDto.java(3 hunks)src/main/java/com/wayble/server/explore/entity/EsWaybleZoneFacility.java(1 hunks)src/main/java/com/wayble/server/explore/entity/WaybleZoneDocument.java(3 hunks)src/main/java/com/wayble/server/explore/repository/recommend/WaybleZoneQueryRecommendRepository.java(1 hunks)src/main/java/com/wayble/server/explore/service/WaybleZoneRecommendService.java(3 hunks)src/main/java/com/wayble/server/user/repository/UserRepository.java(1 hunks)src/main/java/com/wayble/server/wayblezone/controller/WaybleZoneController.java(2 hunks)src/main/resources/elasticsearch/settings/wayble_zone_mappings.json(1 hunks)src/test/java/com/wayble/server/explore/WaybleZoneRecommendApiIntegrationTest.java(12 hunks)src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java(11 hunks)
🧰 Additional context used
🧠 Learnings (3)
src/main/java/com/wayble/server/user/repository/UserRepository.java (1)
Learnt from: seung-in-Yoo
PR: #37
File: src/main/java/com/wayble/server/user/entity/User.java:61-62
Timestamp: 2025-07-13T15:08:59.318Z
Learning: Java에서 같은 패키지 내의 클래스들은 import 구문 없이 서로를 참조할 수 있다. com.wayble.server.user.entity 패키지 내의 클래스들은 명시적인 import 없이 사용 가능하다.
src/test/java/com/wayble/server/explore/WaybleZoneRecommendApiIntegrationTest.java (1)
Learnt from: seung-in-Yoo
PR: #37
File: src/main/java/com/wayble/server/user/entity/User.java:61-62
Timestamp: 2025-07-13T15:08:59.318Z
Learning: Java에서 같은 패키지 내의 클래스들은 import 구문 없이 서로를 참조할 수 있다. com.wayble.server.user.entity 패키지 내의 클래스들은 명시적인 import 없이 사용 가능하다.
src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java (1)
Learnt from: seung-in-Yoo
PR: #37
File: src/main/java/com/wayble/server/user/entity/User.java:61-62
Timestamp: 2025-07-13T15:08:59.318Z
Learning: Java에서 같은 패키지 내의 클래스들은 import 구문 없이 서로를 참조할 수 있다. com.wayble.server.user.entity 패키지 내의 클래스들은 명시적인 import 없이 사용 가능하다.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: run-docker-image-on-ec2
🔇 Additional comments (14)
src/main/java/com/wayble/server/common/entity/BaseEntity.java (1)
27-29: 소프트 삭제 복구 기능이 올바르게 구현되었습니다.
restore()메서드는deletedAt필드를null로 설정하여 소프트 삭제된 엔터티를 복구하는 기능을 제공합니다. 구현이 간단하고 명확하며, 기존의 소프트 삭제 패턴과 잘 어울립니다.src/main/java/com/wayble/server/explore/dto/search/WaybleZoneDocumentRegisterDto.java (1)
4-4: 시설 정보 필드 추가가 깔끔하게 구현되었습니다.
WaybleZoneDocumentRegisterDto에facility필드와 해당 import가 올바르게 추가되었습니다. Record 클래스의 간결한 구조를 유지하면서 새로운 시설 정보를 적절히 통합했습니다.Also applies to: 15-15
src/main/resources/elasticsearch/settings/wayble_zone_mappings.json (1)
11-33: Elasticsearch 매핑 구조가 적절하게 설계됨새로 추가된
facility객체 매핑이 잘 구성되어 있습니다:
- 장애 시설 관련 boolean 필드들이 적절하게 정의됨
floorInfo에 대한 keyword 타입 사용이 정확함- 필드명이 일관성 있고 설명적임
- Java 엔티티 구조와 잘 일치함
src/main/java/com/wayble/server/explore/dto/search/WaybleZoneSearchResponseDto.java (2)
28-31: DTO 구조 확장이 올바르게 구현됨검색 응답에 시설 정보가 적절하게 통합되었습니다. 필드 추가와 매핑 로직이 일관성 있게 구현되어 있습니다.
43-43: 팩토리 메서드 업데이트가 적절함
FacilityResponseDto.from()을 사용한 시설 정보 변환이 올바르게 구현되었습니다.src/main/java/com/wayble/server/explore/dto/recommend/WaybleZoneRecommendResponseDto.java (2)
27-27: 추천 응답 DTO에 시설 정보 통합 완료추천 응답에도 시설 정보가 일관성 있게 추가되어 검색 응답과 동일한 구조를 유지하고 있습니다.
48-48: 팩토리 메서드 업데이트 적절시설 정보 변환 로직이 올바르게 구현되어 있습니다.
src/main/java/com/wayble/server/explore/entity/EsWaybleZoneFacility.java (1)
6-18: 엔티티 설계가 잘 구현됨새로운
EsWaybleZoneFacility엔티티가 적절하게 설계되었습니다:
- 접근성 관련 boolean 필드들이 명확하게 정의됨
- Lombok 어노테이션 사용이 적절함
- 필드명이 일관성 있고 설명적임
- 접근 제어자가 적절하게 설정됨
src/main/java/com/wayble/server/explore/service/WaybleZoneRecommendService.java (1)
60-60: 접근 제한자 변경이 적절합니다내부에서만 사용되는 메서드들을
private으로 변경한 것은 캡슐화 원칙에 부합합니다.Also applies to: 75-75, 91-91
src/test/java/com/wayble/server/explore/WaybleZoneRecommendApiIntegrationTest.java (3)
78-78: 테스트 데이터 샘플 크기 감소샘플 크기가 1000에서 100으로 줄어들었습니다. 성능 개선을 위한 변경으로 보이지만, 추천 알고리즘의 정확도 테스트에 영향을 줄 수 있습니다. 테스트 커버리지가 충분한지 확인이 필요합니다.
271-277: 시설 정보 검증이 포괄적입니다모든 시설 관련 필드에 대한 null 체크가 적절히 구현되었습니다.
403-416: 재현 가능한 랜덤 데이터 생성시드를 사용한 Random 객체로 일관된 테스트 데이터를 생성하는 방식이 좋습니다. 테스트의 재현성을 보장합니다.
src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java (2)
147-147: 엔드포인트 경로 변경 확인
/maps경로로 변경된 것이 PR 목적에 명시된 대로입니다. 향후 특정 지역(XX동) 주변 TOP3 매장 조회 기능과 구분하기 위한 변경으로 이해됩니다.
186-194: 시설 정보 검증 로직이 완벽합니다검색 결과에 포함된 모든 시설 정보 필드를 빠짐없이 검증하고 있습니다.
| implementation 'com.amazonaws:aws-java-sdk-core:1.12.698' | ||
| implementation 'com.amazonaws:aws-java-sdk-s3:1.12.698' |
There was a problem hiding this comment.
중복된 AWS SDK 의존성을 제거해주세요.
AWS SDK 의존성이 두 번 선언되어 있습니다:
- 38-39번째 줄: 첫 번째 선언
- 48-49번째 줄: 두 번째 선언 (주석과 함께)
이런 중복 선언은 빌드 설정의 혼란을 야기할 수 있습니다.
다음과 같이 중복을 제거해주세요:
implementation 'jakarta.servlet:jakarta.servlet-api:6.0.0'
- implementation 'com.amazonaws:aws-java-sdk-core:1.12.698'
- implementation 'com.amazonaws:aws-java-sdk-s3:1.12.698'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
runtimeOnly 'com.mysql:mysql-connector-j'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
//AWS S3
implementation 'com.amazonaws:aws-java-sdk-core:1.12.698'
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.698'Also applies to: 48-49
🤖 Prompt for AI Agents
In build.gradle around lines 38-39 and 48-49, there are duplicate AWS SDK
dependencies declared for 'aws-java-sdk-core' and 'aws-java-sdk-s3'. Remove the
duplicate declarations on lines 48-49, keeping only one set of these
dependencies to avoid confusion in the build configuration.
| private final WaybleZoneSearchService waybleZoneSearchService; | ||
|
|
||
| @GetMapping("") | ||
| @GetMapping("/maps") |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
엔드포인트 경로 변경이 적절하게 구현되었습니다.
검색 API의 경로를 /maps로 변경하여 향후 추가될 TOP3 매장 조회 기능과 분리한 것은 좋은 설계 결정입니다. REST API의 리소스 구조가 더 명확해졌습니다.
기존 클라이언트나 테스트 코드에서 이 엔드포인트를 호출하는 부분이 있는지 확인해주세요:
🏁 Script executed:
#!/bin/bash
# 기존 엔드포인트 경로 사용 확인
echo "=== 테스트 코드에서 기존 경로 사용 확인 ==="
rg -A 3 -B 3 "/api/v1/wayble-zones/search\"" src/test/
echo "=== 전체 프로젝트에서 웨이블존 검색 API 호출 확인 ==="
rg -A 3 -B 3 "wayble-zones/search" --type java
echo "=== 새로운 /maps 경로 사용 확인 ==="
rg -A 3 -B 3 "search/maps" --type javaLength of output: 2563
REST 엔드포인트 경로 변경으로 인한 테스트 수정 필요
컨트롤러에서 엔드포인트를
GET /api/v1/wayble-zones/search → GET /api/v1/wayble-zones/search/maps
로 변경했으므로, 테스트 코드가 여전히 옛 경로를 참조해 실패합니다. 아래 파일을 수정해주세요:
- src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java
- private static final String baseUrl = "/api/v1/wayble-zones/search"; + private static final String baseUrl = "/api/v1/wayble-zones/search/maps";
위 수정 외에도 실제 클라이언트 호출이나 다른 테스트에서 구 경로를 참조하는 부분이 없는지 한번 더 검토 부탁드립니다.
🤖 Prompt for AI Agents
In
src/main/java/com/wayble/server/explore/controller/WaybleZoneSearchController.java
at line 24, the REST endpoint path was changed from GET
/api/v1/wayble-zones/search to GET /api/v1/wayble-zones/search/maps. Update all
test cases in
src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java
to use the new endpoint path /api/v1/wayble-zones/search/maps. Additionally,
review other client calls and tests to ensure no references remain to the old
endpoint path and update them accordingly.
| import org.springframework.data.jpa.repository.Query; | ||
| import org.springframework.data.repository.query.Param; |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
사용되지 않는 import 구문을 확인해주세요.
현재 코드에서 @Query와 @Param 어노테이션을 사용하는 메서드가 없습니다. 이 import 구문들이 향후 구현 예정인 기능을 위한 것인지, 아니면 실수로 추가된 것인지 확인이 필요합니다.
다음 스크립트로 이 import들의 실제 사용 여부를 확인해보겠습니다:
🏁 Script executed:
#!/bin/bash
# UserRepository에서 @Query와 @Param 사용 확인
echo "=== UserRepository 파일에서 @Query 사용 확인 ==="
rg "@Query" src/main/java/com/wayble/server/user/repository/UserRepository.java || echo "사용되지 않음"
echo "=== UserRepository 파일에서 @Param 사용 확인 ==="
rg "@Param" src/main/java/com/wayble/server/user/repository/UserRepository.java || echo "사용되지 않음"
echo "=== user 패키지 전체에서 UserRepository의 @Query/@Param 사용 확인 ==="
rg -A 5 -B 2 "(@Query|@Param)" src/main/java/com/wayble/server/user/Length of output: 2332
불필요한 import 제거 필요
src/main/java/com/wayble/server/user/repository/UserRepository.java 6–7행의
org.springframework.data.jpa.repository.Query 및
org.springframework.data.repository.query.Param 임포트는 현재 코드 내에서 사용되지 않습니다.
- 사용되지 않는 import는 코드 가독성을 저해하고 혼동을 줄 수 있으므로 제거를 권장합니다.
- 만약 향후 쿼리 메서드 구현을 위한 준비라면, TODO 주석 등으로 목적을 명시하거나 실제 구현 시에 import를 추가해주세요.
예시 diff:
- import org.springframework.data.jpa.repository.Query;
- import org.springframework.data.repository.query.Param;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import org.springframework.data.jpa.repository.Query; | |
| import org.springframework.data.repository.query.Param; |
🤖 Prompt for AI Agents
In src/main/java/com/wayble/server/user/repository/UserRepository.java at lines
6 to 7, remove the unused imports org.springframework.data.jpa.repository.Query
and org.springframework.data.repository.query.Param to improve code clarity. If
these imports are intended for future use, add a TODO comment explaining their
purpose; otherwise, delete them to avoid confusion.
| // TODO: JWT에서 userId 추출해서 상세 조회 기록 남기기 | ||
| // waybleZoneVisitLogService.saveVisitLog(null, waybleZoneId); | ||
| Long userId = (Long) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); | ||
| waybleZoneVisitLogService.saveVisitLog(userId, waybleZoneId); |
There was a problem hiding this comment.
인증 추출에서 안전성 검사 추가 필요
현재 구현에서 SecurityContextHolder.getContext().getAuthentication().getPrincipal()을 직접 Long으로 캐스팅하고 있어 잠재적인 ClassCastException 위험이 있습니다. 또한 인증 객체의 null 체크도 누락되어 있습니다.
다음과 같이 안전한 방식으로 개선하는 것을 권장합니다:
- // TODO: JWT에서 userId 추출해서 상세 조회 기록 남기기
- Long userId = (Long) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
- waybleZoneVisitLogService.saveVisitLog(userId, waybleZoneId);
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ if (authentication != null && authentication.getPrincipal() instanceof Long userId) {
+ waybleZoneVisitLogService.saveVisitLog(userId, waybleZoneId);
+ }🤖 Prompt for AI Agents
In
src/main/java/com/wayble/server/wayblezone/controller/WaybleZoneController.java
around lines 60 to 62, the code directly casts the principal from
SecurityContextHolder to Long without null checks or type verification, risking
ClassCastException. To fix this, first check if the Authentication object and
its principal are not null, then verify the principal's type before casting.
Extract the userId safely by handling cases where the principal is not a Long,
and only then call waybleZoneVisitLogService.saveVisitLog with the valid userId.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (12)
src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java (3)
132-152: 메서드명과 실제 구현이 일치하도록 수정하세요.
findTop3WaybleZonesByDistrict라는 메서드명은 특정 district의 상위 3개 zone을 찾는다는 의미를 전달하지만, 실제로는 방문 수 기준 상위 3개를 반환합니다. 메서드명을 더 명확하게 변경하는 것을 제안합니다.-public List<WaybleZoneDistrictResponseDto> findTop3WaybleZonesByDistrict(String district) { +public List<WaybleZoneDistrictResponseDto> findTop3WaybleZonesByDistrictOrderByVisitCount(String district) {
237-237: 정렬 로직을 null-safe하게 개선하세요.
visitCount()가 null을 반환할 가능성이 있다면NullPointerException이 발생할 수 있습니다.-.sorted((a, b) -> Long.compare(b.visitCount(), a.visitCount())) // 방문 수 내림차순 정렬 +.sorted((a, b) -> { + long aCount = a.visitCount() != null ? a.visitCount() : 0L; + long bCount = b.visitCount() != null ? b.visitCount() : 0L; + return Long.compare(bCount, aCount); +}) // 방문 수 내림차순 정렬
188-206: 사용하지 않는 메서드를 제거하거나 aggregation 구현으로 대체하세요.
getVisitCountsByZoneIds메서드를 aggregation으로 개선한다면, 이 메서드는 더 이상 필요하지 않을 수 있습니다.이 메서드를 제거하고 aggregation 기반 구현으로 대체하는 것을 고려해 주세요.
.github/workflows/cd-develop.yml (2)
94-102: Spring 프로필 환경변수 누락 → 설정값 불일치 가능성
CI 환경에서develop프로필을 의도하셨다면 동일 값을 컨테이너에도 주입해 주세요.-p 8080:8080 \ +-e "SPRING_PROFILES_ACTIVE=develop" \
59-59: 불필요한 행 끝 공백
YAML lint 경고가 발생합니다. 작은 부분이지만 차후 머지 충돌을 줄이기 위해 정리해 주세요.docker-compose.yml (2)
30-34: Elasticsearch 실행 시vm.max_map_countsysctl 미설정
호스트 기본값이 65 535인 경우 ES 부팅 도중bootstrap checks failed로 종료됩니다. Compose 레벨에서 설정해 두면 CI, 로컬 모두 안전합니다.environment: - discovery.type=single-node - xpack.security.enabled=false - ES_JAVA_OPTS=-Xms512m -Xmx512m + sysctls: + - vm.max_map_count=262144
46-48: 마지막 줄 개행(Newline at EOF) 누락
일부 도구에서 패치 계산 시 불필요한 노이즈가 생깁니다. 개행을 추가해 주세요.docker-compose.test.yml (2)
28-31: 테스트용 ES도 동일한vm.max_map_count설정 필요
운영‧개발 환경과 동일하게 sysctl 추가를 권장합니다.- xpack.security.enabled=false - ES_JAVA_OPTS=-Xms512m -Xmx512m + sysctls: + - vm.max_map_count=262144
42-45: EOF 개행 누락
위 파일과 동일한 사유로 개행을 추가해 주세요..github/workflows/test-elasticsearch.yml (3)
17-20: GitHub Actions 버전 업데이트 권장
actions/checkout@v4,actions/setup-java@v4,docker/login-action@v3가 릴리스돼 있습니다. self-hosted 러너에서도 최신 기능과 버그 픽스를 활용하기 위해 버전을 올려 주세요.- - uses: actions/checkout@v3 + - uses: actions/checkout@v4(다른 액션도 동일)
105-116: 애플리케이션 헬스 체크를 루프 형태로 변경 권장
고정sleep 30은 서비스 부팅 시간 변동에 취약합니다. ES 대기 루프와 동일한 패턴을 사용하면 실패율을 낮출 수 있습니다.- echo "Waiting for application to start..." - sleep 30 + echo "Waiting for application to start..." + for i in {1..30}; do + if curl -f http://localhost:8081/actuator/health > /dev/null 2>&1; then + echo "Application is healthy!" + break + fi + echo "Waiting... ($i/30)" + sleep 10 + done
59-59: 행 끝 공백 제거
YAML lint 경고가 있으므로 정리하면 좋습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
.github/workflows/cd-develop.yml(1 hunks).github/workflows/test-elasticsearch.yml(1 hunks)docker-compose.test.yml(1 hunks)docker-compose.yml(1 hunks)src/main/java/com/wayble/server/explore/dto/search/WaybleZoneDistrictResponseDto.java(1 hunks)src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java(3 hunks)
🧰 Additional context used
🪛 YAMLlint (1.37.1)
.github/workflows/cd-develop.yml
[error] 59-59: trailing spaces
(trailing-spaces)
.github/workflows/test-elasticsearch.yml
[error] 59-59: trailing spaces
(trailing-spaces)
[error] 109-109: trailing spaces
(trailing-spaces)
[error] 117-117: trailing spaces
(trailing-spaces)
[error] 153-153: no new line character at the end of file
(new-line-at-end-of-file)
docker-compose.test.yml
[error] 45-45: no new line character at the end of file
(new-line-at-end-of-file)
docker-compose.yml
[error] 48-48: no new line character at the end of file
(new-line-at-end-of-file)
🪛 actionlint (1.7.7)
.github/workflows/test-elasticsearch.yml
17-17: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
20-20: the runner of "actions/setup-java@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
34-34: the runner of "docker/login-action@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test-elasticsearch-on-ec2
🔇 Additional comments (1)
src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java (1)
162-162: 하드코딩된 제한값 제거 및 설정 가능하도록 개선district별 zone 개수가 1000개를 초과할 수 있는지 확인 후, 상수나 설정값으로 분리하세요.
확인해야 할 사항:
- 운영 DB나 샘플 데이터를 조회해 district별 zone 개수가 1000개를 넘는지 검증
- 실제 최대 값을 기준으로 적절한 디폴트 한도 설정
예시 수정안:
--- a/src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java +++ b/src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java @@ -1,6 +1,7 @@ public class WaybleZoneQuerySearchRepository { + + // district당 조회 가능한 최대 zone 수 (설정 파일 또는 환경 변수로 관리 권장) + private static final int MAX_ZONES_PER_DISTRICT = 10000; public List<Zone> findZonesByDistrict(String districtId) { NativeQuery searchQuery = NativeQuery.builder() - .withQuery(termQuery) - .withMaxResults(1000) // district 내 모든 zone 조회 + .withQuery(termQuery) + .withMaxResults(MAX_ZONES_PER_DISTRICT) // district 내 모든 zone 조회 .build(); … }
| - name: Stop existing containers if any | ||
| run: | | ||
| CONTAINER_ID=$(sudo docker ps -q --filter "name=github-actions-demo") | ||
| if [ ! -z "$CONTAINER_ID" ]; then | ||
| sudo docker stop $CONTAINER_ID | ||
| # Stop Spring Boot app container | ||
| APP_CONTAINER_ID=$(sudo docker ps -q --filter "name=github-actions-demo") | ||
| if [ ! -z "$APP_CONTAINER_ID" ]; then | ||
| sudo docker stop $APP_CONTAINER_ID | ||
| fi | ||
|
|
||
| # Stop Elasticsearch container | ||
| ES_CONTAINER_ID=$(sudo docker ps -q --filter "name=elasticsearch") | ||
| if [ ! -z "$ES_CONTAINER_ID" ]; then | ||
| sudo docker stop $ES_CONTAINER_ID | ||
| fi | ||
|
|
There was a problem hiding this comment.
컨테이너 stop 후 rm 누락 – 동일한 이름으로 새 컨테이너 실행 시 충돌
docker stop만 호출하면 컨테이너 이름이 여전히 예약돼 있어 이후 docker run --name … 단계에서 Conflict. The container name … is already in use 오류가 발생합니다. docker rm까지 수행해 이름을 해제해 주세요.
- if [ ! -z "$APP_CONTAINER_ID" ]; then
- sudo docker stop $APP_CONTAINER_ID
+ if [ ! -z "$APP_CONTAINER_ID" ]; then
+ sudo docker stop $APP_CONTAINER_ID
+ sudo docker rm $APP_CONTAINER_ID
fi
…
- if [ ! -z "$ES_CONTAINER_ID" ]; then
- sudo docker stop $ES_CONTAINER_ID
+ if [ ! -z "$ES_CONTAINER_ID" ]; then
+ sudo docker stop $ES_CONTAINER_ID
+ sudo docker rm $ES_CONTAINER_ID
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Stop existing containers if any | |
| run: | | |
| CONTAINER_ID=$(sudo docker ps -q --filter "name=github-actions-demo") | |
| if [ ! -z "$CONTAINER_ID" ]; then | |
| sudo docker stop $CONTAINER_ID | |
| # Stop Spring Boot app container | |
| APP_CONTAINER_ID=$(sudo docker ps -q --filter "name=github-actions-demo") | |
| if [ ! -z "$APP_CONTAINER_ID" ]; then | |
| sudo docker stop $APP_CONTAINER_ID | |
| fi | |
| # Stop Elasticsearch container | |
| ES_CONTAINER_ID=$(sudo docker ps -q --filter "name=elasticsearch") | |
| if [ ! -z "$ES_CONTAINER_ID" ]; then | |
| sudo docker stop $ES_CONTAINER_ID | |
| fi | |
| - name: Stop existing containers if any | |
| run: | | |
| # Stop Spring Boot app container | |
| APP_CONTAINER_ID=$(sudo docker ps -q --filter "name=github-actions-demo") | |
| if [ ! -z "$APP_CONTAINER_ID" ]; then | |
| sudo docker stop $APP_CONTAINER_ID | |
| sudo docker rm $APP_CONTAINER_ID | |
| fi | |
| # Stop Elasticsearch container | |
| ES_CONTAINER_ID=$(sudo docker ps -q --filter "name=elasticsearch") | |
| if [ ! -z "$ES_CONTAINER_ID" ]; then | |
| sudo docker stop $ES_CONTAINER_ID | |
| sudo docker rm $ES_CONTAINER_ID | |
| fi |
🧰 Tools
🪛 YAMLlint (1.37.1)
[error] 59-59: trailing spaces
(trailing-spaces)
🤖 Prompt for AI Agents
In .github/workflows/cd-develop.yml around lines 52 to 65, after stopping the
containers with `docker stop`, the containers are not removed, causing name
conflicts when creating new containers with the same name. To fix this, add
`docker rm` commands after each `docker stop` to remove the stopped containers
and free up their names for reuse.
| for i in {1..30}; do | ||
| if curl -s http://localhost:9200/_health > /dev/null 2>&1; then | ||
| echo "Elasticsearch is ready!" | ||
| break |
There was a problem hiding this comment.
Elasticsearch 헬스 체크 엔드포인트 오류
/_health 엔드포인트는 존재하지 않습니다. 일반적으로 /_cluster/health 또는 _cluster/health?wait_for_status=yellow&timeout=... 를 사용합니다. 현 상태로는 5 분 타임아웃 후 앱 컨테이너가 실행돼도 ES 미기동 상태일 가능성이 큽니다.
- if curl -s http://localhost:9200/_health > /dev/null 2>&1; then
+ if curl -s http://localhost:9200/_cluster/health > /dev/null 2>&1; then📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for i in {1..30}; do | |
| if curl -s http://localhost:9200/_health > /dev/null 2>&1; then | |
| echo "Elasticsearch is ready!" | |
| break | |
| for i in {1..30}; do | |
| if curl -s http://localhost:9200/_cluster/health > /dev/null 2>&1; then | |
| echo "Elasticsearch is ready!" | |
| break |
🤖 Prompt for AI Agents
In .github/workflows/cd-develop.yml around lines 85 to 88, the curl command
checks Elasticsearch health using a non-existent endpoint /_health. Replace this
with the correct endpoint /_cluster/health or use
/_cluster/health?wait_for_status=yellow&timeout=... to properly verify
Elasticsearch readiness before proceeding.
| public static WaybleZoneDistrictResponseDto from(WaybleZoneDocument document, Long visitCount) { | ||
| return WaybleZoneDistrictResponseDto.builder() | ||
| .zoneId(document.getZoneId()) | ||
| .zoneName(document.getZoneName()) | ||
| .zoneType(document.getZoneType()) | ||
| .thumbnailImageUrl(document.getThumbnailImageUrl()) | ||
| .latitude(document.getAddress().getLocation().getLat()) | ||
| .longitude(document.getAddress().getLocation().getLon()) | ||
| .averageRating(document.getAverageRating()) | ||
| .reviewCount(document.getReviewCount()) | ||
| .facility(FacilityResponseDto.from(document.getFacility())) | ||
| .visitCount(visitCount != null ? visitCount : 0L) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
null 체크를 추가하여 안정성을 향상시키세요.
from 메서드에서 잠재적인 NullPointerException이 발생할 수 있습니다. 다음 상황을 고려해 주세요:
document파라미터가 null인 경우document.getAddress()또는document.getAddress().getLocation()이 null인 경우document.getFacility()가 null인 경우
다음과 같이 null 체크를 추가하는 것을 제안합니다:
public static WaybleZoneDistrictResponseDto from(WaybleZoneDocument document, Long visitCount) {
+ if (document == null) {
+ throw new IllegalArgumentException("WaybleZoneDocument cannot be null");
+ }
+
+ Double latitude = null;
+ Double longitude = null;
+ if (document.getAddress() != null && document.getAddress().getLocation() != null) {
+ latitude = document.getAddress().getLocation().getLat();
+ longitude = document.getAddress().getLocation().getLon();
+ }
+
return WaybleZoneDistrictResponseDto.builder()
.zoneId(document.getZoneId())
.zoneName(document.getZoneName())
.zoneType(document.getZoneType())
.thumbnailImageUrl(document.getThumbnailImageUrl())
- .latitude(document.getAddress().getLocation().getLat())
- .longitude(document.getAddress().getLocation().getLon())
+ .latitude(latitude)
+ .longitude(longitude)
.averageRating(document.getAverageRating())
.reviewCount(document.getReviewCount())
- .facility(FacilityResponseDto.from(document.getFacility()))
+ .facility(document.getFacility() != null ? FacilityResponseDto.from(document.getFacility()) : null)
.visitCount(visitCount != null ? visitCount : 0L)
.build();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public static WaybleZoneDistrictResponseDto from(WaybleZoneDocument document, Long visitCount) { | |
| return WaybleZoneDistrictResponseDto.builder() | |
| .zoneId(document.getZoneId()) | |
| .zoneName(document.getZoneName()) | |
| .zoneType(document.getZoneType()) | |
| .thumbnailImageUrl(document.getThumbnailImageUrl()) | |
| .latitude(document.getAddress().getLocation().getLat()) | |
| .longitude(document.getAddress().getLocation().getLon()) | |
| .averageRating(document.getAverageRating()) | |
| .reviewCount(document.getReviewCount()) | |
| .facility(FacilityResponseDto.from(document.getFacility())) | |
| .visitCount(visitCount != null ? visitCount : 0L) | |
| .build(); | |
| } | |
| public static WaybleZoneDistrictResponseDto from(WaybleZoneDocument document, Long visitCount) { | |
| if (document == null) { | |
| throw new IllegalArgumentException("WaybleZoneDocument cannot be null"); | |
| } | |
| Double latitude = null; | |
| Double longitude = null; | |
| if (document.getAddress() != null && document.getAddress().getLocation() != null) { | |
| latitude = document.getAddress().getLocation().getLat(); | |
| longitude = document.getAddress().getLocation().getLon(); | |
| } | |
| return WaybleZoneDistrictResponseDto.builder() | |
| .zoneId(document.getZoneId()) | |
| .zoneName(document.getZoneName()) | |
| .zoneType(document.getZoneType()) | |
| .thumbnailImageUrl(document.getThumbnailImageUrl()) | |
| .latitude(latitude) | |
| .longitude(longitude) | |
| .averageRating(document.getAverageRating()) | |
| .reviewCount(document.getReviewCount()) | |
| .facility(document.getFacility() != null | |
| ? FacilityResponseDto.from(document.getFacility()) | |
| : null) | |
| .visitCount(visitCount != null ? visitCount : 0L) | |
| .build(); | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/com/wayble/server/explore/dto/search/WaybleZoneDistrictResponseDto.java
around lines 21 to 34, the from method lacks null checks for the document
parameter and its nested fields, which can cause NullPointerExceptions. Add null
checks to verify if document is null, if document.getAddress() or
document.getAddress().getLocation() are null, and if document.getFacility() is
null before accessing their properties. Provide default values or handle these
null cases gracefully to improve method stability.
| private Map<Long, Long> getVisitCountsByZoneIds(List<Long> zoneIds) { | ||
| Map<Long, Long> visitCountMap = new HashMap<>(); | ||
|
|
||
| // 각 zoneId별로 방문 로그 수를 직접 카운트 | ||
| for (Long zoneId : zoneIds) { | ||
| long count = countVisitLogsByZoneId(zoneId); | ||
| visitCountMap.put(zoneId, count); | ||
| } | ||
|
|
||
| return visitCountMap; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
N+1 쿼리 문제를 해결하여 성능을 개선하세요.
각 zoneId에 대해 개별적으로 쿼리를 실행하는 것은 성능 문제를 야기할 수 있습니다. Elasticsearch의 aggregation 기능을 사용하여 한 번의 쿼리로 모든 zone의 방문 수를 집계하는 것이 더 효율적입니다.
다음과 같이 aggregation을 사용하여 개선할 수 있습니다:
private Map<Long, Long> getVisitCountsByZoneIds(List<Long> zoneIds) {
- Map<Long, Long> visitCountMap = new HashMap<>();
-
- // 각 zoneId별로 방문 로그 수를 직접 카운트
- for (Long zoneId : zoneIds) {
- long count = countVisitLogsByZoneId(zoneId);
- visitCountMap.put(zoneId, count);
- }
-
- return visitCountMap;
+ // Terms aggregation을 사용하여 한 번의 쿼리로 모든 zone의 방문 수 집계
+ Query termsQuery = Query.of(q -> q
+ .terms(t -> t
+ .field("zoneId")
+ .terms(v -> v.value(zoneIds.stream().map(id -> id.toString()).collect(Collectors.toList())))
+ )
+ );
+
+ // Aggregation 쿼리 구성
+ // 실제 구현은 Elasticsearch aggregation API 사용
+ // 여기서는 의사코드로 표현
+ return executeAggregationQuery(termsQuery);
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java
around lines 176 to 186, the current method executes a separate query for each
zoneId causing an N+1 query problem. Refactor this method to use Elasticsearch
aggregation to fetch visit counts for all zoneIds in a single query. Implement a
terms aggregation on zoneId and a count aggregation to retrieve the visit counts
efficiently, then map the results to the visitCountMap to eliminate multiple
queries and improve performance.
✔️ 연관 이슈
📝 작업 내용
웨이블존 추천, 검색 응답 필드에 장애 시설 정보를 추가했습니다.
JWT 추가로 테스트 코드 api 요청 과정에서 문제가 발생해, 해당 오류를 해결했습니다
지도 기반 검색 기능의 엔드포인트를 /search/maps로 변경했습니다.
스크린샷 (선택)
Summary by CodeRabbit
신규 기능
버그 수정
/maps로 변경되어 접근 경로가 수정되었습니다.테스트
기타