refactor: 링크 입력 시 텍스트에서 URL 자동 추출#323
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Discord 스레드 연동용 메타데이터입니다. discord-pr-bot 워크플로가 자동 생성하며, 수정·삭제하면 PR 과 Discord 알림 연동이 끊깁니다. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthrough텍스트에서 첫 번째 http(s) URL을 추출하는 공용 유틸( ChangesURL 자동 추출 기능
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant ByLinkDialog
participant extractUrlFromText
User->>ByLinkDialog: 상품 설명과 URL이 포함된 텍스트 붙여넣기
ByLinkDialog->>extractUrlFromText: URL 추출 요청
extractUrlFromText-->>ByLinkDialog: URL 또는 null 반환
ByLinkDialog->>ByLinkDialog: 입력값 갱신 또는 오류 설정
User->>ByLinkDialog: 제출
ByLinkDialog->>ByLinkDialog: 추출된 submitUrl 사용
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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)
apps/web/src/app/tournament/[id]/create/_components/add-by-link-dialog/AddByLinkDialog.tsx (1)
69-80: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
React.ClipboardEvent타입용 React 네임스페이스 import 추가 필요
handlePaste에서React.ClipboardEvent<HTMLInputElement>를 쓰고 있으니,import * as React from 'react';또는import type React from 'react';를 추가해야 합니다.🤖 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 `@apps/web/src/app/tournament/`[id]/create/_components/add-by-link-dialog/AddByLinkDialog.tsx around lines 69 - 80, Add the missing React import in AddByLinkDialog so the handlePaste handler can use React.ClipboardEvent<HTMLInputElement> without a type error; update the top of AddByLinkDialog.tsx to import React from the React namespace, and keep the existing handlePaste, Input, and onPaste usage unchanged.
🧹 Nitpick comments (3)
apps/web/src/utils/extractUrl.ts (1)
15-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win핵심 로직은 적절하나 유닛 테스트 부재.
여러 다이얼로그와
useShareIntentWish에서 공유되는 유틸인데 테스트가 없습니다. trailing 구두점 제거, 개행 포함 텍스트, URL이 없는 경우 등 엣지 케이스를 커버하는 테스트를 추가하면 회귀를 방지할 수 있습니다.✅ 테스트 예시
describe('extractUrlFromText', () => { it('상품명과 URL이 함께 있을 때 URL만 추출한다', () => { expect(extractUrlFromText('[제작]쿨링 리본 미니원피스\nhttps://s.zigzag.kr/XWnpU1fuZx')).toBe( 'https://s.zigzag.kr/XWnpU1fuZx', ); }); it('trailing 구두점을 제거한다', () => { expect(extractUrlFromText('링크: https://example.com/page).')).toBe('https://example.com/page'); }); it('URL이 없으면 null을 반환한다', () => { expect(extractUrlFromText('상품 설명만 있음')).toBeNull(); }); });🤖 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 `@apps/web/src/utils/extractUrl.ts` around lines 15 - 20, Add unit tests for extractUrlFromText to cover the shared URL parsing behavior used across dialogs and useShareIntentWish. Create tests that verify normal extraction with newline-separated text, trailing punctuation trimming, and returning null when no URL is present, so the edge cases around the extractUrlFromText helper are protected against regressions.apps/web/src/components/get-item-dialog/ByLinkDialog.tsx (1)
76-84: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value
handlePaste가 기존 입력값을 무조건 덮어씀.
handleChange(extractedUrl)이 커서/선택 영역과 무관하게 입력값 전체를 교체합니다. 입력창이 비어있는 일반적인 사용 흐름에서는 문제 없지만, 이미 값이 있는 상태에서 추가 텍스트를 붙여넣는 edge case에서는 기존 값이 사라집니다. 현재 다이얼로그 용도상 리스크는 낮아 보입니다.🤖 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 `@apps/web/src/components/get-item-dialog/ByLinkDialog.tsx` around lines 76 - 84, `handlePaste` in `ByLinkDialog` currently replaces the entire input value whenever a URL is extracted, which can wipe existing text in edge cases. Update `handlePaste` to respect the current input state and selection/caret behavior in `handleChange` flow, so pasted URL text is inserted or applied without blindly overwriting the whole field when there is already content.apps/web/src/app/tournament/[id]/create/_components/add-by-link-dialog/AddByLinkDialog.tsx (1)
29-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
ByLinkDialog.tsx와 거의 동일한handleSubmit/handlePaste로직 중복.PR 목표에서 "ShareIntent, ShareBottomSheet, 링크 입력 다이얼로그 간 중복 로직은 최소화한다"를 명시하고 있는데,
ByLinkDialog.tsx와 이 파일의handleSubmit(URL 추출 fallback)·handlePaste구현이 거의 동일하게 중복됩니다. 공통 훅(예:useUrlInputWithPaste)으로 추출하면 두 다이얼로그 간 일관성을 유지하고 향후 추출 규칙 변경 시 한 곳만 수정하면 됩니다.🤖 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 `@apps/web/src/app/tournament/`[id]/create/_components/add-by-link-dialog/AddByLinkDialog.tsx around lines 29 - 58, `AddByLinkDialog` repeats the same URL extraction and paste-handling logic found in `ByLinkDialog`, so extract the shared `handleSubmit` fallback and `handlePaste` behavior into a common hook such as `useUrlInputWithPaste`. Update `AddByLinkDialog` to use that shared hook (along with the matching logic in `ByLinkDialog`) so both dialogs rely on the same URL parsing and paste normalization path, keeping `handleSubmit`, `handleChange`, and `handlePaste` consistent across components.
🤖 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
`@apps/web/src/app/tournament/`[id]/create/_components/add-by-link-dialog/AddByLinkDialog.tsx:
- Around line 32-40: The AddByLinkDialog submit path has the same trailing-text
bug as ByLinkDialog: when URL_PATTERN.test(trimmedUrl) passes, the full trimmed
input is submitted even if extra text follows the URL. Update the submit logic
in AddByLinkDialog to always normalize through the same URL extraction/trimming
flow used for pasted text, so trailing description text is removed before
calling onSubmit. Keep the fix aligned with the URL parsing behavior in
extractUrlFromText and the existing submitUrl handling.
In `@apps/web/src/components/get-item-dialog/ByLinkDialog.tsx`:
- Around line 44-53: The submitUrl selection in ByLinkDialog is bypassing
extractUrlFromText when URL_PATTERN.test(trimmedUrl) matches, so trailing text
can still be submitted. Update the ByLinkDialog flow to always normalize input
through extractUrlFromText before calling postWishLinkMutation or the other
submit path, and remove the conditional shortcut that returns trimmedUrl
directly. If URL_PATTERN is no longer used in this component, clean up that
unused import as well.
---
Outside diff comments:
In
`@apps/web/src/app/tournament/`[id]/create/_components/add-by-link-dialog/AddByLinkDialog.tsx:
- Around line 69-80: Add the missing React import in AddByLinkDialog so the
handlePaste handler can use React.ClipboardEvent<HTMLInputElement> without a
type error; update the top of AddByLinkDialog.tsx to import React from the React
namespace, and keep the existing handlePaste, Input, and onPaste usage
unchanged.
---
Nitpick comments:
In
`@apps/web/src/app/tournament/`[id]/create/_components/add-by-link-dialog/AddByLinkDialog.tsx:
- Around line 29-58: `AddByLinkDialog` repeats the same URL extraction and
paste-handling logic found in `ByLinkDialog`, so extract the shared
`handleSubmit` fallback and `handlePaste` behavior into a common hook such as
`useUrlInputWithPaste`. Update `AddByLinkDialog` to use that shared hook (along
with the matching logic in `ByLinkDialog`) so both dialogs rely on the same URL
parsing and paste normalization path, keeping `handleSubmit`, `handleChange`,
and `handlePaste` consistent across components.
In `@apps/web/src/components/get-item-dialog/ByLinkDialog.tsx`:
- Around line 76-84: `handlePaste` in `ByLinkDialog` currently replaces the
entire input value whenever a URL is extracted, which can wipe existing text in
edge cases. Update `handlePaste` to respect the current input state and
selection/caret behavior in `handleChange` flow, so pasted URL text is inserted
or applied without blindly overwriting the whole field when there is already
content.
In `@apps/web/src/utils/extractUrl.ts`:
- Around line 15-20: Add unit tests for extractUrlFromText to cover the shared
URL parsing behavior used across dialogs and useShareIntentWish. Create tests
that verify normal extraction with newline-separated text, trailing punctuation
trimming, and returning null when no URL is present, so the edge cases around
the extractUrlFromText helper are protected against regressions.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: cf537c34-0eb7-4f0b-b832-423b39000c3b
📒 Files selected for processing (4)
apps/web/src/app/archive/_hooks/useShareIntentWish.tsapps/web/src/app/tournament/[id]/create/_components/add-by-link-dialog/AddByLinkDialog.tsxapps/web/src/components/get-item-dialog/ByLinkDialog.tsxapps/web/src/utils/extractUrl.ts
| // 상품 설명과 URL 이 함께 붙여넣어진 경우 URL 만 추출해 제출한다 (onPaste 를 타지 않은 경로 안전망). | ||
| const submitUrl = URL_PATTERN.test(trimmedUrl) ? trimmedUrl : extractUrlFromText(trimmedUrl); | ||
|
|
||
| if (!submitUrl) { | ||
| setHasError(true); | ||
| return; | ||
| } | ||
|
|
||
| onSubmit?.(trimmedUrl); | ||
| onSubmit?.(submitUrl); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
ByLinkDialog.tsx와 동일한 trailing 텍스트 미제거 버그.
URL_PATTERN.test(trimmedUrl)이 true인 경우 trimmedUrl 전체가 그대로 submitUrl로 쓰입니다. URL_PATTERN이 끝 anchor 없이 ^https?:\/\/.+만 검사하므로, "https://... 설명" 형태처럼 URL 뒤에 텍스트가 붙은 입력에서는 extractUrlFromText의 trailing 제거 로직을 건너뛰고 잘못된 문자열이 onSubmit으로 전달됩니다. ByLinkDialog.tsx의 동일 로직과 함께 근본 원인이 같으므로 함께 수정이 필요합니다.
🐛 제안하는 수정
- // 상품 설명과 URL 이 함께 붙여넣어진 경우 URL 만 추출해 제출한다 (onPaste 를 타지 않은 경로 안전망).
- const submitUrl = URL_PATTERN.test(trimmedUrl) ? trimmedUrl : extractUrlFromText(trimmedUrl);
+ // 상품 설명과 URL 이 함께 붙여넣어진 경우 URL 만 추출해 제출한다 (onPaste 를 타지 않은 경로 안전망).
+ const submitUrl = extractUrlFromText(trimmedUrl);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 상품 설명과 URL 이 함께 붙여넣어진 경우 URL 만 추출해 제출한다 (onPaste 를 타지 않은 경로 안전망). | |
| const submitUrl = URL_PATTERN.test(trimmedUrl) ? trimmedUrl : extractUrlFromText(trimmedUrl); | |
| if (!submitUrl) { | |
| setHasError(true); | |
| return; | |
| } | |
| onSubmit?.(trimmedUrl); | |
| onSubmit?.(submitUrl); | |
| // 상품 설명과 URL 이 함께 붙여넣어진 경우 URL 만 추출해 제출한다 (onPaste 를 타지 않은 경로 안전망). | |
| const submitUrl = extractUrlFromText(trimmedUrl); | |
| if (!submitUrl) { | |
| setHasError(true); | |
| return; | |
| } | |
| onSubmit?.(submitUrl); |
🤖 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
`@apps/web/src/app/tournament/`[id]/create/_components/add-by-link-dialog/AddByLinkDialog.tsx
around lines 32 - 40, The AddByLinkDialog submit path has the same trailing-text
bug as ByLinkDialog: when URL_PATTERN.test(trimmedUrl) passes, the full trimmed
input is submitted even if extra text follows the URL. Update the submit logic
in AddByLinkDialog to always normalize through the same URL extraction/trimming
flow used for pasted text, so trailing description text is removed before
calling onSubmit. Keep the fix aligned with the URL parsing behavior in
extractUrlFromText and the existing submitUrl handling.
| // 상품 설명과 URL 이 함께 붙여넣어진 경우 URL 만 추출해 제출한다 (onPaste 를 타지 않은 경로 안전망). | ||
| const submitUrl = URL_PATTERN.test(trimmedUrl) ? trimmedUrl : extractUrlFromText(trimmedUrl); | ||
|
|
||
| if (!submitUrl) { | ||
| setHasError(true); | ||
| return; | ||
| } | ||
|
|
||
| if (type === 'wish') | ||
| postWishLinkMutation(trimmedUrl, { | ||
| postWishLinkMutation(submitUrl, { |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
URL_PATTERN.test 매치 시 trailing 텍스트가 제거되지 않고 그대로 제출됨.
URL_PATTERN은 ^https?:\/\/.+로 끝 anchor가 없어, "https://example.com 상품설명"처럼 URL 뒤에 설명이 붙은 입력도 test()가 true를 반환합니다. 이 경우 extractUrlFromText를 타지 않고 trimmedUrl 전체(설명 포함)가 submitUrl로 그대로 제출됩니다. extractUrlFromText가 명시하는 "공백 기준 분리 및 trailing ), ,, . 제거" 규칙이 이 경로에서는 적용되지 않아, onPaste를 타지 않는 자동완성/drag&drop 입력 시 잘못된 값이 API로 전달될 수 있습니다.
extractUrlFromText는 순수 URL 입력에서도 동일하게 매치·추출되므로, ternary 없이 항상 extractUrlFromText만 호출하는 것으로 단순화하면 이 문제가 해결됩니다.
🐛 제안하는 수정
- // 상품 설명과 URL 이 함께 붙여넣어진 경우 URL 만 추출해 제출한다 (onPaste 를 타지 않은 경로 안전망).
- const submitUrl = URL_PATTERN.test(trimmedUrl) ? trimmedUrl : extractUrlFromText(trimmedUrl);
+ // 상품 설명과 URL 이 함께 붙여넣어진 경우 URL 만 추출해 제출한다 (onPaste 를 타지 않은 경로 안전망).
+ const submitUrl = extractUrlFromText(trimmedUrl);이 경우 URL_PATTERN import(line 14)도 이 파일에서 더 이상 필요 없다면 제거해 주세요.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 상품 설명과 URL 이 함께 붙여넣어진 경우 URL 만 추출해 제출한다 (onPaste 를 타지 않은 경로 안전망). | |
| const submitUrl = URL_PATTERN.test(trimmedUrl) ? trimmedUrl : extractUrlFromText(trimmedUrl); | |
| if (!submitUrl) { | |
| setHasError(true); | |
| return; | |
| } | |
| if (type === 'wish') | |
| postWishLinkMutation(trimmedUrl, { | |
| postWishLinkMutation(submitUrl, { | |
| // 상품 설명과 URL 이 함께 붙여넣어진 경우 URL 만 추출해 제출한다 (onPaste 를 타지 않은 경로 안전망). | |
| const submitUrl = extractUrlFromText(trimmedUrl); | |
| if (!submitUrl) { | |
| setHasError(true); | |
| return; | |
| } | |
| if (type === 'wish') | |
| postWishLinkMutation(submitUrl, { |
🤖 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 `@apps/web/src/components/get-item-dialog/ByLinkDialog.tsx` around lines 44 -
53, The submitUrl selection in ByLinkDialog is bypassing extractUrlFromText when
URL_PATTERN.test(trimmedUrl) matches, so trailing text can still be submitted.
Update the ByLinkDialog flow to always normalize input through
extractUrlFromText before calling postWishLinkMutation or the other submit path,
and remove the conditional shortcut that returns trimmedUrl directly. If
URL_PATTERN is no longer used in this component, clean up that unused import as
well.
* refactor: 링크 입력 시 텍스트에서 URL 자동 추출 * fix: URL 패턴 anchor 추가로 트레일링 텍스트 제출 방지
* chore: iOS Smart Banner 설정 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: iOS Universal Link 설정 및 딥링크 라우팅 확장 - AASA 파일 추가 (콜백/api 경로는 exclude) - iOS associatedDomains 설정 - Universal Link/Smart Banner app-argument의 https URL을 WebView 경로로 라우팅 * feat: android app link 관련 config 추가 * chore: proxy.ts refresh 401 임시 안전망 제거 (백엔드 rotation grace 적용) (#295) * fix: iOS 26 Safari 노치 영역이 흰색으로 보이는 문제 수정 (#293) Co-authored-by: Jung Sun A <amber0809@naver.com> * PR 봇을 Slack 에서 Discord 로 이관 (#308) * chore: PR 봇을 Slack 에서 Discord 로 이관 slack-pr-bot.yml 제거 + discord-pr-bot.yml 추가 (서버 PIKI-Server 검증본 기반). 리뷰어 멘션(initial_reviewers + requested_reviewers)·연관 이슈(close #N) 로직 동일. 클라 slack 커밋 이력(#14·#17·#18·#20)을 추적해 서버와 다른 클라 특화를 반영: - 트리거·부모 메시지 조건: ready_for_review → reopened (#17, 기존 slack 봇과 동일). - 요약 파싱 섹션명: Task → 작업 요약 (클라 PR 템플릿이 '## 작업 요약'). 클라 slack 은 두 요약 함수의 섹션명이 '작업 내용'/'작업 요약' 으로 불일치(#20 이 한쪽만 수정)해 부모 메시지 요약이 항상 비던 버그가 있었는데, 이관본은 둘 다 '작업 요약' 으로 통일해 함께 고친다. - 요약에서 중첩 리스트(' -') skip (#18). alert-direct-push job 제외(클라 범위 밖). 배포는 클라가 Vercel 이라 이 이관 범위 밖. secret(DISCORD_BOT_TOKEN 공유·DISCORD_PR_CHANNEL_ID 전용·DISCORD_USER_MAP) 설정 완료. * PR 봇 부모 메시지 라벨을 갱신 메시지와 같은 볼드·대문자 서식으로 통일 부모(최초) 메시지는 '🔥 연관 이슈'·'**branch**' 처럼 볼드 없는 소문자였고 갱신 메시지는 '🔥 **연관 이슈**'·'**Branch**' 로 달라, 첫 갱신 때 라벨 서식이 바뀌어 보였다. 부모 쪽을 갱신 쪽 서식에 맞춰 일치시킨다. * synchronize 알림의 커밋 수·최신 커밋을 페이지네이션에 견고하게 계산 PR 커밋 API 는 30개/페이지(오래된→최신 순)라 첫 페이지만 받아 length 로 세면 31개 이상 PR 에서 커밋 수가 30 으로 잘리고, 첫 페이지 .[-1] 도 최신 커밋이 아니었다. 커밋 수는 이벤트 payload 의 .pull_request.commits 로, 최신 커밋은 마지막 페이지 (per_page=1 & page=총개수)로 집어 정확하게 만든다. * PR 봇 서버 개선 반영 (상태 리액션·부모 고정·스레드 정리·라벨·헬퍼 공유) (#310) * PR 봇에 상태 리액션·부모 고정·스레드 정리·라벨 표시·헬퍼 공유 반영 이관 이후 서버 봇에 쌓인 개선을 클라 봇에 맞춰 반영한다. - 상태 리액션: 부모 메시지에 열림/작업중/머지/종료를 이모지로 전환 표시(429 rate limit 재시도 포함) - 부모 고정: 열린(리뷰 대기) PR 부모를 채널 상단에 고정, 종료 시 해제 - 스레드 정리: 종료 시 스레드명에 결과 프리픽스(✅/🗑️)+archive+lock - 부모 카드에 라벨 표시(build_label_text) - find_meta 가 메타 주석을 JSON 마커로 정확히 매칭 — 파일명을 언급한 리뷰 코멘트를 집어 THREAD_ID 를 못 읽고 이후 스텝이 전부 skip 되던 버그(머지 알림 누락) 해소 - 헬퍼 4개를 $RUNNER_TEMP 공유 스크립트로 추출해 스텝 간 중복 제거(checkout 불필요) - 커밋 수는 .pull_request.commits 로 정확화 클라 적응: push 트리거·alert-direct-push 잡 제외, ready_for_review→reopened, 작업 요약 섹션 매칭+중첩 불릿 스킵, 부모 카드 라벨 서식을 갱신 카드와 통일. 최신 커밋 메시지는 contents:read 없이 동작하도록 PR commits 엔드포인트로 조회. * 재오픈된 PR 의 부모 재고정·스레드 복구 (reopened 흐름 정상화) reopened 는 메타 주석이 이미 있어 post_parent 스텝이 스킵되는데, 부모 고정·상태 복구가 그 스텝 출력에만 의존해 재오픈 시 깨졌다. reopened 는 서버 봇엔 없고 클라 트리거라, 이 흐름 버그는 클라 전용이다. - 부모 재고정: pin 스텝의 부모 id 를 post_parent → find_meta 로 폴백하고 조건도 find_meta.message_id 를 포함하도록 넓혀, 재오픈 시 상단 고정이 복구되게 한다. - 스레드 복구: 종료 시 archived·locked 로 잠그고 이름에 🗑️ 프리픽스를 다는데, 재오픈 시 되돌리는 경로가 없어 이후 synchronize 답글이 잠긴 스레드라 실패했다. reopened 에 unarchive·unlock + 이름 원복 스텝을 추가한다. * fix: 게스트 인증 플로우 버그 2건 수정 + 관련 UX 개선 (#291) * fix: 게스트 MEMBER_ONLY 접근 시 로그인 무한 루프 수정 회원(MEMBER) 토큰일 때만 로그인 페이지를 건너뛰도록 제한. 게스트는 로그인에 남아 archive↔login 무한 루프 방지. * fix: 게스트 자동 로그인 시 현재 요청부터 token 적용되도록 수정 * refactor: 로그인 약관 안내 문구를 page로 분리 * feat: 게스트 세션 살아있는 경우 게스트 로그인 선택 시 리프레시 진행 * style: 토스트 위치 오류 수정 * feat: 게스트 로그인 redirect path가 /archive인 경우 /home으로 이동 및 안내 토스트 노출 * fix: 앱 부팅 시 refresh cookie 죽는 문제 해결 * fix: proxy에서 앱 auth token 도 처리 가능하도록 수정 * refactor: 앱 token refresh timeout 추가 * fix: 앱 토큰 만료(401) 시 WebView 쿠키도 정리 * fix: 앱 refresh 응답 snake_case 파싱 수정 * chore: proxy.ts refresh 401 임시 안전망 제거 (백엔드 rotation grace 적용) (#295) * chore: proxy.ts refresh 401 임시 안전망 제거 (백엔드 rotation grace 적용) (#295) * fix: iOS 26 Safari 노치 영역이 흰색으로 보이는 문제 수정 (#293) Co-authored-by: Jung Sun A <amber0809@naver.com> * refactor: 로그인 후 리다이렉트 로직 유틸로 추출 - 로그인 성공 후 경로 계산 중복 제거 (getPostLoginRedirectPath) * fix: 공유 위시 등록 시 refresh 401이면 죽은 토큰 정리 Share Extension이 만료된 토큰으로 재시도를 반복하지 않도록 refresh 401 시 clearTokens 호출 --------- Co-authored-by: 조영찬 <tigerbone@naver.com> * feat: Sentry 에러 모니터링 도입 (#296) * feat: web Sentry 에러 모니터링 도입 * feat: app Sentry 에러 모니터링 도입 * feat: web/app sentry 에러 로깅 규약 추가 - app: @sentry/react-native 셋업(_layout init+wrap, metro, 플러그인, eas 환경별 주입) - 공용 captureError 유틸(web/app) — 태그/컨텍스트 일관 부착 - API 5xx·네트워크 에러 중앙 수집(axios 인터셉터 / app fetch) - 유저 식별 setUser(id), 세션 만료 시 해제 - error.tsx 캡처, ignoreErrors 노이즈 필터 - app WebView onError/onHttpError, 백그라운드 FCM 핸들러 수집 * test: 에러 검증용 테스트 페이지 생성 * fix: SENTRY_AUTH_TOKEN turbo passThroughEnv 추가 (소스맵 업로드) * feat: Session Replay 마스킹 완화 (이메일만 마스킹, 나머지 노출) * feat: API 에러 리포트 제목을 'API {status} {method} {path}' 형식으로 개선 * Revert "test: 에러 검증용 테스트 페이지 생성" This reverts commit 87f9e53. * fix: 소셜 로그인 5xx 응답 JSON 파싱 실패 시 Sentry 수집 누락 수정 * fix: 로그아웃 시 Sentry 유저 컨텍스트 해제 * fix: Session Replay 타이핑 입력창만 마스킹 (maskAllInputs) * fix: 소셜 로그인 2xx 응답 본문 파싱 실패도 Sentry 수집 * fix: 페이지별 토스트 위치 커스터마이즈 (탭바 페이지 토스트 겹침 수정) (#309) * feat: 토스트 offset 페이지별 override 메커니즘 추가 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: 홈 탭바 위로 토스트 위치 조정 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: 보관 페이지 하단 바 위로 토스트 위치 조정 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: 토스트 offset 수치 조정 * refactor: 토스트 offset zustand 스토어를 CSS :has() 마커 방식으로 대체 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * feat: 딥링크 전환 로딩 오버레이 추가 * feat: 앱 링크 접속시 warm start 기능 추가 * PR 봇 스레드를 팀 전원이 볼 수 있게 (스레드 멤버 자동 추가) (#313) * PR 봇 스레드에 팀원 자동 추가로 팀 전원 사이드바 노출 Discord 공개 스레드는 멤버(생성자·초대·멘션된 사람)만 사이드바에 뜬다. 부모 카드가 작성자만 @멘션해 작성자만 자기 PR 스레드가 보이고 남의 스레드는 안 떴다. - 스레드 생성 직후 DISCORD_USER_MAP 의 팀원 전원을 thread-members 로 추가해 새 PR 스레드가 팀 전원 사이드바에 뜨게 한다(멘션과 달리 조용히 목록에만 올림). - 이 변경 이전 열린 PR 스레드용으로 backfill-thread-members 잡을 추가한다 (workflow_dispatch 수동 실행). notify-discord 는 pull_request 로 가드해 dispatch 시엔 안 돈다. * 연관 이슈 링크의 Discord 임베드 카드 억제 부모 카드의 연관 이슈 링크가 GitHub 임베드 카드로 커지게 떠서 본문(작업 요약 등)을 아래로 밀어냈다. 이슈 URL 을 <> 로 감싸 임베드만 억제하고 클릭 링크는 유지한다. PR 타이틀 링크는 그대로 둬 PR 카드는 계속 뜬다. * 작업 요약 추출이 STAR 의 Task 섹션도 인식 FE 팀 PR 은 ## 작업 요약, 서버 STAR 로 쓴 PR 은 ## Task 를 쓴다. build_summary 가 작업 요약만 찾아 STAR PR 은 '요약 없음'이 됐다. 두 섹션명을 모두 인식하게 해 어느 관례로 쓰든 요약이 채워지게 한다. * 작업 요약의 Task 섹션 인식 제거 FE 팀은 ## 작업 요약을 쓰고 봇이 이미 이를 매칭하므로, STAR 의 ## Task 인식 추가는 불필요해 되돌린다. 요약은 PR 본문에 ## 작업 요약 을 두는 팀 관례로 채운다. * PR 봇 재싱크: 머지 스레드 잠금 해제 + 종합 백필 (서버 #703 반영) (#315) 서버 봇(#703)과 갈라진 두 가지를 클라에도 반영해 동작을 일치시킨다. - 머지 시 close 정리에서 locked 제거(archived 만) — 닫히되 글을 쓸 수 있게(archived 만이라 글을 쓰면 다시 열림). 머지된 PR 스레드가 잠겨 글을 못 쓰던 문제 해소. - 백필 잡을 종합형(backfill-threads)으로 교체: 열린 PR→팀원 추가+열림, 머지 PR→✅+닫힘, 미머지 닫힘→🗑️+닫힘. 기존엔 열린 스레드 팀원 추가만 했다. 과거 find_meta 버그로 안 닫힌 스레드까지 소급 정리(멱등). * refactor: 링크 입력창 모바일 키보드를 일반 텍스트 키보드로 변경 (#325) * feat: 전역 API 에러 안전망 도입 및 개별 훅 에러 처리 정리 (#311) * docs: 역할 분배 문서 추가 * docs: 현재 api 에러 대응 현황 문서 추가 * docs: 에러 처리 정책 문서 추가 * feat: 공통 API 에러 메시지 유틸 추가 * feat: 전역 mutation/query 에러 안전망 도입 * refactor: 비어있는 개별 onError 삭제 (전역 안전망 위임) * refactor: 개별 훅 5xx 토스트 분기 제거 (이중 토스트 방지) * PR 봇을 Slack 에서 Discord 로 이관 (#308) * chore: PR 봇을 Slack 에서 Discord 로 이관 slack-pr-bot.yml 제거 + discord-pr-bot.yml 추가 (서버 PIKI-Server 검증본 기반). 리뷰어 멘션(initial_reviewers + requested_reviewers)·연관 이슈(close #N) 로직 동일. 클라 slack 커밋 이력(#14·#17·#18·#20)을 추적해 서버와 다른 클라 특화를 반영: - 트리거·부모 메시지 조건: ready_for_review → reopened (#17, 기존 slack 봇과 동일). - 요약 파싱 섹션명: Task → 작업 요약 (클라 PR 템플릿이 '## 작업 요약'). 클라 slack 은 두 요약 함수의 섹션명이 '작업 내용'/'작업 요약' 으로 불일치(#20 이 한쪽만 수정)해 부모 메시지 요약이 항상 비던 버그가 있었는데, 이관본은 둘 다 '작업 요약' 으로 통일해 함께 고친다. - 요약에서 중첩 리스트(' -') skip (#18). alert-direct-push job 제외(클라 범위 밖). 배포는 클라가 Vercel 이라 이 이관 범위 밖. secret(DISCORD_BOT_TOKEN 공유·DISCORD_PR_CHANNEL_ID 전용·DISCORD_USER_MAP) 설정 완료. * PR 봇 부모 메시지 라벨을 갱신 메시지와 같은 볼드·대문자 서식으로 통일 부모(최초) 메시지는 '🔥 연관 이슈'·'**branch**' 처럼 볼드 없는 소문자였고 갱신 메시지는 '🔥 **연관 이슈**'·'**Branch**' 로 달라, 첫 갱신 때 라벨 서식이 바뀌어 보였다. 부모 쪽을 갱신 쪽 서식에 맞춰 일치시킨다. * synchronize 알림의 커밋 수·최신 커밋을 페이지네이션에 견고하게 계산 PR 커밋 API 는 30개/페이지(오래된→최신 순)라 첫 페이지만 받아 length 로 세면 31개 이상 PR 에서 커밋 수가 30 으로 잘리고, 첫 페이지 .[-1] 도 최신 커밋이 아니었다. 커밋 수는 이벤트 payload 의 .pull_request.commits 로, 최신 커밋은 마지막 페이지 (per_page=1 & page=총개수)로 집어 정확하게 만든다. * PR 봇 서버 개선 반영 (상태 리액션·부모 고정·스레드 정리·라벨·헬퍼 공유) (#310) * PR 봇에 상태 리액션·부모 고정·스레드 정리·라벨 표시·헬퍼 공유 반영 이관 이후 서버 봇에 쌓인 개선을 클라 봇에 맞춰 반영한다. - 상태 리액션: 부모 메시지에 열림/작업중/머지/종료를 이모지로 전환 표시(429 rate limit 재시도 포함) - 부모 고정: 열린(리뷰 대기) PR 부모를 채널 상단에 고정, 종료 시 해제 - 스레드 정리: 종료 시 스레드명에 결과 프리픽스(✅/🗑️)+archive+lock - 부모 카드에 라벨 표시(build_label_text) - find_meta 가 메타 주석을 JSON 마커로 정확히 매칭 — 파일명을 언급한 리뷰 코멘트를 집어 THREAD_ID 를 못 읽고 이후 스텝이 전부 skip 되던 버그(머지 알림 누락) 해소 - 헬퍼 4개를 $RUNNER_TEMP 공유 스크립트로 추출해 스텝 간 중복 제거(checkout 불필요) - 커밋 수는 .pull_request.commits 로 정확화 클라 적응: push 트리거·alert-direct-push 잡 제외, ready_for_review→reopened, 작업 요약 섹션 매칭+중첩 불릿 스킵, 부모 카드 라벨 서식을 갱신 카드와 통일. 최신 커밋 메시지는 contents:read 없이 동작하도록 PR commits 엔드포인트로 조회. * 재오픈된 PR 의 부모 재고정·스레드 복구 (reopened 흐름 정상화) reopened 는 메타 주석이 이미 있어 post_parent 스텝이 스킵되는데, 부모 고정·상태 복구가 그 스텝 출력에만 의존해 재오픈 시 깨졌다. reopened 는 서버 봇엔 없고 클라 트리거라, 이 흐름 버그는 클라 전용이다. - 부모 재고정: pin 스텝의 부모 id 를 post_parent → find_meta 로 폴백하고 조건도 find_meta.message_id 를 포함하도록 넓혀, 재오픈 시 상단 고정이 복구되게 한다. - 스레드 복구: 종료 시 archived·locked 로 잠그고 이름에 🗑️ 프리픽스를 다는데, 재오픈 시 되돌리는 경로가 없어 이후 synchronize 답글이 잠긴 스레드라 실패했다. reopened 에 unarchive·unlock + 이름 원복 스텝을 추가한다. * fix: 게스트 인증 플로우 버그 2건 수정 + 관련 UX 개선 (#291) * fix: 게스트 MEMBER_ONLY 접근 시 로그인 무한 루프 수정 회원(MEMBER) 토큰일 때만 로그인 페이지를 건너뛰도록 제한. 게스트는 로그인에 남아 archive↔login 무한 루프 방지. * fix: 게스트 자동 로그인 시 현재 요청부터 token 적용되도록 수정 * refactor: 로그인 약관 안내 문구를 page로 분리 * feat: 게스트 세션 살아있는 경우 게스트 로그인 선택 시 리프레시 진행 * style: 토스트 위치 오류 수정 * feat: 게스트 로그인 redirect path가 /archive인 경우 /home으로 이동 및 안내 토스트 노출 * fix: 앱 부팅 시 refresh cookie 죽는 문제 해결 * fix: proxy에서 앱 auth token 도 처리 가능하도록 수정 * refactor: 앱 token refresh timeout 추가 * fix: 앱 토큰 만료(401) 시 WebView 쿠키도 정리 * fix: 앱 refresh 응답 snake_case 파싱 수정 * chore: proxy.ts refresh 401 임시 안전망 제거 (백엔드 rotation grace 적용) (#295) * chore: proxy.ts refresh 401 임시 안전망 제거 (백엔드 rotation grace 적용) (#295) * fix: iOS 26 Safari 노치 영역이 흰색으로 보이는 문제 수정 (#293) Co-authored-by: Jung Sun A <amber0809@naver.com> * refactor: 로그인 후 리다이렉트 로직 유틸로 추출 - 로그인 성공 후 경로 계산 중복 제거 (getPostLoginRedirectPath) * fix: 공유 위시 등록 시 refresh 401이면 죽은 토큰 정리 Share Extension이 만료된 토큰으로 재시도를 반복하지 않도록 refresh 401 시 clearTokens 호출 --------- Co-authored-by: 조영찬 <tigerbone@naver.com> * feat: Sentry 에러 모니터링 도입 (#296) * feat: web Sentry 에러 모니터링 도입 * feat: app Sentry 에러 모니터링 도입 * feat: web/app sentry 에러 로깅 규약 추가 - app: @sentry/react-native 셋업(_layout init+wrap, metro, 플러그인, eas 환경별 주입) - 공용 captureError 유틸(web/app) — 태그/컨텍스트 일관 부착 - API 5xx·네트워크 에러 중앙 수집(axios 인터셉터 / app fetch) - 유저 식별 setUser(id), 세션 만료 시 해제 - error.tsx 캡처, ignoreErrors 노이즈 필터 - app WebView onError/onHttpError, 백그라운드 FCM 핸들러 수집 * test: 에러 검증용 테스트 페이지 생성 * fix: SENTRY_AUTH_TOKEN turbo passThroughEnv 추가 (소스맵 업로드) * feat: Session Replay 마스킹 완화 (이메일만 마스킹, 나머지 노출) * feat: API 에러 리포트 제목을 'API {status} {method} {path}' 형식으로 개선 * Revert "test: 에러 검증용 테스트 페이지 생성" This reverts commit 87f9e53. * fix: 소셜 로그인 5xx 응답 JSON 파싱 실패 시 Sentry 수집 누락 수정 * fix: 로그아웃 시 Sentry 유저 컨텍스트 해제 * fix: Session Replay 타이핑 입력창만 마스킹 (maskAllInputs) * fix: 소셜 로그인 2xx 응답 본문 파싱 실패도 Sentry 수집 * feat: mutation 오류 발생 시 Sentry 로깅 추가 * docs: 역할정리 수정 * docs: 역할 관련 문서 업데이트 --------- Co-authored-by: sevineleven <117634128+sevineleven@users.noreply.github.com> Co-authored-by: 조영찬 <tigerbone@naver.com> * refactor: 게스트 참여 dead code 제거 (#321) * refactor: 게스트 참여 dead code 제거 * refactor: 게스트 참여 dead code 제거 2 --------- Co-authored-by: Jung Sun A <amber0809@naver.com> * chore: update app version * chore: eas config 삭제 * refactor: 링크 입력 시 텍스트에서 URL 자동 추출 (#323) * refactor: 링크 입력 시 텍스트에서 URL 자동 추출 * fix: URL 패턴 anchor 추가로 트레일링 텍스트 제출 방지 * fix: warm start 딥링크 보류 처리 * fix: 앱 내비게이션 타이머 갱신 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: 조영찬 <tigerbone@naver.com> Co-authored-by: sevineleven <117634128+sevineleven@users.noreply.github.com> Co-authored-by: kanghaeun <145974230+kanghaeun@users.noreply.github.com>
작업 요약
extractUrlFromText로 분리하고 URL 유효성 패턴을https?로 통일합니다작업 세부 내용
배경
쇼핑몰 앱·웹에서 상품 링크를 복사하면 상품명/설명 + URL 이 함께 클립보드에 담기는 경우가 많은데, 기존 링크 입력창은 전체 문자열이
https://로 시작해야만 제출 가능해 유효성 검사에 실패했습니다.1. 공통 유틸 신규 —
src/utils/extractUrl.tsextractUrlFromText(text): 텍스트에서 첫https?://URL 추출 + trailing 구두점(),,,.) 제거 — 앱ShareBottomSheet의 추출 규칙과 동일URL_PATTERN:/^https?:\/\/.+/i— 패턴 통일. 기존엔ByLinkDialog만 https 전용이었는데,AddByLinkDialog/useShareIntentWish/앱 공유 흐름이 이미 http 를 허용하고 있어https?로 맞춤2. 다이얼로그 2곳 적용 — 2겹 방어
대상:
ByLinkDialog(위시/토너먼트 공통),AddByLinkDialog(토너먼트 후보)onPasteByLinkDialog의 helperText 도'올바른 URL 형식으로 입력해주세요.'로 통일 (http 허용에 맞춰 문구 조정)3. 중복 로직 정리
useShareIntentWish의 자체 URL match 로직 → 공통 유틸 재사용 (부수 개선: trailing 구두점 제거가 ShareIntent 경로에도 적용)ShareBottomSheet공통화는 보류 — 웹 유틸을 공유하려면packages/core이동 + 앱 재빌드/재심사가 필요해 WEB 라벨 범위 밖. 추후 앱 배포 사이클에 맞춰 별도 검토동작 검증
[제작/빅사이즈]이블렛 ... 원피스+ 개행 +https://s.zigzag.kr/XWnpU1fuZx(이슈 예시)상품설명https://...(input 붙여넣기로 개행 소실)(https://piki.day/item/3),pnpm check-types·pnpm lint통과연관 이슈
closes #317
Summary by CodeRabbit