Skip to content

Conversation

@doup2001
Copy link
Member

@doup2001 doup2001 commented Jan 5, 2026

📌 작업한 내용

  • 공고 상세 및 목록 API에서 대상자 관련 정보(타겟 그룹)를 조회·노출하도록 기능 추가
    • 공고에서 대상자 그룹 데이터를 조회해 응답에 포함하도록 서비스/쿼리 로직 구현
    • 기존 공고 상세 조회 파라미터를 대상자 그룹 처리에 맞게 수정하여 일관된 조회가 가능하도록 개선[1]
  • 홈 화면 API 신규 구현
    • 홈 진입 시 필요한 공고/콘텐츠를 한 번에 내려주는 홈 화면용 전용 API를 설계 및 구현
    • 홈 화면 서비스의 DB 조회 로직을 홈 전용 요구사항에 맞게 리팩터링하여 불필요한 조회를 줄이고 응답 구조를 정리[1]
  • 응답 DTO 구조 정리
    • 기존 공고/홈 관련 응답 DTO에 타겟 그룹 필드를 추가하여 클라이언트에서 대상자 정보를 바로 사용할 수 있도록 개선[1]
  • 공고/교통 관련 비즈니스 로직 리팩터링
    • 보증금(만원 단위) 변환 로직을 별도 유틸 또는 일관된 메서드로 분리해 중복을 제거하고 가독성을 향상[2]
    • 대중교통 시뮬레이터 첫 부분 로직을 수정해 초기 구간 처리 방식과 파라미터 해석을 명확하게 정리[2]

🔍 참고 사항

  • 이번 PR은 “공고 대상자 그룹 노출”을 중심으로 홈 화면 API 구현, DTO 스펙 정리, 관련 조회/변환 로직 리팩터링까지 한 번에 포함하고 있습니다.[1]
  • 프론트엔드에서 공고/홈 화면 응답 스펙(특히 타겟 그룹 필드명, 타입)을 반드시 확인 후 연동해 주세요.[1]
  • 대중교통 시뮬레이터 및 보증금 변환 로직은 기존 호출부에 영향이 없는지 한 번 더 점검 부탁드립니다.[2]

🖼️ 스크린샷

  • API 응답 스펙 정리용 스크린샷 또는 Swagger 캡처는 추후 필요 시 추가 예정입니다.[1]

🔗 관련 이슈

✅ 체크리스트

  • 로컬에서 빌드 및 테스트 완료
  • 코드 리뷰 반영 완료
  • 문서화 필요 여부 확인 (API 스펙/Swagger에 타겟 그룹 및 홈 API 반영)[1]

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 홈 화면에서 지역별 마감 임박 공지사항 조회 기능 추가
    • 공지사항 통합 검색 기능 추가
    • 공지사항 상세정보에 목표 계층 정보 표시
  • 개선사항

    • 금액 표시 단위 일원화 (원 → 만원)
    • 정렬 옵션 레이블 개선 (주변환경 매칭순)
    • 대중교통 경로 안내 개선

✏️ Tip: You can customize this high-level summary in your review settings.

@doup2001 doup2001 self-assigned this Jan 5, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

개요

새로운 홈 기능의 핵심 서비스 계층을 구현하고, 금액 단위를 원에서 만원으로 통일하며, 공고의 대상계층 정보를 응답 데이터에 추가하고, 교통 경로 표시를 개선합니다.

변경 사항

코호트 / 파일 설명
홈 API 새로운 기능
src/main/java/com/pinHouse/server/platform/home/application/service/HomeService.java, src/main/java/com/pinHouse/server/platform/home/application/usecase/HomeUseCase.java, src/main/java/com/pinHouse/server/platform/home/presentation/HomeApi.java, src/main/java/com/pinHouse/server/platform/home/presentation/swagger/HomeApiSpec.java
HomeService 구현체 추가로 마감 임박 공고 조회 및 통합 검색 기능 제공. 지역/군구 추출 로직과 소유권 검증 포함. HomeApi에 두 개의 REST 엔드포인트(/v1/home/notice, /v1/home/search) 노출.
금액 단위 변환 (원 → 만원)
src/main/java/com/pinHouse/server/platform/housing/complex/application/dto/response/DepositMinMaxResponse.java, src/main/java/com/pinHouse/server/platform/housing/complex/application/service/ComplexService.java, src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/ComplexFilterResponse.java, src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/UnitTypeCompareResponse.java, src/main/java/com/pinHouse/server/platform/housing/notice/application/service/ComplexFilterService.java, src/main/java/com/pinHouse/server/platform/like/application/dto/UnityTypeLikeResponse.java, src/main/java/com/pinHouse/server/platform/search/application/dto/FastUnitTypeResponse.java
보증금 및 가격 관련 필드를 10000으로 나누어 만원 단위로 변환. DepositMinMaxResponse에 fromWon(...) 정적 팩토리 메서드 추가. Swagger 스키마 설명 및 예시값 업데이트.
공고 응답에 대상계층 정보 추가
src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilteredResponse.java, src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailResponse.java, src/main/java/com/pinHouse/server/platform/search/application/dto/NoticeSearchResultResponse.java
targetGroups 필드 추가로 공고의 대상 계층 정보를 응답 데이터에 포함.
저장소 쿼리 확장
src/main/java/com/pinHouse/server/platform/housing/notice/domain/repository/NoticeDocumentRepositoryCustom.java, src/main/java/com/pinHouse/server/platform/housing/notice/domain/repository/NoticeDocumentRepositoryImpl.java
findDeadlineApproachingNoticesByRegionAndCounty(...) 메서드 추가로 지역·군구 기반 마감 임박 공고 조회 지원.
교통 경로 표시 리팩토링
src/main/java/com/pinHouse/server/platform/housing/complex/application/util/TransitResponseMapper.java
첫 번째 도보 단계에 출발지 정보 통합. 별도의 DEPART 단계 제거. 도보 거리와 분석 로직 개선.
필터 라벨 업데이트
src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilterRequest.java
DetailSortType.INFRA의 라벨을 "생활태그 매칭순"에서 "주변환경 매칭순"으로 변경.

시퀀스 다이어그램

sequenceDiagram
    participant Client
    participant HomeApi
    participant HomeService
    participant PinPointRepo as PinPoint<br/>Repository
    participant NoticeRepo as NoticeDocument<br/>Repository
    participant LikeQueryUseCase
    
    Client->>HomeApi: GET /v1/home/notice<br/>(pinpointId, page, size)
    HomeApi->>HomeService: getDeadlineApproachingNotices()
    HomeService->>PinPointRepo: findById(pinpointId)
    PinPointRepo-->>HomeService: PinPoint
    HomeService->>HomeService: extractRegionFromAddress()
    HomeService->>HomeService: extractCountyFromAddress()
    HomeService->>NoticeRepo: findDeadlineApproachingNotices<br/>ByRegionAndCounty()
    NoticeRepo-->>HomeService: Page<NoticeDocument>
    HomeService->>LikeQueryUseCase: 좋아요 여부 조회
    LikeQueryUseCase-->>HomeService: Set<String> (liked ids)
    HomeService->>HomeService: 결과 매핑<br/>(isLiked 플래그 추가)
    HomeService-->>HomeApi: SliceResponse<NoticeListResponse>
    HomeApi-->>Client: ApiResponse
Loading
sequenceDiagram
    participant Client
    participant HomeApi
    participant HomeService
    participant NoticeSearchUseCase
    
    Client->>HomeApi: GET /v1/home/search<br/>(keyword, page, sort, status)
    HomeApi->>HomeService: searchNoticesIntegrated()
    HomeService->>NoticeSearchUseCase: 키워드 기반 검색
    NoticeSearchUseCase-->>HomeService: SliceResponse<result>
    HomeService-->>HomeApi: 결과 반환
    HomeApi-->>Client: ApiResponse
Loading

코드 리뷰 예상 소요 시간

🎯 3 (보통) | ⏱️ ~20분

축시

🐰 새로운 홈 화면이 피어났고,
원에서 만원으로 단위 맞추고,
대상 계층도 이제 담아서,
공고 검색이 더 풍성해졌네!
교통도 편리하게 표시되니,
모든 것이 제자리 찾았어! 🏠✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.08% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 홈 기능 구현이라는 주요 변경 사항을 명확히 요약하고 있으나, 목표 설명과 변경 요약에 따르면 대상자 그룹 노출, DTO 리팩터링, 비즈니스 로직 개선까지 포함되어 있어 제목만으로는 전체 범위를 나타내지 못함.
Description check ✅ Passed PR 설명이 작업 내용(📌), 참고 사항(🔍), 관련 이슈, 체크리스트 등 템플릿의 대부분 섹션을 포함하고 있으며, 구체적인 작업 내역과 주의사항이 명확히 기술되어 있음.
✨ Finishing touches
  • 📝 Generate docstrings

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Jan 5, 2026

Test Results

22 tests  ±0   22 ✅ ±0   0s ⏱️ ±0s
 3 suites ±0    0 💤 ±0 
 3 files   ±0    0 ❌ ±0 

Results for commit 0c2a612. ± Comparison against base commit b1ea73e.

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: 6

Fix all issues with AI Agents 🤖
In
@src/main/java/com/pinHouse/server/platform/home/application/service/HomeService.java:
- Around line 70-71: The extracted county string from
extractCountyFromAddress(pinPoint.getAddress()) may contain regex metacharacters
and is later used in a Pattern in NoticeDocumentRepositoryImpl; escape it before
usage by quoting the value with Pattern.quote() (either wrap the county here in
HomeService before passing to the repository or update the repository method
that consumes the county to call Pattern.quote(county) when building the
regex/Pattern) so no unescaped user/address data is interpolated into regexes.
- Line 78: The code treats SliceRequest.page() as 1-based (HomeService uses
PageRequest.of(sliceRequest.page() - 1, ...)) while the Swagger schema says
pages are 0-based; fix by making the API contract 1-based and enforcing it:
update the SliceRequest Swagger/Schema description to say "페이지 번호 (1부터 시작)" and
add validation on the page field (e.g., @Min(1)) in the SliceRequest class so
clients cannot pass 0 or negative values that would cause PageRequest.of() to
receive a negative index; keep the existing subtraction in HomeService
(PageRequest.of(sliceRequest.page() - 1, ...)) so existing logic remains correct
once validation and docs are aligned.

In
@src/main/java/com/pinHouse/server/platform/housing/complex/application/util/TransitResponseMapper.java:
- Around line 330-344: The departureLocation for the first WALK is being derived
from firstTransport/transportSteps which yields the wrong start (e.g., "강남역")
instead of the actual WALK start; update the logic in TransitResponseMapper so
departureLocation is taken from the first WALK in distanceSteps (use the first
distanceSteps element's startName) before the loop and keep using
createWalkStep(step, ChipType.WALK, departureLocation, isFirstWalk) and
isFirstWalk as-is so the initial WALK shows the correct origin.

In
@src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilteredResponse.java:
- Line 114: The call .targetGroups(notice.getTargetGroups()) can pass null;
change it to pass a non-null empty list instead (e.g., wrap
notice.getTargetGroups() with Optional.ofNullable(...).orElse(List.of()) or
Collections.emptyList()) so
NoticeDetailFilteredResponse.builder().targetGroups(...) always receives a safe
list; update imports if necessary and mirror the same null-handling pattern used
in NoticeService.

In
@src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilterRequest.java:
- Line 55: Update the Schema description to match the enum label change: change
the @Schema description on NoticeDetailFilterRequest from "정렬 유형 (거리순/생활태그 매칭순)"
to "정렬 유형 (거리순/주변환경 매칭순)"; ensure the INFRA enum constant label remains "주변환경
매칭순" and review the from() method in the same enum/class to confirm it still
supports expected Korean labels (note: clients sending the old label "생활태그 매칭순"
will now fail with IllegalArgumentException, so coordinate with frontend teams
about backward-compatibility).

In
@src/main/java/com/pinHouse/server/platform/housing/notice/domain/repository/NoticeDocumentRepositoryImpl.java:
- Line 196: Escape regex metacharacters in the county parameter before building
the regex to avoid unintended matches and ReDoS; in NoticeDocumentRepositoryImpl
replace the raw concatenation used in criteria.and("county").regex("^" + county)
with a version that wraps county via Pattern.quote(county) (import
java.util.regex.Pattern) so the regex becomes anchored but safe, and optionally
null-check county before quoting.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilterRequest.java (1)

14-14: 문서 불일치: 스키마 설명 업데이트 필요

스키마 설명에 "생활태그 매칭순"이 명시되어 있지만, 실제 enum 레이블(55 라인)은 "주변환경 매칭순"으로 변경되었습니다. API 문서의 정확성을 위해 설명도 함께 업데이트해야 합니다.

🔎 제안 수정 사항
-@Schema(description = "정렬 유형 (거리순/생활태그 매칭순)", example = "거리 순")
+@Schema(description = "정렬 유형 (거리순/주변환경 매칭순)", example = "거리 순")
🧹 Nitpick comments (7)
src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/UnitTypeCompareResponse.java (1)

147-163: 만원 단위 변환 로직이 올바르게 구현되었습니다.

정수 나눗셈으로 인한 소수점 이하 절삭이 발생하지만, 실제 보증금은 대부분 만원 단위의 배수이므로 문제없을 것으로 보입니다.

선택사항: 입력값 검증 추가

만원 단위가 아닌 보증금 값이 입력될 경우를 대비해 경고 로그를 추가할 수 있습니다:

 long totalDeposit = deposit.getTotal();
+if (totalDeposit % 10000 != 0) {
+    log.warn("Deposit value {} is not a multiple of 10000, truncation will occur", totalDeposit);
+}
 return CostInfo.builder()
         .totalDeposit(totalDeposit / 10000)
src/main/java/com/pinHouse/server/platform/search/application/dto/FastUnitTypeResponse.java (1)

19-24: Schema 어노테이션 추가 승인, 단위 일관성 검토 필요

보증금과 월 임대료에 대한 Swagger 문서화가 추가되어 API 명세가 명확해졌습니다. 다만 보증금은 만원 단위로, 월 임대료는 원 단위로 표시되어 단위 일관성이 부족합니다. 클라이언트에서 두 가지 단위를 혼용해야 하므로 혼란을 야기할 수 있습니다.

프론트엔드 팀과 협의하여 두 필드 모두 동일한 단위(만원 또는 원)로 통일하는 것을 권장합니

src/main/java/com/pinHouse/server/platform/housing/notice/domain/repository/NoticeDocumentRepositoryImpl.java (1)

181-184: 반복되는 todayStart 계산 로직 리팩토링 권장

이 파일 내에서 동일한 todayStart 계산 로직이 4번 반복됩니다 (lines 28-31, 106-109, 129-132, 181-184). 가독성과 유지보수성 향상을 위해 private 헬퍼 메서드로 추출하는 것을 권장합니다.

🔎 헬퍼 메서드 추출 예시

클래스에 private 메서드 추가:

private Instant getTodayStartInSeoul(Instant now) {
    return ZonedDateTime.ofInstant(now, ZoneId.of("Asia/Seoul"))
            .toLocalDate()
            .atStartOfDay(ZoneId.of("Asia/Seoul"))
            .toInstant();
}

사용 예시:

-    Instant todayStart = ZonedDateTime.ofInstant(now, ZoneId.of("Asia/Seoul"))
-            .toLocalDate()
-            .atStartOfDay(ZoneId.of("Asia/Seoul"))
-            .toInstant();
+    Instant todayStart = getTodayStartInSeoul(now);
src/main/java/com/pinHouse/server/platform/home/presentation/HomeApi.java (1)

30-30: 필드 네이밍 일관성 개선 권장

필드명이 homeService이지만 타입은 HomeUseCase입니다. 컨트롤러 레이어에서는 구현체가 아닌 인터페이스에 의존하는 것이 올바른 설계이므로, 필드명을 homeUseCase로 변경하여 타입과 일치시키는 것을 권장합니다.

🔎 네이밍 개선 제안
-    private final HomeUseCase homeService;
+    private final HomeUseCase homeUseCase;

그리고 메서드 내부에서도 수정:

-        SliceResponse<NoticeListResponse> response = homeService.getDeadlineApproachingNotices(
+        SliceResponse<NoticeListResponse> response = homeUseCase.getDeadlineApproachingNotices(
-        SliceResponse<NoticeSearchResultResponse> response = homeService.searchNoticesIntegrated(
+        SliceResponse<NoticeSearchResultResponse> response = homeUseCase.searchNoticesIntegrated(
src/main/java/com/pinHouse/server/platform/home/application/service/HomeService.java (1)

134-168: 광역 단위 추출 로직의 강건성 개선 권장

하드코딩된 문자열 매칭 방식은 기능적으로는 작동하지만, 다음과 같은 경우에 문제가 될 수 있습니다:

  1. 행정구역 개편 시 유지보수 필요 (예: 충청남도 세종시 → 세종특별자치시 분리)
  2. "전북특별자치도"처럼 새로운 명칭 추가 시 대응 필요
  3. 주소 표기 변형 (예: "경기 성남시" vs "경기도 성남시")

한국 행정구역은 자주 변경되지 않으므로 현재 구현으로도 충분하지만, 장기적으로는 다음 중 하나를 고려해보세요:

  • Region enum에 aliases 필드 추가하여 약칭 관리
  • 외부 주소 표준화 라이브러리 활용 (예: Juso API)
🔎 Region enum 개선 예시
public enum Region {
    SEOUL("서울특별시", List.of("서울")),
    GYEONGGI("경기도", List.of("경기")),
    JEONBUK("전북특별자치도", List.of("전북", "전라북도", "전라북"));
    
    private final String fullName;
    private final List<String> aliases;
    
    // 매칭 로직을 enum 내부로 이동
    public static Region fromAddress(String address) {
        for (Region region : values()) {
            if (address.startsWith(region.fullName)) return region;
            for (String alias : region.aliases) {
                if (address.startsWith(alias)) return region;
            }
        }
        return null;
    }
}
src/main/java/com/pinHouse/server/platform/housing/complex/application/util/TransitResponseMapper.java (2)

322-328: WALK 전용 경로에서 isFirstWalk 플래그의 의미론적 불일치

WALK만 있는 경로에서 createWalkStep을 호출할 때 isFirstWalk=false를 전달하고 있습니다. 첫 번째(그리고 유일한) WALK임에도 불구하고 false로 설정되어 있어 의미론적으로 일관성이 부족합니다.

현재는 departureLocation=null이므로 실제 동작에는 영향이 없지만, 코드의 의도를 명확히 하기 위해 isFirstWalk=true로 전달하는 것이 더 적절해 보입니다.

🔎 제안하는 수정
-                steps.add(createWalkStep(step, null, null, false));
+                steps.add(createWalkStep(step, null, null, true));

309-411: 출발 단계 제거 및 첫 WALK 통합 리팩토링 방향성은 좋습니다

별도의 DEPART 단계를 제거하고 첫 번째 WALK 단계에 출발지 정보를 통합한 접근 방식은 UX 측면에서 개선된 것으로 보입니다.

다만 출발지 정보의 정확성(Line 332)을 확인한 후, 프론트엔드 팀과 함께 응답 스펙(특히 첫 번째 WALK의 primaryText 형식)을 검증하는 것을 권장합니다.

추가 제안:

  • Line 336의 isFirstWalk 플래그 초기화와 Line 344의 플래그 업데이트 사이에 주석을 추가하여 의도를 명확히 하면 유지보수에 도움이 될 것 같습니다.
🔎 주석 추가 예시
+        // 첫 번째 WALK 단계에만 출발지 정보를 포함하기 위한 플래그
         boolean isFirstWalk = true;
 
         for (int i = 0; i < distanceSteps.size(); i++) {
             RootResult.DistanceStep step = distanceSteps.get(i);
 
             if (step.type() == RootResult.TransportType.WALK) {
-                // 첫 번째 WALK는 출발지 정보 포함
+                // 첫 번째 WALK는 출발지 정보 포함, 이후부터는 일반 도보 텍스트만 표시
                 steps.add(createWalkStep(step, ChipType.WALK, departureLocation, isFirstWalk));
                 isFirstWalk = false;
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1ea73e and 0c2a612.

📒 Files selected for processing (18)
  • src/main/java/com/pinHouse/server/platform/home/application/service/HomeService.java
  • src/main/java/com/pinHouse/server/platform/home/application/usecase/HomeUseCase.java
  • src/main/java/com/pinHouse/server/platform/home/presentation/HomeApi.java
  • src/main/java/com/pinHouse/server/platform/home/presentation/swagger/HomeApiSpec.java
  • src/main/java/com/pinHouse/server/platform/housing/complex/application/dto/response/DepositMinMaxResponse.java
  • src/main/java/com/pinHouse/server/platform/housing/complex/application/service/ComplexService.java
  • src/main/java/com/pinHouse/server/platform/housing/complex/application/util/TransitResponseMapper.java
  • src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/ComplexFilterResponse.java
  • src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilterRequest.java
  • src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilteredResponse.java
  • src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailResponse.java
  • src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/UnitTypeCompareResponse.java
  • src/main/java/com/pinHouse/server/platform/housing/notice/application/service/ComplexFilterService.java
  • src/main/java/com/pinHouse/server/platform/housing/notice/domain/repository/NoticeDocumentRepositoryCustom.java
  • src/main/java/com/pinHouse/server/platform/housing/notice/domain/repository/NoticeDocumentRepositoryImpl.java
  • src/main/java/com/pinHouse/server/platform/like/application/dto/UnityTypeLikeResponse.java
  • src/main/java/com/pinHouse/server/platform/search/application/dto/FastUnitTypeResponse.java
  • src/main/java/com/pinHouse/server/platform/search/application/dto/NoticeSearchResultResponse.java
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/pinHouse/server/platform/home/application/service/HomeService.java (2)
src/main/java/com/pinHouse/server/platform/housing/notice/domain/repository/NoticeDocumentRepositoryImpl.java (1)
  • RequiredArgsConstructor (18-208)
src/main/java/com/pinHouse/server/platform/home/presentation/HomeApi.java (1)
  • Slf4j (24-86)
🔇 Additional comments (26)
src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/ComplexFilterResponse.java (2)

73-77: 가격 분포 구간의 단위 변경이 정확히 구현되었습니다

가격 구간 필드들이 일관되게 만원 단위로 변경되었으며, 구간 계산 로직도 올바르게 업데이트되었습니다. ComplexFilterServicecalculatePriceDistribution 메서드에서 내부 계산은 원 단위로 수행한 후, PriceDistribution 객체 생성 시 / 10000 연산으로 만원 단위로 변환하고 있습니다. 두 경우 모두(빈 가격 목록과 정상 분포)에서 동일하게 적용되고 있어 구현이 일관성 있게 처리되었습니다.


54-61: 백엔드 구현에서 가격 단위 변환이 올바르게 적용됨 확인

ComplexFilterService.calculateCostFilter() 메서드에서 원 단위로 계산된 모든 가격 값(minPrice, maxPrice, avgPrice)이 DTO 설정 전에 10000으로 나누어 만원 단위로 변환됩니다 (라인 411-413). 가격 분포(PriceDistribution)의 rangeStart, rangeEnd도 동일하게 변환됩니다 (라인 428-429, 458-459). 변환 로직이 명확하게 주석 처리되어 있으며 DTO의 Swagger 문서와 일치합니다.

src/main/java/com/pinHouse/server/platform/like/application/dto/UnityTypeLikeResponse.java (2)

16-17: 단위 비대칭성 확인 필요

deposit은 만원 단위이지만 monthlyRent는 원 단위를 사용하고 있습니다. 이러한 단위 불일치가 의도된 것인지 확인해 주세요. 클라이언트에서 두 필드를 함께 처리할 때 단위 변환 실수가 발생할 수 있습니다.


33-33: 불필요한 지적 - 현재 구현이 정확하고 일관성 있음

이 코드는 검토가 불필요합니다. 다음과 같은 이유로 현재 구현이 정확합니다:

  1. API 스펙 변경 아님: deposit 필드의 주석을 보면 "임대보증금 (만원 단위)"로 이미 명시되어 있습니다. 이는 API가 처음부터 만원 단위로 반환하도록 설계되었으므로 breaking change가 아닙니다.

  2. 정밀도 손실 아님: 정수 나눗셈은 의도적인 단위 변환입니다. 원(Won) 단위에서 만원(10,000 Won) 단위로 변환하는 것이 목적이며, 이는 일관된 비즈니스 로직입니다.

  3. 공통 유틸 사용 불필요: DepositMinMaxResponse.fromWon()은 최소/보통/최대 임대 옵션의 복합 객체를 처리하기 위해 설계되었습니다. UnityTypeLikeResponse는 단일 보증금 값만 필요하므로, long / 10000를 별도 유틸로 추출하는 것은 과도한 설계입니다. 현재의 인라인 나눗셈은 명확하고 간결합니다.

같은 패턴이 FastUnitTypeResponse, UnitTypeCompareResponse 등에서 일관되게 사용되고 있으며, 코드베이스 전체에서 일관성 있게 구현되어 있습니다.

Likely an incorrect or invalid review comment.

src/main/java/com/pinHouse/server/platform/housing/notice/application/service/ComplexFilterService.java (2)

381-416: 가격 필터 계산 로직이 잘 구현되었습니다.

원 단위로 계산 후 마지막에 만원 단위로 변환하는 접근 방식이 정확도를 유지하면서도 명확합니다. 주석도 변환 시점을 명확히 설명하고 있습니다.


421-465: 가격 분포 계산 로직이 올바르게 구현되었습니다.

버킷 계산을 원 단위로 수행한 후 최종 결과만 만원 단위로 변환하여 정확도를 보장합니다. 엣지 케이스(빈 목록, 단일 가격)도 적절히 처리되고 있습니다.

src/main/java/com/pinHouse/server/platform/housing/complex/application/dto/response/DepositMinMaxResponse.java (1)

18-28: 단위 변환을 위한 팩토리 메서드가 잘 설계되었습니다.

fromWon 메서드는 보증금(만원)과 월세(원)의 단위 차이를 명확하게 처리하며, Javadoc 주석이 변환 로직을 잘 설명하고 있습니다. 이 방식은 중복 코드를 제거하고 단위 변환 로직을 한 곳에 집중시켜 유지보수성을 높입니다.

src/main/java/com/pinHouse/server/platform/housing/complex/application/service/ComplexService.java (1)

346-351: fromWon 팩토리 메서드를 일관되게 사용하고 있습니다.

세 가지 옵션(normal, min, max) 모두에서 새로운 팩토리 메서드를 사용하여 단위 변환 로직을 중앙화했습니다. 이는 코드 중복을 제거하고 향후 변환 로직 수정 시 한 곳만 변경하면 되도록 개선한 좋은 리팩토링입니다.

Also applies to: 381-386, 412-417

src/main/java/com/pinHouse/server/platform/home/application/usecase/HomeUseCase.java (1)

1-49: 홈 화면 Use Case 인터페이스가 잘 정의되었습니다.

두 메서드 모두 명확한 책임을 가지고 있으며, Javadoc이 파라미터와 반환값을 상세히 설명하고 있습니다. SliceResponse를 사용한 무한 스크롤 지원과 nullable userId를 통한 비로그인 사용자 지원도 적절합니다.

src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/UnitTypeCompareResponse.java (1)

141-141: 유닛타입 비교 응답의 보증금 단위 표기가 기존 API 표준과 일치합니다.

보증금 필드의 만원 단위 표기는 이 파일에서만의 변경이 아니며, FastUnitTypeResponse, ComplexFilterResponse, UnityTypeLikeResponse 등 기존 Response DTO들에서 이미 동일하게 적용되어 있습니다. 라인 160의 변환 로직(totalDeposit / 10000)도 일관성 있게 구현되어 있으므로, 별도의 추가 조정은 필요하지 않습니다.

src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilteredResponse.java (1)

100-103: 대상 계층 필드 추가 승인

공고 기본 정보에 대상 계층(targetGroups) 필드가 추가되어 클라이언트에서 모집 대상 정보를 직접 활용할 수 있게 되었습니다. Schema 어노테이션도 적절하게 작성되었습니다.

src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailResponse.java (2)

34-35: 대상 계층 필드 추가 승인

공고 상세 응답에 targetGroups 필드가 추가되어 NoticeDetailFilteredResponse와 일관된 응답 구조를 유지합니다.


57-57: targetGroups 매핑 로직 확인

NoticeDetailFilteredResponse와 동일한 패턴으로 targetGroups를 매핑하고 있습니다. null 처리에 대한 검증은 NoticeDetailFilteredResponse 리뷰 코멘트를 참고하세요.

src/main/java/com/pinHouse/server/platform/search/application/dto/NoticeSearchResultResponse.java (3)

8-8: 필요한 import 추가 승인

targetGroups 필드를 위한 List import가 올바르게 추가되었습니다.


40-41: 검색 결과에 대상 계층 필드 추가 승인

공고 검색 결과에 targetGroups 필드가 추가되어 다른 공고 관련 DTO들(NoticeDetailResponse, NoticeDetailFilteredResponse)과 일관된 응답 구조를 제공합니다.


60-60: targetGroups 매핑 로직 승인

from() 메서드에서 targetGroups를 올바르게 매핑하고 있으며, 오버로드된 from(NoticeDocument) 메서드가 from(notice, false)를 호출하여 일관성을 유지합니다. null 처리에 대한 검증은 NoticeDetailFilteredResponse 리뷰 코멘트를 참고하세요.

src/main/java/com/pinHouse/server/platform/housing/notice/domain/repository/NoticeDocumentRepositoryCustom.java (1)

50-65: 마감임박 공고 조회 메서드 구현 확인 완료

지역 기반 마감임박 공고 조회 메서드의 구현이 올바르게 작성되었습니다. NoticeDocumentRepositoryImpl.java에서 메서드가 정확하게 구현되어 있으며, HomeService와 정상적으로 통합되어 있습니다.

  • 지역(city) 필터링: 정확한 일치 조회 ✓
  • 시/군/구(county) 필터링: 정규식 접두어 매칭 ✓
  • 활성 공고 필터링: applyEnd >= 오늘 시작 시간 ✓
  • 마감임박순 정렬: HomeService에서 Pageable에 Sort 명시 ✓
  • 페이징: PageImpl을 통한 올바른 구현 ✓

MongoDB 쿼리 로직도 적절하게 구성되어 있습니다.

src/main/java/com/pinHouse/server/platform/search/application/dto/FastUnitTypeResponse.java (1)

58-58: 정수 나눗셈으로 인한 절삭 발생 가능성

보증금을 만원 단위로 변환할 때 정수 나눗셈(/ 10000)을 사용하면 만원 미만 금액이 절삭됩니다. 예를 들어 5,555원의 경우 0만원으로 처리됩니다.

현재 코드에는 보증금이 10,000의 배수임을 강제하는 검증이 없으며, 이 패턴이 FastUnitTypeResponseUnityTypeLikeResponse 등 여러 DTO에서 반복되고 있습니다. 일반적으로 보증금은 만원 단위로 책정되므로 실무상 문제가 없을 수 있으나, 다음 중 하나를 확인할 필요가 있습니다:

  • 실제 데이터베이스에 만원 미만의 보증금 데이터가 존재하는지 확인
  • 또는 Deposit 클래스에 검증 제약조건(@Min, @PositiveLong 등)을 추가하여 설계 의도를 명시화
src/main/java/com/pinHouse/server/platform/home/presentation/HomeApi.java (2)

37-56: 구현이 올바릅니다

@CheckLogin 애노테이션을 통한 인증 확인, PrincipalDetails에서 userId 추출, 그리고 use case로의 적절한 위임이 잘 구현되어 있습니다.


62-85: 선택적 인증 처리가 올바릅니다

principalDetails의 null 체크를 통해 비로그인 사용자도 검색 API를 사용할 수 있도록 처리한 부분이 좋습니다. 로그인한 사용자에게는 좋아요 정보를 제공하고, 비로그인 사용자에게는 기본 검색 결과만 제공하는 설계가 적절합니다.

src/main/java/com/pinHouse/server/platform/home/application/service/HomeService.java (3)

54-100: 마감임박 공고 조회 로직이 올바르게 구현되었습니다

PinPoint 소유권 검증, 지역/시군구 추출, 페이지네이션, 좋아요 상태 포함 등 핵심 로직이 잘 구현되어 있습니다. 특히 사용자 소유권 검증을 통해 다른 사용자의 PinPoint에 접근하는 것을 방지한 점이 좋습니다.


175-184: 통합 검색 위임 로직이 적절합니다

기존 NoticeSearchUseCase를 재사용하여 중복을 제거한 점이 좋습니다. 홈 화면의 통합 검색이 별도의 복잡한 로직 없이 기존 검색 기능을 그대로 활용하는 설계가 합리적입니다.


107-127: county 추출 로직은 의도된 대로 동작 중입니다

extractCountyFromAddress 메서드의 4가지 테스트 케이스 모두 올바르게 처리됩니다:

  • "서울특별시 강남구" → "강남구" ✓
  • "서울 강남구" → "강남구" ✓
  • "세종특별자치도" → null ✓
  • "제주특별자치도 제주시" → "제주시" ✓

인덱스 1부터 순회하여 첫 번째 토큰을 건너뛰는 방식은 광역 단위를 제외한 시/군/구를 추출하기 위한 올바른 설계입니다. 세종특별자치도처럼 기초자치단체 단계가 없는 지역에서 null을 반환하는 것은 의도된 동작이며, 저장소의 쿼리 메서드가 null인 경우 county 필터링을 건너뛰므로(NoticeDocumentRepositoryImpl 195-196줄) 추가 처리 없이 올바르게 작동합니다.

src/main/java/com/pinHouse/server/platform/home/presentation/swagger/HomeApiSpec.java (1)

17-56: API 명세가 명확하게 작성되었습니다

Swagger 애노테이션이 잘 작성되어 있습니다:

  • 각 엔드포인트의 기능과 제약사항이 명확히 문서화됨
  • 파라미터 설명과 예시가 적절함
  • 인증 요구사항이 명시됨 (마감임박 공고는 로그인 필수, 통합 검색은 선택)

프론트엔드 팀이 이 명세를 바탕으로 API를 정확히 이해하고 통합할 수 있을 것입니다.

src/main/java/com/pinHouse/server/platform/housing/complex/application/util/TransitResponseMapper.java (2)

360-376: LGTM! 단계 필터링 로직이 올바릅니다

0분 소요 구간을 제거하면서 ARRIVE/ALIGHT 단계(minutes=null)는 유지하는 필터링 로직이 정확합니다. 사용자 경험 측면에서도 적절한 처리입니다.


384-411: createWalkStep 구현은 일관되지만, 출발지 데이터 소스 확인 필요

메서드 구현 자체는 깔끔하고 null 처리도 적절합니다. 다만 Line 332에서 지적한 것처럼, departureLocation 파라미터로 전달되는 값이 의미론적으로 올바른지 확인이 필요합니다.

Line 404의 stopName과 Line 405의 primaryText를 분리한 것은 좋은 설계입니다. 내부 로직용 데이터와 사용자 표시용 텍스트를 명확히 구분하고 있습니다.

Comment on lines +70 to +71
NoticeListRequest.Region region = extractRegionFromAddress(pinPoint.getAddress());
String county = extractCountyFromAddress(pinPoint.getAddress());
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

정규식 특수 문자 이스케이프 필요

추출된 county 값이 NoticeDocumentRepositoryImpl의 line 196에서 정규식 패턴에 직접 사용됩니다. 만약 주소에서 추출된 county에 정규식 메타 문자가 포함되어 있으면 의도하지 않은 매칭이나 ReDoS 취약점이 발생할 수 있습니다.

Repository 레이어에서 Pattern.quote()를 사용하여 이스케이프 처리하는 것을 권장합니다 (NoticeDocumentRepositoryImpl.java의 다른 리뷰 코멘트 참조).

🤖 Prompt for AI Agents
In
@src/main/java/com/pinHouse/server/platform/home/application/service/HomeService.java
around lines 70-71, The extracted county string from
extractCountyFromAddress(pinPoint.getAddress()) may contain regex metacharacters
and is later used in a Pattern in NoticeDocumentRepositoryImpl; escape it before
usage by quoting the value with Pattern.quote() (either wrap the county here in
HomeService before passing to the repository or update the repository method
that consumes the county to call Pattern.quote(county) when building the
regex/Pattern) so no unescaped user/address data is interpolated into regexes.


// 페이징 설정 (마감임박순 정렬)
Sort sort = Sort.by(Sort.Order.asc("applyEnd"), Sort.Order.asc("noticeId"));
Pageable pageable = PageRequest.of(sliceRequest.page() - 1, sliceRequest.offSet(), sort);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find SliceRequest file
fd -t f -i slicerequest.java

Repository: PinHouse/PinHouse_BE

Length of output: 147


🏁 Script executed:

cat -n src/main/java/com/pinHouse/server/core/response/response/pageable/SliceRequest.java

Repository: PinHouse/PinHouse_BE

Length of output: 597


🏁 Script executed:

# Check HomeService line 78 with context
sed -n '70,85p' src/main/java/com/pinHouse/server/platform/home/application/service/HomeService.java

Repository: PinHouse/PinHouse_BE

Length of output: 788


🏁 Script executed:

# Search for tests related to HomeService or SliceRequest
find . -type f -name "*HomeServiceTest*" -o -name "*SliceRequestTest*" 2>/dev/null | head -20

# Also search for other usages of SliceRequest
rg "SliceRequest" --type java -B 2 -A 2 | head -100

Repository: PinHouse/PinHouse_BE

Length of output: 11714


🏁 Script executed:

# Check HomeApi to see how SliceRequest is documented
cat -n src/main/java/com/pinHouse/server/platform/home/presentation/HomeApi.java | head -80

Repository: PinHouse/PinHouse_BE

Length of output: 3702


🏁 Script executed:

# Check HomeApiSpec for any additional documentation
cat -n src/main/java/com/pinHouse/server/platform/home/presentation/swagger/HomeApiSpec.java | head -120

Repository: PinHouse/PinHouse_BE

Length of output: 3188


API 문서와 코드 로직의 페이징 규칙 불일치 해결 필수

SliceRequest의 Swagger 문서에서는 "페이지 번호 (0부터 시작)"이라고 명시했으나, 실제 코드는 sliceRequest.page() - 1로 1-based 입력을 가정하고 있습니다. 이로 인한 문제점:

  1. 문서 오류: SliceRequest 스키마에서 "0부터 시작"이라고 했으나, HomeApi 주석과 코드 로직은 page=1을 첫 번째 페이지로 취급
  2. 검증 부재: SliceRequest에 @Min(1) 등의 검증이 없어서, 클라이언트가 page=0을 전송하면 -1이 되어 PageRequest.of()에서 오류 발생
  3. 클라이언트 혼란: API 문서를 신뢰하고 page=0을 보내면 실패함

해결책:

  • SliceRequest의 Swagger 문서를 "페이지 번호 (1부터 시작)"으로 수정
  • page 필드에 @Min(value = 1) 검증 추가
🤖 Prompt for AI Agents
In
@src/main/java/com/pinHouse/server/platform/home/application/service/HomeService.java
around line 78, The code treats SliceRequest.page() as 1-based (HomeService uses
PageRequest.of(sliceRequest.page() - 1, ...)) while the Swagger schema says
pages are 0-based; fix by making the API contract 1-based and enforcing it:
update the SliceRequest Swagger/Schema description to say "페이지 번호 (1부터 시작)" and
add validation on the page field (e.g., @Min(1)) in the SliceRequest class so
clients cannot pass 0 or negative values that would cause PageRequest.of() to
receive a negative index; keep the existing subtraction in HomeService
(PageRequest.of(sliceRequest.page() - 1, ...)) so existing logic remains correct
once validation and docs are aligned.

Comment on lines +330 to +344
// 출발지 정보 (첫 번째 WALK에 사용)
RootResult.DistanceStep firstTransport = transportSteps.get(0);
steps.add(createDepartStep(firstTransport.startName()));
String departureLocation = firstTransport.startName();

// 2. 전체 구간 순회하며 steps 생성
// 전체 구간 순회하며 steps 생성
int transportIndex = 0;
boolean isFirstWalk = true;

for (int i = 0; i < distanceSteps.size(); i++) {
RootResult.DistanceStep step = distanceSteps.get(i);

if (step.type() == RootResult.TransportType.WALK) {
// WALK step 추가 (색상 포함)
steps.add(createWalkStep(step, ChipType.WALK));
// 첫 번째 WALK는 출발지 정보 포함
steps.add(createWalkStep(step, ChipType.WALK, departureLocation, isFirstWalk));
isFirstWalk = false;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd src/main/java/com/pinHouse/server/platform/housing/complex/application/util && \
rg -n "class DistanceStep|record DistanceStep" -A 20

Repository: PinHouse/PinHouse_BE

Length of output: 46


🏁 Script executed:

rg -n "startName|endName" src/main/java/com/pinHouse/server/platform/housing/complex/application/util/TransitResponseMapper.java -B 2 -A 2

Repository: PinHouse/PinHouse_BE

Length of output: 1440


🏁 Script executed:

# Let's also look at the complete TransitResponseMapper file to understand the context better
wc -l src/main/java/com/pinHouse/server/platform/housing/complex/application/util/TransitResponseMapper.java

Repository: PinHouse/PinHouse_BE

Length of output: 170


🏁 Script executed:

# Search for RootResult class to understand DistanceStep structure
rg -n "class RootResult|record RootResult" -A 5

Repository: PinHouse/PinHouse_BE

Length of output: 847


🏁 Script executed:

# Check how the transportation steps are being used
rg -n "transportSteps|distanceSteps" src/main/java/com/pinHouse/server/platform/housing/complex/application/util/TransitResponseMapper.java -B 1 -A 1

Repository: PinHouse/PinHouse_BE

Length of output: 1500


🏁 Script executed:

rg -n "record DistanceStep|class DistanceStep" -A 20 src/main/java/com/pinHouse/server/platform/housing/complex/application/dto/result/RootResult.java

Repository: PinHouse/PinHouse_BE

Length of output: 797


🏁 Script executed:

# Look at createWalkStep method implementation
rg -n "createWalkStep" src/main/java/com/pinHouse/server/platform/housing/complex/application/util/TransitResponseMapper.java -A 15

Repository: PinHouse/PinHouse_BE

Length of output: 2104


🏁 Script executed:

# Look at the entire logic block around lines 330-350 to understand the flow
sed -n '320,365p' src/main/java/com/pinHouse/server/platform/housing/complex/application/util/TransitResponseMapper.java

Repository: PinHouse/PinHouse_BE

Length of output: 1901


🏁 Script executed:

# Search for how DistanceStep is created to understand what startName/endName represent
rg -n "DistanceStep" src/main/java/com/pinHouse/server/platform/housing/complex/application/dto/result/RootResult.java -B 2 -A 10

Repository: PinHouse/PinHouse_BE

Length of output: 2311


첫 번째 WALK의 출발지가 잘못 설정됨

Line 332에서 departureLocationfirstTransport.startName()에서 추출하고 있는데, 이는 의미론적으로 오류입니다.

예시 경로: [WALK: 출발지 → 강남역] → [BUS: 강남역 → 역삼역] → [WALK: 역삼역 → 도착지]

현재 로직:

  • firstTransport = BUS (강남역 → 역삼역)
  • departureLocation = firstTransport.startName() = "강남역"
  • 결과 텍스트: "강남역에서 도보 약 n분"

하지만 첫 번째 WALK는 강남역으로 걸어가는 것이지, 강남역에서 걸어가는 것이 아닙니다.

출발지는 distanceSteps의 첫 번째 WALK 단계의 startName을 사용해야 합니다
// 현재 (잘못된 것)
RootResult.DistanceStep firstTransport = transportSteps.get(0);
String departureLocation = firstTransport.startName();

// 수정 필요 (올바른 것)
RootResult.DistanceStep firstWalk = distanceSteps.get(0);
String departureLocation = firstWalk.startName();
🤖 Prompt for AI Agents
In
@src/main/java/com/pinHouse/server/platform/housing/complex/application/util/TransitResponseMapper.java
around lines 330-344, The departureLocation for the first WALK is being derived
from firstTransport/transportSteps which yields the wrong start (e.g., "강남역")
instead of the actual WALK start; update the logic in TransitResponseMapper so
departureLocation is taken from the first WALK in distanceSteps (use the first
distanceSteps element's startName) before the loop and keep using
createWalkStep(step, ChipType.WALK, departureLocation, isFirstWalk) and
isFirstWalk as-is so the initial WALK shows the correct origin.

.period(period)
.type(notice.getSupplyType())
.housingType(notice.getHouseType())
.targetGroups(notice.getTargetGroups())
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find and examine NoticeDocument class
find . -type f -name "NoticeDocument.java" | head -20

Repository: PinHouse/PinHouse_BE

Length of output: 157


🏁 Script executed:

# Search for targetGroups field and getter in Java files
rg -n "getTargetGroups|targetGroups" --type java -C 3 | head -100

Repository: PinHouse/PinHouse_BE

Length of output: 7714


🏁 Script executed:

# Examine the NoticeDetailFilteredResponse to understand the field definition
cat -n src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilteredResponse.java | head -150

Repository: PinHouse/PinHouse_BE

Length of output: 6566


🏁 Script executed:

cat -n ./src/main/java/com/pinHouse/server/platform/housing/notice/domain/entity/NoticeDocument.java | tail -50

Repository: PinHouse/PinHouse_BE

Length of output: 1303


🏁 Script executed:

# Check for the full NoticeDocument class including getTargetGroups() method
cat ./src/main/java/com/pinHouse/server/platform/housing/notice/domain/entity/NoticeDocument.java

Repository: PinHouse/PinHouse_BE

Length of output: 1471


🏁 Script executed:

# Check for @NotNull or similar annotations on the field
rg -n "@NotNull|@Nullable" ./src/main/java/com/pinHouse/server/platform/housing/notice/domain/entity/NoticeDocument.java

Repository: PinHouse/PinHouse_BE

Length of output: 46


targetGroups의 null 처리 필수

notice.getTargetGroups()는 null을 반환할 수 있습니다. 코드베이스의 NoticeService에서 Optional.ofNullable(n.getTargetGroups()).orElse(List.of())로 null을 처리하는 패턴이 이미 존재하므로, 이 부분도 동일하게 null 안전성을 보장해야 합니다. 빈 리스트로 대체하여 클라이언트에서 null 체크 없이 안전하게 사용할 수 있도록 수정하세요.

🤖 Prompt for AI Agents
In
@src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilteredResponse.java
around line 114, The call .targetGroups(notice.getTargetGroups()) can pass null;
change it to pass a non-null empty list instead (e.g., wrap
notice.getTargetGroups() with Optional.ofNullable(...).orElse(List.of()) or
Collections.emptyList()) so
NoticeDetailFilteredResponse.builder().targetGroups(...) always receives a safe
list; update imports if necessary and mirror the same null-handling pattern used
in NoticeService.


KM("거리 순"),
INFRA("생활태그 매칭순");
INFRA("주변환경 매칭순");
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilterRequest.java

Repository: PinHouse/PinHouse_BE

Length of output: 3416


🏁 Script executed:

# Search for references to the old label "생활태그 매칭순" in the codebase
rg -n "생활태그\s*매칭순" --type java -C 3

# Also search for the new label "주변환경 매칭순" to see if it's referenced elsewhere
rg -n "주변환경\s*매칭순" --type java -C 3

Repository: PinHouse/PinHouse_BE

Length of output: 1985


🏁 Script executed:

# Search for usages of DetailSortType enum to understand API impact
rg -n "DetailSortType" --type java -C 2 | head -50

Repository: PinHouse/PinHouse_BE

Length of output: 4479


🏁 Script executed:

# Check if there are any test files or documentation that reference the sort types
rg -n "거리\s*순|거리순" --type java -C 2 | head -50

Repository: PinHouse/PinHouse_BE

Length of output: 1376


🏁 Script executed:

# Check if from() method is called from request parameter binding or JSON deserialization
rg -n "from\(" --type java -B 2 -A 2 | grep -i "sorttype\|detailsort" | head -20

Repository: PinHouse/PinHouse_BE

Length of output: 2567


🏁 Script executed:

# Search for any test cases that show how the enum is used
fd -e "Test\.java" -o -e "Test\.kt" | xargs grep -l "DetailSortType\|INFRA" 2>/dev/null | head -5

Repository: PinHouse/PinHouse_BE

Length of output: 180


🏁 Script executed:

# Search for converter or handler related to DetailSortType
rg -n "StringToDetailSortTypeConverter\|DetailSortType" --type java | head -30

Repository: PinHouse/PinHouse_BE

Length of output: 46


🏁 Script executed:

# Check if there's a Spring converter for DetailSortType
fd "StringTo.*DetailSortType.*\.java" -o "DetailSortType.*Converter.*\.java"

Repository: PinHouse/PinHouse_BE

Length of output: 249


🏁 Script executed:

# Search in test directory for DetailSortType usage
find . -name "*Test.java" -o -name "*Tests.java" | xargs grep -l "DetailSortType\|INFRA" 2>/dev/null | head -5

Repository: PinHouse/PinHouse_BE

Length of output: 46


🏁 Script executed:

# Search for actual usage of DetailSortType in service/controller layers
rg -n "DetailSortType\|sortType" src/main/java --type java | grep -v "DetailSortType.java" | head -40

Repository: PinHouse/PinHouse_BE

Length of output: 46


🏁 Script executed:

# Check if there's any reference to the old label in frontend, docs, or tests
rg -n "생활태그.*매칭순" . --type-list all 2>/dev/null | head -20

Repository: PinHouse/PinHouse_BE

Length of output: 46


스키마 설명 업데이트 필요: 라벨 변경이 문서에 반영되지 않음

14번 라인의 @Schema 설명이 실제 enum 값과 일치하지 않습니다:

  • 문서: "정렬 유형 (거리순/생활태그 매칭순)"
  • 실제: INFRA 라벨은 "주변환경 매칭순"으로 변경됨

14번 라인의 스키마 설명을 "정렬 유형 (거리순/주변환경 매칭순)"으로 수정하세요.

추가 고려사항: from() 메서드(65-78번 라인)가 한글 라벨 매칭을 지원하므로, 외부 클라이언트가 이전 라벨 "생활태그 매칭순"을 직렬화 역변환할 경우 IllegalArgumentException이 발생합니다. 프론트엔드 팀과 변경 사항을 조율하여 클라이언트 영향을 확인하세요.

🤖 Prompt for AI Agents
In
@src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilterRequest.java
around line 55, Update the Schema description to match the enum label change:
change the @Schema description on NoticeDetailFilterRequest from "정렬 유형
(거리순/생활태그 매칭순)" to "정렬 유형 (거리순/주변환경 매칭순)"; ensure the INFRA enum constant label
remains "주변환경 매칭순" and review the from() method in the same enum/class to
confirm it still supports expected Korean labels (note: clients sending the old
label "생활태그 매칭순" will now fail with IllegalArgumentException, so coordinate with
frontend teams about backward-compatibility).


/// 시/군/구 필터링 (부분 일치: "성남시"로 "성남시 수정구", "성남시 분당구" 등 매칭)
if (county != null && !county.isBlank()) {
criteria.and("county").regex("^" + county);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

정규식 특수 문자 이스케이프 필요

county 파라미터가 정규식 패턴에 직접 연결되고 있습니다. 만약 county에 정규식 메타 문자(., *, +, ?, [, ] 등)가 포함되어 있으면:

  1. 의도하지 않은 매칭이 발생할 수 있습니다
  2. ReDoS(Regular Expression Denial of Service) 공격에 취약할 수 있습니다

예를 들어, county가 "성남.시"인 경우 "성남X시"도 매칭됩니다.

🔎 정규식 이스케이프 적용 방안
     /// 시/군/구 필터링 (부분 일치: "성남시"로 "성남시 수정구", "성남시 분당구" 등 매칭)
     if (county != null && !county.isBlank()) {
-        criteria.and("county").regex("^" + county);
+        criteria.and("county").regex("^" + Pattern.quote(county));
     }

필요한 import 추가:

+import java.util.regex.Pattern;
📝 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
criteria.and("county").regex("^" + county);
criteria.and("county").regex("^" + Pattern.quote(county));
🤖 Prompt for AI Agents
In
@src/main/java/com/pinHouse/server/platform/housing/notice/domain/repository/NoticeDocumentRepositoryImpl.java
around line 196, Escape regex metacharacters in the county parameter before
building the regex to avoid unintended matches and ReDoS; in
NoticeDocumentRepositoryImpl replace the raw concatenation used in
criteria.and("county").regex("^" + county) with a version that wraps county via
Pattern.quote(county) (import java.util.regex.Pattern) so the regex becomes
anchored but safe, and optionally null-check county before quoting.

@doup2001 doup2001 merged commit 236efc2 into develop Jan 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants