JOA HAIR
클라이언트의 의뢰를 받아, 실제 운영 중인 헤어살롱의 브랜드 홍보와 신규 고객 유치를 목표로 제작한 헤어살롱 마케팅 웹사이트입니다. 클라이언트의 요구사항을 분석하고, 이에 맞춰 프로젝트를 기획하고 개발하며, 실무 능력을 향상시켰습니다. 주요 기능으로, Instagram 계정의 실시간 게시물 전시, 캐러셀, 아코디언, 그리드 등 인터랙티브한 컴포넌트 등이 포함되어 있습니다.
개요
프로젝트 기간
2024/05 ~ 운영 중
역할
FE 개발 & 기획·디자인·BE(서버리스)·인프라 등 전 과정 경험
팀 / 솔로
솔로
기술 스택
React, TypeScript, webpack, Babel, styled-components, TanStack Query, Storybook, GitHub Actions, AWS(S3, CloudFront, Route 53), Firebase(Firestore, Cloud Functions)
Preview















사용자 중심의 UI/UX 개선
Google Analytics을 통한 사용자 분석 및 개선


- 서비스 사용자의 약 95%가 호주에 집중된 상황을 인식했습니다. 따라서, AWS 인프라와 Firebase 리전을 호주로 설정하고, CDN(AWS CloudFront)을 구성하여 글로벌 서비스의 로딩 속도를 최적화했습니다.
- 모바일 사용자가 약 85%임을 감안하여 모든 디바이스에 대응하는 100% 반응형 웹을 구현했습니다.
전반적인 GA 사용자 데이터의 개선을 위한 UI/UX 개선 작업
- 비즈니스 및 웹사이트 성격에 어울리는 미적 디자인 및 색 적용(보기에 좋고, 화려한 색감) 및 직관적이고 심플한 웹사이트 메뉴 및 UI 구성
- 사용성(자연스럽고, 직관적인)을 고려한 캐러셀, 아코디언, 그리드, 헤더 오버레이 등 인터랙티브한 컴포넌트들 구현
- 디자인 시스템(스페이싱, 색, 배치 등)을 적용해 웹사이트 디자인의 일관성 향상
- 성능 개선(이미지, webpack)
- 그 외
- 인스타그램 게시물 로딩 중일 때, 사용자가 추가 로드 버튼을 누르면 요청을 방지함으로써 버그를 예방
- 애니메이션 구현(transition, keyframes)
성과



- 고유 방문자 수 7700+명(25/08/12 기준)
- 검색 엔진 총 노출 수 44800+회(25/08/12 기준)
- 평균 참여 시간 30초 -> 55초
- SEO 향상
- 최근 3달 동안, 사용자 획득의 전체 2,140건 중, 직접 검색(Organic Search)이 1,036로 가장 높음(전체의 48.41%)
Compound 패턴을 활용한 UI 컴포넌트 설계

Compound 패턴을 적용한 Accordion 컴포넌트의 실제 프로젝트 내 사용 예시
- 아코디언, 캐러셀 등 단순하지 않은 컴포넌트를 처음으로 구현해야하는 상황에서, 컴포넌트 내 복잡한 상호작용을 구현하는 데에 어려움을 느꼈습니다.
- 조금 더 쉽고, 명확한 방식으로 컴포넌트를 구현하기 위해, 컴포넌트들을 역할별로 여러 개로 나누어 컴포넌트의 유연성과 유지보수성을 높이는 Compound 패턴을 도입했습니다.
- 구현 포인트
- ContextAPI를 이용해 모든 컴포넌트에 상태를 공유
- 컴포넌트 설계시 children props를 사용해 구조를 고정적으로 정하지 않음
- 내부 컴포넌트들을 최상위 컴포넌트의 프로퍼티로 등록해, 모든 컴포넌트를 import 할 필요 없이 Accordion.Header, Accordion.Body처럼 직관적으로 사용하는 방식 적용
- 결과
- 각 컴포넌트들이 하는 일이 명확해짐에 따라 코드 가독성 향상 -> 유지보수성 향상
- children props 사용으로 구조를 고정하지 않기 때문에 컴포넌트를 다양한 패턴으로 재사용할 수 있어 유연성이 높아짐
Tanstack Query 마이그레이션

마이그레이션 전 요청 로직

마이그레이션 후 요청 로직
- 개발 초기에는 캐싱, 요청 빈도, 로직 복잡성 등을 전혀 고려하지 않은 데이터 요청 로직을 사용하고 있었습니다.
- 따라서, 캐싱을 활용할 수 있고, 요청 빈도를 데이터의 변경 주기를 기반으로 제어할 수 있으며, 로직의 복잡성과 코드 수를 줄이는 데 도움이 되는 Tanstack-Query를 학습 차원에서 도입하게 되었습니다.
- 구현 포인트
- 메인페이지의 정적인 인스타그램 포스트에 대해서는 useQuery를, 사용자가 load more 버튼을 누를 때 마다, 인스타그램 포스트가 추가되는 갤러리페이지의 동적 데이터에 대해서는 useInfiniteQuery 활용
- 서버 측에서 변경될 가능성이 적은 메인페이지의 정적 데이터는 한 세션 내에서 재요청이 불필요하다고 판단하여, staleTime을 Infinity로, gcTime은 60분으로 설정
- 자주 변경될 가능성이 높고, 매끄러운 UX를 위해 페이지 재방문마다 초기화가 필요한 갤러리페이지의 동적 데이터에 대해서는 staleTime은 그대로, gcTime은 0분으로 설정
- 결과
- Tanstack-Query를 이용한 코드가 기존의 코드보다 더 많은 기능을 하지만, 로직 복잡성과, 코드 수가 눈에 띄게 줄어 듦(로딩, 에러 등 상태 수동 제어 불필요)
- staleTime과 gcTime 조정을 통해, 불필요한 요청을 줄일 수 있게 됨
- 재요청 간, 캐싱되어 있던 이전 데이터 활용을 통해 자연스러운 화면 전환이 이루어짐
웹사이트 성능 최적화
이미지 최적화

이미지 Preload 적용 전

이미지 Preload 적용 후
webpack 최적화

개발 환경에서의 번들 크기(5.77MB)

최적화 후 최종 번들 크기(99.6KB + 27.7KB)
- Webpack 공식 문서를 기반으로 Code Splitting, Tree Shaking, Minification을 학습·적용
- CloudFront CDN 압축 설정을 적용해 프로덕션 환경에서 번들 크기를 추가로 축소