본문으로 건너뛰기
Hyunki.devContact
COINTEL 앱 화면 세 개를 나란히 배치한 키 비주얼

Work

암호화폐 데이터 분석 및 커뮤니티 플랫폼

Technical SkillsNext.jsJavaScriptMUIEmotionSWRZustandnext-intl
Period2024.01 — 2026.03
RoleFrontend Engineer
OrgMVerse

외주 개발 이후 인수한 수만 줄 규모의 레거시 코드를 리팩터링하고, 컴포넌트·커스텀 훅 공통화부터 디자인 시스템·다국어·성능·SEO까지 프론트엔드 전반을 개선한 프로젝트입니다.

프로젝트 설명

암호화폐 지갑·AI 분석·시장 데이터·뉴스·차트·커뮤니티 기능을 제공하는 글로벌 암호화폐 인텔리전스 플랫폼

담당 및 구현

  • 레거시 코드의 중복 로직을 컴포넌트와 커스텀 훅으로 분리해 재사용 가능한 구조로 개선
  • MUI 커스텀 테마와 variant 패턴을 기반으로 디자인 시스템 구축
  • 글로벌 서비스 확장 — i18n 기반 locale 라우팅 및 KRW/USD/JPY 통화 전환 구현
  • 이미지 로딩과 렌더링 구조를 개선해 LCP·FCP·TBT 등 주요 Lighthouse 성능 지표 향상 및 CLS 안정화
  • env-cmd를 도입해 환경별 빌드 프로세스와 배포 스크립트 분리
  • Flutter 모바일 앱의 웹뷰 연동 및 브릿지 통신 로직 구현

게시판 공통 구조 재설계

게시판별 CRUD와 예외 케이스를 전수 검사한 뒤, 공통 UI·데이터 조회·페이지네이션을 컴포넌트와 훅으로 통합했습니다. 게시판별 차이는 props로 분리하고 URL의 pageIndex와 API offset을 동기화했습니다. 공통 구조를 조합해 게시판 8종 → 19종 확장에 대응했으며, 페이지네이션 관련 19개 파일에서 중복 코드 560줄을 순감했습니다.

디자인 시스템

디자이너에게 스타일 가이드 정립을 제안하고, 색상·타이포그래피·간격·Variant를 MUI 테마의 디자인 토큰으로 중앙화했습니다. 4차례 대규모 리스킨에 대응하며 색상·타이포·간격 중심 리스킨의 구현 기간을 1주 이상 → 3일 내외로 줄였습니다. 전체 화면 검수를 포함하면 1주 이내였으며, 레이아웃·반응형 구조 변경은 별도로 구현했습니다.

SEO · 검색 노출 개선

검색 결과에 서비스가 노출되지 않는 문제를 해결하기 위해 Google Search Console에 사이트를 등록하고, Next.js의 페이지별 메타데이터와 사이트맵을 정비했습니다. 검색 엔진의 수집과 색인을 위한 기반을 정비했습니다.

색인되지 않는 페이지를 추적하는 과정에서 빌드 결과가 정상 화면 대신 에러 페이지를 반환하며 noindex가 적용되는 문제도 발견했습니다. useSearchParams를 Suspense로 감싸 빌드 오류를 해결했습니다. Next.js 빌드 파일 에러 해결 경험

다국어·다통화 글로벌 서비스 확장

영어·달러 중심으로 운영되던 서비스를 한국어·영어·일본어와 원·달러·엔화를 지원하도록 확장했습니다. 단순 번역에 그치지 않고 언어와 통화 변경이 URL·검색 조건·API 요청·가격 표시까지 일관되게 이어지도록 전체 사용자 흐름을 설계했습니다.

next-intl을 기반으로 locale 라우팅과 번역 메시지를 구조화하고, 언어를 변경해도 사용자가 보고 있던 페이지와 검색 조건이 유지되도록 구현했습니다. 통화 선택값은 쿠키와 API 요청에 연결해 사용자가 선택한 통화가 API 요청부터 화면의 가격 표시까지 동일하게 적용되도록 처리했습니다. i18n 적용 과정 · 다국어 메시지 관리 과정

Flutter InAppWebView 브릿지 연동

Flutter 앱에서 웹 서비스를 제공하기 위해 InAppWebView를 연동하고, 웹과 앱 사이의 인증 상태를 동기화하는 양방향 브릿지 통신을 구현했습니다. 웹의 window 객체에 onAppLogin·onAppLogout 콜백을 등록해 앱의 로그인 상태를 전달받고, 웹에서는 로그인 완료 후 기존 페이지로 돌아갈 수 있도록 redirectPath를 앱에 전달했습니다.

iOS WebView의 특정 경로에서만 인증 API가 실패해 웹 Axios·Flutter·AWS CloudWatch 로그를 교차 확인했습니다. OPTIONS는 서버에 도달하지만 본 요청은 도달하지 않는 점으로 WKWebView의 네트워크·쿠키 정책 문제를 특정했습니다. 앱 담당자와 쿠키 공유·App-Bound Domain·ATS 설정을 조정해 인증 실패를 해결했습니다.

초기 번들과 레이아웃 안정화

Bundle Analyzer로 미사용 SVG가 포함되는 re-export 구조를 찾아 개별 경로 import로 전환하고, 초기 화면에 불필요한 컴포넌트는 dynamic import로 분리했습니다. 주요 페이지 청크 JS 파싱 크기는 19.16KB → 3.59KB로 줄었습니다. App Router용 SSR 스타일 캐시, grid와 플레이스홀더로 영역을 확보해 CLS 0.23 → 0.02로 개선했습니다.

초기 로딩 웹폰트 경량화

  • 문제 — 기존 Noto Sans KR과 Inter를 .ttf 파일로 제공하고 있었습니다. 폰트 리소스 용량이 3.0MB에 달했고, 초기 로딩 시 다운로드에 307ms가 소요되면서 텍스트가 늦게 표시되는 문제가 있었습니다.
  • 원인 — 용량이 큰 .ttf 파일을 웹폰트로 그대로 사용하고 있었고, 여러 weight의 폰트 파일을 각각 내려받으면서 초기 로딩 시 폰트 리소스의 부담이 커지고 있었습니다.
  • 해결 — 디자인 개편으로 기본 폰트가 Pretendard로 변경되는 시점에 웹폰트 로딩 방식도 함께 개선했습니다. 기존 .ttf 대신 woff2 포맷과 서브셋을 적용해 다운로드 용량을 줄이고, font-display: swap을 적용해 폰트가 로드되는 동안에도 시스템 폰트로 텍스트를 먼저 표시하도록 했습니다. src 맨 앞에는 local()을 둬 이미 폰트가 설치된 사용자는 다운로드 자체를 건너뛰게 했습니다.
  • 결과 — 개별 폰트 파일 용량을 최대 3.0MB → 271KB로 약 91% 줄였고, 다운로드 시간은 최대 307ms → 16ms로 단축했습니다. 웹폰트 로딩 중에도 텍스트가 먼저 표시되도록 해 초기 화면의 텍스트 노출 지연을 개선했습니다.
최적화 후 @font-face 선언 (Pretendard Bold)
css
@font-face {
  font-family: 'Pretendard';
  font-weight: 700;
  font-display: swap;
  src: local('Pretendard Bold'),
       url('./woff2-subset/Pretendard-Bold.subset.woff2') format('woff2'),
       url('./woff-subset/Pretendard-Bold.subset.woff') format('woff');
}
크롬 네트워크 탭 비교 스크린샷. 위쪽은 NotoSansKR ttf 4개가 각각 3.0MB·307밀리초로 받아지고, 아래쪽은 Pretendard 서브셋 woff2 2개가 267kB·16밀리초로 받아진다
웹폰트 최적화 전후 네트워크 비교 — .ttf 4개에서 Pretendard 서브셋 .woff2 2개로 변경