Skip to content

[NDGL-89] 내 여행 탭 구현#25

Merged
jihee-dev merged 4 commits intodevelopfrom
feature/NDGL-89/impl-my-travel-ui
Feb 17, 2026
Merged

[NDGL-89] 내 여행 탭 구현#25
jihee-dev merged 4 commits intodevelopfrom
feature/NDGL-89/impl-my-travel-ui

Conversation

@jihee-dev
Copy link
Copy Markdown
Member

@jihee-dev jihee-dev commented Feb 17, 2026

NDGL-89 내 여행 탭 구현


연관 문서

디자인

스크린샷 (Optional)

스크린샷 스크린샷
image

변경사항

  • 내 여행 탭 UI 구현

    • 기존 travel 패키지의 TravelScreen / TravelViewModel / TravelContract를 제거하고 mytravel 패키지로 재구성
    • MyTravelScreen / MyTravelViewModel / MyTravelContract 신규 생성
  • 다가오는 여행 카드 섹션 (UpcomingTravelCardSection)

    • 가장 가까운 여행 1건을 카드 형태로 표시
    • D-Day(여행 예정) / N일차(여행 중) 상태 분기 처리
  • 다가오는 여행 목록 섹션 (UpcomingTravelListSection)

  • 예정된 여행 목록을 리스트로 표시

  • 여행이 없을 경우 빈 상태 UI + "새로운 여행지 찾아보기" CTA 버튼 노출

  • 추천 여행 템플릿 섹션 (RecommendedTravelSection)

  • 다가오는 여행 카드와 목록이 모두 비어있을 때 추천 여행 템플릿을 가로 스크롤로 표시

  • async/await로 두 API를 병렬 호출 후 결과 확인하여 조건부 로딩

  • API 연동

  • UserTravelApi에 GET /api/v1/travels/upcoming/list 추가

  • UserTravelRepository에 getUpcomingTravelList() 추가

  • UpcomingTravelList 응답 모델 추가

  • TravelTemplateRepository.getRecommendTravelTemplates() 연동

  • 기타

    • ResourceUtil.kt 추가 (ProgramType → 아이콘 리소스 매핑)
    • 빈 여행가방 이미지 리소스(img_empty_suitcase) 추가

테스트 체크 리스트

  • 다가오는 여행 조회
  • 다가오는 여행 목록 조회
  • 추천 여행 조회

Summary by CodeRabbit

릴리스 노트

  • New Features
    • 예정된 여행, 진행 중인 여행, 추천 여행을 보여주는 여행 화면 추가
    • 여행의 D-Day 및 현재 위치 정보 표시 기능 추가
    • 여행 세부 정보 및 위치 클릭 시 상세 페이지로 이동하는 기능 추가

@jihee-dev jihee-dev self-assigned this Feb 17, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 17, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

여행 기능을 리팩토링하여 기존 TravelRoute/TravelViewModel/TravelScreen을 새로운 MyTravelRoute/MyTravelViewModel/MyTravelScreen으로 교체하고, 데이터 레이어에 예정된 여행 목록 조회 기능을 추가했습니다. 여행 카드, 여행 목록, 추천 여행 섹션 등 새로운 UI 컴포넌트를 추가하고 해당 리소스와 유틸리티 함수를 포함합니다.

Changes

Cohort / File(s) Summary
Data Layer - API & Model
data/travel/src/main/java/.../UserTravelApi.kt, data/travel/src/main/java/.../UpcomingTravelList.kt
새로운 API 엔드포인트 getUpcomingTravelList와 pagination 파라미터 추가, 그리고 UpcomingTravelListUpcomingTravel 데이터 클래스 정의 추가
Data Layer - Repository
data/travel/src/main/java/.../UserTravelRepository.kt
저장소에 getUpcomingTravelList() 메서드 추가로 API 응답을 처리
Feature - Contract & ViewModel
feature/travel/src/main/java/.../MyTravelContract.kt, feature/travel/src/main/java/.../MyTravelViewModel.kt
새로운 MVI 계약 정의(상태, 인텐트, 사이드이펙트) 및 ViewModel 구현으로 여행 데이터 로딩 및 상태 관리
Feature - UI Components
feature/travel/src/main/java/.../MyTravelScreen.kt, feature/travel/src/main/java/.../UpcomingTravelCardSection.kt, feature/travel/src/main/java/.../UpcomingTravelListSection.kt, feature/travel/src/main/java/.../RecommendedTravelSection.kt
여행 화면 및 각 섹션 컴포넌트 구성: 진행 중/예정된 여행 카드, 여행 목록, 추천 여행 항목
Feature - Navigation & Utilities
feature/travel/src/main/java/.../TravelEntry.kt, feature/travel/src/main/java/.../ResourceUtil.kt
TravelRoute를 MyTravelRoute로 교체 및 PlaceDetail 네비게이션 추가; 카테고리 및 프로그램 유형을 리소스로 매핑하는 확장 함수 추가
Removed Legacy Code
feature/travel/src/main/java/.../TravelContract.kt, feature/travel/src/main/java/.../TravelScreen.kt, feature/travel/src/main/java/.../TravelViewModel.kt
이전 여행 기능 구현 제거
Resources
core/ui/src/main/res/drawable/img_empty_suitcase.xml, core/ui/src/main/res/values/strings.xml, feature/travel/src/main/res/values/strings.xml
빈 여행 목록 벡터 드로어블, 공통 문자열 리소스(점 구분자), 여행 기능 한국어 문자열 리소스 추가

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

UI/UX 🎨

🚥 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
Title check ✅ Passed PR 제목은 'NDGL-89 내 여행 탭 구현'으로, 작업 항목 번호와 함께 '내 여행 탭 구현'이라는 명확한 주요 변경 사항을 설명하고 있습니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/NDGL-89/impl-my-travel-ui

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

🧹 Nitpick comments (9)
feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelContract.kt (2)

27-35: UpcomingTravel.UpcomingUpcomingTravelItem의 구조가 완전히 동일합니다

두 클래스의 프로퍼티(travelId, title, startDate, endDate, imageUrl, dDay)가 동일합니다. 의도적으로 카드(card) 상태와 리스트 아이템을 분리한 경우라면 주석으로 명시하는 것이 좋고, 그렇지 않다면 UpcomingTravelItem 대신 UpcomingTravel.Upcoming을 재사용하거나 공통 추상 타입을 도출하는 방향을 검토해 보세요.

Also applies to: 57-65

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelContract.kt`
around lines 27 - 35, UpcomingTravel.Upcoming and UpcomingTravelItem define
identical properties (travelId, title, startDate, endDate, imageUrl, dDay),
causing duplicated model definitions; either reuse UpcomingTravel.Upcoming
wherever UpcomingTravelItem is used or extract a shared data type (e.g.,
UpcomingTravelModel or an interface) and have both UpcomingTravel.Upcoming and
UpcomingTravelItem inherit/alias it, and add a clarifying comment if the
separation is intentional. Locate the classes by their names
UpcomingTravel.Upcoming and UpcomingTravelItem and consolidate or refactor to a
single source of truth to remove duplication.

20-21: UpcomingTravel sealed class의 @Stable 어노테이션을 @Immutable로 변경하는 것을 권장합니다

MyTravelState@Immutable로 선언되어 있으며, 이 계약이 유효하려면 클래스 내 모든 타입 역시 @Stable 또는 @Immutable이어야 합니다. UpcomingTravel의 모든 구체적인 서브클래스(Upcoming, InProgress)가 이미 @Immutable data class이므로, 값이 생성 후 변경되지 않는다는 더 강력한 계약을 사용하려면, 모든 프로퍼티가 val이고 immutable 타입인지 확인한 뒤 sealed class 자체도 @Immutable로 선언하는 것이 더 정확한 의미론적 계약입니다.

♻️ 제안 변경 사항
-    `@Stable`
+    `@Immutable`
     sealed class UpcomingTravel {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelContract.kt`
around lines 20 - 21, Change the annotation on the sealed class UpcomingTravel
from `@Stable` to `@Immutable`; update the declaration of UpcomingTravel to use
`@Immutable` and ensure its subclasses Upcoming and InProgress remain immutable
data classes (all properties are val and use immutable types) to satisfy the
MyTravelState immutability contract.
data/travel/src/main/java/com/yapp/ndgl/data/travel/repository/UserTravelRepository.kt (1)

28-30: 페이지네이션 파라미터가 활용되지 않음

API는 page/size 파라미터를 지원하고, UpcomingTravelList 모델에 hasNext: Boolean이 포함되어 있으나, 현재 repository에서는 항상 서버 기본값으로 호출합니다. 서버의 기본 페이지 크기를 초과하는 예정 여행이 있을 경우 전체 목록을 표시하지 못합니다. 향후 무한 스크롤 또는 페이지네이션 구현 시 hasNext 값을 ViewModel에서 활용하는 구조를 고려해 주세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@data/travel/src/main/java/com/yapp/ndgl/data/travel/repository/UserTravelRepository.kt`
around lines 28 - 30, The repository method getUpcomingTravelList() currently
ignores pagination parameters and always calls
userTravelApi.getUpcomingTravelList() with server defaults; update the
repository to accept page and size params (or default constants) and forward
them to userTravelApi.getUpcomingTravelList(page, size), returning
UpcomingTravelList (which contains hasNext) so ViewModel can use hasNext for
pagination/infinite-scroll; update the function signature
(getUpcomingTravelList(page: Int = 0, size: Int = DEFAULT_PAGE_SIZE)) and ensure
any callers are adjusted to supply page/size as needed.
feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/RecommendedTravelSection.kt (1)

91-98: AsyncImage에 로딩 플레이스홀더 및 오류 상태 추가 고려

현재 썸네일 이미지가 로드되는 동안 또는 로드 실패 시 빈 영역이 표시됩니다. Coil 3의 placeholder/error 파라미터를 활용하면 UX를 개선할 수 있습니다.

♻️ 개선 제안
         AsyncImage(
             model = travel.thumbnail,
             contentDescription = travel.title,
             modifier = Modifier
                 .fillMaxWidth()
                 .height(140.dp),
             contentScale = ContentScale.Crop,
+            placeholder = painterResource(CoreR.drawable.img_empty_suitcase),
+            error = painterResource(CoreR.drawable.img_empty_suitcase),
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/RecommendedTravelSection.kt`
around lines 91 - 98, The AsyncImage usage in RecommendedTravelSection.kt
currently shows an empty area on load/failure; update the AsyncImage call in
this component to provide placeholder and error painters (and optionally a
fallback) so a drawable is shown while loading or on error—use
painterResource(...) or a remembered Painter tied to your placeholder/error
drawables and pass them to the AsyncImage parameters (placeholder=...,
error=..., fallback=...) so the thumbnail (model = travel.thumbnail) displays a
proper placeholder and an error image when loading fails.
feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/UpcomingTravelCardSection.kt (1)

120-144: DayTag 컴포저블이 UpcomingTravelListSection.kt와 완전히 중복됩니다.

두 파일의 DayTag가 동일한 로직과 구조를 가지고 있습니다(스타일/색상/조건 모두 동일). 공통 컴포넌트로 추출하여 재사용하는 것을 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/UpcomingTravelCardSection.kt`
around lines 120 - 144, Duplicate DayTag composable logic should be extracted to
a single reusable component: create a shared DayTag composable (same signature:
DayTag(dDay: Int, modifier: Modifier = Modifier)) in a common UI module or
package, move the Box/Text implementation (preserving stringResource keys
R.string.my_travel_upcoming_travel_d_day_minus and _plus, NDGLTheme
styles/colors, and the conditional dDay logic) into that shared function, then
replace the duplicate DayTag definitions in UpcomingTravelCardSection.kt and
UpcomingTravelListSection.kt with imports and calls to the new shared DayTag to
avoid duplication.
feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelScreen.kt (2)

65-67: FIXME: 인기 여행지 리스트 네비게이션이 미구현 상태입니다.

NavigateToPopularTravelList side effect가 발생해도 실제 네비게이션이 수행되지 않습니다. 후속 작업으로 구현 예정인지 확인해주세요.

이 항목을 추적하기 위한 이슈를 생성해 드릴까요?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelScreen.kt`
around lines 65 - 67, The NavigateToPopularTravelList side effect in
MyTravelScreen (MyTravelSideEffect.NavigateToPopularTravelList) is
unimplemented; update the side-effect handler to perform the actual navigation
(e.g., call the NavController.navigate(...) with the popular-travel route or
invoke the existing navigation callback such as
navigateToPopularTravelList/navigateToPopularTravelListScreen) so the app
transitions to the Popular Travel List when this side effect is emitted; ensure
any required parameters or navigation options are passed and unit-tested if
applicable.

46-46: 불필요한 이중 괄호가 있습니다.

(MyTravelIntent.ClickTravel(travelId = travelId)) → 외부 괄호가 불필요합니다.

🧹 수정 제안
-            viewModel.onIntent((MyTravelIntent.ClickTravel(travelId = travelId)))
+            viewModel.onIntent(MyTravelIntent.ClickTravel(travelId = travelId))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelScreen.kt`
at line 46, The call to viewModel.onIntent uses an unnecessary outer pair of
parentheses around MyTravelIntent.ClickTravel; update the invocation in
MyTravelScreen so you pass the intent directly to viewModel.onIntent (i.e.,
replace viewModel.onIntent((MyTravelIntent.ClickTravel(travelId = travelId)))
with a single-parenthesis call
viewModel.onIntent(MyTravelIntent.ClickTravel(travelId = travelId))) to remove
the redundant parentheses.
feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelViewModel.kt (2)

50-89: LocalDate.now()가 여러 곳에서 독립적으로 호출되어 미세한 날짜 불일치 가능성이 있습니다.

mapToUpcomingTravel (Line 51)과 loadUpcomingTravelList (Line 96)에서 각각 LocalDate.now()를 호출합니다. 자정 전후로 실행될 경우 서로 다른 날짜를 기준으로 D-Day가 계산될 수 있습니다. todayinit 블록에서 한 번만 구하고 파라미터로 전달하면 일관성이 보장됩니다.

♻️ 수정 제안
 init {
     viewModelScope.launch {
+        val today = LocalDate.now()
-        val upcomingDeferred = async { loadUpcomingTravel() }
-        val listDeferred = async { loadUpcomingTravelList() }
+        val upcomingDeferred = async { loadUpcomingTravel(today) }
+        val listDeferred = async { loadUpcomingTravelList(today) }
         ...
     }
 }

-private fun mapToUpcomingTravel(travel: UpcomingTravelResponse): MyTravelState.UpcomingTravel? {
-    val today = LocalDate.now()
+private fun mapToUpcomingTravel(travel: UpcomingTravelResponse, today: LocalDate): MyTravelState.UpcomingTravel? {

Also applies to: 91-108

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelViewModel.kt`
around lines 50 - 89, mapToUpcomingTravel and loadUpcomingTravelList
independently call LocalDate.now(), causing potential date mismatch; capture
LocalDate.now() once in the ViewModel init (e.g., val todayAtInit =
LocalDate.now()) and use that single value everywhere: change
mapToUpcomingTravel to accept a LocalDate parameter (or reference the stored
todayAtInit) and update all callers (including loadUpcomingTravelList) to pass
or use the same today value so D-Day/dayCount calculations are consistent across
methods.

26-39: 초기 데이터 로딩 시 에러 및 로딩 상태가 UI에 반영되지 않습니다.

모든 API 호출이 실패할 경우, 사용자에게 에러 피드백 없이 빈 화면만 보여집니다. 현재 빈 상태 UI에 CTA가 있어 최소한의 UX는 보장되지만, 네트워크 에러와 실제로 데이터가 없는 경우를 구분할 수 없습니다.

추후 isLoading / isError 상태를 MyTravelState에 추가하여 스켈레톤 UI 또는 재시도 UI를 제공하는 것을 고려해주세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelViewModel.kt`
around lines 26 - 39, The init block in MyTravelViewModel launches parallel
loads but doesn't update loading/error state, so UI can't show skeletons or
error feedback; add isLoading and isError fields to MyTravelState and update
them from the init coroutine: set isLoading=true before starting async calls (in
the viewModelScope.launch), set isLoading=false after await, and set
isError=true when any loadUpcomingTravel() or loadUpcomingTravelList() throws
(catch exceptions around the async/await or use try/catch inside each loader),
then reduce to emit the new state; ensure loadRecommendedTravels() is only
invoked when no data and isError is false (or preserve separate handling) so UI
can show retry when isError=true.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@data/travel/src/main/java/com/yapp/ndgl/data/travel/repository/UserTravelRepository.kt`:
- Around line 28-30: getUpcomingTravelList() currently lets
HttpResponseException bubble up on a 204 response; wrap the
userTravelApi.getUpcomingTravelList() call in a try/catch for
HttpResponseException (same pattern as getUpcomingTravel()) and if the caught
exception indicates HTTP 204 return an empty UpcomingTravelList (or the same
null/empty sentinel used by getUpcomingTravel()), otherwise rethrow the
exception; reference functions: UserTravelRepository.getUpcomingTravelList(),
userTravelApi.getUpcomingTravelList(), and HttpResponseException.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelContract.kt`:
- Around line 52-54: The model objects have two issues: clarify the unit for
estimatedDuration and unify thumbnail naming; update the
TravelPlace/RecommendedTravel definitions so estimatedDuration is explicit
(either rename estimatedDuration to estimatedDurationMinutes or add KDoc on the
estimatedDuration property indicating "minutes"), and rename
RecommendedTravel.thumbnail to thumbnailUrl to match TravelPlace (or vice versa
across both classes), updating all usages of RecommendedTravel.thumbnail to
RecommendedTravel.thumbnailUrl and any constructors/serializers to preserve
compatibility.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelScreen.kt`:
- Around line 142-152: The MyTravelScreenPreview currently calls MyTravelScreen
directly and is missing the NDGLTheme wrapper, causing the preview to render
without app theme colors/typography; update MyTravelScreenPreview to wrap the
MyTravelScreen invocation inside NDGLTheme { ... } so the preview uses the app's
theme (referencing MyTravelScreenPreview, MyTravelScreen and MyTravelState).

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/UpcomingTravelCardSection.kt`:
- Line 221: 파일의 UpcomingTravelCardSection.kt 내부에 남아있는 standalone 표현식
place.category는 아무 동작을 하지 않는 불필요한 코드입니다; 해당 표현식을 삭제하여 정리하세요. 만약 의도적으로 카테고리 값을
사용하려던 것이라면 place.category를 반환하거나 변수에 할당하거나 UI에 바인드하는 방식으로 의도한 사용처로 대체하고, 그렇지 않다면
해당 라인(단독 place.category 표현)을 제거하면 됩니다.
- Around line 103-105: The DateTimeFormatter instances in
UpcomingTravelCardSection.kt are recreated on every recomposition; wrap the
DateTimeFormatter.ofPattern(...) calls with Compose's remember to cache them
(same approach as in UpcomingTravelListSection.kt). Update the dateFormatter
declaration(s) used in UpcomingTravelCardSection (and the formatter in
InProgressTravelCard) to use remember {
DateTimeFormatter.ofPattern(stringResource(...)) } so they are not reallocated
each recomposition.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/UpcomingTravelListSection.kt`:
- Around line 199-205: The ripple extends beyond the rounded corners because the
modifier order on the Row applies .clickable before .clip; in
UpcomingTravelListSection (the Row with
modifier.wrapContentSize().clickable(onClick =
onClick).clip(RoundedCornerShape(8.dp)).background(...)), move
.clip(RoundedCornerShape(8.dp)) before .clickable so the clickable ripple is
clipped to the rounded shape and remains inside the background bounds.
- Around line 149-153: The D-Day rendering logic in UpcomingTravelListSection is
inverted: swap the condition so future days (dDay > 0) use the "minus" format
and past/zero use the "plus" format; specifically update the conditional around
dDay in UpcomingTravelListSection to use
stringResource(R.string.my_travel_upcoming_list_d_day_minus, dDay) when dDay > 0
and stringResource(R.string.my_travel_upcoming_list_d_day_plus, dDay) otherwise.
Also change the string resource my_travel_upcoming_list_d_day_minus to include
the minus sign (e.g., "D-%d") so it matches UpcomingTravelCardSection and
produces "D-7" for a 7-day future travel.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/navigation/TravelEntry.kt`:
- Around line 28-31: The navigateToTravelDetail lambda currently calls
navigator.navigate(Route.TravelDetail(travelId.toInt())) which can truncate Long
IDs and cause wrong routing; update the code so Route.TravelDetail accepts a
Long (or add an explicit range check and error handling before conversion) and
pass travelId as Long instead of calling toInt(), i.e., change the
Route.TravelDetail constructor/signature to accept Long and adjust all usages
(or implement a guard in navigateToTravelDetail that validates travelId <=
Int.MAX_VALUE and logs/handles out-of-range IDs) to prevent silent truncation.

In `@feature/travel/src/main/res/values/strings.xml`:
- Line 16: The string resource my_travel_upcoming_list_d_day_minus is missing
the hyphen in its format and should match the card section; update the value
from "D%d" to "D-%d" so list items render as "D-3" consistently (edit the string
named my_travel_upcoming_list_d_day_minus in strings.xml).

---

Nitpick comments:
In
`@data/travel/src/main/java/com/yapp/ndgl/data/travel/repository/UserTravelRepository.kt`:
- Around line 28-30: The repository method getUpcomingTravelList() currently
ignores pagination parameters and always calls
userTravelApi.getUpcomingTravelList() with server defaults; update the
repository to accept page and size params (or default constants) and forward
them to userTravelApi.getUpcomingTravelList(page, size), returning
UpcomingTravelList (which contains hasNext) so ViewModel can use hasNext for
pagination/infinite-scroll; update the function signature
(getUpcomingTravelList(page: Int = 0, size: Int = DEFAULT_PAGE_SIZE)) and ensure
any callers are adjusted to supply page/size as needed.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelContract.kt`:
- Around line 27-35: UpcomingTravel.Upcoming and UpcomingTravelItem define
identical properties (travelId, title, startDate, endDate, imageUrl, dDay),
causing duplicated model definitions; either reuse UpcomingTravel.Upcoming
wherever UpcomingTravelItem is used or extract a shared data type (e.g.,
UpcomingTravelModel or an interface) and have both UpcomingTravel.Upcoming and
UpcomingTravelItem inherit/alias it, and add a clarifying comment if the
separation is intentional. Locate the classes by their names
UpcomingTravel.Upcoming and UpcomingTravelItem and consolidate or refactor to a
single source of truth to remove duplication.
- Around line 20-21: Change the annotation on the sealed class UpcomingTravel
from `@Stable` to `@Immutable`; update the declaration of UpcomingTravel to use
`@Immutable` and ensure its subclasses Upcoming and InProgress remain immutable
data classes (all properties are val and use immutable types) to satisfy the
MyTravelState immutability contract.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelScreen.kt`:
- Around line 65-67: The NavigateToPopularTravelList side effect in
MyTravelScreen (MyTravelSideEffect.NavigateToPopularTravelList) is
unimplemented; update the side-effect handler to perform the actual navigation
(e.g., call the NavController.navigate(...) with the popular-travel route or
invoke the existing navigation callback such as
navigateToPopularTravelList/navigateToPopularTravelListScreen) so the app
transitions to the Popular Travel List when this side effect is emitted; ensure
any required parameters or navigation options are passed and unit-tested if
applicable.
- Line 46: The call to viewModel.onIntent uses an unnecessary outer pair of
parentheses around MyTravelIntent.ClickTravel; update the invocation in
MyTravelScreen so you pass the intent directly to viewModel.onIntent (i.e.,
replace viewModel.onIntent((MyTravelIntent.ClickTravel(travelId = travelId)))
with a single-parenthesis call
viewModel.onIntent(MyTravelIntent.ClickTravel(travelId = travelId))) to remove
the redundant parentheses.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelViewModel.kt`:
- Around line 50-89: mapToUpcomingTravel and loadUpcomingTravelList
independently call LocalDate.now(), causing potential date mismatch; capture
LocalDate.now() once in the ViewModel init (e.g., val todayAtInit =
LocalDate.now()) and use that single value everywhere: change
mapToUpcomingTravel to accept a LocalDate parameter (or reference the stored
todayAtInit) and update all callers (including loadUpcomingTravelList) to pass
or use the same today value so D-Day/dayCount calculations are consistent across
methods.
- Around line 26-39: The init block in MyTravelViewModel launches parallel loads
but doesn't update loading/error state, so UI can't show skeletons or error
feedback; add isLoading and isError fields to MyTravelState and update them from
the init coroutine: set isLoading=true before starting async calls (in the
viewModelScope.launch), set isLoading=false after await, and set isError=true
when any loadUpcomingTravel() or loadUpcomingTravelList() throws (catch
exceptions around the async/await or use try/catch inside each loader), then
reduce to emit the new state; ensure loadRecommendedTravels() is only invoked
when no data and isError is false (or preserve separate handling) so UI can show
retry when isError=true.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/RecommendedTravelSection.kt`:
- Around line 91-98: The AsyncImage usage in RecommendedTravelSection.kt
currently shows an empty area on load/failure; update the AsyncImage call in
this component to provide placeholder and error painters (and optionally a
fallback) so a drawable is shown while loading or on error—use
painterResource(...) or a remembered Painter tied to your placeholder/error
drawables and pass them to the AsyncImage parameters (placeholder=...,
error=..., fallback=...) so the thumbnail (model = travel.thumbnail) displays a
proper placeholder and an error image when loading fails.

In
`@feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/UpcomingTravelCardSection.kt`:
- Around line 120-144: Duplicate DayTag composable logic should be extracted to
a single reusable component: create a shared DayTag composable (same signature:
DayTag(dDay: Int, modifier: Modifier = Modifier)) in a common UI module or
package, move the Box/Text implementation (preserving stringResource keys
R.string.my_travel_upcoming_travel_d_day_minus and _plus, NDGLTheme
styles/colors, and the conditional dDay logic) into that shared function, then
replace the duplicate DayTag definitions in UpcomingTravelCardSection.kt and
UpcomingTravelListSection.kt with imports and calls to the new shared DayTag to
avoid duplication.

Comment thread feature/travel/src/main/res/values/strings.xml Outdated
@jihee-dev jihee-dev force-pushed the feature/NDGL-89/impl-my-travel-ui branch 4 times, most recently from 5a6eb7c to 8fc085b Compare February 17, 2026 22:38
@jihee-dev jihee-dev force-pushed the feature/NDGL-89/impl-my-travel-ui branch from 8fc085b to 94f0187 Compare February 17, 2026 22:45
@jihee-dev jihee-dev force-pushed the feature/NDGL-89/impl-my-travel-ui branch from 94f0187 to 17f0475 Compare February 17, 2026 22:49
@jihee-dev jihee-dev merged commit 71c95e0 into develop Feb 17, 2026
2 checks passed
@jihee-dev jihee-dev deleted the feature/NDGL-89/impl-my-travel-ui branch February 17, 2026 22:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant