Skip to content

[Feat] 교통 카테고리 세분화 (TRANSPORTATION_HUB/TRANSIT) 및 공항/역 장소 검색 지원#49

Merged
toychip merged 2 commits intomainfrom
feat/#48-transportation-category-refinement
Mar 27, 2026
Merged

[Feat] 교통 카테고리 세분화 (TRANSPORTATION_HUB/TRANSIT) 및 공항/역 장소 검색 지원#49
toychip merged 2 commits intomainfrom
feat/#48-transportation-category-refinement

Conversation

@toychip
Copy link
Copy Markdown
Contributor

@toychip toychip commented Mar 27, 2026

관련 이슈


변경 내용

교통 카테고리 세분화

  • 기존 단일 TRANSPORTATION 카테고리를 TRANSPORTATION_HUBTRANSPORTATION_TRANSIT으로 분리
    • HUB: 공항, 기차역, 버스터미널 등 고정된 교통 거점 → Google Places 장소 검색 대상
    • TRANSIT: 셔틀버스, 지하철 이동, 택시 등 단순 이동수단 → 장소 검색 제외
  • Gemini AI 프롬프트에 HUB/TRANSIT 구분 기준 명시

장소 검색 로직 수정

  • PlaceEnrichService 쿼리에서 TRANSIT만 제외, HUB는 정상 검색 수행
  • PlaceStatus에서 TRANSIT만 NOT_REQUIRED 처리, HUB는 FOUND/NOT_FOUND 표시
  • TravelItineraryItem.isRetryable()에서 TRANSIT만 재시도 제외

체크리스트

  • Ktlint
  • 테스트 통과 여부

Summary by CodeRabbit

  • Refactor
    • 교통편 카테고리를 허브(공항·역 등)와 이동수단(셔틀·지하철·택시 등)으로 세분화했습니다.
  • Bug Fixes / Behavior
    • 이동수단 카테고리는 장소 재검색 대상에서 제외되어 검색 재시도가 발생하지 않습니다.
  • Chores
    • 외부 분석 응답 및 저장소 스키마 검증을 반영해 분류 규격과 저장 칸 길이를 조정했습니다.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

📝 Walkthrough

Walkthrough

Category 열거형을 TRANSPORTATION에서 TRANSPORTATION_HUB와 TRANSPORTATION_TRANSIT으로 분리하고, 관련 판정/재시도 로직, 저장소 기본값, AI 프롬프트·파싱, DB 컬럼 길이, 및 테스트를 일관되게 업데이트했습니다.

Changes

Cohort / File(s) Summary
카테고리 정의
linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/Category.kt
TRANSPORTATION을 제거하고 TRANSPORTATION_HUB, TRANSPORTATION_TRANSIT 추가.
도메인 로직 업데이트
linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/PlaceStatus.kt, linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/TravelItineraryItem.kt
PlaceStatus.from 및 TravelItineraryItem.isRetryable/isResolved에서 TRANSPORTATION 조건을 TRANSPORTATION_TRANSIT으로 변경.
저장소 기본값 변경
linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/repository/TravelItineraryItemQuerydslRepository.kt
findRetryableItems 및 findVideoAnalysisTaskIdsWithRetryableItems의 excludeCategory 기본값을 TRANSPORTATIONTRANSPORTATION_TRANSIT으로 변경.
AI 프롬프트 및 파싱
linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt, linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt
프롬프트 스키마와 예제 응답을 TRANSPORTATION_HUB/TRANSPORTATION_TRANSIT으로 분리. AiApiResponse.parseCategory에 "TRANSPORTATION" 입력을 TRANSPORTATION_TRANSIT으로 매핑하는 로직 추가 및 입력 정규화 리팩토링.
엔티티 매핑 변경
linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/entity/TravelItineraryItemEntity.kt
category 컬럼의 길이(length)를 20 → 30으로 확장.
테스트 업데이트
linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/TravelItineraryItemTest.kt
테스트에서 Category.TRANSPORTATIONCategory.TRANSPORTATION_TRANSIT으로 교체하여 재검색 관련 검증 유지.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

  • [Feat] video title cost extraction #47: VideoAnalyzeAdapter의 기본 프롬프트 및 Ai 응답 처리 변경과 겹치며 AI 프롬프트/파싱 수정 부분과 코드 수준 연관이 있습니다.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 주요 변경사항인 TRANSPORTATION 카테고리를 TRANSPORTATION_HUB와 TRANSPORTATION_TRANSIT로 세분화하는 내용을 명확하게 설명하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#48-transportation-category-refinement

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.

Copy link
Copy Markdown

@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: 2

🧹 Nitpick comments (1)
linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/TravelItineraryItemTest.kt (1)

35-36: TRANSPORTATION_HUB 양성 케이스 테스트도 같이 추가하는 것을 권장합니다.

Line 35-36은 TRANSIT 제외 규칙을 잘 검증하지만, 분리의 핵심인 “HUB는 재검색 가능” 케이스가 없어 회귀 방어가 약합니다.

테스트 보강 예시
+    `@Test`
+    fun `교통 허브 카테고리는_장소 검색 대상이므로_재검색 가능할 수 있다`() {
+        val item = createItem(
+            placeId = null,
+            category = Category.TRANSPORTATION_HUB,
+            placeSearchCount = 0,
+        )
+
+        assertTrue(item.isRetryable())
+        assertFalse(item.isResolved())
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/TravelItineraryItemTest.kt`
around lines 35 - 36, 현재 TRANSPORTATION_TRANSIT 케이스만 검증하고 있어 회귀 방어가 약하므로
TravelItineraryItemTest에 TRANSPORTATION_HUB 양성 케이스를 추가하세요: createItem를 사용해
category = Category.TRANSPORTATION_HUB(예: placeId = null, placeSearchCount = 0)인
항목을 생성하고, 기존 TRANSPORTATION_TRANSIT 테스트에서 사용한 동일한 판정 로직(같은 assertion)을 반대로 적용하여
허브 항목은 재검색 가능(또는 제외되지 않음)을 확인하도록 추가 검증을 작성하세요; 즉 createItem 및
Category.TRANSPORTATION_HUB/TRANSPORTATION_TRANSIT를 참조해 동일한 테스트 구조로 허브 케이스를
검증하도록 만드세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/Category.kt`:
- Around line 7-8: The enum value TRANSPORTATION_TRANSIT (21 chars) exceeds the
DB column length for TravelItineraryItemEntity.category (defined with length=20)
and will cause truncation/errors; update the JPA mapping on
TravelItineraryItemEntity for the field category (annotated with
`@Enumerated`(EnumType.STRING)) to increase its length to at least 21 (recommended
32) so enum names like TRANSPORTATION_TRANSIT fit, or alternatively rename the
enum to a shorter value—apply the change to the column length attribute where
category is declared.

In
`@linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt`:
- Around line 183-184: AiApiResponse.parseCategory() currently silently maps
unknown categories to Category.EAT which hides misclassifications; update
parseCategory() to explicitly map the legacy "TRANSPORTATION" string to
Category.TRANSPORTATION_TRANSIT and remove the silent fallback to EAT so
invalid/unknown category strings throw an exception (e.g.,
IllegalArgumentException) instead of being swallowed; add unit tests for
parseCategory() covering: valid categories, the legacy "TRANSPORTATION" ->
TRANSPORTATION_TRANSIT mapping, and an invalid value that must cause an
exception to ensure edge-case validation.

---

Nitpick comments:
In
`@linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/TravelItineraryItemTest.kt`:
- Around line 35-36: 현재 TRANSPORTATION_TRANSIT 케이스만 검증하고 있어 회귀 방어가 약하므로
TravelItineraryItemTest에 TRANSPORTATION_HUB 양성 케이스를 추가하세요: createItem를 사용해
category = Category.TRANSPORTATION_HUB(예: placeId = null, placeSearchCount = 0)인
항목을 생성하고, 기존 TRANSPORTATION_TRANSIT 테스트에서 사용한 동일한 판정 로직(같은 assertion)을 반대로 적용하여
허브 항목은 재검색 가능(또는 제외되지 않음)을 확인하도록 추가 검증을 작성하세요; 즉 createItem 및
Category.TRANSPORTATION_HUB/TRANSPORTATION_TRANSIT를 참조해 동일한 테스트 구조로 허브 케이스를
검증하도록 만드세요.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b6450ae0-05bd-4655-a3d5-5b78419d89ee

📥 Commits

Reviewing files that changed from the base of the PR and between aa7a1b7 and 00c54b3.

📒 Files selected for processing (6)
  • linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/Category.kt
  • linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/PlaceStatus.kt
  • linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/TravelItineraryItem.kt
  • linktrip-application/src/test/kotlin/com/linktrip/application/domain/video/TravelItineraryItemTest.kt
  • linktrip-output-http/src/main/kotlin/com/linktrip/output/http/adapter/VideoAnalyzeAdapter.kt
  • linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/repository/TravelItineraryItemQuerydslRepository.kt
📜 Review details
⏰ 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: 자동 검증 (ktlint + test)
🔇 Additional comments (3)
linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/PlaceStatus.kt (1)

14-14: TRANSIT만 NOT_REQUIRED로 분기한 조건이 의도와 정확히 맞습니다.

Line 14 변경으로 HUB는 검색 대상에 남고, TRANSIT만 제외되어 정책 반영이 일관됩니다.

linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/TravelItineraryItem.kt (1)

23-23: isRetryable()에서 TRANSIT만 제외한 변경은 적절합니다.

Line 23 기준으로 HUB는 검색 흐름을 타고, TRANSIT만 제외되어 도메인 규칙과 일치합니다.

linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/repository/TravelItineraryItemQuerydslRepository.kt (1)

59-60: 기본 제외 카테고리를 TRANSIT으로 맞춘 변경은 좋습니다.

Line 59, Line 78 변경으로 조회 쿼리 기본 동작이 도메인 재검색 규칙과 일관됩니다.

Also applies to: 78-79

Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt (1)

69-77: 레거시 "TRANSPORTATION" 하위 호환성 처리가 적절합니다.

기존 데이터와의 호환성을 위해 "TRANSPORTATION"TRANSPORTATION_TRANSIT으로 매핑하는 것은 PR 목적(단순 이동수단은 장소 검색 제외)에 부합합니다.

다만, 공백 문자열 처리를 명시적으로 추가하면 가독성이 향상됩니다. 현재 " "과 같은 공백 문자열은 trim() 후 빈 문자열이 되어 valueOf("") 예외를 거쳐 EAT로 폴백되는데, 이를 조기 반환으로 처리하면 의도가 더 명확해집니다.

♻️ 공백 문자열 조기 반환 제안
 private fun parseCategory(category: String?): Category {
-    val normalized = category?.trim()?.uppercase() ?: return Category.EAT
+    val normalized = category?.trim()?.uppercase()
+    if (normalized.isNullOrEmpty()) return Category.EAT
     if (normalized == "TRANSPORTATION") return Category.TRANSPORTATION_TRANSIT
     return try {
         Category.valueOf(normalized)
     } catch (_: IllegalArgumentException) {
         Category.EAT
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt`
around lines 69 - 77, The parseCategory function should explicitly handle
blank-only strings: after trimming (in parseCategory) if the result is null or
empty return Category.EAT immediately; keep the existing special-case mapping of
"TRANSPORTATION" to Category.TRANSPORTATION_TRANSIT, and only call
Category.valueOf(normalized) for non-empty, non-TRANSPORTATION values, falling
back to Category.EAT on IllegalArgumentException. Update parseCategory to check
normalized.isEmpty() and return EAT early so blank strings are handled clearly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt`:
- Around line 69-77: The parseCategory function should explicitly handle
blank-only strings: after trimming (in parseCategory) if the result is null or
empty return Category.EAT immediately; keep the existing special-case mapping of
"TRANSPORTATION" to Category.TRANSPORTATION_TRANSIT, and only call
Category.valueOf(normalized) for non-empty, non-TRANSPORTATION values, falling
back to Category.EAT on IllegalArgumentException. Update parseCategory to check
normalized.isEmpty() and return EAT early so blank strings are handled clearly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 71e01340-602b-45c0-b4e9-bf358bf3347b

📥 Commits

Reviewing files that changed from the base of the PR and between 00c54b3 and 0edf742.

📒 Files selected for processing (2)
  • linktrip-output-http/src/main/kotlin/com/linktrip/output/http/dto/AiApiResponse.kt
  • linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/entity/TravelItineraryItemEntity.kt
✅ Files skipped from review due to trivial changes (1)
  • linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/entity/TravelItineraryItemEntity.kt
📜 Review details
⏰ 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: 자동 검증 (ktlint + test)

@toychip toychip merged commit 8a5a2d7 into main Mar 27, 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.

[Feat] 교통 카테고리 세분화 (TRANSPORTATION_HUB/TRANSIT) 및 공항/역 장소 검색 지원

1 participant