리액트 훅 | useEffect에 대해 알아보기

2025. 8. 11. 15:15·FRONTEND/React

리액트를 이용하다가 자주 만나는 훅 중 하나가 바로 useEffect입니다.

이번에는 useEffect에 대해 알아보려고 하는데,

그전에 먼저 부수 효과(side effect)에 대해 가볍게 살펴보겠습니다.

 

0. 부수 효과(side effect)란?

리액트 컴포넌트는 기본적으로 상태(state)를 화면에 그리는 일이 주된 목적입니다.

 

하지만 UI를 그리는 것 외에도 이러한 부가적인 동작이 필요할 때가 있습니다.

- 서버에서 데이터 가져오기(API 호출)

- 브라우저 이벤트 등록/해제

- 로컬스토리지나 쿠키에 값 저장 

...

이처럼 UI 외부에 영향을 주거나, 외부로부터 데이터를 가져오는 일을 부수 효과라고 부릅니다.

리액트에서는 이러한 부수 효과를 안전하게 다루기 위해 useEffect 훅을 제공합니다.

 

부수 효과를 그냥 실행하면 마주할 수 있는 문제

: 리액트의 렌더링은 여러 번 발생할 수 있으며 DOM이 완전히 준비되기 전에도 함수가 실행될 수 있습니다.

- DOM이 없는 상태에서 실행되어 오류 발생

- 같은 작업이 여러번 중복 실행

- 이벤트나 타이머가 해제되지 않아 메모리 누수 발생

 

useEffect의 역할

이러한 문제를 막기 위해 "렌더링이 끝난 뒤" 실행되며,

cleanup 기능을 통해 사용이 끝난 리소스를 안전하게 정리할 수 있게 도와줍니다.

 

1. useEffect란?

컴포넌트가 렌더링된 이후 부수 효과(side effect)를 수행하기 위한 훅입니다.

쉽게 말해 "UI를 그린 다음에 실행할 추가 작업"을 넣는 곳이라고 볼 수 있습니다.

 

기본 문법

useEffect는 크게 2개의 매개변수를 받습니다.

useEffect(() => {
  // 실행할 코드
  return () => {
    // cleanup 코드 (선택)
  };
}, [의존성배열]);

- 첫번째 인자 : 실행할 함수(부수 효과 코드)

- 두번째 인자 : 의존성 배열 - 언제 실행할지를 결정

 

2. 기본 동작 원리

(1) 컴포넌트가 렌더링 된 후, useEffect의 첫 번째 인자가 실행

(2) 의존성 배열에 지정한 값이 변경될 때마다 다시 실행

(3) [cleanup 함수가 존재한다면] 이때 이전 실행에서 반환한 cleanup 함수가 먼저 호출되어 기존 작업 정리

(4) [cleanup 함수가 존재한다면] 컴포넌트가 화면에서 사라질 때도 cleanup 호출

 

3. 예시

(0) 매 마운트 시 실행

- 의존성 배열이 없는 경우, 매 마운트 시 실행

// 0. 모든 마운트 시 실행
useEffect(() => {
  console.log('모든 마운트 시 실행 !! ');
});

 

(1) 마운트 시 한번 실행

- 의존성 배열이 [] 인 경우, 처음 마운트 시에만 실행 

// 1. 마운트 시 한 번 실행 (빈 배열)
useEffect(() => {
  console.log('컴포넌트 처음 마운트');
}, []);

 

(2) 특정 값 변경 감지

- 의존성 값(count)이 바뀔 때만 실행

// 2. 특정 값(count)이 마운트 될 때마다 실행
useEffect(() => {
  console.log(`count값 변경: ${count}`);
}, [count]);

 

(3) 입력값 변경 감지

- 의존성 값(text)가 바뀔 때만 실행

// 3. 입력값이 들어올 때마다 실행
useEffect(() => {
  console.log(`입력값 변경 : ${text}`);
}, [text]);

 

(4) 의존성 배열 기반 제약 로직

- 상태 값이 특정 범위를 넘어가지 않도록 제약

// 4. 최대 클릭 수 지정해서 실행
useEffect(() => {
  console.log(`현재 클릭 횟수 : ${cnt}, 최대 5번까지 클릭 가능 !`);
  if(cnt > 5) {
      alert('최대 5번까지 클릭이 가능합니다 !');
      setCnt(5);
  }
}, [cnt]);

 

(5) cleanup 함수

cleanup 함수의 경우 다음과 같이 동작됩니다.

  1) useEffect 내부 로직 실행, 타이머 설정 
  2) 컴포넌트가 언마운트되거나, 의존성 배열 값이 변경될 때, return 안의 cleanup 함수 실행 

  3) 이전 Effectd에서 만든 타이머, 이벤트 리스터, 구독 등 부수효과를 정리

즉, return은 이전 상태를 깨끗하게 치우는 공간이라고 생각하면 됩니다 !이를 수행하지 않으면 메모리 누수, 중복 이벤트 등록과 같은 문제가 발생할 수 있습니다.

// 5. 카운트다운
useEffect(() => {
  if(!isRunning) return; // 실행 상태가 아니면 종료

  const timer = setInterval(() => {
      setSeconds((prev) => {
        if (prev <= 1) {
          clearInterval(timer); // 0 되면 멈춤
          return 0;
        }
        return prev - 1;
      });
  }, 1000);

  return() => {
      clearInterval(timer); // 언마운트/중지 시 정리
  };
}, [isRunning]);

- useEffect = 내가 방에 들어올 때 하는 일
- cleanup(return) = 방에서 나갈 때 치우는 일
- setInterval = 방 안에 알람시계 하나 두고 1초마다 종 치게 하는 것
- if (!isRunning) return = 이번에 방 들어왔는데, 시계 안 놓고 그냥 나가는 것

- 시작 버튼 누름 (isRunning = true)
  → 방에 들어옴(run A) 새 시계(인터벌 A)를 방 안에 둠
  → 1초마다 seconds를 줄어듦

  → return () => clearInterval(timer)라는 “나갈 때 이 시계 치워라” 메모를 붙이고 나감

- 정지 버튼 누름 (isRunning = false)
  → 다시 방에 들어오기 전(run B 시작 전) React는 규칙상 먼저 이전 run(A)의 cleanup 호출: cleanup 실행
  → 방 안의 시계(인터벌 A) 치움
  → 종이 멈추고 이제 새 run B 실행
  → if (!isRunning)에 걸려 새 시계 안 만듦


- 결과
cleanup 덕분에 이전에 두고 간 시계가 사라져서, seconds를 깎는 동작 자체가 완전히 멈춤
정지 버튼을 누르는 순간부터 시간 변화가 아예 없음
다시 시작 버튼을 누르면 새로운 시계(인터벌 B)가 만들어져서 처음부터 다시 시작

만약, cleanup 부분을 주석처리 했다면? 
정지 버튼을 눌러도 타이머는 계속 진행됩니다.
// return() => {
//    clearInterval(timer); // 언마운트/중지 시 정리
// };

 

시작 버튼 누름
→ isRunning = true
→ 방에 들어오면서 알람시계(인터벌)를 하나 둠.
→ 이 시계는 1초마다 “초마다 줄여라” 하고 종을 침

정지 버튼 누름
→ isRunning = false
→ 방에 들어올 차례(run B)
→ 들어오자마자 if문에서 “이번엔 시계 안 둘래” 하고 그냥 나감
→ 근데 이전에 두고 간 시계는 그대로 방 안에서 종을 치고 있음 그래서 시계는 계속 1초마다 seconds를 줄임
→ 잠깐 멈춘 것 같다가 다시 줄어듦
→ 0이 되면, 시계에 붙여둔 “0이면 스스로 멈춰라” 메모 덕분에 그제야 시계가 멈춤


이번 예제들을 통해 useEffect는 단순히 렌더링 후 실행되는 함수가 아니라,

컴포넌트의 상태 변화나 생명주기에 맞춰 부수 효과를 안전하게 관리하는 도구라는 걸 배웠습니다.

 

특히나, return을 통한 정리가 메모리 누수와 불필요한 동작을 방지한다는 점,

의존성 배열에 따라 실행 시정이 달라진다는 점과 같이

결국 언제 실행할지, 언제 정리할지를 명확히 설계하는 것이 useEffect를 잘 쓰는 핵심이라는 걸 배웠습니다.

'FRONTEND > React' 카테고리의 다른 글

리액트 훅 | useMemo에 대해 알아보기  (6) 2025.08.13
리액트 훅 | useCallback에 대해 알아보기  (8) 2025.08.12
리액트 훅 | useState에 대해 알아보기  (4) 2025.08.08
부모 요소 패딩 값에 영향을 받지 않고 전체 너비 구분선 그리기  (0) 2025.04.04
모던 리액트 Deep Dive | 1.1 자바스크립트와 동등 비교  (0) 2025.03.09
'FRONTEND/React' 카테고리의 다른 글
  • 리액트 훅 | useMemo에 대해 알아보기
  • 리액트 훅 | useCallback에 대해 알아보기
  • 리액트 훅 | useState에 대해 알아보기
  • 부모 요소 패딩 값에 영향을 받지 않고 전체 너비 구분선 그리기
uoaheu
uoaheu
uoaheu 님의 블로그 입니다.
  • uoaheu
    uoaheu 님의 블로그
    uoaheu
  • 전체
    오늘
    어제
    • 분류 전체보기 (50)
      • 알고리즘 (7)
      • CS (9)
      • FRONTEND (9)
        • React (12)
        • Kotlin (1)
        • JS (5)
        • HTML (2)
      • SQL (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    toss 분석
    toss uiux
    리액트usestate
    useactionstate
    엘지유플러스유레카프론트엔드
    멀티캠퍼스it부트캠프
    백준1926번
    토스 uiux
    알고리즘
    토스 앱 분석
    이더넷프레임
    토스분석
    mysql로 피벗테이블만들기
    유레카3기
    혼자서 공부하는 네트워크
    mysql
    부트캠프후기
    boj25418
    mysql 피벗테이블
    BFS
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
uoaheu
리액트 훅 | useEffect에 대해 알아보기
상단으로

티스토리툴바