Skip to content

Feature/search result2#11

Merged
hannah0352 merged 10 commits intomainfrom
feature/search-result2
Sep 20, 2025
Merged

Feature/search result2#11
hannah0352 merged 10 commits intomainfrom
feature/search-result2

Conversation

@hannah0352
Copy link
Collaborator

@hannah0352 hannah0352 commented Sep 20, 2025

📌 작업 내용

  • ServerTimeResult 컴포넌트 개선
  • 검색 결과 페이지 UI/UX 개선

📸 스크린샷

스크린샷 2025-09-20 오전 9 50 50

📝 기타

Summary by CodeRabbit

  • New Features

    • 새 검색 결과 페이지(/search-result) 도입: URL/키워드 검색, 서버 시간 비교, 상세 정보 보기, 알림 설정(모달) 및 카운트다운, 밀리초 표시 토글, 새로고침 및 친화적 오류 처리.
    • 서버 기반 검색 폼 추가: 입력 검증 후 결과 페이지로 이동.
  • Bug Fixes

    • 회원가입 payload 필드 수정으로 인한 제출 오류 해결.
  • Refactor

    • 기존 결과 페이지(/result) 제거 및 경로 통합.
    • 북마크 페이지 인증 UI 단순화(로그인/회원가입 모달 제거, 경고로 대체).
    • 알림 카운트다운 UI 간소화.

@hannah0352 hannah0352 added the design✨ UI 구현 및 개선 label Sep 20, 2025
@coderabbitai
Copy link

coderabbitai bot commented Sep 20, 2025

Walkthrough

검색 결과 기능을 재구성해 결과 경로를 /result에서 /search-result로 전환했고, 서버 시간 비교·알람 UI를 새로운 검색-결과 컴포넌트들로 옮겼다. 사이트 검색용 API 래퍼를 추가했고, 즐겨찾기 페이지의 인증 관련 UI/모달을 제거·단순화했으며 회원가입 페이로드 필드명을 username으로 수정했다.

Changes

Cohort / File(s) Change Summary
검색 결과 페이지 및 컴포넌트 추가
src/app/search-result/page.tsx, src/components/search-result/ServerSearchForm.tsx, src/components/search-result/ServerTimeResult.tsx, src/components/search-result/AlarmModal.tsx, src/components/search-result/AlarmCountdown.tsx
새로운 검색 결과 페이지와 관련 컴포넌트 추가: URL/키워드 검증 및 사이트 조회, /api/time/compare 호출을 통한 서버시간 비교, 실시간 타임 디스플레이, 알람 설정 모달 및 카운트다운(일부 UI 간소화 포함).
홈 페이지 경로/컴포넌트 참조 변경
src/app/page.tsx
ServerSearchFormKoreanStandardTime 경로/임포트 변경 및 제출 후 라우팅을 /search-result로 수정. 텍스트 문장 마침표 수정.
구 결과 페이지 제거
src/app/result/page.tsx
기존 클라이언트 측 결과 페이지(서버시간 비교와 관련 UI/로직 전체) 삭제.
구 컴포넌트 제거
src/components/ServerSearchForm.tsx, src/components/AlarmModal.tsx
이전 위치의 ServerSearchForm 및 기존 AlarmModal 파일 삭제(중복/대체로 인해).
사이트 API 래퍼 추가
src/libs/api/sites.ts
Site, SiteSearchResult, SiteSearchResponse 타입 및 SiteAPI 클래스 추가: searchSites, getAllSites, getPopularSites, getCategories, suggestUrlCorrection 등 메서드 구현; AuthUtils 헤더 사용 및 에러 처리 포함.
즐겨찾기 페이지 단순화
src/app/bookmarks/page.tsx
인증/소셜 UI, 로그인·회원가입 모달 및 관련 상태/로직 제거. 토큰 만료/부재 시 토큰 정리 후 alert로 안내하도록 흐름 단순화. 북마크 로딩/편집 등 핵심 기능은 유지.
회원가입 페이로드 키 정정
src/components/ClientHeader.tsx
회원가입 요청 바디의 필드명 userNameusername으로 변경.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Home as Home(/)
  participant Form as ServerSearchForm
  participant Sites as SiteAPI
  participant Router as Next Router
  participant Result as /search-result
  participant TimeAPI as /api/time/compare

  User->>Home: 페이지 로드
  Home->>Form: 렌더링 (onSubmit 제공)
  User->>Form: URL 또는 키워드 입력 → 제출
  Form->>Sites: searchSites(term, autoDiscover)
  Sites-->>Form: SiteSearchResult (성공/실패)
  alt 사이트 검색 성공
    Form->>Router: push('/search-result?url=...')
    Router-->>Result: 페이지 로드
    Result->>TimeAPI: POST /api/time/compare(finalUrl, metadata)
    TimeAPI-->>Result: 서버시간 비교 결과 및 네트워크 메타
    Result-->>User: 서버시간, 차이, 세부정보 렌더링
  else 실패/결과 없음
    Form-->>User: 에러 메시지 노출
  end
Loading
sequenceDiagram
  autonumber
  actor User
  participant Result as ServerTimeResult
  participant AModal as AlarmModal
  participant ACount as AlarmCountdown

  User->>Result: "알람 설정" 클릭
  Result->>AModal: 모달 오픈
  User->>AModal: 시/분/초 및 옵션 선택
  AModal-->>Result: onConfirm(AlarmData with targetTime)
  Result->>ACount: 카운트다운 시작 (알림/사전알림 트리거)
  ACount-->>User: 실시간 남은시간 업데이트 / 알림 실행
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

feat🛠️

Poem

토끼가 뛰어와 키보드 톡, 새 길을 택했지요 🐇
결과는 이동, 모달은 정리—길이 더 맑아졌네.
시계는 째깍, 알람은 찰칵—설정하면 딱! ⏰
검색은 명확히, 사이트는 똑똑히 찾자꾸나.
폼이 바뀌고 코드가 춤추네, 토끼가 깡총! 🐰

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed 제목 'Feature/search result2'는 PR에서 다루는 'search result' 관련 변경을 언급하고 있어 변경 영역과 연관성이 있습니다. 그러나 'Feature/' 접두사와 끝의 '2'는 불필요한 메타정보로 읽히며 문장 형태로 작성되지 않아 히스토리 스캔 시 직관성이 떨어집니다. 따라서 주요 변경사항(예: ServerTimeResult 개선 및 검색 결과 UI/UX 개선)을 간결하게 반영하도록 제목을 다듬는 것이 좋습니다.
Description Check ✅ Passed PR 본문은 리포지토리의 description_template에 맞춰 '## 📌 작업 내용'과 '## 📸 스크린샷' 섹션을 포함하고 있어 기본 구조를 충족합니다. 작업 내용에서는 'ServerTimeResult 컴포넌트 개선'과 '검색 결과 페이지 UI/UX 개선'이 명시되어 변경 의도는 파악됩니다. 다만 파일 삭제·추가, API/타입 변경, 테스트·마이그레이션 영향 등 리뷰어가 알면 좋은 세부 정보는 누락되어 있어 보완을 권장합니다.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/search-result2

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a928c15 and 9ce342e.

📒 Files selected for processing (1)
  • src/components/ClientHeader.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/ClientHeader.tsx

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hannah0352 hannah0352 self-assigned this Sep 20, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/bookmarks/page.tsx (1)

112-112: 경로 변경 누락: /result → /search-result

검색 결과 경로가 /search-result로 변경되었으나 아래 위치들이 여전히 /result로 열립니다. 모두 /search-result로 수정하세요.

  • src/app/bookmarks/page.tsx (line 112)
-      window.open(`/result?url=${encodeURIComponent(selectedBookmark.custom_url)}`, '_blank');
+      window.open(`/search-result?url=${encodeURIComponent(selectedBookmark.custom_url)}`, '_blank');
  • src/components/bookmarks/BookmarkList.tsx (line 89)
-      window.open(`/result?url=${encodeURIComponent(selectedBookmark.custom_url)}`, '_blank');
+      window.open(`/search-result?url=${encodeURIComponent(selectedBookmark.custom_url)}`, '_blank');
🧹 Nitpick comments (17)
src/components/ClientHeader.tsx (5)

78-86: 프론트 변수명도 username으로 통일 제안

프론트 내부 변수도 username으로 맞추면 매핑 혼동을 줄일 수 있습니다.

-  async function handleSignupSubmit({
-    userName,
+  async function handleSignupSubmit({
+    username,
     email,
     password,
   }: {
-    userName: string;
+    username: string;
     email: string;
     password: string;
   }) {
@@
-          body: JSON.stringify({ username: userName, email, password }),
+          body: JSON.stringify({ username, email, password }),

Also applies to: 93-94


31-41: 스토리지 동기화 범위 확장

다른 탭에서 userName만 변경되는 경우 반영이 늦을 수 있습니다. 두 키 모두에 반응하도록 보완을 권장합니다.

-    const onStorage = (e: StorageEvent) => {
-      if (e.key === 'accessToken') {
+    const onStorage = (e: StorageEvent) => {
+      if (e.key === 'accessToken' || e.key === 'userName') {
         const has = !!localStorage.getItem('accessToken');
         setIsAuthed(has);
         setUserName(localStorage.getItem('userName') || undefined);
       }
     };

63-67: 토큰의 localStorage 저장은 XSS에 취약 — httpOnly 쿠키 전환 검토

가능하면 서버에서 httpOnly+Secure 쿠키로 발급/회수하도록 전환하세요. Next.js Route Handler에서 쿠키 설정, 클라이언트는 Authorization 헤더 대신 쿠키 기반 인증으로 단순화하는 방식을 권장합니다.


51-60: 네트워크 타임아웃 부재

장시간 대기 방지를 위해 AbortController 기반 타임아웃(예: 10s) 적용을 고려해 주세요. 중복 제출 방지도 함께 처리하면 UX가 좋아집니다.

Also applies to: 87-96


109-118: 로그아웃 후 내비게이션은 replace 권장

router.push('/') 대신 router.replace('/')를 쓰면 뒤로 가기로 보호된 화면으로 복귀하는 상황을 줄일 수 있습니다.

-    router.push('/');
+    router.replace('/');
src/components/search-result/AlarmCountdown.tsx (1)

29-53: alarm.targetTime을 직접 사용하고 타이머 드리프트 줄이기

현재 time-of-day(hour/minute/second)로 오늘 날짜 기준 target을 재구성합니다. AlarmModal이 이미 ISO targetTime을 넘기므로 이를 그대로 사용하면 날짜/타임존 오해를 줄일 수 있고, 매 tick마다 Date.now()로 남은 시간을 계산하면 드리프트가 줄어듭니다.

-  useEffect(() => {
-    const now = new Date();
-    const target = new Date();
-
-    target.setHours(parseInt(alarm.time.hour));
-    target.setMinutes(parseInt(alarm.time.minute));
-    target.setSeconds(parseInt(alarm.time.second));
-    target.setMilliseconds(0);
-
-    let seconds = Math.floor((target.getTime() - now.getTime()) / 1000);
-    if (seconds < 0) seconds = 0;
-    setRemainingSeconds(seconds);
-
-    const interval = setInterval(() => {
-      seconds -= 1;
-      setRemainingSeconds(seconds);
-      if (seconds <= 0) {
-        clearInterval(interval);
-        setRemainingSeconds(0);
-        onComplete?.();
-      }
-    }, 1000);
-
-    return () => clearInterval(interval);
-  }, [alarm, onComplete]);
+  useEffect(() => {
+    const target = new Date(alarm.targetTime);
+    const update = () => {
+      const seconds = Math.max(
+        0,
+        Math.floor((target.getTime() - Date.now()) / 1000),
+      );
+      setRemainingSeconds(seconds);
+    };
+    update();
+    const id = setInterval(() => {
+      const seconds = Math.max(
+        0,
+        Math.floor((target.getTime() - Date.now()) / 1000),
+      );
+      setRemainingSeconds(seconds);
+      if (seconds === 0) {
+        clearInterval(id);
+        onComplete?.();
+      }
+    }, 1000);
+    return () => clearInterval(id);
+  }, [alarm.targetTime, onComplete]);
src/app/page.tsx (1)

11-11: 중복 네비게이션 가능성

ServerSearchForm 내부에서도 router.push를 수행합니다. 부모에서도 push하면 동일 경로로 이중 네비게이션이 발생할 수 있습니다. 폼 쪽을 “onSubmit이 있으면 push하지 않음”으로 바꾸는 것을 권장합니다. 해당 수정은 ServerSearchForm.tsx 코멘트를 참조하세요.

src/app/search-result/page.tsx (3)

53-60: URL 판정 로직의 false positive 제거

catch에서 startsWith('http')로 true를 반환하면 유효하지 않은 URL도 URL로 오인될 수 있습니다. 파싱이 실패하면 단순히 false를 반환하세요.

-  const isValidUrl = (str: string) => {
+  const isValidUrl = (str: string) => {
     try {
       new URL(str);
       return true;
     } catch {
-      return str.startsWith('http://') || str.startsWith('https://');
+      return false;
     }
   };

187-188: 중복 검색/네비게이션 가능성

이 페이지에서 onSubmit={handleSubmit}로 넘기면 폼 내부 검색 + 부모의 처리(그리고 폼의 router.push)가 중복 수행될 수 있습니다. ServerSearchForm이 onSubmit이 주어진 경우에는 router.push를 하지 않도록 바꾸면 중복 호출을 방지할 수 있습니다. (ServerSearchForm.tsx 수정안 참조)


217-218: 밀리초 토글 UI 중복 제거

TimeDisplay에도 토글이 있고 여기 KoreanStandardTime에도 기본 토글이 있습니다. 한 곳만 노출되도록 showToggle={false} 권장.

-      <KoreanStandardTime showMilliseconds={showMilliseconds} />
+      <KoreanStandardTime showMilliseconds={showMilliseconds} showToggle={false} />
src/components/search-result/ServerSearchForm.tsx (2)

36-38: 부모 전달 onSubmit과 라우팅이 중복 실행됨

부모가 라우팅을 담당하도록, onSubmit이 제공되면 내부에서 router.push를 수행하지 않도록 변경하세요. 재사용성도 좋아집니다.

-        // 검색된 URL로 이동
-        onSubmit?.(finalUrl);
-        router.push(`/search-result?url=${encodeURIComponent(finalUrl)}`);
+        // 검색된 URL 전달 및 필요 시 라우팅
+        if (onSubmit) {
+          onSubmit(finalUrl);
+        } else {
+          router.push(`/search-result?url=${encodeURIComponent(finalUrl)}`);
+        }

74-76: 여러 줄 에러 메시지 가독성 개선

\n을 렌더링하려면 whitespace-pre-line 클래스 추가가 필요합니다.

-        <div className="mt-4 text-red-500 text-sm text-center max-w-6xl mx-auto">
+        <div className="mt-4 text-red-500 text-sm text-center max-w-6xl mx-auto whitespace-pre-line">
src/components/search-result/AlarmModal.tsx (1)

118-141: 지난 시각 처리 UX 개선 및 parseInt 기수 지정

  • 지난 시각이면 경고 후 종료 대신 다음날로 rollover하면 UX가 좋아집니다.
  • parseInt는 기수(10)를 명시하세요.
-    targetTime.setHours(parseInt(hour));
-    targetTime.setMinutes(parseInt(minute));
-    targetTime.setSeconds(parseInt(second));
+    targetTime.setHours(parseInt(hour, 10));
+    targetTime.setMinutes(parseInt(minute, 10));
+    targetTime.setSeconds(parseInt(second, 10));
     targetTime.setMilliseconds(0);
 
     const timeUntilTarget = targetTime.getTime() - now.getTime();
 
-    if (timeUntilTarget < 0) {
-      alert('❗ 이미 지난 시간입니다. 다시 설정해 주세요.');
-      return;
-    }
+    if (timeUntilTarget < 0) {
+      // 지난 시각이면 다음날 같은 시각으로 설정
+      targetTime.setDate(targetTime.getDate() + 1);
+    }
src/components/search-result/ServerTimeResult.tsx (3)

133-135: 10ms 인터벌은 과합니다 — 30fps(≈33ms) 권장

UI 업데이트는 10ms(100fps)까지 필요하지 않습니다. 33ms로 낮춰도 충분히 부드럽고 배터리/CPU 소모를 줄입니다.

-      const timer = setInterval(() => {
+      const timer = setInterval(() => {
         setCurrentServerTime(new Date(Date.now() + timeDiff));
-      }, 10);
+      }, 33);

166-168: URL 파싱 실패 시 런타임 예외 가능

new URL(data.url)은 잘못된 URL이면 throw합니다. try/catch로 감싸고 폴백을 두세요.

-  const serverUrl = new URL(data.url);
-  const serverName = serverUrl.hostname;
+  let serverName = data.url;
+  try {
+    serverName = new URL(data.url).hostname;
+  } catch {
+    // invalid URL일 경우 원문 표시
+  }

259-267: 0값 숨김 버그: truthy 체크 대신 타입 체크 사용

networkDelay가 0이면 falsy로 처리되어 표시되지 않습니다. typeof === 'number'로 조건을 바꾸세요.

-            {data.networkInfo.networkDelay && (
+            {typeof data.networkInfo.networkDelay === 'number' && (
               <span className="ml-4">
                 네트워크 지연: {data.networkInfo.networkDelay.toFixed(1)}ms
               </span>
             )}
src/libs/api/sites.ts (1)

45-76: 네트워크 신뢰성 강화 제안(옵션)

장시간 응답 지연에 대비해 AbortController로 타임아웃을 두면 UX가 개선됩니다. 필요 시 공통 fetch wrapper로 적용 권장.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 770363c and a928c15.

📒 Files selected for processing (12)
  • src/app/bookmarks/page.tsx (2 hunks)
  • src/app/page.tsx (2 hunks)
  • src/app/result/page.tsx (0 hunks)
  • src/app/search-result/page.tsx (1 hunks)
  • src/components/AlarmModal.tsx (0 hunks)
  • src/components/ClientHeader.tsx (1 hunks)
  • src/components/ServerSearchForm.tsx (0 hunks)
  • src/components/search-result/AlarmCountdown.tsx (1 hunks)
  • src/components/search-result/AlarmModal.tsx (1 hunks)
  • src/components/search-result/ServerSearchForm.tsx (1 hunks)
  • src/components/search-result/ServerTimeResult.tsx (1 hunks)
  • src/libs/api/sites.ts (1 hunks)
💤 Files with no reviewable changes (3)
  • src/components/ServerSearchForm.tsx
  • src/components/AlarmModal.tsx
  • src/app/result/page.tsx
🧰 Additional context used
🧬 Code graph analysis (5)
src/app/search-result/page.tsx (5)
src/components/search-result/ServerTimeResult.tsx (2)
  • ServerTimeData (504-504)
  • ServerTimeResult (97-502)
src/components/search-result/AlarmModal.tsx (1)
  • AlarmData (18-22)
src/libs/api/sites.ts (1)
  • SiteAPI (45-221)
src/components/search-result/ServerSearchForm.tsx (1)
  • ServerSearchForm (14-80)
src/components/search-result/KoreanStandardTime.tsx (1)
  • KoreanStandardTime (5-133)
src/components/search-result/ServerSearchForm.tsx (3)
src/libs/api/sites.ts (1)
  • SiteAPI (45-221)
src/components/ui/Input.tsx (1)
  • Input (5-19)
src/components/ui/Button.tsx (1)
  • Button (4-18)
src/components/search-result/ServerTimeResult.tsx (2)
src/components/search-result/AlarmModal.tsx (2)
  • AlarmData (18-22)
  • AlarmModal (94-274)
src/components/search-result/AlarmCountdown.tsx (1)
  • AlarmCountdown (12-69)
src/app/page.tsx (1)
src/components/search-result/ServerSearchForm.tsx (1)
  • ServerSearchForm (14-80)
src/libs/api/sites.ts (1)
src/libs/auth.ts (1)
  • AuthUtils (3-37)

Comment on lines +94 to +101
// 1. /api/time/compare 엔드포인트 호출
const compareResponse = await fetch(
`${process.env.NEXT_PUBLIC_API_BASE}/api/time/compare`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ targetUrl: finalUrl }),
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

환경변수 미설정 시 API 호출 실패 위험

process.env.NEXT_PUBLIC_API_BASE가 비어 있으면 undefined/api/time/compare로 호출됩니다. 빈 문자열 fallback을 두고 상대 경로로 호출되게 하세요.

-      // 1. /api/time/compare 엔드포인트 호출
-      const compareResponse = await fetch(
-        `${process.env.NEXT_PUBLIC_API_BASE}/api/time/compare`,
-        {
+      // 1. /api/time/compare 엔드포인트 호출
+      const base = process.env.NEXT_PUBLIC_API_BASE ?? '';
+      const compareResponse = await fetch(
+        `${base}/api/time/compare`,
+        {
           method: 'POST',
           headers: { 'Content-Type': 'application/json' },
           body: JSON.stringify({ targetUrl: finalUrl }),
         },
       );
📝 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.

Suggested change
// 1. /api/time/compare 엔드포인트 호출
const compareResponse = await fetch(
`${process.env.NEXT_PUBLIC_API_BASE}/api/time/compare`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ targetUrl: finalUrl }),
},
// 1. /api/time/compare 엔드포인트 호출
const base = process.env.NEXT_PUBLIC_API_BASE ?? '';
const compareResponse = await fetch(
`${base}/api/time/compare`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ targetUrl: finalUrl }),
},
🤖 Prompt for AI Agents
In src/app/search-result/page.tsx around lines 94 to 101, the fetch uses
process.env.NEXT_PUBLIC_API_BASE directly which can be undefined and produce
"undefined/api/..."; update the code to default the env var to an empty string
and normalize it (trim and remove any trailing slash) before constructing the
URL so the call falls back to a relative path when the env var is not set.

Comment on lines 91 to 95
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userName, email, password }),
body: JSON.stringify({ username: userName, email, password }),
},
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

회원가입 payload 키 전환(username) — 변경 자체는 적절, 호출부 일괄 검증 요청

BE가 username(소문자)로 받는 계약과 일치합니다. 같은 엔드포인트를 호출하는 다른 곳에 userName 키가 남아 있지 않은지 확인 부탁드립니다.

다음 스크립트로 전체 레포에서 점검해 주세요:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 회원가입 엔드포인트 호출부 맥락 확인
rg -nP -C2 "api/auth/register" --type ts --type tsx

# payload에 userName 키 사용 흔적 확인
rg -nP --type ts --type tsx '\{[^}]*\buserName\s*:'

# 로그인 응답에서 user.username 사용 위치 확인
rg -nP --type ts --type tsx '\buser\.username\b'

Length of output: 27


회원가입 payload 키 전환(userName → username) — 전체 호출부 재검증 필요

rg가 'unrecognized file type: tsx' 오류로 검색이 중단됐습니다. 레포 전체에서 userName 잔여와 엔드포인트 호출을 재확인하려면 아래 스크립트로 재실행하세요.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 엔드포인트 호출 맥락 확인
rg -nP -C2 "api/auth/register" || true

# payload에 userName 키 사용 흔적 확인
rg -nP '\buserName\b' || true

# 로그인/응답에서 user.username 사용 위치 확인
rg -nP '\buser\.username\b' || true

# JSON.stringify 내부 또는 fetch 옵션에 userName/username 포함 여부 확인
rg -nP 'JSON\.stringify\([^)]*\b(?:userName|username)\b' || true
rg -nP 'fetch\([^,)]*,\s*{[^}]*\b(?:userName|username)\b' || true

Length of output: 2325


회원가입 payload 키 통일 필요 (userName → username) — 수정 필요

  • 수정 대상: src/components/auth/SignupModal.tsx:70 — 현재 payload에 userName: userName.trim() 사용, username으로 변경 필요.
  • 확인: src/components/ClientHeader.tsx는 이미 username으로 전송됨. localStorage의 'userName' 키(libs/auth.ts 등)는 내부 저장용으로 유지 가능하나, API로 전송되는 모든 payload 키는 username으로 통일할 것.
🤖 Prompt for AI Agents
In src/components/auth/SignupModal.tsx around line 70 (and note
src/components/ClientHeader.tsx lines 91-95 is already using `username`), the
signup payload currently uses the key `userName`; change that key to `username`
when building the JSON body (e.g., username: userName.trim()) so the API payload
matches ClientHeader's `username` key; keep localStorage key 'userName'
unchanged if used only for internal storage, but ensure every network request
sends `username`.

@@ -0,0 +1,221 @@
import { AuthUtils } from '@/libs/auth';

const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

환경변수 미설정 시 모든 API가 깨짐

NEXT_PUBLIC_API_BASE가 비면 undefined/...로 호출됩니다. 빈 문자열 fallback 및 트레일링 슬래시 제거를 권장합니다.

-const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE;
+const API_BASE_URL = (process.env.NEXT_PUBLIC_API_BASE ?? '').replace(/\/$/, '');
📝 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.

Suggested change
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE;
const API_BASE_URL = (process.env.NEXT_PUBLIC_API_BASE ?? '').replace(/\/$/, '');
🤖 Prompt for AI Agents
In src/libs/api/sites.ts around line 3, the API base constant is assigned
directly from process.env.NEXT_PUBLIC_API_BASE which can be undefined and
produce requests like "undefined/…"; change it to default to an empty string
when the env var is missing and normalize by trimming whitespace and removing
any trailing slash so callers get a stable base (e.g. use
(process.env.NEXT_PUBLIC_API_BASE ?? '').trim().replace(/\/+$/, '') to produce a
safe API_BASE_URL).

@hannah0352 hannah0352 merged commit 566b922 into main Sep 20, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

design✨ UI 구현 및 개선

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant