Skip to content

Feat: 개발 QA 1차(재림 | 온보딩 기능 및 익스텐션 UI 쪽 수정) #107

Merged
jllee000 merged 16 commits intodevelopfrom
feat/#106/QA-1
Sep 14, 2025
Merged

Feat: 개발 QA 1차(재림 | 온보딩 기능 및 익스텐션 UI 쪽 수정) #107
jllee000 merged 16 commits intodevelopfrom
feat/#106/QA-1

Conversation

@jllee000
Copy link
Collaborator

@jllee000 jllee000 commented Sep 14, 2025

📌 Related Issues

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

📄 Tasks

  1. 온보딩 ui 레이아웃 수정
  2. 사이드바 분기
  3. 디자인 QA 적용
  4. 익스텐션 날짜 로직 수정
  5. 온보딩 타임피커 노출 로직 수정

⭐ PR Point (To Reviewer)

📷 Screenshot

Summary by CodeRabbit

  • 버그 수정

  • 기능 개선

    • 온보딩 경로에서 사이드바를 숨겨 화면을 간소화.
    • 알림 선택 동작을 개선해 시간 선택 UI 표시/숨김 동작을 더 명확히 처리.
    • 로고 클릭 시 공식 사이트로 이동하도록 변경.
  • 동작 변경

    • 북마크 저장 및 저장 실패 시 자동 창 닫힘 동작을 중단해 사용자가 창을 유지하도록 변경.
  • Chores

    • 온보딩 문구를 환영 메시지로 업데이트 및 Mac 안내 문구 수정.
    • 서비스워커 등록 성공 시 불필요한 알림 제거(로그로 대체).
  • 스타일

    • 카드 높이를 고정해 레이아웃 일관성 향상.
    • 알림 박스 비활성 색상, 문단 간격 및 시간 선택기 위치 조정.

@vercel
Copy link

vercel bot commented Sep 14, 2025

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

Project Deployment Preview Comments Updated (UTC)
pinback-client-client Ready Ready Preview Comment Sep 14, 2025 8:47am

@coderabbitai
Copy link

coderabbitai bot commented Sep 14, 2025

Warning

Rate limit exceeded

@jllee000 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 3 minutes and 38 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 877eb54 and a090de2.

📒 Files selected for processing (2)
  • apps/extension/src/App.tsx (1 hunks)
  • apps/extension/src/pages/MainPop.tsx (3 hunks)

Walkthrough

온보딩 텍스트 일부를 변경하고(두 곳), 알림/타임피커 관련 스타일·포지셔닝·토글 동작을 조정했으며, 확장프로그램 설치 시 열리는 온보딩 URL을 프로덕션 도메인으로 변경하고 Layout에서 온보딩 경로일 때 사이드바를 숨기도록 조건부 렌더링을 추가했습니다. 디자인 시스템 BaseCard에 고정 높이(h-[12rem])를 추가했고 서비스워커 등록 성공 핸들러에서 alert를 제거하고 로그만 남기도록 변경했습니다.

Changes

Cohort / File(s) Summary
Onboarding UI copy updates
apps/client/src/pages/onBoarding/components/funnel/step/FinalStep.tsx, apps/client/src/pages/onBoarding/components/funnel/step/MacStep.tsx
한국어 문구 교체(문구 변경만, JSX 구조·로직·props 불변).
Onboarding alarm & time picker
apps/client/src/pages/onBoarding/components/funnel/AlarmBox.tsx, apps/client/src/pages/onBoarding/components/funnel/step/AlarmStep.tsx, apps/client/src/pages/onBoarding/components/timePicker/TimePicker.tsx
Disabled 색상 조정(bg-main100bg-main0), AlarmBox onClick 토글 조건 변경(select === 3 기반) 및 showPicker 제어 명시, AlarmStep 문단 하단 마진 조정, TimePicker 절대 위치 오프셋(bottom/right) 변경.
Layout conditional sidebar
apps/client/src/layout/Layout.tsx
useLocation() 도입 및 isOnboarding = location.pathname.startsWith('/onboarding')로 판단하여 온보딩 경로일 때 Sidebar를 렌더링하지 않도록 조건부 처리 추가.
Extension onboarding URL
apps/extension/src/background.ts
onInstalled 핸들러에서 온보딩 URL을 http://localhost:5173/onboarding?email=${info.email}https://www.pinback.today/onboarding?email=${info.email}로 변경(이메일 저장·딜레이·탭 오픈 흐름 유지).
Design system: BaseCard style
packages/design-system/src/components/card/BaseCard.tsx
BaseCard에 고정 높이 클래스 h-[12rem] 추가(자동 높이→고정 12rem, overflow-hidden 유지).
Service worker registration
apps/client/src/pages/onBoarding/utils/registerServiceWorker.ts
서비스워커 등록 성공 핸들러에서 alert 제거하고 템플릿 문자열로 console.log만 남김; 실패 경로는 유지됨.
Extension popup / bookmarks
apps/extension/src/hooks/useSaveBookmarks.ts, apps/extension/src/pages/MainPop.tsx
북마크 저장 흐름에서 window.close() 호출을 주석 처리하여 창을 닫지 않도록 변경, 저장 불가(제목 없음) 시 기존에 닫던 동작도 주석 처리, 로드 시 remindAt 로그 추가, 로고 클릭 시 https://pinback.today로 이동하도록 변경.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User as 사용자
    participant Ext as 확장 background
    participant LS as 로컬스토리지
    participant Web as 웹 온보딩
    note right of Ext #E6F4EA: onInstalled (URL 변경)

    User->>Ext: 확장 설치
    Ext->>Ext: onInstalled 핸들러 실행
    Ext->>Ext: chrome.identity.getProfileUserInfo 등으로 이메일 조회
    Ext->>LS: 이메일 저장
    Ext->>Ext: 1초 대기
    Ext->>Web: Open https://www.pinback.today/onboarding?email={email}
    Web-->>User: 온보딩 페이지 렌더링
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

🛠️ Feature, frontend

Suggested reviewers

  • jjangminii
  • constantly-dev

Poem

깡충깡충 새 문구로 인사해요 🐇
탭 하나 열고 이메일 맡기고,
카드에 높이 하나 톡— 꽉 차고,
알람은 톡톡, 시간은 살짝 옮기고,
핀백 길에 깡충— 환영해요!

Pre-merge checks

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning PR은 #106(QA 1차)과 연관된 여러 UI/동작 수정(예: FinalStep/MacStep 텍스트 수정, AlarmBox/TimePicker 동작·위치 조정, onboarding 서비스워커 알림 제거, extension 온보딩 URL 변경, BaseCard 높이 고정 등)을 포함하여 #106의 범주에 해당하는 변경을 일부 반영하고 있습니다; 그러나 #25(Progress 컴포넌트 구현)의 코드 요구사항(Progress 컴포넌트 구현, variant 처리, 값 검증(0–100), 접근성 속성, Storybook 스토리·인터랙션 테스트 및 @radix-ui/react-progress 의존성 추가)은 PR 변경내역에 보이지 않습니다. 따라서 연결된 이슈 전체의 요구사항을 만족하지 못합니다. 해결: PR에서 #25 구현까지 의도한 것인지 명확히 해주십시오. 만약 #25가 본 PR 범위가 아니라면 linked_issues에서 제거하거나 별도 PR로 분리해 주세요. 반대로 #25를 포함하려면 Progress 컴포넌트 구현, variant 분기, 접근성 속성, Storybook 스토리·테스트 및 관련 의존성 추가를 포함하도록 코드를 보완하고 PR 설명에 그 내용을 반영해 주세요.
Out of Scope Changes Check ❓ Inconclusive 변경사항 중 packages/design-system의 BaseCard 높이 고정, extension 관련 onboarding URL 및 팝업 동작 변경, 여러 UI 텍스트/레이아웃 조정 등 범위가 넓은 파일들이 포함되어 있어 이들이 #106 QA 요청의 일부인지 혹은 별도 범위인지 불명확합니다. 현재 제공된 정보만으로는 일부 변경이 의도된 QA 범위를 벗어나는지 확실히 판단할 수 없습니다. 해결: 각 변경이 #106(QA) 요청에 포함되는지 작성자에게 확인해 주세요; 의도와 무관한 변경(특히 design-system 변경)은 별도 PR로 분리하거나 PR 설명에 범위·목적을 명확히 기재해 주세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Title Check ✅ Passed PR 제목 "Feat: 개발 QA 1차(재림
Description Check ✅ Passed PR 설명은 템플릿의 핵심 항목인 '📌 Related Issues'와 '📄 Tasks'를 포함하여 관련 이슈(close #106)와 구체적인 작업 항목(온보딩 UI 레이아웃 수정 등)을 명시하고 있어 대체로 요구사항을 충족합니다. 다만 '⭐ PR Point (To Reviewer)' 섹션은 비어 있고 '📷 Screenshot' 섹션도 비어 있어 리뷰어가 주목해야 할 상세 포인트나 변경 UI 확인용 자료가 제공되지 않았습니다. 따라서 필수 정보는 갖추었으나 리뷰 효율을 위해 몇 가지 비핵심 보완이 권장됩니다.

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 github-actions bot added the feat 기능 개발하라 개발 달려라 달려 label Sep 14, 2025
@github-actions
Copy link

github-actions bot commented Sep 14, 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: 0

Caution

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

⚠️ Outside diff range comments (2)
apps/extension/src/background.ts (2)

5-7: 개인정보 콘솔 노출 제거

info.email을 콘솔에 출력하면 사용자 PII가 개발자 도구에 남습니다. 삭제 권장합니다.

-        chrome.storage.local.set({ 'userEmail': info.email }, () => {
-          console.log(info.email);
-        });
+        chrome.storage.local.set({ userEmail: info.email });

20-22: 토큰 콘솔 로깅 금지

액세스 토큰을 콘솔에 기록하면 유출 위험이 큽니다. 즉시 제거하세요.

-    chrome.storage.local.set({ 'token': message.token }, () => {
-      console.log('Token saved!', message.token);
-    });
+    chrome.storage.local.set({ token: message.token });
🧹 Nitpick comments (4)
apps/client/src/pages/onBoarding/components/funnel/step/FinalStep.tsx (1)

5-8: 장식 이미지 대체텍스트와 헤딩 시맨틱 보완 제안

-dotori 아이콘은 장식용으로 보이며, 스크린리더 낭독을 피하려면 빈 alt가 적절합니다.
-시각적 헤딩(head2)을 실제 헤딩 요소(<h2>)로 바꾸면 접근성 트리 구조가 개선됩니다.

아래와 같이 수정을 제안합니다:

-      <img src={dotori} className="mb-[1.2rem]" alt="dotori" />
-      <p className="head2 text-font-black-1">
-       Pinback에 오신 걸 환영해요
-      </p>
+      <img src={dotori} className="mb-[1.2rem]" alt="" aria-hidden="true" />
+      <h2 className="head2 text-font-black-1">
+        Pinback에 오신 걸 환영해요
+      </h2>

추가로, 문구가 하드코딩되어 있어 i18n 적용 범위를 점검해 주세요(예: 번역 키 사용).

apps/client/src/pages/onBoarding/components/funnel/step/MacStep.tsx (1)

6-9: 헤딩 시맨틱 적용 및 장식 이미지 alt 정리

-시각적 헤딩을 <h2>로 변경해 접근성 향상.
-dotori 아이콘은 장식이라면 빈 alt 권장.

-      <img src={dotori} className="mb-[1.2rem]" alt="dotori" />
-      <p className="head2 text-font-black-1">
-        치삐를 만나려면 알림 설정이 필요해요
-      </p>
+      <img src={dotori} className="mb-[1.2rem]" alt="" aria-hidden="true" />
+      <h2 className="head2 text-font-black-1">
+        치삐를 만나려면 알림 설정이 필요해요
+      </h2>

문구 하드코딩 여부(i18n)도 함께 확인 부탁드립니다.

apps/extension/src/background.ts (2)

8-12: 지연 오픈 로직 점검(필요 시 제거 또는 안정화)

1초 지연의 근거가 없다면 제거하거나, 탭 중복 생성 방지(동일 URL 기존 탭 포커싱) 로직을 고려해 주세요.


10-10: 이메일 쿼리 인코딩 및 베이스 URL 환경변수화

  • apps/extension/src/background.ts (라인 10): info.email을 직접 URL에 넣지 말고 encodeURIComponent(info.email) 사용.
  • 하드코딩된 https://www.pinback.today 대신 import.meta.env.VITE_WEB_BASE_URL(또는 프로젝트 콘벤션의 VITE_* 변수)로 분리.
  • 서버 라우트가 소문자 '/onboarding'인지 확인.
📜 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 a0036da and 10f0037.

📒 Files selected for processing (3)
  • apps/client/src/pages/onBoarding/components/funnel/step/FinalStep.tsx (1 hunks)
  • apps/client/src/pages/onBoarding/components/funnel/step/MacStep.tsx (1 hunks)
  • apps/extension/src/background.ts (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

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 (1)
packages/design-system/src/components/card/BaseCard.tsx (1)

8-12: onClick이 있는 카드의 접근성(a11y) 속성 보강 제안

키보드 접근성 확보를 위해 role/tabIndex/키보드 핸들링 및 포인터 커서 부여를 권장합니다. 기존 동작에는 영향 없고, 클릭 가능한 경우에만 활성화됩니다.

     <div
-      onClick={onClick}
-      className="border-gray200 w-[24.8rem] overflow-hidden rounded-[1.2rem] border bg-white h-[12rem]"
+      onClick={onClick}
+      role={onClick ? 'button' : undefined}
+      tabIndex={onClick ? 0 : undefined}
+      onKeyDown={
+        onClick
+          ? (e) => {
+              if (e.key === 'Enter' || e.key === ' ') {
+                e.preventDefault();
+                onClick();
+              }
+            }
+          : undefined
+      }
+      className="border-gray200 w-[24.8rem] overflow-hidden rounded-[1.2rem] border bg-white h-[12rem] cursor-pointer"
     >
📜 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 10f0037 and 4c62f4b.

📒 Files selected for processing (1)
  • packages/design-system/src/components/card/BaseCard.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

<div
onClick={onClick}
className="border-gray200 w-[24.8rem] overflow-hidden rounded-[1.2rem] border bg-white"
className="border-gray200 w-[24.8rem] overflow-hidden rounded-[1.2rem] border bg-white h-[12rem]"
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

BaseCard에 고정 높이(h-[12rem]) 도입은 광범위한 레이아웃/내용 잘림 리스크가 큽니다.

BaseCard는 범용 컨테이너라서 기존 소비처에서 내용 높이가 다양한 경우가 많습니다. 현재 overflow-hidden과 결합되면 텍스트/프로그레스 등 긴 콘텐츠가 잘릴 수 있어 브레이킹 체인지가 될 가능성이 높습니다. 기본은 가변 높이를 유지하고, 필요한 화면에서만 고정 높이를 적용하도록 하거나 최소 높이로 완화하는 편이 안전합니다.

권장(보수적) 패치: 잘림 방지를 위해 최소 높이로 전환

-      className="border-gray200 w-[24.8rem] overflow-hidden rounded-[1.2rem] border bg-white h-[12rem]"
+      className="border-gray200 w-[24.8rem] overflow-hidden rounded-[1.2rem] border bg-white min-h-[12rem]"

사용처 점검용 스크립트(어디서 BaseCard가 쓰이는지 빠르게 훑기):


🏁 Script executed:

#!/bin/bash
# BaseCard 사용처 목록(컨텍스트 2줄 포함)
rg -n --type=tsx -C2 '<BaseCard\b'

Length of output: 27


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C2 '\bBaseCard\b' || true
rg -n -C2 '<BaseCard\b' || true
if [ -f packages/design-system/src/components/card/BaseCard.tsx ]; then
  sed -n '1,240p' packages/design-system/src/components/card/BaseCard.tsx || true
else
  echo "packages/design-system/src/components/card/BaseCard.tsx not found"
fi

Length of output: 4798


BaseCard: 고정 높이(h-[12rem]) → 최소 높이(min-h-[12rem])로 변경 필요

overflow-hidden와 결합된 고정 높이는 소비처에서 콘텐츠가 잘려 브레이킹을 유발할 수 있습니다. 보수적으로 기본은 가변 높이(최소 높이 유지)로 변경하세요.

권장 패치:

-      className="border-gray200 w-[24.8rem] overflow-hidden rounded-[1.2rem] border bg-white h-[12rem]"
+      className="border-gray200 w-[24.8rem] overflow-hidden rounded-[1.2rem] border bg-white min-h-[12rem]"

참고(점검 필요): MyBookmarkCard 내부에 이미 h-[12rem]이 설정되어 있습니다 — packages/design-system/src/components/card/MyBookmarkCard.tsx (inner div). BaseCard 변경 후 소비처 전반을 검토해 고정 높이가 의도된 곳만 유지하세요.

📝 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
className="border-gray200 w-[24.8rem] overflow-hidden rounded-[1.2rem] border bg-white h-[12rem]"
className="border-gray200 w-[24.8rem] overflow-hidden rounded-[1.2rem] border bg-white min-h-[12rem]"
🤖 Prompt for AI Agents
In packages/design-system/src/components/card/BaseCard.tsx around line 10 the
card uses a fixed height class h-[12rem] which combined with overflow-hidden can
truncate variable content; change that class to min-h-[12rem] to allow the card
to grow while preserving the default minimum height, update the className string
accordingly, and after making this change search and review consumer components
(notably packages/design-system/src/components/card/MyBookmarkCard.tsx which
currently applies h-[12rem] internally) to ensure only intended cards keep a
fixed height and adjust consumers if necessary.

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 (2)
apps/client/src/layout/Layout.tsx (2)

1-1: useMatch 도입 시 import 정리

useLocation이 더 이상 필요 없으니 useMatch만 import 하도록 정리해 주세요.

-import { Outlet,useLocation } from 'react-router-dom';
+import { Outlet, useMatch } from 'react-router-dom';

11-11: 온보딩 화면에서 Sidebar 비노출 — 의도와 일치합니다

온보딩 플로우에서 사이드바의 데이터 페칭을 피할 수 있어 UX/성능 측면에서 이점 있습니다. 그대로 진행해도 좋습니다.
추가 제안: 사이드바를 React.lazy로 분리하면 온보딩 초기 번들을 더 줄일 수 있습니다(선택).

예시:

// import { Sidebar } ... 제거
import React, { Suspense } from 'react';
const Sidebar = React.lazy(() => import('@shared/components/sidebar/Sidebar'));

// 사용처
{!isOnboarding && (
  <Suspense fallback={null}>
    <Sidebar />
  </Suspense>
)}
📜 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 175fda4 and 33dcc9c.

📒 Files selected for processing (1)
  • apps/client/src/layout/Layout.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/client/src/layout/Layout.tsx (1)
apps/client/src/shared/components/sidebar/Sidebar.tsx (1)
  • Sidebar (23-193)
⏰ 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/layout/Layout.tsx (1)

5-7: onboarding 경로 판별 개선 — useMatch('/onboarding/*')로 교체 권장 (라우트 검증 필요)

startsWith('/onboarding')는 '/onboarding-old' 등 유사 경로를 오탐할 수 있으므로 React Router의 useMatch('/onboarding/*')로 하위 경로만 매칭하도록 변경하세요. (react-router-dom에서 useMatch를 import하고 useLocation 관련 import/사용은 제거)

File: apps/client/src/layout/Layout.tsx (lines 5-7)

-  const location = useLocation();
-  const isOnboarding = location.pathname.startsWith('/onboarding');
+  const isOnboarding = !!useMatch('/onboarding/*');

자동 검증 실패: 이전 스크립트가 환경에서 오류(unrecognized file type)를 반환했습니다. 수동 확인 또는 스크립트 재실행으로 아래 항목을 확인해 주세요:

  • '/onboarding' 라우트 정의가 일관되게 사용되는지
  • '/onboarding-'로 시작하는 유사 경로(e.g. '/onboarding-old')가 존재하는지
  • 대소문자 변형('onBoarding' 등)이 사용되는지

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

Caution

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

⚠️ Outside diff range comments (1)
apps/client/src/pages/onBoarding/components/funnel/AlarmBox.tsx (1)

27-32: 전역 상수(AlarmsType) 변이 금지

import된 상수 객체를 직접 수정하면 컴포넌트 간 상태 누수, 예측 불가 렌더링, SSR 이슈가 발생할 수 있습니다. 로컬 상태로 관리하고 상위로 lift 하거나 상태 컨텍스트 사용을 권장합니다.

 import { useState } from 'react';
 import { normalizeTime } from '@pages/onBoarding/utils/formatRemindTime';
@@
 const AlarmBox = ({ select, isDisabled, onClick }: AlarmBoxProps) => {
-  const [showPicker, setShowPicker] = useState(false);
+  const [showPicker, setShowPicker] = useState(false);
+  const [customTime, setCustomTime] = useState<string>('');
   const getTimePicker = ({ hour, minute, meridiem }: { hour: string; minute: string; meridiem: string }) => {
-        const formatted = `${meridiem} ${hour}:${minute}`;
-        AlarmsType[2].time = formatted;
+        const formatted = `${meridiem} ${hour}:${minute}`;
+        setCustomTime(formatted);
         setShowPicker(false);
         // 이거 나중에 api 연결때 쓸려고 표시한거.. 그떄 지우겠듬여 console.log('저장된 사용자 알람:', AlarmsType[2].time);
   }
@@
-      {select === 3 && isDisabled && (
+      {select === 3 && isDisabled && (
         <>
-          {AlarmsType[2].time && (
-            <p className="caption2-m text-font-gray-3">{normalizeTime(AlarmsType[2].time)}</p>
-          )}
+          {customTime && (
+            <p className="caption2-m text-font-gray-3">{normalizeTime(customTime)}</p>
+          )}
@@
-              onCancel={() => {
-                AlarmsType[2].time = '';
-              }}
+              onCancel={() => {
+                setCustomTime('');
+                setShowPicker(false);
+              }}

추가로, 저장된 사용자 설정 시간이 상위 단계(온보딩 완료 데이터)에 필요하다면 onChangeCustomTime?: (t: string) => void 콜백을 props로 받아 상위로 끌어올리는 구조를 제안합니다.

Also applies to: 62-65, 69-70

🧹 Nitpick comments (4)
apps/client/src/pages/onBoarding/components/funnel/step/AlarmStep.tsx (1)

11-13: 온보딩 카피 일관성 확인 필요

다른 스텝(MacStep/FinalStep) 카피가 갱신된 반면, 여기만 기존 문구입니다. 의도라면 OK, 아니라면 카피 통일을 권장합니다.

선택지:

-        도토리 찾으러 갈 시간을 정해볼까요?
+        치삐를 만나려면 알림 설정이 필요해요.
apps/client/src/pages/onBoarding/components/timePicker/TimePicker.tsx (1)

51-53: 클래스 오타 및 z-index 값 수정

bod y2-m 오타로 보이며, z-2는 기본 Tailwind에 없습니다. 폰트 스타일 적용이 안 될 수 있어 수정 권장합니다.

-        <p className="bod y2-m z-2 mx-[0.8rem] flex h/[5.6rem] items-center justify-center">
+        <p className="body2-m mx-[0.8rem] flex h-[5.6rem] items-center justify-center">
apps/client/src/pages/onBoarding/components/funnel/AlarmBox.tsx (2)

13-23: TimePicker absolute 기준점 보장

TimePicker가 absolute라 컨테이너에 relative가 없으면 위치가 문맥에 따라 달라질 수 있습니다. 박스 기본 클래스에 relative 추가 권장.

-const boxStyle = cva(
-  'flex h-[22.4rem] w-[18rem] flex-col items-center rounded-[1.2rem] px-[3.9rem] pb-[2.6rem] pt-[3.6rem] cursor-pointer transition',
+const boxStyle = cva(
+  'relative flex h-[22.4rem] w/[18rem] flex-col items-center rounded/[1.2rem] px/[3.9rem] pb/[2.6rem] pt/[3.6rem] cursor-pointer transition',

Also applies to: 35-36


7-10: 네이밍: isDisabled → isSelected 고려

현재 의미상 "선택됨" 상태를 나타내므로 isSelected가 더 직관적입니다. 다음 번 리팩토링 때 rename 고려 부탁드립니다.

Also applies to: 45-58, 60-66

📜 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 33dcc9c and dba84e7.

📒 Files selected for processing (3)
  • apps/client/src/pages/onBoarding/components/funnel/AlarmBox.tsx (3 hunks)
  • apps/client/src/pages/onBoarding/components/funnel/step/AlarmStep.tsx (1 hunks)
  • apps/client/src/pages/onBoarding/components/timePicker/TimePicker.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/client/src/pages/onBoarding/components/funnel/AlarmBox.tsx (1)
apps/client/src/constants/alarms.ts (1)
  • AlarmsType (11-15)
🔇 Additional comments (3)
apps/client/src/pages/onBoarding/components/funnel/step/AlarmStep.tsx (1)

14-16: 여백 조정 LGTM

간격 수정 괜찮습니다. 시각적 리듬이 더 촘촘해졌네요.

apps/client/src/pages/onBoarding/components/timePicker/TimePicker.tsx (1)

41-41: 오버레이 위치 변경 영향 점검

absolute 기준이 되는 상위 엘리먼트에 relative가 없으면 위치가 흔들릴 수 있습니다(특히 -bottom/-right 사용 시). AlarmBox 컨테이너에 relative 추가 권장합니다(별도 코멘트에 diff 제안).

apps/client/src/pages/onBoarding/components/funnel/AlarmBox.tsx (1)

17-19: 비활성 스타일 변경 LGTM

bg-main0로 변경된 비활성 배경이 대비 측면에서 더 명확합니다.

Comment on lines +37 to 41
if (select === 3) {
setShowPicker(true);
} else {
setShowPicker(false);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

TimePicker 토글 로직 버그: 항상 열림

select === 3인 경우 매 클릭마다 무조건 true로 설정되어 닫히지 않습니다. 토글로 변경하세요.

-        if (select === 3) {
-          setShowPicker(true); 
-        } else {
-          setShowPicker(false);
-        }
+        if (select === 3) {
+          setShowPicker((v) => !v);
+        } else {
+          setShowPicker(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
if (select === 3) {
setShowPicker(true);
} else {
setShowPicker(false);
}
if (select === 3) {
setShowPicker((v) => !v);
} else {
setShowPicker(false);
}
🤖 Prompt for AI Agents
In apps/client/src/pages/onBoarding/components/funnel/AlarmBox.tsx around lines
37 to 41, the TimePicker toggle logic incorrectly always sets showPicker to true
when select === 3, preventing it from closing; change the handler so that when
select === 3 you toggle the current state (use a functional update like
setShowPicker(prev => !prev)) and keep setting it to false in the else branch,
ensuring correct open/close behavior.

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/client/src/pages/onBoarding/components/funnel/MainCard.tsx (3)

14-16: Firebase 앱을 렌더 시마다 초기화 — 중복 초기화로 크래시 위험

컴포넌트 바디에서 initializeApp을 매 렌더마다 호출합니다. Firebase는 동일 이름의 앱을 재초기화하면 예외가 납니다. 최소한 getApps()/getApp()으로 가드하고, 가능하면 모듈 스코프로 호이스팅하세요.

적용 diff:

- import { initializeApp } from 'firebase/app';
+ import { initializeApp, getApps, getApp } from 'firebase/app'
-  const app = initializeApp(firebaseConfig);
-  const messaging = getMessaging(app);
+  const app = getApps().length ? getApp() : initializeApp(firebaseConfig);
+  const messaging = getMessaging(app);

권장(모듈 스코프 호이스팅) 예시:

// (컴포넌트 외부)
const app = getApps().length ? getApp() : initializeApp(firebaseConfig);
export const messaging = getMessaging(app);

// (컴포넌트 내부에서는 messaging만 사용)

Also applies to: 62-64


136-145: setState 직후 값 사용으로 잘못된 remindTime 전송

setRemindTime(...) 후 즉시 remindTime을 페이로드로 사용하여 초기값(‘09:00’)이 전송될 수 있습니다. 로컬 변수로 계산한 값을 사용하세요. 또한 선택 시간이 없을 가능성에 대비한 가드가 필요합니다.

적용 diff:

-      const raw = AlarmsType[alarmSelected - 1].time;
-      setRemindTime(normalizeTime(raw));
+      const raw = AlarmsType[alarmSelected - 1]?.time;
+      const normalizedTime = normalizeTime(raw ?? remindTime);
+      setRemindTime(normalizedTime);
@@
-          remindDefault: remindTime,
-          fcmToken: fcmToken,
+          remindDefault: normalizedTime,
+          fcmToken,

201-205: '다음' 버튼 비활성화 조건 수정 — apps/client/src/pages/onBoarding/components/funnel/MainCard.tsx (약 201–205행)

isDisabled={step === 6}은 절대 true가 되지 않습니다; 제출 처리 중 중복 클릭을 막으려면 usePostSignUp 훅의 로딩 플래그(예: isLoading / isPending)를 바인딩하거나 nextStep에서 제출 시작 시 로컬 로딩 상태를 설정해 isDisabled에 연결하세요.

🧹 Nitpick comments (6)
apps/extension/src/hooks/useSaveBookmarks.ts (1)

42-42: 팝업 자동 닫기 중단은 방향 OK. 주석 대신 의도 명시로 정리 권장

주석만 남기면 장기적으로 혼동될 수 있어 의도를 명확히 남겨 주세요.

-      //window.close();
+      // NOTE: 저장 후에도 팝업을 유지합니다(사용자 추가 입력/수정 편의 목적).
+      // 추후 환경설정(자동 닫기 on/off)으로 노출을 고려할 수 있습니다.
apps/extension/src/pages/MainPop.tsx (2)

42-42: 저장 불가 시 팝업 유지 결정 합리적. 의도 주석으로 고정 권장

동일 맥락의 변경(useSaveBookmarks)과 일관성 있습니다. 주석을 의도 명시로 교체하면 유지보수에 유리합니다.

-        //window.close(); 
+        // NOTE: 저장 불가 상황에서도 팝업을 닫지 않습니다(사용자 재시도/카테고리 변경 용이).

81-81: 불필요한 콘솔 로그 제거 또는 빌드 환경 가드 추가

lint 경고가 있으며 런타임 노이즈가 됩니다. 삭제하거나 prod에서 제외해 주세요.

-        console.log(savedData.remindAt);
+        // NOTE: remindAt 로드 확인이 필요하면 개발 환경에서만 로그하세요.
+        // if (process.env.NODE_ENV !== 'production') console.debug('[remindAt]', savedData.remindAt);
apps/client/src/pages/onBoarding/components/funnel/MainCard.tsx (3)

52-59: URL 파라미터 파싱 useEffect 의존성

location.search는 리액티브 값이 아니므로 의존성에 둘 이유가 없습니다. 마운트 1회 실행으로 단순화하세요. 라우터를 쓴다면 useLocation() 사용을 고려하세요.

적용 diff:

-  }, [location.search]);
+  }, []);

70-71: alert 대신 비차단형 피드백(토스트/배너) 사용 권장

모달 alert는 사용자 플로우를 막습니다. 디자인 시스템의 토스트/스낵바로 대체를 권장합니다. 원하시면 교체 패치 드리겠습니다.

Also applies to: 82-83, 87-88


146-151: 회원가입 실패(onError) 처리 추가 필요
회원가입 요청 실패 시 사용자 피드백과 로깅이 없습니다. 최소 onError를 추가해 콘솔 로깅 및 토스트/에러 배너로 안내하고, 필요 시 재시도/로그인 유도 흐름을 구현하세요.
파일: apps/client/src/pages/onBoarding/components/funnel/MainCard.tsx (라인 146-151)

적용 diff:

       postSignData(
         {
           email: userEmail,
           remindDefault: normalizedTime,
           fcmToken,
         },
         {
           onSuccess: () => {
             window.location.href = '/';
           },
+          onError: (err) => {
+            console.error('회원가입 실패:', err);
+            // TODO: 토스트/에러 배너로 사용자 안내
+          },
         }
       );
📜 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 dba84e7 and 877eb54.

📒 Files selected for processing (5)
  • apps/client/src/pages/onBoarding/components/funnel/AlarmBox.tsx (3 hunks)
  • apps/client/src/pages/onBoarding/components/funnel/MainCard.tsx (1 hunks)
  • apps/client/src/pages/onBoarding/utils/registerServiceWorker.ts (1 hunks)
  • apps/extension/src/hooks/useSaveBookmarks.ts (1 hunks)
  • apps/extension/src/pages/MainPop.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/client/src/pages/onBoarding/utils/registerServiceWorker.ts
  • apps/client/src/pages/onBoarding/components/funnel/AlarmBox.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-15T20:00:13.756Z
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#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
📚 Learning: 2025-07-08T11:47:10.642Z
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.

Applied to files:

  • apps/extension/src/pages/MainPop.tsx
🪛 GitHub Check: lint
apps/extension/src/pages/MainPop.tsx

[warning] 81-81:
Unexpected console statement

🔇 Additional comments (4)
apps/extension/src/pages/MainPop.tsx (1)

31-31: 변경 없음

의미 있는 코드 변화가 없어 코멘트 생략합니다.

apps/client/src/pages/onBoarding/components/funnel/MainCard.tsx (3)

191-198: Back 버튼 카피/스타일 변경 OK

가이드에 맞는 카피(“뒤로”)와 보조(secondary) 톤으로 보입니다. 다른 온보딩 화면의 Back 버튼과 톤·간격 일관성만 한번 더 확인 부탁드립니다.


141-144: email 파라미터 유효성 확인 필요

userEmail이 비어 있을 수 있습니다. 서버 요구사항에 따라 차단/보정 로직을 추가하세요(예: 비어있으면 입력 폼으로 이동).


65-76: ServiceWorker 등록을 await하고 getToken에 registration 전달 필요

rg 검색 결과 registerServiceWorker 정의를 찾을 수 없어 자동 검증 불가. 아래 변경을 MainCard.tsx( apps/client/src/pages/onBoarding/components/funnel/MainCard.tsx, 약 65–76행 )에 적용하거나, registerServiceWorker의 반환값이 ServiceWorkerRegistration인지 확인해 주세요.

-      registerServiceWorker();
+      const registration = await registerServiceWorker(); // ServiceWorkerRegistration 반환 전제
       if (permission !== 'granted') {
         alert('알림 권한 허용이 필요합니다!');
         return null;
       }

-      const forFcmtoken = await getToken(messaging, {
-        vapidKey: import.meta.env.VITE_FIREBASE_VAPID_KEY,
-      });
+      const forFcmtoken = await getToken(messaging, {
+        vapidKey: import.meta.env.VITE_FIREBASE_VAPID_KEY,
+        serviceWorkerRegistration: registration,
+      });

registerServiceWorker가 ServiceWorkerRegistration을 반환하지 않으면 유틸 시그니처도 함께 조정하세요.

<div className="flex flex-col justify-between gap-[1.6rem] rounded-[12px] bg-white px-[3.2rem] py-[2.4rem] text-black">
<div className="mr-auto">
<Icon name="main_logo" width={72} height={20} />
<Icon name="main_logo" width={72} height={20} onClick={()=>{window.location.href = 'https://pinback.today';}}/>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

확장 팝업에서 외부 도메인 이동은 새 탭 오픈으로 처리

popup 창 자체 내 location 이동은 UX가 좋지 않고 실패할 수 있습니다. chrome.tabs.create로 새 탭을 여는 것이 확장 UX 베스트 프랙티스입니다. Storybook 등 비확장 환경 호환을 위해 window.open 폴백도 추가합니다.

-            <Icon name="main_logo" width={72} height={20} onClick={()=>{window.location.href = 'https://pinback.today';}}/>
+            <Icon
+              name="main_logo"
+              width={72}
+              height={20}
+              onClick={() => {
+                // 크롬 확장 환경
+                // @ts-ignore - 전역 chrome 타입 미존재 환경 대비
+                if (typeof chrome !== 'undefined' && chrome?.tabs?.create) {
+                  // @ts-ignore
+                  chrome.tabs.create({ url: 'https://pinback.today' });
+                  return;
+                }
+                // 일반 웹/스토리북 환경
+                window.open('https://pinback.today', '_blank', 'noopener');
+              }}
+            />
🤖 Prompt for AI Agents
In apps/extension/src/pages/MainPop.tsx around line 237, the Icon onClick
currently navigates the popup's window to an external domain which is poor UX
and may fail; change the handler to open the link in a new tab using
chrome.tabs.create({ url: 'https://pinback.today' }) and fall back to
window.open('https://pinback.today', '_blank') when chrome.tabs is not available
(e.g., Storybook/dev). Keep the rest of the component unchanged and ensure the
click handler handles any asynchronous API call safely (no blocking UI) and does
not call window.location.href.

@jllee000 jllee000 changed the title Feat: 개발 QA 1차 Feat: 개발 QA 1차(재림 | 온보딩 기능 및 익스텐션 UI 쪽 수정) Sep 14, 2025
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.

[Fix] sp1 QA 온보딩&익스텐션 1차 반영

1 participant