채용공고 1순위가 성능 최적화인데, 내 라이브러리엔 React.memo가 0개였어요

HANUI·
성능최적화프론트엔드채용React커리어

프론트엔드 성능 측정 시리즈

(2편)
  1. 1.채용공고 1순위가 성능 최적화인데, 내 라이브러리엔 React.memo가 0개였어요
  2. 2.프론트엔드 성능 측정 도구 지도 — 어떤 질문에 어떤 도구를 쓰나

프론트엔드 채용공고를 쭉 보다 보면 눈에 띄는 게 있어요. 주요 업무 맨 위 칸이 거의 항상 이거예요.

  • 웹 서비스 성능 최적화 및 개선
  • 사용자 경험 개선을 위한 렌더링 최적화

첫 줄이에요. 두 번째, 세 번째도 아니고요.

그런데 이력서 쪽을 보면 대응되는 문장이 대개 이렇게 적혀 있어요.

React.memo, useMemo, useCallback을 활용한 렌더링 최적화 경험

기업은 결과를 물었는데, 답은 API 이름으로 오고 있어요. 여기에 뭔가 어긋난 게 있다는 생각이 계속 들었는데, 마침 제 저장소로 확인해볼 수 있겠더라고요.

제 라이브러리를 세어봤어요

hanui는 KRDS 기반 컴포넌트 라이브러리예요. 컴포넌트 104개, 블록 42개. 성능 얘기를 블로그에 여러 번 썼고요.

거기서 최적화 API를 몇 번이나 썼는지 세어봤어요.

API사용 파일 수
React.memo0
useCallback16
useMemo8
React.lazy0
useTransition0
useDeferredValue0

React.memo0개예요. 146개 파일에서요.

0이 문제가 아니라, 16이 문제예요

처음엔 "memo를 안 썼구나" 싶었는데, 다시 보니 이상한 건 그게 아니었어요.

useCallback이 16개인데 React.memo가 0개예요.

useCallback이 하는 일은 함수의 참조를 유지하는 것이에요. 그게 왜 필요하냐면, 새 함수가 매번 만들어지면 그걸 prop으로 받는 자식이 "prop이 바뀌었네" 하고 다시 그리거든요. 그런데 그 자식이 memo로 감싸져 있지 않으면, 어차피 부모가 그려질 때 같이 그려져요. 참조가 같든 다르든요.

memo가 하나도 없는 상태에서 useCallback으로 참조를 지켜봐야, 그걸로 막을 리렌더가 없어요.

물론 useCallback이 정당한 경우도 있어요. useEffect 의존성 배열에 넣어야 해서 참조를 안정시키는 경우요. 그래서 갈라봤어요.

10개예요. 이 10개는 비용은 치르고 효과는 못 받고 있을 가능성이 높아요. useCallback도 공짜가 아니거든요. 의존성 배열을 비교해야 하고, 클로저를 붙들고 있어야 해요.

정확히 말하면 "이 10개가 낭비다"라고 단정하는 게 아니에요. 라이브러리라서 밖으로 넘기는 콜백을 안정시키는 게 맞을 수도 있어요. 제가 지금 말할 수 있는 건 딱 하나예요.

저는 이걸 재본 적이 없어요.

그게 바로 채용공고가 걸러내려는 상태예요

정리하면 제 상태가 이랬어요.

  • 최적화 API를 씁니다 (useCallback 16, useMemo 8)
  • 그런데 그게 효과가 있는지는 모릅니다
  • 심지어 memo가 0개라는 것도 오늘 처음 알았습니다

이력서 문장으로 옮기면 이래요. "useCallback, useMemo를 활용한 렌더링 최적화 경험." 거짓말이 아니에요. 실제로 썼으니까요.

그런데 채용공고가 첫 줄에 "성능 최적화"를 적을 때 원하는 건 이게 아니에요. 회사가 겪는 문제는 이런 거예요. 목록이 500줄 넘어가면 스크롤이 끊긴다. 첫 화면이 3초 뒤에 뜬다. 필터를 바꾸면 화면이 잠깐 멈춘다.

이 문제를 푸는 사람은 **"어디가 느린지 찾을 줄 아는 사람"**이에요. memo를 아는 사람이 아니고요. memo는 원인을 찾은 다음에 쓰는 도구지, 원인을 찾아주는 도구가 아니에요.

기법은 검색하면 5분 만에 나와요. 측정은 안 나와요. 그래서 채용에서 값이 매겨지는 건 뒤쪽이에요.

면접에서 갈리는 지점

같은 경험을 말해도 두 가지로 갈려요.

A. "리스트가 느려서 React.memouseCallback으로 최적화했습니다."

B. "리스트 스크롤이 끊긴다는 제보가 있어서 React DevTools Profiler로 봤더니, 행 하나 바뀔 때 전체 200행이 다시 그려지고 있었어요. 부모에서 인라인 화살표 함수를 prop으로 넘기고 있어서 memo가 무력화된 상태였고요. 콜백을 안정시키고 행을 memo로 감싸니 리렌더가 200개에서 1개로 줄었고, 스크롤 프레임이 40fps에서 60fps가 됐습니다."

A는 도구 이름이고 B는 사건이에요. B에는 A가 다 들어 있는데 A에는 B가 없어요.

B를 말하려면 세 개가 필요해요.

  1. 어떻게 알았나 — 제보, Profiler, Lighthouse, 번들 분석
  2. 원인이 뭐였나 — 인라인 콜백, 과한 상태 범위, 큰 의존성
  3. 얼마나 나아졌나 — 리렌더 횟수, fps, KB, ms

세 번째가 제일 자주 빠져요. 그런데 면접관이 제일 듣고 싶은 것도 세 번째예요. 숫자가 있으면 그 사람이 실제로 재봤다는 게 증명되니까요.

오늘 잴 수 있는 것

거창한 도구 없이 지금 확인할 수 있는 게 있어요. 세 개면 충분해요.

1. 내 프로젝트의 최적화 API 분포

숫자가 제 것처럼 나오면(memo 0, useCallback 다수) 한 번 볼 값어치가 있어요.

2. 라우트별 첫 로딩 크기

First Load JS 열을 보세요. 전 라우트가 비슷한 숫자면 코드 분할이 안 걸린 거예요. 저는 이걸로 문서 사이트가 전 페이지 407~409kB로 평평하다는 걸 지난 글에서 확인했어요.

3. 실제로 몇 번 그려지는지

React DevTools의 Profiler에서 "Record why each component rendered"를 켜고 조작해보세요. 한 번 클릭에 몇 개가 다시 그려지는지 숫자로 나와요. 이 숫자가 곧 면접에서 쓸 before 값이에요.

그래서 저는 뭘 할 거냐면

memo를 146개 파일에 붙이는 건 아니에요. 그건 반대 방향의 같은 실수예요 — 재보지도 않고 도구부터 쓰는 것이요.

세어보니 .map()으로 리스트를 그리는 컴포넌트가 29개예요. 테이블, 캘린더, 캐러셀, 메가메뉴 같은 것들이요. 이 중에 행이 많아질 때 실제로 느려지는 게 있는지 먼저 잴 거예요. 느린 게 나오면 그때 memo를 넣고, 넣기 전후를 기록할 거고요.

느린 게 없으면 아무것도 안 넣어요. 그리고 그것도 결과예요. "쟀더니 문제가 없었다"도 측정한 사람만 할 수 있는 말이거든요.

마무리

채용공고가 성능 최적화를 첫 줄에 두는 건, 그게 어려워서가 아니라 할 줄 아는 사람이 드물어서인 것 같아요. memo를 아는 사람은 많은데, 자기 프로젝트가 지금 몇 kB이고 클릭 한 번에 몇 개가 다시 그려지는지 말할 수 있는 사람은 적어요.

저도 오늘 세어보기 전까진 memo가 0개인 걸 몰랐어요. 성능 글을 쓰면서요.

그러니 이력서에 React.memo라고 적기 전에, 자기 프로젝트에서 그 셋을 먼저 재보세요. 숫자가 나오면 그게 그대로 이력서 한 줄이 돼요. "memo 사용 경험"보다 "리렌더 200 → 1"이 훨씬 짧고 강해요.

관련 링크

시리즈: 프론트엔드 성능 측정

HANUI

KRDS 기반 React 컴포넌트 라이브러리. 공공 웹 개발을 더 쉽게.