FEInterview Prep

GitHub Pages

내 생애 가장 자랑스러운 128킬로바이트

아프리카 피처폰을 타깃으로 한 프로젝트에서 전체 페이지 예산을 128KB 로 잡은 극단적 성능 제약 사례. 시스템 폰트, 자체 라이브러리, SVG 최적화로 어떻게 빡빡한 제약을 더 나은 디자인으로 풀어냈는지 보여준다.

2025-08-29·9분 읽기
성능커리어
원문 보기 ↗

핵심 요약

디자인은 "제약 안에서 문제를 푸는 일"이며, 빡빡한 제약이 종종 더 좋은 해를 끌어낸다. 이 글은 EDGE 회선·피처폰·240px 화면을 대상으로 한 프로젝트에서 페이지 전체 예산을 128KB 로 잡고, 각 자원(폰트·JS·이미지)을 바이트 단위로 다시 평가한 경험을 정리한다. 시스템 폰트로 FOUT 제거, Whizz 라는 미니 라이브러리 자작, AJAX 부분 페이지 갱신, SVG 수기 최적화처럼 "왜 안 쓰는가"를 먼저 묻는 결정 패턴이 핵심이다.

성능 최적화는 "빠르게 만드는 기술"이 아니라 제약을 디자인 도구로 사용하는 사고법이다. 한 페이지에 128KB 라는 숫자가 정해지면, 의사결정의 우선순위가 자동으로 정렬된다 — 웹폰트를 버릴지, 프레임워크를 쓸지, 이미지 포맷을 어떻게 고를지가 "취향"이 아니라 "수학"이 된다.

면접에서 "성능 최적화"를 묻는 의도는 라이브러리 이름 나열이 아니라 트레이드오프 판단력이다. 이 글은 빡빡한 예산이 어떻게 결정 트리를 단순화하고, 흔히 "좋은 관행"이라 부르는 기능(웹폰트, 가상 DOM, 라이브러리 사용)을 다시 묻게 만드는지를 보여준다. 답변에 측정·예산·대안 비교가 들어가야 깊이가 생긴다.

학습 포인트

면접 답변으로 연결할 학습 포인트입니다.

성능 예산은 의사결정의 룰러다

한 페이지 전체를 128KB 로 묶는 식의 하드 예산을 먼저 정하면, 라이브러리·이미지·폰트 선택이 의견 싸움이 아니라 비용 계산이 된다. 예산 없는 "최적화"는 어디까지 가야 하는지 모르는 상태에서 도구만 추가하는 일에 가깝다.

page budgetperformance budgetEDGE콜드 캐시
자주 하는 오해

예산을 정하지 않은 채 Lighthouse 점수만 올리려 하면, 항목별 트레이드오프(예: 폰트 vs 이미지)에서 매번 결정이 흔들린다.

시스템 폰트는 0바이트짜리 큰 승리다

웹폰트를 포기하고 OS 시스템 폰트(-apple-system, Roboto, Segoe UI...)를 쓰면 FOUT 제거, 글리프 풍부, 0바이트 비용 세 가지를 동시에 얻는다. 브랜드 폰트가 정말 필요한지 먼저 묻는 게 모든 폰트 최적화의 출발점이다.

FOUTsystem font stackfont-displayfont subsetting
자주 하는 오해

font-display: swap 만 붙이면 끝났다고 생각하기. 본질은 "이 폰트가 정말 페이지 예산을 쓸 가치가 있는가"이며, 답이 No면 시스템 폰트가 가장 빠르다.

프레임워크 비용을 "바이트"로 본다

React/Angular 류는 애플리케이션 코드 한 줄을 쓰기도 전에 예산을 다 써버릴 수 있다. 이 프로젝트는 DOM 쿼리·이벤트·AJAX 만 감싸는 자작 라이브러리 Whizz 와 부분 HTML 교체로 SPA 같은 UX를 구현했다. 코드 분량이 곧 부채라는 관점.

framework costtree shakingprogressive enhancementX-Whizz 헤더
자주 하는 오해

"React 정도는 다 쓰니까 무료"라고 가정하기. 저전력 디바이스/저속망에서는 파싱·실행 시간 자체가 LCP를 늘리는 주범이다.

이미지 최적화는 포맷·크기·트레이드오프 전쟁

PNG는 TinyPNG(Tinify), JPEG는 "두 배 크기 + 품질 0"으로 내보낸 뒤 절반 크기로 축소하는 트릭, 그리고 가능한 곳은 SVG 로 치환. 단, SVG 는 Illustrator/Inkscape 메타데이터·중첩 그룹·과한 좌표 정밀도를 수기로 정리해야 "500B"가 "5KB"로 부풀지 않는다.

TinyPNGMozJPEGSVGO<picture> fallback
자주 하는 오해

도구 한 번만 돌리고 끝내기. 진짜 절감은 디자이너와 함께 path 합치기·정밀도 줄이기를 반복할 때 나온다.

읽는 순서

  1. 1이론

    Lighthouse·WebPageTest·webpack-bundle-analyzer 의 측정 지표(LCP, FID/INP, JS 크기, 이미지 크기)와 의미를 정리한다.

  2. 2구현

    샘플 앱에서 (1) momentdayjs 교체, (2) 라우트 단위 React.lazy, (3) <picture> + WebP/AVIF 폴백, (4) 시스템 폰트 스택 적용을 직접 해보고 번들 크기 변화를 기록한다.

  3. 3실무

    사내 서비스에 성능 예산(예: 초기 JS 200KB, LCP < 2.5s)을 PR 체크로 강제한다. PR마다 bundlesize / Lighthouse CI로 회귀 차단.

  4. 4설명

    "왜 이 라이브러리를 안 썼는가"를 논리적으로 발표할 수 있도록 정리. 트레이드오프 표(기능 vs 바이트 vs 대안)를 만들면 면접 답변이 단단해진다.

면접 연결 질문

medium프론트엔드 성능 최적화를 어떻게 접근하는지 단계별로 설명해주세요.
힌트

[감점 답변] "코드 스플리팅·트리 쉐이킹·이미지 최적화"처럼 기법만 나열. [좋은 답변] (1) 측정: Lighthouse·WebPageTest·webpack-bundle-analyzer 로 현황 파악, (2) 예산 설정: LCP/JS 크기/CLS 같은 수치 목표, (3) 큰 비용부터: 프레임워크/폰트/이미지 순으로 비용을 평가하고 "빼는 결정"을 먼저 시도, (4) 트레이드오프 명시: "이 라이브러리를 빼면 X 기능을 잃지만 LCP가 Y초 줄어든다"식. 이 글의 128KB 예산 사례를 인용하면 임팩트가 커진다.

medium코드 스플리팅과 트리 셰이킹의 차이, 그리고 둘이 동작하지 않는 경우를 설명해주세요.
힌트

[감점 답변] 둘을 비슷한 개념으로 섞어 설명. [좋은 답변] 코드 스플리팅은 "필요할 때만 다운로드"(라우트별 청크, React.lazy, 동적 import()), 트리 셰이킹은 "빌드 타임에 안 쓰는 export 제거". 안 되는 경우: CommonJS(require)는 동적이라 정적 분석 불가, package.jsonsideEffects: false 미선언, ESM이라도 외부에서 함수 본문 안의 분기를 못 잘라낼 때. moment.js → dayjs, 로케일 강제 포함을 IgnorePlugin 으로 제거 같은 사례.

hard웹폰트를 안 쓰는 게 성능에 어떤 차이를 만드나요? `font-display` 옵션과 비교해서요.
힌트

[감점 답변] "폰트 안 쓰면 빠르다" 한 줄. [좋은 답변] 다운로드 비용 0(보통 폰트 1개 = 30~80KB), FOUT/FOIT 자체 제거, OS별 최적화된 글리프 사용. font-display: swap 은 폰트가 늦게 와도 텍스트를 먼저 그려주지만 결국 한 번 더 리렌더(레이아웃 시프트 위험). 반대로 시스템 폰트 스택은 디바이스마다 모양이 살짝 달라지는 것을 받아들여야 한다 — 이 트레이드오프를 비교해서 답하면 깊이가 생긴다.

자기 점검

왜 "극단적인 성능 예산"이 더 나은 디자인을 만드는지 한 문장으로 설명해보세요.
제약예산트레이드오프의사결정
자주 하는 오해

"무조건 빼는 게 좋다"고 단순화하면 비즈니스 가치 있는 기능까지 잘라낸다. 핵심은 "비용/효용을 명시적으로 수치화"하는 데 있다.

`React.lazy` + `Suspense` 의 코드 스플리팅이 성능에 "항상" 도움이 되는지 답해보세요.
라우트 분할초기 번들워터폴프리로드
자주 하는 오해

스플리팅을 무조건 잘게 쪼갤수록 좋다고 생각하는 것. 청크가 너무 잘게 쪼개지면 HTTP 요청이 늘고, 페이지 진입 직후 모두 필요한 코드라면 오히려 느려진다.