From ba921c75eb6418fe3dfb9a10ea13dd4df3e4971d Mon Sep 17 00:00:00 2001 From: Ryu Tae Hyun Date: Sun, 13 Sep 2026 00:13:36 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix(profile):=20=EC=9D=B4=EC=9A=A9=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=EA=B3=BC=20=ED=81=90=EC=9E=89=20=ED=9A=9F?= =?UTF-8?q?=EC=88=98=EC=9D=98=20=ED=96=89=20=EB=86=92=EC=9D=B4=EB=A5=BC=20?= =?UTF-8?q?=EB=A7=9E=EC=B6=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/profile/ui/UserProfileContent.module.css | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/features/user/profile/ui/UserProfileContent.module.css b/src/features/user/profile/ui/UserProfileContent.module.css index 284fa22..71f944c 100644 --- a/src/features/user/profile/ui/UserProfileContent.module.css +++ b/src/features/user/profile/ui/UserProfileContent.module.css @@ -91,14 +91,17 @@ display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-rows: repeat(3, auto); + row-gap: var(--profile-stat-row-gap); column-gap: 18px; margin-top: 24px; } .statsColumn { - display: flex; + display: grid; + grid-row: 1 / -1; + grid-template-rows: subgrid; min-width: 0; - flex-direction: column; - gap: var(--profile-stat-row-gap); + align-items: start; } .card { min-width: 0; @@ -129,9 +132,6 @@ .favoriteSongValueSlot { min-height: 1lh; } -.favoriteSongValueSlot[data-line-clamp="2"] { - min-height: 2lh; -} .musicPowerRow { min-width: 0; } From a4ccfcc2a0f0ea613f78a6414fba96f09a7e8dd8 Mon Sep 17 00:00:00 2001 From: Ryu Tae Hyun Date: Sun, 13 Sep 2026 00:13:49 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix(playlist):=20=ED=95=9C=EA=B8=80=20?= =?UTF-8?q?=EC=A1=B0=ED=95=A9=20=EC=A2=85=EB=A3=8C=20=EC=97=86=EC=9D=B4=20?= =?UTF-8?q?=EC=A0=9C=EB=AA=A9=EC=9D=84=20=EC=9E=90=EB=8F=99=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/queuing-ui-flow/SKILL.md | 1 + .../2026-09-13-track-search-ime-wait.md | 71 +++++++++++++++++++ .../delivery-state.md | 12 ++++ .../2026-09-13-profile-search-fix/plan.md | 11 +++ .../qa-report.md | 27 +++++++ docs/exec-plans/active/README.md | 2 + .../add-track/ui/TrackSearchInput.test.tsx | 42 ++++++++++- .../add-track/ui/TrackSearchInput.tsx | 5 +- .../playlist/model/useTrackSuggestions.ts | 13 ++-- 9 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 docs/agent-harness/incidents/2026-09-13-track-search-ime-wait.md create mode 100644 docs/exec-plans/active/2026-09-13-profile-search-fix/delivery-state.md create mode 100644 docs/exec-plans/active/2026-09-13-profile-search-fix/plan.md create mode 100644 docs/exec-plans/active/2026-09-13-profile-search-fix/qa-report.md diff --git a/.agents/skills/queuing-ui-flow/SKILL.md b/.agents/skills/queuing-ui-flow/SKILL.md index b778385..1579fe8 100644 --- a/.agents/skills/queuing-ui-flow/SKILL.md +++ b/.agents/skills/queuing-ui-flow/SKILL.md @@ -45,6 +45,7 @@ Do not use it for API-only changes unless the UI behavior also changes. - Room list/card callbacks should not create per-item inline closures in parent `map` calls when the card can own the click context; pass the room/item and a stable handler into the card and bind inside the card. - Wheel navigation that must call `preventDefault()` must use a DOM ref plus native `addEventListener("wheel", handler, { passive: false })`; do not rely on JSX `onWheel` for scroll cancellation. - Text inputs or textareas that submit on Enter must ignore Enter during IME composition using `nativeEvent.isComposing`, `keyCode === 229`, or a composition ref before calling submit. +- Debounced live search must not wait indefinitely for `compositionend`: debounce the visible input value and reserve composition guards for keyboard submission/selection. Test a composing input that stops changing without Enter or blur and verify results arrive without repeated idle requests. - Queue reordering lets owners move every pending entry, and the personal queue lets users move every one of their pending entries. `ownerOrdered` is display-only server state and must not disable drag or remove an entry from personal reorder payloads. - For virtualized dnd-kit sortable lists, prefer dragging the original row. Add a `DragOverlay` only with a repeated pointer-drop check that proves every original row remains visible and has no retained opacity, transform, or animation; keep drag transforms out of the card's generic CSS transition. - Room reconnect UI must return to an explicit joining state, repeat the backend join handshake with the saved room access token, then restore token-authenticated chat/room subscriptions and server reads. Topic resubscription alone is not a joined room session, and a password change must not interrupt an already joined token holder. diff --git a/docs/agent-harness/incidents/2026-09-13-track-search-ime-wait.md b/docs/agent-harness/incidents/2026-09-13-track-search-ime-wait.md new file mode 100644 index 0000000..4ea8777 --- /dev/null +++ b/docs/agent-harness/incidents/2026-09-13-track-search-ime-wait.md @@ -0,0 +1,71 @@ +# 한글 조합 종료를 기다리던 노래 제목 자동 검색 + +## Problem + +사용자는 제목 검색이 엔터 없이 계속 검색 중으로 남고 엔터 후 약 0.5초 내 끝난다고 보고했다. + +## Previous Behavior + +TrackSearchInput은 compositionstart/end를 state와 ref로 보관했다. useTrackSuggestions는 그 state가 참이면 검색어를 빈 문자열로 디바운스하고 요청을 비활성화하면서 loading은 참으로 반환했다. + +## Previous Code + +```ts +const debouncedQuery = useDebouncedValue(composing ? "" : input.query, 300); +const enabled = Boolean(userSlug) && open && !composing; +// loading: composing || debouncePending || query.isFetching +``` + +## Updated Code + +```ts +const debouncedQuery = useDebouncedValue(input.query, 300); +const enabled = Boolean(userSlug) && open; +// compositionRef/nativeEvent.isComposing/keyCode 229 still guard keyboard selection. +``` + +## Problem in the Previous Code + +입력값이 더는 변하지 않아도 마지막 한글이 조합 중이면 compositionend가 오지 않을 수 있다. 자동 조회를 조합 종료 이벤트에 종속시키면 실제 요청 없이 검색 중 UI가 무기한 유지된다. Enter 선택 방어와 읽기 전용 자동 조회에 같은 차단 조건을 사용한 것이 문제다. + +## Evidence + +- compositionStart → 한국어 change → 400ms 경과 시 이전 코드는 새 검색을 호출하지 않는다. +- 50ms 간격 40회 change 후 300ms 유휴 시 이전 코드는 검색 0회, 수정 후 1회. +- 이전 코드에서 회귀 2건 실패를 확인한 뒤 수정했다. +- 수정 후 compositionend/Enter 없이 결과가 표시되고 60초 유휴에도 총 검색 1회다. +- 활성 결과가 있어도 조합 중 Enter(native flag 및 composition ref)는 URL 선택을 하지 않는다. +- 기존 URL 차단, 역순 응답 무시·취소, 계정 분리, quota 실패 테스트를 유지한다. +- 네트워크는 API 함수 mock을 사용했다. 실제 사용자 OS IME와 운영 API 응답 시간은 관측하지 않았다. + +## Cause or Remaining Hypotheses + +조합 상태에서 요청을 차단하고 무기한 loading을 표시하는 프런트 경로는 재현으로 확인했다. 사용자 환경의 실제 이벤트 순서와 엔터 후 약 0.5초의 서버 응답 시간은 미확인이다. 서버가 느리다고 단정하지 않는다. + +## Solution Options + +- compositionend만 기다리기: 현재 증상을 유지하므로 제외. +- 조합 ref를 타이머로 강제 해제하기: 조합 중 Enter의 의도치 않은 선택 위험 때문에 제외. +- 입력값 디바운스와 키보드 조합 방어 분리: 선택. + +## Chosen Solution and Rationale + +자동 조회는 300ms 값 안정화로 실행하고 조합 ref는 키보드 동작에만 사용한다. 기존 cache/AbortSignal/오류 및 URL 분류를 유지하는 작은 변경이다. 조합 도중 300ms 이상 쉬면 미완성 검색어로도 요청될 수 있다는 절충은 있으며 연속 입력은 한 요청으로 합친다. + +## Result + +조합 종료/엔터 없이도 마지막 입력의 결과를 받을 수 있다. 고정된 네트워크 완료 시간을 보장하는 변경은 아니다. + +## Reusable Rule + +라이브 검색은 입력 정지에 반응하고, IME 조합 상태는 키보드 제출/선택 방어에 사용한다. 테스트에서 compositionend를 생략해 무기한 대기를 검증한다. + +## Skill or Team Spec Updates + +- `.agents/skills/queuing-ui-flow/SKILL.md`에 조합 종료 없는 자동 검색과 유휴 요청 상한 검증 규칙을 추가했다. + +## Verification + +- targeted: 2 files / 9 tests passed. +- 전체 lint/test/build 및 독립 QA 결과는 `docs/exec-plans/active/2026-09-13-profile-search-fix/qa-report.md`에 기록한다. +- Browser runtime bootstrap과 discovery 결과 연결 가능한 브라우저 없음. 실제 화면·OS IME 검증은 미실행. diff --git a/docs/exec-plans/active/2026-09-13-profile-search-fix/delivery-state.md b/docs/exec-plans/active/2026-09-13-profile-search-fix/delivery-state.md new file mode 100644 index 0000000..b72167a --- /dev/null +++ b/docs/exec-plans/active/2026-09-13-profile-search-fix/delivery-state.md @@ -0,0 +1,12 @@ +# Delivery State + +- status: publishing +- branch: dev +- base: main +- issue: none +- pr: pending +- selected_skills: plan.md 참조 +- local_qa: pass — lint / 714 tests / build / 독립 리뷰 +- ci: pending +- review_threads: none +- next_action: 기능별 커밋 및 dev push / Draft PR 생성 diff --git a/docs/exec-plans/active/2026-09-13-profile-search-fix/plan.md b/docs/exec-plans/active/2026-09-13-profile-search-fix/plan.md new file mode 100644 index 0000000..8092355 --- /dev/null +++ b/docs/exec-plans/active/2026-09-13-profile-search-fix/plan.md @@ -0,0 +1,11 @@ +# 프로필 통계 정렬과 한글 제목 검색 대기 수정 + +- 요청: 이용 시간을 큐잉 횟수 높이에 맞추고, 엔터 없이는 노래 검색이 완료되지 않는 원인을 확인한다. +- branch: dev / base: main +- selected_skills: queuing-feature-delivery, queuing-orchestrator, queuing-ui-flow, queuing-api-boundary, frontend-architecture-guardrails, queuing-qa-reviewer, queuing-incident-curator, browser:control-in-app-browser +- 소유권: 공유 프로필 CSS가 통계 배치, playlist query hook이 검색 디바운스, 입력 컴포넌트가 IME 키보드 선택 방어를 소유한다. API 계약과 mutation은 변경하지 않는다. +- 수용 기준: 짧은 최애곡에서 이용 시간이 기존 큐잉 횟수 위치와 정렬되고 긴 문구에도 양쪽 통계가 같은 행에 있다. 조합 종료/엔터 없이 300ms 입력 정지 후 검색한다. 조합 중 Enter는 선택하지 않는다. 취소/오래된 응답/URL/에러 동작을 유지한다. +- 커밋 순서: 1. fix(profile): 프로필 통계의 행 높이를 맞춤 2. fix(playlist): 한글 조합 종료를 기다리던 자동 검색 수정 (회귀 테스트와 실행·사고 기록 포함). +- 검증: 검색 재현 테스트를 먼저 실행해 기존 실패 확인 → 수정 후 targeted test, lint, 전체 test, build → 독립 read-only QA → commit/push/draft PR. +- 진행: 구현 완료. 수정 전 회귀 실패 확인, targeted 9 tests / 전체 714 tests / lint / build / 독립 QA pass. Draft PR 게시 진행. +- 잔여 위험: 운영 API 응답 시간 및 실제 OS IME 이벤트는 별도 관측하지 않음. diff --git a/docs/exec-plans/active/2026-09-13-profile-search-fix/qa-report.md b/docs/exec-plans/active/2026-09-13-profile-search-fix/qa-report.md new file mode 100644 index 0000000..5288fe2 --- /dev/null +++ b/docs/exec-plans/active/2026-09-13-profile-search-fix/qa-report.md @@ -0,0 +1,27 @@ +# QA Report + +- 독립 reviewer: qa_profile_search (read-only) +- 코드 판정: pass — 차단 이슈 없음. +- 원 요청: 이용 시간을 큐잉 횟수 높이에 정렬하고 엔터 없이 지속되는 제목 검색의 원인을 파악. + +## 경계 검토 + +- 프로필: 두 statsColumn이 동일한 3행 subgrid를 공유하며 최애곡의 강제 2lh 공간을 제거했다. 짧은 문구의 이용 시간은 기존 큐잉 횟수 높이로 올라간다. 긴 문구는 양쪽 둘째 행을 함께 밀어 겹침과 불일치를 막는다. +- 검색: 조합 종료와 자동 검색을 분리하고 입력값 300ms 디바운스를 유지했다. 조합 ref / native isComposing / keyCode 229 키보드 선택 방어는 유지한다. +- API/캐시: endpoint, query key, 계정 분리, cache staleTime, AbortSignal, URL 분류, 오류 처리는 변경하지 않았다. mutation 영향 없음. +- 회귀: 조합 종료 없이 결과 표시, 40회 입력 버스트 이후 1회 요청, 60초 유휴 재요청 없음. + +## 실행 근거 + +- 수정 전 회귀 2건 실패: 조합 중 새 검색이 호출되지 않음. +- targeted: 2 files / 9 tests passed. +- npm run lint: pass, 경고 없음. +- npm run test: 160 files / 714 tests passed. +- npm run build: 첫 sandbox 실행은 컴파일 시작 이후 진행 출력이 없어 중단, 제한 밖 동일 명령 재실행 pass. +- git diff --check: pass. + +## 잔여 제한 + +- Browser 초기화 후 discovery가 []라 실제 화면 좌표 및 OS IME는 미검증. +- 운영 API 응답 시간은 미관측. 300ms는 검색 시작 디바운스이며 완료 시간 보장이 아니다. +- 기존 취소 테스트는 교체 중 abort를 검증하지만 닫기 중 in-flight abort를 직접 assert하지 않는다. 이번 변경에서 취소 경로는 유지됨. diff --git a/docs/exec-plans/active/README.md b/docs/exec-plans/active/README.md index c3a183d..6b8dee5 100644 --- a/docs/exec-plans/active/README.md +++ b/docs/exec-plans/active/README.md @@ -1,5 +1,7 @@ # Active Execution Plans +- [2026-09-13-profile-search-fix](./2026-09-13-profile-search-fix/plan.md): implementing — 프로필 이용 시간 정렬 및 한글 조합 중 자동 검색 대기 수정 + - [2026-09-08-track-search-realtime](./2026-09-08-track-search-realtime/plan.md): ci-pending — Draft PR #62, 노래 검색·친구 UI·큐 중복·실시간 닉네임 통합 개선, 713 tests/lint/build/독립 QA 통과 - [2026-09-05-room-queue-modal-polish](./2026-09-05-room-queue-modal-polish/plan.md): ci-pending — Draft PR #61, 내 노래 현재곡 중복·신청 폼 간격·방 modal dim/높이·Friends 상세 padding 교정 및 로컬 QA 통과 diff --git a/src/features/playlist/add-track/ui/TrackSearchInput.test.tsx b/src/features/playlist/add-track/ui/TrackSearchInput.test.tsx index 8dff972..72245ce 100644 --- a/src/features/playlist/add-track/ui/TrackSearchInput.test.tsx +++ b/src/features/playlist/add-track/ui/TrackSearchInput.test.tsx @@ -103,7 +103,7 @@ describe("TrackSearchInput", () => { ).toHaveAttribute("aria-selected", "true"); client.clear(); }); - it("300ms 디바운스, 조합 중 요청·Enter 차단, 잘못된 URL 제목 검색 차단", async () => { + it("300ms 디바운스와 조합 중 자동 검색, Enter 선택·잘못된 URL 검색 차단", async () => { vi.useFakeTimers(); const { input, client } = setup(); fireEvent.change(input, { target: { value: "아이유" } }); @@ -123,9 +123,20 @@ describe("TrackSearchInput", () => { await act(async () => { await vi.advanceTimersByTimeAsync(400); }); - expect(searchYouTubeVideos).toHaveBeenCalledTimes(1); + expect(searchYouTubeVideos).toHaveBeenCalledTimes(2); + expect(searchYouTubeVideos).toHaveBeenLastCalledWith( + "아이유 좋은", + expect.any(AbortSignal), + ); + await act(async () => { + await vi.advanceTimersByTimeAsync(1); + }); + expect(screen.queryByRole("status")).not.toBeInTheDocument(); + fireEvent.mouseEnter(screen.getByRole("option")); fireEvent.keyDown(input, { key: "Enter", isComposing: true }); expect(input).toHaveValue("아이유 좋은"); + fireEvent.keyDown(input, { key: "Enter" }); + expect(input).toHaveValue("아이유 좋은"); fireEvent.compositionEnd(input); await act(async () => { await vi.advanceTimersByTimeAsync(300); @@ -141,6 +152,33 @@ describe("TrackSearchInput", () => { expect(searchYouTubeVideos).toHaveBeenCalledTimes(2); client.clear(); }); + it("조합 종료 없이 긴 입력 버스트를 검색 한 번으로 합치고 유휴 중 재요청하지 않는다", async () => { + vi.useFakeTimers(); + const { input, client } = setup(); + fireEvent.compositionStart(input); + for (let index = 1; index <= 40; index += 1) { + fireEvent.change(input, { target: { value: "가".repeat(index) } }); + await act(async () => { + await vi.advanceTimersByTimeAsync(50); + }); + } + expect(searchYouTubeVideos).not.toHaveBeenCalled(); + await act(async () => { + await vi.advanceTimersByTimeAsync(250); + }); + expect(searchYouTubeVideos).toHaveBeenCalledTimes(1); + expect(searchYouTubeVideos).toHaveBeenLastCalledWith( + "가".repeat(40), + expect.any(AbortSignal), + ); + await act(async () => { + await vi.advanceTimersByTimeAsync(60_000); + }); + expect(searchYouTubeVideos).toHaveBeenCalledTimes(1); + expect(screen.getAllByRole("option")).toHaveLength(1); + expect(screen.queryByRole("status")).not.toBeInTheDocument(); + client.clear(); + }); it("검색 교체·닫기 시 취소하고 역순 응답을 새 결과로 표시하지 않는다", async () => { let finishOld!: (value: (typeof video)[]) => void; let oldSignal!: AbortSignal; diff --git a/src/features/playlist/add-track/ui/TrackSearchInput.tsx b/src/features/playlist/add-track/ui/TrackSearchInput.tsx index 5865f2a..72cd9f0 100644 --- a/src/features/playlist/add-track/ui/TrackSearchInput.tsx +++ b/src/features/playlist/add-track/ui/TrackSearchInput.tsx @@ -28,7 +28,6 @@ export default function TrackSearchInput({ const inputRef = useRef(null); const listRef = useRef(null); const [open, setOpen] = useState(false); - const [composing, setComposing] = useState(false); const compositionRef = useRef(false); const [active, setActive] = useState(-1); const [position, setPosition] = useState({ @@ -37,7 +36,7 @@ export default function TrackSearchInput({ width: 0, maxHeight: 0, }); - const suggestions = useTrackSuggestions(value, open && !disabled, composing); + const suggestions = useTrackSuggestions(value, open && !disabled); const visible = open && !disabled && @@ -161,12 +160,10 @@ export default function TrackSearchInput({ onBlur={() => setOpen(false)} onCompositionStart={() => { compositionRef.current = true; - setComposing(true); setActive(-1); }} onCompositionEnd={() => { compositionRef.current = false; - setComposing(false); }} onKeyDown={(event) => { if ( diff --git a/src/features/playlist/model/useTrackSuggestions.ts b/src/features/playlist/model/useTrackSuggestions.ts index 9401b5c..5cf10bc 100644 --- a/src/features/playlist/model/useTrackSuggestions.ts +++ b/src/features/playlist/model/useTrackSuggestions.ts @@ -11,17 +11,15 @@ import { import { classifyTrackInput } from "./trackSuggestions"; import { trackSuggestionKeys } from "./trackSuggestionKeys"; -export function useTrackSuggestions( - value: string, - open: boolean, - composing: boolean, -) { +export function useTrackSuggestions(value: string, open: boolean) { const queryClient = useQueryClient(); const me = useMe(); const userSlug = !me.isError ? me.data?.slug ?? null : null; const input = classifyTrackInput(value); - const debouncedQuery = useDebouncedValue(composing ? "" : input.query, 300); - const enabled = Boolean(userSlug) && open && !composing; + // IME can keep the last syllable composing after typing stops. + // Debounce text changes; composition only guards keyboard selection in the UI. + const debouncedQuery = useDebouncedValue(input.query, 300); + const enabled = Boolean(userSlug) && open; const frequent = useQuery({ queryKey: trackSuggestionKeys.frequent(userSlug), queryFn: ({ signal }) => fetchFrequentTracks(signal), @@ -62,7 +60,6 @@ export function useTrackSuggestions( ? query.data ?? [] : [], loading: - composing || (input.kind === "search" && input.query !== debouncedQuery) || query.isFetching, error: query.error, From 454c88fc64cca48f09f87c789a543959d3004c63 Mon Sep 17 00:00:00 2001 From: Ryu Tae Hyun Date: Sun, 13 Sep 2026 00:14:29 +0900 Subject: [PATCH 3/3] =?UTF-8?q?docs(delivery):=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=EA=B3=BC=20=EA=B2=80=EC=83=89=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?PR=2063=20=EC=A0=84=EB=8B=AC=20=EC=83=81=ED=83=9C=EB=A5=BC=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../active/2026-09-13-profile-search-fix/delivery-state.md | 6 +++--- .../active/2026-09-13-profile-search-fix/handoff.md | 7 +++++++ .../active/2026-09-13-profile-search-fix/plan.md | 3 ++- docs/exec-plans/active/README.md | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 docs/exec-plans/active/2026-09-13-profile-search-fix/handoff.md diff --git a/docs/exec-plans/active/2026-09-13-profile-search-fix/delivery-state.md b/docs/exec-plans/active/2026-09-13-profile-search-fix/delivery-state.md index b72167a..398323c 100644 --- a/docs/exec-plans/active/2026-09-13-profile-search-fix/delivery-state.md +++ b/docs/exec-plans/active/2026-09-13-profile-search-fix/delivery-state.md @@ -1,12 +1,12 @@ # Delivery State -- status: publishing +- status: ci-pending - branch: dev - base: main - issue: none -- pr: pending +- pr: https://github.com/Queuing-org/frontend/pull/63 - selected_skills: plan.md 참조 - local_qa: pass — lint / 714 tests / build / 독립 리뷰 - ci: pending - review_threads: none -- next_action: 기능별 커밋 및 dev push / Draft PR 생성 +- next_action: PR #63 CI 및 실제 화면/OS IME 검증 확인 diff --git a/docs/exec-plans/active/2026-09-13-profile-search-fix/handoff.md b/docs/exec-plans/active/2026-09-13-profile-search-fix/handoff.md new file mode 100644 index 0000000..a6fcb37 --- /dev/null +++ b/docs/exec-plans/active/2026-09-13-profile-search-fix/handoff.md @@ -0,0 +1,7 @@ +# Handoff + +- 요청 범위 구현 및 dev 게시 완료: https://github.com/Queuing-org/frontend/pull/63 (Draft). +- 기능 커밋: ba921c7 (프로필 행 높이), a4ccfcc (한글 자동 검색). +- lint / 160 files 714 tests / build / 독립 QA pass. 첫 sandbox 빌드는 진행 정체로 중단 후 제한 밖 동일 명령 통과. +- 다음: PR CI와 실제 화면 정렬·OS IME 확인. 연결 가능한 Browser가 없어 화면/네트워크 실측은 미실행. +- 원인: composition 상태가 조회 enabled를 막고 loading을 강제했다. 입력값 디바운스와 Enter 선택 방어를 분리했다. diff --git a/docs/exec-plans/active/2026-09-13-profile-search-fix/plan.md b/docs/exec-plans/active/2026-09-13-profile-search-fix/plan.md index 8092355..3b5f9ac 100644 --- a/docs/exec-plans/active/2026-09-13-profile-search-fix/plan.md +++ b/docs/exec-plans/active/2026-09-13-profile-search-fix/plan.md @@ -7,5 +7,6 @@ - 수용 기준: 짧은 최애곡에서 이용 시간이 기존 큐잉 횟수 위치와 정렬되고 긴 문구에도 양쪽 통계가 같은 행에 있다. 조합 종료/엔터 없이 300ms 입력 정지 후 검색한다. 조합 중 Enter는 선택하지 않는다. 취소/오래된 응답/URL/에러 동작을 유지한다. - 커밋 순서: 1. fix(profile): 프로필 통계의 행 높이를 맞춤 2. fix(playlist): 한글 조합 종료를 기다리던 자동 검색 수정 (회귀 테스트와 실행·사고 기록 포함). - 검증: 검색 재현 테스트를 먼저 실행해 기존 실패 확인 → 수정 후 targeted test, lint, 전체 test, build → 독립 read-only QA → commit/push/draft PR. -- 진행: 구현 완료. 수정 전 회귀 실패 확인, targeted 9 tests / 전체 714 tests / lint / build / 독립 QA pass. Draft PR 게시 진행. +- 진행: 구현 완료. 수정 전 회귀 실패 확인, targeted 9 tests / 전체 714 tests / lint / build / 독립 QA pass. dev 게시 및 Draft PR #63 생성 완료. CI 대기. +- 커밋: ba921c7 (프로필), a4ccfcc (검색·테스트·원인 기록). - 잔여 위험: 운영 API 응답 시간 및 실제 OS IME 이벤트는 별도 관측하지 않음. diff --git a/docs/exec-plans/active/README.md b/docs/exec-plans/active/README.md index 6b8dee5..ce64337 100644 --- a/docs/exec-plans/active/README.md +++ b/docs/exec-plans/active/README.md @@ -1,6 +1,6 @@ # Active Execution Plans -- [2026-09-13-profile-search-fix](./2026-09-13-profile-search-fix/plan.md): implementing — 프로필 이용 시간 정렬 및 한글 조합 중 자동 검색 대기 수정 +- [2026-09-13-profile-search-fix](./2026-09-13-profile-search-fix/plan.md): ci-pending — Draft PR #63, 프로필 이용 시간 정렬·한글 자동 검색 대기 수정, lint/714 tests/build/독립 QA 통과 - [2026-09-08-track-search-realtime](./2026-09-08-track-search-realtime/plan.md): ci-pending — Draft PR #62, 노래 검색·친구 UI·큐 중복·실시간 닉네임 통합 개선, 713 tests/lint/build/독립 QA 통과