Responsive Design
반응형 디자인 구현 패턴입니다.
개요
Responsive Design 패턴은 다양한 화면 크기와 디바이스에서 최적의 사용자 경험을 제공하는 검증된 구현 방법입니다. 모바일 우선 접근, 브레이크포인트 전략, 유동적 레이아웃, 터치 최적화 등 프로덕션 환경에서 필요한 모든 반응형 시나리오를 다룹니다.
사용 사례:
- 모바일/태블릿/데스크톱 레이아웃
- 반응형 그리드 시스템
- 유동적 타이포그래피
- 터치 친화적 UI
- 적응형 이미지
사용하지 말아야 할 때:
- 데스크톱 전용 애플리케이션
- 고정 크기 대시보드 (예: TV 디스플레이)
- 프린트 전용 문서
기본 패턴
1. 모바일 우선 레이아웃
모바일부터 시작하여 점진적으로 확장하는 기본 패턴입니다.
"use client"
import { Card, Button } from "@vortex/ui-foundation"
export default function MobileFirstLayout() {
return (
<div className="min-h-screen bg-gray-50">
{/* Header - 모든 화면 크기에서 고정 */}
<header className="bg-white shadow-sm sticky top-0 z-10">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
<h1 className="text-xl sm:text-2xl font-bold">Logo</h1>
<nav className="hidden md:flex space-x-4">
<a href="#" className="text-gray-700 hover:text-gray-900">
링크 1
</a>
<a href="#" className="text-gray-700 hover:text-gray-900">
링크 2
</a>
</nav>
</div>
</div>
</header>
{/* Main Content - 모바일: 1열, 태블릿: 2열, 데스크톱: 3열 */}
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{[1, 2, 3, 4, 5, 6].map((item) => (
<Card key={item} className="p-4 sm:p-6">
<h3 className="text-lg sm:text-xl font-bold mb-2">Card {item}</h3>
<p className="text-sm sm:text-base text-gray-600 mb-4">
반응형 카드 콘텐츠입니다.
</p>
<Button className="w-full sm:w-auto">자세히 보기</Button>
</Card>
))}
</div>
</main>
</div>
)
}2. 브레이크포인트 시스템
Tailwind CSS 브레이크포인트를 활용한 반응형 패턴입니다.
// tailwind.config.js
module.exports = {
theme: {
screens: {
sm: "640px", // Mobile landscape, small tablets
md: "768px", // Tablets
lg: "1024px", // Small laptops
xl: "1280px", // Desktops
"2xl": "1536px", // Large desktops
},
},
}"use client"
export default function ResponsiveBreakpoints() {
return (
<div className="p-4 sm:p-6 md:p-8 lg:p-12">
{/* 텍스트 크기 */}
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold mb-4">
반응형 제목
</h1>
{/* 간격 */}
<div className="space-y-2 sm:space-y-4 md:space-y-6">
<p className="text-sm sm:text-base md:text-lg">
화면 크기에 따라 텍스트와 간격이 조정됩니다.
</p>
</div>
{/* 레이아웃 전환 */}
<div className="flex flex-col md:flex-row gap-4 mt-8">
<div className="flex-1 bg-blue-100 p-4 rounded">
모바일: 세로 배치
<br />
데스크톱: 가로 배치
</div>
<div className="flex-1 bg-green-100 p-4 rounded">유동적 레이아웃</div>
</div>
{/* 표시/숨김 */}
<div className="mt-8">
<div className="block md:hidden bg-yellow-100 p-4 rounded">
모바일에만 표시
</div>
<div className="hidden md:block bg-purple-100 p-4 rounded">
데스크톱에만 표시
</div>
</div>
</div>
)
}고급 패턴
3. Container Query 활용
컨테이너 크기 기반 반응형 패턴입니다.
"use client"
import { Card } from "@vortex/ui-foundation"
export default function ContainerQueryPattern() {
return (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* 각 카드가 자신의 컨테이너 크기에 따라 반응 */}
<div className="@container">
<Card className="p-4">
<div className="@sm:flex @sm:items-center @sm:gap-4">
<img
src="/image.jpg"
alt="썸네일"
className="w-full @sm:w-24 h-24 object-cover rounded mb-4 @sm:mb-0"
/>
<div className="flex-1">
<h3 className="text-lg @sm:text-xl font-bold">카드 제목</h3>
<p className="text-sm @sm:text-base text-gray-600">
Container Query로 카드 내부 레이아웃 조정
</p>
</div>
</div>
</Card>
</div>
<div className="@container">
<Card className="p-4">
<div className="@sm:flex @sm:items-center @sm:gap-4">
<img
src="/image2.jpg"
alt="썸네일"
className="w-full @sm:w-24 h-24 object-cover rounded mb-4 @sm:mb-0"
/>
<div className="flex-1">
<h3 className="text-lg @sm:text-xl font-bold">카드 제목 2</h3>
<p className="text-sm @sm:text-base text-gray-600">
동일한 패턴, 독립적 반응
</p>
</div>
</div>
</Card>
</div>
</div>
)
}4. 유동적 타이포그래피
Clamp를 활용한 유동적 폰트 크기 패턴입니다.
// tailwind.config.js
module.exports = {
theme: {
extend: {
fontSize: {
"fluid-sm": "clamp(0.875rem, 0.8rem + 0.4vw, 1rem)", // 14-16px
"fluid-base": "clamp(1rem, 0.9rem + 0.5vw, 1.125rem)", // 16-18px
"fluid-lg": "clamp(1.125rem, 1rem + 0.625vw, 1.5rem)", // 18-24px
"fluid-xl": "clamp(1.25rem, 1rem + 1.25vw, 2rem)", // 20-32px
"fluid-2xl": "clamp(1.5rem, 1rem + 2.5vw, 3rem)", // 24-48px
},
},
},
}"use client"
export default function FluidTypography() {
return (
<div className="p-8">
<h1 className="text-fluid-2xl font-bold mb-4">유동적 제목</h1>
<h2 className="text-fluid-xl font-semibold mb-3">유동적 부제목</h2>
<p className="text-fluid-base text-gray-700 leading-relaxed">
이 텍스트는 화면 크기에 따라 부드럽게 크기가 조정됩니다. 브레이크포인트
없이 자연스러운 확대/축소가 이루어집니다.
</p>
<p className="text-fluid-sm text-gray-500 mt-4">
작은 텍스트도 동일한 패턴 적용
</p>
</div>
)
}5. 터치 최적화 UI
모바일 터치에 최적화된 인터랙션 패턴입니다.
"use client"
import { useState } from "react"
import { Button } from "@vortex/ui-foundation"
export default function TouchOptimizedUI() {
const [activeTab, setActiveTab] = useState(0)
const [startX, setStartX] = useState(0)
const handleTouchStart = (e: React.TouchEvent) => {
setStartX(e.touches[0].clientX)
}
const handleTouchEnd = (e: React.TouchEvent) => {
const endX = e.changedTouches[0].clientX
const diff = startX - endX
// 스와이프 감지 (최소 50px 이동)
if (Math.abs(diff) > 50) {
if (diff > 0 && activeTab < 2) {
// 왼쪽 스와이프 (다음 탭)
setActiveTab(activeTab + 1)
} else if (diff < 0 && activeTab > 0) {
// 오른쪽 스와이프 (이전 탭)
setActiveTab(activeTab - 1)
}
}
}
return (
<div className="max-w-md mx-auto">
{/* 터치 친화적 버튼 (최소 44x44px) */}
<div className="grid grid-cols-3 gap-2 mb-6">
<Button className="h-12 text-lg">버튼 1</Button>
<Button className="h-12 text-lg">버튼 2</Button>
<Button className="h-12 text-lg">버튼 3</Button>
</div>
{/* 스와이프 가능한 탭 */}
<div
className="overflow-hidden"
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
>
<div
className="flex transition-transform duration-300"
style={{ transform: `translateX(-${activeTab * 100}%)` }}
>
{[0, 1, 2].map((index) => (
<div
key={index}
className="w-full shrink-0 p-8 bg-gray-100 rounded-lg"
>
<h3 className="text-2xl font-bold mb-4">탭 {index + 1}</h3>
<p className="text-gray-600">좌우로 스와이프하세요</p>
</div>
))}
</div>
</div>
{/* 탭 인디케이터 */}
<div className="flex justify-center gap-2 mt-4">
{[0, 1, 2].map((index) => (
<button
key={index}
onClick={() => setActiveTab(index)}
className={`w-2 h-2 rounded-full transition-colors ${
activeTab === index ? "bg-blue-600" : "bg-gray-300"
}`}
aria-label={`탭 ${index + 1}로 이동`}
/>
))}
</div>
</div>
)
}6. 반응형 이미지
다양한 화면에 최적화된 이미지 로딩 패턴입니다.
"use client"
import Image from "next/image"
export default function ResponsiveImages() {
return (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{/* Next.js Image - 자동 최적화 */}
<div className="relative aspect-video">
<Image
src="/hero.jpg"
alt="히어로 이미지"
fill
className="object-cover rounded-lg"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
/>
</div>
{/* Picture 태그 - 아트 디렉션 */}
<picture>
<source media="(min-width: 1024px)" srcSet="/image-desktop.jpg" />
<source media="(min-width: 640px)" srcSet="/image-tablet.jpg" />
<img
src="/image-mobile.jpg"
alt="반응형 이미지"
className="w-full h-auto rounded-lg"
/>
</picture>
{/* SVG - 벡터 이미지 */}
<div className="bg-linear-to-br from-blue-500 to-purple-600 aspect-video rounded-lg flex items-center justify-center">
<svg
className="w-1/2 h-1/2 text-white"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z" />
</svg>
</div>
</div>
)
}Best Practices
✅ 권장 사항
-
모바일 우선 (Mobile First)
- 작은 화면부터 디자인
sm:,md:,lg:순서로 확장- 불필요한 요소 제거 (Progressive Enhancement)
-
터치 타겟 크기
- 최소 44x44px (Apple HIG)
- 버튼 간 충분한 간격 (8px 이상)
- 스와이프 제스처 지원
-
성능 최적화
- 이미지 lazy loading
- 적절한 이미지 크기 (
sizes속성) - Critical CSS 인라인
- 불필요한 JS 제거
-
접근성
- 텍스트 대비 4.5:1 이상
- 확대 가능한 텍스트
- 키보드 네비게이션
- 스크린 리더 지원
-
테스트
- 실제 디바이스 테스트
- Chrome DevTools 디바이스 모드
- 가로/세로 모드 모두 확인
- 느린 네트워크 시뮬레이션
⚠️ 피해야 할 것
-
UX 문제
- 데스크톱 UI를 모바일에 그대로 축소
- 작은 터치 타겟 (44px 미만)
- 가로 스크롤 발생
- 뷰포트 너비 고정 (
width=device-width필수)
-
성능 문제
- 모바일에 큰 이미지 로드
- 불필요한 애니메이션
- 과도한 JS 번들
- 서버 사이드 렌더링 없음
-
접근성 문제
- 확대 불가능 (
user-scalable=no) - 낮은 색상 대비
- 키보드 네비게이션 불가
- 확대 불가능 (
성능 고려사항
Critical CSS
<!-- 초기 렌더링에 필요한 CSS만 인라인 -->
<style>
.hero {
min-height: 100vh;
}
.nav {
position: sticky;
top: 0;
}
</style>
<!-- 나머지 CSS는 비동기 로드 -->
<link
rel="stylesheet"
href="/styles.css"
media="print"
onload="this.media='all'"
/>Image Optimization
// Next.js Image 최적화
<Image
src="/hero.jpg"
alt="Hero"
width={1920}
height={1080}
priority // LCP 이미지는 우선 로드
quality={85} // 품질 조정 (75-90 권장)
/>Foundation 예제
범용 반응형 레이아웃
Foundation 컴포넌트로 구현한 중립적인 반응형 레이아웃입니다.
import { Card, Button } from "@vortex/ui-foundation"
export default function FoundationResponsive() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 p-4">
{[1, 2, 3].map((i) => (
<Card key={i} className="p-6">
<h3 className="text-lg md:text-xl font-bold mb-2">Card {i}</h3>
<p className="text-sm md:text-base mb-4">반응형 카드</p>
<Button className="w-full md:w-auto">더보기</Button>
</Card>
))}
</div>
)
}iCignal 예제
Analytics 반응형 대시보드
iCignal Blue 브랜드를 적용한 반응형 대시보드입니다.
import "@vortex/ui-icignal/theme"
import { Card } from "@vortex/ui-icignal"
export default function ISignalDashboard() {
return (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 p-4">
<Card className="p-4 border-blue-500">
<h3 className="text-sm text-gray-600">방문자</h3>
<p className="text-2xl sm:text-3xl font-bold text-blue-600">1,234</p>
</Card>
<Card className="p-4 border-green-500">
<h3 className="text-sm text-gray-600">전환율</h3>
<p className="text-2xl sm:text-3xl font-bold text-green-600">12.3%</p>
</Card>
</div>
)
}Cals 예제
예약 시스템 반응형 카드
Cals Pink 브랜드를 적용한 반응형 예약 카드입니다.
import "@vortex/ui-cals/theme"
import { Card, Badge, Button } from "@vortex/ui-cals"
export default function CalsBookingCard() {
return (
<Card className="p-4 border-pink-500">
<div className="flex flex-col sm:flex-row sm:items-center gap-4">
<div className="flex-1">
<div className="flex flex-col sm:flex-row sm:items-center gap-2 mb-2">
<h3 className="text-lg font-bold">김예약 고객님</h3>
<Badge variant="confirmed">확정</Badge>
</div>
<p className="text-sm text-gray-600">2024-01-15 10:00</p>
</div>
<Button variant="primary" className="bg-pink-500 w-full sm:w-auto">
상세보기
</Button>
</div>
</Card>
)
}CodeSandbox
CodeSandbox 예제는 곧 제공될 예정입니다.
로컬에서 실행하기
-
프로젝트 생성
npx @vortex/cli init my-responsive-project --template next-app cd my-responsive-project -
컴포넌트 추가
# Foundation npx @vortex/cli add card button --package foundation # iCignal npx @vortex/cli add card --package icignal # Cals npx @vortex/cli add card badge button --package cals -
코드 복사 및 실행
pnpm dev -
반응형 테스트
- Chrome DevTools (F12) → Device Toolbar (Ctrl+Shift+M)
- 다양한 디바이스 프리셋 테스트
관련 패턴
- Navigation Patterns - 반응형 네비게이션
- Modal Patterns - 모바일 모달
- Performance Optimization - 이미지 최적화
Last updated on