-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 키워드 검색 기능 추가 #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Walkthrough입력 문자열을 URL로 검증(isValidUrl)하거나 키워드로 검색(getUrlFromKeyword)해 최종 대상 URL을 결정하고, 해당 URL로 /api/time/compare를 호출하며 클라이언트 처리 시간(interval)을 기록하고 에러 메시지/UI를 업데이트하도록 src/app/result/page.tsx가 변경되었습니다. Changes
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, 결과/에러)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~15–25 minutes Poem
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 detailsConfiguration used: .coderabbit.yaml 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this 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.
📒 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을 기록하는 구조는 가독성과 추적성 모두 향상됩니다. 👍
| const response = await fetch( | ||
| `http://localhost:3001/api/sites?search=${encodeURIComponent( | ||
| keyword.trim(), | ||
| )}&limit=5`, | ||
| ); | ||
| const data = await response.json(); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
클라이언트에서 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.
| 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).
…eckTime-Frontend into feature/search-result
📌 작업 내용
기존 URL 직접 입력 방식에 키워드 검색 기능을 추가하였습니다.
URL이 아니면, /api/sites?search=키워드 API를 호출하여 사이트를 검색합니다.
Summary by CodeRabbit
새 기능
버그 수정