Skip to content

Feat/45#46

Merged
kim-subsub merged 7 commits into
developfrom
feat/45
Jun 3, 2026
Merged

Feat/45#46
kim-subsub merged 7 commits into
developfrom
feat/45

Conversation

@kim-subsub

@kim-subsub kim-subsub commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

#45

Tasks

  • url 검색 기능 추가
  • 위협 설명 최신화
  • v3등 조치사항에 대한 버튼 생성

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • URL 직접 입력을 통한 보안 위협 검사 기능 추가
    • 보고서 페이지에 V3 설치 버튼 추가
    • 분석 실패 시 사용자 알림 표시 기능 추가
  • 개선 사항

    • 스캔 히스토리에서 더욱 상세한 위험 정보 확인 가능

@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
veri-q Ready Ready Preview, Comment Jun 3, 2026 2:27am

@kim-subsub kim-subsub self-assigned this Jun 3, 2026
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kim-subsub, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 40 minutes and 43 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9651dab2-435f-436b-8232-15a473419942

📥 Commits

Reviewing files that changed from the base of the PR and between af9b406 and 2ab3197.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • package.json
  • src/features/scan-history/api/fetchScanHistoryData.test.ts
  • src/features/scan-history/api/fetchScanHistoryData.ts
  • src/pages/Loading/hooks/useLoadingPage.ts
  • src/pages/QRScan/hooks/useQRScanPage.test.ts
  • src/pages/QRScan/hooks/useQRScanPage.ts
  • src/pages/Report/ReportPage.tsx
  • src/pages/Report/constants/riskDetectionCatalog.test.ts
  • src/pages/Report/constants/threatText.ts
  • src/shared/store/scanSessionStore.ts

워크스루

이 PR은 사용자가 QRScan 페이지에서 URL을 직접 입력하여 스캔하는 기능을 추가하고, Report 페이지에 분석 실패 알림과 V3 설치 버튼을 추가하며, 스캔 히스토리에서 세부 정보를 기반으로 위험 상태를 강화하는 기능을 구현합니다.

변경 사항

텍스트 URL 스캔 및 보고서 향상

Layer / File(s) Summary
세션 저장소 및 API 기반 인프라
src/shared/store/scanSessionStore.ts, src/shared/store/scanSessionTransitions.ts, src/shared/api/endpoints.ts, src/shared/api/responseAccess/payloadAccess.ts, src/shared/lib/sse/useScanSubscription.ts, src/shared/store/scanSessionStore.test.ts, src/shared/store/scanSessionTransitions.test.ts
pendingTextScanUrl 필드를 세션 스냅샷에 추가하고, clearPendingTextScanUrlsetPendingTextScanUrl 액션을 구현하며, 스캔 응답 처리 시 해당 필드를 정리합니다. /api/v1/scan/text 엔드포인트를 추가하고, SSE 구독에 onOpen 콜백 옵션을 추가하며, 응답 페이로드 탐색 범위를 확대합니다.
QRScan 페이지 URL 텍스트 입력 UI
src/pages/QRScan/hooks/useQRScanPage.ts, src/pages/QRScan/QRScanPage.tsx, src/pages/QRScan/styles/qrScanPage.css.ts
URL 입력값 정규화(normalizeUrlInput) 로직을 추가하고, 입력 변경 및 제출 핸들러(handleUrlInputChange, handleSubmitUrlScan)를 구현하며, URL 검색 폼, 입력 필드, 버튼 및 스크린리더 숨김 스타일을 정의합니다.
URL 스캔 제출 API 및 Loading 페이지 처리
src/features/scan-url/api/submitScanUrl.ts, src/pages/Loading/hooks/useLoadingPage.ts
submitScanUrl 함수로 URL을 BE3에 전송하고, Loading 페이지의 submitPendingTextScan 콜백에서 SSE 열림 이벤트 시 URL 제출을 트리거하며, 성공/실패/캡차 필요 시나리오를 각각 처리합니다.
분석 실패 알림 시스템
src/pages/Report/constants/riskDetectionCatalog.ts, src/pages/Report/constants/threatText.ts, src/pages/Report/lib/analysisFailureNotice.ts, src/pages/Report/ReportPage.tsx, src/pages/Report/styles/reportPage.css.ts, src/pages/Report/constants/riskDetectionCatalog.test.ts, src/pages/Report/lib/analysisFailureNotice.test.ts
위협 코드를 한글 라벨로 변환하는 resolveRiskTypeLookupKeysresolveAnalysisFailureNotice 함수를 추가하고, 분석 실패 라벨 매핑을 구성하며, Report 페이지에서 role="alert" 섹션으로 조건부 렌더링하고, 관련 스타일을 정의합니다. 다양한 위협 코드(CERT, GSB, OTX, REDIRECT, 머신러닝 실패 등)를 카탈로그에 추가합니다.
V3 설치 버튼 및 환경 설정
.env.example, src/pages/Report/ReportPage.tsx, src/pages/Report/styles/reportPage.css.ts
환경 변수 VITE_V3_INSTALL_URL을 추가하고, Report 페이지 내보내기 영역에 V3 설치 버튼을 추가하며, 버튼과 안내 문구 스타일을 정의합니다.
스캔 히스토리 detail 기반 enrichment
src/features/scan-history/api/fetchScanHistoryData.ts, src/features/scan-history/api/fetchScanHistoryData.test.ts
FetchScanHistoryDataOptionsenrichRiskFromDetail 플래그를 추가하고, 웹 대상의 히스토리 항목 상태를 fetchScanDetail로 부터 가져오는 resolveDetailRiskLevel, enrichHistoryItemRiskLevel 헬퍼를 구현하며, fetchRecentScanHistoryData에서 enrichment를 활성화합니다.
테스트 및 개발 설정 업데이트
src/pages/Report/lib/reportPageDataContext.test.ts, src/pages/Report/lib/toReportPageData.test.ts, src/pages/Result/lib/toResultPageData.test.ts, src/pages/ResultNonUrl/lib/toResultNonUrlPageData.test.ts, src/shared/api/responseAccess/payloadAccess.test.ts, vite.config.ts
테스트 스냅샷에 pendingTextScanUrl: null 필드를 추가하여 세션 구조를 일관되게 유지하고, 개발 서버 프록시 대상 결정 로직을 resolveDevProxyTarget 헬퍼로 개선하며, 응답 페이로드 탐색 테스트를 확장합니다.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive PR 제목 'Feat/45'는 너무 모호하고 일반적이며, 변경 사항의 주요 내용을 명확하게 전달하지 못합니다. 구체적인 기능 설명을 포함한 제목으로 변경하세요. 예: 'Add URL search functionality and enhance threat detection' 또는 'Implement URL scanning and analysis failure notices'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (2)
src/shared/store/scanSessionStore.ts (1)

82-103: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

pendingTextScanUrl도 persist/rehydrate 경로에 포함해 주세요.

지금 구현은 setPendingTextScanUrl()로 텍스트 URL을 세션 상태에 넣지만, partialize()mergePersistedLightSession() 어디에서도 이 필드를 저장/복원하지 않습니다. src/pages/QRScan/hooks/useQRScanPage.ts:573-597는 이 값을 넣고 /loading으로 이동하고, src/pages/Loading/hooks/useLoadingPage.ts:138-167pendingTextScanUrl이 있을 때만 submitScanUrl()을 호출합니다. 그래서 로딩 진입 전후에 새로고침이 발생하면 decodedUrl만 복원되고 실제 제출 트리거는 사라져서 텍스트 URL 스캔이 재개되지 않습니다.

🔧 제안 수정
 function mergePersistedLightSession(
   persistedState: unknown,
   currentState: ScanSessionState,
 ): ScanSessionState {
@@
   return {
     ...currentState,
     decodedUrl: typeof persisted.decodedUrl === 'string' ? persisted.decodedUrl : null,
     historySelection: persisted.historySelection ?? null,
     isUrl: typeof persisted.isUrl === 'boolean' ? persisted.isUrl : null,
+    pendingTextScanUrl:
+      typeof persisted.pendingTextScanUrl === 'string' ? persisted.pendingTextScanUrl : null,
     riskLevel: persisted.riskLevel ?? null,
     schemeType:
       typeof persisted.schemeType === 'string'
         ? normalizeScanSchemeTypeAlias(persisted.schemeType)
         : null,
@@
       partialize: (state) => ({
         decodedUrl: state.decodedUrl,
         historySelection: state.historySelection,
         isUrl: state.isUrl,
+        pendingTextScanUrl: state.pendingTextScanUrl,
         riskLevel: state.riskLevel,
         schemeType: state.schemeType,
       }),

Also applies to: 156-163

🤖 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 `@src/shared/store/scanSessionStore.ts` around lines 82 - 103, The persisted
snapshot is missing the pendingTextScanUrl field so the text-scan submit trigger
is lost on rehydrate; update the persistence and merge logic to include
pendingTextScanUrl: add pendingTextScanUrl to the list returned by partialize
(so setPendingTextScanUrl is serialized) and restore it in
mergePersistedLightSession by reading persisted.pendingTextScanUrl with a proper
type check (string|null/undefined) and assigning it into the returned
ScanSessionState; reference pendingTextScanUrl, partialize,
mergePersistedLightSession, ScanSessionSnapshot and ScanSessionState when making
these changes.
src/pages/Loading/hooks/useLoadingPage.ts (1)

69-83: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

scanResponse 갱신으로 진행 상태를 다시 초기화하면 안 됩니다.

텍스트 URL 플로우에서는 SSE progress가 submitScanUrl() 응답보다 먼저 올 수 있는데, 그 뒤 Line 70 조건이 다시 타면서 resetProgress()/setConnecting()이 이미 표시 중인 진행 상태를 "연결 중"으로 덮어씁니다. detailResolutionFailedRefdetailResolutionStartedRef도 같이 초기화돼서 terminal progress 이후의 상세 조회 상태까지 흔들릴 수 있습니다. 이 초기화는 새 스캔을 시작할 때만 실행되도록 scanResponse 변화와 분리하는 편이 안전합니다.

🤖 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 `@src/pages/Loading/hooks/useLoadingPage.ts` around lines 69 - 83, The effect
is wrongly resetting progress on any scanResponse update; split the logic so
scanResponse changes do not trigger
resetProgress/detailResolutionFailedRef/detailResolutionStartedRef/setConnecting.
Keep the existing useEffect that depends on scanResponse to handle navigation
and the isSameScanSource early return, but move the
resetProgress/detailResolutionFailedRef.current =
false/detailResolutionStartedRef.current = false and setConnecting() into a new
useEffect that only depends on pendingTextScanUrl, finalResult (and the
setters/resetProgress refs) and only runs when starting a new scan (e.g., when
pendingTextScanUrl is truthy or finalResult transitions to a new scan), ensuring
scanResponse is not in that dependency list so SSE progress updates won't
overwrite the current progress UI.
🤖 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 `@src/features/scan-history/api/fetchScanHistoryData.ts`:
- Around line 125-127: The current code blocks fetchRecentScanHistoryData on
await Promise.all(...) when options.enrichRiskFromDetail is true; instead,
return the base historyItems immediately and kick off enrichment in the
background: don't await Promise.all for enrichHistoryItemRiskLevel, start an
async task (using Promise.allSettled) that maps enriched results back to history
item ids and then emits/updates via the same update path (e.g., call the
consumer callback or setScanHistoryItems) when enrichment completes; ensure you
reference options.enrichRiskFromDetail, enrichedHistoryItems, and
enrichHistoryItemRiskLevel and handle failures gracefully so a slow detail fetch
updates status later without blocking initial render.

In `@src/features/scan-url/api/submitScanUrl.ts`:
- Around line 10-16: submitScanUrl currently sends the user-provided URL in the
GET query (axiosBe3.get to apiEndpoints.scanText), which leaks sensitive tokens
to logs; change it to use a POST request (axiosBe3.post) sending { url } in the
request body instead of params, preserve the timeout by passing the same
getUploadTimeoutMs() option, keep the response generic type BackendScanResponse,
and update any calling code or backend contract if necessary to accept POST/body
for submitScanUrl.

In `@src/pages/QRScan/hooks/useQRScanPage.ts`:
- Around line 143-165: The normalizeUrlInput function currently prepends
"https://" to any input that doesn't start with http(s) which transforms
non-HTTP schemes like "ftp://..." into a different URL; change the logic to
first detect if the input already contains a scheme (e.g. using a scheme regex
like ^[a-zA-Z][\w+.-]*://) and if a scheme exists only accept it when
parsedUrl.protocol is 'http:' or 'https:' (otherwise return null), but if no
scheme exists then prepend 'https://' and continue parsing as before; keep error
handling in the try/catch and return null on parse failure so only true HTTP(S)
targets are normalized and accepted.

In `@src/pages/Report/constants/threatText.ts`:
- Around line 400-405: The CERT TIMEOUT entry (title '인증서 검증 시간 초과 감지',
englishLabel 'CERT TIMEOUT') and the separate CERTIFICATE REQUEST TIMEOUT entry
both register the same aliases (e.g., 'certificate_timeout',
'tls_certificate_timeout'), causing the later entry to shadow the earlier one
when building riskDetectionLookup; fix by consolidating these into a single
canonical entry or removing the duplicate aliases from one of them so each alias
appears exactly once (update the names arrays on the CERT TIMEOUT and/or
CERTIFICATE REQUEST TIMEOUT objects to remove duplication and ensure
riskDetectionLookup maps the alias to the intended description).

In `@src/pages/Report/ReportPage.tsx`:
- Around line 56-58: The V3 install button uses V3_INSTALL_URL and disables
based only on empty string, so if VITE_V3_INSTALL_URL is unsafe
openExternalLink() returns false and the button appears active but does nothing;
fix by validating the URL at load: run the same safety check used by
openExternalLink (or extract a helper like isSafeExternalUrl(url)) for
V3_INSTALL_URL, and if the check fails set V3_INSTALL_URL =
DEFAULT_V3_INSTALL_URL (or set a boolean isV3UrlSafe = false) and use that
boolean to disable the button; update both the V3_INSTALL_URL initialization and
the button disabled logic (references: V3_INSTALL_URL, DEFAULT_V3_INSTALL_URL,
openExternalLink, and the install button rendering) so unsafe overrides won’t
leave the button clickable but inert.

---

Outside diff comments:
In `@src/pages/Loading/hooks/useLoadingPage.ts`:
- Around line 69-83: The effect is wrongly resetting progress on any
scanResponse update; split the logic so scanResponse changes do not trigger
resetProgress/detailResolutionFailedRef/detailResolutionStartedRef/setConnecting.
Keep the existing useEffect that depends on scanResponse to handle navigation
and the isSameScanSource early return, but move the
resetProgress/detailResolutionFailedRef.current =
false/detailResolutionStartedRef.current = false and setConnecting() into a new
useEffect that only depends on pendingTextScanUrl, finalResult (and the
setters/resetProgress refs) and only runs when starting a new scan (e.g., when
pendingTextScanUrl is truthy or finalResult transitions to a new scan), ensuring
scanResponse is not in that dependency list so SSE progress updates won't
overwrite the current progress UI.

In `@src/shared/store/scanSessionStore.ts`:
- Around line 82-103: The persisted snapshot is missing the pendingTextScanUrl
field so the text-scan submit trigger is lost on rehydrate; update the
persistence and merge logic to include pendingTextScanUrl: add
pendingTextScanUrl to the list returned by partialize (so setPendingTextScanUrl
is serialized) and restore it in mergePersistedLightSession by reading
persisted.pendingTextScanUrl with a proper type check (string|null/undefined)
and assigning it into the returned ScanSessionState; reference
pendingTextScanUrl, partialize, mergePersistedLightSession, ScanSessionSnapshot
and ScanSessionState when making these changes.
🪄 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

Run ID: db8a5244-88e6-4283-8753-a96f719ef4c4

📥 Commits

Reviewing files that changed from the base of the PR and between 3a6e4f7 and af9b406.

📒 Files selected for processing (28)
  • .env.example
  • src/features/scan-history/api/fetchScanHistoryData.test.ts
  • src/features/scan-history/api/fetchScanHistoryData.ts
  • src/features/scan-url/api/submitScanUrl.ts
  • src/pages/Loading/hooks/useLoadingPage.ts
  • src/pages/QRScan/QRScanPage.tsx
  • src/pages/QRScan/hooks/useQRScanPage.ts
  • src/pages/QRScan/styles/qrScanPage.css.ts
  • src/pages/Report/ReportPage.tsx
  • src/pages/Report/constants/riskDetectionCatalog.test.ts
  • src/pages/Report/constants/riskDetectionCatalog.ts
  • src/pages/Report/constants/threatText.ts
  • src/pages/Report/lib/analysisFailureNotice.test.ts
  • src/pages/Report/lib/analysisFailureNotice.ts
  • src/pages/Report/lib/reportPageDataContext.test.ts
  • src/pages/Report/lib/toReportPageData.test.ts
  • src/pages/Report/styles/reportPage.css.ts
  • src/pages/Result/lib/toResultPageData.test.ts
  • src/pages/ResultNonUrl/lib/toResultNonUrlPageData.test.ts
  • src/shared/api/endpoints.ts
  • src/shared/api/responseAccess/payloadAccess.test.ts
  • src/shared/api/responseAccess/payloadAccess.ts
  • src/shared/lib/sse/useScanSubscription.ts
  • src/shared/store/scanSessionStore.test.ts
  • src/shared/store/scanSessionStore.ts
  • src/shared/store/scanSessionTransitions.test.ts
  • src/shared/store/scanSessionTransitions.ts
  • vite.config.ts

Comment thread src/features/scan-history/api/fetchScanHistoryData.ts Outdated
Comment thread src/features/scan-url/api/submitScanUrl.ts
Comment thread src/pages/QRScan/hooks/useQRScanPage.ts Outdated
Comment thread src/pages/Report/constants/threatText.ts
Comment thread src/pages/Report/ReportPage.tsx
@kim-subsub kim-subsub merged commit d676ed3 into develop Jun 3, 2026
8 checks passed
@kim-subsub kim-subsub deleted the feat/45 branch June 3, 2026 02:30
@kim-subsub kim-subsub restored the feat/45 branch June 3, 2026 07:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant