Skip to content

chore(release): dev 통합 변경을 main 릴리스 후보로 반영 - #261

Merged
minbros merged 9 commits into
mainfrom
dev
Nov 8, 2026
Merged

minbros merged 9 commits into
mainfrom
dev

Conversation

@minbros

@minbros minbros commented Nov 8, 2026 •

Copy link
Copy Markdown
Member

변경 내용

  • 일정 이벤트 응답 순서·캐시 갱신과 STOMP 재연결 복구 개선
  • 지도 검색 범위, 장소 사진 재시도, 일정 시간 입력 및 공통 UI 개선
  • 초대 캠페인 인증 복귀와 홈 피드백 배너 반영

변경 이유

  • dev에 통합된 변경을 main 릴리스 후보로 검증합니다.

테스트

  • npm run lint — 오류 0, 기존 경고 4
  • npm test — 224개 파일, 1,813개 테스트 통과
  • npm run build — 승인된 개발 환경 주입 후 통과 (최초 미주입 실행은 API_BASE_URL 누락으로 실패)
  • 그 외 수동 검증: 최종 dev 조합의 실제 로컬 통합 QA에서 로그인·세션, 방, 지도·북마크, 일정, 일반 채팅·동일 계정 두 탭 실시간 수신, 지출, 개인 준비물, 모바일 주요 진입·편집의 요청 8개 범주를 확인했습니다. 데스크톱과 Android Chrome157 모바일 에뮬레이션을 사용했으며 가입·방 생성 및 일정 정렬·일차 이동은 데스크톱에서 검증했습니다. 개발 모드의 React Query Devtools preload 404는 관찰됐으나 해당 핵심 흐름 실패는 없었습니다. AI는 OFF이며 실기기·교차 사용자 권한·전체 네트워크/오류 상태·production 리소스 검증은 포함하지 않습니다. 사용자 수락된 FE 캐시 잔여 이슈는 유지합니다. 소유 앱·DB 자원은 정리했습니다.
  • 알려진 일정 캐시 잔여 이슈(FE-cache-250-1)는 사용자 승인에 따라 수정하지 않았습니다.

KangShinGyu98 and others added 9 commits October 28, 2026 18:59
* fix(auth): 세션 확인 API에서 백엔드 장애와 인증 실패 구분

- 백엔드 도달 실패·5xx·429는 503, 인증 실패가 확정된 경우만 401
- refresh 직후 재확인이 장애로 실패해도 새 쿠키 전달
- proxy의 세션 판정은 그대로 유지

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(stomp): 재연결을 stompjs 지수 백오프에 맡기고 수동 루프 제거

- reconnectDelay·EXPONENTIAL·maxReconnectDelay(5초)로 재시도 간격 위임
- 재연결 직전 세션 확인을 beforeConnect로 옮기고 401이면 재연결 중단 후 로그아웃
- 연결 문제 배너는 끊긴 지 45초가 지나면 표시하고 연결되면 해제
- 다시 시도는 연결 시도 중이 아닐 때만 바로 재연결

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(stomp): 네트워크가 돌아오면 재연결 예약을 기다리지 않고 바로 연결

- 브라우저 online 이벤트에서 다시 시도와 같은 경로로 즉시 재연결

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(stomp): 끊긴 동안 권한을 잃은 방은 재연결 뒤 퇴장

- 구독 오류(ERROR)를 받으면 방 접근을 HTTP로 확인해 거부면 퇴장 처리
- 구독 오류로 연결이 닫히면 5초 뒤 다시 연결해 1초 간격 반복 방지

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(rooms): 화면 밖에서 무효화된 일정·멤버·안읽음 배지를 다시 볼 때 재조회

- refetchOnMount: false 제거, staleTime: Infinity는 유지해 무효화된 경우에만 조회

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(plan): 일정 전체 재조회를 한 조회로 모아 늦은 응답이 새 일정을 덮지 않게

- 취소된 일정 조회는 캐시에 쓰지 않고, 미리보기를 기다린 뒤 한 번에 쓰기
- 본인 일차 변경·여행 날짜 변경·ROOM_SCHEDULES_RESYNCED는 room-schedules 조회를 거쳐 재조회
- 일차 목록을 고치는 동안 전체 재조회가 진행 중이면 다시 시작
- 재조회로 순서가 바뀐 일차는 이웃이 바뀐 구간 경로만 무효화

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(stomp): 방 토픽을 구독할 때마다 놓친 방 데이터 다시 받기

- 첫 구독·재연결·/home 복귀·방 전환 때 일정·북마크·안읽음 배지를 무효화
- 개인 큐를 구독할 때 놓친 입장 요청 목록을 무효화
- 화면에 있는 조회만 바로 다시 받고, 준비물·가계부·채팅 목록은 대상에서 제외

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): 재조회가 실패해도 보이던 일정·북마크 목록 유지

- 모바일 일정·경로 카드·북마크 폴더·폴더 상세·북마크에서 추가 모달은 처음 불러오기에 실패했을 때만 오류 화면
- 경로 카드는 재조회 실패 뒤에도 받아 둔 일정으로 장소 목록을 계속 읽음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(stomp): 멤버·접속 상태 패치가 다시 받을 멤버 목록의 재조회를 지우지 않게

- 무효화된 멤버 목록에는 이벤트를 패치하지 않아 다음 마운트 때 재조회 유지
- 멤버 목록을 받은 적이 없으면 한 명짜리 목록을 만들지 않음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(plan): 드래그 중 일정이 바뀌면 순서를 저장하지 않고 알림

- 데스크톱 같은 일차·다른 일차, 모바일, 일차 이동 모두 드래그 시작 때 순서와 놓을 때 순서를 비교
- 같은 일차 이동은 드롭 시점 목록 대신 드래그한 항목 id로 요청

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(plan): 연결이 끊긴 동안과 놓친 변경을 다시 받는 동안 일정 드래그 잠금

- 데스크톱·모바일·일차 이동 손잡이는 기존 비활성 표시를 쓰고 목록은 그대로 보임
- 드래그 중에 끊기면 놓아도 저장하지 않고 알림

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(chat): 연결이 바뀌어도 읽음 처리 함수를 그대로 유지

- STOMP client·연결 상태를 보낼 때 ref로 읽어 markMessagesRead가 연결마다 새로 만들어지지 않게 함
- 이 함수에 기대는 채팅 최초 기록 조회가 재연결 때마다 다시 시작되던 원인을 없앰

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(chat): 실시간 채팅을 sequence 커서로 붙이고 놓친 메시지를 끝까지 따라잡기

- 기록 조회·최신으로 이동·아래 페이지·따라잡기 중 받은 메시지를 모았다가 결과 반영 뒤 같은 규칙으로 처리(로딩 중 받은 일반 채팅 유실 수정)
- 커서+1만 목록 끝에 붙이고, 번호가 비면 afterSequence로 200개씩 끝까지 받음
- 재연결·다른 방에서 돌아올 때 놓친 메시지를 따라잡고, 재연결로 새로 붙은 메시지 앞에 구분선을 두고 그 자리로 스크롤
- 중간 창에서는 새 메시지를 붙이지 않고 같은 id만 갱신, AI 요청은 대기 상태로 되돌리지 않음
- 빈 방은 afterSequence=0 대신 최신 페이지를 받고, 400·403·404는 다시 보내지 않음(네트워크·5xx는 1·2·4초 간격 재시도)
- 최초 기록 조회가 재연결로 멈추지 않게 하고, 끝내 실패했으면 재연결 때 다시 받음
- 목록을 sequence 순으로 정렬하고, 최신으로 이동은 목록을 비우지 않고 한 번에 교체
- 메시지·읽음 위치 조회 API가 HttpError를 던지게 함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(chat): 거의 동시에 보낸 채팅이 보낸 순서대로 보이게 정렬

- 서버는 저장 충돌을 재시도한 순서로 sequence를 매겨, 번호 순 정렬이면 1~4가 3·4·1·2로 보였음
- 목록은 작성 시각 순으로 되돌리고, sequence 순서는 빈 번호 판단에만 사용
- 최초 기록·최신으로 이동 결과도 작성 시각 순으로 정렬

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(chat): 놓친 채팅 페이지 테스트의 기대 개수 수정

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(plan): 드래그 중 일정이 바뀌어도 놓은 자리로 순서 저장

- 드래그 시작과 놓을 때의 순서 비교, "일정이 바뀌어서 옮기지 않았어요" 알림 제거(데스크톱·모바일·일차 이동)
- 같은 일차 이동은 계속 드래그한 항목 id로 요청
- 연결이 끊긴 동안과 다시 받는 동안의 드래그 잠금은 유지

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(stomp): 재연결을 7회 실패하면 멈추고 배너 표시

- 재연결 간격 상한을 5초에서 30초로 늘림(1~2초 시작, 2배씩)
- 끊긴 뒤 7번째 재연결까지 실패하면 재연결을 멈추고 연결 문제 배너를 띄움
- 45초 타이머 배너를 없앰
- 배너의 '다시 시도'는 횟수를 처음부터 다시 셈. 멈춘 뒤 online 이벤트로는 다시 시작하지 않음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(stomp): 재연결을 멈춘 뒤에도 네트워크가 돌아오면 다시 연결

- 7회 실패로 재연결을 멈춘 뒤 online 이벤트가 오면 횟수를 처음부터 다시 세며 재연결
- 다시 온라인이 됐다는 것은 인터넷이 완전히 끊겼다가 돌아온 경우를 말하고, 클릭이나 화면 전환은 포함하지 않음
- 화면 전환·클릭에 재시도를 걸면 무한히 재시도하게 되고, 이 상황은 다른 호출은 멀쩡하고 ws만 막힌 한정적인 경우라서 '다시 시도' 버튼에 맡김

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(stomp): 연결 문제 배너 배경을 불투명하게

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- 배포된 main 변경을 반영하고 dev의 기존 변경 이력을 보존
* fix(places): 사진 조회 실패 시 반복 요청하지 않도록 재시도 규칙 변경

- 전역 재시도를 일시적 네트워크 오류 1회로 제한 (429·4xx·5xx는 재시도하지 않음)
- 장소 API 실패를 status가 담긴 HttpError로 던짐
- 사진 retry 2·미리보기 retry 1 개별 설정 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(places): 사진 카드를 batch로 조회하고 실패한 장만 한 번 더 조회

- 같은 커밋에 보이는 사진 2장 이상은 batch, 1장은 단건 API로 보냄
- batch 항목 실패는 실패한 장만 한 번 더 보내고 그래도 실패하면 끝냄
- 요청 자체가 실패하면 단건으로 나눠 보내지 않음
- 쓰이지 않던 사진 batch 시딩 코드 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(places): 지도 북마크 핀·북마크 미리보기를 batch로 조회

- 같은 커밋에 마운트된 미리보기는 batch로 모으고 실패한 곳만 한 번 더 조회
- 지도 핀이 batch를 먼저 보낸 뒤 실패하면 전부 단건으로 다시 받던 단계 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(route): 경로 batch의 경로 없음 답을 단건으로 다시 묻지 않음

- NO_ROUTE·LAST_ITEM·SCHEDULE_ITEM_NOT_FOUND 구간은 경로 없음(null)으로 시딩

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schedule): 일정 불러오기에서 같은 장소 미리보기를 반복 조회하지 않도록 수정

- 모든 일차 장소를 batch 한 번으로 받고 실패한 곳만 batch로 한 번 더 조회, 단건 조회 제거
- 일차 블록은 일정 불러오기가 끝난 뒤 켜서 같은 장소를 다시 받지 않음
- 자리표시자가 남은 일차는 다시 들어올 때 그 장소만 batch로 다시 조회
- 미리보기 batch가 실패해도 일정 전체를 실패로 만들지 않고 자리표시자로 표시

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schedule): 일정 불러오는 동안 일차에 빈 상태 대신 스켈레톤 표시

- 일차 쿼리를 일정 불러오기 중에도 켜고, 진행 중인 불러오기를 기다려 그 결과를 사용
- 같은 진입 안에서 장소 미리보기를 다시 받지 않음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(places): 100곳이 넘을 때 뒤 묶음이 실패해도 앞 묶음에서 받은 장소는 쓰도록 수정

- 로더가 서버 한도(100곳)씩 나눠 차례로 보내고, 받은 묶음은 바로 씀
- 요청이 실패하면 그 묶음과 남은 묶음은 그 에러로 끝내고, 실패 항목도 다시 보내지 않음(요청 수는 이전과 같음)
- 마지막 묶음이 1곳이면 단건 API로 보냄

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schedule): 기다리던 일정 불러오기가 취소되면 새 불러오기를 기다리도록 수정

- 취소된 불러오기를 기다리던 일차 조회가 '조회 중'으로 멈춰 그 일차 경로를 받지 않던 문제 방지
- 진행 중인 일정 불러오기를 취소하고 다시 받는 변경(#247)과 함께 쓰일 때를 대비

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(places): 사진 batch 응답 id 형식이 달라도 같은 장소로 맞추도록 수정

- 응답 id의 `places/` 접두를 떼고 요청 id 기준으로 결과를 돌려줌
- id가 빈 항목이 있어도 batch 전체를 실패로 처리하지 않음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(places): status 없는 미리보기 batch 항목도 id·이름이 있으면 사용

- 레거시 응답(status 없음)을 예전처럼 받아 일정·북마크 핀이 자리표시자로 남지 않게 함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(bookmark): 미리보기를 받지 못하면 내부 에러 문구 대신 안내 문구 표시

- 요청 id·API 오류 코드가 담긴 에러 메시지를 화면에 그대로 보여 주지 않음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(plan): 사진을 다시 받아도 불러오지 못하면 일정 카드에 깨진 이미지 대신 회색 칸 표시

- 사진 오류 처리에 다시 받기까지 실패했을 때 알리는 `onPhotoUnavailable` 추가
- 못 불러온 사진 주소는 그리지 않는 `useVisiblePlacePhotoUrl` 추가(주소가 바뀌면 다시 그림)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(places): 북마크·채팅·장소 상세 사진도 불러오지 못하면 사진 없음 모양 표시

- 북마크·채팅 장소 카드는 지도 핀 아이콘, 장소 상세는 회색 칸으로 둠

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(places): 사진 조회 옵션을 함수로 빼고 테스트가 훅 대신 쓰게 함

- 훅이 아닌 함수에서 훅을 부르던 테스트의 lint 오류(react-hooks/rules-of-hooks) 수정
- 조회 키·queryFn·캐시 설정은 그대로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(places): 사진 없음 처리 훅 테스트가 렌더 중 바깥 변수를 바꾸지 않게 수정

- lint 오류(react-hooks/globals) 수정

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(route): 경로를 다시 받다가 실패해도 마지막 답이 경로 없음이면 오류 대신 그대로 표시

- 순서 변경 등으로 다시 받던 "경로 없음" 구간이 429를 받으면 "경로 조회 오류"로 바뀌던 문제 수정
- 받은 답 없이 실패하면 지금처럼 오류 표시

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(route): 지도가 방금 실패한 경로 구간을 다시 요청하지 않도록 수정

- 목록 요청이 429 등으로 실패한 구간을 지도가 fetchQuery로 한 번 더 보내던 문제 수정
- 진행 중인 요청에 합류하고, 받은 적 없는 구간은 지금처럼 받음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schedule): 미리보기 실패 상태에서 다시 들어와도 지운 장소가 되살아나지 않도록 수정

- 일차 목록이 있으면 room-schedules의 장소 목록으로 다시 만들지 않고 자리표시자 장소의 미리보기만 다시 받아 채움
- room-schedules의 장소 목록은 장소 추가·삭제·수정을 따라가지 않아, 다시 만들면 지운 장소가 되살아났음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: minbros <realminbros@gmail.com>
* chore(ui): shadcn base-nova 설치와 컴포넌트 코드 받기

- @base-ui/react, class-variance-authority 설치
- components.json 작성(style base-nova, init 없이 globals.css 유지)
- shadcn add로 sonner·spinner·alert·skeleton·badge·avatar·toggle·field·input·textarea·checkbox·switch·radio-group 받기(label·separator는 field가 함께 받음)
- 받은 코드는 그대로 두고 적용할 때 디자인 시스템에 맞춰 고친다. add가 함께 설치한 cn·next-themes도 그때 정리한다

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(conventions): shadcn 원칙을 Base UI·cva 기준으로 수정

- 원칙 1-1: Radix 대신 Base UI(base-nova), init 없이 add로만 받기, cn import·dark: 정리, asChild 대신 render
- 원칙 3-6·안티패턴 17: 변형은 클래스 맵 대신 cva로 모으기
- Design.md 1-3·3-3: 설치 전 문구 정리

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(icons): shadcn 부품에 쓸 Figma 아이콘 추가

- Toast: circle-check(Positive)·circle-ban(Negative)
- Spinner: progress
- Checkbox·Radio: checkbox·checkbox-activated·radio-button(Figma 원본 선 2px·1.8px 유지)
- Avatar 대체: user
- 지도 카테고리 Chip: travel-restaurant·hotel·themepark·museum·train
- Design.md 2-1대로 width·height 삭제, currentColor, 원본에 있던 24×24 clipPath 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Toast를 디자인 시스템 Toast에 맞춤

- Figma Toast Base(5285:5064) 값으로 그림: 최소 높이 52, 여백 4·8·4·16, 간격 8, 모서리 8, border/default 1px, fill/elevation, 그림자 없음
- sonner 기본 CSS가 레이어 밖이라 unstyled + classNames로 토큰만 씀, 라이트 테마 고정
- 아이콘: Positive circle-check, Negative circle-ban(20px), 일반 안내는 아이콘 없음
- 닫기 44px·액션 XS Neutral Weak 모양, 접근성 라벨 한국어('알림', '닫기')
- next-themes·lucide·shadcn CSS 변수 제거, 화면은 위치·여백·시간만 넘기도록 Props 제한
- 쓰는 곳이 없어진 next-themes 의존성 삭제

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Spinner를 디자인 시스템 progress 아이콘으로 그림

- Figma progress 아이콘(252:30981)과 Button Loading 색: 기본 icon/default, 버튼 안 inverse
- 크기는 Figma 아이콘 크기(12·16·20·24·28·32, 기본 20)만 받고 svg에 px 클래스로도 줌(shadcn 부모 규칙에 덮이지 않게)
- 루트는 output(암묵 status), 기본 aria-label '불러오는 중', 동작 줄이기에서는 회전 멈춤
- lucide·cn 패키지 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Alert를 지금 화면의 안내 상자 모양으로 정리

- Figma에 인라인 Alert가 없어 지금 상자 3종을 tone으로 옮김: neutral 회색 상자, brand 파란 틴트, warning 주황 테두리
- size lg(로그인·약관 화면)·md(앱 안 상자), orientation vertical·horizontal(동작 위치)
- 금지 색(primary 투명도, muted-brown 등)은 가까운 토큰·원시 색으로 바꿈
- 폭·바깥 margin을 두지 않아 넘치지 않게 함, role="alert" 기본(덮어쓰기 가능)
- shadcn 아이콘 칸·absolute 동작 위치·cn 패키지 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Skeleton을 지금 화면 스켈레톤 모양으로 정리

- Figma에 없어 지금 블록 모양을 기본값으로: fill-strong, 0.25rem 모서리, 펄스, 동작 줄이기에서 멈춤
- tone subtle(fill)로 일정 목록 스켈레톤의 옅은 톤을 보존
- render={<span />} 지원(output·span 안), 기본 block, aria-hidden
- bg-muted(우때에 없는 색)·cn 패키지 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Badge를 디자인 시스템 Badge에 맞춤

- Figma 15 Badges: variant weak·solid·outline × tone 6종 × size lg(24)·md(20)
- Figma 값이 코드 토큰과 다른 색(primary/subtle은 blue-50, status subtle·strong)은 원시 변수로 맞춤
- 모서리 4px, 아이콘 lg 14·md 12px 고정, 비조작 라벨이라 hover·focus 없음
- shadcn variant(default·secondary·destructive 등)·cn 패키지 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Avatar를 지금 화면 아바타 모양으로 정리

- Figma Avatar가 없어 지금 크기(24·28·32·36·40·56)·모양(원·둥근 사각)·톤·접속 링을 variant로 옮김
- 기본값은 Figma Header Profile(36px, fill/strong 원)
- 사진: referrer 없이 요청, 받는 동안 배경색, 실패하면 바로 대체, 주소가 바뀌어도 새 사진이 올 때까지 이전 사진 유지
- AvatarGroup·AvatarGroupCount는 버튼 안에 둘 수 있게 span으로, 쓰지 않는 AvatarBadge 삭제

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Chip을 디자인 시스템 Chip에 맞춤

- toggle.tsx를 chip.tsx로 바꿈: Base UI Toggle 위의 Chip, 모양만 맡는 chipVariants(제거용 칩 등 버튼이 아닌 칩용)
- Figma 14 Chips: Solid·Outline × lg 44·md 36·sm 32, Selected(aria-pressed)·Pressed(:active)·Disabled 색
- Outline 선택+눌림 배경은 원시 변수로 Figma primary/subtle(blue-50)
- 120ms 색 전환, 모바일에서 md·sm 누르는 영역만 44px로 넓힘
- hover·그림자 없음(Figma에 없음)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Field·Input·Textarea를 디자인 시스템 Text Field에 맞춤

- Input: Figma Text Field 48px, Focused·Error·Disabled·Read Only를 DOM 상태(:focus·aria-invalid·:disabled·:read-only)로 그림
- Textarea: Figma Textarea Field 96px 고정 높이와 스크롤
- Field: Figma Header·Footer에 맞춰 FieldHeader·FieldLabel·FieldSuffix·FieldFooter·FieldDescription·FieldError·FieldCount
- 오류 글자는 status/negative/strong(원시 변수 red-800), 오류 테두리는 status-negative
- 쓰지 않는 Field 부품·separator.tsx와 이름 없는 role="group" 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Checkbox를 디자인 시스템 checkbox 아이콘으로 그림

- Figma checkbox 아이콘: 해제는 border/default 외곽선, 선택은 primary 외곽선과 체크
- size md 24·sm 20(가계부 결제자·부담자 시안 크기)
- 비활성은 커서만 바꿈(저장 중 깜빡임 방지), 일부 선택(indeterminate)은 받지 않음
- lucide·cn 패키지 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Switch를 지금 화면 스위치 모양으로 정리

- Figma에 없어 쿠키 설정(md 트랙 48×28, 누르는 영역 56×44)과 경로 표시(sm 36×20) 모양을 옮김
- 지금처럼 <button type="button" role="switch">로 그림(nativeButton)
- 금지 색은 가까운 값으로: md 꺼짐 gray-600, sm 꺼짐 fill-strong, 손잡이 그림자는 지금 값 그대로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Radio를 디자인 시스템 radio button 아이콘으로 그림

- Figma radio button 아이콘: 해제는 border/default 원, 선택은 primary/subtle(원시 변수 blue-50) 원과 10px primary 점
- 간격은 쓰는 쪽이 정하도록 RadioGroup 기본 grid gap 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(deps): 받은 shadcn 코드가 쓰던 cn 패키지 제거

- ui 파일이 모두 @/lib/utils의 cn(타이포 토큰 등록)으로 바뀌어 쓰는 곳이 없음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(toast): 앱 토스트를 ui Toaster로 교체

- AppToaster가 ui/sonner의 Toaster를 그림(기기별 위치·여백은 그대로), richColors 제거
- unstyled로 바뀌어 쓰이지 않는 sonner [data-icon] 24px 규칙 삭제
- toast() 호출부는 그대로 sonner를 씀

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(spinner): 로딩 표시를 ui Spinner로 교체

- LoadingIndicator 27곳(21파일)을 Spinner로 바꾸고 LoadingIndicator.tsx 삭제(label → aria-label, size 14 → 16, 40 → 32)
- 버튼 안 2곳(승인 상태 확인·시간 시트 확인)은 색 className 대신 variant="inverse"
- 테두리 링 3곳(AuthFlowSpinner·RoomGrid·RoomTripSettingsSection)을 Spinner size 32로, RoomGrid의 role=status 래퍼 이름은 Spinner로 옮김
- lucide Loader2 3곳 교체: 이동 수단 행은 size 16·라벨(줄 높이가 튀지 않게 align-top), 일정 카드 사진·AI 처리 중은 장식용 aria-hidden

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(chip): 지도 칩과 목적지 칩을 ui Chip으로 교체

- 지도 카테고리·선택 카테고리·'영업 중' 칩을 Chip(outline md)으로, 아이콘은 Figma travel 아이콘(글자색을 따름)으로 바꿈
- 지도 칩 그림자를 칩 대신 가로 스크롤 줄 drop-shadow로 옮김(Figma Chip에는 그림자가 없음)
- 카테고리 초기화 버튼은 chipVariants와 CloseIcon, 평점 트리거 helper는 하나만 남기고 칩 값에 맞춤
- 목적지 입력값 칩을 chipVariants outline md로 바꾸고 제거 버튼 누르는 영역을 44×44로 넓힘
- 쓰지 않게 된 map-search SVG 6개와 MapSearchIcon 이름 정리

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(conventions): LoadingIndicator를 Spinner로 바꾼 것과 ui 부품 우선 규칙 반영

- AGENTS.md 로딩 원칙·원칙 2-4·안티패턴 6: 작은 영역 로딩 표시는 Spinner
- 원칙 1-1: 맞는 부품에 src/components/ui 포함, 기존 사용처는 '두지' 않고 따로 옮김
- Design.md 1-4: Chips(5624-159)와 Badges(5864-12983) 페이지 링크를 나눔

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(alert): 안내 상자를 ui Alert로 교체

- 로그인·약관 동의 화면·약관 섹션 오류 상자를 Alert brand lg로, 약관 없음 안내를 Alert lg role=status로 바꿈
- login-error-alert.tsx를 login/page.tsx에 합쳐 삭제하고 '!' 원은 grid로 제목 줄 가운데에 맞춤
- 장소 상세·경로 카드 오류와 모바일 장소 검색 오류를 Alert(기본·가로)로 바꾸고 바깥 margin은 감싸는 div padding으로 옮김
- 준비물 알림을 Alert warning 가로로 바꾸고 .alert·.alert button CSS 삭제(아래 16px은 감싸는 div pb-4)
- 버튼 글자 토큰에 덧붙인 font-medium·font-semibold 제거(약관 화면 버튼은 label-l-emphasis 700)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(badge): 일차·역할·장소 분류 라벨을 ui Badge로 교체

- 일정·가계부 일차, 구성원 역할, 참여 승인 대기 상태 라벨을 Badge(weak md)로 바꿈(방장·승인 대기·일차는 brand, 참여자·참여 거절은 neutral)
- PC 장소 카드와 북마크 행의 장소 분류 라벨을 Badge(neutral md)로 바꾸고, 장소 카드는 안쪽 truncate span으로 말줄임 유지
- 쓰지 않게 된 PLAN_PLACE_CARD_TW.primaryTypeBadge 삭제

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(skeleton): 로딩 스켈레톤 블록을 ui Skeleton으로 교체

- 북마크 목록·폴더 상세, 장소 상세·시트 요약, 사진 자리, 경로 카드·후보 카드·경로 상세의 직접 만든 블록을 Skeleton으로 바꿈(bg-fill 블록은 tone subtle, 모서리만 className)
- output 안 블록은 render={<span />}로 span 유지
- 폴더 상세 래퍼의 animate-pulse를 지우고 블록마다 펄스
- PlaceDetailSkeleton의 Bar 헬퍼 삭제, 건드린 템플릿 리터럴 래퍼는 cn()으로
- bg-gray-200 블록은 bg-fill-strong이 되고, 동작 줄이기 설정에서는 펄스가 멈춤
- 일정 목록 스켈레톤(PlanLoadingSkeleton)은 모바일 바탕과 블록 색이 같아 묶음 펄스만 보이는 문제가 있어 결정 전까지 그대로 둠

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(avatar): 사용자·채팅·가계부·리뷰 아바타를 ui Avatar로 교체

- UserAvatar를 ui Avatar로 다시 짜고 색·모양을 size·shape·tone·ring variant로만 받음(호출부 className 색 덮기 제거)
- 헤더 구성원 미리보기를 AvatarGroup·AvatarGroupCount로 옮기고, 본인이 위인 겹침 순서와 화살표 원을 묶음 안에 둠
- 채팅 메시지·장소 공유·새 메시지 미리보기·가계부 인물 아바타를 UserAvatar로 합치고 ChatMemberAvatarRing·chatMessageChrome.avatarSm 삭제
- WOORI·홈 헤더 빈 원·리뷰 작성자 이니셜을 ui Avatar로 교체
- 이름이 옆에 보이는 아바타는 장식으로 숨김(aria-hidden, alt="")

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(text-field): 입력·메모 칸을 ui Field·Input·Textarea로 교체

- 북마크 제목, 가계부 제목·금액·메모·예산, 여행 제목, 준비물 이름·메모, 모바일 메모 시트, 일정 카드 메모를 Field·FieldLabel·Input·Textarea로 바꾸고 라벨은 htmlFor/id로 연결
- 북마크 글자 수는 FieldSuffix, 가계부·준비물 메모 글자 수는 FieldFooter·FieldCount, 예산·준비물 입력 오류는 FieldError(role=alert), 모바일 메모 안내는 FieldDescription으로 옮김
- 예산 모달은 입력 검증 오류만 칸의 Error 상태로 그리고, 저장 실패·충돌 문구는 지금처럼 칸 밖 문장으로 둠
- 준비물 이름·메모에 aria-invalid·aria-describedby를 달아 오류를 Figma Error 모양으로 그림
- ExpenseAmountInput은 ui Input을 그리고 금액 고정폭 숫자(tabular-nums)만 더함
- 쓰지 않게 된 expenseInputClass·PLAN_PLACE_CARD_TW.memoTextarea·준비물 CSS 규칙·RoomTripEditFields 로컬 Field의 htmlFor 분기·일정 메모 rows 삭제
- 준비물 CSS의 레이어 밖 focus-visible 규칙에서 input·textarea를 빼 포커스를 primary 테두리로 통일
- react-test-renderer 가짜 이벤트에 currentTarget 추가(Base UI Input이 읽음, 기대값은 그대로)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(checkbox-switch): 체크박스·스위치를 ui Checkbox·Switch로 교체

- 준비물 행·상세 헤더 체크박스를 Checkbox md로 바꾸고 label 래퍼(24px, 모바일 44px)는 유지, 비활성 커서는 래퍼 전체에 적용
- 가계부 결제자·부담자를 Checkbox sm으로 바꾸고, fieldset disabled가 닿지 않아 ExpenseRolePicker가 disabled를 받아 저장 중 비활성으로 둠
- 가계부 행 포커스 링을 has-[:focus-visible]로 바꿔 키보드 포커스에서만 표시
- 약관 전체 동의는 Checkbox md, 항목은 sm으로 바꾸고 전체 동의 행을 가운데 정렬
- 일정 '지도에 경로 표시'를 label 행 + Switch sm으로, 쿠키 설정 분석 쿠키를 Switch md로 교체(받은 checked 값 사용)
- 바텀시트 드래그 제외 선택자에 label·checkbox·switch·radio role 추가
- 쓰지 않게 된 준비물 CSS 규칙, packingIcons.checkbox·checkboxChecked, 체크박스 SVG 4개 삭제
- react-test-renderer 가짜 onChange 호출을 onCheckedChange 호출로 바꿈(기대값 그대로)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(text-field): 일정 메모칸이 내용 따라 3~5줄로 자라게 함

- ui Textarea에 sizing 변형 추가: fixed(기본, Figma 96px 고정)·content(3줄 98px~5줄 146px, 넘치면 스크롤)
- content는 값이 바뀔 때마다 높이를 내용에 맞추고, 내용이 줄면 다시 줄어든다
- PC 일정 메모(PlanItemMemoForm)만 content를 쓴다(교체 때 사라진 끌어서 늘리기를 대신함)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(chat): 나간 사람 아이콘 규칙을 ChatSenderAvatar 하나로 모음

- 채팅 묶음·장소 공유 카드·새 메시지 미리보기에 복사돼 있던 사진 자리 규칙을 한 컴포넌트로 옮김
- 화면 결과는 같음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(expenses): 예산 입력 오류와 저장 실패를 상태 하나로 합침

- 함께 뜨지 않는 두 오류를 kind(field·save)가 있는 유니온 하나로 둠(원칙 2-1)
- 설명 id도 하나를 나눠 씀, 화면 결과는 같음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(text-field): 새 여행 만들기 입력을 Text Field로 교체

- 제목은 ui Field·Input, 글자 수는 Header Suffix로 옮김
- 목적지는 여행 정보 수정과 같은 배치(검색 칸 → 칩 → 경고)와 field 모양 검색 칸으로 바꿈
- 카드 클래스(TRIP_FORM_FIELD_*)와 쓰지 않던 읽기 전용 분기 삭제

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(chip): Chip을 Badge처럼 쓰는 부품으로 바꾸고 className 칩을 없앰

- ui Chip은 기본 span(useRender), 누르는 칩은 render로 button·Toggle을 넘긴다
- ChipRemove(칩 안 X 버튼) 추가, chipVariants는 밖으로 내보내지 않음
- 지도 고르는 칩은 MapChipToggle(Outline md 고정, 모바일 44px 누르는 영역)로 옮김
- 목적지 칩 2곳·지도 초기화 버튼의 chipVariants className을 Chip으로 바꿈

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(text-field): 일정 메모칸을 늘리지 않고 Figma 96px 고정 높이로 되돌림

- 3~5줄로 자라던 sizing 변형과 높이 측정 코드를 지움
- 3줄부터 스크롤된다(다른 Textarea와 같음)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(chip): 목적지 칩을 Selected 모양으로 바꾸고 X 누르는 영역이 라벨을 덮지 않게 함

- Chip에 selected 변형 추가(Figma Selected=True·Enabled: Outline은 테두리·글자 primary-strong)
- 목적지 칩 2곳(새 여행 만들기·여행 정보 수정)을 selected로 그림
- ChipRemove 누르는 영역 44×44 → 28×44(라벨 끝 8px을 덮던 것 없앰)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(chip): 칩 X 버튼에 손가락 커서 추가

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(text-field): 목적지 검색칸을 Figma Search Bar 규격에 맞춤

- 앞에 돋보기(SearchIcon 20px, icon/subtle)를 보이고 지우기는 CloseIcon 20px·32px 영역으로 바꿈
- 모서리 10 → 8px, 입력 글자 text/default·플레이스홀더 text/subtle
- 테두리: 포커스 primary/default, 값이 있고 포커스가 없으면 primary/strong(Filled)
- 새 여행 만들기·여행 정보 수정 두 곳에 적용(field 모양만, 기본 모양은 그대로)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(text-field): 목적지 검색칸 아래에 선택 안내를 Footer 보조 문구로 다시 보임

- field 모양에서도 '직접 입력은 불가하며, 검색 후 목록에서 선택해 주세요.'를 보이고 입력의 aria-describedby로 잇는다
- 글자는 Figma Footer Helper Text(caption/sm/regular·text/subtle, 칸 아래 4px)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(badge): 대기 화면 '참여 거절' 배지를 Critical 톤으로 바꿈

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(waiting): 대기 화면 상단 로고를 콤비네이션 마크 M으로 바꿈

- 심볼 S(23px) → 콤비네이션 마크 M(147×45px)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(chip): 지도 카테고리 칩 아이콘을 교체 전처럼 파란색으로 되돌림

- 음식점·호텔 등 카테고리 아이콘을 글자색 대신 primary-strong(#005bd9)으로 그린다(선택 여부와 무관)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(chip): 칩 안 아이콘을 16px·선 1.5px로 맞춤

- Figma Chip 예시(chevron 5628:50·check 5628:56)의 16px·1.5px를 따른다
- 기본 아이콘 선 1.8(createIcon)을 칩 안에서만 CSS로 덮는다(칩 밖 아이콘은 그대로 1.8)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(ui): Figma Search Bar를 SearchBar 부품으로 만들고 장소 검색칸에 적용

- ui SearchBar·SearchBarIcon·SearchBarInput·SearchBarClear 추가(Search Bar 5002:4377: 48px, 모서리 8, 앞 아이콘·지우기 20px, 포커스 primary·값 있음 primary-strong 테두리)
- PC 지도 검색칸, PC 일정 '장소 추가하기'는 Figma 모양(모서리 8)
- 모바일 검색 전용 화면(지도 검색·장소 추가 검색)은 shape="pill"로 알약 모양을 두고 아이콘·테두리만 맞춤
- 지우기 아이콘을 원형 X·옛 지도 SVG에서 CloseIcon으로, 쓰지 않게 된 public/map-search/clear.svg 삭제
- 접힌 지도 검색 버튼(46px)과 검색 동작(PC는 입력 중 지우기 숨김 등)은 그대로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): 모바일 검색 화면 뒤로가기를 SearchBarBack으로 통일

- 아이콘 ChevronLeftIcon 20px, 누르는 영역은 지우기와 같은 32px × 칸 높이, 포커스 테두리·손가락 커서 동일
- 모바일 지도 검색(20×48·회색 hover)과 장소 추가 검색(약 28×28·표시 없음)이 달랐던 것을 맞춤

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(text-field): Textarea를 손으로 세로 늘릴 수 있게 함(96px~8줄)

- 시작 96px(rows 무관)은 그대로, resize-y와 min-h-24로 96보다 줄지 않게, 최대 8줄 218px
- Figma Textarea Field는 자동 확장·최대 높이를 정하지 않아 8줄은 코드에서 정한 값, 터치 기기는 손잡이가 없어 그대로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(ui): 동작 부품 shadcn base-nova 코드 받기

- button, dialog, alert-dialog, drawer, dropdown-menu, select, combobox, popover, tabs와 combobox가 쓰는 input-group을 받은 그대로 둠(모두 Base UI 기반, drawer도 vaul 없음)
- add가 함께 설치한 cn 패키지는 부품을 고칠 때 @/lib/utils로 바꾸고 지운다
- add가 덮어쓴 input·textarea는 우리 파일로 되돌림

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(deps): TanStack Form 추가

- 대화상자 안 폼을 새로 쓸 때 @tanstack/react-form으로 칸별 검증·제출 상태를 다룬다(zod는 나중에)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(ui): Base UI 대화상자·팝업이 앱 위에 뜨도록 루트 설정

- 앱 내용을 isolate 묶음으로 감싸 body로 포털되는 Base UI 팝업이 앱의 z-index와 섞이지 않게 함(토스트는 밖에 두어 대화상자 위에 보임)
- iOS 26 Safari용 body { position: relative } 추가(Base UI 빠른 시작 안내)
- 랜딩·로그인 PC·모바일에서 감싸기 전후 배치가 같음을 확인

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(icons): check·sign/logout 아이콘 추가

- Figma ❸ Icon의 check(252:27411)·sign/logout(252:30979) 24px 원본, 선 1.8·currentColor
- Select·Combobox 선택 표시와 메뉴의 나가기·로그아웃 항목에 쓴다

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Button을 디자인 시스템 Button에 맞춤

- Figma Button(5272:3019)·Button Base(5002:3814): variant solid·weak·outline × tone neutral·brand·critical × size lg 56·md 48·sm 36·xs 32, 등록된 6종만 타입으로 허용
- Hover·Pressed·Disabled(opacity 없이 fill/default·text/disabled)·Loading 색을 Figma 값으로, 비활성은 data-disabled로 그림
- loading이면 내용 자리를 남기고 가운데 progress(lg 24·그 외 20), 중복 실행 차단·포커스 유지, 이름은 loadingLabel(aria-labelledby)
- buttonVariants는 내보내지 않음(className으로 버튼 모양 흉내 금지)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Dialog를 디자인 시스템 Modal에 맞춤

- Figma Modal(5956:24724): padding 20·간격 20·모서리 12·fill/elevate·shadow/default, 딤은 지금 앱 값 black/40
- 제목 줄에 닫기 X(24, 누르는 영역 44), Footer는 취소→확인 꽉 채움, 폭은 Figma 353 기본 + 지금 화면 폭 변형
- closeDisabled면 Esc·딤·X·취소로 닫히지 않고, 안쪽 입력이 막은 Esc는 대화상자를 닫지 않음
- 열림·닫힘은 Base UI data-starting/ending-style 전환, 중첩 대화상자도 딤을 그림

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): AlertDialog를 디자인 시스템 Alert Dialog에 맞춤

- Figma Alert Dialog(5621:3002): 353~400 폭·padding 24·모서리 10·그림자 없음·가운데 정렬, 버튼 md 48 꽉 채움(가로 취소→주요, 세로 주요→취소)
- 확인 버튼은 Neutral Solid 또는 Critical Solid만, 첫 포커스는 취소 버튼
- backdropCloseLabel을 주면 딤을 눌러 닫힘(지금 확인창 동작), closeDisabled로 처리 중 닫힘을 막음, z-50·최대 높이 스크롤

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Drawer를 디자인 시스템 Bottom Sheet에 맞춤

- Figma Bottom Sheet(5924:13961): 위 모서리 20, 딤 gray-950 20%, 제목 title/lg·영역 간격 20, 손잡이 36×4
- 아래로 스와이프해 닫기(Base UI 기준), 버튼·입력·label·checkbox/switch/radio 위에서는 끌리지 않음
- Dialog와 같은 closeDisabled·Esc 규칙, 중첩 시트도 딤을 그림

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): DropdownMenu를 디자인 시스템 Dropdown Menu에 맞춤

- Figma Dropdown Menu(5864:13084)·Item(5956:16861): 모서리 12·테두리 없음·shadow/default·최소 폭 160·항목 48/50, 위험 항목 red-800
- 기본 modal={false}로 바깥 클릭이 뒤로 전달되고 스크롤을 막지 않는 지금 동작 유지, 키보드 포커스 윤곽선
- 링크 항목은 누르면 닫힘, 북마크 2단 메뉴용 하위 메뉴 파트

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Select를 디자인 시스템 Select에 맞춤

- Figma 08 Select(5047:440): 트리거 48·모서리 8, 목록 fill/elevate·border·shadow·행 56+구분선, 선택 행 체크 16
- 기본 alignItemWithTrigger={false}·modal={false}(아래로 열리고 바깥 클릭이 전달되는 지금 동작)
- 오류는 Text Field 모양(aria-invalid 빨간 테두리, Field 라벨·FieldError), 포커스 윤곽선·hover 테두리

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Combobox를 디자인 시스템 Select Field에 맞추고 input-group 삭제

- Figma 07 Select Field(5030:666): 검색칸은 SearchBar 모양, 목록 fill/subtle·border·그림자 없음·행 56+구분선, 선택 행 체크 16
- 통화 선택처럼 트리거 → 팝업 안 검색 → 목록 패턴용 파트만 남김
- input-group은 SearchBar와 겹치고 쓰는 곳이 없어 지움

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Popover 표면을 Modal과 같게 맞춤

- Figma에 일반 팝오버가 없어 Modal 표면(fill/elevate·모서리 12·shadow/default), 여백·폭은 지금 헤더 미리보기 값(panel)
- 기간 칸 아래 달력용 bare 변형(기준 요소 폭), 기본 sideOffset 8

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): Tabs를 디자인 시스템 Tabs에 맞춤

- Figma 11 Tabs(5138:2632): Line은 탭마다 고정 2px 막대(border-subtle, 선택 primary), Chip Solid·Outline 변형, 좌우 padding 24
- shadcn 전용 변형(data-horizontal 등)을 data-[orientation]으로 바꿈

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(deps): shadcn add가 함께 설치한 cn 패키지 제거

- 받은 부품을 모두 @/lib/utils의 cn(타이포 토큰 등록)으로 바꿔 쓰는 곳이 없음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(conventions): Button·Base UI 팝업·대화상자 폼 원칙 반영

- 원칙 1-1: 범용 Button 보류 문구를 ui Button 사용법으로, 팝업 z-index는 모두 z-50 + 앱 isolate 묶음, 예시를 ui Dialog로
- 원칙 2-9 새로 씀: 대화상자 폼은 TanStack Form, 검증은 칸마다, 버튼 loading·closeDisabled는 mutation isPending 하나로
- 안티패턴 6: 손으로 만든 메뉴·셀렉트·탭과 useState 폼을 신호로, 쓸 부품 목록 갱신

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(ui): 반응형 대화상자와 본문 스크롤 DialogBody 추가

- ResponsiveDialog: PC는 Dialog, 모바일 기기(isMobileDevice)는 아래 시트(Drawer)로 같은 내용을 그린다
- DialogBody: 긴 폼에서 제목 줄·Footer는 고정하고 본문만 스크롤한다(시트 DrawerBody와 같은 방식)
- 원칙 1-1 부품 표에 ResponsiveDialog 추가

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(settings): 확인창 ConfirmDialog를 ui AlertDialog 위로 옮기기

- 확인창 모양 3종(default·ledger·alert)을 Figma Alert Dialog 하나로 통일하고 appearance prop 삭제
- 처리 중에는 확인 버튼에 progress를 보이고 Esc·딤·취소를 모두 막는다(문구 '처리 중…' 대신)
- 딤을 누르면 닫히는 지금 동작은 유지, 닫기 X는 Figma에 없어 뺐다
- 삭제·비우기 확인창(북마크 폴더·장소, 일차, 일정 장소, 메모)에 destructive를 줘 Critical 버튼으로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(expenses): 가계부 대화상자·선택기를 ui 부품과 TanStack Form으로

- 비용 추가·수정, 비용 목록, 정산 요약: showModal 대화상자 → ResponsiveDialog(PC Dialog, 모바일 기기 시트)
- 비용 추가·수정 폼을 TanStack Form으로 옮기고 칸마다 오류(빨간 테두리 + 아래 문구)를 보인다. 카테고리 Select도 같은 오류 모양
- validateExpense를 칸별 검증 함수로 나눔(기존 결과·문구는 그대로)
- 카테고리·일차·연결 장소는 ui Select, 통화는 ui Combobox(팝업 안 검색, 검색어 입력 뒤 Enter로 첫 결과 선택)
- 통화 목록이 없거나 목록 밖 통화여도 통화 칸에 폼 값을 보인다
- 비용 삭제 확인은 AlertDialog, 실패하면 포커스를 그 삭제 버튼으로 돌린다
- 정산 요약 대화상자는 시트 높이를 BelowNav로 고정하고 쓰지 않던 settlement 분기 삭제

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(expenses): 가계부 패널 버튼·메뉴·탭·예산 대화상자 교체

- 가계부 버튼을 ui Button으로(expenseButtonClass 삭제), 비용 더보기 메뉴는 DropdownMenu
- 정산 범위·분석 기준 탭은 Tabs(Chip Outline). 일차 필터 알약은 그대로
- 예산 대화상자: Dialog + TanStack Form. 입력 오류는 칸 아래, 저장 실패는 칸 밖 문장(지금 구분 유지), 저장 중 progress·닫기 막기
- 조회 다시 시도 버튼은 테스트가 disabled를 확인해 loading 대신 지금처럼 문구 + disabled

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(plan): PC 일정 대화상자·메뉴를 ui 부품으로

- 시간 설정: Dialog + TanStack Form, 적용 중 progress·닫기 막기
- 메모 덮어쓰기 확인은 ConfirmDialog, 메모 인라인 취소·저장은 Button sm
- 일차 메뉴는 DropdownMenu(포털 클릭이 일차 섹션으로 올라가지 않게 감쌈)
- 북마크에서 추가: ResponsiveDialog(PC Dialog, 모바일 기기 시트)
- 쓰지 않게 된 timeSaveButtonCompact 토큰 삭제

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(plan): 모바일 일정 시트를 ui Drawer로 옮기고 MobileBottomSheet 삭제

- 일차 관리·장소 메뉴·메모·시간 시트를 Base UI Drawer로(끌어서 닫기·포커스·스크롤 잠금은 Base UI 기본)
- 메모 시트는 TanStack Form, 2000자 넘으면 칸 오류. 저장·삭제는 누른 버튼에만 progress
- 시간 시트 저장 버튼은 Button loading(폼은 버튼·휠 구성이라 그대로)
- 장소 메뉴 '일정 추가'로 검색 화면을 열 때는 시트의 포커스 복귀를 꺼 검색창 포커스·키보드를 지킨다
- 메뉴 행은 MobileSheetMenuItem으로 옮기고, 쓰는 곳이 없어진 MobileBottomSheet와 테스트 삭제
- drawer.tsx 주석의 '지금 시트'를 '옛 시트'로 정리

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(place): 장소·북마크 대화상자·메뉴·탭을 ui 부품으로

- 북마크에 담기·일정에 추가: createPortal 오버레이 → Dialog, 처리 중 progress·닫기 막기. 일정 목록은 DialogBody로 포커스 윤곽이 잘리지 않게
- 북마크 생성·편집: Dialog + TanStack Form(제목 50자 검증·글자 수), 색 선택은 그대로
- 북마크 폴더·장소 메뉴는 DropdownMenu. 장소의 '카테고리 변경'은 하위 메뉴 대신 지금처럼 메뉴 안 단계 전환
- 장소 상세 홈·리뷰 탭은 ui Tabs(line)
- 쓰지 않게 된 DropdownMenuSub 파트와 place Tab 타입 삭제

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(map): 평점 필터·날짜 선택을 ui Select·Popover·Dialog로

- 지도 평점 필터: FilterDropdown → ui Select(트리거는 칩 모양, 고르면 선택 칩). 스크롤·창 크기 변경 때 닫힘은 유지
- 날짜 선택: 칸 아래 달력은 Popover, 가운데 팝업은 Base UI Dialog(대화상자 위에서 열면 Esc는 달력만 닫는다)
- 쓰지 않게 된 filter-dropdown·map-chip-button과 드롭다운 z 상수 삭제
- 다음 커밋(여행 정보 수정 대화상자 교체)과 함께 대화상자 안 날짜 팝업이 위에 뜬다

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(rooms): 여행방·홈 대화상자·메뉴를 ui 부품으로

- 초대하기·여행 정보 수정: SettingsDialog → Dialog. 적용하기는 Button(대화상자 규칙 Neutral Solid)
- 여행 정보 수정 폼 값은 TanStack Form으로 관리(칸 표시 props·저장 호출은 테스트가 확인해 유지)
- 기간 축소 확인은 폼의 자식 AlertDialog로 대화상자 위에 뜬다
- 목적지 입력의 Esc는 추천 목록이 열려 있을 때만 목록을 닫고, 닫혀 있으면 대화상자가 닫힌다
- 홈 프로필·방 카드 메뉴는 DropdownMenu(아이콘은 디자인 시스템 아이콘), 쓰지 않게 된 Figma svg 4개 삭제

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(layout): 헤더·구성원·설정의 메뉴·대화상자를 ui 부품으로

- 모바일 헤더 메뉴·구성원 옵션 메뉴는 DropdownMenu, 헤더 구성원 미리보기는 Popover(안의 구성원 메뉴를 눌러도 닫히지 않음)
- 구성원 안내·회원 탈퇴 2종은 AlertDialog(탈퇴는 Critical), 쿠키 설정은 Dialog(포커스 복귀 순서는 지금과 같음)
- 쿠키 저장·동의 배너·내 정보 버튼을 SettingsActionButton → ui Button
- 쿠키 동의 배너를 z-40으로 낮춰 Base UI 대화상자(z-50)가 배너를 덮게 한다

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(packing): 준비물 대화상자·메뉴·시트를 ui 부품으로

- 카테고리·준비물 이름 대화상자: Dialog + TanStack Form(이름 검증은 칸 오류), 메모도 TanStack Form
- 카테고리 메뉴는 DropdownMenu, 삭제 확인은 ConfirmDialog, 버튼은 ui Button
- 상세는 PC 오른쪽 패널 그대로, 모바일 기기는 Drawer. 시트 이름은 준비물 이름
- 동기화 오류처럼 저장할 수 없는 상태면 저장·추가 버튼을 비활성으로 보인다(다른 항목 저장 중에는 제출만 막음)
- 쓰지 않게 된 CSS 규칙과 테스트의 SettingsDialog mock 삭제

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(ui): 쓰지 않게 된 옛 대화상자·시트 부품 삭제

- SettingsDialog·SettingsActionButton·useSheetDrag·BottomSheetDragHandle 삭제(모든 화면이 ui 부품으로 옮김)
- 지운 SettingsDialog의 테스트 케이스 삭제
- ui 주석에서 옛 부품을 가리키는 표현 정리

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(analytics): 쿠키 설정 대화상자 폭을 sm(384)으로 줄이기

- lg(640) → sm(384), dialog size 주석의 사용처도 옮김

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(analytics): 쿠키 설정에 대화상자 취소·저장 버튼 줄 두기

- 개인정보 처리방침을 한 줄로 두고 그 아래 Footer(취소 Neutral Weak → 저장 Neutral Solid)
- 취소는 대화상자를 닫음(onCancel), 저장 결과 문장은 버튼 아래
- 테스트 렌더에 onCancel 추가(기대값 그대로)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(rooms): 초대하기 대화상자 폭을 md(448)로 넓히기

- sm(384) → md(448), dialog size 주석의 사용처도 옮김(회원 탈퇴는 AlertDialog로 바뀌어 뺌)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(settings): 회원 탈퇴 글자 버튼에 손가락 커서 추가

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(analytics): 쿠키 설정 대화상자 폭을 md(448)로 한 단계 넓히기

- sm(384)이 좁아 어색해서 md(448), dialog size 주석의 사용처도 옮김

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(ui): 확인창 본문 글자를 16px(body/md)로 고정

- Figma body/lg(18)가 제목에 비해 커 보여 body-m-regular(16/24)로, 제목 22는 그대로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(expenses): 예산 수정 대화상자 폭을 sm(384)으로 줄이기

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(expenses): 카테고리 필터 칸을 184로 넓혀 '전체 카테고리' 줄바꿈 막기

- 목록 폭이 트리거 폭(144·160)과 같아, 체크 자리 48과 스크롤 막대 15가 생기면 '전체 카테고리'가 두 줄이 됨
- 두 폭 구분 없이 184 하나로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(chat): PC 채팅 입력칸을 3줄 높이로 키우기

- 입력바 100 → 124(14px 글자 3줄), 모바일 기기는 100 그대로, 작은 패널 110 그대로
- 높이를 인라인 style에서 클래스(mobile: 변형)로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(expenses): PC 가계부 일차 필터가 넘치면 스크롤 막대로 옆으로 넘기게

- 스크롤 막대를 늘 숨겨 마우스로는 뒤쪽 일차(Day 20 등)에 닿을 수 없었음
- PC는 얇은 막대(북마크에서 추가 칩 줄과 같은 모양), 모바일 기기는 지금처럼 숨김

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(expenses): 모바일 비용 편집 통화 칸에서 코드 3글자가 잘리지 않게

- 통화 칸 96 → 112: ui Combobox 트리거의 여백·▼ 자리(66)를 빼면 코드 자리가 30이라 KRW·BHD가 말줄임됐음
- 16px에서 가장 넓은 코드(MWK 37)도 들어가는 46 확보

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(packing): 저장할 수 없는 상태에서 준비물 삭제 확인 버튼을 비활성으로

- ConfirmDialog에 confirmDisabled 추가(확인 버튼만 비활성, 취소·닫기는 그대로)
- 동기화 오류 등 ready가 아닐 때 '삭제'가 눌리는데 아무 반응이 없던 문제(옛 화면은 비활성)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(expenses): 정산 요약 대화상자의 조회 재시도 진행을 progress로

- 대화상자 안 버튼만 loading(progress·aria-busy), 패널의 재시도 버튼은 대화상자 밖이라 문구 그대로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(rooms): 여행 정보 수정 칸을 form.Field로 그리고 칸별 오류 표시

- 제목·목적지·기간을 form.Field + validators로, 오류는 칸 아래 FieldError(제목은 aria-invalid로 Text Field 오류 모양)
- 검증 함수는 trip-form.ts의 tripTitleError·tripDestinationsError·tripDateRangeError(나중에 zod 스키마 자리), 적용하기 잠금도 같은 함수
- RoomTripEditFields는 읽기 전용 칸만 남겨 RoomTripReadOnlyFields로, 목적지는 새 여행 만들기와 같은 DestinationChipsField 재사용
- 테스트는 실제 제목 칸·목적지·기간 onChange로 바꾸고 칸별 오류 테스트 추가

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(plan): 일정 시간·여행 정보 수정·방 메뉴·북마크 메뉴 테스트를 Base UI 화면 기준으로

- 포털된 대화상자·메뉴를 document에서 찾고, dialog[open] 대신 role="dialog"·menuitem
- Tab 순환·메뉴 좌표 단정은 Base UI 몫이라 빼고, 바깥 aria-hidden·포커스 복귀·메뉴 단계 전환으로 검사
- 저장 중 닫힘 막힘, 제목 칸 오류(aria-invalid·칸 아래 문장), 목적지 비움 오류, 기간 축소 확인 저장 테스트 추가

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(packing): 준비물·확인창 테스트를 Base UI 화면 기준으로

- react-test-renderer 대신 react-dom + document 조회, props 직접 호출 대신 실제 클릭·입력 이벤트
- 확인창: X 없음, 바깥 aria-hidden, 진행 중 접근성 이름 '처리 중'·aria-busy, 첫 포커스 '취소'
- 시트 끌기 수치·Tab 순환 테스트는 지우고, 저장 중 닫힘 막힘·모바일 메모 초안 유지·삭제 확인 confirmDisabled 테스트 추가

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(analytics): 쿠키 설정·모바일 헤더 메뉴·지도 평점 필터 테스트를 Base UI 화면 기준으로

- dialog[open]·aria-modal·inert 대신 role="dialog"·바깥 aria-hidden, 메뉴·Select 목록은 document에서 menuitem·option으로
- 옛 SettingsDialog 클래스 계약(거터·640 폭) 테스트는 지움
- 쿠키 설정 취소(저장 안 하고 닫힘)·저장 결과 문장이 버튼 아래 live 영역에 보이는지, 모바일 메뉴 항목으로 수정 대화상자 열기 테스트 추가

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(expenses): 가계부 패널·분석 탭·메뉴·예산 테스트를 Base UI 화면 기준으로

- react-test-renderer(Base UI 메뉴 트리거가 렌더 단계에서 실패) 대신 react-dom + document 조회, props 직접 호출 대신 실제 메뉴·Select·탭 조작
- 확인창 mock 대신 실제 alertdialog, 예산 칸 오류는 aria-invalid·aria-describedby FieldError로 검사
- 정산 요약 대화상자 안 재시도는 progress(aria-busy·이름 '조회 다시 시도 중'), 패널 재시도는 native disabled 그대로인지 테스트 추가

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(expenses): 비용 편집·카테고리 Select·통화 Combobox 테스트를 Base UI 화면 기준으로

- react-test-renderer props 호출 대신 react-dom + 실제 클릭·입력·키 이벤트, 가짜 native dialog 제거
- 오류는 칸별(aria-invalid·칸 아래 FieldError)로, 폼 아래 문장은 저장 실패·권한 같은 폼 전체 오류만
- 카테고리 칸 오류, 목록·통화 검색 Esc는 팝업만 닫힘, PC 딤은 안 닫고 모바일 시트 딤은 닫음, 빈 값 '장소 연결 없음' 표시 테스트 추가

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(expenses): 가계부 Provider·딤·비용 목록 테스트를 Base UI 화면 기준으로, 옛 끌기 테스트 삭제

- ExpenseDialog.drag.test 삭제: 옛 useSheetDrag 수치(translateY·80px) 검사라 끌기는 Base UI Drawer 몫
- ExpenseBackdrop.test를 새 동작으로 다시 씀: 모바일 시트는 딤으로 닫힘, PC는 딤으로 안 닫힘, 저장·삭제 중엔 딤·Esc·X·취소 모두 막힘
- ExpenseProvider·ExpenseScopePanel은 react-dom + document 조회, 삭제 중 확인 버튼 progress(aria-busy·이름 '삭제 중')

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(rooms): 여행 정보 수정 대화상자가 저장 중에 Esc·딤·X로 닫히지 않게

- 저장 요청(useUpdateRoom)을 대화상자가 만들어 폼에 내려 주고, 대화상자는 그 isPending으로 closeDisabled
- 저장 중 Esc·X로 닫히지 않는 테스트 추가

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(date-picker): 칸 아래 달력에서 Tab이 페이지 맨 앞으로 빠져나가지 않게 포커스 가두기

- Popover modal="trap-focus" + 보이지 않는 Popover.Close(Base UI는 닫기 부품이 있어야 가둠, Tab 순서 밖, 누르면 Esc처럼 취소)
- 달력이 body로 포털된 뒤 마지막 버튼에서 Tab을 누르면 로고 링크로 튀고 달력이 열린 채 남던 회귀

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(ui): Sonar 표시 분기와 라벨 접근성 경고 해소

* refactor(ui): 팝업 이벤트 경계와 Sonar 품질 경고 정리

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: minbros <realminbros@gmail.com>
Co-authored-by: Minhyung Kim <127458006+minbros@users.noreply.github.com>
- 일정 편집·정렬·일차 이동 시 명시한 시간값 보존
- 모바일 시간 선택기의 미완성 초안·초기화·취소와 휠 정착 동작 정합성 개선
- 최신 공통 Drawer와 캐시·경로 개선을 유지하며 충돌 해결
- 복사·공유 링크에 초대 UTM을 부착하고 기존 캠페인은 보존
- 로그인·가입·재동의 후 초대 복귀까지 같은 탭 문맥 유지
- 인증·동의·식별자 보호와 초대 메뉴 회귀 테스트 보강
* fix(places): 자동완성 요청을 취소하는 모든 경로에서 대기 타이머와 늦게 온 응답 버리기

- 입력창이 닫힌 뒤 대기 중인 타이머가 Google 자동완성을 한 번 더 보내던 것을 언마운트 때 취소(건별 과금)
- 응답을 가리는 번호를 타이머가 실행될 때만 올려서, 보낸 뒤에 닫거나(선택·Enter) 짧게 지우거나 다시 입력하면 늦게 온 추천이 목록을 다시 열었음
- 취소(타이머 취소 + 번호 올림)를 cancelPending 하나로 모아 다시 입력·닫기·언마운트가 모두 거치게 하고, 번호는 예약할 때 잡음(지도 검색창 cancel과 같은 방식)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(places): 모두검색을 자동완성 첫 후보 대신 지도 중심에서 검색

- 첫 후보 좌표를 중심으로 쓰면 이름만 같은 다른 나라 장소로 지도가 넘어감(도쿄 지도 '음식점' → 용인 '음식점')
- 모두검색은 첫 후보를 확정(resolve)하지 않음, 검색어에 지명이 있으면 Google이 위치 우선을 무시해 다른 도시 검색은 유지
- 쓰지 않게 된 검색 store의 fallback 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(places): 현 위치에서 검색은 화면 안 결과만 보여 주기

- Text Search 위치 우선은 영역 밖 결과도 돌려줘 재검색하면 이동 전 장소로 지도가 되돌아감
- 재검색 결과를 화면 경계로 거르고, 남는 게 없으면 결과 없음
- 재검색 위치 우선 반경을 5km 고정 대신 화면을 덮는 크기로 맞춤
- 지도 store에 화면 경계(bounds) 추가

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(map): 검색 결과로 지도가 움직인 뒤 '현 위치에서 검색' 버튼이 바로 뜨지 않게

- 기준점을 검색 요청 때 화면 대신 결과로 옮긴 지도가 멈춘 화면으로 다시 잡음(맞춤 확대·결과 1개 확대·핀/카드 선택 이동)
- 멈추기 전에 사용자가 지도를 누르거나 휠을 굴리면 취소해 사용자의 이동은 기준점으로 삼지 않음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(map): 검색 실패 안내를 '검색에 실패했어요.'와 다시 시도 아이콘으로

- 모두검색이 실패하면 오류 원문("Failed to fetch") 대신 '검색에 실패했어요.'를 보여 줌
- 글자 링크 '다시 시도'를 오른쪽 끝 다시 시도 아이콘 버튼(누르는 영역 44px)으로 바꿈
- 후보 클릭 실패 문구는 이번 범위에서 제외(그대로)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(places): 자동완성 실패 안내에 오류 원문 대신 기본 안내 보여 주기

- 추천을 못 받으면 '모든 결과보기' 아래 안내에 "Failed to fetch" 같은 원문이 나오던 것을 기본 안내("검색 추천을 불러오지 못했어요. 다시 입력해주세요.")로 바꿈
- 서버가 준 안내(429 "잠시 후 다시 검색해주세요." 등)는 그대로 보여 줌

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(map): 검색창에 다시 들어가면 결과 없음·실패 안내를 숨기기

- 안내 상자와 추천 목록이 둘 다 검색창 바로 아래라, 다시 검색하려고 누르면 안내가 추천 목록을 덮음
- 검색창 입력에 포커스가 있는 동안 안내 상자를 숨김(CSS group-has, 상태 추가 없음)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(map): '현 위치에서 검색'의 실패·결과 없음은 그 버튼 자리에서 알리기

- 실패하면 하단 버튼이 '다시 시도'로 남고, 누르면 같은 화면을 다시 찾음(보이던 결과는 그대로)
- 결과가 0개면 버튼 자리에 '검색 결과가 없어요'(X로 닫기)를 띄우고 검색창 아래에는 띄우지 않음
- 지도를 옮기면 새 화면 검색이라 다시 '현 위치에서 검색'
- 검색 store에 검색을 시작한 곳(searchMode)을 두고 안내 위치를 계산

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(places): '현 위치에서 검색'은 Google에 지금 화면 사각형 안만 찾도록 요청

- 위치 우선(locationBias)으로 찾은 뒤 프론트에서 화면 밖 결과를 거르던 방식을 새 API GET /places/search/area(locationRestriction)로 바꿈
- Google이 화면 안에서 결과를 채워 주므로 버려지는 결과가 없고, 프론트 필터는 지움
- 백엔드 feature/place-search-restriction이 먼저 배포돼야 함(없으면 재검색이 실패해 버튼이 '다시 시도'로 남음)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(map): '검색 결과가 없어요' 안내를 '현 위치에서 검색' 버튼과 같은 모양으로

- 회색 테두리 Chip 대신 같은 자리의 '현 위치에서 검색' 알약(파란 3px 테두리·흰 바탕·같은 글자)을 그대로 씀
- 알약 클래스를 SearchHereFloatingButton에서 함께 쓰도록 내보냄, 닫기 X는 버튼 아이콘과 같은 파란색

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(map): 카테고리 '현 위치에서 검색'이 실패하면 버튼이 '다시 시도'로 남게

- 카테고리 칩 검색(Places JS SDK)이 오류로 실패하면 핀만 비우고 아무 안내가 없었음(결과 0개는 그대로 안내 없음)
- MapDiscoverPlaces가 실패를 알리고 TripMap이 useState로 들어 버튼에 넘김, 누르면 같은 화면을 다시 찾음
- 실패 뒤 지도를 옮기면 새 화면 검색이라 다시 '현 위치에서 검색'

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- 싸이버거 이벤트 문구·기간·배지·버거 사진을 빼고 참여 유도 문구와 리뷰 3D 아이콘으로 교체
- 피드백 폼 링크를 이벤트 문항을 뺀 새 폼으로 변경(배너·사이드바·헤더 공통)
- 쓰지 않는 이벤트 배너 이미지(burger·desktop·mobile) 삭제

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(schedule): 늦게 온 일차 장소 목록 응답이 최신 목록을 덮지 않도록 수정

- 일차 목록 queryFn이 캐시만 읽던 것을 서버(GET …/items)에서 받도록 변경. 지도 등 같은 키를 읽는 곳도 같은 조회 옵션을 써서 무효화하면 항상 서버에서 받음
- STOMP 이벤트는 목록을 직접 받아 쓰지 않고 무효화. 같은 일차를 받는 중이면 TanStack이 옛 조회를 취소하고 그 응답을 버림
- 일정 전체 응답은 요청을 보낸 뒤 바뀐 일차 목록을 덮지 않음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schedule): 본인 일정 변경은 응답을 반영한 뒤 그 일차를 다시 받도록 수정

- 장소 추가·시간·메모·이동수단·삭제·순서 변경은 응답을 setQueryData로 반영하고 onSettled에서 그 일차를 무효화
- 장소 추가는 mutationFn에서 요청만 하고 캐시 반영은 onSuccess로 옮김
- 본인 시간·메모 변경의 STOMP 반향(UPDATED)은 진행 중인 조회에 합류하고, 다른 사람 변경이 섞이면 새로 받음
- 손으로 넣었던 분기 삭제: 쓸 때마다 일정 전체 재조회, 못 찾으면 무효화, 실패 시 직접 GET

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(schedule): 저장 중 표시를 복사한 상태 대신 mutation 요청 값으로 계산

- 이동수단 선택: 서버 값을 useState로 복사·되돌리던 것을, 저장 중에는 고른 수단(variables), 끝나면 서버 값을 보여 주도록 변경
- 모바일 편집 목록의 순서 변경·일차 이동: pending useState 대신 variables·isPending으로 새 자리를 그림
- 순서 변경의 연쇄 PATCH 대기 useState 삭제. onSuccess가 끝날 때까지 isPending이 이어짐

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(schedule): 일차 장소 목록 조회 순서와 본인 변경 뒤 재조회 테스트

- 늦게 온 옛 조회 응답, 다시 받는 중 본인 삭제, 일정 전체 응답이 더 새 일차 목록을 덮는지 확인
- 바뀐 함수 모양(공용 조회 옵션, 장소 추가 응답, 모바일 드래그 표시)에 맞춰 기존 테스트 수정

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(conventions): 데이터 조회·갱신 원칙을 서버 상태 기준으로 수정

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(schedule): Sonar 분기와 순차 조회 경고 해소

* fix(route): 경로 캐시 key에 도착 장소를 넣어 순서를 되돌리면 받아 둔 경로를 쓰도록 수정

- 경로 key를 (방, 일차, 출발 항목, 도착 장소, 이동수단)으로 바꾼다 — 목록·지도·batch 시딩이 다음 장소의 googlePlaceId를 넘긴다
- 순서가 바뀐 구간은 key가 달라져 옛 도착지의 거리·선을 보여 주지 않는다
- 도착 장소가 없는 구간은 조회하지 않는다

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(route): 도착 장소가 key에 들어가 필요 없어진 경로 무효화 제거

- 순서 변경·장소 추가·삭제·일차 간 이동·이동수단 변경(본인·STOMP)과 일정 전체 재조회 뒤의 구간 경로 무효화를 지운다 — 바뀐 구간은 새 key로 받는다
- 구간 계산·무효화 모듈(scheduleStompRouteScope.ts)과 STOMP 경로 무효화 디바운스 삭제
- 장소 삭제는 응답으로 일차 목록에서 그 장소만 빼고, 일차 삭제 뒤의 경로 무효화도 지운다
- 지도 에폭 증가 호출 삭제 (지도 key 정리는 다음 커밋)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(map): 경로 무효화가 없어져 지도 선 key의 에폭 제거

- 지도 선 key는 (방, 일차, 출발 항목, 출발·도착 장소, 이동수단)만 쓴다 — 도착 장소가 바뀌면 key가 달라져 새 선을 그린다
- 에폭 스토어(plan-map-directions-epoch-store) 삭제
- 선의 React key도 에폭 대신 도착 장소로 구분한다

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(route): 진입 batch·일정 목록·지도가 같은 경로 캐시를 쓰는지 확인

- batch가 받아 둔 구간 경로를 목록 칸(다음 장소 googlePlaceId)과 지도(지도 구간 계산)가 단건 요청 없이 쓰는지 본다

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(route): 단건 경로 조회에 도착 항목을 보내고 경로 key의 도착 값을 항목 ID로 변경

- 서버가 그 순간의 순서로 다음 항목을 고르던 단건 GET에 destinationItemId를 보낸다 — 요청한 구간과 응답 구간이 항상 같다
- 경로 key·지도 선 key·sessionStorage key의 도착 값을 다음 장소의 itemId로 바꾼다
- 서버에 이 파라미터가 배포되기 전에는 서버가 무시하고 지금처럼 다음 항목으로 계산한다

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(route): batch 경로를 응답의 도착 항목으로 저장해 기다리는 사이 순서가 바뀌어도 버리지 않도록 수정

- batch 응답의 toItemId(서버가 계산한 구간의 도착 항목)로 경로 key를 만든다 — 요청 때 목록에서 짐작하지 않는다
- 응답을 기다리는 사이 순서가 바뀌면 결과를 통째로 버리던 일정 지문 비교를 지운다
- 마지막 항목·항목 없음 답은 도착 항목이 없어 저장하지 않고, 그 칸이 도착 항목을 지정한 단건 조회로 받는다

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(route): 경로 key 설명 주석의 도착 장소를 도착 항목으로 정리

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(api): 변경 요청에 탭 ID 헤더(X-Client-Tab-Id) 싣기

- 탭마다 한 번 만드는 getTabId 추가(모듈 변수, randomUUID 없으면 getRandomValues로 v4)
- 이미 preflight를 하는 PATCH·PUT·DELETE·JSON POST에만 헤더를 붙임
- 백엔드가 이 값을 STOMP payload originTabId로 돌려줘 이 탭의 반향을 가려낼 수 있게 함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(stomp): 같은 계정 다른 탭에서 바꾼 일정을 탭 ID로 구분해 반영

- 이벤트 originTabId가 이 탭 ID와 같을 때만 반향으로 보고 건너뜀(isOwnEcho)
- originTabId가 없는 옛 이벤트는 지금처럼 actorUserId로 판정해 배포 순서와 무관
- SCHEDULE_ITEM_UPDATED: 이 탭 반향은 건너뛰고, 다른 탭 변경은 진행 중인 조회에 합류하지 않고 다시 받음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(stomp): 같은 계정 다른 탭에서 바꾼 북마크를 탭 ID로 구분해 반영

- bookmarks 이벤트도 isOwnEcho로 판정(originTabId 같을 때만 건너뜀, 없으면 actorUserId)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(stomp): 탭 ID가 없는 일정·북마크 이벤트도 다른 탭의 변경으로 받기

- isOwnEcho는 originTabId가 이 탭 ID와 같을 때만 반향으로 판정(계정 판정 제거)
- 탭 ID를 못 만든 탭의 변경도 같은 계정의 다른 탭이 놓치지 않게 함
- SCHEDULE_ITEM_UPDATED의 본인 반향 합류(joinInFlight) 분기 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(plan): 쓰는 곳이 없어진 일차 무효화의 joinInFlight 옵션 제거

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(stomp): 일정 이벤트 반향 판정을 switch 앞 한 곳으로 모으기

- case마다 반복하던 isOwnEcho를 한 번만 둬 새 이벤트 종류에서 빠뜨리지 않게 함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(stomp): 다른 멤버가 흉내 낸 탭 ID로 이 탭의 갱신이 막히지 않게 하기

- 반향은 originTabId가 이 탭이고 actorUserId도 나일 때만으로 판정
- 탭 ID는 방 멤버 모두에게 보이므로 서버가 인증으로 정한 actorUserId를 함께 봄

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: minbros <realminbros@gmail.com>
Co-authored-by: Minhyung Kim <127458006+minbros@users.noreply.github.com>
@vercel

vercel Bot commented Nov 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
frontend-server Ready Ready Preview Nov 8, 2026 12:25pm UTC

Request Review

@sonarqubecloud

sonarqubecloud Bot commented Nov 8, 2026

Copy link
Copy Markdown

@minbros
minbros merged commit 548e1c6 into main Nov 8, 2026
4 checks passed

This branch was successfully deployed

1 active deployment
Preview — a0ff1887 Deployed Nov 8, 2026 by vercel[bot]
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.

3 participants