Skip to content

[FEAT] 온보딩 화면 컴포넌트 구현#120

Merged
ehye1 merged 20 commits into
developfrom
feat/web/118-onboarding-components
Jul 10, 2026
Merged

[FEAT] 온보딩 화면 컴포넌트 구현#120
ehye1 merged 20 commits into
developfrom
feat/web/118-onboarding-components

Conversation

@ehye1

@ehye1 ehye1 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

ISSUE 🔗

close #118



What is this PR? 🔍

온보딩 화면에서 사용할 컴포넌트 5종(OnboardingButton, OnboardingGoogleButton, OnboardingTimeDropdown, OnboardingStepButton, OnboardingSelectCard)을 구현하고, 코드래빗 리뷰 반영 및 컴포넌트·컨테이너 계층 분리 작업을 진행했습니다.

배경

  • 기존 구조: 온보딩 관련 컴포넌트가 존재하지 않았습니다.
  • 필요 사항: 온보딩 플로우(언어/활동 선택, 시간 설정, 로그인, 단계 표시) 구현에 필요한 UI 컴포넌트가 필요했습니다.
  • 해결 방향: 순수 UI 컴포넌트는 onboarding/_components에, 클라이언트 상태와 번역을 소유하는 컴포넌트는 onboarding/_containers에 분리했습니다. 컴포넌트에서 "use client"useTranslations를 제거해 page.tsx가 Server Component를 유지할 수 있도록 했습니다.

OnboardingButton

  • 변경 요약: 다음/이전/시작하기 버튼 컴포넌트를 구현하고, 번역 책임을 컨테이너로 위임했습니다.
  • 이유: 컴포넌트 내부에서 useTranslations를 직접 호출하면 "use client" 선언이 강제되어 부모 Server Component에서 클라이언트 경계가 당겨집니다. 또한 버튼 텍스트를 컴포넌트 안에서 결정하면 재사용 시 i18n 키가 고정되는 제약이 생깁니다.
  • 구현 방식: label: string prop을 추가해 텍스트를 외부에서 주입하고, OnboardingButtonContaineruseTranslations로 번역된 텍스트를 내려줍니다. variant: "next" | "prev" | "start", isActive, disabled prop으로 3종 버튼을 단일 인터페이스에서 처리합니다.
  • 경계 · 제약: 상태 소유와 번역은 OnboardingButtonContainer가 담당합니다.

OnboardingGoogleButton

  • 변경 요약: Google 소셜 로그인/캘린더 연동 버튼 컴포넌트에서 "use client"와 번역 로직을 제거했습니다.
  • 이유: OnboardingButton과 동일하게 useTranslations 호출로 인해 "use client"가 강제되는 구조였습니다.
  • 구현 방식: label: string prop을 추가해 버튼 텍스트를 외부에서 주입합니다. OnboardingGoogleButtonContainerlogin | connectCalendar variant에 따른 번역 텍스트를 내려줍니다. 너비는 w-full로 선언해 부모에서 제어합니다.

OnboardingTimeDropdown

  • 변경 요약: "use client" 제거 및 getAmPm 함수를 utils/get-am-pm.ts로 분리했습니다.
  • 이유: useState나 이벤트 핸들러 없이 onChange prop을 받는 순수 제어 컴포넌트이므로 "use client" 선언이 불필요합니다. getAmPm 함수는 온보딩 외 다른 곳에서도 재사용될 수 있어 유틸로 분리했습니다.
  • 구현 방식: getAmPm 함수를 apps/timo-web/utils/get-am-pm.ts로 추출하고 절대 경로(@/utils/get-am-pm)로 import합니다. 피그마 레이아웃의 outer panel과 inner list 2중 구조를 Dropdown.Panel과 내부 div로 각각 표현했습니다.
  • 경계 · 제약: 상태 소유는 OnboardingTimeContainer가 담당합니다.

컨테이너 계층

  • 변경 요약: OnboardingButtonContainer, OnboardingGoogleButtonContainer, OnboardingTimeContainer_containers/에 추가했습니다.
  • 이유: "use client"useTranslations/useState를 컴포넌트에서 분리해 _components/의 순수 UI 컴포넌트 컨벤션을 유지하기 위함입니다.
  • 구현 방식: 각 컨테이너가 useTranslations 또는 useState를 소유하고 번역된 텍스트와 상태를 하위 컴포넌트에 주입합니다. OnboardingTimeContainer는 기존 OnboardingTimeField를 대체합니다.

OnboardingStepButton

  • 변경 요약: 1~4 단계 진행 상태를 표시하는 컴포넌트를 추가했습니다.
  • 이유: 온보딩 플로우의 현재 단계를 시각적으로 표시해야 했습니다.
  • 구현 방식: step prop(1|2|3|4)을 받아 현재 단계는 bg-timo-blue-300 text-white, 나머지는 bg-timo-blue-65 text-timo-blue-100으로 스타일링합니다. 단계 목록을 ol/li 시맨틱 태그로 구성했습니다.

디자인 시스템 에셋

  • 변경 요약: ChevronSmall 아이콘 SVG 소스 5종, ChevronDownGray/ChevronUpGray 2종, google-logo.svg를 추가했습니다.
  • 이유: 기존 Chevron 아이콘이 피그마 온보딩 디자인의 thin 스타일과 달랐고, OnboardingTimeDropdown 트리거에는 gray 계열의 별도 chevron이 필요했습니다.
  • 구현 방식: packages/timo-design-system/src/icons/source/에 SVG를 추가하고 icons:generate 스크립트로 컴포넌트를 생성했습니다.

scrollbar.css

  • 변경 요약: *::-webkit-scrollbar 너비를 6px에서 8px로 수정했습니다.
  • 이유: 파일에 6px로 명시돼 있었으나 브라우저에서 실제 렌더링되는 값이 8px였습니다. JS로 offsetWidth - clientWidth를 측정해 확인했습니다.



To Reviewers

컴포넌트에서 "use client"useTranslations를 제거하고 컨테이너로 위임하는 구조로 전환했습니다. _components/는 순수 UI만, _containers/는 상태·번역을 담당하는 분리 기준이 온보딩 페이지 구현 시에도 유지되는지 확인 부탁드립니다.

OnboardingButton"next" variant 사용 시 이제 label prop이 필수입니다. 기존 코드에서 "next_active", "start_inactive" variant를 사용 중인 곳이 있다면 마이그레이션이 필요합니다.

Screenshot 📷

Timo.-.Chrome.2026-07-08.12-52-38.mp4



Test Checklist ✔

  • lint-staged(prettier + eslint) 통과
  • 브라우저에서 /onboarding 라우트 렌더링 확인 — OnboardingTimeDropdown AM/PM 표시 및 드롭다운 동작 확인
  • pnpm check-types — 미실행
  • pnpm build — 미실행

ehye1 and others added 2 commits July 8, 2026 12:53
온보딩 컴포넌트에서 사용할 ChevronSmall SVG 소스 파일 5종과
Google 로그인 버튼에 사용할 google-logo.svg 에셋을 추가했습니다.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
OnboardingButton, OnboardingLoginButton, OnboardingTimeDropdown,
OnboardingStepButton 컴포넌트를 구현했습니다.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
timo Ready Ready Preview, Comment Jul 10, 2026 7:10am

@github-actions github-actions Bot added ⏰ Timo-web Timo 웹 서비스 ⌚ Timo-Design-system Timo 디자인 시스템 ✨ Feature 새로운 기능(기능성) 구현 ♥️ 혜원 혜원양 labels Jul 8, 2026
@ehye1 ehye1 changed the title [FEAT] 온보딩 컴포넌트 구현 [FEAT] 온보딩 화면 컴포넌트 구현 Jul 8, 2026
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

온보딩 화면의 버튼, 단계 표시, Google 연동 버튼, 시간 선택 UI가 추가됐고, 관련 번역 키도 en/ko 메시지에 등록됐다. 전역 웹킷 스크롤바 크기도 함께 조정됐다.

Changes

온보딩 UI

Layer / File(s) Summary
OnboardingButton 타입 및 구현
apps/timo-web/app/[locale]/onboarding/_components/OnboardingButton.tsx
variant별 번역 키와 허용 props를 분리하고, prev/start/next 분기를 각각 렌더링했다.
OnboardingStepButton 구현
apps/timo-web/app/[locale]/onboarding/_components/OnboardingStepButton.tsx
`step: 1
OnboardingGoogleButton 구현
apps/timo-web/app/[locale]/onboarding/_components/OnboardingGoogleButton.tsx
Google 로그인/캘린더 연결 variant와 선택 상태에 따라 텍스트·스타일·로고를 렌더링했다.
OnboardingTimeDropdown 및 TimeField
apps/timo-web/app/[locale]/onboarding/_components/OnboardingTimeDropdown.tsx, apps/timo-web/app/[locale]/onboarding/_components/OnboardingTimeField.tsx
시간 옵션 목록, AM/PM 표시, 선택 콜백, 로컬 상태를 사용하는 시간 선택 흐름을 추가했다.
다국어 메시지 추가
apps/timo-web/messages/en.json, apps/timo-web/messages/ko.json
온보딩 버튼과 Google 버튼에 필요한 번역 문자열을 추가했다.

스크롤바 스타일

Layer / File(s) Summary
Scrollbar size update
packages/tailwind-config/scrollbar.css
웹킷 스크롤바의 width와 height를 8px로 변경했다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • Team-Timo/Timo-client#78: OnboardingTimeDropdown가 의존하는 Dropdown/Dropdown.Item의 동작과 연결됩니다.
  • Team-Timo/Timo-client#105: useTranslations("Onboarding")가 사용하는 i18n 메시지 구조 확장과 맞닿아 있습니다.

Suggested reviewers: jjangminii, kimminna, yumin-kim2

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning OnboardingButton variant 구성과 OnboardingLoginButton/Dropdown의 요구 상태가 요약상 맞지 않아 #118을 완전히 충족하지 못합니다. OnboardingButton variant을 5개로 맞추고, 로그인 버튼은 selected/305px 규격을 적용한 뒤, Dropdown의 off/on/selected 상태를 Figma대로 보완하세요.
✅ Passed checks (4 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed 변경은 온보딩 UI와 필요한 스크롤바 조정에 집중되어 있어, 명백한 무관 변경은 보이지 않습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed 온보딩 화면용 컴포넌트 구현이라는 핵심 변경을 잘 요약해 관련성이 높습니다.
Description check ✅ Passed 온보딩 UI 컴포넌트 구현과 컨테이너 분리라는 변경 내용과 전반적으로 일치합니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/web/118-onboarding-components

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

Storybook Preview

항목 링크
Storybook 열기
Chromatic 빌드 확인

마지막 업데이트: 2026-07-10 07:10 UTC

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

Timo Performance Report

Bundle Size — timo-web
라우트 크기 First Load JS
/[locale] 0 B 🟡 205.79 kB
/[locale]/focus 0 B 🟡 205.79 kB
/[locale]/home 0 B 🟡 205.79 kB
/[locale]/onboarding 0 B 🟡 205.79 kB
/[locale]/settings 0 B 🟡 205.79 kB
/[locale]/settings/account 0 B 🟡 205.79 kB
/[locale]/settings/policy 0 B 🟡 205.79 kB
/[locale]/statistics 0 B 🟡 205.79 kB
/[locale]/today 0 B 🟡 205.79 kB

공유 번들: 205.79 kB
🟢 < 200kB  |  🟡 < 350kB  |  🔴 ≥ 350kB (First Load JS · gzip)

Lighthouse — timo-web

⚠️ Lighthouse 결과를 가져오지 못했습니다.

Image Optimization — timo-web

public/ 디렉토리에 이미지가 없습니다.

측정 커밋: 765be9d

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/timo-web/app/`[locale]/onboarding/_components/OnboardingButton.tsx:
- Around line 32-55: `OnboardingButton`의 비활성 variant 처리에서 `active`와 `disabled`가
분리되어 있지 않아, `다음`과 `시작하기_비활성`이 시각적으로만 비활성화되고 실제로는 클릭 가능한 상태입니다. `variant`에 따라
`button`의 `disabled` 속성을 설정하도록 수정하고, `이전`은 회색 스타일이어도 계속 클릭 가능해야 하므로 `active` 판정과
`disabled` 판정을 별도로 처리하세요. `OnboardingButton`의 `variant`, `active`, `LABEL` 렌더링
흐름을 기준으로 조건을 정리해 주세요.

In `@apps/timo-web/app/`[locale]/onboarding/_components/OnboardingLoginButton.tsx:
- Around line 5-15: The boolean prop in OnboardingLoginButtonProps should follow
the naming convention by renaming selected to isSelected and updating the
OnboardingLoginButton destructuring/defaults and all call sites that pass or
read this prop accordingly. Make sure the component interface, function
signature, and any conditional styling or behavior tied to selected use the new
isSelected name consistently.

In `@apps/timo-web/app/`[locale]/onboarding/_components/OnboardingStepButton.tsx:
- Around line 10-31: The step indicator in OnboardingStepButton is rendered with
non-semantic div wrappers even though it represents an ordered sequence. Update
the JSX in OnboardingStepButton to use an ordered list structure with ol and li
for the STEPS.map output, keeping the existing styling and active/inactive
classes via cn while preserving the current visual layout and keys.

In
`@apps/timo-web/app/`[locale]/onboarding/_components/OnboardingTimeDropdown.tsx:
- Around line 11-14: `TIME_OPTIONS`가 `Array.from({ length: 25 })`로 생성되어 24:00이
00:00과 중복됩니다. `OnboardingTimeDropdown`의 `TIME_OPTIONS` 생성 로직을 수정해 일반적인 선택기 범위인
00:00~23:00만 포함하도록 `length`를 24로 조정하세요.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fd8159bf-43a4-4f3c-9b22-0f62ea6f03e6

📥 Commits

Reviewing files that changed from the base of the PR and between cf96425 and e43b142.

⛔ Files ignored due to path filters (6)
  • packages/timo-design-system/src/assets/images/google-logo.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/chevron-small-down.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/chevron-small-left.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/chevron-small-right-white.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/chevron-small-right.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/chevron-small-up.svg is excluded by !**/*.svg
📒 Files selected for processing (4)
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingButton.tsx
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingLoginButton.tsx
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingStepButton.tsx
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingTimeDropdown.tsx

Comment thread apps/timo-web/app/[locale]/onboarding/_components/OnboardingButton.tsx Outdated
Comment thread apps/timo-web/app/[locale]/onboarding/_components/OnboardingLoginButton.tsx Outdated
- OnboardingButton variant를 영문 키로 변경 후 useTranslations 연결
- OnboardingLoginButton → OnboardingGoogleButton으로 rename, variant 기반 번역 적용
- OnboardingTimeDropdown "use client" 추가
- ko.json / en.json에 Onboarding 네임스페이스 추가
- 온보딩 page.tsx에 컴포넌트 테스트 렌더링 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ehye1 and others added 2 commits July 9, 2026 03:32
…적용 (#118)

- OnboardingButton: next/start_inactive variant에 disabled 속성 추가
- OnboardingGoogleButton: selected → isSelected boolean prop 컨벤션 적용

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
internalValue 상태를 추가해 비제어 모드에서도 선택값이 저장되도록 수정했습니다.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 (2)
apps/timo-web/app/[locale]/onboarding/_components/OnboardingGoogleButton.tsx (2)

24-32: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

aria-pressed 속성을 추가해 선택 상태를 스크린 리더에 전달해야 합니다.

isSelected prop으로 시각적 스타일은 전환되지만, 보조 기술에는 이 상태가 전달되지 않습니다. 토글/선택 버튼으로 사용되는 경우 aria-pressed={isSelected}를 추가해 접근성을 보장해야 합니다.

참고: WAI-ARIA Button Pattern - Toggle Button

As per coding guidelines, "텍스트 없는 버튼에 aria-label 필수" 및 접근성 규칙을 따라야 합니다.

♿ 제안: aria-pressed 추가
     <button
       type="button"
       onClick={onClick}
+      aria-pressed={isSelected}
       className={cn(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/app/`[locale]/onboarding/_components/OnboardingGoogleButton.tsx
around lines 24 - 32, The OnboardingGoogleButton toggle state is only reflected
in styling, so update the button in OnboardingGoogleButton to expose that state
to assistive tech by adding the pressed/selected accessibility attribute based
on isSelected. Keep the existing button behavior and className logic, and ensure
the same component also satisfies the no-text-button accessibility rule by
providing an appropriate label for the button element.

Source: Path instructions


34-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

불필요한 div 래핑을 제거하고 스타일을 버튼으로 통합할 수 있습니다.

버튼이 이미 flex items-center justify-center를 가지고 있어 내부 div의 flex 속성이 중복됩니다. gap-2.5 px-2를 버튼 className으로 이동하고 line 34의 div를 제거해 DOM 중첩을 줄일 수 있습니다.

As per coding guidelines, "불필요한 div 래핑 금지 — 단순 그룹핑 목적이면 Fragment <>…</> 사용" 규칙을 따라야 합니다.

♻️ 제안: div 제거 및 스타일 통합
       className={cn(
         "flex w-[305px] items-center justify-center rounded-[4px] border py-2.5",
         isSelected
           ? "border-timo-blue-300 bg-timo-blue-50"
           : "border-timo-gray-500 bg-white",
       )}
     >
-      <div className="flex items-center gap-2.5 px-2">
-        <div className="flex size-[22px] items-center justify-center">
+      <div className="flex size-[22px] items-center justify-center">
         <Image src={googleLogo} alt="Google" width={18} height={18} />
       </div>
       <span className="typo-headline-m-16 text-timo-blue-300">
         {t(`onboardingGoogleButton.${variant}`)}
       </span>
-      </div>
     </button>

버튼 className에 gap-2.5 px-2 추가:

       className={cn(
-        "flex w-[305px] items-center justify-center rounded-[4px] border py-2.5",
+        "flex w-[305px] items-center justify-center gap-2.5 rounded-[4px] border px-2 py-2.5",
         isSelected
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/app/`[locale]/onboarding/_components/OnboardingGoogleButton.tsx
around lines 34 - 35, Remove the unnecessary wrapper div inside
OnboardingGoogleButton and move its spacing styles onto the button itself: the
button already handles flex centering, so `gap-2.5` and `px-2` should be applied
to the Button element while the inner div at that spot can be deleted (or
replaced with a Fragment if needed). Keep the existing icon/content structure
intact and verify the button still renders with the same alignment using the
OnboardingGoogleButton component.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/timo-web/app/`[locale]/onboarding/_components/OnboardingButton.tsx:
- Around line 41-42: The boolean locals in OnboardingButton are named without
the required prefix; rename active and disabled to isActive and isDisabled to
match the boolean naming guideline. Update the declarations in OnboardingButton
and any subsequent references in that component so the new names are used
consistently.

---

Outside diff comments:
In
`@apps/timo-web/app/`[locale]/onboarding/_components/OnboardingGoogleButton.tsx:
- Around line 24-32: The OnboardingGoogleButton toggle state is only reflected
in styling, so update the button in OnboardingGoogleButton to expose that state
to assistive tech by adding the pressed/selected accessibility attribute based
on isSelected. Keep the existing button behavior and className logic, and ensure
the same component also satisfies the no-text-button accessibility rule by
providing an appropriate label for the button element.
- Around line 34-35: Remove the unnecessary wrapper div inside
OnboardingGoogleButton and move its spacing styles onto the button itself: the
button already handles flex centering, so `gap-2.5` and `px-2` should be applied
to the Button element while the inner div at that spot can be deleted (or
replaced with a Fragment if needed). Keep the existing icon/content structure
intact and verify the button still renders with the same alignment using the
OnboardingGoogleButton component.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b68fd815-c46b-4375-9507-640746eb5453

📥 Commits

Reviewing files that changed from the base of the PR and between 300728d and 0d2c23b.

📒 Files selected for processing (5)
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingButton.tsx
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingGoogleButton.tsx
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingTimeDropdown.tsx
  • apps/timo-web/messages/en.json
  • apps/timo-web/messages/ko.json

Comment thread apps/timo-web/app/[locale]/onboarding/_components/OnboardingButton.tsx Outdated
ehye1 added 2 commits July 9, 2026 20:52
- active → isActive, disabled → isDisabled로 변수명을 수정했습니다
- 스텝 인디케이터의 div 태그를 ol/li 시맨틱 태그로 교체했습니다

@yumin-kim2 yumin-kim2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[OnboardingTimeDropdown] 피그마 명세서를 보면 "07:00 AM", "23:00 AM"처럼
시간 옆에 AM/PM 표기가 있는데, 현재 구현에서는 표시가 안 되고 있어요.

image
TIME_OPTIONS가 24시간제 문자열("00:00"~"24:00")만 생성하고 있어서, AM/PM을 계산하는 로직이 없는 것 같습니다.

12시간제 + AM/PM으로 변환해서 표시하는 로직 추가가 필요해 보입니다-!!

@jjangminii jjangminii left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

여러 컴포넌트 하시느라 번거로웠을 텐데 고생하셨습니다~
코멘트 확인해주세요~~

Comment on lines +19 to +30
export interface OnboardingTimeDropdownProps {
value?: string;
placeholder?: string;
onChange?: (time: string) => void;
}

export const OnboardingTimeDropdown = ({
value,
placeholder,
onChange,
}: OnboardingTimeDropdownProps) => {
const [internalValue, setInternalValue] = useState<string | undefined>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

value가 optional이라서 부모가 안 넘기면 내부 internalValue state로 동작하고, 넘기면 부모 값을 따라가는 구조로 보여요. React는 렌더 도중 controlled <-> uncontrolled로 전환되는 걸 경고하는데, 이 구조에서는 그 상황이 발생할 수 있어요.
Checkbox처럼 value를 필수로 받고 내부 state를 없애면 팀 컨벤션과 일치하고 동작도 예측 가능해져요. 온보딩에서 기상/취침 시간을 상태로 들고 있어야 하는 건 어차피 부모 컨테이너 몫이니까요-!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

처음에 구현할 때 부모에서 value를 넘기지 않아도 트리거에 선택값이 표시되도록 내부 상태를 두었어요.
정민님이 주신 아티클 보니까 제어/비제어 방식 섞어서 구현하기보다는 상태를 한 곳에서만 관리하는 것이 컴포넌트를 더 단순하게 유지하고 동작을 예측하기가 쉬운 것 같아요.
어차피 온보딩 페이지에서는 최종 선택 시간을 부모가 서버에 전달해야 하니까 부모가 상태를 관리하는 구조가 자연스러운 것 같네요.
말씀하신 것처럼 내부 상태 제거하고 순수 제어 컴포넌트로 수정하겠습니다!


export interface OnboardingGoogleButtonProps {
variant: OnboardingGoogleButtonVariant;
isSelected?: boolean;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

사소한거긴 한데 PR 설명에는 selected prop이라고 적혀있어서 pr 내용에 실제 코드랑 prop명 일치시키면 좋을 것 같아요-!

Comment on lines +11 to +21
export type OnboardingButtonVariant =
| "next"
| "next_active"
| "prev"
| "start"
| "start_inactive";

export interface OnboardingButtonProps {
variant: OnboardingButtonVariant;
onClick?: () => void;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
export type OnboardingButtonVariant =
| "next"
| "next_active"
| "prev"
| "start"
| "start_inactive";
export interface OnboardingButtonProps {
variant: OnboardingButtonVariant;
onClick?: () => void;
}
type OnboardingButtonVariant = "next" | "prev" | "start";
interface OnboardingButtonProps {
variant: OnboardingButtonVariant;
isActive?: boolean;
disabled?: boolean;
onClick?: () => void;
}

현재 next/next_active, start/start_inactive처럼 활성/비활성 상태가 variant로 분리되어 있는데, variant가 늘어날수록 BUTTON_TRANSLATION_KEY Record도 같이 커져서 유지보수가 어려워질 것 같아요. 활성 상태는 별도 prop으로 분리하는 게 어떨까요?

@ehye1 ehye1 Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

맞네요! 말씀하신대로 variant는 버튼 종류(next/prec/start)만 담당하게 하고 활성 상태는 isActive prop으로 분리해서 반영하겠습니다!

Comment on lines +11 to +12
const PANEL_SCROLLBAR =
"[scrollbar-width:thin] [scrollbar-color:var(--color-timo-gray-600)_transparent] [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-timo-gray-600 [&::-webkit-scrollbar-thumb:hover]:bg-timo-gray-700";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

스크롤바.. 이미 전역에 적용해뒀는데.. 왜..?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

전역 스크롤바와 onboardingTimeDropdown 안에 스크롤바 디자인이 달라서 새로 지정했었습니다,,
해당 컴포넌트에서만 사용하는 스타일인만큼 CSS Module로 분리했는데 안되네요,, 도와주세요..

type="button"
onClick={onClick}
className={cn(
"flex w-[305px] items-center justify-center rounded-[4px] border py-2.5",

@jjangminii jjangminii Jul 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

w-[305px] 고정 너비로 사용하고 계신데 부모 컨테이너에 맞게 w-full로 바꾸고 너비는 소비하는 쪽에서 제어하는 게 재사용성이 높다고 생각이들어요.

Comment on lines +63 to +64
{variant === "next" && <ChevronSmallRightIcon />}
{variant === "next_active" && <ChevronSmallRightWhiteIcon />}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

여기도 isActive/isDisabled로 분리하면 아이콘 렌더링 조건도 자연스럽게 단순해질 것 같아요~

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/timo-web/app/`[locale]/onboarding/_components/OnboardingTimeField.tsx:
- Around line 1-17: OnboardingTimeField currently uses internal state and a
client-only hook, which violates the _components/ rule that these should be pure
UI and work without "use client". Update OnboardingTimeField to be a controlled
component that receives value and onChange props and forwards them to
OnboardingTimeDropdown, removing useState and the "use client" directive; if
state ownership must stay local, move that responsibility to the parent
container instead and keep OnboardingTimeField prop-only.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 52d27919-f935-44c7-a73b-ce8c29d2607a

📥 Commits

Reviewing files that changed from the base of the PR and between bb0fade and 43c8b5d.

⛔ Files ignored due to path filters (2)
  • packages/timo-design-system/src/icons/source/chevron-down-gray.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/chevron-up-gray.svg is excluded by !**/*.svg
📒 Files selected for processing (5)
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingButton.tsx
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingGoogleButton.tsx
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingTimeDropdown.tsx
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingTimeField.tsx
  • packages/tailwind-config/scrollbar.css

- OnboardingTimeDropdown import 경로를 ./ 에서 ../_components/로 수정했습니다
Comment thread apps/timo-web/app/[locale]/onboarding/_containers/OnboardingTimeField.tsx Outdated
Comment thread apps/timo-web/app/[locale]/onboarding/_containers/OnboardingTimeField.tsx Outdated
Comment thread apps/timo-web/app/[locale]/onboarding/_components/OnboardingTimeDropdown.tsx Outdated
Comment on lines +15 to +18
const getAmPm = (time: string): "AM" | "PM" => {
const hour = parseInt(time.split(":")[0] ?? "0", 10);
return hour >= 12 && hour < 24 ? "PM" : "AM";
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

이거 유틸로 빼도 재사용하기 좋을 거 같아요
Jsdoc 추가해서 빼줍시당

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

넵!! timo-web/utils에 유틸함수 Jsdoc 추가해서 새로 생성했습니다! 🫰🏻

Comment thread apps/timo-web/app/[locale]/onboarding/_components/OnboardingStepButton.tsx Outdated
Comment thread apps/timo-web/app/[locale]/onboarding/_components/OnboardingGoogleButton.tsx Outdated
Comment on lines +22 to +30
export type OnboardingButtonProps =
| {
variant: "next";
isActive: boolean;
disabled?: boolean;
onClick?: () => void;
}
| { variant: "prev"; onClick?: () => void }
| { variant: "start"; onClick?: () => void };

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

버튼 상태에 따른 분기 처리도 많아 보이고, 지금 컴포넌트 자체도 UI 전용이라기보단 여러 책임을 관리하고 있는 것 같아서 책임 원칙에 따른 컴포넌트 분리가 필요해 보이고,

컴포넌트 내부 폴더엔 최대한 클라이언트 로직을 제거한 순수 UI 컴포넌트만 위치하게 수정해 보면 좋겠습니다!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

onboardingButton, onboardingGoogleButton도 컨테이너/컴포넌트로 분리했습니다!
use client 사용에 항상 주의하면서 파일을 만들어야 할 것 같네요,,(컴포넌트와 로직이 한꺼번에 클라이언트 번들에 들어가면서 초기 로딩에 영향을 주니까!!)

start: "button.start",
};

export type OnboardingButtonProps =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Props는 인터페이스로 통일합시다!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

원래는 버튼 3종류가 서로 다른 특성을 가지고 있어서 유니언으로 나눴었습니다!
image

  • next (다음 버튼)
    isActive: 필수 — 활성화 여부에 따라 색상/아이콘이 바뀜 (파란색 / 회색)
    disabled: 선택 — 강제 비활성화 가능
    스타일: 파란/회색 배경 + 오른쪽 화살표 아이콘
  • prev (이전 버튼)
    isActive, disabled 없음 — 항상 같은 스타일(회색 배경 + 왼쪽 화살표)
    hover 효과만 있음
  • start (시작 버튼)
    isActive, disabled 없음 — 항상 같은 스타일(파란 배경, 아이콘 없음)
    텍스트만 다름

그랬는데 props가 붙어도 큰 문제가 없고,, 컨벤션 일관성이 더 중요한 것 같네요. 수정하겠습니다!!💗

- 컴포넌트에서 "use client"와 번역 로직을 제거하고 컨테이너로 이동했습니다
- OnboardingButton, OnboardingGoogleButton에 label prop을 추가해 번역 책임을 컨테이너로 위임했습니다
- OnboardingButtonContainer, OnboardingGoogleButtonContainer, OnboardingTimeContainer를 신규 추가했습니다
- OnboardingTimeField를 OnboardingTimeContainer로 대체했습니다
- getAmPm 함수를 utils/get-am-pm.ts로 분리했습니다
- 외부에서 사용하지 않는 interface export를 제거했습니다

@kimminna kimminna left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

고생했따!! 마지막으로 export 하나만 뺴줘용


type OnboardingGoogleButtonVariant = "login" | "connectCalendar";

export interface OnboardingGoogleButtonProps {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
export interface OnboardingGoogleButtonProps {
interface OnboardingGoogleButtonProps {

- OnboardingGoogleButtonProps 인터페이스의 불필요한 export 키워드를 제거했습니다

@yumin-kim2 yumin-kim2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

수정사항 확인했습니다! 고생하셨서요~~❣️

@ehye1 ehye1 merged commit f3e2dd6 into develop Jul 10, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능(기능성) 구현 ⌚ Timo-Design-system Timo 디자인 시스템 ⏰ Timo-web Timo 웹 서비스 ♥️ 혜원 혜원양

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 온보딩 화면 OnboardingButton, OnboardingLoginButton, OnboardingDropdown, OnboardingStepButton 컴포넌트 구현

4 participants