Skip to content

Conversation

@jllee000
Copy link
Contributor

@jllee000 jllee000 commented Jul 15, 2025

📌 Related Issues

관련된 Issue를 태그해주세요. (e.g. - close #25)

✅ 체크 리스트

  • PR 제목의 형식을 잘 작성했나요? e.g. Feat(client): PR 템플릿 작성
  • 빌드가 성공했나요? (pnpm build)

📄 Tasks

⭐ PR Point (To Reviewer)

📷 Screenshot

Summary by CodeRabbit

  • 신규 기능

    • 현재 활성 탭의 URL 정보를 표시하고 저장할 수 있는 기능이 추가되었습니다.
    • 사이트별 체류 시간을 측정하여 알림으로 표시하는 기능이 추가되었습니다.
  • 버그 수정

    • 빌드 설정에서 잘못된 파일 확장자를 올바르게 수정하여 백그라운드 스크립트가 정상적으로 동작하도록 개선되었습니다.
  • 개선 사항

    • 확장 프로그램의 권한 및 접근 URL 범위가 모든 사이트로 확대되었습니다.
    • UI에서 URL 정보를 동적으로 전달 및 표시하도록 개선되었습니다.
  • 기타

    • 개발 편의를 위한 의존성이 추가되었습니다.
    • 일부 메시지 및 코드 스타일이 개선되었습니다.

@coderabbitai
Copy link

coderabbitai bot commented Jul 15, 2025

Walkthrough

크롬 확장 프로그램의 기능 확장과 구조 개선이 이루어졌습니다. manifest 권한이 확대되고, App 및 ModalPop 컴포넌트가 상태 기반으로 개선되었으며, background 스크립트가 추가되어 메시지 및 알림 처리를 담당합니다. 개발 환경 및 빌드 설정도 일부 수정되었습니다.

Changes

파일/경로 그룹 변경 요약
apps/extension/package.json vite-plugin-chrome-extension 개발 의존성 추가
apps/extension/public/manifest.json permissions, host_permissions, content_scripts matches 확장
apps/extension/src/App.tsx 활성 탭 URL 조회 및 상태 관리, ModalPop에 urlInfo prop 전달
apps/extension/src/background.ts background 스크립트 신설, 메시지 수신 및 알림 생성
apps/extension/src/components/modalPop/ModalPop.tsx urlInfo prop 추가, 저장 버튼 클릭 시 메시지 전송 로직 추가
apps/extension/src/content.ts 단순 로그 메시지 텍스트 변경
apps/extension/src/popup.tsx React import 방식 변경
apps/extension/vite.config.ts background 엔트리 경로 확장자 수정 (.Js → .ts)

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App(Component)
    participant ModalPop(Component)
    participant ChromeAPI
    participant Background

    User->>App: 확장 프로그램 실행
    App->>ChromeAPI: 활성 탭 정보 요청
    ChromeAPI-->>App: 활성 탭 URL 반환
    App->>ModalPop: urlInfo prop 전달
    User->>ModalPop: 저장하기 버튼 클릭
    ModalPop->>ChromeAPI: SAVE_BOOKMARK 메시지 전송
    ChromeAPI->>Background: 메시지 전달
    Background->>ChromeAPI: 알림(Notification) 생성
Loading

Assessment against linked issues

Objective Addressed Explanation
shadcn/ui 초기 세팅, 디자인 시스템 세팅 관련 (#25) 변경사항이 확장 프로그램(Extension) 코드에만 적용되어 있고, 디자인 시스템(shadcn/ui) 세팅과 직접적으로 관련된 변경은 없음.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
크롬 확장 프로그램 manifest, App, ModalPop, background, content, popup 등 apps/extension 내 전체 변경 (apps/extension/*) 모든 변경이 디자인 시스템(shadcn/ui) 세팅(#25)과 무관하며, 확장 프로그램 기능 개발에 해당하므로 본 이슈의 범위를 벗어남.

Possibly related PRs

  • [Feat] Dropdown 컴포넌트 구현 pinback-client#28: 이 PR은 크롬 확장 프로그램의 초기 보일러플레이트를 도입하며, 현재 PR은 이를 확장 및 개선하는 코드 변경을 포함하고 있어 코드 레벨에서 직접적으로 연관됨.

Suggested reviewers

  • karnelll
  • constantly-dev

Poem

🐰
확장 기능이 한 뼘 더 자라,
탭의 URL도 쏙쏙 알아.
저장 버튼 꾹 누르면,
알림이 번쩍, 기쁨이 가득!
토끼도 깡총, 코드도 깡총,
오늘도 개발은 신나게 통!

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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.

Documentation and Community

  • 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: 6

🧹 Nitpick comments (3)
apps/extension/src/popup.tsx (2)

2-2: React 네임스페이스 임포트가 실제로 사용되지 않습니다

createRoot 로 렌더링하는 JSX 구문은 최신 JSX 런타임( jsx: "react-jsx" ) 설정이라면 React 객체를 참조하지 않습니다.
따라서 import * as React from 'react'; 는 번들에 불필요한 네임스페이스를 추가하고 ESLint 규칙( no-unused-vars )에도 저촉될 수 있습니다.

-import * as React from 'react';
+// React 17 이전 런타임을 사용하는 경우에만 필요
+// import React from 'react';

사용 중인 TS 컴파일 옵션을 한 번 확인해 주세요. 최신 런타임이라면 임포트를 완전히 제거해도 됩니다.


8-12: 루트 엘리먼트 null 처리 간결화 제안

null 검사를 분기하지 않고 널 단언으로 처리하면 코드가 더 간결해집니다. 팝업 HTML에 #root 가 없으면 애초에 런타임 오류가 발생하므로, 개발 단계에서 빨리 실패(fail-fast)하게 하는 것도 한 방법입니다.

-const rootEl = document.getElementById('root');
-if (rootEl) {
-  createRoot(rootEl).render(<App />);
-} else {
-  console.error('❌ root element not found!');
-}
+const rootEl = document.getElementById('root')!;
+createRoot(rootEl).render(<App />);

버전 관리 정책에 따라 선택적으로 반영해 주세요.

apps/extension/src/App.tsx (1)

15-15: console.log 메시지를 영어로 변경하는 것을 권장합니다.

일관성을 위해 로그 메시지를 영어로 작성하는 것이 좋습니다.

다음과 같이 변경하는 것을 권장합니다:

-        console.log('📌 URL:', activeTab.url);
+        console.log('📌 Current URL:', activeTab.url);

-          console.log('저장');
+          console.log('Bookmark saved');

Also applies to: 18-18

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1c8cbb1 and 5812e1d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • apps/extension/package.json (1 hunks)
  • apps/extension/public/manifest.json (2 hunks)
  • apps/extension/src/App.tsx (1 hunks)
  • apps/extension/src/background.ts (1 hunks)
  • apps/extension/src/components/modalPop/ModalPop.tsx (2 hunks)
  • apps/extension/src/content.ts (1 hunks)
  • apps/extension/src/popup.tsx (1 hunks)
  • apps/extension/vite.config.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
apps/extension/src/popup.tsx (2)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/App.tsx:10-21
Timestamp: 2025-07-08T11:47:10.642Z
Learning: In apps/extension/src/App.tsx, the InfoBox component currently uses a hardcoded external URL for the icon prop as a temporary static placeholder. The plan is to replace this with dynamic favicon extraction from bookmarked websites in future iterations.
apps/extension/public/manifest.json (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/App.tsx:10-21
Timestamp: 2025-07-08T11:47:10.642Z
Learning: In apps/extension/src/App.tsx, the InfoBox component currently uses a hardcoded external URL for the icon prop as a temporary static placeholder. The plan is to replace this with dynamic favicon extraction from bookmarked websites in future iterations.
apps/extension/src/App.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/App.tsx:10-21
Timestamp: 2025-07-08T11:47:10.642Z
Learning: In apps/extension/src/App.tsx, the InfoBox component currently uses a hardcoded external URL for the icon prop as a temporary static placeholder. The plan is to replace this with dynamic favicon extraction from bookmarked websites in future iterations.
apps/extension/src/components/modalPop/ModalPop.tsx (2)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/App.tsx:10-21
Timestamp: 2025-07-08T11:47:10.642Z
Learning: In apps/extension/src/App.tsx, the InfoBox component currently uses a hardcoded external URL for the icon prop as a temporary static placeholder. The plan is to replace this with dynamic favicon extraction from bookmarked websites in future iterations.
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#43
File: apps/client/src/shared/components/ui/cards/BookmarkCard.tsx:0-0
Timestamp: 2025-07-11T20:47:15.009Z
Learning: React 컴포넌트에서 button 요소를 사용할 때는 항상 type 속성을 명시적으로 지정해야 합니다. 일반적인 클릭 버튼의 경우 type="button"을, 폼 제출 버튼의 경우 type="submit"을 명시해야 합니다. 이렇게 해야 의도하지 않은 폼 제출 동작을 방지할 수 있습니다.
🔇 Additional comments (10)
apps/extension/src/content.ts (1)

1-1: 단순한 메시지 변경으로 문제없음

콘솔 로그 메시지 변경이 적절합니다.

apps/extension/package.json (1)

45-45: 크롬 확장 프로그램 빌드를 위한 적절한 의존성 추가

vite-plugin-chrome-extension 플러그인 추가가 적절합니다.

apps/extension/vite.config.ts (1)

30-30: 올바른 파일 확장자 수정

백그라운드 스크립트 경로를 .ts로 수정한 것이 적절합니다.

apps/extension/src/components/modalPop/ModalPop.tsx (1)

12-14: 인터페이스 정의는 적절함

ModalPopProps 인터페이스 정의가 적절합니다.

apps/extension/public/manifest.json (3)

6-6: "tabs" 권한 추가를 승인합니다.

App.tsx에서 활성 탭을 쿼리하는 기능에 필요한 권한이 올바르게 추가되었습니다.


25-25: content_scripts 매칭 범위 검토 결과

apps/extension/src/content.ts를 확인한 결과, content script는 단순히 console.log('Hello from content!')만 수행하므로 보안·성능상 즉각적인 문제는 없습니다.
다만, 향후 실제 사용 목적에 맞춰 아래 사항을 검토하시길 권장드립니다.

  • apps/extension/public/manifest.json의 "matches": ["<all_urls>"]
    실제 필요한 URL 패턴(ex. https://example.com/*)으로 좁히기

7-7: host_permissions 최소 범위 사용 검토 요청

manifest.json의 "host_permissions": ["<all_urls>"]는 보안상 권한이 너무 광범위합니다.
코드 분석 결과 네트워크 호출(fetch/XHR)이나 프로그래매틱 스크립트 주입(chrome.scripting.executeScript 또는 chrome.tabs.executeScript) 사용 흔적이 없으며, content_scripts 역시 모든 URL("<all_urls>")에만 선언되어 있습니다.

따라서 아래를 검토해 주세요:

  • apps/extension/public/manifest.json
    • host_permissions: 실제 동작에 필요한 최소 도메인 패턴(예: https://your-domain.com/*)만 허용하도록 수정
    • content_scripts.matches: 필요 도메인으로 동기화
  • 프로그래매틱 스크립트 주입 API 사용 여부 확인
    • chrome.scripting.executeScript 또는 chrome.tabs.executeScript 호출이 없다면 host_permissions 항목 제거 검토
apps/extension/src/App.tsx (3)

5-5: React hooks import를 승인합니다.

useState와 useEffect가 올바르게 import되었습니다.


8-8: URL 상태 초기화를 승인합니다.

빈 문자열로 초기화하는 것이 적절합니다.


23-29: UI 구조가 적절합니다.

현재 URL을 표시하고 ModalPop에 전달하는 구조가 잘 구성되어 있습니다.

Comment on lines +2 to +15
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'PAGE_DURATION') {
const { url, duration } = message.payload;
console.log(`🌐 ${url} 체류시간: ${duration}초`);

// optionally alert
chrome.notifications.create({
type: 'basic',
iconUrl: 'icon48.png',
title: '체류시간 측정',
message: `${new URL(url).hostname}에서 ${duration}초 머물렀어요!`,
});
}
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 처리 및 입력 검증 추가 필요

메시지 핸들러에서 다음 사항들이 개선되어야 합니다:

  1. URL 파싱 시 에러 처리
  2. 메시지 payload 구조 검증
  3. chrome.notifications.create 에러 처리

다음과 같이 개선하는 것을 권장합니다:

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'PAGE_DURATION') {
-    const { url, duration } = message.payload;
+    const { url, duration } = message.payload || {};
+    
+    if (!url || !duration) {
+      console.error('Invalid payload structure');
+      return;
+    }
+    
     console.log(`🌐 ${url} 체류시간: ${duration}초`);

     // optionally alert
-    chrome.notifications.create({
-      type: 'basic',
-      iconUrl: 'icon48.png',
-      title: '체류시간 측정',
-      message: `${new URL(url).hostname}에서 ${duration}초 머물렀어요!`,
-    });
+    try {
+      const hostname = new URL(url).hostname;
+      chrome.notifications.create({
+        type: 'basic',
+        iconUrl: 'icon48.png',
+        title: '체류시간 측정',
+        message: `${hostname}에서 ${duration}초 머물렀어요!`,
+      });
+    } catch (error) {
+      console.error('Failed to create notification:', error);
+    }
   }
 });
📝 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
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'PAGE_DURATION') {
const { url, duration } = message.payload;
console.log(`🌐 ${url} 체류시간: ${duration}초`);
// optionally alert
chrome.notifications.create({
type: 'basic',
iconUrl: 'icon48.png',
title: '체류시간 측정',
message: `${new URL(url).hostname}에서 ${duration}초 머물렀어요!`,
});
}
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'PAGE_DURATION') {
const { url, duration } = message.payload || {};
if (!url || !duration) {
console.error('Invalid payload structure');
return;
}
console.log(`🌐 ${url} 체류시간: ${duration}초`);
// optionally alert
try {
const hostname = new URL(url).hostname;
chrome.notifications.create({
type: 'basic',
iconUrl: 'icon48.png',
title: '체류시간 측정',
message: `${hostname}에서 ${duration}초 머물렀어요!`,
});
} catch (error) {
console.error('Failed to create notification:', error);
}
}
});
🤖 Prompt for AI Agents
In apps/extension/src/background.ts lines 2 to 15, the message handler lacks
error handling and input validation. Add checks to verify that message.payload
exists and contains valid url and duration fields before processing. Wrap the
URL parsing in a try-catch block to handle invalid URLs gracefully. Also,
provide a callback or promise handling for chrome.notifications.create to catch
and log any errors during notification creation.

interface ModalPopProps {
urlInfo?: string;
}
const ModalPop = (urlInfo: ModalPopProps) => {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

매개변수 구조분해 문법 오류 수정 필요

함수 매개변수에서 props 구조분해가 잘못되었습니다.

다음과 같이 수정해야 합니다:

-const ModalPop = (urlInfo: ModalPopProps) => {
+const ModalPop = ({ urlInfo }: ModalPopProps) => {
📝 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 ModalPop = (urlInfo: ModalPopProps) => {
const ModalPop = ({ urlInfo }: ModalPopProps) => {
🤖 Prompt for AI Agents
In apps/extension/src/components/modalPop/ModalPop.tsx at line 15, the function
parameter uses incorrect destructuring syntax for props. Change the parameter
from a single object to destructured props by replacing "urlInfo: ModalPopProps"
with "{ urlInfo }: ModalPopProps" to correctly extract urlInfo from the props
object.

Comment on lines +16 to +28
const handleSave = () => {
chrome.runtime.sendMessage(
{
type: 'SAVE_BOOKMARK',
payload: {
url: { urlInfo },
},
},
(response) => {
console.log('✅ 응답 받음:', response);
}
);
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 처리 및 payload 구조 개선 필요

handleSave 함수에서 다음 사항들이 개선되어야 합니다:

  1. payload 구조가 잘못됨
  2. 에러 처리 부재

다음과 같이 개선하는 것을 권장합니다:

 const handleSave = () => {
   chrome.runtime.sendMessage(
     {
       type: 'SAVE_BOOKMARK',
       payload: {
-        url: { urlInfo },
+        url: urlInfo,
       },
     },
     (response) => {
-      console.log('✅ 응답 받음:', response);
+      if (chrome.runtime.lastError) {
+        console.error('메시지 전송 실패:', chrome.runtime.lastError);
+      } else {
+        console.log('✅ 응답 받음:', response);
+      }
     }
   );
 };
📝 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 handleSave = () => {
chrome.runtime.sendMessage(
{
type: 'SAVE_BOOKMARK',
payload: {
url: { urlInfo },
},
},
(response) => {
console.log('✅ 응답 받음:', response);
}
);
};
const handleSave = () => {
chrome.runtime.sendMessage(
{
type: 'SAVE_BOOKMARK',
payload: {
url: urlInfo,
},
},
(response) => {
if (chrome.runtime.lastError) {
console.error('메시지 전송 실패:', chrome.runtime.lastError);
} else {
console.log('✅ 응답 받음:', response);
}
}
);
};
🤖 Prompt for AI Agents
In apps/extension/src/components/modalPop/ModalPop.tsx around lines 16 to 28,
the handleSave function has an incorrect payload structure and lacks error
handling. Fix the payload by removing the unnecessary nested object around
urlInfo so that payload directly contains the url value. Add error handling in
the callback of chrome.runtime.sendMessage to check for runtime.lastError and
handle it appropriately, such as logging or user notification.

Comment on lines +105 to +110
<CommonBtn
text="저장하기"
size="large"
type="green"
onClick={handleSave}
/>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

버튼 타입 속성 추가 필요

리트리빙된 학습 내용에 따르면, React 컴포넌트에서 button 요소에는 항상 type 속성을 명시해야 합니다.

CommonBtn 컴포넌트에 type="button" 속성을 추가하는 것을 권장합니다:

 <CommonBtn
+  type="button"
   text="저장하기"
   size="large"
   type="green"
   onClick={handleSave}
 />

참고: 만약 CommonBtn 컴포넌트가 내부적으로 button 요소를 렌더링한다면, 해당 컴포넌트에서 type 속성을 처리하도록 해야 합니다.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/extension/src/components/modalPop/ModalPop.tsx around lines 105 to 110,
the CommonBtn component lacks an explicit button type attribute, which can cause
unintended form submissions. Add a type="button" prop to the CommonBtn usage
here. Also verify that the CommonBtn component correctly passes this type prop
to the underlying button element it renders.

<div>
<h1 className="text-lg font-bold">📎 북마크 저장!</h1>
<p className="mt-2 break-words text-sm">{url}</p>
<ModalPop urlInfo={url} />;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

JSX 구문 오류를 수정해주세요.

JSX 요소 뒤에 세미콜론이 잘못 위치했습니다.

다음과 같이 수정해주세요:

-      <ModalPop urlInfo={url} />;
+      <ModalPop urlInfo={url} />
📝 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
<ModalPop urlInfo={url} />;
<ModalPop urlInfo={url} />
🤖 Prompt for AI Agents
In apps/extension/src/App.tsx at line 27, remove the semicolon immediately
following the JSX element <ModalPop urlInfo={url} /> as it is a syntax error in
JSX. The JSX element should be written without a trailing semicolon.

Comment on lines +10 to +22
useEffect(() => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const activeTab = tabs[0];
if (activeTab?.url) {
setUrl(activeTab.url);
console.log('📌 URL:', activeTab.url);

chrome.storage.local.set({ bookmarkedUrl: activeTab.url }, () => {
console.log('저장');
});
}
});
}, []);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 핸들링 추가를 권장합니다.

chrome.tabs.query와 chrome.storage.local.set 작업에 에러 핸들링이 필요합니다.

다음과 같이 에러 핸들링을 추가하는 것을 권장합니다:

  useEffect(() => {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      const activeTab = tabs[0];
      if (activeTab?.url) {
        setUrl(activeTab.url);
        console.log('📌 URL:', activeTab.url);

-        chrome.storage.local.set({ bookmarkedUrl: activeTab.url }, () => {
-          console.log('저장');
-        });
+        chrome.storage.local.set({ bookmarkedUrl: activeTab.url }, () => {
+          if (chrome.runtime.lastError) {
+            console.error('저장 실패:', chrome.runtime.lastError);
+          } else {
+            console.log('저장 완료');
+          }
+        });
      }
    });
  }, []);
📝 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
useEffect(() => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const activeTab = tabs[0];
if (activeTab?.url) {
setUrl(activeTab.url);
console.log('📌 URL:', activeTab.url);
chrome.storage.local.set({ bookmarkedUrl: activeTab.url }, () => {
console.log('저장');
});
}
});
}, []);
useEffect(() => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const activeTab = tabs[0];
if (activeTab?.url) {
setUrl(activeTab.url);
console.log('📌 URL:', activeTab.url);
chrome.storage.local.set({ bookmarkedUrl: activeTab.url }, () => {
if (chrome.runtime.lastError) {
console.error('저장 실패:', chrome.runtime.lastError);
} else {
console.log('저장 완료');
}
});
}
});
}, []);
🤖 Prompt for AI Agents
In apps/extension/src/App.tsx around lines 10 to 22, the chrome.tabs.query and
chrome.storage.local.set calls lack error handling. To fix this, add checks for
errors in their callbacks by inspecting chrome.runtime.lastError and handle or
log any errors appropriately. This will ensure that any issues during these
asynchronous operations are caught and managed gracefully.

Copy link
Contributor

@karnelll karnelll left a comment

Choose a reason for hiding this comment

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

전체적으로 구조가 잘 정리되어 있어 읽기 편했고, 디자인 시스템 컴포넌트를 활용해 UI를 깔끔하게 구성해주신 점이 좋았어요!! 간단한 코멘트만 확인 부탁드립니다 :) 고생하셨어요👍

Comment on lines +2 to +15
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'PAGE_DURATION') {
const { url, duration } = message.payload;
console.log(`🌐 ${url} 체류시간: ${duration}초`);

// optionally alert
chrome.notifications.create({
type: 'basic',
iconUrl: 'icon48.png',
title: '체류시간 측정',
message: `${new URL(url).hostname}에서 ${duration}초 머물렀어요!`,
});
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

타입을 별도 파일로 분리해두면 재사용성과 안정성이 높아지고, IDE 자동완성도 더 잘 작동해서 분리해두면 좋을 것 같습니다!
현재 메시지 핸들러에서 전달받는 메시지에 타입이 명시되어 있지 않아 런타임 에러도 방지할 수 있어요 👍

// types/messages.ts
export interface PageDurationMessage {
  type: 'PAGE_DURATION';
  payload: {
    url: string;
    duration: number;
  };
}

export type ExtensionMessage = PageDurationMessage;
// background.ts
import type { ExtensionMessage } from '@/types/messages';

chrome.runtime.onMessage.addListener(
  (message: ExtensionMessage, sender, sendResponse) => {
    if (message.type === 'PAGE_DURATION') {
      const { url, duration } = message.payload;
      // 안전하게 로직 처리 가능
    }
  }
);

interface ModalPopProps {
urlInfo?: string;
}
const ModalPop = (urlInfo: ModalPopProps) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

현재 urlInfo에 대한 유효성 검사가 없어, 간단하게 !urlInfo 체크와 try/catchnew URL(urlInfo) 정도의 방어처리만 추가해두면 안정성이 높아질 것 같습니다!😀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 익스텐션 외부 윈두우 체류시간 측정 로직

3 participants