학습 공간 개념 · 판단 · 실험

Frontend Performance Lab

React

Effect의 동기화와 자원 정리

Effect는 렌더링 결과를 외부 시스템과 동기화하며 cleanup 함수로 이전 연결을 안전하게 정리합니다.

이럴 때 필요해요

채팅방 구독 누수, 오래된 API 응답 덮어쓰기, 화면 깜빡임과 개발 모드 중복 실행의 원인을 정확히 설명합니다.

어떻게 동작하나요?

사용자가 채팅방 목록에서 '1번 방'을 누르면 그 방의 서버 소켓에 연결하고, '2번 방'으로 바꾸면 1번 방 연결을 끊고 2번 방에 새로 연결해야 합니다. 이처럼 렌더링된 React 상태를 브라우저 외부 시스템(네트워크, 타이머, DOM 이벤트 등)과 맞추는 작업을 Effect로 다룹니다. 반면 '구매하기 버튼 클릭'처럼 특정 사용자 행동으로 일어나는 일은 이벤트 핸들러에 두고, 기존 props·state로 바로 계산되는 값은 렌더 본문에서 계산합니다.

useEffect는 의존성 배열의 값이 바뀔 때마다 이전 cleanup 함수를 먼저 실행해 기존 연결을 정리한 뒤, 새로운 값으로 setup 함수를 실행합니다. 개발 환경의 Strict Mode에서 컴포넌트를 한 번 더 마운트·해제해 보는 것도 이 setup과 cleanup이 짝이 맞게 작성되었는지 검증하기 위해서입니다. 따라서 빈 배열([])을 넣었다고 해서 어떤 환경에서든 무조건 단 한 번만 실행된다는 보장으로 생각하면 안 됩니다.

만약 툴팁 위치 계산처럼 브라우저가 화면을 그리기(Paint) 직전에 DOM 크기를 재고 스타일을 바로 고쳐야 화면 깜빡임을 막을 수 있다면 useLayoutEffect를 사용합니다. 그 외의 일반적인 외부 동기화에는 화면 표시를 막지 않는 useEffect를 씁니다. 또한 Effect 안에서 데이터를 요청할 때는 AbortController나 취소 플래그로 이전 요청의 늦은 응답을 무시하되, 클라이언트 취소가 이미 서버에서 처리된 변경까지 되돌려 주지는 않는다는 점도 기억해야 합니다.

코드 예제

동작을 설명하는 읽기 전용 예제입니다.

example.jsx
useEffect(() => {
  const connection = connect(roomId);
  connection.open();
  return () => connection.close();
}, [roomId]);
jsx · 읽기 전용5줄

설명용 의사 코드입니다. roomId가 바뀔 때마다 cleanup 함수가 이전 방 연결을 먼저 닫아 주고, 이어서 새 roomId로 연결을 엽니다.

자주 헷갈리는 포인트

  • useEffect를 클래스 컴포넌트 수명주기(componentDidMount 등)의 단순 1:1 별명으로 설명하지 않습니다.
  • 실행 횟수를 억지로 줄이려고 Effect 내부에서 실제로 읽는 반응형 값을 의존성 배열에서 빼서 오래된 값을 참조하는 버그를 만들지 않습니다.

내 말로 설명해 보기

채팅방 ID가 A에서 B로 바뀔 때 이전 연결 정리(cleanup)와 새 연결 설정(setup)은 어떤 순서로 실행되나요?

실전 시나리오에 적용해 보기

함께 읽으면 좋은 개념

관련 면접 질문 4개

React 컴포넌트의 수명은 어떻게 이해해야 하나요?

컴포넌트는 트리에 처음 추가되는 마운트(Mount), 입력 변화에 따라 화면을 다시 계산하는 업데이트(Update), 트리에서 제거되는 언마운트(Unmount) 과정을 거칩니다. 클래스 컴포넌트는 이를 수명주기 메서드로 다루고, 함수 컴포넌트는 순수한 렌더 계산과 useEffect를 통한 외부 시스템 동기화의 시작·정리(cleanup)로 나누어 다룹니다. 특히 렌더 호출 횟수와 실제 커밋 횟수가 항상 같지는 않으므로 외부 자원 연결은 언제든 안전하게 정리하고 재연결할 수 있도록 작성해야 합니다.

꼬리질문과 참고자료 →

클래스 컴포넌트의 주요 수명주기 메서드는 어떤 책임을 갖나요?

클래스 컴포넌트에서 render는 부작용 없이 다음 UI를 계산하고, componentDidMount는 초기 마운트 직후의 외부 연결이나 초기 요청을, componentDidUpdate는 커밋된 업데이트 이후의 동기화를, componentWillUnmount는 구독 해제 같은 자원 정리를 담당합니다. 특히 componentDidUpdate에서 상태를 갱신할 때는 이전 props·state와 반드시 비교해 무한 업데이트 루프를 막아야 합니다. 또한 과거의 UNSAFE_ 계열 메서드는 새로운 코드의 기본 패턴으로 사용하지 않습니다.

꼬리질문과 참고자료 →

함수 컴포넌트에서는 수명주기와 비슷한 작업을 어떻게 구성하나요?

함수 컴포넌트에서는 수명주기 시점이 아니라 '외부 시스템과의 동기화'를 기준으로 useEffect의 setup과 cleanup을 구성합니다. 의존성 배열에 포함된 반응형 값이 바뀌면 이전 연결을 cleanup으로 정리한 뒤 새 값으로 setup을 다시 실행하고, 컴포넌트가 언마운트될 때도 마지막으로 정리합니다. 반면 사용자 클릭처럼 특정 사건으로 일어나는 작업은 이벤트 핸들러에 두고, 기존 props·state로 즉시 계산 가능한 값은 렌더 본문에서 바로 계산합니다.

꼬리질문과 참고자료 →
나머지 질문 1개

공식 참고 자료

← React