0725 프론트엔드 실무 심화 (8/N): 성능 최적화, 렌더링과 이미지 최적화
✅ 1. 프론트엔드 성능 최적화란 무엇인가?
- 프론트엔드 성능 최적화는 사용자가 화면을 더 빠르고 부드럽게 사용할 수 있도록 렌더링, 네트워크 요청, 이미지, JavaScript 번들, 상태 업데이트를 개선하는 작업입니다.
- 단순히 “빠르게 보이게 하는 것”이 아니라, 고객이 이탈하지 않고 관리자도 답답함 없이 업무를 처리하게 만드는 작업입니다.
- 온라인 휴대폰 판매몰에서는 상품 상세, 배너, 상담 신청 모달, 관리자 테이블, 이미지 로딩 속도가 특히 중요합니다.
사용자 접속
↓
HTML/CSS/JS 로드
↓
이미지 로드
↓
API 데이터 요청
↓
React 렌더링
↓
사용자 클릭/입력
↓
화면 반응
➕ 1-1. 성능이 중요한 이유
- 고객 화면이 느리면 상담 신청 전 이탈할 수 있습니다.
- 상품 이미지가 늦게 뜨면 페이지 신뢰도가 떨어집니다.
- 상담 신청 버튼 반응이 느리면 중복 클릭이 생깁니다.
- 관리자 테이블이 느리면 운영 업무 속도가 떨어집니다.
- 성능 문제는 기능 오류가 아니어도 사용자는 “사이트가 불안정하다”고 느낍니다.
느린 상품 상세
↓
고객이 혜택 확인 전에 이탈
↓
상담 신청 감소
느린 관리자 테이블
↓
운영자가 필터/상태 변경을 반복 클릭
↓
중복 요청, 업무 실수 증가
✅ 2. 프론트엔드 성능의 주요 지점
- 프론트엔드 성능은 한 가지 원인으로만 느려지지 않습니다.
- 화면이 느릴 때는 여러 지점을 나눠서 봐야 합니다.
| 영역 | 문제 예시 |
|---|
| 네트워크 | API 응답 느림, 이미지 용량 큼 |
| JavaScript | 번들 크기 큼, 불필요한 라이브러리 |
| React 렌더링 | 불필요한 re-render, 무거운 컴포넌트 |
| 이미지 | 원본 크기 과다, 포맷 미최적화 |
| CSS/Layout | layout shift, 무거운 애니메이션 |
| 상태 관리 | 전역 상태 변경으로 전체 렌더링 |
| 테이블 | row 과다 렌더링, 컬럼 계산 반복 |
| 폼 | 입력할 때마다 무거운 validation |
➕ 2-1. 성능 최적화 기본 순서
1. 측정한다
2. 병목을 찾는다
3. 큰 문제부터 고친다
4. 다시 측정한다
5. 과한 최적화는 피한다
- 성능 최적화는 감으로 하면 안 됩니다.
- “왠지 느린 것 같다”가 아니라 어디서 느린지 확인해야 합니다.
✅ 3. 측정 없이 최적화하지 않기
- 성능 최적화에서 가장 흔한 실수는 측정 없이
memo, useMemo, useCallback을 남발하는 것입니다.
- 실제 병목은 이미지 용량이나 API 응답인데 React 코드만 만지는 경우가 많습니다.
➕ 3-1. 먼저 확인할 것
페이지 첫 로딩 시간
이미지 로딩 시간
API 응답 시간
번들 크기
렌더링 횟수
사용자 클릭 후 반응 시간
테이블 row 수
검색/필터 변경 시 체감 속도
➕ 3-2. 사용할 수 있는 도구
Chrome DevTools Network
Chrome DevTools Performance
React DevTools Profiler
Lighthouse
Web Vitals
TanStack Query Devtools
브라우저 console.time
- 처음에는 Chrome DevTools Network만 제대로 봐도 많은 문제를 찾을 수 있습니다.
- 이미지가 몇 MB인지, API가 몇 초 걸리는지부터 확인해야 합니다.
✅ 4. Core Web Vitals 기본
- 고객 화면에서는 Core Web Vitals 개념을 알아두면 좋습니다.
- 구글 검색 노출과 사용자 경험 측면에서 참고할 수 있는 성능 지표입니다.
| 지표 | 의미 |
|---|
| LCP | 주요 콘텐츠가 화면에 보이는 시간 |
| INP | 사용자 입력에 대한 반응성 |
| CLS | 화면이 갑자기 밀리는 정도 |
➕ 4-1. 쉽게 이해하기
LCP:
상품 이미지나 핵심 제목이 빨리 보이는가?
INP:
상담 신청 버튼을 눌렀을 때 바로 반응하는가?
CLS:
이미지나 배너가 늦게 뜨면서 화면이 밀리지 않는가?
➕ 4-2. 휴대폰 판매몰 기준
상품 상세:
대표 이미지, 상품명, 월 예상 요금, 상담 신청 버튼이 빨리 보여야 함
배너:
이미지 크기와 높이를 미리 잡아 layout shift 방지
상담 신청:
버튼 클릭 후 즉시 로딩/모달 반응 제공
- 고객 화면에서는 “첫 화면에서 핵심 정보가 얼마나 빨리 보이는가”가 중요합니다.
- 관리자 화면에서는 Web Vitals보다 업무 반응성과 테이블 성능이 더 중요할 수 있습니다.
✅ 5. 네트워크 성능 최적화
- 네트워크 성능은 API, 이미지, JS, CSS 파일 크기와 요청 수에 영향을 받습니다.
- 화면이 느릴 때는 Network 탭에서 어떤 요청이 오래 걸리는지 먼저 봐야 합니다.
➕ 5-1. 확인할 항목
이미지 파일 크기
JS 번들 크기
CSS 크기
API 응답 시간
중복 API 호출
실패 요청
캐시 여부
압축 여부
➕ 5-2. 나쁜 예시
상품 상세 진입 시:
5MB 이미지 4장 로딩
사용하지 않는 관리자 JS까지 같이 로딩
같은 상품 API를 3번 호출
배너 이미지 크기 미지정으로 화면 밀림
➕ 5-3. 좋은 방향
이미지는 WebP/적절한 크기로 제공
필요한 JS만 로딩
같은 서버 데이터는 TanStack Query 캐시 활용
첫 화면에 필요한 데이터부터 로딩
아래쪽 섹션은 지연 로딩 고려
- 가장 효과가 큰 최적화는 보통 이미지와 불필요한 요청 제거입니다.
✅ 6. 이미지 최적화
- 온라인 휴대폰 판매몰에서는 이미지가 매우 중요합니다.
- 상품 이미지, 배너, 사전예약 이미지, 리뷰 이미지가 크면 페이지가 느려집니다.
➕ 6-1. 이미지 최적화 기준
원본 그대로 업로드하지 않기
화면 크기에 맞는 이미지 제공
WebP/AVIF 검토
이미지 width/height 지정
Lazy loading 적용
중요 이미지는 우선 로딩
불필요하게 투명 PNG 남발하지 않기
➕ 6-2. 이미지 포맷 기준
| 포맷 | 특징 | 사용 기준 |
|---|
| JPG/JPEG | 사진에 적합 | 실사 상품 이미지 |
| PNG | 투명 배경 가능, 용량 큼 | 누끼 이미지, 로고 |
| WebP | 용량 효율 좋음 | 대부분의 웹 이미지 |
| SVG | 벡터, 아이콘/로고 | 단순 아이콘, 로고 |
| AVIF | 압축률 좋음 | 지원 환경 확인 필요 |
➕ 6-3. 실무 기준
상품 실사:
WebP 또는 최적화된 JPG
누끼 상품 이미지:
필요할 때만 PNG, 가능하면 WebP 투명 지원 검토
아이콘:
SVG
배너:
PC/모바일 비율 별도 제작 고려
- 무조건 PNG가 고화질이라는 생각은 버려야 합니다.
- PNG는 투명 배경에는 좋지만, 큰 배너에 쓰면 용량이 커질 수 있습니다.
✅ 7. 이미지 크기 지정과 CLS 방지
- 이미지가 늦게 로딩되면서 화면이 아래로 밀리면 사용자 경험이 나빠집니다.
- 이를 Layout Shift라고 볼 수 있습니다.
➕ 7-1. 나쁜 예시
<img src="/banner.png" alt="사전예약 배너" />
- 이미지가 로드되기 전까지 브라우저가 높이를 예측하기 어렵습니다.
- 로딩 후 화면이 갑자기 밀릴 수 있습니다.
➕ 7-2. 좋은 예시
<img
src="/banner.webp"
alt="사전예약 배너"
width={1200}
height={400}
/>
➕ 7-3. CSS 비율 유지
.banner-image {
width: 100%;
aspect-ratio: 3 / 1;
object-fit: cover;
}
- 이미지 영역의 비율을 미리 잡아두면 화면 밀림이 줄어듭니다.
- 배너와 상품 이미지는 특히 width/height 또는 aspect-ratio를 챙겨야 합니다.
✅ 8. Lazy Loading
- Lazy Loading은 화면에 당장 보이지 않는 이미지를 나중에 로딩하는 방식입니다.
- 상품 상세 아래쪽 리뷰, FAQ 이미지, 상세 스펙 이미지 등에 유용합니다.
➕ 8-1. 기본 예시
<img
src="/product-detail.webp"
alt="상품 상세 이미지"
loading="lazy"
/>
➕ 8-2. Lazy Loading에 적합한 이미지
페이지 아래쪽 상세 이미지
리뷰 이미지
FAQ 이미지
관리자 상세 모달 내부 이미지
아래쪽 배너
➕ 8-3. Lazy Loading을 조심할 이미지
첫 화면 대표 이미지
LCP 대상 이미지
Hero 이미지
상단 핵심 배너
- 첫 화면 핵심 이미지는 lazy loading하면 오히려 느리게 보일 수 있습니다.
- 중요한 이미지는 빠르게 로딩하고, 아래쪽 이미지만 지연 로딩하는 것이 좋습니다.
✅ 9. 코드 스플리팅
- 코드 스플리팅(Code Splitting)은 필요한 JavaScript만 나눠서 로딩하는 방식입니다.
- 고객 화면에서 관리자 페이지 코드까지 한 번에 로딩하면 비효율적입니다.
➕ 9-1. 필요한 이유
초기 JS 번들 감소
첫 로딩 속도 개선
관리자 기능을 고객 화면에서 로딩하지 않음
무거운 라이브러리 지연 로딩 가능
➕ 9-2. React lazy 예시
const AdminDashboardPage = React.lazy(() =>
import('./pages/admin/AdminDashboardPage'),
);
➕ 9-3. Suspense 예시
<Suspense fallback={<PageLoading />}>
<AdminDashboardPage />
</Suspense>
- 라우트 단위 코드 스플리팅은 효과가 큽니다.
- 관리자 페이지, 차트 페이지, 엑셀/에디터 기능처럼 무거운 화면은 분리하면 좋습니다.
✅ 10. 무거운 라이브러리 지연 로딩
- 차트, 에디터, 엑셀, 지도, 이미지 편집 라이브러리는 번들 크기를 크게 만들 수 있습니다.
- 처음부터 로딩하지 말고 실제 필요할 때 로딩하는 방식을 고려해야 합니다.
➕ 10-1. 지연 로딩 후보
차트 라이브러리
엑셀 관련 라이브러리
리치 텍스트 에디터
이미지 크롭/편집 도구
지도 라이브러리
대형 날짜 선택기
➕ 10-2. 관리자 화면 예시
상담 목록:
일반 테이블 코드만 로딩
엑셀 다운로드 모달 열기:
엑셀 관련 모듈 로딩
통계 대시보드 진입:
차트 라이브러리 로딩
- 모든 기능을 첫 화면 번들에 넣으면 초기 로딩이 느려집니다.
- 자주 쓰지 않는 무거운 기능은 나중에 불러오는 것이 좋습니다.
✅ 11. React 렌더링 성능
- React 성능 문제는 보통 불필요한 re-render에서 시작됩니다.
- 상태가 바뀔 때 필요한 컴포넌트만 다시 렌더링되어야 합니다.
➕ 11-1. re-render가 많아지는 원인
전역 상태를 너무 넓게 사용
부모 컴포넌트에서 상태를 과하게 많이 가짐
inline object/function을 props로 계속 전달
큰 테이블 row가 모두 다시 렌더링
필터 입력마다 전체 테이블 재계산
context 값 변경으로 하위 전체 렌더링
➕ 11-2. 먼저 할 것
React DevTools Profiler로 확인
어떤 컴포넌트가 자주 렌더링되는지 확인
렌더링 비용이 큰 컴포넌트부터 개선
불필요한 전역 상태 분리
상태 위치를 더 가까운 컴포넌트로 이동
memo, useMemo, useCallback은 무조건 쓰는 것이 아닙니다.
- 실제로 렌더링 비용이 있는 곳에 선택적으로 써야 합니다.
✅ 12. React.memo 사용 기준
React.memo는 props가 바뀌지 않으면 컴포넌트 렌더링을 건너뛸 수 있게 도와줍니다.
- 하지만 props가 매번 새 객체/함수면 효과가 떨어집니다.
➕ 12-1. 적합한 경우
테이블 Row가 많음
상태 Badge가 많이 반복됨
무거운 카드 컴포넌트
props가 자주 바뀌지 않음
렌더링 비용이 실제로 있음
➕ 12-2. 예시
const ConsultTableRow = React.memo(function ConsultTableRow({
consult,
onOpenDetail,
}: {
consult: Consult;
onOpenDetail: (id: number) => void;
}) {
return (
<tr>
<td>{consult.createdAt}</td>
<td>{consult.customerName}</td>
<td>{consult.phone}</td>
<td>
<button onClick={() => onOpenDetail(consult.id)}>
상세
</button>
</td>
</tr>
);
});
➕ 12-3. 주의할 점
모든 컴포넌트에 memo 붙이지 않기
props가 매번 새로 만들어지면 효과 낮음
비교 비용이 더 클 수 있음
코드 복잡도 증가 고려
- memo는 만능이 아닙니다.
- 테이블 row처럼 반복이 많고 렌더링 비용이 있는 곳부터 검토하면 됩니다.
✅ 13. useMemo 사용 기준
useMemo는 계산 결과를 메모이제이션합니다.
- 무거운 계산이나 derived data가 있을 때 유용합니다.
➕ 13-1. 적합한 경우
큰 목록을 필터링/정렬
복잡한 컬럼 정의
차트 데이터 가공
권한 기반 메뉴 계산
상태별 count 계산
➕ 13-2. 예시
const columns = useMemo(
() => [
{ key: 'createdAt', label: '신청일' },
{ key: 'customerName', label: '고객명' },
{ key: 'phone', label: '전화번호' },
{ key: 'status', label: '상태' },
],
[],
);
➕ 13-3. 권한 메뉴 계산 예시
const visibleMenus = useMemo(
() =>
adminMenus.filter((menu) =>
hasPermission(me?.permissions, menu.requiredPermission),
),
[me?.permissions],
);
- 간단한 계산에는 굳이
useMemo가 필요 없습니다.
- 코드가 복잡해지는 비용도 같이 봐야 합니다.
✅ 14. useCallback 사용 기준
useCallback은 함수를 메모이제이션합니다.
- memo된 자식 컴포넌트에 함수를 props로 넘길 때 유용할 수 있습니다.
➕ 14-1. 적합한 경우
React.memo된 자식 컴포넌트에 handler 전달
테이블 row 액션 handler
무거운 child component에 callback 전달
dependency가 안정적인 함수
➕ 14-2. 예시
const handleOpenDetail = useCallback((consultId: number) => {
setSelectedConsultId(consultId);
setIsDetailOpen(true);
}, []);
<ConsultTable
items={consults}
onOpenDetail={handleOpenDetail}
/>
➕ 14-3. 주의할 점
모든 함수에 useCallback 붙이지 않기
dependency 관리가 복잡해질 수 있음
자식이 memo되지 않았다면 효과가 적을 수 있음
useCallback도 성능을 무조건 좋게 만드는 것이 아닙니다.
- 렌더링 최적화가 필요한 구조에서만 쓰는 것이 좋습니다.
✅ 15. 상태 위치 최적화
- 성능 문제는 상태가 너무 위에 있어서 생기는 경우가 많습니다.
- 상태는 필요한 가장 가까운 곳에 두는 것이 좋습니다.
➕ 15-1. 나쁜 예시
AdminPage가 모든 상태를 가짐:
검색 폼 입력값
상세 모달 열림
테이블 row hover
드롭다운 열림
토스트 상태
권한 상태
- 작은 상태가 바뀔 때 큰 페이지 전체가 다시 렌더링될 수 있습니다.
➕ 15-2. 좋은 방향
검색 조건:
URL 상태
서버 데이터:
TanStack Query
모달 열림:
해당 기능 컴포넌트 근처
드롭다운 열림:
드롭다운 컴포넌트 내부
전역 토스트:
전역 UI store
- 상태를 가까운 곳에 두면 불필요한 렌더링과 복잡도가 줄어듭니다.
✅ 16. Context 성능 주의
- Context는 편하지만 값이 바뀌면 해당 Context를 사용하는 하위 컴포넌트들이 다시 렌더링됩니다.
- 전역 상태처럼 무분별하게 쓰면 성능 문제가 생길 수 있습니다.
➕ 16-1. 주의할 Context
자주 바뀌는 입력값
테이블 hover/selection
대량 데이터 목록
실시간 상태
자주 갱신되는 통계
➕ 16-2. Context에 적합한 것
테마
로그인 사용자 정보
언어 설정
FormProvider
Modal 내부 공유 상태
- 자주 바뀌는 값은 Context보다 로컬 상태나 전용 상태 관리 도구를 고려하는 것이 좋습니다.
- Context를 쓸 때는 provider 범위를 너무 크게 잡지 않는 것도 중요합니다.
✅ 17. 관리자 테이블 성능 최적화
- 관리자 테이블은 프론트 성능 문제가 자주 발생하는 영역입니다.
- 상담/주문/상품 데이터가 늘어나면 테이블 렌더링과 검색이 느려질 수 있습니다.
➕ 17-1. 기본 원칙
서버 페이지네이션 사용
한 번에 너무 많은 row 렌더링하지 않기
limit 최대값 제한
검색/필터는 서버에서 처리
무거운 컬럼 렌더링 최소화
row action handler 최적화
대량 데이터는 Export로 분리
➕ 17-2. 나쁜 방식
전체 상담 5000건을 한 번에 가져옴
프론트에서 filter/sort
모든 row에 복잡한 모달 상태 포함
각 row마다 API query 실행
전화번호/상태 포맷을 렌더링마다 반복 계산
➕ 17-3. 좋은 방식
page/limit 기반 서버 조회
queryKey에 필터 조건 포함
row는 표시만 담당
상세 데이터는 모달 열 때 조회
상태 라벨/variant는 매핑 객체로 관리
필요 시 row memo 적용
- 테이블은 “많은 데이터를 한 번에 보여주는 것”보다 “필요한 데이터를 빠르게 찾게 하는 것”이 중요합니다.
✅ 18. 가상 스크롤
- 가상 스크롤(Virtualization)은 실제 화면에 보이는 row만 렌더링하는 방식입니다.
- row가 수백~수천 개 이상일 때 성능에 도움이 됩니다.
➕ 18-1. 필요한 경우
한 페이지에 수백 개 이상 row 렌더링
스크롤이 버벅임
row 높이가 비교적 일정함
대량 데이터를 화면에서 빠르게 탐색해야 함
➕ 18-2. 조심할 점
구현 복잡도 증가
접근성 고려 필요
row 높이가 동적이면 어려움
일반 페이지네이션이면 굳이 필요 없을 수 있음
- 일반 관리자 페이지에서는 먼저 서버 페이지네이션과 limit 제한을 적용하는 것이 우선입니다.
- 그래도 부족할 때 가상 스크롤을 검토하면 됩니다.
✅ 19. API 요청 최적화
- 같은 API를 여러 번 호출하거나 불필요한 refetch가 많으면 성능이 떨어집니다.
- TanStack Query를 잘 쓰면 중복 요청을 줄일 수 있습니다.
➕ 19-1. 중복 요청 원인
같은 데이터를 여러 컴포넌트에서 각각 useEffect로 호출
queryKey가 매번 바뀜
검색 input 입력마다 즉시 API 호출
페이지 진입마다 캐시 없이 새 요청
mutation 후 너무 넓은 invalidate
➕ 19-2. 해결 방향
TanStack Query queryKey 통일
staleTime 조정
검색어 debounce
검색 버튼 제출 방식 사용
필요한 query만 invalidate
상세 데이터는 필요할 때 enabled 조건으로 조회
➕ 19-3. enabled 옵션
const consultDetailQuery = useQuery({
queryKey: ['admin', 'consult', selectedConsultId],
queryFn: () => adminConsultApi.getConsultDetail(selectedConsultId!),
enabled: !!selectedConsultId,
});
- 상세 모달을 열기 전에는 상세 API를 호출하지 않게 할 수 있습니다.
- 필요할 때만 요청하는 것이 중요합니다.
✅ 20. 검색 입력 Debounce
- 검색어 입력마다 API를 호출하면 요청이 너무 많아집니다.
- 자동 검색을 한다면 debounce가 필요합니다.
➕ 20-1. Debounce 개념
사용자가 입력 중:
API 호출하지 않음
입력이 멈춘 뒤 일정 시간:
API 호출
➕ 20-2. 기준
고객 검색:
300ms ~ 500ms
관리자 검색:
검색 버튼 방식 또는 500ms debounce
무거운 검색:
검색 버튼 방식 권장
➕ 20-3. 관리자 페이지 기준
상담/주문 목록:
검색 버튼 방식 추천
상품 자동완성:
debounce 추천
간단한 필터:
즉시 적용 가능
- 관리자 목록은 URL 상태와 연결되는 경우가 많으므로 검색 버튼 방식이 더 안정적일 수 있습니다.
- 자동 검색은 UX는 좋지만 API 요청이 늘어날 수 있습니다.
✅ 21. Prefetch
- Prefetch는 사용자가 필요로 할 가능성이 높은 데이터를 미리 가져오는 방식입니다.
- 잘 쓰면 화면 전환이 빨라집니다.
➕ 21-1. 적합한 상황
상품 카드 hover 시 상세 데이터 미리 가져오기
다음 페이지 데이터 미리 가져오기
관리자 상세 모달 열 가능성이 높은 데이터
자주 보는 카테고리 데이터
➕ 21-2. 다음 페이지 prefetch 예시
useEffect(() => {
if (data?.meta.page < data?.meta.totalPages) {
queryClient.prefetchQuery({
queryKey: ['admin', 'consults', { page: page + 1, limit }],
queryFn: () =>
adminConsultApi.getConsults({
page: page + 1,
limit,
}),
});
}
}, [data, page, limit, queryClient]);
➕ 21-3. 주의할 점
너무 많은 prefetch는 API 낭비
모바일 데이터 사용량 증가 가능
권한 필요한 데이터는 주의
정말 쓸 가능성이 높은 것만
- Prefetch는 체감 속도를 올릴 수 있지만, 요청이 늘어납니다.
- 꼭 필요한 곳에만 적용해야 합니다.
✅ 22. 애니메이션 성능
- 애니메이션은 UI를 부드럽게 만들지만, 잘못 쓰면 성능을 떨어뜨립니다.
- 특히 모바일에서 무거운 그림자, blur, layout 변경 애니메이션은 버벅임을 만들 수 있습니다.
➕ 22-1. 좋은 애니메이션 대상
transform
opacity
➕ 22-2. 조심할 대상
width
height
top
left
margin
padding
box-shadow 과다
filter blur 과다
➕ 22-3. 기준
짧고 빠르게
사용자 행동을 보조하는 목적
중요 정보 전달 방해 금지
모바일에서 과한 효과 피하기
- 애니메이션은 “멋”보다 “피드백”이 목적이어야 합니다.
- 상담 신청 버튼, 모달 열림, 토스트 정도는 적절히 쓰면 좋습니다.
✅ 23. 관리자 페이지와 고객 페이지 최적화 차이
| 구분 | 고객 페이지 | 관리자 페이지 |
|---|
| 핵심 목표 | 빠른 첫 화면, 전환율 | 빠른 업무 처리, 안정성 |
| 중요 지표 | LCP, INP, CLS | API 응답, 테이블 렌더링, 필터 반응 |
| 주요 병목 | 이미지, 배너, JS 번들 | 테이블, 필터, 상태 변경, 대량 데이터 |
| 최적화 | 이미지/코드 스플리팅/CTA 반응 | 서버 페이지네이션/queryKey/mutation 관리 |
| 메시지 | 안심, 간결 | 정확, 다음 행동 안내 |
➕ 23-1. 고객 페이지 우선순위
대표 이미지 최적화
상품명/혜택/CTA 빠르게 표시
배너 용량 줄이기
상담 신청 모달 빠른 반응
모바일 layout shift 방지
➕ 23-2. 관리자 페이지 우선순위
목록 API 응답 최적화
서버 페이지네이션
검색/필터 URL 상태
queryKey 정리
상태 변경 후 필요한 query만 갱신
테이블 row 렌더링 최적화
- 고객 화면과 관리자 화면은 최적화 기준이 다릅니다.
- 같은 방식으로 접근하면 중요한 문제를 놓칠 수 있습니다.
✅ 24. 성능 최적화에서 자주 하는 실수
➕ 24-1. memo부터 붙이는 실수
문제:
측정 없이 React.memo/useMemo/useCallback 남발
결과:
코드는 복잡해졌는데 체감 속도 개선 없음
실제 문제는 이미지나 API였음
➕ 24-2. 이미지 원본 그대로 쓰는 실수
문제:
PC용 대형 배너를 모바일에도 그대로 사용
결과:
모바일 로딩 느림
데이터 사용량 증가
전환율 저하
➕ 24-3. 서버 데이터를 전부 가져오는 실수
문제:
관리자 상담 5000건을 한 번에 받아서 프론트에서 필터링
결과:
API 느림
브라우저 렌더링 느림
메모리 사용 증가
➕ 24-4. queryKey 조건 누락
문제:
필터 조건은 바뀌는데 queryKey는 같음
결과:
잘못된 캐시 표시
화면 갱신 안 됨
✅ 25. 실무 체크리스트
➕ 25-1. 고객 화면 성능 체크리스트
- 첫 화면 대표 이미지 용량이 과하지 않은가?
- 상품명, 핵심 혜택, CTA가 빠르게 보이는가?
- 이미지 width/height 또는 aspect-ratio가 지정되어 있는가?
- 아래쪽 이미지는 lazy loading이 적용되어 있는가?
- 모바일 배너가 너무 큰 이미지를 쓰지 않는가?
- 상담 신청 모달이 빠르게 열리는가?
- 버튼 클릭 후 즉시 피드백이 있는가?
- 불필요한 관리자 코드가 고객 화면 번들에 포함되지 않는가?
➕ 25-2. 관리자 화면 성능 체크리스트
- 목록은 서버 페이지네이션을 사용하는가?
- limit 최대값이 제한되어 있는가?
- queryKey에 모든 검색 조건이 포함되어 있는가?
- 상태 변경 후 필요한 query만 invalidate하는가?
- 상세 데이터는 필요할 때만 조회하는가?
- 테이블 row가 과하게 무겁지 않은가?
- 검색 입력마다 불필요하게 API를 호출하지 않는가?
- 대량 데이터는 ExportJob으로 분리되어 있는가?
➕ 25-3. 이미지 체크리스트
- WebP 또는 최적화된 이미지 포맷을 사용하는가?
- PC/모바일 이미지 비율을 따로 고려했는가?
- 투명 PNG를 과하게 사용하지 않는가?
- 이미지 alt가 적절한가?
- LCP 이미지에 lazy loading을 걸지 않았는가?
- 아래쪽 이미지는 lazy loading을 적용했는가?
- 배너 이미지 텍스트가 모바일에서도 읽히는가?
- S3/CloudFront 캐시 정책을 고려했는가?
➕ 25-4. React 렌더링 체크리스트
- React DevTools Profiler로 병목을 확인했는가?
- 상태가 필요한 위치보다 너무 위에 있지 않은가?
- 전역 상태 변경으로 큰 화면이 전부 렌더링되지 않는가?
- 테이블 row memo가 필요한 수준인지 확인했는가?
- 무거운 계산은 useMemo를 검토했는가?
- handler props 때문에 memo가 깨지는지 확인했는가?
- Context 값이 너무 자주 바뀌지 않는가?
- 최적화로 코드가 과하게 복잡해지지 않았는가?
✅ 26. AI를 활용해 프론트엔드 성능을 점검할 때 질문법
- AI에게 성능 점검을 요청할 때는 화면 종류, 현재 문제 증상, API 구조, 이미지 사용 방식, 상태 관리 방식, 테이블 규모를 함께 알려줘야 합니다.
- “사이트가 느려”라고만 하면 일반론만 나옵니다.
➕ 26-1. 좋은 질문 예시
React + TanStack Query 기반 온라인 휴대폰 판매몰 프론트엔드 성능을 점검하고 싶어.
상황:
1. 고객 화면에는 상품 목록, 상품 상세, 상담 신청 모달이 있음
2. 상품 상세에는 대표 이미지, 배너, 혜택 안내, 리뷰, FAQ가 있음
3. 관리자 화면에는 상담 목록 테이블, 검색/필터, 상태 변경, 엑셀 다운로드가 있음
4. 상담 목록은 page, limit, keyword, status, source 조건으로 API 조회함
5. 서버 데이터는 TanStack Query로 관리함
6. 일부 배너 이미지는 PNG이고 용량이 큰 편임
7. 모바일에서 상품 상세 첫 로딩이 느리게 느껴짐
8. 관리자 테이블은 필터 변경 시 약간 버벅임
요청:
- 고객 화면과 관리자 화면을 나눠 성능 병목 후보 정리
- Chrome DevTools에서 확인할 항목
- 이미지 최적화 기준
- queryKey/staleTime/invalidate 점검 기준
- 테이블 렌더링 최적화 기준
- React.memo/useMemo/useCallback 사용 기준
- 우선순위 높은 개선 순서
- 과한 최적화를 피하는 기준
을 실무 기준으로 정리해줘.
➕ 26-2. AI 답변 검증 기준
- 측정 없이 memo부터 붙이라고 하지 않는가?
- 이미지 용량과 포맷을 우선 점검하는가?
- 고객 화면과 관리자 화면의 최적화 기준을 구분하는가?
- TanStack Query의 queryKey, staleTime, invalidate를 점검하는가?
- 서버 페이지네이션과 limit 제한을 언급하는가?
- lazy loading을 첫 화면 이미지에 무조건 적용하라고 하지 않는가?
- 실제 병목 확인 도구를 제안하는가?
- 현재 서비스 규모에 맞는 개선 순서를 제안하는가?
📌 요약
- 프론트엔드 성능 최적화는 렌더링, 네트워크 요청, 이미지, JavaScript 번들, 상태 업데이트를 개선해 사용자가 더 빠르고 부드럽게 서비스를 쓰게 만드는 작업입니다.
- 성능 최적화는 감으로 하지 말고 Chrome DevTools, Lighthouse, React Profiler, Network 탭으로 먼저 측정해야 합니다.
- 고객 화면에서는 상품 이미지, 배너, 첫 화면 핵심 정보, 상담 신청 버튼 반응성이 중요합니다.
- 관리자 화면에서는 목록 API 응답, 서버 페이지네이션, 검색/필터 반응, 테이블 row 렌더링, 상태 변경 후 query 갱신이 중요합니다.
- 이미지 최적화는 체감 성능에 가장 큰 영향을 주는 경우가 많으며, WebP, 적절한 크기, width/height 지정, lazy loading을 상황에 맞게 적용해야 합니다.
- 첫 화면 대표 이미지나 LCP 대상 이미지에는 무조건 lazy loading을 적용하면 오히려 느리게 보일 수 있습니다.
- 코드 스플리팅을 통해 고객 화면에서 관리자 페이지 코드나 무거운 차트/엑셀 라이브러리를 불필요하게 로딩하지 않게 해야 합니다.
React.memo, useMemo, useCallback은 실제 렌더링 병목이 확인된 곳에 선택적으로 사용해야 하며, 무조건 붙이면 코드만 복잡해질 수 있습니다.
- 관리자 테이블은 서버 페이지네이션, queryKey 정리, 필요한 데이터만 조회, 상세 데이터 지연 조회가 기본입니다.
- 성능 최적화의 핵심은 “측정 → 병목 확인 → 큰 문제부터 개선 → 다시 측정”입니다.