diff --git a/app/work/website/layout.tsx b/app/work/website/layout.tsx new file mode 100644 index 0000000..f31aae3 --- /dev/null +++ b/app/work/website/layout.tsx @@ -0,0 +1,31 @@ +import type { Metadata } from 'next'; + +export const metadata: Metadata = { + title: '홈페이지·쇼핑몰·랜딩페이지 제작 | 반응형 웹 개발 외주', + description: + '소상공인·스타트업·기업 홈페이지, 쇼핑몰, 랜딩페이지 제작. 템플릿 없이 직접 개발. Next.js 기반 반응형 웹, SEO 기본 적용. 스타터 20만원~, 계약서 포함, 3개월 유지보수.', + keywords: [ + '홈페이지 제작 외주', + '쇼핑몰 제작 외주', + '랜딩페이지 제작', + '소상공인 홈페이지', + '스타트업 웹사이트', + '반응형 홈페이지 제작', + 'Next.js 개발 외주', + '기업 홈페이지 제작', + '웹사이트 제작 비용', + 'SEO 최적화 홈페이지', + '홈페이지 개발 프리랜서', + ], + openGraph: { + title: '홈페이지·쇼핑몰 제작 | 쟁승메이드', + description: + '소상공인·스타트업 홈페이지 제작. 템플릿 없이 직접 개발, SEO 포함, 20만원~.', + url: 'https://jaengseung-made.com/work/website', + }, + robots: { index: false, follow: false }, +}; + +export default function WebsiteLayout({ children }: { children: React.ReactNode }) { + return children; +} diff --git a/app/work/website/page.tsx b/app/work/website/page.tsx new file mode 100644 index 0000000..638b9fd --- /dev/null +++ b/app/work/website/page.tsx @@ -0,0 +1,962 @@ +'use client'; + +import Link from 'next/link'; +import { useState, useEffect, useRef } from 'react'; +import ContactModal from '@/app/components/ContactModal'; +import { trackCTAClick } from '@/lib/gtag'; + +const samples = [ + { + type: 'corporate', + title: '기업 홈페이지', + subtitle: '테크솔루션㈜', + desc: '"홈페이지가 없어서 B2B 영업 때마다 명함만 건넸다"는 고민을 해결한 기업 브랜드 사이트', + gradient: 'linear-gradient(135deg, #0a192f 0%, #112240 50%, #1a3a6c 100%)', + accent: '#4fc3f7', + tags: ['기업', 'B2B', '신뢰'], + icon: '🏢', + }, + { + type: 'bakery', + title: '베이커리 홈페이지', + subtitle: '르 쁘띠 포르', + desc: '"인스타 팔로워는 많은데 실제 방문 예약이 없다"는 F&B 매장의 전환율 문제를 해결한 사이트', + gradient: 'linear-gradient(135deg, #78350f 0%, #92400e 50%, #d97706 100%)', + accent: '#fbbf24', + tags: ['F&B', '로컬', '예약'], + icon: '🥐', + }, + { + type: 'portfolio', + title: '개인 포트폴리오', + subtitle: 'Kim Jisu', + desc: '"링크드인에 PDF 올리면 아무도 안 보더라"는 문제를 해결한 채용·수주 전환형 포트폴리오', + gradient: 'linear-gradient(135deg, #000000 0%, #0d0d0d 50%, #001a00 100%)', + accent: '#00ff88', + tags: ['크리에이터', '디자이너', '수주'], + icon: '✦', + }, + { + type: 'dashboard', + title: '관리자 대시보드', + subtitle: 'DataFlow SaaS', + desc: '"엑셀로 수기 집계하다가 오류가 나서 야근"을 없애는 실시간 데이터 대시보드', + gradient: 'linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f2a3a 100%)', + accent: '#38bdf8', + tags: ['SaaS', '자동화', '관리'], + icon: '📊', + }, + { + type: 'game', + title: '게임 매칭 시스템', + subtitle: 'NEXUS ARENA', + desc: '"디스코드에서 수동으로 팀 짜다가 싸움 났다"는 커뮤니티의 매칭·랭킹 자동화 플랫폼', + gradient: 'linear-gradient(135deg, #000000 0%, #0a0a1a 50%, #0d0029 100%)', + accent: '#a855f7', + tags: ['게임', '커뮤니티', '자동화'], + icon: '⚡', + }, + { + type: 'interior', + title: '인테리어 업체 소개', + subtitle: 'AURUM Interior', + desc: '"포트폴리오 사진을 카톡으로만 보내다가 고급 고객을 놓쳤다"는 문제를 해결한 브랜드 사이트', + gradient: 'linear-gradient(135deg, #2C1810 0%, #4A3728 50%, #6B4E37 100%)', + accent: '#D4A853', + tags: ['인테리어', '포트폴리오', '고급'], + icon: '◈', + }, + { + type: 'reading', + title: '독서 기록 노트', + subtitle: '나의 독서 기록', + desc: '읽은 책과 감상을 아름답게 기록하는 나만의 독서 저널 — 이런 것도 만들 수 있습니다', + gradient: 'linear-gradient(135deg, #0C0B09 0%, #1A1710 50%, #2A2218 100%)', + accent: '#D4A853', + tags: ['라이프', '독서', '기록'], + icon: '◻', + }, + { + type: 'shopping', + title: '개인 쇼핑몰', + subtitle: 'MELLOW STUDIO', + desc: '"카페24 기본 테마가 너무 흔해서 브랜드가 안 살아난다"는 고민을 해결한 커스텀 쇼핑몰', + gradient: 'linear-gradient(135deg, #2A2018 0%, #4A3C2C 50%, #7A6A52 100%)', + accent: '#C4A882', + tags: ['쇼핑몰', '패션', '라이프'], + icon: '◇', + }, +]; + +const processSteps = [ + { step: '01', title: '무료 상담', desc: '요구사항 파악 및 방향성 논의', icon: '💬' }, + { step: '02', title: '기획', desc: '사이트맵 & 와이어프레임', icon: '📋' }, + { step: '03', title: '디자인', desc: 'UI/UX 시안 제작', icon: '🎨' }, + { step: '04', title: '개발', desc: '반응형 퍼블리싱 & 기능 구현', icon: '⚙️' }, + { step: '05', title: '납품', desc: '검수 완료 후 도메인 배포', icon: '🚀' }, +]; + +const plans = [ + { + name: '스타터', + price: '20', + unit: '만원~', + color: '#38bdf8', + features: ['5페이지 이내', '반응형 디자인', '기본 SEO 설정', '1개월 유지보수', '3~5영업일 납품'], + note: '개인 블로그, 소규모 소개 사이트', + productId: 'website_starter', + }, + { + name: '비즈니스', + price: '100', + unit: '만원~', + color: '#818cf8', + featured: true, + features: ['10페이지 이내', '반응형 디자인', '관리자 페이지', 'SEO 최적화', '3개월 유지보수', '1~2주 납품'], + note: '기업 사이트, 브랜드 페이지', + productId: 'website_business', + }, + { + name: '프리미엄', + price: '200', + unit: '만원~', + color: '#f472b6', + features: ['페이지 수 무제한', '맞춤 디자인', '결제/회원 시스템', 'DB 연동', '6개월 유지보수', '일정 협의'], + note: '쇼핑몰, SaaS, 복합 시스템', + productId: 'website_premium', + }, +]; + +const faqs = [ + { + q: '제작 기간은 얼마나 걸리나요?', + a: '규모에 따라 다르지만, 스타터는 3~5영업일, 비즈니스는 1~2주, 프리미엄은 협의 후 결정합니다. 빠른 납품이 필요한 경우 별도 상담해 주세요.', + }, + { + q: '수정은 몇 번까지 가능한가요?', + a: '기획 확정 후 디자인 시안 수정은 2회, 개발 완료 후 기능 수정은 유지보수 기간 내 자유롭게 가능합니다. 추가 기능 개발은 별도 견적으로 진행합니다.', + }, + { + q: '도메인과 호스팅도 도와주시나요?', + a: '네, 도메인 구매부터 서버 세팅, 배포까지 전 과정을 도와드립니다. Vercel, AWS, 카페24 등 원하시는 플랫폼에 맞춰 배포해 드립니다.', + }, + { + q: '앱(모바일 앱)이나 쇼핑몰도 개발 가능한가요?', + a: '네. iOS/Android 앱(React Native), 모바일 웹앱, 결제 연동 쇼핑몰, 회원/관리자 시스템 등 모두 개발 가능합니다. 프리미엄 플랜 이상이거나 규모에 따라 별도 견적으로 진행됩니다. 먼저 어떤 기능이 필요한지 상담해 주세요.', + }, + { + q: '계약금은 어떻게 되나요? 중간에 취소하면 어떻게 되나요?', + a: '계약서 체결 후 착수금 30%를 먼저 입금받고 개발을 시작합니다. 납품 전 취소 시 완성 비율에 따라 정산하며, 착수 전 전액 환불됩니다. 모든 조건은 계약서에 명시됩니다.', + }, +]; + +function useReveal() { + const ref = useRef(null); + useEffect(() => { + const el = ref.current; + if (!el) return; + const scroller = (document.querySelector('.main-content') as HTMLElement | null) ?? document.documentElement; + const obs = new IntersectionObserver( + ([entry]) => { if (entry.isIntersecting) { el.classList.add('ws-visible'); obs.disconnect(); } }, + { threshold: 0.1, root: scroller === document.documentElement ? null : scroller } + ); + obs.observe(el); + return () => obs.disconnect(); + }, []); + return ref; +} + +function SampleMiniPreview({ type }: { type: string }) { + const W = 700, H = 350; + const inner = (content: React.ReactNode, bg: string) => ( +
+
+ {content} +
+
+ ); + + switch (type) { + case 'corporate': + return inner( +
+
+
TECHSOLUTION
+
+ {['서비스','솔루션','고객사','연락처'].map(t => {t})} +
+
상담 신청
+
+
+
ENTERPRISE IT SOLUTIONS
+
기업 IT 인프라,
처음부터 끝까지
+
15년 경험의 엔터프라이즈 IT 전문 기업.
클라우드·보안·DX 통합 솔루션을 제공합니다.
+
+
무료 상담 신청
+
서비스 소개서
+
+
+ {[['15+','년 업력'],['340+','완료 프로젝트'],['180+','기업 고객'],['99.9%','가동률']].map(([n,l]) => ( +
{n}
{l}
+ ))} +
+
+
, '#fff' + ); + + case 'bakery': + return inner( +
+
+
Le Petit Fort
ARTISAN BOULANGERIE
+
+ {['메뉴','스토리','예약'].map(t => {t})} +
+
방문 예약
+
+
+
+
Since 2018 · Paris Recipe
+
매일 아침
구워내는
정직한 빵
+
프랑스산 에슈레 버터와 천연 발효종만으로
만드는 정직한 아르티장 베이커리.
+
+
오늘의 빵 보기
+
매장 안내
+
+
+
+ {[{n:'버터 크루아상',p:'3,200',c:'#d97706'},{n:'소금빵',p:'2,800',c:'#b45309'},{n:'딸기 케이크',p:'7,500',c:'#be185d'},{n:'캄파뉴',p:'8,900',c:'#065f46'}].map(item => ( +
+
+
{item.n}
+
₩{item.p}
+
+ ))} +
+
+
, '#fffbf5' + ); + + case 'portfolio': + return inner( +
+
+
+
KJ_
+
+ {['About','Work','Skills','Contact'].map(t => {t})} +
+
+
+ Available +
HIRE ME
+
+
+
+
+
+
FULL-STACK DEVELOPER
+
Kim
Jisu
+
React · Next.js · TypeScript · Node.js
디자인과 코드의 경계를 탐험합니다.
+
+
VIEW WORK
+
CONTACT
+
+
+
+
+ KJ +
+
+
+
, '#000' + ); + + case 'dashboard': + return inner( +
+
+
DataFlow
+ {['대시보드','분석','리포트','사용자','설정'].map((item, i) => ( +
{item}
+ ))} +
+
+
+
실시간 현황
+
이번 달
+
+
+ {[{l:'총 매출',v:'₩48.2M',c:'#38bdf8',u:true},{l:'신규 사용자',v:'1,247',c:'#34d399',u:true},{l:'전환율',v:'12.4%',c:'#a78bfa',u:false}].map(s => ( +
+
{s.l}
+
{s.v}
+
{s.u ? '↑ +8.3%' : '↓ -1.2%'}
+
+ ))} +
+
+
월간 매출 추이
+
+ {[40,55,35,65,80,60,90,75,85,95,70,100].map((h, i) => ( +
+ ))} +
+
+
+
, '#0f172a' + ); + + case 'game': + return inner( +
+
+
+
+
NEXUSARENA
+
+ {['랭킹','매칭','챔피언','스토어'].map(t => {t})} +
+
PLAY NOW
+
+
+
+
SEASON 7 · COMPETITIVE
+
NEXUS
ARENA
+
실시간 매칭 · 랭크 시스템 · 글로벌 토너먼트
지금 바로 전장에 참전하세요.
+
+
PLAY NOW
+
랭킹 보기
+
+
+
+ {[{name:'VIPER',role:'Assassin',c:'#06b6d4'},{name:'NOVA',role:'Mage',c:'#a855f7'},{name:'IRON',role:'Tank',c:'#94a3b8'},{name:'KIRA',role:'Support',c:'#ec4899'}].map(ch => ( +
+
+
+
+
{ch.name}
+
{ch.role}
+
+ ))} +
+
+
, '#000' + ); + + case 'interior': + return inner( +
+
+
AURUM
INTERIOR DESIGN
+
+ {['포트폴리오','서비스','견적 문의'].map(t => {t})} +
+
CONTACT
+
+
+
+
Premium Interior Design
+
공간이
이야기가
되는 순간
+
20년 경험의 인테리어 전문가가
당신만의 공간을 완성합니다.
+
포트폴리오 보기
+
+
+ {['linear-gradient(135deg, #c9b99a, #a8927a)','linear-gradient(135deg, #8B7355, #6B5A47)','linear-gradient(135deg, #D4C5A9, #B8A88A)','linear-gradient(135deg, #7C6555, #5C4A3A)'].map((bg, i) => ( +
+ ))} +
+
+
, '#faf8f4' + ); + + case 'reading': + return inner( +
+
+
나의 독서 기록
+
+ {['서재','월별 기록','통계'].map(t => {t})} +
+
+
+
+
My Reading Journal
+
읽은 책들이
별처럼
빛나는 공간
+
독서 기록을 아름답게.
감상과 인용구를 나만의 서재에 담아보세요.
+
기록 시작하기
+
+ {[['47','완독'],['1,240','페이지'],['12','이번 달']].map(([n,l]) => ( +
{n}
{l}
+ ))} +
+
+
+ {[{h:130,bg:'linear-gradient(180deg,#1e3a5f,#0a1628)',sp:'#2563eb'},{h:152,bg:'linear-gradient(180deg,#2C1810,#1a0e0a)',sp:'#D4A853'},{h:118,bg:'linear-gradient(180deg,#1a1a1a,#0d0d0d)',sp:'#6b7280'},{h:142,bg:'linear-gradient(180deg,#1e1b4b,#0f0d2e)',sp:'#7c3aed'},{h:120,bg:'linear-gradient(180deg,#064e3b,#022c22)',sp:'#10b981'}].map((b, i) => ( +
+
+
+ ))} +
+
+
, '#0C0B09' + ); + + case 'shopping': + return inner( +
+
+
MELLOW STUDIO
+
+ {['NEW','OUTER','TOP','BOTTOM'].map(t => {t})} +
+
🔍🛍 2
+
+
+
+
+
+
NEW ARRIVAL
+
SS 2025
+
+
+
+
+
COLLECTION
+
Quiet
Luxury
+
소음 없이 존재하는 옷.
절제된 아름다움을 입으세요.
+
SHOP NOW
+
+
+ {[['#c8b89a','₩328K'],['#8a7860','₩498K'],['#d4c5a9','₩218K']].map(([bg, p], i) => ( +
+
+
{p}
+
+ ))} +
+
+
+
, '#F4F2EF' + ); + + default: + return
; + } +} + +export default function WebsiteServicePage() { + const [openFaq, setOpenFaq] = useState(null); + const [showTop, setShowTop] = useState(false); + const [modalOpen, setModalOpen] = useState(false); + const [modalService, setModalService] = useState('홈페이지 제작'); + + const openModal = (service: string) => { + trackCTAClick(service, '/work/website'); + setModalService(service); + setModalOpen(true); + }; + + useEffect(() => { + const scroller = (document.querySelector('.main-content') as HTMLElement | null) ?? document.documentElement; + const onScroll = () => setShowTop(scroller.scrollTop > 400); + scroller.addEventListener('scroll', onScroll, { passive: true }); + return () => scroller.removeEventListener('scroll', onScroll); + }, []); + + const samplesRef = useReveal(); + const processRef = useReveal(); + const pricingRef = useReveal(); + const faqRef = useReveal(); + const ctaRef = useReveal(); + + return ( +
+ setModalOpen(false)} + service={modalService} + checklist={[ + '원하시는 홈페이지 종류 (소개/쇼핑몰/SaaS 등)', + '참고하고 싶은 사이트 URL (있으면)', + '필요한 주요 페이지 및 기능', + '희망 납품 일정 및 예산 범위', + '디자인 선호 스타일 (모던/감성/심플 등)', + ]} + accentColor="text-indigo-400" + headerFrom="#0a0a1a" + headerTo="#1e1b4b" + /> + + + {/* Back Banner */} +
+ + ← 홈페이지 제작 서비스로 돌아가기 + + | + + SAMPLE · 베이커리 홈페이지 + +
+ + {/* Navbar */} + + + {/* Hero */} +
+ {/* Decorative circles */} +
+
+ + {/* SVG Croissant illustration */} +
+ + {/* Croissant shape */} + + + {/* Layered flakes */} + + + + {/* Shine */} + + {/* Steam */} + + + + +
+ + {/* Wheat decoration top right */} + + + {[15, 35, 55, 75].map((y, i) => ( + + + + + ))} + + +
+
+ + 오늘도 새벽 4시부터 굽고 있습니다 +
+ +
+ “매일 아침, 정성을 굽습니다” +
+

+ 갓 구운 빵의
+ 따뜻한 향기
+ 기다립니다 +

+

+ 파리 전통 방식으로 매일 새벽 4시부터
정성껏 굽는 르 쁘띠 포르의 빵을 만나보세요. +

+ + {/* Social proof */} +
+
+ {['#d97706', '#b45309', '#92400e', '#78350f'].map((c, i) => ( +
0 ? -8 : 0 }} /> + ))} +
+
+
+ {[1,2,3,4,5].map(i => ( + + ))} +
+ 4.9점 · 1,200+ 리뷰 +
+
+ +
+ + +
+
+
+ + {/* Season Special Banner */} +
+
+ + Spring Limited +
+ | + + 🌸 딸기 타르틀렛 — 4월 한정 메뉴 출시 + + | + 자세히 보기 → +
+ + {/* Menu */} +
+
+
+
+ Today's Menu +
+

+ 오늘의 추천 메뉴 +

+

+ 매일 새벽 구운 신선한 빵을 만나보세요 +

+
+
+ {menuItems.map((item) => ( +
+ {/* Card visual — SVG pattern instead of emoji */} +
+ {/* Subtle dotted pattern */} + + + + + + + {/* Bread silhouette SVG */} + + + + + + + + {item.tag} +
+
+
+ {item.name} +
+
+ {item.desc} +
+
+ {item.ingredients.map(ig => ( + {ig} + ))} +
+
+ + ₩{item.price} + + +
+
+
+ ))} +
+
+
+ + {/* Seasonal Specials */} +
+
+
+
+ Seasonal Specials +
+

+ 계절마다 새로운 감동 +

+
+
+ {specials.map((s) => ( +
+
+
+
+
+
{s.season} 한정
+
{s.item}
+
{s.desc}
+
+ ))} +
+
+
+ + {/* Story */} +
+
+
+
+ Our Story +
+

+ 2009년부터
한 자리를 지켜온
+ 우리 동네 빵집 +

+

+ 파리에서 5년간 수업한 오너 셰프가 고향 서울로 돌아와 차린 작은 베이커리. 대기업 프랜차이즈가 넘쳐나는 세상에서도 손으로 빚고, 눈으로 확인하는 전통 방식을 고집합니다. +

+

+ 밀가루, 버터, 소금, 물. 단 네 가지 재료로 만드는 우리의 빵에는 흉내낼 수 없는 진심이 담겨있습니다. +

+
+ {[{ n: '15+', l: '년 경력' }, { n: '200+', l: '종류의 빵' }, { n: '4시', l: '매일 기상' }].map((s) => ( +
+
{s.n}
+
{s.l}
+
+ ))} +
+
+ + {/* Chef card with SVG illustration */} +
+ {/* Pattern overlay */} + + + + + + + {/* Chef SVG illustration */} +
+
+ + {/* Chef hat */} + + + + {/* Face */} + + + + + +
+
+ Chef Kim Dongwoo +
+
+ Le Cordon Bleu Paris 졸업
+ 2009년 르 쁘띠 포르 창업 +
+ {/* Awards */} +
+ {['미쉐린 추천', '블루리본', '로컬 레전드'].map(award => ( + {award} + ))} +
+
+
+
+
+ + {/* Customer Reviews */} +
+
+
+
+ Guest Reviews +
+

+ 고객들의 이야기 +

+
+ {[1,2,3,4,5].map(i => ( + + ))} + 4.9 +  (1,243개 리뷰) +
+
+
+ {reviews.map((r) => ( +
+ {/* Stars */} +
+ {[1,2,3,4,5].map(i => ( + + ))} + {r.verified && ( + + ✓ 인증 방문 + + )} +
+ {/* Quote SVG */} + + + +

+ {r.text} +

+
+
+ {r.name[0]} +
+
+
{r.name}
+
{r.date}
+
+
+
+ ))} +
+
+
+ + {/* Hours & Location */} +
+
+
+
Hours
+

운영 시간

+
+ {hours.map((h) => ( +
+ {h.day} + {h.time} +
+ ))} +
+
+

+ ⚡ 품절 시 조기 마감될 수 있습니다.
+ 인스타그램에서 당일 재고를 확인하세요. +

+
+
+
+
Location
+

매장 위치

+

+ 서울특별시 마포구 연남동 224-14
연남로 68 르 쁘띠 포르 +

+
+ {[ + { icon: 'M', text: '2호선 홍대입구역 3번 출구 도보 5분' }, + { icon: 'T', text: '02-334-5678' }, + { icon: '@', text: '@lepetitfort_seoul' }, + ].map((info) => ( +
+
+ {info.icon} +
+ {info.text} +
+ ))} +
+
+
+
+ + {/* CTA — Custom Cake */} +
+
+ + Custom Order +
+

+ 특별한 날을 위한 케이크 +

+

+ 생일, 기념일, 웨딩 케이크까지.
+ 최소 3일 전 예약 시 원하시는 케이크를 제작해드립니다. +

+

+ 가격 상담 무료 · 사진 참고 가능 · 직접 수령 또는 배달 가능 +

+ +
+ + {/* Footer */} +
+
+
+ Le Petit Fort — Artisan Boulangerie +
+
+ © 2024 르 쁘띠 포르. All rights reserved. +
+
+
+ {['Instagram', 'Naver Map', 'Kakao'].map((s) => ( + {s} + ))} +
+
+
+ ); +} diff --git a/app/work/website/samples/corporate/page.tsx b/app/work/website/samples/corporate/page.tsx new file mode 100644 index 0000000..63f3995 --- /dev/null +++ b/app/work/website/samples/corporate/page.tsx @@ -0,0 +1,279 @@ +import Link from 'next/link'; + +export default function CorporateSample() { + const services = [ + { + title: 'IT 인프라 구축', + desc: '기업 맞춤형 서버 환경 설계부터 클라우드 마이그레이션까지, 안정적인 IT 기반을 구축합니다.', + detail: '온프레미스 · 하이브리드 클라우드 · AWS · Azure', + icon: ( + + + + ), + }, + { + title: '사이버 보안 솔루션', + desc: '최신 위협에 대응하는 엔터프라이즈급 보안 시스템. 침해사고 예방부터 대응까지 통합 관리합니다.', + detail: 'ISMS · 취약점 분석 · 보안 모니터링 · 컴플라이언스', + icon: ( + + + + ), + }, + { + title: '디지털 전환 (DX)', + desc: '레거시 시스템을 현대화하고 비즈니스 프로세스를 자동화하는 DX 컨설팅을 제공합니다.', + detail: 'ERP 연동 · 프로세스 자동화 · 데이터 시각화 · AI 도입', + icon: ( + + + + ), + }, + ]; + + const stats = [ + { num: '15+', label: '년 업력' }, + { num: '340+', label: '완료 프로젝트' }, + { num: '180+', label: '기업 고객사' }, + { num: '99.9%', label: '서비스 가동률' }, + ]; + + const certs = ['AWS Partner', 'ISO 27001', 'ISMS-P 인증', 'Microsoft Partner', 'Google Cloud']; + + const clients = ['삼성전자', 'LG유플러스', '현대모비스', 'SK하이닉스', 'KT', '신한은행', 'NH농협', '롯데정보통신']; + + const testimonials = [ + { + quote: '마이그레이션 기간 동안 단 한 번의 서비스 중단도 없었습니다. 완벽한 이전이었습니다.', + name: '이○○ CTO', + company: '핀테크 스타트업', + }, + { + quote: '보안 감사 이후 취약점 제로. 컴플라이언스 대응이 이렇게 빠를 수 있다는 게 놀라웠습니다.', + name: '박○○ IT팀장', + company: '중견 제조사', + }, + ]; + + return ( +
+ + + {/* Back Banner */} +
+ + ← 홈페이지 제작 서비스로 돌아가기 + + | + SAMPLE · 기업 홈페이지 +
+ + {/* Navbar */} + + + {/* Hero */} +
+ {/* Animated grid */} +
+
+ + {/* Floating tech nodes */} + {[ + { x: '68%', y: '20%', delay: '0s', size: 6, color: '#60a5fa' }, + { x: '78%', y: '55%', delay: '1s', size: 4, color: '#a78bfa' }, + { x: '85%', y: '35%', delay: '2s', size: 8, color: '#34d399' }, + { x: '72%', y: '72%', delay: '0.5s', size: 5, color: '#60a5fa' }, + ].map((dot, i) => ( +
+ ))} + + {/* SVG Tech Illustration */} + + + + + + + + + + + +
+
+
+ Enterprise IT Solutions +
+

+ 디지털 혁신으로
+ 비즈니스의 미래
+ 설계합니다 +

+

+ 15년의 경험과 검증된 기술력으로 기업의 IT 인프라를 혁신합니다.
+ 클라우드, 보안, 디지털 전환까지 원스톱으로 제공합니다. +

+
+ + +
+
+
+ + {/* Stats Bar */} +
+
+ {stats.map((s, i) => ( +
+
{s.num}
+
{s.label}
+
+ ))} +
+
+ + {/* Certifications */} +
+
+ 인증 · 파트너십 + {certs.map((c) => ( +
+ {c} +
+ ))} +
+
+ + {/* Services */} +
+
+
+
Our Services
+

핵심 서비스

+

기업의 성장을 이끄는 검증된 IT 솔루션

+
+
+ {services.map((svc, i) => ( +
+
+
+ {svc.icon} +
+

{svc.title}

+

{svc.desc}

+
{svc.detail}
+
+ 자세히 보기 + +
+
+ ))} +
+
+
+ + {/* Testimonials */} +
+
+
+
Client Reviews
+

고객 후기

+
+
+ {testimonials.map((t, i) => ( +
+
"
+

{t.quote}

+
+
+ {t.name[0]} +
+
+
{t.name}
+
{t.company}
+
+
+
+ ))} +
+
+
+ + {/* Clients */} +
+
+
Trusted By Leading Companies
+
+ {clients.map((c) => ( +
{c}
+ ))} +
+
+
+ + {/* Contact CTA */} +
+
+
Get Started
+

+ 프로젝트를 시작하세요 +

+

+ IT 솔루션이 필요하시면 언제든지 연락해주세요.
+ 전담 컨설턴트가 최적의 방안을 제안해드립니다. +

+
+ + +
+
+
+ + {/* Footer */} +
+
© 2024 ㈜테크솔루션. All rights reserved.
+
서울특별시 강남구 테헤란로 123 테크타워 15F
+
+
+ ); +} diff --git a/app/work/website/samples/dashboard/page.tsx b/app/work/website/samples/dashboard/page.tsx new file mode 100644 index 0000000..a86df67 --- /dev/null +++ b/app/work/website/samples/dashboard/page.tsx @@ -0,0 +1,436 @@ +'use client'; + +import Link from 'next/link'; +import { useState } from 'react'; + +const kpis = [ + { label: '월 활성 사용자', value: '124,832', change: '+12.4%', up: true, color: '#3b82f6', sparkline: [40, 55, 45, 70, 60, 85, 100] }, + { label: '월 매출', value: '₩284M', change: '+8.7%', up: true, color: '#10b981', sparkline: [50, 60, 55, 75, 80, 78, 100] }, + { label: '전환율', value: '3.62%', change: '-0.3%', up: false, color: '#f59e0b', sparkline: [90, 80, 85, 75, 70, 72, 68] }, + { label: '고객 만족도', value: '4.8', change: '+0.2', up: true, color: '#8b5cf6', sparkline: [70, 72, 74, 76, 78, 80, 85] }, +]; + +const lineData = [ + { month: 'Jan', revenue: 65, users: 48 }, + { month: 'Feb', revenue: 78, users: 55 }, + { month: 'Mar', revenue: 72, users: 52 }, + { month: 'Apr', revenue: 89, users: 64 }, + { month: 'May', revenue: 95, users: 71 }, + { month: 'Jun', revenue: 82, users: 66 }, + { month: 'Jul', revenue: 110, users: 88 }, + { month: 'Aug', revenue: 128, users: 100 }, +]; + +const activities = [ + { user: 'lee@company.com', action: '프리미엄 플랜 구독', time: '2분 전', status: 'success', avatar: 'L' }, + { user: 'park@startup.io', action: 'API 한도 초과 경고', time: '14분 전', status: 'warning', avatar: 'P' }, + { user: 'kim@corp.kr', action: '팀 멤버 5명 초대', time: '31분 전', status: 'info', avatar: 'K' }, + { user: 'choi@brand.com', action: '결제 실패 (카드 만료)', time: '1시간 전', status: 'error', avatar: 'C' }, + { user: 'jung@agency.co', action: '새 워크스페이스 생성', time: '2시간 전', status: 'success', avatar: 'J' }, +]; + +const menus = [ + { id: 'overview', label: 'Overview', dot: '#3b82f6' }, + { id: 'analytics', label: 'Analytics', dot: '#10b981' }, + { id: 'users', label: 'Users', dot: null }, + { id: 'revenue', label: 'Revenue', dot: null }, + { id: 'reports', label: 'Reports', dot: null }, + { id: 'settings', label: 'Settings', dot: null }, +]; + +const channels = [ + { label: 'Organic Search', val: 78, color: '#3b82f6' }, + { label: 'Direct', val: 55, color: '#10b981' }, + { label: 'Social Media', val: 42, color: '#a855f7' }, + { label: 'Email', val: 34, color: '#f59e0b' }, + { label: 'Referral', val: 20, color: '#ec4899' }, +]; + +const alerts = [ + { type: 'error', msg: 'API 응답 지연 (p99 > 2s)', time: '5분 전' }, + { type: 'warning', msg: '스토리지 사용량 85% 초과', time: '32분 전' }, + { type: 'success', msg: '일일 백업 완료', time: '1시간 전' }, +]; + +const statusColor: Record = { success: '#10b981', warning: '#f59e0b', error: '#ef4444', info: '#3b82f6' }; + +function SparkLine({ data, color }: { data: number[]; color: string }) { + const max = Math.max(...data); + const min = Math.min(...data); + const h = 28; + const w = 72; + const step = w / (data.length - 1); + const pts = data.map((v, i) => { + const x = i * step; + const y = h - ((v - min) / (max - min || 1)) * h; + return `${x},${y}`; + }).join(' '); + return ( + + + + + ); +} + +function LineChart({ data }: { data: typeof lineData }) { + const chartH = 130; + const chartW = 440; + const padL = 32; + const padB = 24; + const innerW = chartW - padL; + const innerH = chartH - padB; + const maxRev = Math.max(...data.map(d => d.revenue)); + + const revPts = data.map((d, i) => { + const x = padL + (i / (data.length - 1)) * innerW; + const y = (1 - d.revenue / maxRev) * innerH; + return `${x},${y}`; + }).join(' '); + + const usrPts = data.map((d, i) => { + const x = padL + (i / (data.length - 1)) * innerW; + const y = (1 - d.users / maxRev) * innerH; + return `${x},${y}`; + }).join(' '); + + return ( + + {/* Grid lines */} + {[0, 0.25, 0.5, 0.75, 1].map((t, i) => ( + + + + {Math.round(maxRev * (1 - t))} + + + ))} + {/* Area fills */} + + + + + + + + + + + + + {/* Lines */} + + + {/* Dots at last point */} + {[{ pts: revPts, color: '#3b82f6' }, { pts: usrPts, color: '#10b981' }].map(({ pts: p, color }) => { + const last = p.split(' ').pop()!; + const [lx, ly] = last.split(',').map(Number); + return ; + })} + {/* X axis labels */} + {data.map((d, i) => ( + {d.month} + ))} + + ); +} + +export default function DashboardSample() { + const [activeMenu, setActiveMenu] = useState('overview'); + const [alertsVisible, setAlertsVisible] = useState(true); + + return ( +
+ + + {/* Back Banner */} +
+ + ← 홈페이지 제작 서비스로 돌아가기 + + | + + SAMPLE · 관리자 대시보드 + +
+ +
+ {/* Sidebar */} + + + {/* Main */} +
+ {/* Top bar */} +
+
+
+ + + + + +
+
+
+ + {/* Alert bell */} + + +
+
+ +
+ {/* Alerts panel */} + {alertsVisible && ( +
+
+
시스템 알림
+ +
+
+ {alerts.map((a, i) => ( +
+
+
+
{a.msg}
+
{a.time}
+
+
+ ))} +
+
+ )} + + {/* Page header */} +
+
+

Overview

+
2024.08.14 · 오전 10:32 업데이트
+
+
+ + {/* KPI Cards */} +
+ {kpis.map((kpi) => ( +
+
+
{kpi.label}
+ +
+
{kpi.value}
+
+ {kpi.up ? '↑' : '↓'} {kpi.change} +
+
+ ))} +
+ + {/* Charts row */} +
+ {/* Line Chart */} +
+
+
월별 매출 & 사용자 추이
+
+
+
+ 매출 +
+
+
+ 사용자 +
+
+ {['1M', '3M', '6M', '1Y'].map((p) => ( + + ))} +
+
+
+ +
+ + {/* Channel Progress */} +
+
채널별 전환율
+ {channels.map((p) => ( +
+
+ {p.label} + {p.val}% +
+
+
+
+
+ ))} + {/* Donut summary */} +
+ + {(() => { + const total = channels.reduce((s, c) => s + c.val, 0); + let offset = 0; + const r = 20; + const circ = 2 * Math.PI * r; + return channels.map((c) => { + const dash = (c.val / total) * circ; + const el = ( + + ); + offset += dash; + return el; + }); + })()} + + ALL + +
+ Organic 채널이
+ 최고 전환율 78%
+ 달성 중 +
+
+
+
+ + {/* Activity Table */} +
+
+
최근 활동
+ +
+ + + + {['사용자', '활동', '시간', '상태'].map((h) => ( + + ))} + + + + {activities.map((a, i) => ( + + + + + + + ))} + +
{h}
+
+
{a.avatar}
+ {a.user} +
+
{a.action}{a.time} +
+
+ + {a.status === 'success' ? 'OK' : a.status === 'warning' ? 'WARN' : a.status === 'error' ? 'ERR' : 'INFO'} + +
+
+
+
+
+
+
+ ); +} diff --git a/app/work/website/samples/game/page.tsx b/app/work/website/samples/game/page.tsx new file mode 100644 index 0000000..910eec2 --- /dev/null +++ b/app/work/website/samples/game/page.tsx @@ -0,0 +1,428 @@ +'use client'; + +import Link from 'next/link'; +import { useState, useEffect } from 'react'; + +const rankings = [ + { rank: 1, name: 'ShadowViper_KR', score: 9_842, tier: 'GRANDMASTER', wins: 312, kda: '18.4', change: '+2' }, + { rank: 2, name: 'NightFalcon', score: 9_610, tier: 'GRANDMASTER', wins: 289, kda: '15.2', change: '0' }, + { rank: 3, name: 'Xenon_X', score: 9_241, tier: 'MASTER', wins: 267, kda: '12.9', change: '+1' }, + { rank: 4, name: 'KR_Dominator', score: 8_970, tier: 'MASTER', wins: 251, kda: '11.7', change: '-1' }, + { rank: 5, name: 'Pulse_Wave', score: 8_834, tier: 'DIAMOND', wins: 238, kda: '10.3', change: '+3' }, +]; + +const modes = [ + { id: 'solo', name: 'SOLO', sub: '1 vs 1', desc: '순수한 실력으로 맞붙는 1대1 대결', color: '#06b6d4', players: '12,400', season: 'S7' }, + { id: 'duo', name: 'DUO', sub: '2 vs 2', desc: '파트너와 함께하는 전략적 팀플레이', color: '#a855f7', players: '28,700', season: 'S7' }, + { id: 'squad', name: 'SQUAD', sub: '5 vs 5', desc: '전략과 팀워크로 승리를 쟁취', color: '#f59e0b', players: '7,100', season: 'S7' }, +]; + +const recentMatches = [ + { result: 'WIN', mode: 'DUO', duration: '18:32', kills: 12, deaths: 2, assists: 8, rating: '+32' }, + { result: 'WIN', mode: 'SOLO', duration: '22:11', kills: 8, deaths: 1, assists: 4, rating: '+24' }, + { result: 'LOSS', mode: 'SQUAD', duration: '31:45', kills: 5, deaths: 5, assists: 12, rating: '-18' }, + { result: 'WIN', mode: 'DUO', duration: '15:20', kills: 15, deaths: 3, assists: 6, rating: '+40' }, +]; + +const champions = [ + { name: 'VIPER', role: 'Assassin', color: '#06b6d4', power: 92, winRate: '63%' }, + { name: 'KIRA', role: 'Support', color: '#ec4899', power: 78, winRate: '58%' }, + { name: 'IRON', role: 'Tank', color: '#94a3b8', power: 85, winRate: '55%' }, + { name: 'NOVA', role: 'Mage', color: '#a855f7', power: 88, winRate: '61%' }, +]; + +const tierColor: Record = { + GRANDMASTER: '#fbbf24', + MASTER: '#a855f7', + DIAMOND: '#60a5fa', +}; + +const tierIcon = (tier: string) => ( + + + + +); + +export default function GameSample() { + const [onlinePlayers, setOnlinePlayers] = useState(48_219); + const [matchingCount, setMatchingCount] = useState(1_342); + const [matchingActive, setMatchingActive] = useState(false); + const [matchTimer, setMatchTimer] = useState(0); + const [selectedChampion, setSelectedChampion] = useState(0); + const [seasonProgress] = useState(67); + + useEffect(() => { + const interval = setInterval(() => { + setOnlinePlayers((p) => p + Math.floor(Math.random() * 6 - 2)); + setMatchingCount((c) => c + Math.floor(Math.random() * 4 - 1)); + }, 2000); + return () => clearInterval(interval); + }, []); + + useEffect(() => { + let timer: ReturnType; + if (matchingActive) { + timer = setInterval(() => setMatchTimer((t) => t + 1), 1000); + } else { + setMatchTimer(0); + } + return () => clearInterval(timer); + }, [matchingActive]); + + return ( +
+ + + {/* Back Banner */} +
+ + ← 홈페이지 제작 서비스로 돌아가기 + + | + + SAMPLE · 게임 매칭 시스템 + +
+ + {/* Navbar */} + + + {/* Hero */} +
+ {/* Grid */} +
+ {/* Scan */} +
+ + {/* Floating particles */} + {[ + { left: '10%', top: '20%', size: 4, delay: '0s', color: '#06b6d4' }, + { left: '85%', top: '30%', size: 6, delay: '1s', color: '#a855f7' }, + { left: '20%', top: '70%', size: 3, delay: '2s', color: '#06b6d4' }, + { left: '75%', top: '65%', size: 5, delay: '0.5s', color: '#f59e0b' }, + { left: '50%', top: '15%', size: 3, delay: '1.5s', color: '#a855f7' }, + { left: '60%', top: '80%', size: 4, delay: '2.5s', color: '#10b981' }, + ].map((p, i) => ( +
+ ))} + + {/* Corner brackets */} + {[ + { top: 40, left: 40, borderTop: '2px solid #06b6d4', borderLeft: '2px solid #06b6d4' }, + { top: 40, right: 40, borderTop: '2px solid #a855f7', borderRight: '2px solid #a855f7' }, + { bottom: 40, left: 40, borderBottom: '2px solid #06b6d4', borderLeft: '2px solid #06b6d4' }, + { bottom: 40, right: 40, borderBottom: '2px solid #a855f7', borderRight: '2px solid #a855f7' }, + ].map((s, i) => ( +
+ ))} + +
+
+ Season 7 · RANKED MATCH +
+

+ NEXUS
+ ARENA +

+

+ ENTER THE ARENA. CLAIM YOUR GLORY. +

+ + {/* Live Stats */} +
+ {[ + { label: 'ONLINE', val: onlinePlayers.toLocaleString(), color: '#06b6d4' }, + { label: 'IN MATCH', val: matchingCount.toLocaleString(), color: '#a855f7' }, + { label: 'SERVERS', val: '24', color: '#10b981' }, + { label: 'AVG WAIT', val: '< 30s', color: '#f59e0b' }, + ].map((s) => ( +
+
{s.val}
+
{s.label}
+
+ ))} +
+ + {/* Match button */} + {!matchingActive ? ( + + ) : ( +
+
+ MATCHING... {String(Math.floor(matchTimer / 60)).padStart(2, '0')}:{String(matchTimer % 60).padStart(2, '0')} +
+ +
+ )} +
+
+ + {/* Season Pass */} +
+
+
+
+
SEASON 7 PASS
+
+ Lv. 42 + / 100 +
+
+
+
+ {seasonProgress}% 완료 + 다음 보상까지 1,840 XP +
+
+
+
+
+
+ {['스킨 3개', '이모트 5개', '칭호 2개', '골드 5,000'].map((reward) => ( +
+ {reward} +
+ ))} +
+
+
+
+ + {/* Champion Select */} +
+
+
+
+
// ROSTER
+

+ CHAMPIONS. +

+
+ +
+
+ {champions.map((c, i) => ( +
setSelectedChampion(i)} + // @ts-expect-error CSS variable + style={{ '--champ-color': c.color, border: `1px solid ${selectedChampion === i ? c.color : c.color + '25'}`, borderRadius: 6, padding: '22px 18px', background: selectedChampion === i ? c.color + '10' : 'rgba(0,0,0,0.6)', position: 'relative', overflow: 'hidden' }} + > + {selectedChampion === i && ( +
+
SELECTED
+
+ )} +
+ {/* Champion SVG silhouette */} +
+ + + + + +
+
{c.name}
+
{c.role}
+
+
+ POWER + {c.power} +
+
+
+
+
+
+ Win Rate: {c.winRate} +
+
+ ))} +
+
+
+ + {/* Game Modes */} +
+
+
+

+ GAME MODES +

+
+
+
+ {modes.map((mode) => ( +
+
+ {/* Mode icon */} +
+ + {mode.id === 'solo' && } + {mode.id === 'duo' && <>} + {mode.id === 'squad' && <>} + +
+
+
{mode.name}
+
{mode.sub}
+
+
{mode.desc}
+
+
+
{mode.players} IN QUEUE
+
AVG WAIT: < 30s
+
+ +
+
+ ))} +
+
+
+ + {/* Rankings + Recent Matches */} +
+
+ {/* Rankings */} +
+
+

+ GLOBAL RANKING. +

+
Season 7 · Top 100
+
+
+
+ {['RANK', 'PLAYER', 'SCORE', 'WINS', 'K/D/A'].map((h) => ( +
{h}
+ ))} +
+ {rankings.map((r, i) => ( +
+
+ {r.rank < 10 ? `0${r.rank}` : r.rank} +
+
+
+ {tierIcon(r.tier)} + {r.name} +
+
+ {r.tier} + + {r.change === '0' ? '–' : r.change} + +
+
+
{r.score.toLocaleString()}
+
{r.wins}
+
{r.kda}
+
+ ))} +
+
+ + {/* Recent Matches */} +
+
+

+ RECENT MATCHES. +

+
Last 10 games
+
+
+ {recentMatches.map((m, i) => ( +
+
+
{m.result}
+
{m.mode}
+
+
+
+ {m.kills} / {m.deaths} / {m.assists} + K/D/A +
+
{m.duration}
+
+
+ {m.rating} +
RATING
+
+
+ ))} +
+
+
+
+ + {/* Footer */} +
+
NEXUS ARENA
+
+ © 2024 NEXUS ARENA STUDIOS. ALL RIGHTS RESERVED. +
+
+ {['Twitter', 'Discord', 'YouTube', 'Twitch'].map((s) => ( + {s} + ))} +
+
+
+ ); +} diff --git a/app/work/website/samples/interior/page.tsx b/app/work/website/samples/interior/page.tsx new file mode 100644 index 0000000..24f33a6 --- /dev/null +++ b/app/work/website/samples/interior/page.tsx @@ -0,0 +1,612 @@ +'use client'; + +import Link from 'next/link'; +import { useState, useEffect } from 'react'; + +/* ══════════════════════════════════════════════ + DATA +══════════════════════════════════════════════ */ +const portfolio = [ + { title: '한남동 단독주택', cat: '주거 인테리어', area: '245㎡', img: 'https://picsum.photos/seed/interior-hannam/800/600' }, + { title: '청담 파인다이닝', cat: '상업 공간', area: '190㎡', img: 'https://picsum.photos/seed/interior-dining/800/600' }, + { title: '성수 브랜드 오피스', cat: '업무 공간', area: '380㎡', img: 'https://picsum.photos/seed/interior-office/800/600' }, + { title: '용산 아파트 리모델링', cat: '리모델링', area: '95㎡', img: 'https://picsum.photos/seed/interior-remodel/800/600' }, + { title: '강남 카페 에스프레소랩', cat: '상업 공간', area: '120㎡', img: 'https://picsum.photos/seed/interior-cafe/800/600' }, +]; + +const services = [ + { + title: '주거 인테리어', sub: 'Residential', + desc: '생활의 리듬에 맞춘 공간을 설계합니다. 단독주택부터 아파트까지, 당신의 일상이 더 아름다워지도록 모든 디테일을 손수 고릅니다.', + details: ['공간 기획 및 3D 시뮬레이션', '자재 선정 동행 서비스', '시공 전 과정 PM', '준공 후 AS 1년'], + img: 'https://picsum.photos/seed/interior-residential/800/600', + }, + { + title: '상업 공간 디자인', sub: 'Commercial', + desc: '브랜드의 철학이 공간 언어로 번역됩니다. 첫 방문객이 문을 열었을 때 느끼는 그 감정까지 설계의 범위입니다.', + details: ['브랜드 아이덴티티 반영', '동선 및 고객 UX 설계', '조명·음향 플래닝', '설비 협력사 연계'], + img: 'https://picsum.photos/seed/interior-commercial/800/600', + }, + { + title: '리모델링 & 재생', sub: 'Remodeling', + desc: '기존 공간의 가능성을 새로운 시선으로 바라봅니다. 구조적 변경부터 마감재 교체까지, 완전한 변신을 지원합니다.', + details: ['현장 실측 및 구조 분석', '철거~완공 원스톱', '예산 내 최적 시공', '친환경 자재 우선 적용'], + img: 'https://picsum.photos/seed/interior-remodeling/800/600', + }, +]; + +const testimonials = [ + { name: '하윤서', role: '한남동 단독주택 의뢰인', u: 'hayunseo', rating: 5, highlight: '계획보다 적은 예산', + text: '처음엔 예산이 걱정됐는데, 아우라 팀이 범위를 명확히 정해줘서 오히려 계획보다 적게 들었습니다. 무엇보다 완공된 공간에서 매일 아침 커피 한 잔 하는 지금이 너무 행복해요.' }, + { name: '박도현', role: '카페 에스프레소랩 대표', u: 'parkdohyun', rating: 5, highlight: '매출 340% 상승', + text: '우리 브랜드 철학을 완벽하게 공간으로 옮겨줬습니다. 오픈 첫날부터 SNS 바이럴이 터졌고, 오픈 3개월 만에 매출이 전년 대비 340% 올랐어요.' }, + { name: '이서진', role: '루미너스 COO', u: 'leeseojin', rating: 5, highlight: '직원 만족도 93점', + text: '직원들이 출근하고 싶은 공간을 만드는 게 목표였습니다. 리모델링 후 직원 만족도 설문에서 93점, 퇴직률이 절반으로 줄었습니다.' }, +]; + +const steps = [ + { num: '01', title: '무료 상담', desc: '공간 사진, 예산, 취향을 공유해 주세요. 72시간 내 맞춤 제안서를 드립니다.' }, + { num: '02', title: '콘셉트 기획', desc: '무드보드와 3D 시뮬레이션으로 완공 이후를 미리 경험합니다.' }, + { num: '03', title: '시공', desc: '전담 PM이 공정마다 현장을 점검하고 일일 리포트를 공유합니다.' }, + { num: '04', title: '준공 & AS', desc: '완공 후 1년간 무상 AS. 공간이 오래 아름답도록 함께합니다.' }, +]; + +/* ══════════════════════════════════════════════ + SVG ICONS +══════════════════════════════════════════════ */ +const StarIcon = ({ filled }: { filled: boolean }) => ( + + + +); +const CheckIcon = () => ( + + + + +); +const ArrowRight = ({ color = '#8B6914' }: { color?: string }) => ( + + + +); + +/* ══════════════════════════════════════════════ + CONSTANTS +══════════════════════════════════════════════ */ +const BANNER_H = 40; +const NAV_H = 72; + +/* ══════════════════════════════════════════════ + PAGE COMPONENT +══════════════════════════════════════════════ */ +export default function InteriorSample() { + const [scrolled, setScrolled] = useState(false); + const [showTop, setShowTop] = useState(false); + + useEffect(() => { + const scroller: HTMLElement = + (document.querySelector('.main-content') as HTMLElement | null) ?? + document.documentElement; + + const onNavScroll = () => { + setScrolled(scroller.scrollTop > 60); + setShowTop(scroller.scrollTop > 400); + }; + scroller.addEventListener('scroll', onNavScroll, { passive: true }); + + const scrollToTop = () => scroller.scrollTo({ top: 0, behavior: 'smooth' }); + const topBtn = document.getElementById('au-top-btn'); + if (topBtn) topBtn.addEventListener('click', scrollToTop); + + const observer = new IntersectionObserver( + (entries) => entries.forEach((e) => { if (e.isIntersecting) e.target.classList.add('au-visible'); }), + { threshold: 0.08, root: scroller === document.documentElement ? null : scroller } + ); + document.querySelectorAll('.au-reveal').forEach((el) => observer.observe(el)); + + return () => { + scroller.removeEventListener('scroll', onNavScroll); + if (topBtn) topBtn.removeEventListener('click', scrollToTop); + observer.disconnect(); + }; + }, []); + + const GOLD = '#8B6914'; + const DARK = '#1C1A17'; + const SAGE = '#4E5C3E'; + const CREAM = '#FAF8F5'; + const SURFACE = '#F0ECE4'; + + return ( +
+ + {/* ══ 폰트 + 전역 CSS ══ */} + + + {/* Back Banner */} +
+ + ← 홈페이지 제작 서비스로 돌아가기 + + | + + SAMPLE · 개인 포트폴리오 + +
+ + {/* Awards Marquee */} +
+
+ {[...awards, ...awards].map((a, i) => ( +
+ + {a.name} + {a.count} +
+ ))} +
+
+ + {/* Navbar */} + + + {/* Hero */} +
+
+
+ +
+
+
+ {'> Hello, World. I am'} +
+

+ Kim
+ Jisu + +

+
+ Product Designer & Creative Developer +
+

+ 픽셀 하나하나에 의미를 담는 디자이너. 아름다움과 기능의 교차점에서 디지털 경험을 설계합니다. +

+
+ + +
+
+ + {/* Stats panel */} +
+ {[ + { val: '6+', label: 'YEARS EXP.', color: '#00ff88' }, + { val: '30+', label: 'PROJECTS', color: '#a855f7' }, + { val: '2×', label: 'AWWWARDS', color: '#f59e0b' }, + { val: '340%', label: 'MAX CVR LIFT', color: '#06b6d4' }, + ].map((s) => ( +
+
{s.val}
+
{s.label}
+
+ ))} +
+
+
+ + {/* Services */} +
+
+
+
// WHAT I DO
+

+ Services. +

+
+
+ {services.map((svc) => ( +
+
+
+ {svc.icon} +
+
{svc.title}
+
{svc.desc}
+
+ ))} +
+
+
+ + {/* Awards Detail */} +
+
+
+ {awards.map((a) => ( +
+ +
+
{a.name}
+
{a.count}
+
+
+ ))} +
+
+
+ + {/* Projects */} +
+
+
+
+
// SELECTED WORK
+

+ Projects. +

+
+ 2019 — 2024 +
+ {/* Category filter */} +
+ {categories.map((cat) => ( + + ))} +
+
+ {filteredProjects.map((proj, i) => ( +
setHoveredProject(i)} + onMouseLeave={() => setHoveredProject(null)} + style={{ border: '1px solid #111827', borderRadius: 12, overflow: 'hidden', cursor: 'pointer', background: '#0a0a0a', position: 'relative' }} + > +
+
+
{proj.title}
+
+ {/* Hover overlay */} +
+
{proj.desc}
+ +
+
+ {proj.tag} +
+
+ {proj.year} +
+
+
+
{proj.title}
+
{proj.desc}
+
+
+ ))} +
+
+
+ + {/* Skills + Timeline */} +
+
+
+
// EXPERTISE
+

+ Skills. +

+
+ {skills.map((s) => ( +
+
+
+ {s.name} + {s.category} +
+ {s.level}% +
+
+
+
+
+ ))} +
+
+
+
// JOURNEY
+

+ Timeline. +

+
+
+ {timeline.map((t, i) => ( +
+
+
{t.year}
+
{t.event}
+ {t.type === 'award' && ( +
+ AWARD +
+ )} +
+ ))} +
+
+
+
+ + {/* Testimonials */} +
+
+
// CLIENT VOICES
+

+ Testimonials. +

+
+ {testimonials.map((t) => ( +
+ + + +

{t.text}

+
+
+ {t.client[0]} +
+
+
{t.client}
+
{t.role}
+
+
+
+ ))} +
+
+
+ + {/* Contact */} +
+
+ {'> LET\'S COLLABORATE'} +
+

+ Have a project? +

+

+ jisu.kim@design.studio +

+

+ @jisu_creates · Response within 24h +

+ +
+
+ ); +} diff --git a/app/work/website/samples/reading/page.tsx b/app/work/website/samples/reading/page.tsx new file mode 100644 index 0000000..2575d53 --- /dev/null +++ b/app/work/website/samples/reading/page.tsx @@ -0,0 +1,663 @@ +'use client'; + +import Link from 'next/link'; +import { useEffect, useRef, useState } from 'react'; + +/* ═══════════════════════════════════════ + SVG ICONS +═══════════════════════════════════════ */ +const BookOpenIcon = () => ( + + + +); +const StarIcon = ({ filled = true }: { filled?: boolean }) => ( + + + +); +const QuoteIcon = () => ( + + + +); +const CheckIcon = () => ( + + + +); +const ArrowUpRight = () => ( + + + +); +const ChevronDown = () => ( + + + +); + +/* ═══════════════════════════════════════ + COUNTUP HOOK +═══════════════════════════════════════ */ +function useCountUp(target: number, duration = 1600) { + const [count, setCount] = useState(0); + const ref = useRef(null); + + useEffect(() => { + const el = ref.current; + if (!el) return; + const obs = new IntersectionObserver( + ([entry]) => { + if (!entry.isIntersecting) return; + obs.disconnect(); + const start = performance.now(); + const tick = (now: number) => { + const elapsed = now - start; + const progress = Math.min(elapsed / duration, 1); + const ease = 1 - Math.pow(1 - progress, 3); + setCount(Math.round(target * ease)); + if (progress < 1) requestAnimationFrame(tick); + }; + requestAnimationFrame(tick); + }, + { threshold: 0.4 } + ); + obs.observe(el); + return () => obs.disconnect(); + }, [target, duration]); + + return { count, ref }; +} + +/* ═══════════════════════════════════════ + STAT ITEM +═══════════════════════════════════════ */ +function StatItem({ value, suffix, label }: { value: number; suffix?: string; label: string }) { + const { count, ref } = useCountUp(value); + return ( +
+
+ {count} + {suffix && {suffix}} +
+

{label}

+
+ ); +} + +/* ═══════════════════════════════════════ + BOOK CARD +═══════════════════════════════════════ */ +interface Book { + seed: string; title: string; author: string; rating: number; genre: string; year: number; note?: string; +} + +const books: Book[] = [ + { seed: 'book1', title: '파친코', author: '이민진', rating: 5, genre: '소설', year: 2024, note: '역사 속에 묻힌 삶의 무게를 느꼈다' }, + { seed: 'book2', title: '어린 왕자', author: '생텍쥐페리', rating: 5, genre: '고전', year: 2023 }, + { seed: 'book3', title: '원씽', author: '게리 켈러', rating: 4, genre: '자기계발', year: 2024 }, + { seed: 'book4', title: '채식주의자', author: '한강', rating: 5, genre: '소설', year: 2023, note: '불편함 속의 아름다움' }, + { seed: 'book5', title: '지능의 본질', author: '제프 호킨스', rating: 4, genre: '과학', year: 2024 }, + { seed: 'book6', title: '82년생 김지영', author: '조남주', rating: 4, genre: '소설', year: 2022 }, + { seed: 'book7', title: '노인과 바다', author: '헤밍웨이', rating: 5, genre: '고전', year: 2022 }, + { seed: 'book8', title: '생각에 관한 생각', author: '다니엘 카너먼', rating: 4, genre: '심리학', year: 2023 }, +]; + +const genreColors: Record = { + '소설': '#6B5B95', '고전': '#D4A853', '자기계발': '#4A7C59', '과학': '#2E6EA6', '심리학': '#8B4E62' +}; + +function BookCard({ book, large = false }: { book: Book; large?: boolean }) { + return ( +
{ + (e.currentTarget as HTMLElement).style.transform = 'translateY(-6px)'; + (e.currentTarget as HTMLElement).style.boxShadow = '0 20px 60px rgba(212,168,83,0.12)'; + }} + onMouseLeave={e => { + (e.currentTarget as HTMLElement).style.transform = 'translateY(0)'; + (e.currentTarget as HTMLElement).style.boxShadow = 'none'; + }} + > + {/* cover */} +
+ {book.title} +
+ {/* genre badge */} +
{book.genre}
+ {/* rating */} +
+ {[1,2,3,4,5].map(i => )} +
+
+ {/* info */} +
+

{book.author} · {book.year}

+

{book.title}

+ {book.note &&

"{book.note}"

} +
+
+ ); +} + +/* ═══════════════════════════════════════ + MAIN PAGE +═══════════════════════════════════════ */ +export default function ReadingPage() { + const [activeGenre, setActiveGenre] = useState('전체'); + const [showTop, setShowTop] = useState(false); + + useEffect(() => { + const scroller: HTMLElement = + (document.querySelector('.main-content') as HTMLElement | null) ?? + document.documentElement; + + const onScroll = () => setShowTop(scroller.scrollTop > 400); + scroller.addEventListener('scroll', onScroll, { passive: true }); + + const obs = new IntersectionObserver( + entries => entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('rd-visible'); }), + { threshold: 0.1, root: scroller === document.documentElement ? null : scroller } + ); + document.querySelectorAll('.rd-reveal').forEach(el => obs.observe(el)); + return () => { + scroller.removeEventListener('scroll', onScroll); + obs.disconnect(); + }; + }, []); + + const genres = ['전체', '소설', '고전', '자기계발', '과학', '심리학']; + const filteredBooks = activeGenre === '전체' ? books : books.filter(b => b.genre === activeGenre); + + const quotes = [ + { text: '당신이 무엇을 읽느냐가 당신이 누구인지를 말해준다. 책은 당신이 선택한 삶의 궤적이다.', author: '파친코 — 이민진', genre: '소설' }, + { text: '어른들은 숫자를 좋아한다. 새 친구 이야기를 해줄 때, 중요한 것을 절대 묻지 않는다. 그의 목소리가 어떤지, 어떤 놀이를 좋아하는지.', author: '어린 왕자 — 생텍쥐페리', genre: '고전' }, + { text: '성공한 사람들은 하나에 집중하고 나머지를 내려놓는 법을 배웠다. 한 가지를 잘하면 나머지가 따라온다.', author: '원씽 — 게리 켈러', genre: '자기계발' }, + { text: '빠른 생각은 직관이고 느린 생각은 이성이다. 우리는 대부분 빠른 생각이 옳다고 착각하며 살아간다.', author: '생각에 관한 생각 — 다니엘 카너먼', genre: '심리학' }, + ]; + + const currentlyReading = { + seed: 'current1', title: '도둑맞은 집중력', author: '요한 하리', genre: '자기계발', + progress: 67, totalPages: 380, currentPage: 255, + startDate: '2024.03.10', note: '현대 사회가 어떻게 우리의 집중력을 앗아가는지 설득력 있게 풀어냄' + }; + + const tbr = [ + { seed: 'tbr1', title: '총, 균, 쇠', author: '재레드 다이아몬드', genre: '역사', priority: '높음' }, + { seed: 'tbr2', title: '코스모스', author: '칼 세이건', genre: '과학', priority: '높음' }, + { seed: 'tbr3', title: '사피엔스', author: '유발 하라리', genre: '역사', priority: '중간' }, + ]; + + const monthlyData = [ + { month: '10월', books: 2 }, { month: '11월', books: 3 }, { month: '12월', books: 1 }, + { month: '1월', books: 4 }, { month: '2월', books: 3 }, { month: '3월', books: 2 }, + ]; + const maxBooks = Math.max(...monthlyData.map(d => d.books)); + + return ( + <> +