diff --git a/frontend/src/pages/Home/ui/HomePage.tsx b/frontend/src/pages/Home/ui/HomePage.tsx
index d5dcf027..365a73c0 100644
--- a/frontend/src/pages/Home/ui/HomePage.tsx
+++ b/frontend/src/pages/Home/ui/HomePage.tsx
@@ -1,16 +1,21 @@
-// PR 2 (feature/home-layout) 단계의 HomePage.tsx
import { SiteNav } from '@/widgets/site-nav'
+import { HomeHero } from '@/widgets/home-hero'
+import { HomeServices } from '@/widgets/home-services'
+import { HomeQuote } from '@/widgets/home-quote'
+import { HomeFaq } from '@/widgets/home-faq'
+import { HomeCta } from '@/widgets/home-cta'
import { SiteFooter } from '@/widgets/site-footer'
-// 아직 PR 3에서 만들 예정이므로 주석 처리!
-// import { HomeHero } from '@/widgets/home-hero'
export default function HomePage() {
return (
- {/* 글로벌 레이아웃은 추후 분리 예정*/}
- {/* TODO: 다음 PR에서 위젯들 추가 예정 */}
+
+
+
+
+
diff --git a/frontend/src/shared/hooks/index.ts b/frontend/src/shared/hooks/index.ts
index e69de29b..f0e17fce 100644
--- a/frontend/src/shared/hooks/index.ts
+++ b/frontend/src/shared/hooks/index.ts
@@ -0,0 +1,2 @@
+export { useTypewriter } from './useTypewriter'
+export type { UseTypewriterOptions } from './useTypewriter'
diff --git a/frontend/src/shared/hooks/useTypewriter.ts b/frontend/src/shared/hooks/useTypewriter.ts
new file mode 100644
index 00000000..d7f36398
--- /dev/null
+++ b/frontend/src/shared/hooks/useTypewriter.ts
@@ -0,0 +1,61 @@
+import { useEffect, useState } from 'react'
+
+export type UseTypewriterOptions = {
+ startDelayMs?: number
+ stepMs?: number
+ respectReducedMotion?: boolean
+}
+
+export function useTypewriter(
+ text: string,
+ options: UseTypewriterOptions = {},
+) {
+ const {
+ startDelayMs = 0,
+ stepMs = 100,
+ respectReducedMotion = true,
+ } = options
+
+ const [typed, setTyped] = useState('')
+
+ useEffect(() => {
+ const reduce =
+ respectReducedMotion &&
+ typeof window !== 'undefined' &&
+ window.matchMedia?.('(prefers-reduced-motion: reduce)').matches
+
+ if (reduce) {
+ setTyped(text)
+ return
+ }
+
+ setTyped('')
+ let rafId = 0
+ let startTime: number | null = null
+
+ const tick = (now: number) => {
+ if (startTime === null) startTime = now
+ const elapsed = now - startTime - startDelayMs
+ if (elapsed < 0) {
+ rafId = window.requestAnimationFrame(tick)
+ return
+ }
+ const next = Math.min(
+ Math.floor(elapsed / stepMs) + 1,
+ text.length,
+ )
+ setTyped(text.slice(0, next))
+ if (next < text.length) {
+ rafId = window.requestAnimationFrame(tick)
+ }
+ }
+
+ rafId = window.requestAnimationFrame(tick)
+
+ return () => {
+ window.cancelAnimationFrame(rafId)
+ }
+ }, [text, startDelayMs, stepMs, respectReducedMotion])
+
+ return { typed, done: typed.length >= text.length }
+}
diff --git a/frontend/src/widgets/home-cta/index.ts b/frontend/src/widgets/home-cta/index.ts
new file mode 100644
index 00000000..408926af
--- /dev/null
+++ b/frontend/src/widgets/home-cta/index.ts
@@ -0,0 +1 @@
+export { HomeCta } from './ui/HomeCta'
diff --git a/frontend/src/widgets/home-cta/ui/HomeCta.tsx b/frontend/src/widgets/home-cta/ui/HomeCta.tsx
new file mode 100644
index 00000000..953fca4f
--- /dev/null
+++ b/frontend/src/widgets/home-cta/ui/HomeCta.tsx
@@ -0,0 +1,51 @@
+export function HomeCta() {
+ return (
+
+
+
+

+
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/widgets/home-faq/index.ts b/frontend/src/widgets/home-faq/index.ts
new file mode 100644
index 00000000..f9e10fb2
--- /dev/null
+++ b/frontend/src/widgets/home-faq/index.ts
@@ -0,0 +1 @@
+export { HomeFaq } from './ui/HomeFaq'
diff --git a/frontend/src/widgets/home-faq/ui/HomeFaq.tsx b/frontend/src/widgets/home-faq/ui/HomeFaq.tsx
new file mode 100644
index 00000000..ab517cff
--- /dev/null
+++ b/frontend/src/widgets/home-faq/ui/HomeFaq.tsx
@@ -0,0 +1,86 @@
+const faqs = [
+ {
+ q: 'Stack Up은 무료 크레딧을 제공하나요?',
+ a: '월 2회의 무료 면접 세션을 제공합니다. 학교 종합설계 프로젝트 기준이라 결제·구독 기능은 포함되지 않아요.',
+ },
+ {
+ q: '어떤 형식의 이력서를 지원하나요?',
+ a: 'Phase 1 기준 PDF만 지원합니다. HWP·DOCX는 PDF로 변환 후 업로드해 주세요.',
+ },
+ {
+ q: 'GitHub 외 다른 로그인은 가능한가요?',
+ a: '레포 분석이 핵심 기능이라, Phase 1에서는 GitHub OAuth만 제공합니다.',
+ },
+ {
+ q: '꼬리질문은 얼마나 빨리 받을 수 있나요?',
+ a: '평균 3초 이내 응답을 목표로 합니다. Flash 모델과 사전 구축 RAG 인덱스로 지연을 최소화해요.',
+ },
+ {
+ q: '음성·비언어 분석은 어떻게 동작하나요?',
+ a: 'WebRTC로 마이크·웹캠 스트림을 받아 말 속도(WPM)·간투어·시선·자세를 분석합니다. 권한을 거부하면 텍스트 입력으로 진행할 수 있어요.',
+ },
+]
+
+export function HomeFaq() {
+ return (
+
+
+
+ FAQ
+
+
+
+ Questions, answered.
+
+
+
+
+
+
+
+
+ {faqs.map((f) => (
+ -
+
+
+
+ {f.q}
+
+
+ +
+
+
+
+ {f.a}
+
+
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/frontend/src/widgets/home-hero/index.ts b/frontend/src/widgets/home-hero/index.ts
new file mode 100644
index 00000000..ec86fcb7
--- /dev/null
+++ b/frontend/src/widgets/home-hero/index.ts
@@ -0,0 +1 @@
+export { HomeHero } from './ui/HomeHero'
diff --git a/frontend/src/widgets/home-hero/ui/HomeHero.tsx b/frontend/src/widgets/home-hero/ui/HomeHero.tsx
new file mode 100644
index 00000000..40e4ecb1
--- /dev/null
+++ b/frontend/src/widgets/home-hero/ui/HomeHero.tsx
@@ -0,0 +1,38 @@
+import { Laptop } from './Laptop'
+import { ScreenContent } from './ScreenContent'
+
+export function HomeHero() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ Phase 1 · MVP · 2026
+
+
+
+
+ )
+}
diff --git a/frontend/src/widgets/home-hero/ui/Laptop.tsx b/frontend/src/widgets/home-hero/ui/Laptop.tsx
new file mode 100644
index 00000000..a0c505a0
--- /dev/null
+++ b/frontend/src/widgets/home-hero/ui/Laptop.tsx
@@ -0,0 +1,59 @@
+import type { ReactNode } from 'react'
+
+export function Laptop({ children }: { children: ReactNode }) {
+ return (
+
+ {/* Lid */}
+
+ {/* Bezel */}
+
+ {/* Camera dot */}
+
+
+ {/* Screen */}
+
+ {children}
+
+
+
+
+ {/* Base / hinge */}
+
+
+ )
+}
diff --git a/frontend/src/widgets/home-hero/ui/ScreenContent.tsx b/frontend/src/widgets/home-hero/ui/ScreenContent.tsx
new file mode 100644
index 00000000..0d50d1fb
--- /dev/null
+++ b/frontend/src/widgets/home-hero/ui/ScreenContent.tsx
@@ -0,0 +1,60 @@
+import { useTypewriter } from '@/shared/hooks'
+
+const TYPED_TEXT = 'Stack Up'
+
+export function ScreenContent() {
+ const { typed, done } = useTypewriter(TYPED_TEXT, {
+ startDelayMs: 850,
+ stepMs: 130,
+ })
+
+ return (
+
+
+ IT Interview Solution
+
+
+
+ {typed}
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/widgets/home-quote/index.ts b/frontend/src/widgets/home-quote/index.ts
new file mode 100644
index 00000000..e685ef69
--- /dev/null
+++ b/frontend/src/widgets/home-quote/index.ts
@@ -0,0 +1 @@
+export { HomeQuote } from './ui/HomeQuote'
diff --git a/frontend/src/widgets/home-quote/ui/HomeQuote.tsx b/frontend/src/widgets/home-quote/ui/HomeQuote.tsx
new file mode 100644
index 00000000..e27dba30
--- /dev/null
+++ b/frontend/src/widgets/home-quote/ui/HomeQuote.tsx
@@ -0,0 +1,88 @@
+export function HomeQuote() {
+ return (
+
+
+
+
+ {/* decorative left panel */}
+
+
+ {/* quote panel */}
+
+
+ “
+
+
+
+
+ Stack Up은 올인원 IT 면접 솔루션입니다. 본인 코드를 가장 잘 아는
+ 면접관 — GitHub 레포에서 출발해, 답변의 깊이까지 따라 들어갑니다.
+
+
+
+
+
+
+ 박상우 · 신재호 · 정준모 · 조서현
+
+
+ Team StackUp · CNU
+
+
+
+
+
+ 2026 · Phase 1
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/widgets/home-services/index.ts b/frontend/src/widgets/home-services/index.ts
new file mode 100644
index 00000000..b58cf390
--- /dev/null
+++ b/frontend/src/widgets/home-services/index.ts
@@ -0,0 +1 @@
+export { HomeServices } from './ui/HomeServices'
diff --git a/frontend/src/widgets/home-services/ui/HomeServices.tsx b/frontend/src/widgets/home-services/ui/HomeServices.tsx
new file mode 100644
index 00000000..e19a1334
--- /dev/null
+++ b/frontend/src/widgets/home-services/ui/HomeServices.tsx
@@ -0,0 +1,80 @@
+type ServiceCard = {
+ tag: string
+ title: string
+ image: string
+}
+
+const services: ServiceCard[] = [
+ {
+ tag: 'Service',
+ title: 'Frontend Interview',
+ image: '/second-section-frontend-interview.png',
+ },
+ {
+ tag: 'Service',
+ title: 'Backend Interview',
+ image: '/second-section-backend-interview.avif',
+ },
+ {
+ tag: 'Service',
+ title: 'CS / Full Stack',
+ image: '/second-section-cs-interview.avif',
+ },
+]
+
+export function HomeServices() {
+ return (
+
+ )
+}