[FEAT] 온보딩 퍼널 및 로그인 페이지 구현#139
Conversation
- 온보딩 퍼널 구현을 위해 @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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Walkthrough로그인 화면과 로케일별 페이지를 추가하고, 언어·예측 정확도·생활 패턴·캘린더 연결로 구성된 4단계 온보딩 퍼널을 구현했습니다. 단계별 상태와 입력 검증, 다국어 문구, 퍼널 이동 및 payload 생성이 포함됩니다. Changes로그인 화면
온보딩 퍼널
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 표시
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Timo Performance ReportBundle Size — timo-web
Lighthouse — timo-web
Image Optimization — timo-web
측정 커밋: |
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (4)
packages/timo-design-system/src/icons/source/alert.svgis excluded by!**/*.svgpackages/timo-design-system/src/icons/source/moon.svgis excluded by!**/*.svgpackages/timo-design-system/src/icons/source/sun.svgis excluded by!**/*.svgpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (15)
apps/timo-web/app/[locale]/login/_containers/LoginContainer.tsxapps/timo-web/app/[locale]/login/page.tsxapps/timo-web/app/[locale]/onboarding/_components/OnboardingButton.tsxapps/timo-web/app/[locale]/onboarding/_components/OnboardingSelectCard.tsxapps/timo-web/app/[locale]/onboarding/_components/OnboardingStepButton.tsxapps/timo-web/app/[locale]/onboarding/_containers/CalendarConnectStepContainer.tsxapps/timo-web/app/[locale]/onboarding/_containers/LanguageStepContainer.tsxapps/timo-web/app/[locale]/onboarding/_containers/LifePatternStepContainer.tsxapps/timo-web/app/[locale]/onboarding/_containers/OnboardingFunnelContainer.tsxapps/timo-web/app/[locale]/onboarding/_containers/TimePredictionStepContainer.tsxapps/timo-web/app/[locale]/onboarding/_types/onboarding-funnel.tsapps/timo-web/app/[locale]/onboarding/page.tsxapps/timo-web/messages/en.jsonapps/timo-web/messages/ko.jsonapps/timo-web/package.json
| import Image from "next/image"; | ||
| import { useTranslations } from "next-intl"; | ||
|
|
||
| import { OnboardingGoogleButtonContainer } from "@/app/[locale]/onboarding/_containers/OnboardingGoogleButtonContainer"; |
There was a problem hiding this comment.
📐 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="온보딩 애니메이션" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
aria-label이 한국어 하드코딩 + 맥락 부적절
ariaLabel="온보딩 애니메이션"이 두 가지 문제를 가지고 있습니다:
- 비국제화: 한국어로 고정되어 있어 영어 사용자 화면 리더기에서 한국어가 음성 출력됩니다.
- 맥락 오류: 로그인 페이지에서 "온보딩 애니메이션"이라는 라벨이 의미적으로 맞지 않습니다.
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.
| 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.
| <span className="typo-headline-b-18 text-timo-black"> | ||
| {t("headline")} | ||
| </span> |
There was a problem hiding this comment.
🎯 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.
| <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
| const isBedTimeInvalid = Boolean( | ||
| wakeUpTime && bedTime && bedTime <= wakeUpTime, | ||
| ); | ||
| const canProceed = Boolean(wakeUpTime && bedTime && !isBedTimeInvalid); |
There was a problem hiding this comment.
🎯 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.
| <LottiePlayer | ||
| src="/lottie/onboarding.json" | ||
| className="hidden shrink-0 lg:block lg:size-[350px] xl:size-[430px] 2xl:size-[500px]" | ||
| ariaLabel="온보딩 애니메이션" | ||
| /> |
There was a problem hiding this comment.
🔒 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.
| 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); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
중복된 검증 로직 및 console.log 사용자 데이터 출력
두 가지 개선 사항이 있습니다:
-
검증 로직 중복:
onNext/onStart콜백마다 모든answers필드를 확인하는 동일한 패턴이 4회 반복됩니다. 헬퍼 함수로 추출하면 유지보수성이 향상됩니다. -
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.
| "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." | ||
| }, |
There was a problem hiding this comment.
🎯 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.
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")에 저장되므로 브라우저 뒤로가기/앞으로가기가 자동으로 처리됩니다.스텝 타입 선언
각 스텝이 이전 스텝의 필드를 이어받는 구조로, 스텝이 넘어갈수록 필드의 필수/옵션 여부가 좁혀집니다.
interface가 아닌type으로 선언한 이유는 use-funnel 내부의CompareMergeContext가keyof로 필드를 비교하는데,interface는 선언 병합 가능성 때문에keyof추론이 깨져 타입 오류가 발생했기 때문입니다(파일 내 주석 및eslint-disable참고).사용 입력값(
answers)을 별도useState로 관리한 이유퍼널 context만으로 입력값을 관리하면 "뒤로 갔다가 새로 push"할 때 이미 진행한 forward 히스토리(예: 선택했던
predictionAccuracy)가 통째로 날아가는 문제가 실제로 발생했습니다. 리마운트되지 않는OnboardingFunnelContainer레벨useState로 옮겨 이 문제를 우회했습니다.렌더링 구조
현재 활성 스텝만 렌더링되며,
history.push(다음스텝, context)/history.back()으로 전환합니다.2. 온보딩 스텝 화면
#108, #118에서 만들어둔 컴포넌트를 그대로 재사용했습니다. 새로운 디자인 시스템 컴포넌트는 없습니다.
LanguageStepContainerTimePredictionStepContainerLifePatternStepContainerbedTime <= wakeUpTime이면 경고 + 다음 버튼 비활성화CalendarConnectStepContainer3. 로그인 페이지
로그인은 OAuth 리다이렉트(외부 도메인 이동)라 퍼널 스텝에 포함할 수 없어
/[locale]/login라우트를 별도로 추가했습니다.OnboardingGoogleButtonContainer(variant="login")를 재사용했습니다.4. Lottie 애니메이션 연동
PR #131 병합 후 로그인·온보딩 화면 좌측에
/lottie/onboarding.json을LottiePlayer로 교체했습니다. 반응형으로 구현했습니다.< lglg (1024px)xl (1280px)2xl (1536px)5. 디자인 시스템 아이콘 추가
생활패턴 스텝에 필요한 아이콘 소스 SVG를 추가했습니다.
sun.svgmoon.svgalert.svgpnpm icons:generate로 자동 생성되며, 생성 결과물은.gitignore대상이라 소스 SVG만 포함됩니다.Screenshot 📷
To Reviewers
OnboardingFunnelSteps가type이어야 하는 이유가 use-funnel 라이브러리 자체 제약인지, 사용 방식 문제인지 확인 부탁드립니다.OnboardingFunnelContaineruseState로 관리한 구조 선택에 대한 의견 부탁드립니다.Test Checklist ✔
pnpm check-types통과pnpm lint통과 (--max-warnings 0)pnpm build— 미실행: CI에서 확인 예정