Skip to content
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

Feature: 유튜브 요약 #110

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open

Feature: 유튜브 요약 #110

wants to merge 17 commits into from

Conversation

guesung
Copy link
Owner

@guesung guesung commented Nov 29, 2024

Summary by CodeRabbit

  • New Features

    • YouTube 콘텐츠를 가져오는 기능 추가.
    • 메모 카드 헤더에서 레이아웃 및 이미지 로딩 방식 개선.
    • 요약 헤더에서 카테고리 기반의 제목 표시 기능 추가.
    • 오류 메시지 처리 기능 추가.
    • 사용자 입력을 위한 새로운 옵션 관리 컴포넌트 추가.
    • 기본 프롬프트를 위한 새로운 상수 추가.
    • 새로운 쿼리 관리 컴포넌트 추가.
    • 설정 페이지의 React 구성 요소 초기화 방식 개선.
  • Bug Fixes

    • URL 생성 로직 수정으로 URL 형식 개선.
  • Documentation

    • SummaryContext 인터페이스에 카테고리 및 오류 메시지 속성 추가.
  • Refactor

    • useSummary 훅에서 카테고리 및 오류 메시지 상태 관리 추가.
    • 스토리지 키 관리 방식 변경.
    • 옵션 관리 컴포넌트의 내부 로직 개선.

Copy link

coderabbitai bot commented Nov 29, 2024

Walkthrough

이번 풀 리퀘스트에서는 web-memo 프로젝트의 package.json 파일에 youtube-transcript라는 새로운 의존성이 추가되었습니다. 또한, 여러 파일에서 페이지 콘텐츠를 가져오고 응답하는 기능이 향상되었습니다. 특히, YouTube 링크를 확인하고 해당 콘텐츠를 가져오는 새로운 함수들이 추가되었으며, 콘텐츠의 카테고리를 구분하는 기능도 포함되었습니다. 이와 함께 여러 UI 컴포넌트의 레이아웃 및 구조가 수정되었습니다.

Changes

파일 경로 변경 요약
package.json 새로운 의존성 "youtube-transcript": "^1.2.1" 추가
packages/shared/src/utils/extension/bridge/pageContent.ts YoutubeTranscript 임포트 추가, Category 타입 정의, 여러 새로운 헬퍼 함수 추가, requestPageContentresponsePageContent 함수 수정
packages/shared/src/utils/extension/bridge/type.ts BridgeResponse 타입 제거
packages/shared/src/utils/extension/module/Tab.ts sendMessage 메서드의 시그니처 변경, BridgeResponse 임포트 제거
packages/web/src/app/[lng]/memos/components/MemoCardHeader/index.tsx LinkImage 컴포넌트의 클래스 이름 변경, priority 속성 추가
pages/side-panel/src/components/SummaryHeader.tsx category 속성 추가, 레이아웃 조정
pages/side-panel/src/components/SummaryProvider.tsx SummaryContext 인터페이스에 categoryerrorMessage 속성 추가
pages/side-panel/src/hooks/useSummary.ts categoryerrorMessage 상태 변수 추가, startSummary 함수 수정, 반환값에 categoryerrorMessage 포함
pages/side-panel/src/utils/Url.ts getMemoUrlgetMemoWishListUrl 함수의 URL 반환값에서 선행 슬래시 제거
chrome-extension/public/_locales/en/messages.json 여러 메시지의 내용 수정, 친근한 톤으로 변경
chrome-extension/public/_locales/ko/messages.json 여러 메시지의 내용 수정, 친근한 톤으로 변경
chrome-extension/utils/getPrompt.ts getPrompt 함수의 기본 매개변수 값 설정, 출력 템플릿 형식 수정
pages/side-panel/src/components/Summary.tsx 오류 처리 추가, errorMessage가 존재할 경우 오류 메시지 렌더링

Possibly related PRs

  • Feature/calendar #112: package.json에 새로운 의존성을 추가하는 두 개의 PR이 관련되어 있으며, 하나는 youtube-transcript, 다른 하나는 dayjs입니다. 두 PR 모두 의존성 관리와 관련이 있습니다.

🐰 새로운 의존성 추가되니,
유튜브도 함께 즐길 수 있네!
콘텐츠 가져오고, 카테고리 정해,
메모도 더 풍성해지는 걸!
함께 뛰어놀자, 기분 좋게! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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 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.

@guesung guesung changed the title Feature/youtube summary Feature: 유튜브 요약 Nov 29, 2024
@guesung guesung changed the base branch from master to develop November 29, 2024 06:58
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: 7

🧹 Outside diff range and nitpick comments (6)
packages/shared/src/utils/extension/module/Tab.ts (1)

Line range hint 9-16: 제네릭 타입 매개변수 추가로 인한 타입 안전성 향상

응답 타입을 명시적으로 지정할 수 있게 되어 타입 안전성이 향상되었습니다. 이는 메시지 통신의 계약을 더 명확하게 만들어줍니다.

하지만 정적 메서드에서 this 사용에 대해 주의가 필요합니다.

다음과 같이 수정하는 것을 고려해보세요:

  static async sendMessage<TPayload, TResponse>(type: BridgeType, payload?: TPayload) {
-   const tab = await this.get();
+   const tab = await Tab.get();
    if (!tab.id) throw new Error('Tab not found');
    const message = await chrome.tabs.sendMessage<BridgeRequest<TPayload>, TResponse>(tab.id, {
      type,
      payload,
    });
    if (!message) throw new Error('Message not found');
    return message;
  }
🧰 Tools
🪛 Biome (1.9.4)

[error] 10-10: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

pages/side-panel/src/components/SummaryHeader.tsx (1)

11-12: 가독성과 국제화를 위한 개선 제안

조건부 렌더링과 텍스트 처리를 다음과 같이 개선하면 좋을 것 같습니다:

- <div className="text-md font-bold">요약 {category === 'youtube' ? '- 유튜브' : '- 웹페이지'}</div>
+ const categoryText = category === 'youtube' ? '유튜브' : '웹페이지';
+ <div className="text-md font-bold">
+   <span>요약 - {categoryText}</span>
+ </div>

추가로 고려할 사항:

  • 텍스트를 상수로 분리하거나 국제화(i18n) 처리를 고려해보세요
  • 카테고리 타입을 상수나 enum으로 정의하면 더 안전한 타입 체크가 가능합니다
pages/side-panel/src/hooks/useSummary.ts (2)

13-13: 카테고리 상태의 기본값에 대한 문서화 필요

'others'를 기본값으로 사용하는 이유와 가능한 카테고리 값들에 대한 설명을 주석으로 추가하면 좋을 것 같습니다.

+ // 'others': 기본 카테고리, YouTube 이외의 모든 콘텐츠에 사용됨
  const [category, setCategory] = useState<Category>('others');

Line range hint 35-46: 타입 안정성 및 문서화 개선 필요

반환 타입을 명시적으로 정의하고 문서화하면 좋을 것 같습니다.

+ interface SummaryHookResult {
+   isSummaryLoading: boolean;
+   summary: string;
+   refetchSummary: () => void;
+   category: Category;
+ }
+
+ /**
+  * 페이지 콘텐츠 요약을 위한 커스텀 훅
+  * @returns {SummaryHookResult} 요약 상태와 관련 함수들
+  */
  export default function useSummary() {
packages/web/src/app/[lng]/memos/components/MemoCardHeader/index.tsx (1)

22-23: favicon에 priority 속성이 필요한지 검토해주세요.

favicon은 크기가 작은 이미지이므로 priority 속성이 불필요할 수 있습니다. 이 속성은 hero 이미지와 같은 중요한 이미지에 사용하는 것이 좋습니다.

성능 최적화를 위해 다음과 같이 수정하는 것을 고려해보세요:

  <Image
    src={memo.favIconUrl}
    width={12}
    height={12}
    alt="favicon"
    className="h-3 w-3 object-contain"
-   priority
  />
package.json (1)

61-61: 버전 고정을 고려해보세요

현재 ^1.2.1로 설정된 버전 범위는 자동으로 마이너 버전 업데이트를 허용합니다. 빌드의 재현성을 높이기 위해 정확한 버전으로 고정하는 것을 추천드립니다.

-    "youtube-transcript": "^1.2.1"
+    "youtube-transcript": "1.2.1"
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 3f57689 and f00ec78.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • package.json (1 hunks)
  • packages/shared/src/utils/extension/bridge/pageContent.ts (1 hunks)
  • packages/shared/src/utils/extension/bridge/type.ts (0 hunks)
  • packages/shared/src/utils/extension/module/Tab.ts (1 hunks)
  • packages/web/src/app/[lng]/memos/components/MemoCardHeader/index.tsx (1 hunks)
  • pages/side-panel/src/components/SummaryHeader.tsx (1 hunks)
  • pages/side-panel/src/components/SummaryProvider.tsx (1 hunks)
  • pages/side-panel/src/hooks/useSummary.ts (2 hunks)
  • pages/side-panel/src/utils/Url.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/shared/src/utils/extension/bridge/type.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/shared/src/utils/extension/module/Tab.ts

[error] 10-10: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (9)
pages/side-panel/src/utils/Url.ts (1)

8-8: URL 구성 방식 검증 필요

PATHS.memos의 선행 슬래시(/) 포함 여부를 확인하여 올바른 URL이 생성되는지 검증이 필요합니다.

✅ Verification successful

From the search results, I found the definition of PATHS.memos in packages/shared/src/constants/Path.ts:

memos: '/memos',

This confirms that PATHS.memos includes a leading slash. Therefore, I can generate the final response:

URL 구성이 올바르게 되어있음을 확인했습니다

PATHS.memos는 이미 선행 슬래시(/)를 포함하고 있어 ${CONFIG.webUrl}${PATHS.memos}와 같은 URL 구성이 정상적으로 동작합니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: PATHS.memos 상수의 정의를 확인하여 선행 슬래시 포함 여부 검증

# Search for PATHS.memos definition
rg -l "PATHS\s*=.*memos.*" 

Length of output: 28


Script:

#!/bin/bash
# Let's try different search patterns to locate PATHS constant definition
# First, search for files containing 'PATHS'
rg -l "PATHS" 

# Also search for any file that might define URL paths or routes
rg -l "const.*PATHS|export.*PATHS"

# Search for memos-related path definitions with more context
rg "memos.*" -A 3 -B 3

Length of output: 29138

packages/shared/src/utils/extension/module/Tab.ts (1)

9-9: 타입 변경의 영향 확인 필요

새로운 제네릭 타입 매개변수 TResponse의 도입으로 인해 이 메서드를 호출하는 다른 코드들의 수정이 필요할 수 있습니다.

✅ Verification successful

Let me gather more information about the response types and their usage.


제네릭 타입 변경은 안전합니다

Tab.sendMessage 메서드의 구현과 사용처를 분석한 결과:

  • pageContent.ts에서는 이미 응답 타입을 명시적으로 지정하고 있습니다: Tab.sendMessage<void, { content: string; category: Category }>
  • observeMemoPage.ts에서는 제네릭 타입을 생략하고 있으며, 타입 추론에 의존합니다.
  • 메서드 구현에서 TResponse 타입은 chrome.tabs.sendMessage의 응답 타입으로 올바르게 사용되고 있습니다.

따라서 이 변경은 기존 코드와 호환되며 추가적인 수정이 필요하지 않습니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: sendMessage 메서드의 사용처를 확인합니다.

# Tab.sendMessage 호출 패턴을 검색
rg "Tab\.sendMessage" -A 2 -B 2

Length of output: 1192


Script:

#!/bin/bash
# Check the implementation of Tab.sendMessage to understand the response type usage
ast-grep --pattern 'static async sendMessage<$_, $_>($_, $_) {
  $$$
}'

# Also check for any other potential usages of Tab.sendMessage
rg "Tab\.sendMessage" --type typescript

Length of output: 150


Script:

#!/bin/bash
# Let's try different approaches to find the implementation and usages

# Find the Tab class implementation
ast-grep --pattern 'class Tab {
  $$$
}'

# Search for all sendMessage usages with .ts extension
rg "Tab\.sendMessage" -g "*.ts"

# Find files that might contain response handling
rg "Tab\.sendMessage.*\>" -g "*.ts" -A 3

Length of output: 2274

pages/side-panel/src/components/SummaryProvider.tsx (1)

Line range hint 1-24: 컨텍스트 초기값 검토 필요

현재 구현에서 컨텍스트의 초기값이 더미 함수와 빈 문자열로 설정되어 있습니다. 실제 사용 시나리오에서 이러한 초기값들이 적절한지 검토가 필요합니다.

다음 사항들을 고려해보시기 바랍니다:

  1. 초기값으로 인한 잠재적 오류 가능성
  2. 컨텍스트 사용 전 초기화 상태 확인 방법
  3. 컨텍스트 소비자들의 안전한 사용을 위한 가이드라인
pages/side-panel/src/components/SummaryHeader.tsx (2)

8-8: 컨텍스트 사용이 깔끔합니다!

컨텍스트에서 필요한 값들을 구조 분해 할당으로 깔끔하게 가져오고 있습니다.


13-26: 컨트롤 섹션의 구조가 잘 정리되었습니다!

flex와 gap을 활용한 레이아웃 구성이 깔끔하고, ErrorBoundary 사용으로 안정성도 확보되었습니다.

pages/side-panel/src/hooks/useSummary.ts (1)

4-4: Category 타입 정의 및 사용 검증 필요

Category 타입이 올바르게 정의되어 있고 일관되게 사용되는지 확인이 필요합니다.

✅ Verification successful

The verification results show that the Category type is well-defined and consistently used throughout the codebase. Let me generate the final response:

Category 타입이 올바르게 정의되어 있고 일관되게 사용됨을 확인했습니다

Category 타입은 packages/shared/src/utils/extension/bridge/pageContent.ts에 'youtube' | 'others' 타입으로 명확하게 정의되어 있으며, 다음과 같이 일관되게 사용되고 있습니다:

  • 페이지 콘텐츠 브릿지에서 카테고리 판별 및 콘텐츠 처리
  • 사이드 패널의 useSummary 훅에서 상태 관리
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Category 타입 정의와 사용처 확인

# Category 타입 정의 검색
echo "Category 타입 정의:"
ast-grep --pattern 'type Category = $$$'

# Category 타입 사용처 검색
echo "\nCategory 타입 사용처:"
rg "Category" -A 2

Length of output: 14993

packages/web/src/app/[lng]/memos/components/MemoCardHeader/index.tsx (1)

15-15: 레이아웃 개선이 잘 되었습니다!

flex와 items-center를 사용하여 favicon과 제목의 정렬이 개선되었습니다.

package.json (1)

60-61: youtube-transcript 패키지 검증이 필요합니다

youtube-transcript 패키지를 프로덕션 의존성으로 추가하셨네요. 안정성과 보안을 위해 다음 사항들을 확인해주시기 바랍니다.

✅ Verification successful

Let me gather more information about the package's implementation and usage to provide a comprehensive response.


Let me check the actual implementation to understand how the package is being used.


youtube-transcript 패키지 검토 완료

youtube-transcript 패키지는 다음과 같은 특징을 보여 안전하게 사용 가능합니다:

  • MIT 라이선스로 상업적 사용에 문제없음
  • 번들 크기가 매우 작음 (gzip: 1.4KB)
  • 알려진 보안 취약점 없음
  • 외부 의존성이 없어 안정적
  • 코드베이스에서 유튜브 자막 추출 용도로만 제한적으로 사용됨

다만, 버전을 고정하시는 것을 권장드립니다:

"youtube-transcript": "1.2.1"
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify youtube-transcript package

# Check npm package info
curl -s https://registry.npmjs.org/youtube-transcript | jq '{
  "latest_version": .["dist-tags"].latest,
  "license": .license,
  "downloads_last_month": ."downloads-last-month",
  "dependencies": .versions["1.2.1"].dependencies
}'

# Check for known vulnerabilities
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "youtube-transcript") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
    }
  }
}'

Length of output: 638


Script:

#!/bin/bash
# Check package size and dependencies
curl -s https://bundlephobia.com/api/size?[email protected] | jq '{
  size: .size,
  gzip: .gzip,
  dependencySizes: .dependencySizes
}'

# Check package implementation
fd -e js -e ts . | xargs rg -l "youtube-transcript" || true

# Look for any existing YouTube related functionality
rg -i "youtube" --type js --type ts -l || true

Length of output: 684


Script:

#!/bin/bash
# Check the implementation in pageContent.ts
cat "packages/shared/src/utils/extension/bridge/pageContent.ts"

# Check YouTube related components
cat "packages/web/src/components/YoutubeEmbed.tsx"
cat "pages/side-panel/src/components/SummaryHeader.tsx"

Length of output: 3220

packages/shared/src/utils/extension/bridge/pageContent.ts (1)

25-27: ⚠️ Potential issue

getContent 함수에서 예외 처리 누락

getContentFromYoutube 함수가 예외를 던질 수 있으므로, getContent 함수에서도 이를 처리하여 응용 프로그램이 중단되지 않도록 해야 합니다.

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

const getContent = async (url: string, category: Category) => {
-  if (category === 'youtube') return await getContentFromYoutube(url);
+  if (category === 'youtube') {
+    return await getContentFromYoutube(url);
+  }
  return getContentFromWeb();
};

또는 getContentFromYoutube에서 예외를 처리하므로 추가 수정이 필요하지 않을 수도 있습니다.

Likely invalid or redundant comment.

};

export const getMemoWishListUrl = (id?: number) => {
const searchParams = new SearchParams([]);
searchParams.set('isWish', 'true');
if (id) searchParams.set('id', String(id));

return `${CONFIG.webUrl}/${PATHS.memos}?${searchParams.getSearchParams()}`;
return `${CONFIG.webUrl}${PATHS.memos}?${searchParams.getSearchParams()}`;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

검색 파라미터 처리 방식 불일치

getMemoUrl과 getMemoWishListUrl 함수 간에 검색 파라미터 처리 방식이 일관되지 않습니다. getMemoWishListUrl에서는 '?'를 직접 추가하고 있으나, getMemoUrl에서는 SearchParams.getSearchParams()의 반환값을 그대로 사용합니다.

다음과 같이 수정하여 일관성을 유지하는 것을 추천드립니다:

-  return `${CONFIG.webUrl}${PATHS.memos}?${searchParams.getSearchParams()}`;
+  return `${CONFIG.webUrl}${PATHS.memos}${searchParams.getSearchParams()}`;
📝 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
return `${CONFIG.webUrl}${PATHS.memos}?${searchParams.getSearchParams()}`;
return `${CONFIG.webUrl}${PATHS.memos}${searchParams.getSearchParams()}`;

@@ -6,6 +6,7 @@ interface SummaryContext extends ReturnType<typeof useSummary> {}
const SummaryContext = createContext<SummaryContext>({
refetchSummary: async () => {},
summary: '',
category: 'others',
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

카테고리 타입 안전성 개선 필요

카테고리 값이 문자열 리터럴로 하드코딩되어 있습니다. 타입 안전성을 높이기 위해 가능한 카테고리 값들을 열거형으로 정의하는 것이 좋습니다.

다음과 같이 변경하는 것을 제안드립니다:

+type ContentCategory = 'youtube' | 'others';
+
 interface SummaryContext extends ReturnType<typeof useSummary> {}
 
 const SummaryContext = createContext<SummaryContext>({
   refetchSummary: async () => {},
   summary: '',
-  category: 'others',
+  category: 'others' as ContentCategory,
   isSummaryLoading: false,
 });
📝 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
category: 'others',
type ContentCategory = 'youtube' | 'others';
interface SummaryContext extends ReturnType<typeof useSummary> {}
const SummaryContext = createContext<SummaryContext>({
refetchSummary: async () => {},
summary: '',
category: 'others' as ContentCategory,
isSummaryLoading: false,
});

Comment on lines +19 to +21
const { content, category } = await requestPageContent();
pageContent = content;
setCategory(category);
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. 에러 발생 시 카테고리 상태도 초기화해야 합니다.
  2. 구조 분해 할당을 더 간단하게 작성할 수 있습니다.
- const { content, category } = await requestPageContent();
- pageContent = content;
- setCategory(category);
+ const response = await requestPageContent();
+ pageContent = response.content;
+ setCategory(response.category);

에러 처리 부분도 다음과 같이 수정이 필요합니다:

  } catch (e) {
    setSummary(I18n.get('error_get_page_content'));
+   setCategory('others');
    return;
  }

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

Comment on lines 14 to 16
const checkYoutube = (url: string) => url.startsWith('https://www.youtube.com/watch?');
const getCategory = (url: string) => {
if (checkYoutube(url)) return 'youtube';
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

YouTube URL 체크 로직 개선 필요

checkYoutube 함수는 단순히 URL이 특정 문자열로 시작하는지를 확인하고 있습니다. 다양한 형태의 YouTube URL을 처리하기 위해 정규식을 사용하여 보다 정확한 검증이 필요합니다.

예를 들어, 다음과 같이 수정할 수 있습니다:

-const checkYoutube = (url: string) => url.startsWith('https://www.youtube.com/watch?');
+const checkYoutube = (url: string) => {
+  const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+$/;
+  return youtubeRegex.test(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
const checkYoutube = (url: string) => url.startsWith('https://www.youtube.com/watch?');
const getCategory = (url: string) => {
if (checkYoutube(url)) return 'youtube';
const checkYoutube = (url: string) => {
const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+$/;
return youtubeRegex.test(url);
};
const getCategory = (url: string) => {
if (checkYoutube(url)) return 'youtube';

Comment on lines +37 to +38
Runtime.onMessage(BRIDGE_TYPE_PAGE_CONTENT, async (_, __, sendResponse) => {
sendResponse({ content, category });
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

이벤트 리스너 등록 위치 수정 필요

Runtime.onMessage 이벤트 리스너가 responsePageContent 함수 내부에서 등록되어 있어 함수 호출 시마다 리스너가 중복 등록될 수 있습니다. 이를 방지하기 위해 리스너 등록을 함수 외부로 이동시키는 것이 좋습니다.

-export const responsePageContent = async () => {
+export const responsePageContent = () => {
+  Runtime.onMessage(BRIDGE_TYPE_PAGE_CONTENT, async (_, __, sendResponse) => {
+    const url = location.href;
+    const category = getCategory(url);
+    const content = await getContent(url, category);
+    sendResponse({ content, category });
+  });
};

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

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

🧹 Outside diff range and nitpick comments (9)
pages/side-panel/src/components/Summary.tsx (2)

9-9: 에러 처리 구현이 잘 되었습니다만, 스타일링을 개선하면 좋겠습니다.

에러 메시지 표시를 위한 조건부 렌더링이 잘 구현되었습니다. 하지만 에러 메시지의 가시성을 높이기 위해 스타일링을 추가하면 좋을 것 같습니다.

다음과 같은 스타일링 추가를 제안드립니다:

-  if (errorMessage) return <p className="whitespace-pre-wrap">{errorMessage}</p>;
+  if (errorMessage) return <p className="whitespace-pre-wrap text-red-500 p-4 bg-red-50 rounded-md">{errorMessage}</p>;

Line range hint 7-13: 에러 상태 관련 테스트 케이스 추가 필요

에러 처리 로직이 추가되었으니, 이에 대한 테스트 케이스도 함께 작성하면 좋겠습니다.

테스트 케이스 작성을 도와드릴까요? 다음과 같은 시나리오들을 테스트할 수 있습니다:

  • 에러 메시지가 없는 경우
  • 에러 메시지가 있는 경우
  • 에러 메시지와 summary가 동시에 있는 경우
packages/shared/src/utils/extension/module/Tab.ts (2)

9-17: 응답 타입의 유연성이 개선되었습니다

제네릭 TResponse 타입의 도입으로 메시지 응답의 타입 안정성이 향상되었습니다. 다만, 에러 메시지를 좀 더 구체적으로 개선하면 좋을 것 같습니다.

다음과 같이 에러 메시지를 더 상세하게 수정하는 것을 제안합니다:

-    if (!tab.id) throw new Error('Tab not found');
+    if (!tab.id) throw new Error('활성 탭의 ID를 찾을 수 없습니다');

-    if (!message) throw new Error('Message not found');
+    if (!message) throw new Error('탭으로부터 응답을 받지 못했습니다');
🧰 Tools
🪛 Biome (1.9.4)

[error] 10-10: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


10-10: 정적 메서드에서 this 사용 개선

정적 메서드에서 this를 사용하면 코드의 의도를 파악하기 어려울 수 있습니다. 클래스 이름을 직접 사용하는 것이 더 명확할 것 같습니다.

-    const tab = await this.get();
+    const tab = await Tab.get();
🧰 Tools
🪛 Biome (1.9.4)

[error] 10-10: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

pages/side-panel/src/components/SummaryHeader.tsx (1)

8-8: 타입 안정성 개선이 필요합니다

category 값의 타입 안정성을 높이기 위해 다음과 같은 개선을 제안합니다:

+ type Category = 'youtube' | 'webpage' | 'others';
- const { isSummaryLoading, refetchSummary, category } = useSummaryContext();
+ const { isSummaryLoading, refetchSummary, category }: { category: Category, ... } = useSummaryContext();

  const getCategoryText = () => {
-   if (category === 'youtube') return '- 유튜브';
-   return '- 웹페이지';
+   switch (category) {
+     case 'youtube':
+       return '- 유튜브';
+     case 'webpage':
+       return '- 웹페이지';
+     default:
+       return '';
+   }
  };

Also applies to: 10-13

pages/side-panel/src/hooks/useSummary.ts (1)

46-47: 반환값 구조가 잘 정의되었습니다

category와 errorMessage가 적절하게 추가되었습니다. 다만, 반환 타입을 명시적으로 정의하면 더 좋을 것 같습니다.

다음과 같은 인터페이스 정의를 추가하는 것을 고려해보세요:

interface UseSummaryReturn {
  isSummaryLoading: boolean;
  summary: string;
  refetchSummary: () => void;
  category: Category;
  errorMessage: string;
}
packages/shared/src/utils/extension/bridge/pageContent.ts (1)

24-27: 오류 컨텍스트 개선이 필요합니다.

getContent 함수에서 발생하는 오류에 카테고리 정보를 포함시키면 디버깅에 도움이 될 것 같습니다.

 const getContent = async (url: string, category: Category) => {
-  if (category === 'youtube') return await getContentFromYoutube(url);
-  return getContentFromWeb();
+  try {
+    if (category === 'youtube') return await getContentFromYoutube(url);
+    return getContentFromWeb();
+  } catch (error) {
+    throw new Error(`콘텐츠 추출 실패 (카테고리: ${category}): ${error.message}`);
+  }
 };
chrome-extension/public/_locales/ko/messages.json (1)

57-60: 오류 메시지의 가독성을 개선해보세요.

현재 메시지는 매우 유용한 정보를 담고 있지만, 줄 바꿈과 구분이 명확하지 않아 읽기 어려울 수 있습니다.

다음과 같이 개선해보는 것은 어떨까요?

-    "message": "앗! 페이지 내용을 읽는데 실패했어요.\n1. 유튜브의 경우에는 요약하는데 3초 정도 걸릴 수 있어요. 잠시 후에 다시 시도해주세요.\n2. 새로 설치하신 경우에는 페이지를 한 번 새로고침해주세요!\n3. 새 탭, 크롬 웹스토어, 크롬 설정 페이지에서는 아쉽게도 요약 기능을 사용할 수 없어요 😅 다른 페이지로 이동하신 후 다시 시도 버튼을 눌러주시겠어요?\n \n"
+    "message": "앗! 페이지 내용을 읽는데 실패했어요.\n\n1. 유튜브의 경우에는 요약하는데 3초 정도 걸릴 수 있어요.\n   잠시 후에 다시 시도해주세요.\n\n2. 새로 설치하신 경우에는 페이지를 한 번 새로고침해주세요!\n\n3. 새 탭, 크롬 웹스토어, 크롬 설정 페이지에서는\n   아쉽게도 요약 기능을 사용할 수 없어요 😅\n   다른 페이지로 이동하신 후 다시 시도해주세요!"
chrome-extension/public/_locales/en/messages.json (1)

57-57: 에러 메시지의 가독성 개선이 필요합니다.

현재 메시지가 너무 길어 한 번에 읽기 어려울 수 있습니다. 다음과 같이 구조화된 형식으로 변경하는 것을 제안드립니다:

- "message": "Oops! Summary feature isn't available on new tabs, Chrome Web Store, or Chrome settings pages. Would you mind trying again on a different page?\n- If you just installed the extension, please refresh the page once!\n- For YouTube pages, summarizing may take about 3 seconds. Thanks for your patience ⏳"
+ "message": "죄송합니다! 다음 페이지에서는 요약 기능을 사용할 수 없습니다:\n• 새 탭\n• Chrome 웹 스토어\n• Chrome 설정\n\n도움말:\n• 확장 프로그램을 방금 설치하셨다면 페이지를 새로고침해 주세요\n• YouTube 영상 요약은 약 3초 정도 소요됩니다 ⏳"

Also applies to: 60-60

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f00ec78 and 3a585b8.

📒 Files selected for processing (9)
  • chrome-extension/public/_locales/en/messages.json (2 hunks)
  • chrome-extension/public/_locales/ko/messages.json (2 hunks)
  • chrome-extension/utils/getPrompt.ts (0 hunks)
  • packages/shared/src/utils/extension/bridge/pageContent.ts (1 hunks)
  • packages/shared/src/utils/extension/module/Tab.ts (1 hunks)
  • pages/side-panel/src/components/Summary.tsx (1 hunks)
  • pages/side-panel/src/components/SummaryHeader.tsx (1 hunks)
  • pages/side-panel/src/components/SummaryProvider.tsx (1 hunks)
  • pages/side-panel/src/hooks/useSummary.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • chrome-extension/utils/getPrompt.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • pages/side-panel/src/components/SummaryProvider.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
packages/shared/src/utils/extension/module/Tab.ts

[error] 10-10: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (17)
pages/side-panel/src/components/Summary.tsx (1)

7-7: 컨텍스트 구조분해가 적절히 구현되었습니다.

errorMessage를 포함한 컨텍스트 구조분해가 깔끔하게 구현되었습니다.

packages/shared/src/utils/extension/module/Tab.ts (1)

1-1: LGTM: 임포트 변경이 적절합니다

제네릭 응답 타입을 사용하는 새로운 구현에 맞게 불필요한 BridgeResponse 임포트가 제거되었습니다.

pages/side-panel/src/components/SummaryHeader.tsx (1)

16-31: 구현이 잘 되었습니다!

  • 레이아웃 구조가 깔끔하고 적절합니다
  • 에러 바운더리와 로딩 상태 처리가 잘 구현되어 있습니다
  • 반응형 디자인이 잘 적용되어 있습니다
pages/side-panel/src/hooks/useSummary.ts (2)

2-14: 상태 관리가 잘 구현되었습니다!

Category 타입과 새로운 상태 변수들이 적절하게 추가되었습니다.


Line range hint 20-34: 에러 처리 로직이 여전히 개선이 필요합니다

이전 리뷰에서 지적된 문제가 아직 해결되지 않았습니다. 에러 발생 시 카테고리를 초기화하는 것이 필요합니다.

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

    } catch (e) {
      setErrorMessage(I18n.get('error_get_page_content'));
+     setCategory('others');
      return;
    }
packages/shared/src/utils/extension/bridge/pageContent.ts (5)

1-6: 타입 정의와 임포트가 명확하게 구성되어 있습니다.

Category 타입이 명확하게 정의되어 있으며, 필요한 의존성들이 적절하게 임포트되어 있습니다.


11-12: 타입 안정성이 잘 보장되어 있습니다.

제네릭을 사용하여 메시지의 요청/응답 타입이 명확하게 정의되어 있습니다.


14-18: YouTube URL 검증 로직 개선이 필요합니다.

현재 구현은 https://www.youtube.com/watch? 형식만 처리할 수 있습니다. 다음과 같은 URL 형식도 처리할 수 있도록 개선이 필요합니다:

  • youtu.be 단축 URL
  • 모바일 URL (m.youtube.com)
  • 임베드 URL (youtube.com/embed)

20-23: YouTube 트랜스크립트 추출 시 예외 처리가 필요합니다.

다음과 같은 상황에서 오류가 발생할 수 있습니다:

  • 자막이 없는 영상
  • 비공개 영상
  • 네트워크 오류

32-39: 메시지 응답 처리 개선이 필요합니다.

  1. 비동기 응답 처리를 위해 true를 반환해야 합니다.
  2. 이벤트 리스너가 중복 등록될 수 있는 문제가 있습니다.
chrome-extension/public/_locales/ko/messages.json (4)

3-3: 확장 프로그램 설명이 더욱 친근하고 매력적으로 개선되었습니다!

사용자 친화적인 톤과 이모지 사용이 적절합니다.


6-6: 확장 프로그램 이름이 사용자와의 관계를 잘 표현하고 있습니다!

"든든한 친구"라는 표현이 프로그램의 성격을 잘 나타내고 있습니다.


9-9: 빈 상태 메시지가 사용자를 자연스럽게 행동으로 이끕니다!

긍정적인 톤과 이모지 사용이 적절합니다.


66-66: 저장 공간 초과 메시지가 적절한 해결책을 제시합니다!

사용자에게 친근하게 정리를 권유하는 톤이 좋습니다.

chrome-extension/public/_locales/en/messages.json (3)

3-3: 브랜딩 메시지가 잘 개선되었습니다! ✨

친근하고 따뜻한 톤으로 잘 수정되었으며, 이모지 사용도 적절합니다.

Also applies to: 6-6


9-9: 빈 상태 메시지가 사용자 친화적으로 개선되었습니다! 👍

사용자의 첫 메모 작성을 자연스럽게 유도하는 메시지로 잘 작성되었습니다.


66-66: 저장 공간 경고 메시지가 적절히 개선되었습니다! 💾

기술적인 제한사항을 유머러스하게 전달하면서도 필요한 액션을 명확히 안내하고 있습니다.

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

🧹 Outside diff range and nitpick comments (25)
packages/shared/src/types/storage.ts (2)

1-1: 상수 임포트 변경이 잘 되었습니다.

상수 관리를 중앙화하여 STORAGE_KEYS로 통합한 것은 좋은 접근입니다. 다만, 이 상수의 역할을 설명하는 간단한 주석을 추가하면 좋을 것 같습니다.

다음과 같은 주석 추가를 제안합니다:

+// 스토리지 키 상수 관리를 위한 중앙 집중식 객체
import { STORAGE_KEYS } from '@src/constants';

3-3: 타입 정의가 더 안전하게 개선되었습니다.

StorageKeyTypeSTORAGE_KEYS 객체의 값들로 직접 유도하는 방식은 다음과 같은 이점이 있습니다:

  • 타입 안정성 향상: 스토리지 키가 추가되거나 변경될 때 자동으로 타입이 업데이트됩니다
  • 유지보수성 개선: 타입을 수동으로 관리할 필요가 없어졌습니다
pages/options/src/components/index.ts (1)

Line range hint 1-5: 배럴 내보내기 구조 개선 제안

현재 컴포넌트들이 개별적으로 내보내지고 있습니다. 더 나은 유지보수성과 일관성을 위해 다음과 같은 구조를 제안드립니다:

-export { default as Header } from './Header';
-export { default as Option } from './Option';
-export { default as OptionForm } from './OptionForm';
-export { default as QueryProvider } from './QueryProvider';
+export type {
+  HeaderProps,
+  OptionProps,
+  OptionFormProps,
+  QueryProviderProps,
+} from './types';
+
+export {
+  Header,
+  Option,
+  OptionForm,
+  QueryProvider,
+} from './components';

이렇게 구성하면:

  1. 타입과 컴포넌트를 명확하게 구분할 수 있습니다
  2. 향후 컴포넌트 추가 시 더 체계적으로 관리할 수 있습니다
  3. 번들러의 트리쉐이킹 최적화가 더 효과적으로 동작할 수 있습니다
packages/shared/src/constants/index.ts (2)

7-7: 새로운 export 문이 일관성 있게 구현되었습니다.

기존 export 패턴을 잘 따르고 있으며, 알파벳 순서도 적절히 유지되었습니다. 다만, 새로 추가된 Prompt 상수들의 용도와 구조를 README나 주석으로 문서화하면 좋을 것 같습니다.


7-7: 공유 패키지의 상수 관리에 대한 제안

shared 패키지에 추가된 Prompt 상수들이 여러 곳에서 사용될 것으로 예상됩니다. 다음 사항들을 고려해 주시면 좋겠습니다:

  1. 타입 정의를 명확히 하여 타입 안정성 보장
  2. 각 프롬프트의 사용 사례와 예상 결과를 문서화
  3. 프롬프트 수정 시 영향받는 기능들을 파악할 수 있도록 관련 정보 추가
pages/options/src/Options.tsx (1)

7-12: 레이아웃 구조가 깔끔하게 정리되었습니다.

다음 사항들이 잘 구현되었습니다:

  • 중앙 정렬된 레이아웃 (mx-auto)
  • 반응형 디자인을 고려한 최대 너비 설정
  • 적절한 여백 처리

하나의 제안사항이 있습니다:

접근성 향상을 위해 다음과 같이 main 태그에 rolearia-label을 추가하는 것을 고려해보세요:

-      <main className="mx-auto max-w-[1000px] px-8 text-start text-base">
+      <main 
+        role="main"
+        aria-label="옵션 설정"
+        className="mx-auto max-w-[1000px] px-8 text-start text-base"
+      >
packages/shared/src/constants/Prompt.ts (1)

9-16: 웹 콘텐츠 프롬프트와 YouTube 프롬프트의 중복 로직 개선 필요

웹 콘텐츠와 YouTube 콘텐츠의 프롬프트 구조가 거의 동일합니다. 중복을 제거하고 재사용성을 높일 수 있습니다.

다음과 같이 공통 템플릿을 분리하는 것을 제안합니다:

+const COMMON_TEMPLATE = `
+핵심 내용 요약
+- 주요 주제 또는 메시지 (2-3문장)
+- 가장 중요한 정보 포인트 (3-5개)
+- 특별히 강조된 내용이나 차별점
+`;

 export const DEFAULT_PROMPTS = {
   youtube: `\
-유튜브 콘텐츠 요약 프롬프트 아래 영상의 내용을 체계적으로 분석하고 핵심 정보를 요약해주세요.
-핵심 내용 요약
-- 영상의 주요 주제 또는 메시지 (2-3문장)
-- 가장 중요한 정보 포인트 (3-5개)
-- 특별히 강조된 내용이나 차별점\
+유튜브 콘텐츠 요약 프롬프트 아래 영상의 내용을 체계적으로 분석하고 핵심 정보를 요약해주세요.\n${COMMON_TEMPLATE}\
 `,
   web: `\
-웹사이트 콘텐츠 요약 프롬프트 아래 웹사이트의 내용을 체계적으로 분석하고 핵심 정보를 요약해주세요.
-핵심 내용 요약
-- 주요 주제 또는 메시지 (2-3문장)
-- 가장 중요한 정보 포인트 (3-5개)
-- 특별히 강조된 내용이나 차별점\
+웹사이트 콘텐츠 요약 프롬프트 아래 웹사이트의 내용을 체계적으로 분석하고 핵심 정보를 요약해주세요.\n${COMMON_TEMPLATE}\
 `,
 };
pages/options/src/components/Header.tsx (1)

5-11: UI 구현이 깔끔하며, 접근성을 고려한 점이 좋습니다.

  • Tailwind CSS를 활용한 일관된 스타일링
  • 로고 이미지에 대한 적절한 alt 텍스트 제공
  • Flexbox를 활용한 효율적인 레이아웃

모바일 환경을 위한 반응형 디자인을 고려해보시는 것은 어떨까요? 다음과 같은 수정을 제안드립니다:

-    <header className="border-b border-gray-200 bg-white px-6 py-4 shadow-sm">
+    <header className="border-b border-gray-200 bg-white px-4 sm:px-6 py-3 sm:py-4 shadow-sm">
     <div className="flex items-center justify-between">
       <div className="flex items-center gap-3">
-        <img src="/icon-48.png" alt="logo" className="h-8 w-8" />
-        <h1 className="text-2xl font-bold text-gray-800">설정</h1>
+        <img src="/icon-48.png" alt="logo" className="h-6 w-6 sm:h-8 sm:w-8" />
+        <h1 className="text-xl sm:text-2xl font-bold text-gray-800">설정</h1>
       </div>
     </div>
    </header>
pages/options/src/index.tsx (1)

16-17: 렌더링 로직이 최신 React 방식으로 구현되었습니다.

다만, 에러 바운더리 구현을 고려해보시는 것이 좋을 것 같습니다. YouTube 콘텐츠를 다루는 기능이 추가되었으므로, 외부 API 호출 실패 시의 폴백 UI를 제공하면 좋을 것 같습니다.

다음과 같은 에러 바운더리 구현을 제안드립니다:

+import { ErrorBoundary } from 'react-error-boundary';
+
+function ErrorFallback({ error }: { error: Error }) {
+  return (
+    <div role="alert">
+      <p>오류가 발생했습니다:</p>
+      <pre>{error.message}</pre>
+    </div>
+  );
+}

 function init() {
   const appContainer = document.querySelector('#app-container');
   if (!appContainer) {
     throw new Error('Can not find #app-container');
   }
   const root = createRoot(appContainer);
-  root.render(<Options />);
+  root.render(
+    <ErrorBoundary FallbackComponent={ErrorFallback}>
+      <Options />
+    </ErrorBoundary>
+  );
 }
pages/options/src/components/QueryProvider.tsx (1)

5-14: QueryClient 구성을 개선할 수 있습니다.

현재 구현은 작동하지만, 다음과 같은 개선사항을 고려해보세요:

  1. 캐시 관리 및 메모리 누수 방지를 위한 구성
  2. 오류 처리 전략 추가
  3. 재시도 정책 설정

다음과 같이 개선된 구성을 제안합니다:

 export default function QueryProvider({ children }: PropsWithChildren) {
   const [queryClient] = useState(
     () =>
       new QueryClient({
-        defaultOptions: { queries: { refetchOnWindowFocus: true } },
+        defaultOptions: {
+          queries: {
+            refetchOnWindowFocus: true,
+            retry: 1,
+            staleTime: 5 * 60 * 1000,
+            cacheTime: 10 * 60 * 1000,
+          },
+        },
       }),
   );

+  useEffect(() => {
+    return () => {
+      queryClient.clear();
+    };
+  }, [queryClient]);

   return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
 }

추가로 필요한 import:

import { useEffect } from 'react';
pages/options/src/index.css (2)

14-20: border 리셋 설정에 대한 설명 추가 필요

border 스타일을 전역적으로 리셋하는 것은 일관된 스타일링을 위해 좋은 접근입니다. 하지만 이 설정의 목적과 영향을 설명하는 주석을 추가하면 좋겠습니다.

+/* 모든 요소의 기본 테두리 스타일을 리셋하여 일관된 스타일링 제공 */
*,
::before,
::after {
  border-width: 0;
  border-style: solid;
  border-color: theme('borderColor.DEFAULT', currentColor);
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 19-19: Unexpected unknown function: theme

Use a known function instead.
See MDN web docs for more details.

(lint/correctness/noUnknownFunction)


CDN 폰트 URL이 유효하지 않아 수정이 필요합니다

CDN URL(https://cdn.jsdelivr.net/gh/Project-Noonnu/[email protected]/Pretendard-Regular.woff2)이 404 오류를 반환하고 있습니다. 이는 웹 폰트가 정상적으로 로드되지 않아 사용자 경험에 영향을 미칠 수 있습니다.

다음 중 하나의 조치가 필요합니다:

  • 유효한 Pretendard 폰트 CDN URL로 업데이트
  • 로컬에 폰트 파일을 호스팅하여 안정적인 제공 보장
  • Google Fonts나 다른 신뢰할 수 있는 폰트 서비스로 대체
🔗 Analysis chain

Line range hint 6-10: CDN 폰트 로딩 성능 확인 필요

폰트 설정이 잘 구성되어 있으며 font-display: swap을 사용한 것은 좋은 방법입니다. 하지만 CDN을 통한 폰트 로딩이 성능에 영향을 미칠 수 있습니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# CDN 폰트 URL이 유효한지 확인
curl -I 'https://cdn.jsdelivr.net/gh/Project-Noonnu/[email protected]/Pretendard-Regular.woff2'

Length of output: 1071

🧰 Tools
🪛 Biome (1.9.4)

[error] 19-19: Unexpected unknown function: theme

Use a known function instead.
See MDN web docs for more details.

(lint/correctness/noUnknownFunction)

packages/shared/src/utils/extension/module/Storage.ts (1)

1-20: 스토리지 클래스 아키텍처 개선 제안

현재 구현은 chrome.storage.sync의 기본적인 래퍼 기능만 제공합니다. 다음과 같은 기능 추가를 고려해보세요:

  1. 데이터 유효성 검증 레이어
  2. 스토리지 작업 로깅
  3. 캐싱 메커니즘
  4. 마이그레이션 지원
🧰 Tools
🪛 Biome (1.9.4)

[error] 5-20: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

packages/shared/src/utils/extension/Prompt.ts (1)

9-17: 단일 책임 원칙(SRP)을 적용한 리팩토링 제안

현재 함수가 스토리지 접근과 프롬프트 생성이라는 두 가지 책임을 가지고 있습니다. 이를 분리하여 유지보수성과 테스트 용이성을 향상시킬 수 있습니다.

다음과 같은 구조로 분리하는 것을 제안합니다:

const getPromptsFromStorage = async () => {
  try {
    const youtubePrompts = await Storage.get(STORAGE_KEYS.youtubePrompts);
    const webPrompts = await Storage.get(STORAGE_KEYS.webPrompts);
    return {
      youtube: youtubePrompts ?? DEFAULT_PROMPTS.youtube,
      web: webPrompts ?? DEFAULT_PROMPTS.web
    };
  } catch (error) {
    console.error('Failed to retrieve prompts from storage:', error);
    return DEFAULT_PROMPTS;
  }
};

export const getPrompt = async ({ language, type }: GetPromptProps): Promise<string> => {
  if (!language?.trim()) {
    throw new Error('Language parameter is required');
  }

  const prompts = await getPromptsFromStorage();
  const languagePrompt = `${PROMPT.language} ${language}`;
  
  return type === 'youtube' 
    ? prompts.youtube + languagePrompt 
    : prompts.web + languagePrompt;
};
pages/side-panel/src/components/SummaryHeader.tsx (2)

8-8: 타입 안전성 개선 필요

category 값의 타입이 명시적으로 정의되어 있지 않습니다. 타입 안전성을 위해 다음과 같이 타입을 정의하는 것을 추천드립니다:

type Category = 'youtube' | 'webpage' | 'others';

35-37: 접근성 개선 필요

설정 버튼에 접근성 레이블이 없습니다. 스크린 리더 사용자를 위해 다음과 같이 aria-label을 추가하는 것이 좋습니다:

-<Button variant="ghost" size="icon" onClick={handleOptionClick}>
+<Button 
+  variant="ghost" 
+  size="icon" 
+  onClick={handleOptionClick}
+  aria-label="설정 열기">
  <SettingsIcon size={16} />
</Button>
packages/shared/src/utils/extension/bridge/pageContent.ts (2)

11-12: JSDoc 문서 개선이 필요합니다.

현재 JSDoc에 반환 타입과 카테고리에 대한 설명이 누락되어 있습니다.

다음과 같이 개선하는 것을 제안합니다:

/**
 * Tab에게 페이지 컨텐츠를 요청한다.
+ * @returns {Promise<{content: string, category: Category}>} 페이지 컨텐츠와 카테고리
+ * @description 'youtube' 또는 'others' 카테고리에 따라 다른 컨텐츠를 반환합니다.
 */

24-27: getContent 함수의 에러 처리와 로깅 개선이 필요합니다.

카테고리별 컨텐츠 추출 실패 시의 처리가 누락되어 있습니다.

다음과 같은 개선을 제안합니다:

const getContent = async (url: string, category: Category) => {
+  try {
    if (category === 'youtube') return await getContentFromYoutube(url);
    return getContentFromWeb();
+  } catch (error) {
+    console.error(`컨텐츠 추출 실패 (카테고리: ${category}):`, error);
+    return '컨텐츠를 추출할 수 없습니다.';
+  }
};
pages/options/src/components/OptionForm.tsx (1)

Line range hint 13-95: 컴포넌트 아키텍처 개선 제안

  1. 비동기 작업에 대한 사용자 피드백이 없습니다:
    • 데이터 다운로드 중
    • 저장 완료/실패
    • 초기화 완료
  2. ref 대신 제어 컴포넌트 사용을 고려해보세요:
    • 폼 상태 관리가 더 명확해집니다
    • react-hook-form 같은 라이브러리 활용이 가능합니다

이러한 개선사항들을 구현하는데 도움이 필요하시다면 말씀해 주세요.

pages/options/src/components/Option.tsx (3)

4-4: 사용하지 않는 Form 임포트 제거 필요

Form 컴포넌트가 임포트되었지만 코드에서 사용되지 않고 있습니다.

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

-import { Button, Form, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@extension/ui';
+import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@extension/ui';
🧰 Tools
🪛 eslint

[error] 4-4: 'Form' is defined but never used.

(@typescript-eslint/no-unused-vars)


11-17: 폼 유효성 검사 규칙 추가 필요

현재 폼 필드에 대한 유효성 검사 규칙이 없습니다. 프롬프트 길이 제한(1000자)을 폼 레벨에서 먼저 검증하는 것이 좋습니다.

다음과 같이 수정을 제안드립니다:

  const { register, handleSubmit, setValue, watch } = useForm({
    defaultValues: {
      youtubePrompt: '',
      webPrompt: '',
      language: 'ko',
    },
+   rules: {
+     youtubePrompt: {
+       maxLength: { value: 1000, message: '프롬프트는 최대 1000자까지만 입력할 수 있습니다.' }
+     },
+     webPrompt: {
+       maxLength: { value: 1000, message: '프롬프트는 최대 1000자까지만 입력할 수 있습니다.' }
+     }
+   }
  });

40-87: UI/UX 및 접근성 개선 필요

현재 UI에 로딩 표시기가 없고, 텍스트 영역의 글자 수 표시가 없으며, 접근성 개선이 필요합니다.

다음과 같은 개선을 제안드립니다:

  1. 로딩 상태 표시
  2. 텍스트 영역 글자 수 표시
  3. ARIA 레이블 추가
  return (
+   {isLoading ? (
+     <div>로딩 중...</div>
+   ) : (
    <div className="container mx-auto space-y-8 p-4">
      <section className="mb-8">
-       <h2 className="mb-4 text-xl font-semibold">언어 설정</h2>
+       <h2 className="mb-4 text-xl font-semibold" id="language-setting">언어 설정</h2>
        <Select
          defaultValue="ko"
          onValueChange={value => setValue('language', value)}
          value={watch('language')}
+         aria-labelledby="language-setting"
        >
        ...
      </section>

      <form onSubmit={onSubmit} className="space-y-8">
        <section>
          <h2 className="mb-4 text-xl font-semibold">YouTube 프롬프트 설정</h2>
          <div className="space-y-2 rounded-lg border p-4">
            <div className="flex-1">
              <textarea
                className="border-input bg-background min-h-[400px] w-full rounded-md border px-3 py-2"
                placeholder="프롬프트를 입력하세요"
+               aria-label="YouTube 프롬프트"
                {...register('youtubePrompt')}
              />
+             <div className="text-sm text-gray-500 mt-2">
+               {watch('youtubePrompt')?.length || 0}/1000자
+             </div>
            </div>
          </div>
        </section>
        ...
      </form>
    </div>
+   )}
  );
chrome-extension/lib/background/index.ts (3)

Line range hint 77-83: OpenAI 모델명에 오타가 있습니다.

'gpt-4o-mini'는 올바른 모델명이 아닙니다. 이는 런타임 에러를 발생시킬 수 있습니다.

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

- model: 'gpt-4o-mini',
+ model: 'gpt-4',  // 또는 사용하고자 하는 실제 모델명

Line range hint 85-89: 스트림 처리에 대한 에러 처리가 필요합니다.

스트림 처리 중 발생할 수 있는 오류나 연결 종료에 대한 처리가 없습니다.

다음과 같이 수정하는 것을 제안드립니다:

- for await (const chunk of stream) {
-   const message = chunk.choices[0]?.delta?.content;
-   port.postMessage(message);
- }
+ try {
+   for await (const chunk of stream) {
+     const message = chunk.choices[0]?.delta?.content;
+     if (message) port.postMessage(message);
+   }
+ } catch (error) {
+   console.error('스트림 처리 중 오류 발생:', error);
+   port.postMessage({ error: '요약 생성 중 오류가 발생했습니다.' });
+ } finally {
+   port.postMessage({ done: true });
+ }

Line range hint 1-89: 코드 모듈화를 고려해보세요.

현재 background 스크립트가 너무 많은 책임을 가지고 있습니다. 다음과 같은 모듈화를 제안드립니다:

  1. 설치 관련 로직
  2. 컨텍스트 메뉴 관련 로직
  3. 메시지 처리 로직
  4. 탭 관리 로직

각 책임을 별도의 모듈로 분리하면 코드의 유지보수성과 테스트 용이성이 향상될 것입니다. 새로운 파일 구조를 제안드립니다:

background/
  ├── installation.ts
  ├── contextMenu.ts
  ├── messageHandler.ts
  ├── tabManager.ts
  └── index.ts
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 3a585b8 and 45dd3bf.

📒 Files selected for processing (22)
  • chrome-extension/lib/background/index.ts (3 hunks)
  • chrome-extension/utils/getPrompt.ts (0 hunks)
  • chrome-extension/utils/index.ts (0 hunks)
  • packages/shared/src/constants/Prompt.ts (1 hunks)
  • packages/shared/src/constants/Storage.ts (1 hunks)
  • packages/shared/src/constants/index.ts (1 hunks)
  • packages/shared/src/hooks/extension/useOptionQuery.ts (1 hunks)
  • packages/shared/src/types/storage.ts (1 hunks)
  • packages/shared/src/utils/extension/Prompt.ts (1 hunks)
  • packages/shared/src/utils/extension/bridge/pageContent.ts (1 hunks)
  • packages/shared/src/utils/extension/index.ts (1 hunks)
  • packages/shared/src/utils/extension/module/Storage.ts (1 hunks)
  • pages/options/src/Options.tsx (1 hunks)
  • pages/options/src/components/Header.tsx (1 hunks)
  • pages/options/src/components/Option.tsx (1 hunks)
  • pages/options/src/components/OptionForm.tsx (2 hunks)
  • pages/options/src/components/QueryProvider.tsx (1 hunks)
  • pages/options/src/components/index.ts (1 hunks)
  • pages/options/src/index.css (1 hunks)
  • pages/options/src/index.tsx (2 hunks)
  • pages/side-panel/src/components/SummaryHeader.tsx (1 hunks)
  • pages/side-panel/src/hooks/useSummary.ts (3 hunks)
💤 Files with no reviewable changes (2)
  • chrome-extension/utils/getPrompt.ts
  • chrome-extension/utils/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • pages/side-panel/src/hooks/useSummary.ts
🧰 Additional context used
🪛 eslint
pages/options/src/components/Option.tsx

[error] 4-4: 'Form' is defined but never used.

(@typescript-eslint/no-unused-vars)

🪛 Biome (1.9.4)
pages/options/src/index.css

[error] 19-19: Unexpected unknown function: theme

Use a known function instead.
See MDN web docs for more details.

(lint/correctness/noUnknownFunction)

🔇 Additional comments (19)
packages/shared/src/constants/Storage.ts (2)

1-5: 타입 안전성이 보장된 새로운 스토리지 키 구조로 잘 개선되었습니다.

상수들을 하나의 객체로 구조화하고 as const를 사용하여 타입 안전성을 보장한 점이 좋습니다. 스토리지 키들이 용도별로 잘 구분되어 있어 향후 유지보수성도 향상될 것으로 보입니다.


1-5: 기존 상수 제거로 인한 영향도를 확인해주세요.

STORAGE_OPTION_LANGUAGESTORAGE_OPTION_LIST 상수가 제거되었습니다. 이 변경으로 인한 영향을 받을 수 있는 코드를 확인해보시기 바랍니다.

다음 스크립트로 영향도를 확인할 수 있습니다:

packages/shared/src/utils/extension/index.ts (1)

4-4: 모듈 내보내기가 올바르게 구성되었습니다!

새로운 Prompt 모듈의 내보내기가 적절하게 추가되었습니다.

Prompt 모듈의 존재 여부와 내용을 확인하겠습니다:

✅ Verification successful

Prompt 모듈이 정상적으로 존재하며 예상된 기능을 포함하고 있습니다!

  • packages/shared/src/utils/extension/Prompt.ts 파일이 확인되었습니다.
  • 모듈이 DEFAULT_PROMPTS, PROMPT, STORAGE_KEYS 상수들을 올바르게 사용하고 있습니다.
  • YouTube와 웹 콘텐츠에 대한 프롬프트 처리 로직이 적절히 구현되어 있습니다.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Prompt 모듈의 존재 여부와 내용을 확인합니다.

# Prompt 모듈 파일의 존재 여부 확인
fd -t f "Prompt.ts" "packages/shared/src/utils/extension"

# 모듈 내용 확인 (DEFAULT_PROMPTS와 PROMPT 상수 정의 확인)
rg -A 5 "DEFAULT_PROMPTS|PROMPT =" "packages/shared/src/utils/extension"

Length of output: 1316

packages/shared/src/hooks/extension/useOptionQuery.ts (1)

1-1: 스토리지 키 상수 변경이 올바르게 적용되었습니다.

상수 구조 변경이 일관성 있게 적용되었으며, 코드가 더 체계적으로 구성되었습니다.

스토리지 키 사용의 일관성을 확인하기 위해 다음 스크립트를 실행합니다:

Also applies to: 7-7

✅ Verification successful

스토리지 키 상수 변경이 성공적으로 완료되었습니다.

검증 결과, 이전 STORAGE_OPTION_LANGUAGE 상수는 완전히 제거되었으며, 새로운 STORAGE_KEYS.language 상수가 다음 파일들에서 일관되게 사용되고 있습니다:

  • packages/shared/src/hooks/extension/useOptionQuery.ts
  • pages/options/src/components/Option.tsx
  • pages/options/src/components/OptionForm.tsx
  • chrome-extension/lib/background/index.ts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: STORAGE_KEYS 상수의 일관된 사용을 확인합니다.

# STORAGE_OPTION_LANGUAGE의 사용이 완전히 제거되었는지 확인
echo "이전 상수 사용 검사:"
rg "STORAGE_OPTION_LANGUAGE"

# 새로운 STORAGE_KEYS.language의 사용 확인
echo "\n새로운 상수 사용 검사:"
rg "STORAGE_KEYS.language"

Length of output: 814

pages/options/src/Options.tsx (1)

3-3: 컴포넌트 의존성이 개선되었습니다.

컴포넌트 구조가 단순화되어 유지보수가 더 쉬워졌습니다. QueryProvider를 사용하여 데이터 관리를 중앙화한 것이 좋은 접근 방식입니다.

packages/shared/src/constants/Prompt.ts (1)

1-8: 프롬프트 구조가 명확하고 체계적입니다.

YouTube 콘텐츠 요약을 위한 프롬프트가 잘 구성되어 있습니다. 주제, 핵심 포인트, 차별점 등을 단계별로 요청하는 구조가 효과적입니다.

pages/options/src/components/Header.tsx (1)

3-3: 컴포넌트 이름 변경이 적절합니다.

컴포넌트의 이름을 Page에서 Header로 변경한 것이 컴포넌트의 실제 역할과 더 잘 부합합니다.

pages/options/src/index.tsx (2)

5-8: React 18의 새로운 렌더링 API를 적절하게 도입했습니다.

createRoot를 사용하여 React 18의 동시성 렌더링 기능을 활용할 수 있게 되었습니다.


Line range hint 1-4: 초기화 로직이 적절히 구성되어 있습니다.

Sentry 초기화와 전역 스타일 임포트가 잘 되어있습니다.

pages/options/src/components/QueryProvider.tsx (1)

1-3: 의존성 및 클라이언트 지시자가 올바르게 구성되었습니다!

필요한 모든 의존성이 적절하게 가져와졌으며, Next.js의 클라이언트 컴포넌트 지시자가 올바르게 배치되었습니다.

pages/options/src/index.css (1)

22-24: 타이포그래피 설정 승인

Tailwind Typography 플러그인과 잘 통합되어 있으며, 헤딩 색상을 CSS 변수를 통해 관리하는 것은 좋은 접근입니다.

packages/shared/src/utils/extension/module/Storage.ts (1)

6-13: 타입 안전성 개선 필요

제네릭 타입 T를 사용하여 코드가 간단해졌지만, 런타임에서 실제 저장된 값이 예상된 타입과 일치하는지 확인할 수 없습니다.

타입 안전성을 위해 다음과 같은 검증 로직 추가를 고려해보세요:

  static async get<T>(key: StorageKeyType): Promise<T> {
    try {
      const storage = await chrome.storage.sync.get(key);
+     if (storage[key] === undefined) {
+       throw new Error(`${key} 값을 찾을 수 없습니다`);
+     }
      return storage[key];
    } catch (error) {
      throw new Error(I18n.get('error_get_storage'));
    }
  }
packages/shared/src/utils/extension/bridge/pageContent.ts (4)

1-6: 타입 정의와 임포트가 명확하게 구성되어 있습니다.

Category 타입이 'youtube'와 'others'로 명확하게 구분되어 있어 확장성이 좋습니다.


14-18: 🛠️ Refactor suggestion

YouTube URL 검증 로직 개선이 필요합니다.

현재 구현은 기본적인 URL 형식만 확인하고 있어, 다양한 YouTube URL 패턴(예: youtu.be, 모바일 URL 등)을 처리하지 못할 수 있습니다.

다음과 같이 정규식을 사용한 개선을 제안합니다:

-const checkYoutube = (url: string) => url.startsWith('https://www.youtube.com/watch?');
+const checkYoutube = (url: string) => {
+  const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+$/;
+  return youtubeRegex.test(url);
+};

20-23: ⚠️ Potential issue

YouTube 트랜스크립트 추출 시 예외 처리가 필요합니다.

자막이 없는 영상이나 네트워크 오류 발생 시 처리가 누락되어 있습니다.

다음과 같이 예외 처리를 추가하는 것을 제안합니다:

const getContentFromYoutube = async (url: string) => {
-  const transcripts = await YoutubeTranscript.fetchTranscript(url);
-  return transcripts.map(transcript => transcript.text).join('\n');
+  try {
+    const transcripts = await YoutubeTranscript.fetchTranscript(url);
+    return transcripts.map(transcript => transcript.text).join('\n');
+  } catch (error) {
+    console.error('YouTube 트랜스크립트 추출 실패:', error);
+    return '이 동영상에는 자막이 없거나 자막을 불러올 수 없습니다.';
+  }
};

32-38: ⚠️ Potential issue

메시지 응답 처리 개선이 필요합니다.

  1. 이벤트 리스너가 함수 내부에서 등록되어 중복 등록될 수 있습니다.
  2. 비동기 응답 처리가 올바르게 명시되어 있지 않습니다.

다음과 같은 개선을 제안합니다:

-export const responsePageContent = async () => {
-  const url = location.href;
-  const category = getCategory(url);
-  const content = await getContent(url, category);
-
-  Runtime.onMessage(BRIDGE_TYPE_PAGE_CONTENT, async (_, __, sendResponse) => {
-    sendResponse({ content, category });
-  });
-};
+// 이벤트 리스너를 전역 범위에서 한 번만 등록
+Runtime.onMessage(BRIDGE_TYPE_PAGE_CONTENT, async (_, __, sendResponse) => {
+  try {
+    const url = location.href;
+    const category = getCategory(url);
+    const content = await getContent(url, category);
+    sendResponse({ content, category });
+  } catch (error) {
+    console.error('페이지 컨텐츠 응답 실패:', error);
+    sendResponse({ content: '컨텐츠를 불러올 수 없습니다.', category: 'others' });
+  }
+  return true; // 비동기 응답 처리를 명시
+});
+
+export const responsePageContent = () => {
+  // 이벤트 리스너는 이미 등록되어 있으므로 빈 함수로 둡니다.
+};
pages/options/src/components/OptionForm.tsx (1)

3-3: 상수 구조화가 잘 되었습니다!

상수들을 STORAGE_KEYS 객체로 구조화한 것은 좋은 리팩토링 방향입니다.

chrome-extension/lib/background/index.ts (2)

3-3: 임포트 구문이 잘 구성되어 있습니다!

필요한 상수와 유틸리티 함수들이 적절하게 임포트되어 있습니다.

Also applies to: 6-6


71-74: 메시지 구조 분해가 개선되었습니다.

타입과 페이지 콘텐츠를 명확하게 구조 분해하여 가독성이 향상되었습니다.

packages/shared/src/constants/Prompt.ts Show resolved Hide resolved
packages/shared/src/utils/extension/Prompt.ts Outdated Show resolved Hide resolved
packages/shared/src/utils/extension/Prompt.ts Outdated Show resolved Hide resolved
pages/options/src/components/OptionForm.tsx Outdated Show resolved Hide resolved
pages/options/src/components/Option.tsx Show resolved Hide resolved
pages/options/src/components/Option.tsx Outdated Show resolved Hide resolved
chrome-extension/lib/background/index.ts Show resolved Hide resolved
Copy link

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

🧹 Outside diff range and nitpick comments (4)
packages/ui/global.css (1)

86-92: 텍스트 스타일링이 테마 시스템과 잘 통합되어 있습니다.

.prose 클래스가 기존 테마 시스템과 잘 통합되어 있습니다. 제목과 강조 텍스트에 대한 스타일링이 --foreground 변수를 활용하여 일관성 있게 구현되어 있습니다.

다만, 다음과 같은 개선사항을 고려해보시기 바랍니다:

 .prose {
   --tw-prose-headings: var(--foreground);
+  --tw-prose-bold: var(--foreground);
 
   strong {
-    color: var(--foreground);
+    color: var(--tw-prose-bold);
   }
 }

이렇게 수정하면 강조 텍스트의 색상을 별도의 변수로 관리할 수 있어 더 유연한 스타일링이 가능해집니다.

pages/options/src/Options.tsx (1)

9-15: 컴포넌트 구조가 개선되었습니다

  • QueryProvider로 상태 관리 래핑
  • 깔끔한 레이아웃 구조
  • 토스트 알림 기능 추가

다만 다음 사항들을 고려해보시면 좋을 것 같습니다:

  1. main 태그에 역할을 명확히 하는 aria-label 추가
  2. 반응형 디자인을 위한 미디어 쿼리 검토
 <QueryProvider>
-  <main className="mx-auto max-w-[1000px] px-8 text-start text-base">
+  <main 
+    aria-label="옵션 설정"
+    className="mx-auto max-w-[1000px] px-4 sm:px-8 text-start text-base"
+  >
     <Header />
     <Option />
   </main>
   <Toaster />
 </QueryProvider>
packages/shared/src/utils/extension/bridge/pageContent.ts (1)

6-6: 타입 안전성 개선을 위한 제안

Category 타입이 리터럴 유니온 타입으로 잘 정의되어 있습니다. 하지만 향후 확장성을 고려하여 상수 객체로 관리하는 것이 좋을 것 같습니다.

+export const CATEGORIES = {
+  YOUTUBE: 'youtube',
+  OTHERS: 'others',
+} as const;
-export type Category = 'youtube' | 'others';
+export type Category = typeof CATEGORIES[keyof typeof CATEGORIES];
packages/shared/src/utils/extension/Prompt.ts (1)

4-7: 인터페이스 문서화 추가 필요

인터페이스의 목적과 각 속성에 대한 설명을 JSDoc 형식으로 추가하면 코드의 가독성과 유지보수성이 향상될 것 같습니다.

다음과 같이 수정하는 것을 제안합니다:

+/**
+ * 프롬프트 생성에 필요한 속성들을 정의하는 인터페이스
+ */
 interface GetPromptProps {
+  /** 프롬프트 생성에 사용될 언어 */
   language: string;
+  /** 프롬프트의 카테고리 (youtube 또는 web) */
   category: Category;
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 45dd3bf and c1c41b1.

📒 Files selected for processing (9)
  • chrome-extension/lib/background/index.ts (3 hunks)
  • packages/shared/src/utils/extension/Prompt.ts (1 hunks)
  • packages/shared/src/utils/extension/bridge/pageContent.ts (1 hunks)
  • packages/ui/global.css (1 hunks)
  • pages/options/src/Options.tsx (1 hunks)
  • pages/options/src/components/Option.tsx (1 hunks)
  • pages/options/src/components/OptionForm.tsx (2 hunks)
  • pages/options/src/index.css (1 hunks)
  • pages/side-panel/src/index.css (0 hunks)
💤 Files with no reviewable changes (1)
  • pages/side-panel/src/index.css
🚧 Files skipped from review as they are similar to previous changes (3)
  • pages/options/src/components/Option.tsx
  • pages/options/src/components/OptionForm.tsx
  • chrome-extension/lib/background/index.ts
🧰 Additional context used
🪛 Biome (1.9.4)
pages/options/src/index.css

[error] 19-19: Unexpected unknown function: theme

Use a known function instead.
See MDN web docs for more details.

(lint/correctness/noUnknownFunction)

🔇 Additional comments (9)
pages/options/src/index.css (2)

Line range hint 6-11: 폰트 설정이 최적화되어 있습니다.

font-display: swap를 사용하여 웹 폰트 로딩을 최적화한 것이 좋습니다. CDN을 통한 폰트 로딩도 적절하게 구현되어 있습니다.

🧰 Tools
🪛 Biome (1.9.4)

[error] 19-19: Unexpected unknown function: theme

Use a known function instead.
See MDN web docs for more details.

(lint/correctness/noUnknownFunction)


14-20: 테두리 스타일링이 전역적으로 적용됩니다.

전역 테두리 스타일링이 일관성 있게 적용되어 있습니다. theme() 함수는 Tailwind CSS에서 유효한 함수이므로 정적 분석 도구의 경고는 무시해도 됩니다.

🧰 Tools
🪛 Biome (1.9.4)

[error] 19-19: Unexpected unknown function: theme

Use a known function instead.
See MDN web docs for more details.

(lint/correctness/noUnknownFunction)

pages/options/src/Options.tsx (1)

3-3: 새로운 의존성 추가 확인 필요

새로운 컴포넌트들의 의존성이 올바르게 추가되었는지 확인이 필요합니다.

다음 스크립트를 실행하여 필요한 패키지들이 package.json에 올바르게 추가되었는지 확인하겠습니다:

Also applies to: 5-5

✅ Verification successful

의존성이 올바르게 구성되어 있습니다

필요한 모든 패키지가 적절하게 설정되어 있는 것을 확인했습니다:

  • @extension/ui는 워크스페이스 패키지로 존재하며 Toaster 컴포넌트를 포함하고 있습니다 (@radix-ui/react-toast 의존성 확인)
  • @tanstack/react-query는 루트 package.json에 정상적으로 추가되어 있습니다 (^5.59.0)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify package dependencies
rg -l "@extension/ui|@tanstack/react-query" package.json

Length of output: 71


Script:

#!/bin/bash
# Check the actual content of package.json to verify dependencies
cat package.json

Length of output: 3901


Script:

#!/bin/bash
# Check if @extension/ui exists in workspace packages
fd -t d "ui" packages/

# Also check for any package.json files in ui directory
fd "package.json" -t f packages/ui/

Length of output: 160


Script:

#!/bin/bash
# Check the content of ui package.json
cat packages/ui/package.json

Length of output: 2590

packages/shared/src/utils/extension/bridge/pageContent.ts (4)

11-12: 타입 안전성이 잘 구현되었습니다

제네릭을 사용하여 메시지 응답 타입을 명시적으로 정의한 것이 좋습니다.


14-18: 🛠️ Refactor suggestion

YouTube URL 검증 로직 개선 필요

현재 URL 검증이 단순 문자열 비교로 되어 있어, 다양한 YouTube URL 형식을 처리하지 못할 수 있습니다.

-const checkYoutube = (url: string) => url.startsWith('https://www.youtube.com/watch?');
+const checkYoutube = (url: string) => {
+  const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+$/;
+  return youtubeRegex.test(url);
+};

20-23: ⚠️ Potential issue

YouTube 트랜스크립트 가져오기 예외 처리 필요

자막이 없는 영상이나 네트워크 오류 발생 시 처리가 필요합니다.


37-39: ⚠️ Potential issue

이벤트 리스너 등록 위치 및 메모리 누수 문제

이벤트 리스너가 함수 내부에서 등록되어 중복 등록 및 메모리 누수가 발생할 수 있습니다.

packages/shared/src/utils/extension/Prompt.ts (2)

9-17: 🛠️ Refactor suggestion

함수 구현 개선 필요

다음과 같은 개선사항들이 필요해 보입니다:

  1. Storage 작업에 대한 에러 처리
  2. 반환 타입 명시
  3. 입력값 검증
  4. 템플릿 리터럴 가독성 개선

다음과 같은 수정을 제안합니다:

-export const getPrompt = async ({ language, category }: GetPromptProps) => {
+export const getPrompt = async ({ language, category }: GetPromptProps): Promise<string> => {
+  if (!language?.trim()) {
+    throw new Error('언어 파라미터는 필수입니다');
+  }
+
+  try {
     const youtubePrompts = (await Storage.get(STORAGE_KEYS.youtubePrompts)) ?? DEFAULT_PROMPTS.youtube;
     const webPrompts = (await Storage.get(STORAGE_KEYS.webPrompts)) ?? DEFAULT_PROMPTS.web;
 
-    const languagePrompt = `${PROMPT.language} ${LANGUAGE_MAP[language] ?? 'English'}`;
+    const targetLanguage = LANGUAGE_MAP[language] ?? 'English';
+    const languagePrompt = `${PROMPT.language} ${targetLanguage}`;
 
-    if (category === 'youtube') return `${youtubePrompts} ${languagePrompt}`;
-    return `${webPrompts} ${languagePrompt}`;
+    const basePrompt = category === 'youtube' ? youtubePrompts : webPrompts;
+    return `${basePrompt} ${languagePrompt}`;
+  } catch (error) {
+    console.error('프롬프트 생성 중 오류 발생:', error);
+    throw new Error('프롬프트를 생성할 수 없습니다');
+  }
 };

Likely invalid or redundant comment.


13-13: LANGUAGE_MAP 구현 확인 필요

LANGUAGE_MAP이 지원하는 언어 목록과 매핑이 올바르게 구현되어 있는지 확인이 필요합니다.

다음 스크립트로 LANGUAGE_MAP 구현을 확인할 수 있습니다:

✅ Verification successful

LANGUAGE_MAP 구현이 적절하게 되어 있음을 확인했습니다

LANGUAGE_MAP은 다음과 같이 올바르게 구현되어 있습니다:

  • 52개의 언어 코드와 해당하는 영어 이름이 정확하게 매핑되어 있습니다
  • 지역별 변형(예: 'en-US', 'zh-CN' 등)도 적절히 포함되어 있습니다
  • LANGUAGE_LIST에는 각 언어의 원어 표기도 함께 정의되어 있습니다
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# LANGUAGE_MAP 구현 확인

# constants 파일에서 LANGUAGE_MAP 정의 검색
rg -A 10 "LANGUAGE_MAP\s*=\s*" 

Length of output: 32


Script:

#!/bin/bash
# LANGUAGE_MAP 정의를 찾기 위한 다양한 검색 시도

# 1. 파일 내용에서 LANGUAGE_MAP 검색
rg "LANGUAGE_MAP" -B 2 -A 5

# 2. LANGUAGE_MAP이 정의된 파일 찾기
fd -e ts -e js | xargs grep -l "LANGUAGE_MAP"

# 3. 상수 정의가 있을 만한 파일들 검색
fd "constant|config" -e ts -e js

Length of output: 4263


Script:

#!/bin/bash
# LANGUAGE_MAP의 전체 구현 내용 확인
cat packages/shared/src/constants/Language.ts

Length of output: 4816

if (checkYoutube(url)) return 'youtube';
return 'others';
};
const getContentFromWeb = () => document.body.innerText;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

웹 콘텐츠 추출 성능 개선 필요

document.body.innerText는 전체 페이지 텍스트를 가져오므로 성능 문제가 발생할 수 있습니다. 주요 콘텐츠 영역만 선택적으로 추출하는 것이 좋습니다.

-const getContentFromWeb = () => document.body.innerText;
+const getContentFromWeb = () => {
+  // 주요 콘텐츠 영역 선택자 (예: article, main 등)
+  const mainContent = document.querySelector('article, main, .content');
+  return mainContent?.innerText || document.body.innerText;
+};
📝 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 getContentFromWeb = () => document.body.innerText;
const getContentFromWeb = () => {
// 주요 콘텐츠 영역 선택자 (예: article, main 등)
const mainContent = document.querySelector('article, main, .content');
return mainContent?.innerText || document.body.innerText;
};

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