심화 · 개념 시각화

개념 시각화 — 눈으로 보는 React

React가 어려운 건 대부분 눈에 안 보이는 동작 때문입니다. 언제 리렌더? 되는지, useRef는 왜 화면을 안 바꾸는지, useEffect가 언제 도는지, useMemo가 정말 캐시?를 쓰는지 — 직접 눌러보고 숫자와 로그로 확인하세요.

1. 리렌더 비교 — useState vs useRef

두 박스 모두 "이 컴포넌트가 몇 번 그려졌는지"를 표시합니다. useState 값을 바꾸면 숫자도 화면도 바뀌지만, useRef 값을 바꾸면 렌더 횟수도, 화면의 값도 그대로예요. 직접 눌러 비교해보세요.

관찰 포인트
  • useState 값 +1 → 화면의 값과 렌더 횟수가 함께 증가
  • useRef 값 +1 → 아무 변화 없음(렌더 안 함). 그러다 강제 리렌더를 누르면 그동안 쌓인 값이 한꺼번에 보임 → 값은 바뀌고 있었지만 화면만 안 그려졌던 것

→ 자세한 설명: 2장 useRef

2. useEffect 타임라인 — 언제 실행되나

버튼을 누르면 useEffect정리 함수?언제 실행되는지 아래 로그에 시간순으로 찍힙니다. "dep 변경"을 누르면 정리 → 다시 실행 순서로 도는 걸 볼 수 있어요.

관찰 포인트
  • 처음 — "이펙트 실행 (dep=0)" 한 번 (의존성 []가 아니라 [dep]라 첫 마운트에 실행)
  • dep 변경 — "cleanup 실행 (dep=0)" → "이펙트 실행 (dep=1)". 정리가 먼저!
  • 언마운트 — "cleanup 실행"만. 컴포넌트가 사라질 때 뒷정리

→ 자세한 설명: 2장 정리 함수

3. useMemo 캐싱 — 정말 다시 계산할까?

useMemo관련 값(의존성)이 바뀔 때만 다시 계산하고, 그 외에는 저장해둔 결과를 재사용합니다. 아래 "실제 계산 실행 횟수"를 보면 진짜 그런지 알 수 있어요.

관찰 포인트
  • a 변경 → "계산 실행 횟수" 증가 (의존성이 바뀌어 재계산)
  • b 변경 → 화면(b)은 바뀌지만 "계산 실행 횟수"는 그대로 (캐시 재사용)

4. prop drilling vs useContext

값 하나를 깊은 컴포넌트(D)에 전달하는 두 방법을 비교합니다. props로 보내면 중간 컴포넌트(B·C)가 쓰지도 않으면서 계속 넘겨줘야 하고(prop drilling), useContext를 쓰면 B·C는 모른 채 D가 바로 꺼내 씁니다. 버튼으로 전환해보세요.

관찰 포인트
  • props 모드 — B·C에 "(user 중계만 함)" 표시. 자기가 안 쓰는 값을 억지로 넘김
  • context 모드 — B·C는 깨끗(중계 없음). D가 useContext로 직접 꺼냄

→ 자세한 설명: 2장 useContext

5. 렌더 → 재조정 → 가상 DOM (단계별)

상태가 바뀌면 React가 화면을 어떻게 "조금만" 바꾸는지 단계별로 봅니다. 가상 DOM?으로 먼저 비교(재조정?)하고 달라진 부분만 실제 화면에 반영해요.

1단계. 버튼 클릭 → count가 0에서 1로. React가 컴포넌트 함수를 다시 실행합니다(리렌더).

setCount(1)상태 변경
Counter()함수 다시 실행

2단계. 실행 결과로 새 가상 DOM(가벼운 설계도)을 만듭니다.

새 가상 DOM
<h1> 제목
<p> count: 1
<button> +1

3단계. 이전 가상 DOM과 비교(재조정). 달라진 곳은 <p>의 글자뿐!

이전
<h1> 제목
<p> count: 0
<button> +1
vs
새 것
<h1> 제목
<p> count: 1
<button> +1

4단계. 실제 DOM은 그 <p> 하나만 바꿉니다. 나머지는 건드리지 않아 빠릅니다.

실제 화면(DOM)
<h1> 제목
<p> count: 1 ✓
<button> +1
달라진 곳 실제로 반영

6. useQuery 생명주기 — 캐시와 무효화 (단계별)

React Query?가 서버 데이터를 어떻게 캐시?하고, 데이터 변경 후 무효화?로 다시 불러오는지 단계별로 봅니다.

1단계. 화면이 useQuery(['users'])로 목록을 요청. 캐시에 아직 없음(캐시 미스).

화면
useQuery(['users'])
캐시
(비어 있음)
서버

2단계. 서버에 요청해 응답을 기다립니다(로딩 중 = isLoading).

화면
⏳ 로딩…
캐시
서버
데이터 준비

3단계. 응답 도착 → 캐시에 저장 → 화면에 데이터 표시(data).

화면
목록 표시
캐시
['users'] 저장됨
서버

4단계. 다른 화면에서 같은 useQuery(['users'])캐시 히트! 서버 요청 없이 즉시 표시.

다른 화면
즉시 표시
캐시
['users'] 재사용
서버
요청 안 함

5단계. 데이터를 바꿈(useMutation으로 추가/수정/삭제) 후 invalidateQueries(['users']) → 캐시를 '오래됨'으로 표시.

useMutation
저장 완료
캐시
['users'] 오래됨

6단계. '오래됨' 캐시는 자동으로 다시 요청(refetch)해 최신 데이터로 갱신됩니다.

화면
최신 목록
캐시
새로 채움
서버
refetch
활성/요청 캐시 보관

→ 자세한 설명: 4장 상태관리 라이브러리