Skip to content

Feat(extension): 익스텐션 ui 반영 및 바운더리 최적화 작업#213

Merged
jllee000 merged 14 commits intodevelopfrom
feat/#212/extension-optimization
Dec 22, 2025
Merged

Feat(extension): 익스텐션 ui 반영 및 바운더리 최적화 작업#213
jllee000 merged 14 commits intodevelopfrom
feat/#212/extension-optimization

Conversation

@jllee000
Copy link
Collaborator

@jllee000 jllee000 commented Dec 13, 2025

📌 Related Issues

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

📄 Tasks

  1. 헤더 수정
  2. 구글 로그인 팝업 추가 및 랜딩
  3. 바운더리 최적화 (SuspenseQuery) -> 구조상 불가.. 하단의 내용 참고해주세용!
  4. 코드 스플리팅 (MainPop.tsx)
  5. 토큰 유무에 따른 상태 분기 (토큰 없다? == 회원가입 안되어있거나, 로그인 풀린 유저 취급)

⭐ PR Point (To Reviewer)

1. Suspense가 동작하는 조건

React Suspense는 렌더링 과정 중에 Promise가 발생(throw)될 때 동작합니다.
즉, "컴포넌트가 렌더링되는 도중" 데이터가 아직 준비되지 않았음을 React가 인지해야 하며, 이 떄만 가 로딩 UI를 대신 렌더링

2. 현재 데이터 로딩 구조

[usePageMeta 훅]

  1. 컴포넌트가 먼저 렌더링됨
  2. 렌더 이후 useEffect가 실행됨
  3. chrome.tabs.query를 통해 활성 탭 정보를 조회
  4. OG 메타데이터를 비동기로 조회
  5. 결과를 state에 저장한 뒤 재렌더링

이 구조에서는 렌더링 도중에 Promise가 발생하지 않음 + 비동기 로직은 모두 렌더 이후(useEffect)에 실행
"렌더 중간에 감지하는" Suspense 입장에선.. 개입할 수 있는 시점이 없음

3. 그럼 [진혁] 왈 : useEffect를 제거하고 useQuery로 변경하면 되지않냐??

=> 찾아보니..

chrome.tabs.query는 콜백 기반의 Chrome Extension API이기 때문에,
이를 그대로 useQuery에 사용할 수는 없으며 Promise로 감싸는 과정이 필요합니다.

라고 함...

강제로 Promise 감싸면,

const getActiveTabUrl = () =>
  new Promise<string>((resolve, reject) => {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      const tab = tabs[0];
      if (!tab?.url) reject();
      else resolve(tab.url);
    });
  });

이렇게 Promise로 감싸면 useQuery 자체는 사용할 수 있다함, 근데?

Suspense는 React 렌더링 단계에서 발생한 Promise만 감지할 수 있기 때문에,
useQuery를 사용하더라도 기본 구조에서는 Suspense의 개입이 불가능하다함!

제일 최종적으로는,, 크롬 익스텐션 상

(1) suspense: true 옵션이 존재하긴 하나, Chrome Extension 팝업 환경에서는 전체 UI가 fallback으로 대체되거나 에러 시 팝업 전체가 중단되는 문제가 있어 적용하지 않는다 함
(2) chrome.tabs.query 및 chrome.storage API
얘네는 side-effect이며
React 규칙상 렌더링 단계에서 호출할 수 없고, 반드시 effect 이후에 실행되어야 합니다.
이로 인해 “렌더 중 데이터 로딩”이라는 Suspense의 전제 조건을 만족 불가

📷 Screenshot (UI 적인 변화 기록)

[ 토큰 비어있을 시 (로그인 안되어있을 시 UI케이스 추가) ]
image

[ 헤더 수정 ]
image

Summary by CodeRabbit

  • New Features

    • 토큰 기반 접근 흐름 추가 — 인증 없으면 로그인 팝업(LogOutPop) 노출, 인증 시 메인/중복 팝업 표시
  • UI/UX

    • 패널 상단에 새로운 헤더 추가(호버 시 홈 아이콘 변환, 클릭 시 사이트 열기)
    • 온보딩 최종 단계의 좌우 패딩 조정
  • 버그 수정

    • 일부 경로에서 자동 창 닫힘 호출 제거로 팝업 종료 동작 변경
  • 아이콘

    • 아이콘 세트에 신규 아이콘 4종 추가

✏️ Tip: You can customize this high-level summary in your review settings.

@jllee000 jllee000 self-assigned this Dec 13, 2025
@jllee000 jllee000 added the feat 기능 개발하라 개발 달려라 달려 label Dec 13, 2025
@vercel
Copy link

vercel bot commented Dec 13, 2025

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

Project Deployment Review Updated (UTC)
pinback-client-client Ready Ready Preview, Comment Dec 19, 2025 0:29am
pinback-client-landing Ready Ready Preview, Comment Dec 19, 2025 0:29am

@coderabbitai
Copy link

coderabbitai bot commented Dec 13, 2025

Walkthrough

익스텐션 팝업에 신규 Header 컴포넌트 추가, 토큰 기반 렌더링 도입(토큰 없으면 LogOutPop 표시), 여러 위치의 자동 창 닫기(window.close()) 호출 제거, 아이콘 목록 및 tsconfig 경로 별칭 확장 등의 변경이 포함됨.

Changes

Cohort / File(s) 변경 요약
Header 컴포넌트 추가
apps/extension/src/shared/components/Header.tsx
신규 Header 컴포넌트 추가: 호버 시 홈 아이콘을 ext_home1ext_home2 전환, 클릭 시 chrome.tabs.createhttps://www.pinback.today/ 오픈. 기본 내보내기 추가.
메인 팝업 및 로그아웃 UI
apps/extension/src/pages/MainPop.tsx, apps/extension/src/pages/LogOutPop.tsx
MainPop의 상단 아이콘 블록을 Header로 교체하고, 추가 성공 경로의 window.close() 호출 제거. LogOutPop 신규 추가: 토큰 없을 때 표시되는 로그인/온보딩 UI 및 Google 로그인 버튼.
앱 흐름 / 토큰 처리
apps/extension/src/App.tsx
chrome.storage.local에서 토큰을 읽어 isToken으로 분기: 토큰 존재 시 기존 Duplicate/Main 팝업 흐름(이전 savedData prop 제거), 토큰 부재 시 LogOutPop 렌더링으로 변경.
페이지 메타 훅 변경
apps/extension/src/hooks/usePageMeta.ts
내부 크롬 페이지 및 제목 없음 분기에서 window.close() 호출 제거(조기 반환 주석은 유지되지만 실제 조기 반환 동작은 비활성화).
아이콘 이름 확장
packages/design-system/src/icons/iconNames.ts
iconNames 배열에 'ext_home1', 'ext_home2', 'google', 'logout_chippi.2512' 항목 추가 — 공개 IconName 값 확장.
타입스크립트 경로 설정
apps/extension/tsconfig.json
@shared/*["src/shared/*"] 경로 별칭 추가 및 @hooks/* 포맷 정정(공백 추가).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • 주의 검토 포인트:
    • Header.tsx: 마우스 이벤트 처리와 아이콘 전환, 클릭 시 chrome.tabs.create 호출 URL/권한 확인
    • App.tsx: 토큰 판정 로직과 기존 Duplicate/Main 흐름 통합으로 인한 렌더 분기 변화
    • usePageMeta.ts: window.close() 제거로 인한 팝업 종료/후속 흐름 영향 확인
    • iconNames.ts: 새 아이콘명이 디자인 시스템 소비지에서 정상 로드되는지 확인
    • tsconfig.json: 경로 별칭이 실제 빌드/임포트에 반영되는지 검증

Possibly related PRs

Suggested labels

🛠️ Feature, frontend

Suggested reviewers

  • karnelll
  • constantly-dev

Poem

🐰 깡충깡충 헤더 달려가요, 아이콘은 두 발로 춤추고
토큰 문이 닫히면 로그아웃 팝업이 손짓하네
창 닫기 소리는 조용히 줄고, 길은 이어지고
홈을 누르면 탭이 반짝 열리고
업데이트에 토끼가 박수 쳐요 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive PR 변경사항이 이슈 #212의 요구사항(헤더 수정, 바운더리 로직 수정)을 구현했으나, 링크된 이슈 #25(프로그래스 바 구현)와는 관련 없는 변경사항이 포함되어 있습니다. 이슈 #212와의 직접적 연관성은 높으나, 이슈 #25(프로그래스 바 구현)가 링크된 이유가 명확하지 않습니다. 실제 구현된 변경사항이 이슈 #25 요구사항을 충족하는지 확인이 필요합니다.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 '익스텐션 ui 반영 및 바운더리 최적화 작업'으로 변경사항의 핵심인 헤더 UI 수정, 로그인 팝업 추가, 토큰 기반 상태 분기 등을 포괄적으로 설명하고 있습니다.
Description check ✅ Passed PR 설명이 템플릿 구조에 따라 관련 이슈(#212), 5가지 작업 내용, Suspense 적용 불가 이유에 대한 상세한 기술 설명, UI 스크린샷을 포함하고 있습니다.
Out of Scope Changes check ✅ Passed PR의 모든 주요 변경사항(Header 컴포넌트 추가, LogOutPop 컴포넌트 추가, 토큰 기반 상태 분기, 아이콘 확장, tsconfig 경로 별칭 추가)이 이슈 #212의 헤더 수정 및 바운더리 최적화라는 목표에 부합합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#212/extension-optimization

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7d0101 and 41c5de9.

📒 Files selected for processing (1)
  • apps/client/src/pages/onBoarding/components/funnel/step/FinalStep.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: storybook
🔇 Additional comments (1)
apps/client/src/pages/onBoarding/components/funnel/step/FinalStep.tsx (1)

7-7: LGTM!

수평 패딩 추가가 올바르게 적용되었습니다. Tailwind의 임의 값 문법이 정확하며, 컴포넌트 내 다른 간격 값들과도 일관성이 있습니다.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Dec 13, 2025

✅ Storybook chromatic 배포 확인:
🐿️ storybook

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

Caution

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

⚠️ Outside diff range comments (3)
apps/extension/src/hooks/useSaveBookmarks.ts (1)

21-31: chrome.storage.local.get/set에서 chrome.runtime.lastError 미처리
bookmarks 로컬 저장이 실패해도 조용히 진행될 수 있습니다(특히 확장 스토리지/권한/쿼터 이슈). 최소한 reject 처리해 catch로 떨어지게 만드는 게 안전합니다.

-      const result = await new Promise<{ bookmarks?: any[] }>((resolve) => {
-        chrome.storage.local.get(['bookmarks'], (items) => resolve(items));
-      });
+      const result = await new Promise<{ bookmarks?: unknown[] }>((resolve, reject) => {
+        chrome.storage.local.get(['bookmarks'], (items) => {
+          if (chrome.runtime.lastError) return reject(chrome.runtime.lastError);
+          resolve(items);
+        });
+      });

...
-      await new Promise<void>((resolve) => {
-        chrome.storage.local.set({ bookmarks }, resolve);
-      });
+      await new Promise<void>((resolve, reject) => {
+        chrome.storage.local.set({ bookmarks }, () => {
+          if (chrome.runtime.lastError) return reject(chrome.runtime.lastError);
+          resolve();
+        });
+      });
apps/extension/src/hooks/usePageMeta.ts (1)

31-57: 예외 처리 누락으로 loading이 영구 true가 될 수 있음
chrome.tabs.query 콜백이 async인데 getOgMeta(내부적으로 fetch)에서 throw되면 setLoading(false)가 실행되지 않을 수 있습니다. try/catch로 최소한 로딩 종료와 fallback meta를 보장하세요.

   useEffect(() => {
     chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => {
-      const activeTab = tabs[0];
-      if (!activeTab?.url) {
-        setLoading(false);
-        return;
-      }
-
-      const currentUrl = activeTab.url;
-
-      chrome.storage.local.set({ bookmarkedUrl: currentUrl });
-      const newMeta = await getOgMeta(currentUrl);
+      try {
+        const activeTab = tabs[0];
+        if (!activeTab?.url) {
+          setLoading(false);
+          return;
+        }
+
+        const currentUrl = activeTab.url;
+        chrome.storage.local.set({ bookmarkedUrl: currentUrl });
+        const newMeta = await getOgMeta(currentUrl);
+
+        setMeta(newMeta);
+        chrome.storage.local.set({ titleSave: newMeta.title });
+      } catch (e) {
+        setMeta({ url: '', title: '', description: '', imgUrl: '' });
+      } finally {
+        setLoading(false);
+      }
-      setMeta(newMeta);
-      setLoading(false);
-
-      chrome.storage.local.set({ titleSave: newMeta.title });
     });
   }, []);
apps/extension/src/pages/MainPop.tsx (1)

173-178: getKSTISOString() 타임존 계산이 잘못됨(지역에 따라 더 틀어질 수 있음)
현재 구현은 “KST”를 보장하지 않습니다. 의도가 “항상 KST 기준 문자열”이라면 UTC 기준으로 +9시간을 더하는 방식이 안전합니다.

-  function getKSTISOString() {
-    const now = new Date();
-    const offset = now.getTimezoneOffset() * 60000; // UTC 기준 오프셋 (분 단위)
-    const kst = new Date(now.getTime() - offset); // UTC → KST 보정
-    return kst.toISOString().slice(0, 19); // 밀리초, Z 제거
-  }
+  function getKSTISOString() {
+    const KST_OFFSET_MS = 9 * 60 * 60 * 1000;
+    return new Date(Date.now() + KST_OFFSET_MS).toISOString().slice(0, 19);
+  }
🧹 Nitpick comments (5)
apps/extension/src/hooks/useSaveBookmarks.ts (2)

83-85: window.close() 주석 유지 대신 “동작 플래그화” 또는 제거 권장
지금처럼 “주석으로 남긴 동작 코드”는 의도/정책이 바뀔 때 중복 주석이 쌓이기 쉽습니다. shouldAutoClose 같은 옵션(기본 false)으로 분기하거나, 정말 개발 중 임시라면 TODO와 함께 이슈 트래킹이 더 안전해요.

-      // window 닫기
-      // window.close();
+      // TODO(#212): 자동 닫기 정책 확정 시 플래그로 제어
+      // if (shouldAutoClose) window.close();

50-52: 북마크 폴더 parentId: '2' 상수 의존은 최소 주석/근거 보강 권장
Chrome에선 보통 “기타 북마크”가 id 2이긴 한데, 환경/브라우저에 따라 달라질 여지가 있어요. 그대로 갈 거면 “Chrome root mapping 전제”를 주석으로 명확히 남기거나, 가능하면 트리에서 “Other bookmarks”를 찾아 id를 얻는 방식이 더 안전합니다.

apps/extension/src/pages/MainPop.tsx (3)

287-298: 홈 아이콘: hover는 좋지만 클릭/키보드 접근성 보완 권장
cursor-pointer만으로는 접근성/명확한 인터랙션이 부족할 수 있어요. 가능하면 <button aria-label="홈으로">로 감싸고 onClick, onKeyDown(Enter/Space)를 지원하세요.


50-50: hover 상태는 CSS로도 대체 가능(선택 사항)
단순 hover 스왑이면 상태(isHover) 대신 group-hover/hover:로 처리하면 리렌더를 줄일 수 있습니다(Icon 컴포넌트가 두 상태 렌더를 지원한다는 전제).


245-248: window.close() 주석 방치 대신 정책을 한 곳에서 관리 권장
edit 성공 시 close를 막는 의도는 이해되지만, 이 패턴이 여러 군데 퍼지면 나중에 정책 변경이 어렵습니다(설정/상수/훅으로 일원화 추천).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0956c4 and 82abbab.

⛔ Files ignored due to path filters (4)
  • apps/extension/src/assets/home.svg is excluded by !**/*.svg
  • packages/design-system/src/icons/source/chippi_profile.svg is excluded by !**/*.svg
  • packages/design-system/src/icons/source/ext_home1.svg is excluded by !**/*.svg
  • packages/design-system/src/icons/source/ext_home2.svg is excluded by !**/*.svg
📒 Files selected for processing (4)
  • apps/extension/src/hooks/usePageMeta.ts (1 hunks)
  • apps/extension/src/hooks/useSaveBookmarks.ts (1 hunks)
  • apps/extension/src/pages/MainPop.tsx (4 hunks)
  • packages/design-system/src/icons/iconNames.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: jjangminii
Repo: Pinback-Team/pinback-client PR: 52
File: apps/client/src/shared/components/sidebar/SideItem.tsx:57-60
Timestamp: 2025-09-03T08:57:48.626Z
Learning: SideItem 컴포넌트에서 아이콘 디자인이 일반적인 기대와 반대여서, 회전 로직도 반대로 구현되었습니다. 이를 명확히 하기 위해 prop 이름을 open에서 close로 변경하여 실제 동작과 일치시켰습니다.
📚 Learning: 2025-07-08T11:47:10.642Z
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 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.

Applied to files:

  • packages/design-system/src/icons/iconNames.ts
  • apps/extension/src/pages/MainPop.tsx
📚 Learning: 2025-07-15T20:00:13.756Z
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 80
File: apps/client/src/shared/components/ui/modalPop/ModalPop.tsx:36-41
Timestamp: 2025-07-15T20:00:13.756Z
Learning: In apps/client/src/shared/components/ui/modalPop/ModalPop.tsx, the InfoBox component uses hardcoded values for title, location, and icon URL as temporary test data. These should be replaced with dynamic data from props when implementing actual functionality and should be marked with TODO comments for future changes.

Applied to files:

  • apps/extension/src/pages/MainPop.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: storybook
🔇 Additional comments (2)
apps/extension/src/hooks/usePageMeta.ts (1)

42-53: 내부 페이지/타이틀 가드가 사실상 제거됨(의도 확인 필요)
현재는 내부 페이지(chrome://, edge://, about: 등)에서도 OG fetch를 시도하게 됩니다. 자동 닫기를 없애더라도 “저장 불가 UI 상태로 전환” 같은 형태의 가드는 남기는 게 안전해요.

packages/design-system/src/icons/iconNames.ts (1)

2-7: 아이콘 네이밍 추가는 OK — 실제 에셋/빌드 파이프라인 동기화만 확인
자동 생성 파일인 만큼 ext_home1, ext_home2에 대응하는 에셋이 디자인 시스템 아이콘 번들에 포함되는지(Storybook/빌드)만 확인하면 됩니다.

Comment on lines 71 to 76
useEffect(() => {
if (!loading && !title) {
alert('이 페이지는 저장할 수 없어요 🐿️');
window.close();
// window.close();
}
}, [loading, title]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

“저장 불가 페이지”에서 이제 닫지 않으니, 후속 UX(버튼 비활성/에러 UI)가 필요
현재는 alert만 띄우고 화면이 계속 남아서 사용자가 저장을 시도할 수 있습니다(타이틀 공백 등). isInvalidPage 상태를 두고 저장 버튼 disable + 안내 문구 렌더링 같은 처리가 있어야 흐름이 끊기지 않습니다.

🤖 Prompt for AI Agents
In apps/extension/src/pages/MainPop.tsx around lines 71-76, the effect currently
only alerts when the page is not savable but leaves the UI interactive;
introduce an isInvalidPage boolean state (derived from !loading && !title) and
set it in the useEffect (or compute it from dependencies), then use that state
to disable the Save button(s) and render a visible inline notice explaining why
saving is disabled (e.g., "타이틀이 필요합니다" or similar) in the main render; ensure
the Save handler also early-returns when isInvalidPage to guard against direct
calls.

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

🧹 Nitpick comments (2)
apps/extension/src/shared/components/Header.tsx (2)

7-17: chrome.tabs.create는 실행 환경 가드(+ 에러 처리) 추가를 권장합니다.
Storybook/테스트/웹 런타임에서 chrome이 없으면 런타임 에러가 날 수 있어 if (globalThis.chrome?.tabs?.create) ... else window.open(...) 같은 방어 코드(또는 호출부에서 extension-only 보장)를 확인해 주세요.


7-14: hover 토글은 onPointerEnter/Leave + focus 상태까지 포함하는 편이 안전합니다.
터치/펜 입력이나 키보드 포커스에서는 onMouseEnter/Leave만으로 상태 전환이 안 될 수 있어 onPointerEnter/Leave 또는 onFocus/onBlur도 함께 고려해 주세요.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82abbab and 3f3bde4.

📒 Files selected for processing (3)
  • apps/extension/src/pages/MainPop.tsx (2 hunks)
  • apps/extension/src/shared/components/Header.tsx (1 hunks)
  • apps/extension/tsconfig.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/extension/src/pages/MainPop.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-08T11:47:10.642Z
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 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.

Applied to files:

  • apps/extension/src/shared/components/Header.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: storybook
🔇 Additional comments (1)
apps/extension/tsconfig.json (1)

23-33: @shared/* 경로 별칭 추가는 좋고, 빌드/린트/테스트 쪽 별칭 동기화만 확인해 주세요.
tsconfig.paths만 추가하면 일부 도구(Vite resolve.alias, eslint import resolver, 테스트 러너)가 @shared/*를 못 찾는 케이스가 있어서, 해당 설정들도 동일 별칭을 쓰는지 점검하면 안전합니다.

Comment on lines 3 to 19
const Header = () => {
const [isHover, setIsHover] = useState(false);
return (
<div className="flex items-center justify-between">
<Icon
name={isHover ? 'ext_home2' : 'ext_home1'}
width={28}
height={28}
className="cursor-pointer"
onMouseEnter={() => setIsHover(true)}
onMouseLeave={() => setIsHover(false)}
onClick={() => {
chrome.tabs.create({ url: 'https://www.pinback.today/' });
}}
/>
<Icon name="main_logo" width={72} height={20} />
</div>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

클릭 가능한 아이콘에 키보드 접근성(버튼/aria/포커스) 추가가 필요합니다.
현재는 마우스 이벤트 기반이라 키보드/스크린리더 사용자에게 동작이 막힙니다. button으로 감싸고 aria-label, type="button", onKeyDown(Enter/Space) 또는 기본 버튼 클릭만으로 동작하게 하는 쪽을 권장합니다.

Copy link
Member

@constantly-dev constantly-dev left a comment

Choose a reason for hiding this comment

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

수고하셨습니다 간단한 코멘트만 확인해주세요~~

지금 로그인하고 북마크한 정보의
<br /> 리마인드 알람을 받아보세요
</p>
<button
Copy link
Member

Choose a reason for hiding this comment

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

button은 type 명시적으로 지정해주세요!

const Header = () => {
const [isHover, setIsHover] = useState(false);
return (
<div className="flex items-center justify-between">
Copy link
Member

Choose a reason for hiding this comment

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

sematic태그를 이용하는 것은 어떨까요? header태그!

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

♻️ Duplicate comments (1)
apps/extension/src/pages/LogOutPop.tsx (1)

19-26: 버튼에 type="button" 속성 명시 필요

의도하지 않은 폼 제출을 방지하기 위해 버튼에 type 속성을 명시적으로 지정해야 합니다.

🔎 수정 제안
       <button
+        type="button"
         className="sub4-sb mt-[1.6rem] flex h-[4.4rem] w-[25.15rem] items-center justify-between rounded-full border border-gray-100 bg-white px-[2rem]"
         onClick={() => {
🧹 Nitpick comments (2)
packages/design-system/src/icons/iconNames.ts (1)

23-23: 아이콘 이름에 점(.) 문자 포함 검증 필요

logout_chippi.2512에 점(.)과 숫자가 포함되어 있습니다. 점 문자는 파일 확장자로 오인될 수 있고, 일부 시스템에서 예기치 않은 동작을 유발할 수 있습니다. 다른 아이콘들은 언더스코어(_)만 사용하는 컨벤션을 따르고 있습니다.

logout_chippi_2512 형태로 변경하는 것을 권장합니다.

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

38-38: 주석 처리된 코드 제거

디버깅 또는 테스트용으로 보이는 주석 처리된 <LogOutPop />을 제거해 주세요.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f3bde4 and 20927b2.

⛔ Files ignored due to path filters (7)
  • packages/design-system/src/icons/source/google.svg is excluded by !**/*.svg
  • packages/design-system/src/icons/source/logout_chippi.2512.svg is excluded by !**/*.svg
  • packages/design-system/src/icons/source/tooltip_1.svg is excluded by !**/*.svg
  • packages/design-system/src/icons/source/tooltip_2.svg is excluded by !**/*.svg
  • packages/design-system/src/icons/source/tooltip_3.svg is excluded by !**/*.svg
  • packages/design-system/src/icons/source/tooltip_4.svg is excluded by !**/*.svg
  • packages/design-system/src/icons/source/tooltip_5.svg is excluded by !**/*.svg
📒 Files selected for processing (3)
  • apps/extension/src/App.tsx (2 hunks)
  • apps/extension/src/pages/LogOutPop.tsx (1 hunks)
  • packages/design-system/src/icons/iconNames.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-07-11T20:47:15.055Z
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 43
File: apps/client/src/shared/components/ui/cards/BookmarkCard.tsx:0-0
Timestamp: 2025-07-11T20:47:15.055Z
Learning: React 컴포넌트에서 button 요소를 사용할 때는 항상 type 속성을 명시적으로 지정해야 합니다. 일반적인 클릭 버튼의 경우 type="button"을, 폼 제출 버튼의 경우 type="submit"을 명시해야 합니다. 이렇게 해야 의도하지 않은 폼 제출 동작을 방지할 수 있습니다.

Applied to files:

  • apps/extension/src/pages/LogOutPop.tsx
📚 Learning: 2025-07-13T09:18:25.323Z
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 63
File: packages/design-system/src/components/commonBtn/CommonBtn.tsx:37-42
Timestamp: 2025-07-13T09:18:25.323Z
Learning: React 버튼 컴포넌트에서 submit, button 등 다양한 버튼 타입을 사용해야 하는 경우, buttonType prop을 추가하여 HTML button 요소의 type 속성을 설정할 수 있도록 해야 합니다. 기본값은 'button'으로 설정하여 의도하지 않은 폼 제출을 방지해야 합니다.

Applied to files:

  • apps/extension/src/pages/LogOutPop.tsx
📚 Learning: 2025-07-15T20:00:13.756Z
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 80
File: apps/client/src/shared/components/ui/modalPop/ModalPop.tsx:36-41
Timestamp: 2025-07-15T20:00:13.756Z
Learning: In apps/client/src/shared/components/ui/modalPop/ModalPop.tsx, the InfoBox component uses hardcoded values for title, location, and icon URL as temporary test data. These should be replaced with dynamic data from props when implementing actual functionality and should be marked with TODO comments for future changes.

Applied to files:

  • apps/extension/src/App.tsx
📚 Learning: 2025-07-08T11:47:10.642Z
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 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.

Applied to files:

  • packages/design-system/src/icons/iconNames.ts
🧬 Code graph analysis (1)
apps/extension/src/App.tsx (2)
apps/extension/src/hooks/usePageMeta.ts (1)
  • usePageMeta (21-61)
apps/extension/src/apis/query/queries.ts (1)
  • useGetArticleSaved (53-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: storybook
🔇 Additional comments (3)
packages/design-system/src/icons/iconNames.ts (1)

1-32: 자동 생성 파일 수동 수정 확인

파일 주석에 "자동 생성 파일입니다. (직접 수정 금지)"라고 명시되어 있습니다. 이 변경이 아이콘 생성 스크립트를 통해 추가된 것인지, 아니면 수동으로 편집된 것인지 확인해 주세요. 수동 수정의 경우 다음 빌드 시 덮어써질 수 있습니다.

apps/extension/src/pages/LogOutPop.tsx (1)

3-31: 컴포넌트 구현 LGTM

로그아웃 상태 UI가 잘 구현되어 있습니다. Chrome extension API를 사용한 탭 생성 로직도 적절합니다.

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

9-10: 토큰 없을 때 불필요한 API 호출 검토

useGetArticleSaved가 토큰 존재 여부와 관계없이 호출됩니다. 인증이 필요한 API라면 토큰이 없을 때 불필요한 에러나 네트워크 요청이 발생할 수 있습니다. 해당 훅에서 토큰 기반 enabled 조건이 이미 처리되어 있는지 확인해 주세요.

Comment on lines +14 to +20
const [isToken, setIsToken] = useState<boolean | null>(null);

useEffect(() => {
chrome.storage.local.get('token', (result) => {
setIsToken(!!result.token);
});
}, []);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

토큰 확인 전 로딩 상태 처리 필요

isTokennull(초기값)일 때 falsy로 평가되어 LogOutPop이 렌더링됩니다. 이로 인해 로그인된 사용자도 토큰 확인이 완료되기 전까지 잠깐 로그아웃 화면을 보게 됩니다.

로딩 상태를 명시적으로 처리하는 것을 권장합니다:

🔎 수정 제안
   const [isToken, setIsToken] = useState<boolean | null>(null);

+  // 토큰 확인 중일 때 로딩 표시
+  if (isToken === null) {
+    return null; // 또는 로딩 스피너
+  }

   return (
     <>
-      {/* <LogOutPop /> */}
       {isToken ? (

Also applies to: 39-50

🤖 Prompt for AI Agents
In apps/extension/src/App.tsx around lines 14 to 20 (and similarly at 39-50),
the component treats isToken null as falsy and immediately renders the LogOutPop
before chrome.storage check completes; change the logic to explicitly represent
loading (keep isToken initial value null) and avoid rendering auth-dependent UI
while isToken === null: add a loading branch (or return null / a spinner) until
chrome.storage callback sets isToken to true/false, and ensure all conditional
renders that currently check falsy values use explicit comparisons (isToken ===
true / isToken === false) so users don't see the logout screen briefly.

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

🧹 Nitpick comments (1)
apps/extension/src/pages/LogOutPop.tsx (1)

19-30: 버튼 구현이 올바르지만, 에러 처리와 URL 관리 개선이 필요합니다.

type="button"이 올바르게 명시되어 있어 좋습니다 (이전 리뷰 코멘트 반영됨).

다음 개선사항을 권장합니다:

  1. chrome.tabs.create 호출에 에러 핸들링을 추가하여 실패 시 사용자에게 피드백을 제공하세요.
  2. 하드코딩된 URL을 상수로 추출하여 유지보수성을 높이세요.
🔎 개선된 구현 예시

파일 상단에 상수 추가:

+const ONBOARDING_URL = 'https://www.pinback.today/onboarding?step=SOCIAL_LOGIN';
+
 const LogOutPop = () => {

버튼 핸들러에 에러 처리 추가:

       <button
         className="sub4-sb mt-[1.6rem] flex h-[4.4rem] w-[25.15rem] items-center justify-between rounded-full border border-gray-100 bg-white px-[2rem]"
         type="button"
         onClick={() => {
-          chrome.tabs.create({
-            url: 'https://www.pinback.today/onboarding?step=SOCIAL_LOGIN',
-          });
+          try {
+            chrome.tabs.create({
+              url: ONBOARDING_URL,
+            });
+          } catch (error) {
+            console.error('Failed to open onboarding tab:', error);
+            // 선택사항: 사용자에게 에러 토스트 표시
+          }
         }}
       >
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20927b2 and f7d0101.

📒 Files selected for processing (2)
  • apps/extension/src/pages/LogOutPop.tsx (1 hunks)
  • apps/extension/src/shared/components/Header.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/extension/src/shared/components/Header.tsx
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: jjangminii
Repo: Pinback-Team/pinback-client PR: 52
File: apps/client/src/shared/components/sidebar/SideItem.tsx:57-60
Timestamp: 2025-09-03T08:57:48.626Z
Learning: SideItem 컴포넌트에서 아이콘 디자인이 일반적인 기대와 반대여서, 회전 로직도 반대로 구현되었습니다. 이를 명확히 하기 위해 prop 이름을 open에서 close로 변경하여 실제 동작과 일치시켰습니다.
📚 Learning: 2025-07-11T20:47:15.055Z
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 43
File: apps/client/src/shared/components/ui/cards/BookmarkCard.tsx:0-0
Timestamp: 2025-07-11T20:47:15.055Z
Learning: React 컴포넌트에서 button 요소를 사용할 때는 항상 type 속성을 명시적으로 지정해야 합니다. 일반적인 클릭 버튼의 경우 type="button"을, 폼 제출 버튼의 경우 type="submit"을 명시해야 합니다. 이렇게 해야 의도하지 않은 폼 제출 동작을 방지할 수 있습니다.

Applied to files:

  • apps/extension/src/pages/LogOutPop.tsx
📚 Learning: 2025-07-13T09:18:25.323Z
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 63
File: packages/design-system/src/components/commonBtn/CommonBtn.tsx:37-42
Timestamp: 2025-07-13T09:18:25.323Z
Learning: React 버튼 컴포넌트에서 submit, button 등 다양한 버튼 타입을 사용해야 하는 경우, buttonType prop을 추가하여 HTML button 요소의 type 속성을 설정할 수 있도록 해야 합니다. 기본값은 'button'으로 설정하여 의도하지 않은 폼 제출을 방지해야 합니다.

Applied to files:

  • apps/extension/src/pages/LogOutPop.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: storybook
🔇 Additional comments (2)
apps/extension/src/pages/LogOutPop.tsx (2)

1-2: 임포트 구조가 올바릅니다.

디자인 시스템의 Icon 컴포넌트를 올바르게 임포트하고 있습니다.


3-18: 컴포넌트 구조와 UI가 깔끔합니다.

로고, 일러스트레이션, 안내 문구가 잘 구성되어 있으며, 사용자 친화적인 메시지를 제공합니다.

Copy link
Collaborator

@jjangminii jjangminii left a comment

Choose a reason for hiding this comment

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

익스텐션에서 서스펜스 적용은 까다롭네요....... 확실히공부가 많이 필요한 부분 같아요 수고하셨습니다-!

Copy link
Collaborator

Choose a reason for hiding this comment

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

툴팁 이미지 변경된거같은데 이부분은 파일 뺴고 올려주실 수 있나요?

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

Labels

feat 기능 개발하라 개발 달려라 달려

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Refactor] 익스텐션 최적화

3 participants