본문 바로가기
React

React 개발 주의점 요약

by 코딩마스터^^ 2026. 2. 9.

1️⃣ React를 “UI 라이브러리”로 다시 이해하기

핵심 관점

  • React는 MVC / MVVM 프레임워크가 아님
  • React의 책임은 딱 하나:
  • 상태(state) → UI를 어떻게 그릴지 선언하는 것

👉

  • 데이터 fetching ❌
  • 상태 저장 전략 ❌
  • 라우팅 ❌

이건 전부 React 바깥의 문제
(그래서 Next.js, React Query, Zustand 같은 도구가 붙음)


2️⃣ React의 렌더링 모델 (중급 핵심)

React 렌더링 = 2단계

  1. Render Phase
    • JSX → Virtual DOM 계산
    • 순수 계산 단계
    • 부작용 ❌
  2. Commit Phase
    • 실제 DOM 반영
    • useEffect 실행
    • 브라우저 페인트

👉 이걸 이해해야:

  • 왜 렌더링이 여러 번 일어나는지
  • useEffect에 로직 넣으면 문제가 되는지
  • 성능 최적화가 필요한지 감이 옴

렌더링이 발생하는 조건

  • state 변경
  • props 변경
  • 부모 컴포넌트 리렌더링

⚠️ 부모 리렌더링 = 자식 전부 리렌더링
(값 안 바뀌어도!)


3️⃣ 상태(State)를 어떻게 나눌 것인가

❌ 나쁜 상태 설계

  • 모든 상태를 최상단에 몰아넣음
  • 필요 이상으로 공유
  • 렌더링 범위가 과도하게 커짐

✅ 좋은 기준

“이 상태를 누가 쓰는가?”

  • 한 컴포넌트만 사용 → 로컬 state
  • 몇 개의 형제 컴포넌트 → 공통 부모
  • 앱 전역 → 전역 상태 (필요할 때만)

👉 state는 최대한 아래에 둔다


4️⃣ props drilling vs 전역 상태

props drilling 문제

  • 3~4단계만 내려가도 가독성 급락
  • 중간 컴포넌트는 값도 안 쓰는데 전달만 함

해결 전략

  • Context → 전역 설정 / 테마 / 인증 정보
  • 전역 상태 라이브러리 → 비즈니스 상태

⚠️ Context 남용 ❌
→ 값 바뀌면 구독 중인 컴포넌트 전부 리렌더링


5️⃣ useEffect를 “부작용 관리자”로 쓰기

useEffect의 본질

  • 렌더링 이후에 실행되는 부작용 처리기
  • 렌더링 로직과 분리되어야 함

❌ 흔한 실수

  • 계산 로직을 useEffect에 넣음
  • state → state 연쇄 업데이트
  • 의존성 배열 무시

✅ 좋은 패턴

  • 렌더링 = JSX에서
  • 부작용 = useEffect에서
  • 계산 = useMemo / 함수

 

 

🧠 useEffect를 “부작용 관리자”로 이해하기

0️⃣ 한 문장 정의 (이거 하나만 기억해도 반은 끝)

useEffect는 ‘렌더링 결과를 바탕으로, React 바깥 세계에 영향을 주는 코드’를 관리하는 곳이다.


1️⃣ “부작용(side effect)”이 뭐냐면

React 안에서의 “정상적인 일”

  • state 계산
  • props 받아서 JSX 반환
  • 화면 그리기

👉 이건 React 내부 세계


“부작용”이란?

React가 모르는 바깥 세계에 손을 대는 것

예를 들면:

  • 서버 API 호출
  • 이벤트 리스너 등록
  • setInterval / setTimeout
  • localStorage 접근
  • console.log (사실 이것도 side effect)

👉 이런 건 렌더링과 분리돼야 함


2️⃣ 왜 렌더링 중에 하면 안 될까?

React 렌더링의 특징

  • 여러 번 실행될 수 있음
  • 중간에 취소될 수도 있음
  • 같은 렌더링을 두 번 할 수도 있음 (Strict Mode)

그래서 이런 코드가 문제됨

 
function Component() { fetch('/api/data'); // ❌ 렌더링 중 side effect return <div />; }

👉 렌더링 = “계산”이어야 하는데
👉 바깥 세상에 계속 요청을 보내버림


3️⃣ useEffect는 왜 안전한가?

React가 보장하는 것

  • DOM에 반영
  • 화면이 그려진 다음
  • 한 번만 / 필요할 때만 실행
 
useEffect(() => { fetch('/api/data'); // ⭕ }, []);

👉 그래서 useEffect는
“렌더링 이후에 실행되는 부작용 전용 공간”


4️⃣ 흔한 오해: “state 바꾸는 것도 부작용 아닌가요?”

좋은 질문이고, 많이 헷갈림.

❌ 잘못된 생각

 
useEffect(() => { setCount(count + 1); }, [count]);

👉 이건 부작용 관리가 아니라 렌더링 제어 실패


이유

  • state 변경 → 렌더링
  • 렌더링 → effect 실행
  • effect → state 변경
  • 무한 루프 💥

👉 useEffect로 상태 흐름을 만들면 안 됨


5️⃣ 언제 useEffect를 써야 하나? (판단 공식)

이 질문 하나로 결정해:

“이 코드가 없어도 화면은 그려질 수 있는가?”

YES → useEffect

  • API 호출
  • 로그
  • 이벤트 연결
  • 외부 라이브러리 초기화

NO → 렌더링 로직

  • JSX
  • 조건 분기
  • 계산 값

6️⃣ 올바른 역할 분리 예제

❌ 나쁜 예 (로직 섞임)

 
useEffect(() => { const total = price * count; setTotal(total); }, [price, count]);

✅ 좋은 예 (역할 분리)

 
const total = price * count;

👉 계산은 렌더링
👉 부작용은 effect


7️⃣ 의존성 배열은 “트리거 선언”이다

이렇게 생각해

 
useEffect(() => { doSomething(); }, [a, b]);

👉 뜻:

“a 또는 b가 바뀌면, 이 부작용을 다시 실행해줘”

❌ “한 번만 실행하려고 [] 쓴다”
⭕ “아무것도 의존하지 않는다”


8️⃣ cleanup 함수의 진짜 의미

 
useEffect(() => {
window.addEventListener('resize', handler);
 
   return () => {
   window.removeEventListener('resize', handler);
   };
 
}, []);

👉 cleanup은:

  • 컴포넌트가 사라질 때
  • 다음 effect 실행 전에

“이전 부작용을 되돌리는 코드”


9️⃣ useEffect가 많아질수록 생기는 문제

신호들

  • effect가 3개 이상
  • effect 안에서 state 변경
  • 의존성 배열이 이해 안 됨

👉 이건 설계 문제

해결책

  • effect 쪼개기
  • 로직을 custom hook으로 분리
  • derived state 제거

🔟 실무에서 이렇게 생각하면 편하다

useEffect 체크리스트

  • 이 코드가 React 바깥에 영향을 주는가?
  • 렌더링 중 실행되면 문제가 되는가?
  • 실행 시점을 React에게 맡겨야 하는가?

👉 3개 다 YES면 useEffect


🧠 진짜 핵심 한 줄 요약

useEffect는 “렌더링 이후에만 실행되어야 하는 코드의 격리 공간”이다.


💥 이해됐는지 확인용 질문

이 코드, useEffect 써야 할까?

 
const filtered = list.filter(item => item.active);

👉 답: NO
이유: 렌더링 계산일 뿐, 부작용 없음


다음으로 하면 이해가 완전히 굳는다 👇

  • 🔥 useEffect 안 쓰고 API 호출 구조 짜보기
  • 🔥 무한 루프 생기는 이유 디버깅
  • 🔥 “useEffect 지옥” 리팩토링 실전

6️⃣ 의존성 배열을 다루는 기준

핵심 원칙

useEffect는 “이 값이 바뀌면 다시 실행”이라는 선언

  • eslint 경고 무시 ❌
  • 의존성 줄이려고 값 숨기기 ❌

대안

  • 로직 분리
  • effect 쪼개기
  • custom hook으로 추출

7️⃣ 성능 최적화, 언제 해야 하나

기본 원칙

“느릴 때만 최적화한다”

 


자주 쓰이는 도구

  • React.memo
  • useMemo
  • useCallback

하지만

  • 모든 컴포넌트에 쓰면 ❌
  • 메모이제이션도 비용임

👉 렌더링 병목 지점에만 사용


8️⃣ React Compiler 관점에서의 변화

React Compiler 목적

  • 개발자가 직접 memo 관리 안 해도 되게
  • 불필요한 리렌더링 자동 제거

👉 중급 개발자에게 중요한 포인트:

  • 순수 함수형 컴포넌트 작성 습관
  • 부작용 없는 렌더링

9️⃣ Server Component / Client Component 분리 사고

서버 컴포넌트

  • 데이터 가져오기
  • 무거운 로직
  • JS 번들 줄이기

클라이언트 컴포넌트

  • 인터랙션
  • 상태
  • 이벤트

👉 가능하면 서버, 필요하면 클라이언트


🔟 React 구조 설계 감각

좋은 컴포넌트의 조건

  • 하나의 책임
  • props 이름이 명확
  • 상태가 최소화됨
  • 테스트 가능

흔한 안티패턴

  • 거대한 컴포넌트
  • UI + 비즈니스 로직 섞임
  • useEffect 지옥
  • 상태 중복

⚠️ 중급자가 꼭 넘어야 할 함정들

  • “Context = 전역 상태”라고 생각 ❌
  • 모든 상태를 Redux/Zustand에 넣음 ❌
  • 렌더링 원인 추적 못함
  • effect가 많아질수록 구조 붕괴

🧠 중급 개발자 한 줄 요약

  • React는 상태 → UI 선언 도구
  • 렌더링 원인을 항상 의식하라
  • 상태는 최소, 하단, 국소적
  • effect는 부작용 전용
  • 최적화는 문제 생길 때만
  • 구조가 성능을 만든다

댓글