Skip to content

[FEAT] 온보딩 퍼널 및 로그인 페이지 구현#139

Open
ehye1 wants to merge 8 commits into
developfrom
feat/web/134-onboarding-funnel
Open

[FEAT] 온보딩 퍼널 및 로그인 페이지 구현#139
ehye1 wants to merge 8 commits into
developfrom
feat/web/134-onboarding-funnel

Conversation

@ehye1

@ehye1 ehye1 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

ISSUE 🔗

close #134


What is this PR? 🔍

온보딩 4단계 퍼널(언어 선택 → 작업 시간 예측 → 생활 패턴 → 구글 캘린더 연동)과 로그인 페이지를 구현했습니다.


배경

이전 이슈(#108, #118)에서 순수 UI 컴포넌트(OnboardingSelectCard, OnboardingButton, OnboardingGoogleButton, OnboardingStepButton, OnboardingTimeDropdown)만 구현된 상태였고, /[locale]/onboarding은 빈 페이지였습니다. 컴포넌트를 실제 4단계 흐름으로 조립하고, 스텝 간 데이터를 이어가고, 완료 시점에 payload를 조립하는 로직이 없어 온보딩이 동작하지 않는 상태를 이번 PR에서 해결했습니다.


구현 상세

1. 퍼널 아키텍처 — @use-funnel/browser

@use-funnel/browser인가

App Router 환경에서는 @use-funnel/next(Pages Router 전용)가 아니라 @use-funnel/browser가 올바른 선택입니다. 스텝 상태는 URL 쿼리스트링(id: "onboarding")에 저장되므로 브라우저 뒤로가기/앞으로가기가 자동으로 처리됩니다.

스텝 타입 선언

// _types/onboarding-funnel.ts
export type OnboardingFunnelSteps = {
  Language:        { language?: "ko" | "en" };
  TimePrediction:  { language: "ko" | "en"; predictionAccuracy?: PredictionAccuracy };
  LifePattern:     { language: "ko" | "en"; predictionAccuracy: PredictionAccuracy; wakeUpTime?: string; bedTime?: string };
  CalendarConnect: { language: "ko" | "en"; predictionAccuracy: PredictionAccuracy; wakeUpTime: string; bedTime: string };
};

각 스텝이 이전 스텝의 필드를 이어받는 구조로, 스텝이 넘어갈수록 필드의 필수/옵션 여부가 좁혀집니다. interface가 아닌 type으로 선언한 이유는 use-funnel 내부의 CompareMergeContextkeyof로 필드를 비교하는데, interface는 선언 병합 가능성 때문에 keyof 추론이 깨져 타입 오류가 발생했기 때문입니다(파일 내 주석 및 eslint-disable 참고).

사용 입력값(answers)을 별도 useState로 관리한 이유

퍼널 context만으로 입력값을 관리하면 "뒤로 갔다가 새로 push"할 때 이미 진행한 forward 히스토리(예: 선택했던 predictionAccuracy)가 통째로 날아가는 문제가 실제로 발생했습니다. 리마운트되지 않는 OnboardingFunnelContainer 레벨 useState로 옮겨 이 문제를 우회했습니다.

// OnboardingFunnelContainer.tsx
const [answers, setAnswers] = useState<Partial<OnboardingFunnelSteps["CalendarConnect"]>>({});

const funnel = useFunnel<OnboardingFunnelSteps>({
  id: "onboarding",
  initial: { step: "Language", context: {} },
});

// 스텝 전환 예시 (Language → TimePrediction)
onNext={() => {
  if (!answers.language) return;
  history.push("TimePrediction", { language: answers.language });
}}

렌더링 구조

<funnel.Render
  Language={({ history }) => <LanguageStepContainer ... />}
  TimePrediction={({ history }) => <TimePredictionStepContainer ... />}
  LifePattern={({ history }) => <LifePatternStepContainer ... />}
  CalendarConnect={({ history }) => <CalendarConnectStepContainer ... />}
/>

현재 활성 스텝만 렌더링되며, history.push(다음스텝, context) / history.back()으로 전환합니다.


2. 온보딩 스텝 화면

#108, #118에서 만들어둔 컴포넌트를 그대로 재사용했습니다. 새로운 디자인 시스템 컴포넌트는 없습니다.

스텝 컨테이너 주요 동작
1 LanguageStepContainer 언어 선택(ko/en), 선택 해제 토글
2 TimePredictionStepContainer 작업 시간 예측 정확도 선택(1~4)
3 LifePatternStepContainer 기상/취침 시간 선택, bedTime <= wakeUpTime이면 경고 + 다음 버튼 비활성화
4 CalendarConnectStepContainer 구글 캘린더 연동 버튼, "시작하기" 클릭 시 payload 조립

TODO: "시작하기" 클릭 시 현재는 console.log로만 payload를 출력합니다. 백엔드 스펙 확정 후 실제 POST 호출 및 locale 리다이렉트를 후속 이슈로 진행 예정입니다.


3. 로그인 페이지

로그인은 OAuth 리다이렉트(외부 도메인 이동)라 퍼널 스텝에 포함할 수 없어 /[locale]/login 라우트를 별도로 추가했습니다. OnboardingGoogleButtonContainer(variant="login")를 재사용했습니다.

TODO: 구글 로그인 리다이렉트 URL, 약관/개인정보처리방침 링크는 백엔드 확정 후 교체 예정입니다.


4. Lottie 애니메이션 연동

PR #131 병합 후 로그인·온보딩 화면 좌측에 /lottie/onboarding.jsonLottiePlayer로 교체했습니다. 반응형으로 구현했습니다.

브레이크포인트 Lottie 크기 간격
< lg 숨김
lg (1024px) 350px 64px
xl (1280px) 430px 144px
2xl (1536px) 500px 225px

5. 디자인 시스템 아이콘 추가

생활패턴 스텝에 필요한 아이콘 소스 SVG를 추가했습니다.

아이콘 용도
sun.svg 기상 시간 라벨
moon.svg 취침 시간 라벨
alert.svg 취침 시간 경고 문구

pnpm icons:generate로 자동 생성되며, 생성 결과물은 .gitignore 대상이라 소스 SVG만 포함됩니다.


Screenshot 📷

image image image

To Reviewers

  • OnboardingFunnelStepstype이어야 하는 이유가 use-funnel 라이브러리 자체 제약인지, 사용 방식 문제인지 확인 부탁드립니다.
  • 사용자 입력값을 퍼널 context 대신 OnboardingFunnelContainer useState로 관리한 구조 선택에 대한 의견 부탁드립니다.
  • API 연결(온보딩 제출, 구글 캘린더 연동, 로그인 리다이렉트)은 모두 의도적으로 제외했습니다. 백엔드 스펙 확정 후 후속 이슈로 진행 예정입니다.

Test Checklist ✔

  • pnpm check-types 통과
  • pnpm lint 통과 (--max-warnings 0)
  • 각 스텝 구현 시점마다 브라우저에서 개별 동작 확인 (뒤로가기 시 입력값 유지, 생활패턴 검증 문구 등)
  • 로그인 → 4단계 → 시작하기 전체 흐름 end-to-end 재확인 — 최근 변경(Lottie 연동 등) 이후 미실행
  • pnpm build — 미실행: CI에서 확인 예정

ehye1 and others added 8 commits July 11, 2026 01:56
- 온보딩 퍼널 구현을 위해 @use-funnel/browser 패키지를 추가했습니다
- 온보딩 화면에서 사용할 alert, moon, sun SVG 아이콘을 추가했습니다
- 로그인 페이지 한/영 메시지를 추가했습니다
- 언어 선택, 시간 예측, 생활 패턴, 캘린더 연결 단계 메시지를 추가했습니다
- LoginContainer 컨테이너를 구현했습니다
- 로그인 페이지를 추가했습니다
- 온보딩 퍼널 타입(onboarding-funnel.ts)을 정의했습니다
- 언어 선택, 시간 예측, 생활 패턴, 캘린더 연결 단계 컨테이너를 구현했습니다
- OnboardingFunnelContainer로 퍼널 흐름을 조합했습니다
- 온보딩 페이지에 OnboardingFunnelContainer를 연동했습니다
- Pretendard 폰트 메트릭(line-height 1.5)으로 인한 시각적 상하 어긋남을 수정했습니다
- leading-none으로 line-height를 1로 줄이고 translate-y-px로 1px 보정했습니다
- onboarding/page.tsx 충돌을 HEAD(OnboardingFunnelContainer) 기준으로 해결했습니다
- en.json, ko.json에 Login 키와 Error/NotFound/GlobalError 키를 모두 포함했습니다
- 로그인·온보딩 화면에 onboarding Lottie 애니메이션 추가 (반응형 lg/xl/2xl)
- 바깥 래퍼를 div에서 main으로 변경 (시맨틱)
- 로그인 태그라인 span → p 변경 (시맨틱)
- OnboardingButtonProps, OnboardingSelectCardProps 불필요 export 제거

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

vercel Bot commented Jul 10, 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 6:59pm

@github-actions github-actions Bot added ⏰ Timo-web Timo 웹 서비스 ⌚ Timo-Design-system Timo 디자인 시스템 labels Jul 10, 2026
@github-actions github-actions Bot added ✨ Feature 새로운 기능(기능성) 구현 ♥️ 혜원 혜원양 labels Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

로그인 화면과 로케일별 페이지를 추가하고, 언어·예측 정확도·생활 패턴·캘린더 연결로 구성된 4단계 온보딩 퍼널을 구현했습니다. 단계별 상태와 입력 검증, 다국어 문구, 퍼널 이동 및 payload 생성이 포함됩니다.

Changes

로그인 화면

Layer / File(s) Summary
로그인 페이지 구성
apps/timo-web/app/[locale]/login/..., apps/timo-web/messages/*.json
로그인 컨테이너가 번역된 제목·설명·약관 안내와 Google 로그인 버튼을 렌더링하며 로케일 로그인 페이지에 연결됩니다.

온보딩 퍼널

Layer / File(s) Summary
온보딩 단계와 입력 검증
apps/timo-web/app/[locale]/onboarding/_types/..., apps/timo-web/app/[locale]/onboarding/_containers/..., apps/timo-web/app/[locale]/onboarding/_components/..., apps/timo-web/messages/*.json
4개 단계의 선택·입력 UI와 진행 조건을 추가하고, 생활 패턴 시간 검증 및 캘린더 연결 상태를 구현했습니다.
온보딩 퍼널 연결
apps/timo-web/app/[locale]/onboarding/..., apps/timo-web/package.json
@use-funnel/browser 기반으로 단계 상태, 다음·이전 이동, 최종 payload 생성을 구성하고 온보딩 페이지에 연결했습니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant OnboardingFunnelContainer
  participant StepContainer
  participant useFunnel

  User->>StepContainer: 단계 입력 또는 선택
  StepContainer->>OnboardingFunnelContainer: 선택값 및 이동 callback 전달
  OnboardingFunnelContainer->>useFunnel: 다음 단계로 history.push
  useFunnel-->>OnboardingFunnelContainer: 현재 단계 렌더링
  OnboardingFunnelContainer->>StepContainer: 다음 단계 UI 표시
Loading

Possibly related PRs

  • Team-Timo/Timo-client#105: 로케일 라우팅과 next-intl 메시지 구조가 새 로그인·온보딩 페이지의 기반이 됩니다.
  • Team-Timo/Timo-client#117: 새 온보딩 퍼널에서 사용하는 OnboardingSelectCard와 props 타입을 도입한 변경입니다.
  • Team-Timo/Timo-client#120: 퍼널에서 재사용하는 온보딩 버튼 컴포넌트와 단계 버튼 스타일을 수정한 변경입니다.

Suggested reviewers: yumin-kim2, jjangminii, kimminna

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning 온보딩 퍼널은 구현됐지만 제출 후 선택 언어 locale 리다이렉트가 보여지지 않아 #134의 완료 조건을 모두 충족하지 못합니다. locale 리다이렉트와 제출 완료 후 이동 로직을 추가해 이슈의 마무리 조건을 맞추세요.
Out of Scope Changes check ⚠️ Warning 로그인 페이지, Lottie 적용, 아이콘 추가는 #134의 온보딩 퍼널 요구와 직접 관련되지 않은 추가 변경입니다. #134 범위와 분리해 별도 PR로 내거나, 현재 PR의 추가 변경 목적을 설명에 더 명확히 적어 주세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 온보딩 퍼널과 로그인 페이지 구현이라는 핵심 변경을 정확히 요약합니다.
Description check ✅ Passed 설명이 온보딩 4단계 퍼널과 로그인 페이지 구현을 직접 설명해 변경 내용과 일치합니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/web/134-onboarding-funnel

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

Copy link
Copy Markdown

Timo Performance Report

Bundle Size — timo-web
라우트 크기 First Load JS
/[locale]/home 72.41 kB 🟡 278.25 kB
/[locale]/today 55.82 kB 🟡 261.65 kB
/[locale]/focus 52.66 kB 🟡 258.50 kB
/[locale]/settings/account 0 B 🟡 205.84 kB
/[locale]/settings 0 B 🟡 205.84 kB
/[locale]/settings/policy 0 B 🟡 205.84 kB
/[locale]/statistics 50.58 kB 🟡 256.41 kB
/[locale]/[...rest] 0 B 🟡 205.84 kB
/[locale]/login 114.23 kB 🟡 320.07 kB
/[locale]/onboarding 127.73 kB 🟡 333.57 kB
/[locale] 0 B 🟡 205.84 kB

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

Lighthouse — timo-web
URL Perf A11y LCP CLS TBT
/en/home 🔴 68 🟢 96 🔴 14.0s 🟢 0.000 🟡 326ms
/en/today 🟡 71 🟡 93 🔴 13.7s 🟢 0.000 🟡 232ms
/en/focus 🟡 72 🟡 91 🔴 13.6s 🟢 0.000 🟡 203ms
/en/statistics 🟡 73 🟢 95 🔴 13.7s 🟢 0.000 🟢 179ms

Perf ≥ 70 / A11y ≥ 85 목표
LCP 🟢 < 2.5s 🟡 < 4s 🔴 ≥ 4s  |  CLS 🟢 < 0.1 🟡 < 0.25 🔴 ≥ 0.25  |  TBT 🟢 < 200ms 🟡 < 600ms 🔴 ≥ 600ms

Image Optimization — timo-web

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

측정 커밋: 4e9ba53

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

🤖 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]/login/_containers/LoginContainer.tsx:
- Line 7: login이 onboarding 도메인의 OnboardingGoogleButtonContainer를 직접 import하지
않도록 공유 컴포넌트로 추출하세요. OnboardingGoogleButtonContainer와 OnboardingGoogleButton을 공용
위치로 이동하고, onboarding 전용 variant·번역 키 의존성을 제거해 호출 측에서 label prop을 주입하도록 리팩터링한 뒤
모든 import와 사용처를 갱신하세요.
- Around line 29-31: Replace the span rendering t("headline") in LoginContainer
with an h1 element, preserving the existing typo-headline-b-18 and
text-timo-black classes, and verify the page contains exactly one h1 without
introducing heading-level jumps.
- Line 18: Replace the hardcoded ariaLabel in LoginContainer with
t("animationLabel") from the Login translation namespace, and add the
animationLabel key with appropriate English and Korean values to the
corresponding Login translation objects.

In
`@apps/timo-web/app/`[locale]/onboarding/_containers/LifePatternStepContainer.tsx:
- Around line 28-31: Update the validation around isBedTimeInvalid and
canProceed to support sleep intervals that cross midnight, such as a 23:00
wake-up and 07:00 bedtime, instead of relying on direct HH:MM string ordering.
Convert the times to comparable numeric values and distinguish valid overnight
schedules from genuinely invalid same-day schedules while preserving rejection
of identical times.

In
`@apps/timo-web/app/`[locale]/onboarding/_containers/OnboardingFunnelContainer.tsx:
- Around line 33-37: Update the LottiePlayer usage in OnboardingFunnelContainer
to remove the hardcoded Korean ariaLabel and mark the decorative animation with
aria-hidden="true", ensuring it is excluded from screen-reader output in every
locale.
- Around line 84-119: Extract the repeated answers validation used by the
onboarding funnel’s onNext/onStart callbacks into a shared helper, such as a
function near the OnboardingFunnelContainer, and reuse it in all four callbacks
while preserving the current early-return behavior. Remove the console.log in
the CalendarConnect onStart handler so user sleep-pattern data is not emitted to
the browser console; keep the payload for the eventual submission API.

In `@apps/timo-web/messages/en.json`:
- Around line 101-107: Update the Korean lifePattern.title translation to match
the English title’s intent of instructing users to set their wake-up and
bedtime, while preserving the existing wakeUpTime and bedTime labels and the
natural Korean wording.
🪄 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: c2225616-c3b9-4335-a57e-7c783e5272cf

📥 Commits

Reviewing files that changed from the base of the PR and between 190a031 and d0142fb.

⛔ Files ignored due to path filters (4)
  • packages/timo-design-system/src/icons/source/alert.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/moon.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/sun.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • apps/timo-web/app/[locale]/login/_containers/LoginContainer.tsx
  • apps/timo-web/app/[locale]/login/page.tsx
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingButton.tsx
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingSelectCard.tsx
  • apps/timo-web/app/[locale]/onboarding/_components/OnboardingStepButton.tsx
  • apps/timo-web/app/[locale]/onboarding/_containers/CalendarConnectStepContainer.tsx
  • apps/timo-web/app/[locale]/onboarding/_containers/LanguageStepContainer.tsx
  • apps/timo-web/app/[locale]/onboarding/_containers/LifePatternStepContainer.tsx
  • apps/timo-web/app/[locale]/onboarding/_containers/OnboardingFunnelContainer.tsx
  • apps/timo-web/app/[locale]/onboarding/_containers/TimePredictionStepContainer.tsx
  • apps/timo-web/app/[locale]/onboarding/_types/onboarding-funnel.ts
  • apps/timo-web/app/[locale]/onboarding/page.tsx
  • apps/timo-web/messages/en.json
  • apps/timo-web/messages/ko.json
  • apps/timo-web/package.json

import Image from "next/image";
import { useTranslations } from "next-intl";

import { OnboardingGoogleButtonContainer } from "@/app/[locale]/onboarding/_containers/OnboardingGoogleButtonContainer";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

도메인 간 직접 import 위반 — 공유 컴포넌트로 추출 필요

login 도메인이 onboarding 도메인의 OnboardingGoogleButtonContainer를 직접 import하고 있습니다. 아키텍처 규칙에서 "도메인 간 직접 import 금지 — 공유 로직은 반드시 lib/ 또는 packages/로 추출"을 명시하고 있습니다. 컴포넌트 이름 자체에 "Onboarding"이 포함되어 있어 도메인 종속성이 명확히 드러납니다.

OnboardingGoogleButtonContainer와 하위 OnboardingGoogleButton@/components/google-button/ 같은 공유 위치로 추출하고, variant 기반 라벨 조회 대신 label prop을 주입받도록 리팩터링을 권장합니다. 번역 키(onboardingGoogleButton.*)도 공유 네임스페이스로 이동하거나 호출侧에서 전달하는 구조가 적합합니다.

As per path instructions, "도메인 간 직접 import 금지 — 공유 로직은 반드시 lib/ 또는 packages/로 추출" 규칙을 따라야 합니다.

🤖 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]/login/_containers/LoginContainer.tsx at line 7,
login이 onboarding 도메인의 OnboardingGoogleButtonContainer를 직접 import하지 않도록 공유 컴포넌트로
추출하세요. OnboardingGoogleButtonContainer와 OnboardingGoogleButton을 공용 위치로 이동하고,
onboarding 전용 variant·번역 키 의존성을 제거해 호출 측에서 label prop을 주입하도록 리팩터링한 뒤 모든 import와
사용처를 갱신하세요.

Source: Path instructions

<LottiePlayer
src="/lottie/onboarding.json"
className="hidden shrink-0 lg:block lg:size-[350px] xl:size-[430px] 2xl:size-[500px]"
ariaLabel="온보딩 애니메이션"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

aria-label이 한국어 하드코딩 + 맥락 부적절

ariaLabel="온보딩 애니메이션"이 두 가지 문제를 가지고 있습니다:

  1. 비국제화: 한국어로 고정되어 있어 영어 사용자 화면 리더기에서 한국어가 음성 출력됩니다.
  2. 맥락 오류: 로그인 페이지에서 "온보딩 애니메이션"이라는 라벨이 의미적으로 맞지 않습니다.

Login 번역 네임스페이스에 애니메이션 라벨 키를 추가하고 t("animationLabel") 형태로 사용하세요.

♿ 제안 수정
       <LottiePlayer
         src="/lottie/onboarding.json"
         className="hidden shrink-0 lg:block lg:size-[350px] xl:size-[430px] 2xl:size-[500px]"
-        ariaLabel="온보딩 애니메이션"
+        ariaLabel={t("animationLabel")}
       />

en.json / ko.json Login 객체에 추가:

"animationLabel": "Login animation"
"animationLabel": "로그인 애니메이션"
📝 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
ariaLabel="온보딩 애니메이션"
ariaLabel={t("animationLabel")}
🤖 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]/login/_containers/LoginContainer.tsx at line 18,
Replace the hardcoded ariaLabel in LoginContainer with t("animationLabel") from
the Login translation namespace, and add the animationLabel key with appropriate
English and Korean values to the corresponding Login translation objects.

Comment on lines +29 to +31
<span className="typo-headline-b-18 text-timo-black">
{t("headline")}
</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

페이지 <h1> 부재 — 접근성 위반

{t("headline")}<span>으로 렌더링되고 있으며, 페이지 전체에 <h1> 요소가 없습니다. 경로 규칙에서 "헤딩 레벨 점프 금지, 페이지당 <h1> 하나"를 요구하고 있습니다. 화면 리더기 사용자는 헤딩 기반 탐색이 불가능합니다.

typo-headline-b-18 스타일이 적용된 headline을 <h1>로 변경하세요. 타이포그래피 클래스는 그대로 유지할 수 있습니다.

♿ 제안 수정
             <div className="flex w-full flex-col items-center gap-0.5">
               <p className="typo-headline-m-16 text-timo-blue-300">
                 Less Chaos More Focus
               </p>
-              <span className="typo-headline-b-18 text-timo-black">
+              <h1 className="typo-headline-b-18 text-timo-black">
                 {t("headline")}
-              </span>
+              </h1>
             </div>

As per path instructions, "헤딩 레벨 점프 금지, 페이지당 <h1> 하나" 규칙을 따라야 합니다.

📝 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
<span className="typo-headline-b-18 text-timo-black">
{t("headline")}
</span>
<span className="typo-headline-m-16 text-timo-blue-300">
Less Chaos More Focus
</span>
<h1 className="typo-headline-b-18 text-timo-black">
{t("headline")}
</h1>
🤖 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]/login/_containers/LoginContainer.tsx around lines
29 - 31, Replace the span rendering t("headline") in LoginContainer with an h1
element, preserving the existing typo-headline-b-18 and text-timo-black classes,
and verify the page contains exactly one h1 without introducing heading-level
jumps.

Source: Path instructions

Comment on lines +28 to +31
const isBedTimeInvalid = Boolean(
wakeUpTime && bedTime && bedTime <= wakeUpTime,
);
const canProceed = Boolean(wakeUpTime && bedTime && !isBedTimeInvalid);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

자정을 넘나드는 수면 패턴 검증 누락

bedTime <= wakeUpTime 문자열 비교는 "HH:MM" 형식에서 같은 날 내 패턴만 처리합니다. 예를 들어 23:00 기상·07:00 취침 같은 교대근무자 패턴은 bedTime("07:00") <= wakeUpTime("23:00")true가 되어 유효한 입력이 차단됩니다. 대상 사용자층이 일반적인 주간 생활 패턴으로 한정되어 있다면 의도된 동작일 수 있으나, 그렇지 않다면 자정을 넘는 case를 고려해야 합니다.

🤖 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/_containers/LifePatternStepContainer.tsx
around lines 28 - 31, Update the validation around isBedTimeInvalid and
canProceed to support sleep intervals that cross midnight, such as a 23:00
wake-up and 07:00 bedtime, instead of relying on direct HH:MM string ordering.
Convert the times to comparable numeric values and distinguish valid overnight
schedules from genuinely invalid same-day schedules while preserving rejection
of identical times.

Comment on lines +33 to +37
<LottiePlayer
src="/lottie/onboarding.json"
className="hidden shrink-0 lg:block lg:size-[350px] xl:size-[430px] 2xl:size-[500px]"
ariaLabel="온보딩 애니메이션"
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

LottiePlayer ariaLabel 하드코딩 및 접근성 개선 필요

ariaLabel="온보딩 애니메이션"이 한국어로 하드코딩되어 있어 영어 locale에서도 한국어가 음성 출력됩니다. 또한 이 애니메이션은 장식용이므로 스크린 리더가 읽어주지 않도록 aria-hidden="true" 처리가 더 적절할 수 있습니다.

🤖 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/_containers/OnboardingFunnelContainer.tsx
around lines 33 - 37, Update the LottiePlayer usage in OnboardingFunnelContainer
to remove the hardcoded Korean ariaLabel and mark the decorative animation with
aria-hidden="true", ensuring it is excluded from screen-reader output in every
locale.

Comment on lines +84 to +119
onNext={() => {
if (
!answers.language ||
!answers.predictionAccuracy ||
!answers.wakeUpTime ||
!answers.bedTime
)
return;
history.push("CalendarConnect", {
language: answers.language,
predictionAccuracy: answers.predictionAccuracy,
wakeUpTime: answers.wakeUpTime,
bedTime: answers.bedTime,
});
}}
/>
)}
CalendarConnect={({ history }) => (
<CalendarConnectStepContainer
onPrev={() => history.back()}
onStart={() => {
if (
!answers.language ||
!answers.predictionAccuracy ||
!answers.wakeUpTime ||
!answers.bedTime
)
return;
const payload: OnboardingFunnelSteps["CalendarConnect"] = {
language: answers.language,
predictionAccuracy: answers.predictionAccuracy,
wakeUpTime: answers.wakeUpTime,
bedTime: answers.bedTime,
};
// TODO: 실제 제출 API 연동 + 완료 후 payload.language로 locale 리다이렉트
console.log("[onboarding] submit payload", payload);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

중복된 검증 로직 및 console.log 사용자 데이터 출력

두 가지 개선 사항이 있습니다:

  1. 검증 로직 중복: onNext/onStart 콜백마다 모든 answers 필드를 확인하는 동일한 패턴이 4회 반복됩니다. 헬퍼 함수로 추출하면 유지보수성이 향상됩니다.

  2. console.log 사용자 데이터 출력: 119번째 줄의 console.log는 사용자 생활 패턴 데이터(wakeUpTime, bedTime 등)를 출력합니다. TODO로 표시되어 있으나, 프로덕션 빌드에 남아있으면 브라우저 콘솔에서 사용자 데이터가 노출될 수 있습니다.

♻️ 제안: 검증 헬퍼 추출 및 console.log 정리
+ const hasAllAnswers = (a: Partial<OnboardingFunnelSteps["CalendarConnect"]>) =>
+   Boolean(a.language && a.predictionAccuracy && a.wakeUpTime && a.bedTime);
+
 export const OnboardingFunnelContainer = () => {
   // ...
-                onNext={() => {
-                  if (
-                    !answers.language ||
-                    !answers.predictionAccuracy ||
-                    !answers.wakeUpTime ||
-                    !answers.bedTime
-                  )
-                    return;
-                  history.push("CalendarConnect", {
+                onNext={() => {
+                  if (!hasAllAnswers(answers)) return;
+                  history.push("CalendarConnect", {
                     language: answers.language,
                     predictionAccuracy: answers.predictionAccuracy,
                     wakeUpTime: answers.wakeUpTime,
                     bedTime: answers.bedTime,
                   });
                 }}
-                  // TODO: 실제 제출 API 연동 + 완료 후 payload.language로 locale 리다이렉트
-                  console.log("[onboarding] submit payload", payload);
+                  // TODO: 실제 제출 API 연동 + 완료 후 payload.language로 locale 리다이렉트
🤖 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/_containers/OnboardingFunnelContainer.tsx
around lines 84 - 119, Extract the repeated answers validation used by the
onboarding funnel’s onNext/onStart callbacks into a shared helper, such as a
function near the OnboardingFunnelContainer, and reuse it in all four callbacks
while preserving the current early-return behavior. Remove the console.log in
the CalendarConnect onStart handler so user sleep-pattern data is not emitted to
the browser console; keep the payload for the eventual submission API.

Comment on lines +101 to +107
"lifePattern": {
"title": "Set your wake-up and bedtime.",
"description": "The information you provide will only be used for time pattern analysis.",
"wakeUpTime": "Wake-up time",
"bedTime": "Bedtime",
"bedTimeError": "Please set your bedtime later than your wake-up time."
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

lifePattern.title ko.json과 의미 불일치

영문은 "Set your wake-up and bedtime."(기상·취침 시간 설정 지시)인 반면, ko.json Line 99는 "평소 생활 패턴을 알려주세요."(생활 패턴 안내 요청)로 의미가 다릅니다. 필드 라벨(wakeUpTime, bedTime)이 시간 설정을 명시하고 있어 영문 제목이 의도에 더 부합해 보입니다.

ko.json 제목을 영문 의도와 맞추는 것을 권장합니다:

🌐 제안 수정 (ko.json Line 99)
-    "title": "평소 생활 패턴을 알려주세요.",
+    "title": "기상 시간과 취침 시간을 설정해 주세요.",
🤖 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/messages/en.json` around lines 101 - 107, Update the Korean
lifePattern.title translation to match the English title’s intent of instructing
users to set their wake-up and bedtime, while preserving the existing wakeUpTime
and bedTime labels and the natural Korean wording.

@github-actions

Copy link
Copy Markdown

Storybook Preview

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

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

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] 온보딩 퍼널 구현

1 participant