feat(plan): Routes API 기반 일정 경로 조회와 실시간 갱신 개선#114
Conversation
- 구간별 선택 이동수단을 지도 폴리라인 경로 조회에 연결 - 지도 경로 상태 및 단위 테스트 추가
- 무효화된 경로 캐시 재사용 방지 - 삭제 후 재추가 회귀 테스트 추가
- 생성 요청에 orderIndex를 전달하고 후속 재정렬 제거 - 생성·변경 항목 델타를 일정 캐시에 병합
- DirectionsService를 Route.computeRoutes로 교체 - Google Maps 타입 갱신 및 경로 조회 테스트 추가
- 펼침 아이콘을 헤더 오른쪽으로 정렬 - 레이아웃 회귀 테스트 추가
- 일정 생성·삭제·정렬·이동 시 서버의 affectedRouteItemIds를 사용 - 이동수단 변경 후 항목 재조회와 경로 무효화 순서를 보장 - 경로 캐시 갱신 회귀 테스트 추가
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
🚧 Files skipped from review as they are similar to previous changes (6)
📝 WalkthroughWalkthrough일정 항목 생성이 서버 지정 위치와 델타 응답을 사용하도록 확장되었습니다. STOMP 경로 무효화가 서버 제공 ID 기반으로 정밀화되었고, 지도 경로 계산·이동수단 전달·일정 헤더 레이아웃이 수정되었습니다. Changes일정 델타 동기화
지도 이동수단과 경로 계산
일정 섹션 토글 레이아웃
개발 환경 설정
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant PlanClient
participant ScheduleItemsAPI
participant QueryCache
participant RouteInvalidation
PlanClient->>ScheduleItemsAPI: orderIndex를 포함한 항목 생성
ScheduleItemsAPI-->>PlanClient: 생성·변경·영향 라우트 델타 반환
PlanClient->>QueryCache: 델타를 계획 장소 캐시에 병합
PlanClient->>RouteInvalidation: 영향받은 경로 소스 무효화
Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/lib/stomp/schedules-dispatch.ts (1)
155-189: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win비동기 GET 시작 전에 route 버킷을 캡처하고 제거하세요.
현재 GET 진행 중 새 이벤트가 같은
mapKey에 버킷을 추가하면, 이전 요청이 그 신규 버킷까지 삭제합니다. 이후 두 번째 조회는 route 무효화 없이 끝나 지도 경로가 stale 상태로 남을 수 있으며, catch 경로도 신규 버킷을 지웁니다.타이머 콜백 시작 시 버킷을 읽고 즉시 삭제한 뒤, 캡처한 값만 해당 비동기 작업에서 처리하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/stomp/schedules-dispatch.ts` around lines 155 - 189, In the setTimeout callback within the schedule-place sync flow, capture the current value from syncSchedulePlacesRouteInvalidationPending using mapKey and delete that entry before starting the asynchronous getScheduleItems request. Use only the captured route bucket for scope checks, source invalidation, and map updates, and ensure the catch path does not delete a newer bucket added while the request is running.
🧹 Nitpick comments (2)
src/app/(main)/plan/_components/itinerary/PlanDaySection.test.tsx (1)
8-27: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
@testing-library/react사용을 고려해 보세요.정규식을 이용해 정적 HTML 문자열을 파싱하고 검사하는 방식은 마크업 구조가 약간만 변경되어도 테스트가 깨지기 쉽습니다.
React 생태계의 표준적인 테스트 도구인
@testing-library/react를 활용해 DOM 노드를 직접 탐색하고 검증하는 방식으로 리팩터링하는 것을 추천합니다. (단, Vitest 설정에jsdom또는happy-dom환경이 구성되어 있어야 합니다.)🛠 제안하는 리팩터링
-import { createElement } from "react"; -import { renderToStaticMarkup } from "react-dom/server"; +import { render, screen } from "`@testing-library/react`"; import { describe, expect, it } from "vitest"; import { PlanDaySection } from "./PlanDaySection"; describe("PlanDaySection", () => { it("펼침 토글 아이콘을 제목과 겹치지 않게 버튼 오른쪽에 배치한다", () => { - const html = renderToStaticMarkup( - createElement( - PlanDaySection, - { - title: "1일차", - subtitle: "7월 16일 (목)", - }, - createElement("div", null, "일정"), - ), - ); - - const triggerMarkup = html.match( - /<button[^>]*aria-expanded="true"[^>]*>([\s\S]*?)<\/button>/, - )?.[1]; - - expect(triggerMarkup).toContain("ml-auto"); - expect(triggerMarkup).toContain("shrink-0"); - expect(html).not.toContain("absolute left-1/2"); + const { container } = render( + <PlanDaySection title="1일차" subtitle="7월 16일 (목)"> + <div>일정</div> + </PlanDaySection> + ); + + const button = screen.getByRole("button", { expanded: true }); + const iconContainer = button.querySelector("span[aria-hidden='true']"); + + expect(iconContainer?.className).toContain("ml-auto"); + expect(iconContainer?.className).toContain("shrink-0"); + expect(container.innerHTML).not.toContain("absolute left-1/2"); }); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/`(main)/plan/_components/itinerary/PlanDaySection.test.tsx around lines 8 - 27, Refactor the PlanDaySection test to use `@testing-library/react` rendering and DOM queries instead of renderToStaticMarkup and regular-expression parsing. Locate the expanded button through its accessible state, assert its class list contains ml-auto and shrink-0, and verify no matching centered absolute element exists; ensure the test environment provides jsdom or happy-dom.src/lib/plan/planItineraryMapSegments.test.ts (1)
5-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win정규화와 기본값 폴백도 회귀 테스트로 고정해 주세요.
현재 테스트는 이미 canonical한 값만 검증하므로, 소문자 입력과 누락된 이동수단이 각각
WALKING,DRIVING으로 변환되는지 추가 검증하는 편이 안전합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/plan/planItineraryMapSegments.test.ts` around lines 5 - 22, Extend the test around flattenPlanItinerarySegmentsFromPlaces with cases covering lowercase travelMode input and an omitted travel mode. Assert that normalization produces “WALKING” for the lowercase value and the default fallback produces “DRIVING” when the value is missing, while preserving the existing canonical-value assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/maps.ts`:
- Around line 267-281: Update fetchPlanSegmentPathLatLng in src/lib/maps.ts to
use DRIVING when segmentTravelMode is omitted, matching
SCHEDULE_TRAVEL_MODE_DEFAULT instead of the current WALKING fallback. Add or
update the omitted-mode test in src/lib/maps-routes.test.ts around lines 48-65
to assert that computeRoutes receives travelMode: "DRIVING".
In `@src/lib/plan/scheduleItemPlaces.ts`:
- Around line 731-753: Separate the post-creation cache synchronization from the
mutation success path around getScheduleItems,
mergeOrRefetchSchedulePlanPlacesFromItems,
invalidateScheduleItemRouteForSources, and bumpPlanMapRouteSegments. Handle
synchronization failures as best-effort: mark the affected schedule/route caches
stale or invalidate them, then return response.createdItem instead of rejecting
after the POST has succeeded.
---
Outside diff comments:
In `@src/lib/stomp/schedules-dispatch.ts`:
- Around line 155-189: In the setTimeout callback within the schedule-place sync
flow, capture the current value from syncSchedulePlacesRouteInvalidationPending
using mapKey and delete that entry before starting the asynchronous
getScheduleItems request. Use only the captured route bucket for scope checks,
source invalidation, and map updates, and ensure the catch path does not delete
a newer bucket added while the request is running.
---
Nitpick comments:
In `@src/app/`(main)/plan/_components/itinerary/PlanDaySection.test.tsx:
- Around line 8-27: Refactor the PlanDaySection test to use
`@testing-library/react` rendering and DOM queries instead of renderToStaticMarkup
and regular-expression parsing. Locate the expanded button through its
accessible state, assert its class list contains ml-auto and shrink-0, and
verify no matching centered absolute element exists; ensure the test environment
provides jsdom or happy-dom.
In `@src/lib/plan/planItineraryMapSegments.test.ts`:
- Around line 5-22: Extend the test around
flattenPlanItinerarySegmentsFromPlaces with cases covering lowercase travelMode
input and an omitted travel mode. Assert that normalization produces “WALKING”
for the lowercase value and the default fallback produces “DRIVING” when the
value is missing, while preserving the existing canonical-value assertion.
🪄 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 Plus
Run ID: 50240381-ccfb-455e-8055-e802cea993ff
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (19)
.gitignoredocs/superpowers/plans/2026-07-16-schedule-item-insert-delta.mdpackage.jsonsrc/app/(main)/plan/_components/itinerary/PlanDaySection.test.tsxsrc/app/(main)/plan/_components/itinerary/PlanDaySection.tsxsrc/components/map/PlanItineraryMapRoutes.tsxsrc/lib/api/rooms/index.tssrc/lib/api/rooms/schedule-items.tssrc/lib/maps-routes.test.tssrc/lib/maps.tssrc/lib/plan/planItineraryMapSegments.test.tssrc/lib/plan/planItineraryMapSegments.tssrc/lib/plan/scheduleItemPlaces.create.test.tssrc/lib/plan/scheduleItemPlaces.test.tssrc/lib/plan/scheduleItemPlaces.tssrc/lib/plan/scheduleItemRoutePersistedQuery.test.tssrc/lib/plan/scheduleItemRoutePersistedQuery.tssrc/lib/stomp/schedules-dispatch.test.tssrc/lib/stomp/schedules-dispatch.ts
- 생성 성공 이후 캐시 동기화 실패를 stale fallback으로 처리 - STOMP 경로 버킷 경쟁 조건 방지 - 이동수단 기본값과 정규화 회귀 테스트 보강
변경 사항
affectedRouteItemIds와 일정 생성 델타 응답을 사용해 영향받는 경로 구간만 무효화합니다.배경
STOMP 이벤트 수신 시 클라이언트 캐시만으로 영향받는 경로 구간을 추론하면, 항목 재조회 시점이나 일차 간 이동 순서에 따라 오래된 구간이 남을 수 있었습니다. 또한 이동수단별로 분리된 경로 쿼리와 지도 렌더링 상태를 함께 갱신할 필요가 있었습니다.
서버가 계산한 영향 범위를 단일 기준으로 사용하고, 항목 데이터 재조회 후 해당 구간의 쿼리와 지도 세그먼트를 갱신하도록 순서를 정리했습니다.
사용자 영향
검증
npm test: 20개 파일, 42개 테스트 통과npm run build: 통과npm run lint: 변경 범위 밖 기존 오류 37건으로 실패Summary by CodeRabbit