Skip to content

Conversation

@hannah0352
Copy link
Collaborator

@hannah0352 hannah0352 commented Aug 15, 2025

📌 작업 내용

기존 URL 직접 입력 방식에 키워드 검색 기능을 추가하였습니다.
URL이 아니면, /api/sites?search=키워드 API를 호출하여 사이트를 검색합니다.

Summary by CodeRabbit

  • 새 기능

    • 키워드 또는 URL 입력을 모두 지원해 대상 사이트를 자동으로 해석하고 최종 대상 URL로 요청을 전송합니다.
    • 검색 키워드로 결과가 없을 때 예시 키워드를 포함한 안내를 제공합니다.
    • 전체 처리 소요 시간을 화면에 표시합니다.
  • 버그 수정

    • 입력 유효성 검사와 오류 안내를 개선하여 잘못된 입력 시 명확한 메시지를 제공합니다.
    • 실패 시에도 입력/대상 정보가 결과에 남아 추적성을 높였습니다.

@coderabbitai
Copy link

coderabbitai bot commented Aug 15, 2025

Walkthrough

입력 문자열을 URL로 검증(isValidUrl)하거나 키워드로 검색(getUrlFromKeyword)해 최종 대상 URL을 결정하고, 해당 URL로 /api/time/compare를 호출하며 클라이언트 처리 시간(interval)을 기록하고 에러 메시지/UI를 업데이트하도록 src/app/result/page.tsx가 변경되었습니다.

Changes

Cohort / File(s) Change Summary
결과 페이지 제출 및 URL 해석 흐름
src/app/result/page.tsx
- isValidUrl 추가로 입력 URL 검증
- Site 인터페이스 및 getUrlFromKeyword(keyword) 구현 (로컬 API /api/sites 조회, 최대 5개, 대소문자 무시한 정확 매칭 우선)
- handleSubmit(input: string) 재구성: 입력을 URL 또는 키워드로 해석해 finalUrl 결정; 실패 시 샘플 키워드 포함 에러 설정 및 중단
- /api/time/compare 호출에 targetUrl: finalUrl 사용
- 성공/오류/예외 경로에서 serverTimeData.url 업데이트(성공·오류: finalUrl, 예외: 원입력값)
- 클라이언트 처리 총시간(startTime→endTime)을 serverTimeData.interval에 기록
- ServerSearchForm 호출 흐름을 수정해 키워드 검색 결과를 handleSubmit에 전달하도록 연동
- 동일 로직을 코드의 두 유사 섹션에 병렬 적용(중복 반영)

Sequence Diagram(s)

sequenceDiagram
  participant U as 사용자
  participant R as ResultPage (handleSubmit)
  participant V as isValidUrl
  participant S as /api/sites (getUrlFromKeyword)
  participant T as /api/time/compare
  U->>R: 제출(input)
  R->>V: isValidUrl(input)?
  alt 입력이 유효한 URL
    V-->>R: true
    R->>T: POST /api/time/compare { targetUrl: input }
  else 키워드 처리
    V-->>R: false
    R->>S: GET /api/sites?keyword=input&limit=5
    alt 검색 결과 존재
      S-->>R: 사이트 목록
      R->>R: finalUrl 결정(정확 매칭 우선 또는 첫 결과)
      R->>T: POST /api/time/compare { targetUrl: finalUrl }
    else 결과 없음
      S-->>R: 빈 목록
      R-->>U: 에러 메시지(샘플 키워드 포함)
    end
  end
  T-->>R: 비교 결과 / 에러
  R->>R: endTime 측정 및 interval 계산
  R-->>U: serverTimeData (url, interval, 결과/에러)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15–25 minutes

Poem

귀가 쫑긋, 키워드 콕! URL 한 줌 찾아요 🐇
시계는 똑딱, interval 쿵쿵 재요 ⏱️
못 찾으면 예시 건네고, 로그에 살짝 웃음—
API로 건너뛰어, 결과 페이지로 hop!

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 498f8e1 and 88757de.

📒 Files selected for processing (1)
  • src/app/result/page.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/result/page.tsx
✨ 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-result

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 2

🔭 Outside diff range comments (1)
src/app/result/page.tsx (1)

185-187: 오류 상태에서 URL 파싱으로 런타임 예외 발생 가능

data.error가 있는 경우에도 new URL(data.url)이 먼저 실행되어 키워드 입력(예: "무신사")처럼 URL이 아닌 값이면 즉시 예외가 발생합니다. 오류 UI를 렌더링하기 전에 크래시가 나므로 가드시 필요합니다.

아래처럼 hostname 계산을 안전하게 처리하세요.

-  const serverUrl = new URL(data.url);
-  const serverName = serverUrl.hostname;
+  let serverName = data.url;
+  try {
+    serverName = new URL(data.url).hostname;
+  } catch {
+    // URL이 아닌 경우 그대로 표시 (키워드 등)
+  }
🧹 Nitpick comments (11)
src/app/result/page.tsx (11)

342-350: isValidUrl 판정 기준이 모호합니다 (잘못된 URL도 true가 될 수 있음)

catch 블록에서 http/https 접두사만 검사해 true를 반환하므로 "http://" 같은 불완전한 입력도 유효로 처리됩니다. 엄격한 유효성 검증이 목적이라면 false를 반환하도록 하거나, 함수명을 isLikelyUrl 같은 의도 표현으로 바꾸는 것을 권장합니다.

옵션 A(엄격 검증) 적용 예:

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

옵션 B(의도 명확화): 함수명을 isLikelyUrl로 변경하고 현재 로직 유지(선호 시).


352-365: 타입 정의 위치 정리 제안 (선택)

Site 인터페이스가 컴포넌트 내부에 정의되어 있어 재사용성이 떨어집니다. 반복 사용 가능성이 있으니 types 디렉터리로 분리하거나 api 응답 타입과 함께 정의해 두면 유지보수에 유리합니다.


369-389: 과도한 콘솔 로그는 운영 환경에서 노이즈가 될 수 있습니다

키워드 검색 시작/응답/목록 전체를 콘솔에 출력하고 있어 운영 빌드에서 불필요한 노이즈가 발생할 수 있습니다. NODE_ENV 체크로 개발 모드에서만 출력하도록 하거나, 디버그 로거를 사용해 토글 가능하게 해 주세요.

-  console.log(`키워드 검색 시작: "${keyword}"`);
+  if (process.env.NODE_ENV !== 'production') {
+    console.log(`키워드 검색 시작: "${keyword}"`);
+  }
...
-  console.log('검색 API 응답:', data);
+  if (process.env.NODE_ENV !== 'production') {
+    console.log('검색 API 응답:', data);
+  }
...
-  console.log(`"${keyword}" 검색 결과 (${sites.length}개):`);
+  if (process.env.NODE_ENV !== 'production') {
+    console.log(`"${keyword}" 검색 결과 (${sites.length}개):`);
+  }

397-408: API가 반환하는 URL이 스킴 없이 오면 후속 단계에서 실패할 수 있습니다

검색 결과의 url 필드가 스킴을 포함하지 않는다면(예: "example.com") new URL(...)이나 백엔드에서 URL 유효성 검사 시 실패할 수 있습니다. 반환 전에 https:// 스킴을 보정해 주세요.

+      const toAbsoluteUrl = (url: string) =>
+        /^https?:\/\//i.test(url) ? url : `https://${url}`;
...
-        if (exactMatch) {
+        if (exactMatch) {
           console.log(`정확 매치 발견: ${exactMatch.name} - ${exactMatch.url}`);
-          return exactMatch.url;
+          return toAbsoluteUrl(exactMatch.url);
         }
...
-        return firstResult.url;
+        return toAbsoluteUrl(firstResult.url);

426-446: 키워드 미발견 케이스의 에러 메시지 표시는 줄바꿈 적용이 필요합니다

현재 p 태그는 기본적으로 줄바꿈을 무시합니다. "\n"이 포함된 메시지를 의도대로 보여주려면 whitespace-pre-line 클래스를 추가하세요.

아래 위치에 적용(렌더링 블록):

-        <p className="text-gray-600 mb-6">{data.error}</p>
+        <p className="text-gray-600 mb-6 whitespace-pre-line">{data.error}</p>

(라인 191-196 부근)


---

`489-494`: **에러 응답 문구 구성은 OK, 다만 API 비정상 응답 사유를 상세화하면 디버깅에 유리합니다**

status/statusText만이 아니라 응답 바디의 에러 메시지를 함께 포함하는 것을 권장합니다(가능 시).

```diff
-        error: `API 통신 오류: ${compareResponse.status} ${compareResponse.statusText}`,
+        error: `API 통신 오류: ${compareResponse.status} ${compareResponse.statusText}`,

추가로:

  • compareResponse.text()를 별도 로깅하거나
  • 서버에서 에러 바디를 표준화해 message를 추출

498-504: catch 블록에서 url: input 사용은 의도적이지만, 일관성 측면에서 finalUrl 우선 표시도 고려해 보세요

키워드 매칭이 성공한 뒤 네트워크 예외가 난 경우, 사용자 입장에서는 최종 대상(finalUrl)을 보는 편이 더 유익할 수 있습니다.

-        url: input,
+        url: finalUrl ?? input,

289-293: 0ms networkDelay가 숨겨질 수 있습니다

truthy 체크로 0이 렌더링되지 않습니다. null/undefined만 거르는 비교를 사용하세요.

-            {data.networkInfo.networkDelay && (
+            {data.networkInfo.networkDelay != null && (
               <span className="ml-4">
                 네트워크 지연: {data.networkInfo.networkDelay.toFixed(1)}ms
               </span>
             )}

510-528: useEffect가 두 역할(초기 요청 트리거 + 이벤트 리스너 등록)을 동시에 수행합니다

의존성(initialUrl) 변화 때마다 이벤트 리스너가 재등록됩니다. 역할을 분리해 불필요한 attach/detach를 줄이는 편이 깔끔합니다.

분리 예시(참고 코드):

// 1) 이벤트 리스너 등록
useEffect(() => {
  const listener = (e: Event) => {
    const customEvent = e as CustomEvent<boolean>;
    if (typeof customEvent.detail === 'boolean') {
      setShowMilliseconds(customEvent.detail);
    }
  };
  document.addEventListener('toggleMilliseconds', listener);
  return () => document.removeEventListener('toggleMilliseconds', listener);
}, []);

// 2) 초기 URL 처리
useEffect(() => {
  if (initialUrl && typeof initialUrl === 'string' && initialUrl.trim() !== '') {
    handleSubmit(initialUrl);
  }
}, [initialUrl]);

170-179: getTimeDifferenceText: direction 변수 미사용 및 이중 절대값 처리

direction은 빈 문자열로만 설정되고, 호출부에서 Math.abs를 한 번 더 적용합니다. 간소화가 가능합니다.

-    const absDiff = Math.abs(diff);
-    const direction = diff > 0 ? '' : '';
-
-    if (absDiff < 1000) {
-      return `${direction}${diff.toFixed(2)}ms`;
+    const absDiff = Math.abs(diff);
+    if (absDiff < 1000) {
+      return `${absDiff.toFixed(2)}ms`;
     } else {
-      return `${direction}${(diff / 1000).toFixed(2)}초 (±${(
-        absDiff / 1000
-      ).toFixed(2)}초)`;
+      return `${(absDiff / 1000).toFixed(2)}초 (±${(absDiff / 1000).toFixed(2)}초)`;
     }

545-545: 중복 요청 가능성: onSubmit 호출 + Router push 모두 발생합니다

ServerSearchForm은 자체적으로 router.push(/result?url=...)를 수행합니다(참고: src/components/ServerSearchForm.tsx 12-45). result 페이지에서도 onSubmit으로 handleSubmit을 바로 호출하면:

  • 즉시 1회 호출
  • URL 쿼리 변경으로 useEffect(initialUrl) 통해 다시 1회 호출
    총 2회 요청이 발생할 가능성이 큽니다.

간단히 onSubmit을 제거하고 URL 변경 트리거만 사용하세요:

-        <ServerSearchForm onSubmit={(input) => handleSubmit(input)} />
+        <ServerSearchForm />

원한다면 ServerSearchForm에 prop으로 "push를 비활성화"하는 옵션을 추가해 역할을 더 명확히 분리할 수도 있습니다. 중복 호출 여부를 네트워크 탭에서 확인 부탁드립니다.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a8115f7 and 498f8e1.

📒 Files selected for processing (1)
  • src/app/result/page.tsx (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/app/result/page.tsx (1)
src/components/ServerSearchForm.tsx (1)
  • ServerSearchForm (13-46)
🔇 Additional comments (1)
src/app/result/page.tsx (1)

418-425: 핸들러 리팩토링과 처리 시간 측정 추가 좋습니다

입력 단일 진입점(handleSubmit)으로 정리하고 clientTime/interval을 기록하는 구조는 가독성과 추적성 모두 향상됩니다. 👍

Comment on lines +371 to +377
const response = await fetch(
`http://localhost:3001/api/sites?search=${encodeURIComponent(
keyword.trim(),
)}&limit=5`,
);
const data = await response.json();

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

클라이언트에서 localhost 절대 경로 사용 — 배포/HTTPS에서 실패합니다

http://localhost:3001 고정 호출은 배포 환경에서 실패(도메인 불일치)하고, HTTPS 페이지에서는 Mixed Content 차단됩니다. 같은 오리진의 Next API 라우트를 프록시로 쓰거나 환경변수를 사용하세요. 또한 response.ok 체크가 없어 4xx/5xx에서 JSON 파싱 에러 가능성이 있습니다.

-      const response = await fetch(
-        `http://localhost:3001/api/sites?search=${encodeURIComponent(
-          keyword.trim(),
-        )}&limit=5`,
-      );
+      const response = await fetch(
+        `/api/sites?search=${encodeURIComponent(keyword.trim())}&limit=5`,
+      );
+      if (!response.ok) {
+        console.error(`검색 API 오류: ${response.status} ${response.statusText}`);
+        return null;
+      }
       const data = await response.json();
📝 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 response = await fetch(
`http://localhost:3001/api/sites?search=${encodeURIComponent(
keyword.trim(),
)}&limit=5`,
);
const data = await response.json();
const response = await fetch(
`/api/sites?search=${encodeURIComponent(keyword.trim())}&limit=5`,
);
if (!response.ok) {
console.error(`검색 API 오류: ${response.status} ${response.statusText}`);
return null;
}
const data = await response.json();
🤖 Prompt for AI Agents
In src/app/result/page.tsx around lines 371 to 377, the code is calling a
hard-coded http://localhost:3001 which will fail in deployed or HTTPS
environments and also doesn't check response.ok; change the fetch to use a
same-origin Next.js API route (e.g. /api/sites?search=...) or build the base URL
from an environment variable (NEXT_PUBLIC_API_URL) so it works in
production/HTTPS, and add a response.ok check before calling response.json() to
throw or handle HTTP errors (and handle exceptions from fetch/json parsing).

@hannah0352 hannah0352 closed this Aug 15, 2025
@hannah0352 hannah0352 reopened this Aug 15, 2025
@hannah0352 hannah0352 merged commit d969f64 into main Aug 15, 2025
1 check passed
@hannah0352 hannah0352 added the feat🛠️ 기능 구현 label Aug 15, 2025
@coderabbitai coderabbitai bot mentioned this pull request Sep 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat🛠️ 기능 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants