React useMemo와 useCallback, 언제 쓰면 이득인가 | 렌더링 최적화 실전
이 글의 핵심
React에서 useMemo·useCallback은 참조 동일성과 비용 큰 계산을 묶는 도구입니다. 원리·사용 시기·과최적화 피하기·Profiler로 확인하는 법을 정리했습니다.
들어가며
React 18/19에서 함수 컴포넌트는 props·state·부모 리렌더에 따라 자주 다시 실행됩니다. useMemo와 useCallback은 이전 결과를 재사용해 불필요한 연산·참조 변경을 줄이는 도구입니다. React useMemo·useCallback을 언제 쓸지는 “비싼 계산인가, 참조 안정이 필요한가”로 먼저 갈라집니다. 다만 둘 자체도 비용이 있어 남발하면 오히려 느려질 수 있습니다.
이 글은 언제 메모이제이션이 이득인지를 판별하는 기준과, 프로파일러로 검증하는 순서를 담았습니다. 비동기 흐름은 async 가이드, 디버깅은 비동기 디버깅 사례와 연결됩니다.
왜 useMemo·useCallback이 존재하나요?
함수 컴포넌트는 매 렌더마다 새로 실행되므로, 안에서 만든 객체·함수 참조도 기본적으로 새로 생깁니다. 그 참조가 React.memo 자식, useEffect 의존성, 외부 훅의 안정성에 영향을 주기 때문에, 필요할 때만 이전 참조를 재사용하려는 도구가 useMemo와 useCallback입니다.
프로덕션에서 주의할 점
- 측정 없이 전 레이어에 적용하면 메모리·비교 비용만 늘고 체감 개선이 없을 수 있습니다. React DevTools Profiler로 먼저 병목을 확인하는 것을 권장합니다.
- 의존성 배열을 잘못 맞추면 “메모했는데도 갱신이 이상하다” 또는 “안 바뀌어야 하는데 바뀐다”가 납니다. ESLint
exhaustive-deps와 팀 규칙을 함께 씁니다. - 서버 컴포넌트로 데이터를 끌어올 수 있는 부분은 클라이언트 메모이제이션 부담 자체가 줄어듭니다. 경계 설계를 먼저 검토합니다.
실전 경험에서 배운 교훈
이 기술을 실무 프로젝트에 처음 도입했을 때, 공식 문서만으로는 알 수 없었던 많은 함정들이 있었습니다. 특히 프로덕션 환경에서 발생하는 엣지 케이스들은 로컬 개발 환경에서는 재현조차 되지 않았죠.
가장 어려웠던 점은 성능 최적화였습니다. 처음엔 “동작만 하면 되겠지”라고 생각했지만, 실제 사용자 트래픽이 몰리면서 병목 지점들이 하나씩 드러났습니다. 특히 데이터베이스 쿼리 최적화, 캐싱 전략, 에러 핸들링 구조 등은 여러 번의 장애를 겪으면서 개선해 나갔습니다.
이 글에서는 그런 시행착오를 통해 얻은 실전 노하우와, “이렇게 하면 안 된다”는 교훈들을 함께 정리했습니다. 특히 트러블슈팅 섹션은 실제 장애 대응 경험을 바탕으로 작성했으니, 비슷한 문제를 마주했을 때 참고하시면 도움이 될 것입니다.
개념 설명
리렌더링 기본 원리
- 리렌더: 부모가 렌더되면 기본적으로 자식도 다시 렌더됩니다(조건 없다면).
- 참조 동일성: 객체·함수·배열 리터럴은 렌더마다 새 참조가 됩니다.
React.memo로 감싼 자식이나useEffect의 의존성 배열에서 불필요한 변화로 이어질 수 있습니다.
useMemo와 useCallback
- useMemo: 값(객체·배열·계산 결과)을 의존성이 바뀔 때만 다시 계산합니다.
- useCallback: 함수 참조를 의존성이 바뀔 때만 유지합니다. 사실상
useMemo(() => fn, deps)의 문법 설탕입니다.
언제 사용하나?
useMemo 사용 시기:
- 비용이 큰 계산: 배열 필터링, 정렬, 복잡한 연산
- 참조 안정화: 객체나 배열을 props로 전달할 때
- 의존성 배열:
useEffect의 의존성으로 사용될 때useCallback사용 시기: React.memo자식: 메모이제이션된 자식에게 콜백 전달- 의존성 배열:
useEffect의 의존성으로 사용될 때 - 외부 훅: 참조 안정성이 필요한 외부 라이브러리
언제 사용하지 말아야 하나?
- 가벼운 연산: 단순 계산은 메모이제이션 비용이 더 클 수 있음
- 측정 없이: Profiler로 병목을 확인하기 전
- 모든 함수: 과도한 메모이제이션은 코드 복잡도만 증가
실전 구현 (단계별 코드)
1) 비용 큰 리스트 필터링 — useMemo
문제: 매 렌더마다 필터링 재실행
import { useState } from "react";
type Item = { id: string; label: string; score: number };
export function LeaderboardBad({ items }: { items: Item[] }) {
const [query, setQuery] = useState("");
const q = query.trim().toLowerCase();
const visible = !q
? items
: items.filter((it) => it.label.toLowerCase().includes(q));
return (
<section>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<ul>
{visible.map((it) => (
<li key={it.id}>
{it.label} — {it.score}
</li>
))}
</ul>
</section>
);
}
문제점:
- 부모가 리렌더될 때마다 필터링 재실행
items가 수천 건이면 성능 저하 해결:useMemo로 캐싱
import { useMemo, useState } from "react";
type Item = { id: string; label: string; score: number };
export function Leaderboard({ items }: { items: Item[] }) {
const [query, setQuery] = useState("");
const visible = useMemo(() => {
console.log("필터링 실행");
const q = query.trim().toLowerCase();
if (!q) return items;
return items.filter((it) => it.label.toLowerCase().includes(q));
}, [items, query]);
return (
<section>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<ul>
{visible.map((it) => (
<li key={it.id}>
{it.label} — {it.score}
</li>
))}
</ul>
</section>
);
}
효과:
items와query가 변경될 때만 필터링 실행- 부모 리렌더 시 캐시된 결과 재사용
2) 자식 콜백 안정화 — useCallback + memo
문제: 매 렌더마다 새 함수 생성
import { memo, useState } from "react";
const Row = memo(function Row({
id,
onSelect,
}: {
id: string;
onSelect: (id: string) => void;
}) {
console.log(`Row ${id} 렌더`);
return <button onClick={() => onSelect(id)}>{id}</button>;
});
export function TableBad({ ids }: { ids: string[] }) {
const [active, setActive] = useState<string | null>(null);
const handleSelect = (id: string) => {
setActive(id);
};
return (
<div>
<p>active: {active}</p>
{ids.map((id) => (
<Row key={id} id={id} onSelect={handleSelect} />
))}
</div>
);
}
문제점:
handleSelect가 매 렌더마다 새로 생성Row의memo가 무력화됨- 모든
Row가 매번 리렌더 해결:useCallback으로 안정화
import { memo, useCallback, useState } from "react";
const Row = memo(function Row({
id,
onSelect,
}: {
id: string;
onSelect: (id: string) => void;
}) {
console.log(`Row ${id} 렌더`);
return <button onClick={() => onSelect(id)}>{id}</button>;
});
export function Table({ ids }: { ids: string[] }) {
const [active, setActive] = useState<string | null>(null);
const handleSelect = useCallback((id: string) => {
setActive(id);
}, []);
return (
<div>
<p>active: {active}</p>
{ids.map((id) => (
<Row key={id} id={id} onSelect={handleSelect} />
))}
</div>
);
}
효과:
handleSelect참조가 안정적Row의memo가 제대로 작동- 클릭한
Row만 리렌더
3) 객체 props 안정화 — useMemo
문제: 매 렌더마다 새 객체 생성
import { memo } from "react";
const Chart = memo(function Chart({ config }: { config: { theme: string; width: number } }) {
console.log("Chart 렌더");
return <div style={{ width: config.width }}>차트 ({config.theme})</div>;
});
export function DashboardBad() {
const [count, setCount] = useState(0);
const config = { theme: "dark", width: 600 };
return (
<div>
<button onClick={() => setCount(count + 1)}>카운트: {count}</button>
<Chart config={config} />
</div>
);
}
문제점:
config객체가 매 렌더마다 새로 생성Chart의memo가 무력화됨 해결:useMemo로 안정화
import { memo, useMemo, useState } from "react";
const Chart = memo(function Chart({ config }: { config: { theme: string; width: number } }) {
console.log("Chart 렌더");
return <div style={{ width: config.width }}>차트 ({config.theme})</div>;
});
export function Dashboard() {
const [count, setCount] = useState(0);
const config = useMemo(() => ({ theme: "dark", width: 600 }), []);
return (
<div>
<button onClick={() => setCount(count + 1)}>카운트: {count}</button>
<Chart config={config} />
</div>
);
}
효과:
config참조가 안정적Chart는count변경 시 리렌더되지 않음
4) Context 값 분리 — Provider 안에서 객체 쪼개기
문제: Context 값이 매 렌더마다 새로 생성
import { createContext, useContext, useState } from "react";
const ThemeContext = createContext<{ theme: string; setTheme: (t: string) => void } | null>(null);
export function AppBad() {
const [theme, setTheme] = useState("light");
const value = { theme, setTheme };
return (
<ThemeContext.Provider value={value}>
<Header />
<Content />
</ThemeContext.Provider>
);
}
문제점:
value객체가 매 렌더마다 새로 생성- 모든 구독자가 리렌더
해결:
useMemo로 안정화
import { createContext, useContext, useMemo, useState } from "react";
const ThemeContext = createContext<{ theme: string; setTheme: (t: string) => void } | null>(null);
export function App() {
const [theme, setTheme] = useState("light");
const value = useMemo(() => ({ theme, setTheme }), [theme]);
return (
<ThemeContext.Provider value={value}>
<Header />
<Content />
</ThemeContext.Provider>
);
}
효과:
theme이 변경될 때만 새value생성- 불필요한 리렌더 방지
5) useEffect 의존성 안정화
문제: 함수가 의존성에 포함되어 무한 루프
import { useEffect, useState } from "react";
export function DataFetcherBad() {
const [data, setData] = useState(null);
const fetchData = async () => {
const res = await fetch("/api/data");
const json = await res.json();
setData(json);
};
useEffect(() => {
fetchData();
}, [fetchData]);
return <div>{JSON.stringify(data)}</div>;
}
문제점:
fetchData가 매 렌더마다 새로 생성useEffect가 매번 실행 (무한 루프 가능) 해결:useCallback으로 안정화
import { useCallback, useEffect, useState } from "react";
export function DataFetcher() {
const [data, setData] = useState(null);
const fetchData = useCallback(async () => {
const res = await fetch("/api/data");
const json = await res.json();
setData(json);
}, []);
useEffect(() => {
fetchData();
}, [fetchData]);
return <div>{JSON.stringify(data)}</div>;
}
효과:
fetchData참조가 안정적useEffect가 마운트 시 한 번만 실행
고급 활용: 자식 메모·커스텀 비교
memo의 커스텀 비교 함수
기본 memo (얕은 비교)
import { memo } from "react";
const User = memo(function User({ user }: { user: { id: string; name: string } }) {
console.log("User 렌더");
return <div>{user.name}</div>;
});
커스텀 비교 함수
import { memo } from "react";
const User = memo(
function User({ user }: { user: { id: string; name: string; updatedAt: number } }) {
console.log("User 렌더");
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id;
}
);
주의사항:
- 커스텀 비교는 복잡도 증가
- 디버깅이 어려워질 수 있음
- 측정 후 필요할 때만 사용
React Compiler (실험적)
자동 메모이제이션
// React Compiler가 활성화되면 자동으로 최적화
export function AutoOptimized({ items }: { items: Item[] }) {
const filtered = items.filter((it) => it.score > 50);
return (
<ul>
{filtered.map((it) => (
<li key={it.id}>{it.label}</li>
))}
</ul>
);
}
현재 상태:
- React 19에서 실험적 기능
- 프로덕션에서는 수동 메모이제이션 권장
Server Components와의 조합
서버 컴포넌트에서 데이터 가져오기
// app/page.tsx (Server Component)
async function getData() {
const res = await fetch("https://api.example.com/data");
return res.json();
}
export default async function Page() {
const data = await getData();
return <ClientComponent data={data} />;
}
클라이언트 컴포넌트에서 메모이제이션
// components/ClientComponent.tsx
"use client";
import { useMemo } from "react";
export function ClientComponent({ data }: { data: Item[] }) {
const sorted = useMemo(() => {
return data.sort((a, b) => b.score - a.score);
}, [data]);
return (
<ul>
{sorted.map((it) => (
<li key={it.id}>{it.label}</li>
))}
</ul>
);
}
효과:
- 서버에서 데이터 가져오기 (클라이언트 부담 감소)
- 클라이언트에서 필요한 부분만 메모이제이션
성능·비교: 이득이 나는 조건
비교표
| 상황 | useMemo | useCallback |
|---|---|---|
| 무거운 순수 계산 | 후보 | 해당 없음 |
| 안정 참조가 필요한 객체/배열 props | 후보 | 객체를 만들 때 함수도 함께 고정하려면 둘 다 |
memo 자식에 넘기는 핸들러 | 보통 직접 이득 적음 | 자식 메모와 세트일 때 의미 |
| 이미 가벼운 연산 | 오버헤드만 증가 가능 | 동일 |
성능 벤치마크
테스트 환경: 10,000개 아이템, 검색 쿼리 변경
useMemo 없이
const visible = items.filter((it) => it.label.includes(query));
- 렌더 시간: ~50ms
- 매 렌더마다 필터링 실행
useMemo사용
const visible = useMemo(
() => items.filter((it) => it.label.includes(query)),
[items, query]
);
- 렌더 시간: ~5ms (캐시 히트 시)
query변경 시에만 필터링 실행 측정 방법: React DevTools Profiler
- Profiler 탭 열기
- 녹화 시작
- 상호작용 (입력, 클릭 등)
- 녹화 중지
- 커밋 시간 비교
메모이제이션 비용
useMemo 오버헤드:
- 의존성 배열 비교: ~0.1ms
- 메모리 사용: 이전 결과 저장 언제 손해인가?:
- 가벼운 연산 (< 1ms)
- 의존성이 매번 변경
- 메모리 제약이 큰 환경
실무 사례
사례 1: 차트 라이브러리 최적화
문제: 차트 데이터 가공이 무거움
import { useMemo } from "react";
import { LineChart } from "recharts";
export function SalesChart({ sales }: { sales: Sale[] }) {
const chartData = useMemo(() => {
return sales.map((sale) => ({
date: sale.date,
revenue: sale.items.reduce((sum, item) => sum + item.price * item.quantity, 0),
}));
}, [sales]);
return <LineChart data={chartData} />;
}
효과:
sales가 변경될 때만 데이터 가공- 차트 리렌더 성능 개선
사례 2: 가상 스크롤 최적화
문제: 스크롤 시 모든 아이템 리렌더
import { memo, useCallback, useMemo, useState } from "react";
const VirtualRow = memo(function VirtualRow({
index,
item,
onClick,
}: {
index: number;
item: Item;
onClick: (id: string) => void;
}) {
return (
<div onClick={() => onClick(item.id)}>
{index}: {item.label}
</div>
);
});
export function VirtualList({ items }: { items: Item[] }) {
const [selected, setSelected] = useState<string | null>(null);
const [scrollTop, setScrollTop] = useState(0);
const visibleItems = useMemo(() => {
const startIndex = Math.floor(scrollTop / 50);
return items.slice(startIndex, startIndex + 20);
}, [items, scrollTop]);
const handleClick = useCallback((id: string) => {
setSelected(id);
}, []);
return (
<div onScroll={(e) => setScrollTop(e.currentTarget.scrollTop)}>
{visibleItems.map((item, index) => (
<VirtualRow key={item.id} index={index} item={item} onClick={handleClick} />
))}
</div>
);
}
효과:
- 보이는 아이템만 렌더링
- 스크롤 성능 대폭 개선
사례 3: 폼 검증 최적화
문제: 입력마다 전체 폼 검증 실행
import { useMemo, useState } from "react";
export function SignupForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const errors = useMemo(() => {
const errs: string[] = [];
if (email && !email.includes("@")) {
errs.push("유효한 이메일을 입력하세요");
}
if (password && password.length < 8) {
errs.push("비밀번호는 8자 이상이어야 합니다");
}
return errs;
}, [email, password]);
return (
<form>
<input value={email} onChange={(e) => setEmail(e.target.value)} />
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
{errors.map((err, i) => (
<p key={i}>{err}</p>
))}
</form>
);
}
효과:
email이나password가 변경될 때만 검증- 불필요한 검증 방지
트러블슈팅
흔한 실수와 해결
| 실수 | 결과 | 해결 |
|---|---|---|
Profiler 없이 useCallback만 증설 | 의존성 비교 비용만 증가 | 병목 구간만 좁혀 적용 |
| Context에 매 렌더 새 객체 | 구독 컴포넌트 전부 리렌더 | 값 분리·useMemo로 value 안정화 |
useMemo 안에서 부수효과 | Concurrent 등에서 예측 어려움 | 부수효과는 useEffect로 |
memo만 쓰고 props 참조는 불안정 | 메모 무력화 | 콜백·객체를 의도적으로 안정화 |
증상별 해결 방법
증상: 메모했는데도 자식이 매번 렌더된다
// 문제
const Parent = () => {
const [count, setCount] = useState(0);
const config = { theme: "dark" };
return <Child config={config} />;
};
// 해결
const Parent = () => {
const [count, setCount] = useState(0);
const config = useMemo(() => ({ theme: "dark" }), []);
return <Child config={config} />;
};
증상: useMemo가 갱신이 안 된다
// 문제: 의존성 배열 누락
const filtered = useMemo(() => items.filter((it) => it.score > threshold), [items]);
// 해결: threshold 추가
const filtered = useMemo(() => items.filter((it) => it.score > threshold), [items, threshold]);
증상: 코드만 복잡해지고 체감 없음
// 문제: 가벼운 연산에 메모이제이션
const doubled = useMemo(() => count * 2, [count]);
// 해결: 메모이제이션 제거
const doubled = count * 2;
증상: Concurrent 렌더에서 이상하다
// 문제: useMemo 안에서 부수효과
const data = useMemo(() => {
logAnalytics("data computed");
return items.filter((it) => it.active);
}, [items]);
// 해결: useEffect로 분리
const data = useMemo(() => items.filter((it) => it.active), [items]);
useEffect(() => {
logAnalytics("data computed");
}, [data]);
ESLint 규칙 설정
exhaustive-deps 활성화
{
"rules": {
"react-hooks/exhaustive-deps": "error"
}
}
의존성 배열 자동 수정
npm run lint -- --fix
마무리
useMemo와 useCallback은 “렌더 비용 줄이기”와 “참조 안정화” 두 축으로 이해하면 선택이 쉬워집니다.
핵심 원칙:
- Profiler로 병목 확인 후 적용
- 가벼운 연산은 메모이제이션하지 않기
- 의존성 배열 정확히 관리 (ESLint 활용)
React.memo와 세트로 사용 (콜백 안정화)- 과도한 메모이제이션 경계 (코드 복잡도 증가) 패턴 전반은 JavaScript 패턴과 함께 보면 컴포넌트 설계까지 연결하기 좋습니다.
심화 부록: 구현·운영 관점
이 부록은 앞선 본문에서 다룬 주제(「React useMemo와 useCallback, 언제 쓰면 이득인가 | 렌더링 최적화 실전」)를 구현·런타임·운영 관점에서 다시 압축합니다. 도메인별 세부 구현은 글마다 다르지만, 입력 검증 → 핵심 연산 → 부작용(I/O·네트워크·동시성) → 관측의 흐름으로 장애를 나누면 원인 추적이 빨라집니다.
내부 동작과 핵심 메커니즘
flowchart TD A[입력·요청·이벤트] --> B[파싱·검증·디코딩] B --> C[핵심 연산·상태 전이] C --> D[부작용: I/O·네트워크·동시성] D --> E[결과·관측·저장]
sequenceDiagram participant C as 클라이언트/호출자 participant B as 경계(런타임·게이트웨이·프로세스) participant D as 의존성(API·DB·큐·파일) C->>B: 요청/이벤트 B->>D: 조회·쓰기·RPC D-->>B: 지연·부분 실패·재시도 가능 B-->>C: 응답 또는 오류(코드·상관 ID)
- 불변 조건(Invariant): 버퍼 경계, 프로토콜 상태, 트랜잭션 격리, FD 상한 등 단계별로 문장으로 적어 두면 디버깅 비용이 줄어듭니다.
- 결정성: 순수 층과 시간·네트워크·스케줄에 의존하는 층을 분리해야 테스트와 장애 분석이 쉬워집니다.
- 경계 비용: 직렬화, 인코딩, syscall 횟수, 락 경합, 할당·GC, 캐시 미스를 의심 목록에 둡니다.
- 백프레셔: 생산자가 소비자보다 빠를 때 버퍼·큐·스트림에서 속도를 줄이는 신호를 어디에 둘지 정의합니다.
프로덕션 운영 패턴
| 영역 | 운영 관점 질문 |
|---|---|
| 관측성 | 요청 단위 상관 ID, 에러율·지연 p95/p99, 의존성 타임아웃·재시도가 대시보드에 보이는가 |
| 안전성 | 입력 검증·권한·비밀·감사 로그가 코드 경로마다 일관적인가 |
| 신뢰성 | 재시도는 멱등 연산에만 적용되는가, 서킷 브레이커·백오프·DLQ가 있는가 |
| 성능 | 캐시·배치 크기·커넥션 풀·인덱스·백프레셔가 데이터 규모에 맞는가 |
| 배포 | 롤백 룬북, 카나리/블루그린, 마이그레이션·피처 플래그가 문서화되어 있는가 |
| 용량 | 피크 트래픽·디스크·FD·스레드 풀 상한을 주기적으로 검증하는가 |
스테이징은 데이터 양·네트워크 RTT·동시성을 프로덕션에 가깝게 맞출수록 재현율이 올라갑니다.
확장 예시: 엔드투엔드 미니 시나리오
앞선 본문 주제(「React useMemo와 useCallback, 언제 쓰면 이득인가 | 렌더링 최적화 실전」)를 배포·운영 흐름에 맞춰 옮긴 체크리스트입니다. 도메인에 맞게 단계 이름만 바꿔 적용할 수 있습니다.
- 입력 계약 고정: 스키마·버전·최대 페이로드·타임아웃·에러 코드를 경계에 둔다.
- 핵심 경로 계측: 요청 ID, 단계별 지연, 외부 호출 결과 코드를 로그·메트릭·트레이스에서 한 흐름으로 본다.
- 실패 주입: 의존성 타임아웃·5xx·부분 데이터·락 대기를 스테이징에서 재현한다.
- 호환·롤백: 설정/마이그레이션/클라이언트 버전을 되돌릴 수 있는지 확인한다.
- 부하 후 검증: 피크 대비 p95/p99, 에러율, 리소스 상한, 알림 임계값을 점검한다.
handle(request):
ctx = newCorrelationId()
validated = validateSchema(request)
authorize(validated, ctx)
result = domainCore(validated)
persistOrEmit(result, idempotentKey)
recordMetrics(ctx, latency, outcome)
return result
문제 해결(Troubleshooting)
| 증상 | 가능 원인 | 조치 |
|---|---|---|
| 간헐적 실패 | 레이스, 타임아웃, 외부 의존성, DNS | 최소 재현 스크립트, 분산 트레이스·로그 상관관계, 재시도·서킷 설정 점검 |
| 성능 저하 | N+1, 동기 I/O, 락 경합, 과도한 직렬화, 캐시 미스 | 프로파일러·APM으로 핫스팟 확인 후 한 가지씩 제거 |
| 메모리 증가 | 캐시 무제한, 구독/리스너 누수, 대용량 버퍼, 커넥션 미반납 | 상한·TTL·힙/FD 스냅샷 비교 |
| 빌드·배포만 실패 | 환경 변수, 권한, 플랫폼 차이, lockfile | CI 로그와 로컬 diff, 런타임·이미지 버전 핀 |
| 설정 불일치 | 프로필·시크릿·기본값, 리전 | 스키마 검증된 설정 단일 소스와 배포 매트릭스 표준화 |
| 데이터 불일치 | 비멱등 재시도, 부분 쓰기, 캐시 무효화 누락 | 멱등 키·아웃박스·트랜잭션 경계 재검토 |
권장 순서: (1) 최소 재현 (2) 최근 변경 범위 축소 (3) 환경·의존성 차이 (4) 관측으로 가설 검증 (5) 수정 후 회귀·부하 테스트.
배포 전에는 git add → git commit → git push 후 npm run deploy 순서를 권장합니다.
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. React에서 useMemo·useCallback은 참조 동일성과 비용 큰 계산을 묶는 도구입니다. 원리·사용 시기·과최적화 피하기·Profiler로 확인하는 법을 정리했습니다. 실전 예제와 코드로 개념부터 활용까지… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ Parallel Algorithms | ‘병렬 알고리즘’ 가이드
- CSS 애니메이션 | Transition, Animation, Transform
- SQL 쿼리 최적화 실전 가이드 | 인덱스·실행 계획
이 글에서 다루는 키워드 (관련 검색어)
React, useMemo, useCallback, 최적화, 렌더링, 메모이제이션, hooks 등으로 검색하시면 이 글이 도움이 됩니다.