30년 전통의 국내 원목 가구 브랜드 HDKD. 오랫동안 운영돼 온 PHP 기반 홈페이지를 React + TypeScript 기반의 반응형 웹사이트로 다시 지었습니다. 북유럽 미니멀 무드의 우드 톤 디자인, 스크롤 애니메이션, 문의 폼까지 — 한 페이지 랜딩 사이트로 리뉴얼한 과정을 공유합니다.
가구 브랜드의 웹사이트는 제품 사진 몇 장이 전부인 경우가 많습니다. HDKD도 예외는 아니었습니다. 상품 정보가 빽빽하게 늘어선 구식 레이아웃에 모바일에서는 글자가 잘리고, 브랜드가 가진 "30년 전통 · 장인 제작 · 천연 원목"이라는 매력이 전혀 드러나지 않았습니다. 브랜드의 품격을 그대로 전달하는 웹사이트로 새로 짓기로 했습니다.
기술 교체가 목적이 아니었습니다. "국내 장인이 직접 만드는 원목 가구"라는 브랜드 스토리를 어떻게 웹에서 들려줄까가 출발점이었습니다. 그래서 단순한 제품 나열 대신, 히어로 → 브랜드 가치 → 제품 → 회사 소개 → 문의로 이어지는 한 페이지 스토리텔링 구조를 선택했습니다. 사용자가 스크롤을 따라 자연스럽게 브랜드 이야기를 경험하게 하는 랜딩 페이지 방식입니다.
| 구성 | 내용 |
|---|---|
| 히어로 | 풀스크린 배경 이미지 + 브랜드 슬로건 |
| 특징 | 자체 제작, 천연 원목 소재, 평생 A/S |
| 제품 | 대표 가구 6종 카드 갤러리 |
| 회사소개 | 30년 역사 · 누적 200+ 제품 · 98% 재구매율 카운터 |
| 문의 | Web3Forms 연동 문의 폼 |
스택은 React + TypeScript + Vite를 사용했습니다. 한 페이지 규모라 상태 관리 라이브러리 없이 useState만으로 충분했습니다. 흥미로운 결정은 스타일 방식이었습니다. 별도의 CSS 파일 대신 CSS 변수와 인라인 스타일로 디자인 시스템을 구축했습니다. 브랜드 컬러 6개를 상수로 정의해 두고 컴포넌트 전체에서 참조하는 방식입니다.
// 브랜드 컬러 팔레트 — 모든 컴포넌트가 이 상수를 참조
const C = {
bg: '#FAFAF8',
wood: '#8B6F47', // 원목 브라운 (메인)
woodLight: '#C4A882',
text: '#1A1A1A',
muted: '#6B6B6B',
beige: '#F5EFE6', // 베이지 (서브)
}
원목 브라운 #8B6F47과 베이지 #F5EFE6를 중심으로 한 우드 톤 팔레트는 가구 브랜드의 정체성을 그대로 담아냅니다. 인라인 스타일이라 컴포넌트 파일 하나로 모든 스타일이 관리되는 것도 장점이었습니다.
사이트의 첫인상은 풀스크린 히어로가 결정합니다. 원목 가구 분위기의 배경 이미지 위에 어두운 그라디언트 오버레이를 깔고, 그 위에 슬로건을 올렸습니다. "자연에서 온 가구, 삶에 스며들다" — 여기서 woodLight 색상으로 강조한 부분이 브랜드 무드를 만듭니다.
히어로의 문구는 motion의 initial/animate로 부드럽게 페이드인 되고, 하단의 스크롤 인디케이터는 y: [0, 8, 0] 무한 반복 애니메이션으로 사용자의 시선을 아래로 유도합니다.
이 프로젝트에서 가장 재미있었던 부분은 스크롤 기반 애니메이션이었습니다. motion의 whileInView는 요소가 뷰포트에 들어오는 순간 애니메이션을 시작합니다. viewport={{ once: true }}를 함께 쓰면 사용자가 스크롤을 올렸다 내려도 애니메이션이 반복되지 않습니다.
<motion.div
initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: i * 0.12 }}
>
...
특징 카드에는 delay: i * 0.12를, 제품 카드에는 delay: (i % 3) * 0.1을 줘서 카드들이 순차적으로 나타나는 리듬을 만들었습니다. 같은 패턴을 반복하니 코드는 짧아지고 화면은 풍성해집니다.
회사소개 섹션에는 30년 역사 · 누적 200+ 제품 · 만족 고객 5,000+ · 재구매율 98%라는 신뢰 지표를 보여줍니다. 단순한 숫자 나열 대신, 사용자가 해당 영역을 보는 순간 0부터 실제 값까지 카운트업 되는 인터랙션을 넣었습니다.
function useCountUp(target: number, duration = 1800) {
const [count, setCount] = useState(0)
const ref = useRef<HTMLSpanElement>(null)
const inView = useInView(ref, { once: true })
useEffect(() => {
if (!inView) return
let start = 0
const step = (timestamp: number) => {
if (!start) start = timestamp
const progress = Math.min((timestamp - start) / duration, 1)
const eased = 1 - Math.pow(1 - progress, 3) // easeOutCubic
setCount(Math.round(eased * target))
if (progress < 1) requestAnimationFrame(step)
}
requestAnimationFrame(step)
}, [inView, target, duration])
return { count, ref }
}
useInView로 카운터가 화면에 들어온 시점을 감지하고, requestAnimationFrame으로 부드럽게 숫자를 올립니다. easeOutCubic 이징을 적용해 처음엔 빠르게, 끝으로 갈수록 천천히 증가하는 자연스러운 느낌을 줍니다.
제품 카드에는 두 가지 호버 효과를 넣었습니다. 카드 전체는 whileHover={{ y: -8 }}로 살짝 떠오르고, 카드 안의 이미지는 whileHover={{ scale: 1.06 }}로 부드럽게 확대됩니다. 이미지가 카드 영역을 넘치지 않도록 overflow: hidden으로 감싼 것이 포인트입니다.
각 카드에는 이름과 한 줄 설명, 별점 5개를 배치해 제품의 품질감을 암시적으로 전달했습니다. 실제 상품 데이터가 있다면 API 연동으로 바꾸면 되는 구조라 확장도 쉽습니다.
랜딩 페이지의 목적은 결국 문의 유도입니다. 백엔드 서버가 없는 환경이라 Web3Forms를 사용했습니다. 클라이언트에서 폼 데이터를 Web3Forms API로 POST하면 이메일로 전달되는 구조입니다.
폼에는 이름 · 연락처 · 문의 내용 세 필드를 두고, 전송 전 클라이언트 검증을 거칩니다. 오류가 있는 필드는 빨간 테두리와 함께 메시지를 표시하고, 전송 중에는 버튼이 "전송 중..."으로 바뀌며, 성공하면 체크 아이콘과 함께 완료 화면으로 전환됩니다. resize: none으로 textarea 크기 변형도 막았습니다.
가구 구매 문의는 대부분 모바일에서 들어옵니다. 네비게이션은 데스크톱에서 상단에 펼쳐지고, 모바일에서는 햄버거 메뉴로 접힙니다. 햄버거 아이콘은 세 줄이 X자로 변하는 CSS 애니메이션으로 직접 구현했습니다. 타이포그래피는 clamp() 함수로 뷰포트 크기에 따라 유동적으로 조절되어 320px 폰에서도 제목이 잘리지 않습니다.
이 프로젝트에서 배운 것은, 웹사이트 리뉴얼의 완성도는 기술보다 디자인 결정과 스토리텔링이 좌우한다는 점입니다. 브랜드가 가진 이야기를 명확히 하고, 그 이야기를 일관된 디자인 언어로 전달하면, 어떤 스택을 쓰든 좋은 결과물이 나옵니다.