Skip to content

feat: 스캔 내역 클릭 시 상세 보고서 페이지 연결#37

Merged
kim-subsub merged 2 commits into
developfrom
feat/36
May 3, 2026
Merged

feat: 스캔 내역 클릭 시 상세 보고서 페이지 연결#37
kim-subsub merged 2 commits into
developfrom
feat/36

Conversation

@Ahreum02

@Ahreum02 Ahreum02 commented May 3, 2026

Copy link
Copy Markdown
Contributor

Tasks

  • _스캔 내역 클릭 시 상세 보고서 페이지가 나오지 않는 오류 수정

Summary by CodeRabbit

버그 수정 및 개선

  • 버그 수정

    • 웹 스캔과 비웹 스캔 분류 로직 개선으로 더욱 일관된 동작 제공
    • URL 스킴 정규화 및 검증 강화
    • 스캔 결과 페이지 네비게이션 안정성 향상
    • HTTP(S) URL 인식 및 히스토리 항목 처리 최적화
  • 테스트

    • 스캔 분류 기능에 대한 테스트 커버리지 추가

@vercel

vercel Bot commented May 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 May 3, 2026 3:01am

@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Ahreum02 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes and 52 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, 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 have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 27cbaacc-9655-4cb6-84f1-a72c11d602bd

📥 Commits

Reviewing files that changed from the base of the PR and between 2e57698 and 5f48bba.

📒 Files selected for processing (5)
  • src/pages/QRScan/hooks/useQRScanPage.ts
  • src/pages/ScanList/ScanListPage.tsx
  • src/shared/lib/scan-session/scanClassification.test.ts
  • src/shared/lib/scan-session/scanClassification.ts
  • src/shared/store/scanSessionStore.ts

Walkthrough

스캔 분류 로직을 통합하는 리팩토링으로, 새로운 scanClassification 모듈에서 스킴 타입 정규화와 웹 스캔 대상 검증을 중앙화합니다. 페이지 훅과 스토어는 이제 공유 헬퍼를 통해 일관되게 웹 대상을 분류하고, 라우팅 및 상태 관리 로직을 단순화합니다.

Changes

스캔 분류 통합

Layer / File(s) Summary
분류 헬퍼 정의
src/shared/lib/scan-session/scanClassification.ts
normalizeScanSchemeTypeAlias, isHttpUrl, isWebScanTarget 함수를 추가하여 스킴 정규화 및 웹 대상 검증을 표준화합니다.
저장소 통합
src/shared/store/scanSessionStore.ts
리하이드레이션 및 스킴/URL 해석 로직을 새로운 정규화 헬퍼로 업데이트하여 인라인 정규화를 제거합니다.
페이지 훅 업데이트
src/pages/Loading/hooks/useLoadingPage.ts, src/pages/QRScan/hooks/useQRScanPage.ts
스캔 응답 및 히스토리 항목 분류에 isWebScanTarget 헬퍼를 적용합니다.
라우팅 리팩토링
src/pages/ScanList/ScanListPage.tsx
웹 항목을 전용 /report 페이지로 라우팅하도록 변경하고, isWebScanListItem을 공유 헬퍼로 위임합니다.
테스트 커버리지
src/shared/lib/scan-session/scanClassification.test.ts, src/shared/store/scanSessionStore.test.ts
스킴 정규화, HTTP URL 검증, 웹 스캔 분류 및 히스토리 선택 정규화에 대한 테스트를 추가합니다.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

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.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 변경사항의 주요 내용을 명확하게 설명합니다. 스캔 내역 클릭 시 상세 보고서 페이지 연결이라는 핵심 목표를 간결하고 구체적으로 표현하고 있습니다.
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.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value).


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
Review rate limit: 0/1 reviews remaining, refill in 43 minutes and 52 seconds.

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.

🧹 Nitpick comments (4)
src/pages/ScanList/ScanListPage.tsx (1)

88-94: 💤 Low value

isWebScanListItem 래퍼 함수가 불필요합니다.

단 한 줄로 isWebScanTarget을 그대로 위임하므로, 추가적인 가독성이나 추상화 효과가 없습니다.

♻️ 인라인 직접 호출로 간소화
-function isWebScanListItem(item: {
-  isUrl: boolean | null;
-  schemeType: string | null;
-  url: string;
-}): boolean {
-  return isWebScanTarget(item);
-}

 // ...
-const nextRoute = isWebScanListItem(item) ? reportRoute : nonUrlResultRoute;
+const nextRoute = isWebScanTarget(item) ? reportRoute : nonUrlResultRoute;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/ScanList/ScanListPage.tsx` around lines 88 - 94, The function
isWebScanListItem simply delegates to isWebScanTarget and is unused for added
abstraction; remove the isWebScanListItem wrapper function and replace its call
sites to call isWebScanTarget directly, making sure any necessary import or
scope for isWebScanTarget is present where the wrapper was used and that the
parameter shape expected by isWebScanTarget matches the previous usage.
src/shared/store/scanSessionStore.ts (1)

137-158: 💤 Low value

resolveIsUrl 내 이중 정규화는 무해하지만 중복입니다.

resolveSchemeType이 이미 normalizeScanSchemeTypeAlias를 적용하여 정규화된 값을 반환하므로, resolveIsUrl에서 동일 함수를 다시 호출하는 것은 불필요합니다. 동작에는 영향이 없습니다.

♻️ 간소화 제안
 function resolveIsUrl(
   source: unknown,
   decodedUrl: string | null,
   schemeType: string | null,
 ): boolean | null {
   const explicitIsUrl = pickBoolean(source, ['isUrl', 'is_url']);
-  const normalizedSchemeType = normalizeScanSchemeTypeAlias(schemeType);
 
-  if (normalizedSchemeType) {
-    return normalizedSchemeType === 'WEB';
+  if (schemeType) {
+    return schemeType === 'WEB';
   }
   ...
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/shared/store/scanSessionStore.ts` around lines 137 - 158, The function
resolveIsUrl redundantly calls normalizeScanSchemeTypeAlias on schemeType even
though callers already pass a normalized value (from resolveSchemeType); remove
the duplicate normalization and instead use the incoming schemeType directly
(e.g., compare schemeType === 'WEB'), leaving the explicitIsUrl (pickBoolean)
and isHttpUrl checks unchanged so behavior remains the same; update resolveIsUrl
to accept and use the normalized schemeType without calling
normalizeScanSchemeTypeAlias.
src/pages/QRScan/hooks/useQRScanPage.ts (1)

140-165: 💤 Low value

isNonWebScanResponse 내 불필요한 사전 정규화 및 빈 문자열 폴백

Line 157에서 rawSchemeType.trim().toUpperCase()로 사전 정규화한 후 isWebScanTarget에 넘기지만, isWebScanTarget 내부의 normalizeScanSchemeTypeAlias가 동일한 정규화를 한 번 더 수행합니다. 또한 스킴 타입 부재 시 null 대신 ''을 폴백으로 사용하는 것은 ScanTargetLike.schemeType: string | null | undefined 관례와 일치하지 않습니다 (''normalizeScanSchemeTypeAlias!schemeType 체크로 우연히 동작함).

♻️ 정리 제안
 function isNonWebScanResponse(scanResponse: Record<string, unknown>): boolean {
   const rawSchemeType =
     typeof scanResponse.schemeType === 'string'
       ? scanResponse.schemeType
       : scanResponse.scheme_type;
   const targetValue = /* ... unchanged ... */;
-  const schemeType = typeof rawSchemeType === 'string' ? rawSchemeType.trim().toUpperCase() : '';
+  const schemeType = typeof rawSchemeType === 'string' ? rawSchemeType : null;
   const rawIsUrl = scanResponse.isUrl !== undefined ? scanResponse.isUrl : scanResponse.is_url;

   return !isWebScanTarget({
     isUrl: typeof rawIsUrl === 'boolean' ? rawIsUrl : null,
     schemeType,
     url: targetValue,
   });
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/QRScan/hooks/useQRScanPage.ts` around lines 140 - 165, The function
isNonWebScanResponse is pre-normalizing schemeType
(rawSchemeType.trim().toUpperCase()) and falling back to an empty string, which
duplicates work done by isWebScanTarget/normalizeScanSchemeTypeAlias and breaks
the expected ScanTargetLike.schemeType contract; change isNonWebScanResponse to
stop trimming/uppercasing and instead pass the raw scheme value as string | null
| undefined (i.e. use rawSchemeType directly or null when absent), and keep the
isUrl handling as boolean | null, so isWebScanTarget can perform its own
normalizeScanSchemeTypeAlias logic.
src/shared/lib/scan-session/scanClassification.test.ts (1)

5-26: ⚡ Quick win

핵심 헬퍼 함수의 엣지 케이스 테스트가 누락되어 있습니다.

이 유틸리티는 4개 이상의 소비처에서 사용되는 공유 분류 로직의 핵심입니다. 아래 케이스들을 추가하면 회귀 방지 효과가 높습니다.

✅ 추가 권장 테스트 케이스
  describe('scanClassification', () => {
    it('normalizes URL scheme aliases to WEB', () => {
      expect(normalizeScanSchemeTypeAlias('URL')).toBe('WEB');
      expect(normalizeScanSchemeTypeAlias(' web ')).toBe('WEB');
+     expect(normalizeScanSchemeTypeAlias('SMS')).toBe('SMS');
+     expect(normalizeScanSchemeTypeAlias(null)).toBe(null);
+     expect(normalizeScanSchemeTypeAlias('   ')).toBe(null);
    });

    it('detects http and https targets as web URLs', () => {
      expect(isHttpUrl('https://example.com')).toBe(true);
      expect(isHttpUrl('http://example.com')).toBe(true);
      expect(isHttpUrl('sms:01012345678')).toBe(false);
+     expect(isHttpUrl(null)).toBe(false);
+     expect(isHttpUrl(undefined)).toBe(false);
+     expect(isHttpUrl('  https://example.com')).toBe(true); // trim 동작 확인
    });

    it('treats history items with URL scheme aliases as web scans', () => {
      expect(isWebScanTarget({ isUrl: false, schemeType: 'URL', url: 'https://example.com/path' })).toBe(true);
+   });
+
+   it('falls back to HTTP URL when no schemeType', () => {
+     expect(isWebScanTarget({ schemeType: null, url: 'https://example.com' })).toBe(true);
+   });
+
+   it('returns false for explicit non-web schemeType', () => {
+     expect(isWebScanTarget({ schemeType: 'SMS', isUrl: true, url: 'https://example.com' })).toBe(false);
+   });
+
+   it('falls back to isUrl flag when schemeType and url are absent', () => {
+     expect(isWebScanTarget({ isUrl: true })).toBe(true);
+     expect(isWebScanTarget({ isUrl: false })).toBe(false);
+     expect(isWebScanTarget({})).toBe(false);
    });
  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/shared/lib/scan-session/scanClassification.test.ts` around lines 5 - 26,
Add comprehensive edge-case tests for the three helpers:
normalizeScanSchemeTypeAlias, isHttpUrl, and isWebScanTarget. Specifically, add
tests that verify normalizeScanSchemeTypeAlias handles null/undefined, empty
string, mixed-case and surrounding whitespace (e.g., ' Url ' -> 'WEB'), and
unknown scheme strings; add tests for isHttpUrl with non-string inputs
(null/undefined/number), uppercase/lowercase scheme variants, and other schemes
like 'ftp:' and 'mailto:' to ensure false; and add isWebScanTarget tests for
history items where isUrl is true (should prefer isUrl), schemeType is unknown
or null, url is empty/invalid, and when schemeType contains whitespace/casing
differences to ensure consistent classification. Use the existing function names
normalizeScanSchemeTypeAlias, isHttpUrl, and isWebScanTarget to locate and add
these new test cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/pages/QRScan/hooks/useQRScanPage.ts`:
- Around line 140-165: The function isNonWebScanResponse is pre-normalizing
schemeType (rawSchemeType.trim().toUpperCase()) and falling back to an empty
string, which duplicates work done by
isWebScanTarget/normalizeScanSchemeTypeAlias and breaks the expected
ScanTargetLike.schemeType contract; change isNonWebScanResponse to stop
trimming/uppercasing and instead pass the raw scheme value as string | null |
undefined (i.e. use rawSchemeType directly or null when absent), and keep the
isUrl handling as boolean | null, so isWebScanTarget can perform its own
normalizeScanSchemeTypeAlias logic.

In `@src/pages/ScanList/ScanListPage.tsx`:
- Around line 88-94: The function isWebScanListItem simply delegates to
isWebScanTarget and is unused for added abstraction; remove the
isWebScanListItem wrapper function and replace its call sites to call
isWebScanTarget directly, making sure any necessary import or scope for
isWebScanTarget is present where the wrapper was used and that the parameter
shape expected by isWebScanTarget matches the previous usage.

In `@src/shared/lib/scan-session/scanClassification.test.ts`:
- Around line 5-26: Add comprehensive edge-case tests for the three helpers:
normalizeScanSchemeTypeAlias, isHttpUrl, and isWebScanTarget. Specifically, add
tests that verify normalizeScanSchemeTypeAlias handles null/undefined, empty
string, mixed-case and surrounding whitespace (e.g., ' Url ' -> 'WEB'), and
unknown scheme strings; add tests for isHttpUrl with non-string inputs
(null/undefined/number), uppercase/lowercase scheme variants, and other schemes
like 'ftp:' and 'mailto:' to ensure false; and add isWebScanTarget tests for
history items where isUrl is true (should prefer isUrl), schemeType is unknown
or null, url is empty/invalid, and when schemeType contains whitespace/casing
differences to ensure consistent classification. Use the existing function names
normalizeScanSchemeTypeAlias, isHttpUrl, and isWebScanTarget to locate and add
these new test cases.

In `@src/shared/store/scanSessionStore.ts`:
- Around line 137-158: The function resolveIsUrl redundantly calls
normalizeScanSchemeTypeAlias on schemeType even though callers already pass a
normalized value (from resolveSchemeType); remove the duplicate normalization
and instead use the incoming schemeType directly (e.g., compare schemeType ===
'WEB'), leaving the explicitIsUrl (pickBoolean) and isHttpUrl checks unchanged
so behavior remains the same; update resolveIsUrl to accept and use the
normalized schemeType without calling normalizeScanSchemeTypeAlias.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9e5ea637-f7a6-46de-8f39-6099630fa184

📥 Commits

Reviewing files that changed from the base of the PR and between 8b541e6 and 2e57698.

📒 Files selected for processing (7)
  • src/pages/Loading/hooks/useLoadingPage.ts
  • src/pages/QRScan/hooks/useQRScanPage.ts
  • src/pages/ScanList/ScanListPage.tsx
  • src/shared/lib/scan-session/scanClassification.test.ts
  • src/shared/lib/scan-session/scanClassification.ts
  • src/shared/store/scanSessionStore.test.ts
  • src/shared/store/scanSessionStore.ts

@kim-subsub kim-subsub merged commit 51afe1a into develop May 3, 2026
8 checks passed
@kim-subsub kim-subsub deleted the feat/36 branch May 3, 2026 06:37
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.

2 participants