Skip to content

Conversation

@KiSeungMin
Copy link
Member

@KiSeungMin KiSeungMin commented Aug 12, 2025

✔️ 연관 이슈

📝 작업 내용

  • 지도에서 경사로, 엘리베이터를 거리순으로 조회하는 로직을 구현했습니다.
  • 해당 로직 테스트 코드 작성 및 검증을 완료했습니다.

스크린샷 (선택)

image

Summary by CodeRabbit

  • 신기능
    • 주변 편의시설 검색 API 추가(/api/v1/facilities/search). 위도/경도와 시설 유형으로 필터링하며, 가까운 거리순으로 결과를 반환합니다. 입력값 유효성 검사를 적용하고 표준 응답 형식으로 목록을 제공합니다.
  • 테스트
    • 통합 테스트 추가: 램프/엘리베이터 검색 시나리오, 거리 정렬, 인증 흐름 등을 검증하여 안정성과 정확성을 확인했습니다.

@KiSeungMin KiSeungMin self-assigned this Aug 12, 2025
@KiSeungMin KiSeungMin added the 💡 feature 기능 구현 및 개발 label Aug 12, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 12, 2025

Walkthrough

지도 시설 검색 기능을 추가했다. 엘라스틱서치 문서/리포지토리/쿼리 리포지토리/서비스/컨트롤러를 신규 도입하고, 위경도+유형 조건으로 가까운 시설을 조회하는 GET API를 제공한다. DTO·엔티티·열거형을 추가했으며, 통합 테스트로 램프/엘리베이터 조회 및 정렬을 검증했다.

Changes

Cohort / File(s) Summary
Controller
src/main/java/.../controller/WaybleFacilitySearchController.java
신규 GET /api/v1/facilities/search 엔드포인트 추가. 조건 DTO 검증 바인딩, 서비스 위임, 공통 응답으로 반환.
DTOs
src/main/java/.../dto/facility/WaybleFacilityConditionDto.java, .../dto/facility/WaybleFacilityRegisterDto.java, .../dto/facility/WaybleFacilityResponseDto.java
조건/등록/응답 DTO 신설. 위경도 검증 추가, FacilityType 포함. 문서→응답 매핑 팩토리 메서드 제공.
Entities
src/main/java/.../entity/FacilityType.java, .../entity/WaybleFacilityDocument.java
시설 유형 enum(ELEVATOR, RAMP) 및 ES 문서 엔티티(geo-point 위치, 유형) 추가.
Repositories
src/main/java/.../repository/facility/WaybleFacilityDocumentRepository.java, .../repository/facility/WaybleFacilityQuerySearchRepository.java
ES CRUD 리포지토리와 지오 거리/유형 필터 쿼리 리포지토리 추가. 거리 정렬, 페이지 크기 제한 적용.
Service
src/main/java/.../service/WaybleFacilityDocumentService.java
조회 서비스 추가. 쿼리 리포지토리 호출해 DTO 목록 반환.
Tests
src/test/java/.../WaybleFacilityApiIntegrationTest.java
통합 테스트 추가. 시드 데이터 생성, 인증 포함 요청으로 RAMP/ELEVATOR 검색 및 거리 오름차순 검증.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Controller as FacilitySearchController
  participant Service as FacilityDocumentService
  participant Repo as FacilityQuerySearchRepository
  participant ES as Elasticsearch

  Client->>Controller: GET /api/v1/facilities/search?lat,lon,facilityType
  Controller->>Service: findNearbyFacilityDocuments(conditionDto)
  Service->>Repo: findNearbyFacilitiesByType(condition)
  Repo->>ES: Geo-distance query + type filter, sort by distance
  ES-->>Repo: Documents
  Repo-->>Service: List<WaybleFacilityResponseDto>
  Service-->>Controller: List<WaybleFacilityResponseDto>
  Controller-->>Client: CommonResponse.success(data)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
경사로, 엘리베이터 생성, 조회 로직 구현 (#128) 조회만 구현됨. 생성 로직/엔드포인트 부재.
기능 테스트 코드 구현 (#128)

Assessment against linked issues: Out-of-scope changes

(해당 없음)

Possibly related PRs

Suggested labels

🤔 test

Suggested reviewers

  • seung-in-Yoo
  • wonjun-lee-fcwj245

Poem

귀가 쫑긋 토끼 개발자, 점프 점프 뛰어가며
위도 경도 톡톡 두드려, 가까운 길 찾았지요.
램프도 엘리베이터도 줄지어 안녕하네!
지도의 길목마다 바람처럼 정렬되어—
오늘도 깡총, 쿼리 타고 행복 배달합니다. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/seungmin

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@KiSeungMin KiSeungMin changed the title Feature/seungmin [feat] 지도에서 경사로, 엘리베이터를 거리순으로 조회하는 로직 구현 Aug 12, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (16)
src/main/java/com/wayble/server/explore/entity/FacilityType.java (1)

3-6: 도메인 중복 가능성: 기존 Type enum과의 관계 정리 제안

direction.entity.type.Type에도 RAMP/ELEVATOR가 존재합니다. 중복 도메인을 유지할지, 재사용/매핑 전략을 둘지 명확히 하면 장기 유지보수 시 혼란을 줄일 수 있습니다.

src/main/java/com/wayble/server/explore/repository/facility/WaybleFacilityDocumentRepository.java (1)

8-10: 불필요한 메서드 재정의 제거

ElasticsearchRepository(CrudRepository)에서 이미 제공하는 findAll()을 재선언할 필요가 없습니다. 제거하여 인터페이스를 간결하게 유지하세요.

다음 수정안을 권장합니다:

 public interface WaybleFacilityDocumentRepository extends ElasticsearchRepository<WaybleFacilityDocument, String> {
-    List<WaybleFacilityDocument> findAll();
 }
src/main/java/com/wayble/server/explore/controller/WaybleFacilitySearchController.java (2)

24-25: 사소한 스타일: 빈 문자열 매핑 제거

@RequestMapping에 기본 경로가 있으므로 @GetMapping에 빈 문자열은 불필요합니다. 가독성을 위해 제거하세요.

-    @GetMapping("")
+    @GetMapping
     public CommonResponse<List<WaybleFacilityResponseDto>> findNearbyFacilities(

25-29: 거리 반환 여부 검토

PR 목표가 “거리 순 정렬”이라면, 응답에 distance 필드를 포함하면 클라이언트가 정렬 근거를 명확히 인지할 수 있습니다. 현재 DTO에는 위도/경도/유형만 있어 후처리가 어렵습니다.

원한다면 WaybleFacilityResponseDto에 Double distance 추가 및 리포지토리 매핑 개선안을 제안하겠습니다.

src/main/java/com/wayble/server/explore/dto/facility/WaybleFacilityResponseDto.java (2)

16-22: NPE 방어 로직 추가 제안

facilityDocument 또는 location이 null이면 NPE가 발생합니다. 사전 검증으로 방어하면 안정성이 높아집니다.

-    public static WaybleFacilityResponseDto from(WaybleFacilityDocument facilityDocument) {
-        return WaybleFacilityResponseDto.builder()
-                .latitude(facilityDocument.getLocation().getLat())
-                .longitude(facilityDocument.getLocation().getLon())
-                .facilityType(facilityDocument.getFacilityType())
-                .build();
-    }
+    public static WaybleFacilityResponseDto from(WaybleFacilityDocument facilityDocument) {
+        java.util.Objects.requireNonNull(facilityDocument, "facilityDocument must not be null");
+        java.util.Objects.requireNonNull(facilityDocument.getLocation(), "facilityDocument.location must not be null");
+        return WaybleFacilityResponseDto.builder()
+                .latitude(facilityDocument.getLocation().getLat())
+                .longitude(facilityDocument.getLocation().getLon())
+                .facilityType(facilityDocument.getFacilityType())
+                .build();
+    }

추가 import:

import java.util.Objects;

8-15: 식별자/거리 포함 고려

클라이언트에서 마커 식별/후속 조회가 필요하다면 id 또는 distance를 DTO에 포함하는 것을 고려하세요.

src/main/java/com/wayble/server/explore/entity/WaybleFacilityDocument.java (1)

26-27: ES 매핑 명시화 제안: enum을 Keyword로 지정

enum은 기본적으로 문자열로 저장되지만, Term Query 최적화를 위해 Keyword 명시를 권장합니다.

-import org.springframework.data.elasticsearch.annotations.Field;
+import org.springframework.data.elasticsearch.annotations.Field;
+import org.springframework.data.elasticsearch.annotations.FieldType;
@@
-    private FacilityType facilityType;
+    @Field(type = FieldType.Keyword)
+    private FacilityType facilityType;
src/main/java/com/wayble/server/explore/dto/facility/WaybleFacilityConditionDto.java (1)

8-21: 테스트 보강 제안: Validation 실패 케이스

  • facilityType 누락 시 400 반환 여부
  • 위도/경도 범위 초과 시 400 반환 여부

위 케이스에 대한 통합/단위 테스트가 있는지 확인해 주세요. 없다면 추가를 권장합니다.

원하시면 검증 실패 케이스를 포함한 테스트 템플릿을 생성해 드리겠습니다.

src/main/java/com/wayble/server/explore/repository/facility/WaybleFacilityQuerySearchRepository.java (4)

24-27: 매직 넘버 제거 및 주석 불일치(5km vs 10km) 수정 제안

주석은 5km로 표기되어 있으나 값은 10.0입니다. 혼선을 방지하고 재사용성을 높이기 위해 상수로 분리하고 주석을 정정하세요.

아래와 같이 수정하면 가독성과 유지보수성이 좋아집니다.

 private final ElasticsearchOperations operations;
 private static final IndexCoordinates INDEX = IndexCoordinates.of("wayble_facility_document");
 private static final int LIMIT = 50;
+private static final double DEFAULT_RADIUS_KM = 10.0;
@@
-        double radius = 10.0; // 기본 반경 5km
+        double radius = DEFAULT_RADIUS_KM; // 기본 반경 10km
         String radiusWithUnit = radius + "km";

Also applies to: 34-36


31-33: 메서드 의도 명확화: 타입이 선택(optional)일 경우 네이밍/계약 보완

메서드명이 findNearbyFacilitiesByType라서 타입이 필수처럼 읽히지만, 실제로는 facilityType이 null이면 전체를 조회합니다. 일관성을 위해 아래 중 하나를 권장합니다.

  • 메서드명을 findNearbyFacilities로 변경하거나 Javadoc에 “facilityType이 null이면 전체 조회”라고 명시
  • 혹은 facilityType을 필수로 강제(Bean Validation 또는 Objects.requireNonNull 사용)

83-87: LIMIT와 반경을 외부 파라미터로 열어 확장성 확보

현재 LIMIT(50)과 반경(10km)은 고정입니다. 향후 요구사항(“더 많이/덜 보기”, “반경 슬라이더”)을 고려하면 pageable과 radiusKm를 외부에서 주입받는 설계가 유리합니다. Zone 검색과의 API 일관성도 좋아집니다.

  • Service/Controller 레이어에서 Pageable과 radiusKm(double)를 받아 Repository에 전달
  • 기본값(DEFAULT_RADIUS_KM, 기본 LIMIT)은 null/미지정 시에만 적용

93-99: 정렬 거리(sort values)를 응답에 포함하는 방안 검토(Zone API와 일관성)

현재 응답 DTO에는 거리 값이 없습니다. Zone 검색 응답처럼 ES sort values를 활용해 거리(km)를 포함하면 FE/테스트에서 반복 계산(haversine) 없이도 정렬/표시가 가능하고, API 간 일관성이 향상됩니다.

예시(개념):

  • SearchHit.getSortValues()의 첫 번째 값을 미터로 간주 후 km로 변환하여 DTO에 distance 필드 추가
  • WaybleFacilityResponseDto에 distance(Double) 추가 및 from(hit, doc) 팩토리 제공
src/test/java/com/wayble/server/explore/WaybleFacilityApiIntegrationTest.java (4)

81-99: 테스트 데이터 저장 최적화: saveAll 사용

루프 내에서 save를 두 번 호출하기보다 saveAll(List.of(...))로 묶으면 호출 수를 절반으로 줄여 테스트 시간을 단축할 수 있습니다.

-            waybleFacilityDocumentRepository.save(rampDocument);
-            waybleFacilityDocumentRepository.save(elevatorDocument);
+            waybleFacilityDocumentRepository.saveAll(List.of(rampDocument, elevatorDocument));

251-254: 랜덤 씨드 고정으로 테스트 재현성 확보

현재 무작위 데이터를 매 실행 시 생성합니다. 씨드를 고정하면 드문 플레이키니스 가능성을 줄이고 디버깅이 쉬워집니다.

-        Random rnd = new Random();
+        Random rnd = new Random(42);

101-106: 데이터 정리 범위 검토: deleteAll은 다른 테스트에 영향 가능

테스트 종료 시 deleteAll은 인덱스의 모든 문서를 제거합니다. 동일 인덱스를 공유하는 다른 테스트가 있다면 간섭이 발생할 수 있습니다. 테스트에서 생성한 문서의 id를 수집해 deleteAllById로 한정 삭제하는 방안을 고려하세요.


138-141: 테스트 로그 과다 출력 정리

System.out.println/printf가 다수 포함되어 있어 CI 로그를 오염시킬 수 있습니다. 실패 시에만 출력되도록 Logger(DEBUG)로 내리거나 제거를 권장합니다.

예:

  • 테스트 성공 시 출력 제거
  • 실패 메시지는 assert에 포함하여 가독성 유지

Also applies to: 155-156, 169-176, 196-198, 212-214, 227-234

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbb4fb4 and 9d051e3.

📒 Files selected for processing (10)
  • src/main/java/com/wayble/server/explore/controller/WaybleFacilitySearchController.java (1 hunks)
  • src/main/java/com/wayble/server/explore/dto/facility/WaybleFacilityConditionDto.java (1 hunks)
  • src/main/java/com/wayble/server/explore/dto/facility/WaybleFacilityRegisterDto.java (1 hunks)
  • src/main/java/com/wayble/server/explore/dto/facility/WaybleFacilityResponseDto.java (1 hunks)
  • src/main/java/com/wayble/server/explore/entity/FacilityType.java (1 hunks)
  • src/main/java/com/wayble/server/explore/entity/WaybleFacilityDocument.java (1 hunks)
  • src/main/java/com/wayble/server/explore/repository/facility/WaybleFacilityDocumentRepository.java (1 hunks)
  • src/main/java/com/wayble/server/explore/repository/facility/WaybleFacilityQuerySearchRepository.java (1 hunks)
  • src/main/java/com/wayble/server/explore/service/WaybleFacilityDocumentService.java (1 hunks)
  • src/test/java/com/wayble/server/explore/WaybleFacilityApiIntegrationTest.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (8)
src/main/java/com/wayble/server/explore/entity/FacilityType.java (1)
src/main/java/com/wayble/server/direction/entity/type/Type.java (1)
  • Getter (5-22)
src/main/java/com/wayble/server/explore/dto/facility/WaybleFacilityConditionDto.java (8)
src/main/java/com/wayble/server/admin/dto/wayblezone/AdminWaybleZoneCreateDto.java (1)
  • AdminWaybleZoneCreateDto (9-39)
src/main/java/com/wayble/server/admin/dto/wayblezone/AdminWaybleZoneDetailDto.java (2)
  • FacilityInfo (40-43)
  • AdminWaybleZoneDetailDto (10-59)
src/main/java/com/wayble/server/explore/dto/search/request/WaybleZoneSearchConditionDto.java (1)
  • Builder (10-30)
src/main/java/com/wayble/server/explore/dto/recommend/WaybleZoneRecommendConditionDto.java (1)
  • Builder (8-23)
src/main/java/com/wayble/server/admin/dto/wayblezone/AdminWaybleZoneUpdateDto.java (1)
  • AdminWaybleZoneUpdateDto (9-63)
src/main/java/com/wayble/server/admin/dto/wayblezone/AdminWaybleZoneThumbnailDto.java (1)
  • AdminWaybleZoneThumbnailDto (6-16)
src/main/java/com/wayble/server/direction/dto/TransportationRequestDto.java (1)
  • Location (12-16)
src/main/java/com/wayble/server/direction/entity/WaybleMarker.java (1)
  • WaybleMarker (5-11)
src/main/java/com/wayble/server/explore/repository/facility/WaybleFacilityQuerySearchRepository.java (1)
src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java (1)
  • Repository (23-126)
src/main/java/com/wayble/server/explore/repository/facility/WaybleFacilityDocumentRepository.java (2)
src/main/java/com/wayble/server/explore/repository/WaybleZoneDocumentRepository.java (1)
  • WaybleZoneDocumentRepository (9-12)
src/main/java/com/wayble/server/explore/service/WaybleZoneDocumentService.java (1)
  • Service (13-36)
src/main/java/com/wayble/server/explore/entity/WaybleFacilityDocument.java (5)
src/main/java/com/wayble/server/explore/entity/WaybleZoneDocument.java (2)
  • ToString (10-68)
  • fromEntity (43-54)
src/main/java/com/wayble/server/direction/entity/transportation/Facility.java (1)
  • Entity (11-46)
src/main/java/com/wayble/server/explore/entity/EsWaybleZoneFacility.java (1)
  • ToString (6-34)
src/main/java/com/wayble/server/explore/service/WaybleZoneDocumentService.java (2)
  • Service (13-36)
  • saveDocumentFromEntity (25-27)
src/main/java/com/wayble/server/explore/repository/WaybleZoneDocumentRepository.java (1)
  • WaybleZoneDocumentRepository (9-12)
src/main/java/com/wayble/server/explore/dto/facility/WaybleFacilityRegisterDto.java (7)
src/main/java/com/wayble/server/admin/dto/wayblezone/AdminWaybleZoneCreateDto.java (1)
  • AdminWaybleZoneCreateDto (9-39)
src/main/java/com/wayble/server/common/dto/FacilityDto.java (1)
  • Builder (5-13)
src/main/java/com/wayble/server/wayblezone/dto/WaybleZoneRegisterDto.java (1)
  • Builder (8-21)
src/main/java/com/wayble/server/admin/dto/wayblezone/AdminWaybleZoneDetailDto.java (2)
  • FacilityInfo (40-43)
  • AdminWaybleZoneDetailDto (10-59)
src/main/java/com/wayble/server/explore/dto/recommend/WaybleZoneRecommendConditionDto.java (1)
  • Builder (8-23)
src/main/java/com/wayble/server/explore/dto/common/FacilityResponseDto.java (1)
  • Builder (6-29)
src/main/java/com/wayble/server/explore/dto/search/request/WaybleZoneSearchConditionDto.java (1)
  • Builder (10-30)
src/main/java/com/wayble/server/explore/dto/facility/WaybleFacilityResponseDto.java (3)
src/main/java/com/wayble/server/explore/dto/common/FacilityResponseDto.java (1)
  • Builder (6-29)
src/main/java/com/wayble/server/common/dto/FacilityDto.java (1)
  • Builder (5-13)
src/main/java/com/wayble/server/explore/dto/search/response/WaybleZoneSearchResponseDto.java (1)
  • Builder (8-22)
src/test/java/com/wayble/server/explore/WaybleFacilityApiIntegrationTest.java (2)
src/test/java/com/wayble/server/explore/WaybleZoneRecommendApiIntegrationTest.java (1)
  • createRandomFacility (370-383)
src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java (7)
  • Test (201-256)
  • createRandomFacility (580-593)
  • Test (258-310)
  • Test (419-474)
  • Test (364-417)
  • Test (312-362)
  • Test (476-531)
🔇 Additional comments (8)
src/main/java/com/wayble/server/explore/entity/FacilityType.java (1)

3-6: LGTM: 단순 enum 정의 적절

필요 상수만 담은 단순 enum 도입은 명확하고 사용처와도 일관됩니다.

src/main/java/com/wayble/server/explore/service/WaybleFacilityDocumentService.java (1)

19-21: 위임 계층 구성 적절

컨트롤러 → 서비스 → 쿼리 리포지토리로의 위임 구조가 명확합니다. 메서드 명과 역할도 일관적입니다.

src/main/java/com/wayble/server/explore/entity/WaybleFacilityDocument.java (1)

17-25: LGTM: GeoPoint 매핑 적절

@GeoPointField 사용으로 위치 기반 정렬/검색에 적합합니다. 인덱스명과 문서 구조도 일관적입니다.

src/main/java/com/wayble/server/explore/dto/facility/WaybleFacilityRegisterDto.java (1)

9-22: 좌표 검증 일관성 좋음

위도/경도 범위 및 NotNull 검증이 기존 DTO들과 일관되며 적절합니다.

src/main/java/com/wayble/server/explore/repository/facility/WaybleFacilityDocumentRepository.java (1)

8-10: ID 타입 확인 요청

엔티티의 id가 String이며 저장 시 외부에서 직접 지정하는지(예: UUID) 또는 ES 자동 생성인지에 따라 컨벤션이 달라질 수 있습니다. 현재 사용처에서 id 생성 전략이 명확한지 확인 부탁드립니다.

src/main/java/com/wayble/server/explore/repository/facility/WaybleFacilityQuerySearchRepository.java (2)

31-36: ES bool 쿼리 + geo-distance 정렬 + 페이지네이션 구성, 전반적으로 적절합니다

  • 시설 타입 필터(옵션) + 위치 기반 필터 + 거리 오름차순 정렬 + LIMIT(50) 페이징 조합이 요구사항(가까운 순 조회)에 부합합니다.
  • NativeQuery 사용과 DTO 매핑도 깔끔합니다.

Also applies to: 68-87, 89-99


41-48: facilityType 필드 매핑 확인 필요: "facilityType.keyword" 사용의 호환성 점검

현재 term 쿼리 필드가 "facilityType.keyword"로 지정되어 있습니다. Enum 필드는 대개 keyword 타입으로 매핑되어 별도의 ".keyword" 서브필드가 없을 수 있습니다. 매핑이 keyword 단일 필드라면 "facilityType"에 대해 term 쿼리를 수행해야 합니다. 매핑과 불일치 시 결과가 비어버릴 수 있습니다.

매핑이 keyword 단일 필드인 경우 다음과 같이 변경을 권장합니다.

-                                .term(t -> t
-                                        .field("facilityType.keyword")
-                                        .value(condition.facilityType().name())
-                                )
+                                .term(t -> t
+                                        .field("facilityType")
+                                        .value(condition.facilityType().name())
+                                )

확인을 위해 운영/테스트 ES 인덱스의 매핑에서 facilityType 필드 타입과 서브필드 존재 여부를 검증해 주세요.

src/test/java/com/wayble/server/explore/WaybleFacilityApiIntegrationTest.java (1)

108-118: 테스트 시나리오 전반은 목적에 부합합니다

  • MockMvc로 API 호출 → JSON 파싱 → DTO 리스트 검증 → 거리 오름차순 확인 플로우가 명료합니다.
  • null 필드 체크 및 타입 필터링 검증도 적절합니다.

Also applies to: 121-176, 179-235

Comment on lines +19 to +21
FacilityType facilityType
) {
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Null 허용 시 NPE 위험: facilityType에 @NotNull 추가 필요

WaybleFacilityQuerySearchRepository에서 condition.facilityType().name()을 사용하는 것으로 보입니다. facilityType이 null이면 NPE가 발생합니다. NotNull 제약을 추가하세요.

다음 수정안을 제안합니다:

-        FacilityType facilityType
+        @NotNull(message = "시설 타입 입력은 필수입니다.")
+        FacilityType facilityType

테스트에 facilityType 누락 케이스(Negative)도 추가해 주세요.

📝 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.

Suggested change
FacilityType facilityType
) {
}
// src/main/java/com/wayble/server/explore/dto/facility/WaybleFacilityConditionDto.java
import javax.validation.constraints.NotNull; // ← make sure this import is present
public class WaybleFacilityConditionDto {
// …
public WaybleFacilityConditionDto(
@NotNull(message = "시설 타입 입력은 필수입니다.")
FacilityType facilityType
) {
this.facilityType = facilityType;
}
// …
}
🤖 Prompt for AI Agents
In
src/main/java/com/wayble/server/explore/dto/facility/WaybleFacilityConditionDto.java
around lines 19-21, facilityType is nullable which risks NPE when calling
condition.facilityType().name(); add a @NotNull annotation to the facilityType
field (and import the correct javax/jakarta validation package used by the
project) so validation fails early, update constructor/getter if needed to
retain the annotation, and add a unit test that covers the negative case where
facilityType is missing to ensure validation prevents the NPE.

Comment on lines +21 to +23
FacilityType facilityType
){
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

등록 시 facilityType 필수값 보장 필요

등록 DTO에서도 facilityType 누락을 방지하기 위해 @NotNull을 추가하세요.

-        FacilityType facilityType
+        @NotNull(message = "시설 타입 입력은 필수입니다.")
+        FacilityType facilityType
📝 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.

Suggested change
FacilityType facilityType
){
}
@NotNull(message = "시설 타입 입력은 필수입니다.")
FacilityType facilityType
){
}
🤖 Prompt for AI Agents
In
src/main/java/com/wayble/server/explore/dto/facility/WaybleFacilityRegisterDto.java
around lines 21-23, the facilityType field in the registration DTO is missing a
nullability constraint; add the @NotNull annotation to the facilityType
declaration (and the corresponding import, e.g.
javax.validation.constraints.NotNull or jakarta.validation.constraints.NotNull
depending on the project), ensuring the constructor/field carries the annotation
so validation will reject requests without facilityType; run/adjust any
validation tests or configuration if needed to enable request-level validation.

Comment on lines +6 to +8
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

트랜잭션 어노테이션 일관성/적용 범위 조정

  • jakarta.transaction.Transactional 보다는 Spring 관리형 트랜잭션(org.springframework.transaction.annotation.Transactional) 사용을 권장합니다.
  • 읽기 전용 서비스이므로 readOnly = true 지정 또는 제거가 적절합니다(ES 조회는 트랜잭션 불필요).

다음 수정안을 제안합니다:

-import jakarta.transaction.Transactional;
+import org.springframework.transaction.annotation.Transactional;
@@
-@Transactional
+@Transactional(readOnly = true)
 public class WaybleFacilityDocumentService {

Also applies to: 12-15

🤖 Prompt for AI Agents
In
src/main/java/com/wayble/server/explore/service/WaybleFacilityDocumentService.java
around lines 6-8 (also applies to lines 12-15), replace the import of
jakarta.transaction.Transactional with Spring's
org.springframework.transaction.annotation.Transactional and adjust the
annotation usage: for this read-only ES query service either annotate the
class/methods with @Transactional(readOnly = true) or remove transactional
annotations entirely if Elasticsearch operations do not require a transaction;
ensure imports and annotations are updated consistently across the indicated
lines.

Comment on lines +146 to +176
assertThat(dtoList).isNotEmpty();

// null 값 검증
for (WaybleFacilityResponseDto dto : dtoList) {
assertThat(dto.latitude()).isNotNull();
assertThat(dto.longitude()).isNotNull();
assertThat(dto.facilityType()).isNotNull();
assertThat(dto.facilityType()).isEqualTo(FacilityType.RAMP);
System.out.println(dto);
}

// 거리순 정렬 검증
if (dtoList.size() > 1) {
for (int i = 0; i < dtoList.size() - 1; i++) {
WaybleFacilityResponseDto current = dtoList.get(i);
WaybleFacilityResponseDto next = dtoList.get(i + 1);

double currentDistance = haversine(LATITUDE, LONGITUDE,
current.latitude(), current.longitude());
double nextDistance = haversine(LATITUDE, LONGITUDE,
next.latitude(), next.longitude());

assertThat(currentDistance).isLessThanOrEqualTo(nextDistance);
System.out.printf("Index %d: Distance = %.3f km%n", i, currentDistance);
}
// 마지막 요소의 거리도 출력
WaybleFacilityResponseDto last = dtoList.get(dtoList.size() - 1);
double lastDistance = haversine(LATITUDE, LONGITUDE,
last.latitude(), last.longitude());
System.out.printf("Index %d: Distance = %.3f km%n", dtoList.size() - 1, lastDistance);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

반경(10km) 이내 제한 검증과 LIMIT(50) 상한 검증 추가 제안

현재는 정렬만 검증합니다. Repository가 반경 10km 및 LIMIT 50을 적용하므로, 응답이 반경 내이며 결과 수가 상한을 넘지 않는지도 함께 검증하면 회귀를 더 잘 잡아냅니다.

아래와 같이 보강을 제안합니다(반경 상수는 Repository 기본값 10km 기준).

         assertThat(dtoList).isNotEmpty();
+        // LIMIT(50) 검증
+        assertThat(dtoList.size()).isLessThanOrEqualTo(50);
@@
             // 마지막 요소의 거리도 출력
             WaybleFacilityResponseDto last = dtoList.get(dtoList.size() - 1);
             double lastDistance = haversine(LATITUDE, LONGITUDE, 
                     last.latitude(), last.longitude());
             System.out.printf("Index %d: Distance = %.3f km%n", dtoList.size() - 1, lastDistance);
+            // 반경(10km) 이내 검증 (±50m 허용)
+            assertThat(lastDistance)
+                    .withFailMessage("반경 초과: 마지막 요소 거리=%.5f km", lastDistance)
+                    .isLessThanOrEqualTo(10.05);
📝 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.

Suggested change
assertThat(dtoList).isNotEmpty();
// null 값 검증
for (WaybleFacilityResponseDto dto : dtoList) {
assertThat(dto.latitude()).isNotNull();
assertThat(dto.longitude()).isNotNull();
assertThat(dto.facilityType()).isNotNull();
assertThat(dto.facilityType()).isEqualTo(FacilityType.RAMP);
System.out.println(dto);
}
// 거리순 정렬 검증
if (dtoList.size() > 1) {
for (int i = 0; i < dtoList.size() - 1; i++) {
WaybleFacilityResponseDto current = dtoList.get(i);
WaybleFacilityResponseDto next = dtoList.get(i + 1);
double currentDistance = haversine(LATITUDE, LONGITUDE,
current.latitude(), current.longitude());
double nextDistance = haversine(LATITUDE, LONGITUDE,
next.latitude(), next.longitude());
assertThat(currentDistance).isLessThanOrEqualTo(nextDistance);
System.out.printf("Index %d: Distance = %.3f km%n", i, currentDistance);
}
// 마지막 요소의 거리도 출력
WaybleFacilityResponseDto last = dtoList.get(dtoList.size() - 1);
double lastDistance = haversine(LATITUDE, LONGITUDE,
last.latitude(), last.longitude());
System.out.printf("Index %d: Distance = %.3f km%n", dtoList.size() - 1, lastDistance);
}
assertThat(dtoList).isNotEmpty();
// LIMIT(50) 검증
assertThat(dtoList.size()).isLessThanOrEqualTo(50);
// null 값 검증
for (WaybleFacilityResponseDto dto : dtoList) {
assertThat(dto.latitude()).isNotNull();
assertThat(dto.longitude()).isNotNull();
assertThat(dto.facilityType()).isNotNull();
assertThat(dto.facilityType()).isEqualTo(FacilityType.RAMP);
System.out.println(dto);
}
// 거리순 정렬 검증
if (dtoList.size() > 1) {
for (int i = 0; i < dtoList.size() - 1; i++) {
WaybleFacilityResponseDto current = dtoList.get(i);
WaybleFacilityResponseDto next = dtoList.get(i + 1);
double currentDistance = haversine(LATITUDE, LONGITUDE,
current.latitude(), current.longitude());
double nextDistance = haversine(LATITUDE, LONGITUDE,
next.latitude(), next.longitude());
assertThat(currentDistance).isLessThanOrEqualTo(nextDistance);
System.out.printf("Index %d: Distance = %.3f km%n", i, currentDistance);
}
// 마지막 요소의 거리도 출력
WaybleFacilityResponseDto last = dtoList.get(dtoList.size() - 1);
double lastDistance = haversine(LATITUDE, LONGITUDE,
last.latitude(), last.longitude());
System.out.printf("Index %d: Distance = %.3f km%n", dtoList.size() - 1, lastDistance);
// 반경(10km) 이내 검증 (±50m 허용)
assertThat(lastDistance)
.withFailMessage("반경 초과: 마지막 요소 거리=%.5f km", lastDistance)
.isLessThanOrEqualTo(10.05);
}
🤖 Prompt for AI Agents
In src/test/java/com/wayble/server/explore/WaybleFacilityApiIntegrationTest.java
around lines 146 to 176, add assertions to enforce the repository constraints:
compute the haversine distance for each dto and assert it is <= 10.0 km (use a
RADIUS_KM constant set to 10.0 to match the repo default), and assert that
dtoList.size() is <= 50 to verify the LIMIT; keep the existing sort-checks
intact and place the new size check before iterating, and the per-item radius
check inside the loop alongside the non-null assertions.

Comment on lines +204 to +235
assertThat(dtoList).isNotEmpty();

// null 값 검증
for (WaybleFacilityResponseDto dto : dtoList) {
assertThat(dto.latitude()).isNotNull();
assertThat(dto.longitude()).isNotNull();
assertThat(dto.facilityType()).isNotNull();
assertThat(dto.facilityType()).isEqualTo(FacilityType.ELEVATOR);
System.out.println(dto);
}

// 거리순 정렬 검증
if (dtoList.size() > 1) {
for (int i = 0; i < dtoList.size() - 1; i++) {
WaybleFacilityResponseDto current = dtoList.get(i);
WaybleFacilityResponseDto next = dtoList.get(i + 1);

double currentDistance = haversine(LATITUDE, LONGITUDE,
current.latitude(), current.longitude());
double nextDistance = haversine(LATITUDE, LONGITUDE,
next.latitude(), next.longitude());

assertThat(currentDistance).isLessThanOrEqualTo(nextDistance);
System.out.printf("Index %d: Distance = %.3f km%n", i, currentDistance);
}
// 마지막 요소의 거리도 출력
WaybleFacilityResponseDto last = dtoList.get(dtoList.size() - 1);
double lastDistance = haversine(LATITUDE, LONGITUDE,
last.latitude(), last.longitude());
System.out.printf("Index %d: Distance = %.3f km%n", dtoList.size() - 1, lastDistance);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

엘리베이터 테스트에도 동일한 반경/LIMIT 검증 적용

위 경사로 테스트와 동일한 검증을 엘리베이터 케이스에도 추가해 주세요. 상한 및 반경 준수 여부를 양 테스트에서 모두 확인하는 것이 안전합니다.

         assertThat(dtoList).isNotEmpty();
+        // LIMIT(50) 검증
+        assertThat(dtoList.size()).isLessThanOrEqualTo(50);
@@
             WaybleFacilityResponseDto last = dtoList.get(dtoList.size() - 1);
             double lastDistance = haversine(LATITUDE, LONGITUDE, 
                     last.latitude(), last.longitude());
             System.out.printf("Index %d: Distance = %.3f km%n", dtoList.size() - 1, lastDistance);
+            // 반경(10km) 이내 검증 (±50m 허용)
+            assertThat(lastDistance)
+                    .withFailMessage("반경 초과: 마지막 요소 거리=%.5f km", lastDistance)
+                    .isLessThanOrEqualTo(10.05);
📝 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.

Suggested change
assertThat(dtoList).isNotEmpty();
// null 값 검증
for (WaybleFacilityResponseDto dto : dtoList) {
assertThat(dto.latitude()).isNotNull();
assertThat(dto.longitude()).isNotNull();
assertThat(dto.facilityType()).isNotNull();
assertThat(dto.facilityType()).isEqualTo(FacilityType.ELEVATOR);
System.out.println(dto);
}
// 거리순 정렬 검증
if (dtoList.size() > 1) {
for (int i = 0; i < dtoList.size() - 1; i++) {
WaybleFacilityResponseDto current = dtoList.get(i);
WaybleFacilityResponseDto next = dtoList.get(i + 1);
double currentDistance = haversine(LATITUDE, LONGITUDE,
current.latitude(), current.longitude());
double nextDistance = haversine(LATITUDE, LONGITUDE,
next.latitude(), next.longitude());
assertThat(currentDistance).isLessThanOrEqualTo(nextDistance);
System.out.printf("Index %d: Distance = %.3f km%n", i, currentDistance);
}
// 마지막 요소의 거리도 출력
WaybleFacilityResponseDto last = dtoList.get(dtoList.size() - 1);
double lastDistance = haversine(LATITUDE, LONGITUDE,
last.latitude(), last.longitude());
System.out.printf("Index %d: Distance = %.3f km%n", dtoList.size() - 1, lastDistance);
}
}
assertThat(dtoList).isNotEmpty();
// LIMIT(50) 검증
assertThat(dtoList.size()).isLessThanOrEqualTo(50);
// null 값 검증
for (WaybleFacilityResponseDto dto : dtoList) {
assertThat(dto.latitude()).isNotNull();
assertThat(dto.longitude()).isNotNull();
assertThat(dto.facilityType()).isNotNull();
assertThat(dto.facilityType()).isEqualTo(FacilityType.ELEVATOR);
System.out.println(dto);
}
// 거리순 정렬 검증
if (dtoList.size() > 1) {
for (int i = 0; i < dtoList.size() - 1; i++) {
WaybleFacilityResponseDto current = dtoList.get(i);
WaybleFacilityResponseDto next = dtoList.get(i + 1);
double currentDistance = haversine(LATITUDE, LONGITUDE,
current.latitude(), current.longitude());
double nextDistance = haversine(LATITUDE, LONGITUDE,
next.latitude(), next.longitude());
assertThat(currentDistance).isLessThanOrEqualTo(nextDistance);
System.out.printf("Index %d: Distance = %.3f km%n", i, currentDistance);
}
// 마지막 요소의 거리도 출력
WaybleFacilityResponseDto last = dtoList.get(dtoList.size() - 1);
double lastDistance = haversine(LATITUDE, LONGITUDE,
last.latitude(), last.longitude());
System.out.printf("Index %d: Distance = %.3f km%n", dtoList.size() - 1, lastDistance);
// 반경(10km) 이내 검증 (±50m 허용)
assertThat(lastDistance)
.withFailMessage("반경 초과: 마지막 요소 거리=%.5f km", lastDistance)
.isLessThanOrEqualTo(10.05);
}
🤖 Prompt for AI Agents
In src/test/java/com/wayble/server/explore/WaybleFacilityApiIntegrationTest.java
around lines 204 to 235, the elevator test currently checks non-null fields and
ordering by distance but lacks the same radius and LIMIT validations used in the
ramp test; add assertions to (1) assert dtoList.size() is <= the shared LIMIT
constant used by the ramp test, and (2) assert every dto's computed haversine
distance from (LATITUDE, LONGITUDE) is <= the shared RADIUS constant (or the
same radius value used in the ramp test). Use the existing LATITUDE, LONGITUDE,
LIMIT and RADIUS values (or extract them to shared test constants if needed) and
place these checks before/alongside the distance-order assertions so both
elevator and ramp tests enforce identical bounding/limit constraints.

Copy link
Member

@seung-in-Yoo seung-in-Yoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 코드 구현한거 보면서 많이 배워가네요 수고하셨습니다🙂

@KiSeungMin KiSeungMin merged commit 03ed0a3 into develop Aug 12, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💡 feature 기능 구현 및 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants