React 라이프사이클 & Effect Hook

김명원·2024년 12월 23일
0

learnReact

목록 보기
7/26

🎣 Hooks & 라이프사이클

🌟 React에서 Hook

React에서 Hook은 함수형 컴포넌트에서 상태 관리와 라이프사이클 메서드를 사용할 수 있게 해주는 강력한 도구입니다.

🔧 useState

  • 상태 관리를 위한 기본적인 Hook.

📜 React HOOK 규칙

1️⃣ 훅은 반드시 최상위 레벨에서만 호출해야 합니다.

  • 반복문, 조건문, 중첩된 함수 내에서 훅 호출은 금지입니다.
  • 이유: 리액트는 훅이 항상 동일한 순서로 호출된다고 가정하기 때문입니다. 이를 어기면 상태 관리와 훅 동작에 오류가 발생할 수 있습니다.
// ❌ 잘못된 예시
function MyComponent() {
  if (someCondition) {
    useEffect(() => {
      // ...
    }, []);
  }

  for (let i = 0; i < 3; i++) {
    useState(i);  // 반복문 내에서 훅 호출 금지
  }

  function nestedFunction() {
    useState(0);  // 중첩된 함수 내에서 훅 호출 금지
  }

  return <div>안녕하세요!</div>;
}

// ✅ 올바른 예시
function MyComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('컴포넌트가 마운트되었습니다.');

    return () => {
      console.log('컴포넌트가 언마운트되었습니다.');
    };
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

2️⃣ 훅은 리액트 함수 내에서만 호출해야 합니다.

  • 반드시 리액트 함수 컴포넌트커스텀 훅에서 호출해야 합니다.
  • 이유: 리액트의 상태 관리와 효과 처리는 컴포넌트 내부에서만 유효하기 때문입니다.
// ❌ 잘못된 예시: 일반 함수에서 훅 호출
function notAReactComponent() {
  const [state, setState] = useState(0); // 오류 발생
}

// ✅ 올바른 예시: 리액트 함수 컴포넌트에서 훅 호출
function MyComponent() {
  const [state, setState] = useState(0);
  return <div>안녕하세요!</div>;
}

// 🎯 커스텀 훅 예시
function useCustomHook() {
  const [state, setState] = useState(0);

  useEffect(() => {
    console.log('커스텀 훅이 실행되었습니다.');
  }, []);

  return state;
}

function MyComponent() {
  const state = useCustomHook();
  return <div>{state}</div>;
}

🔄 React 라이프사이클이란?

리액트 라이프사이클은 컴포넌트가 생성, 업데이트, 제거되는 과정을 뜻합니다. 함수형 컴포넌트에서는 useEffect 훅으로 관리할 수 있습니다.

🎯 useEffect

useEffect는 기본적으로 컴포넌트가 렌더링된 직후에 실행됩니다.

useEffect(() => {
  // 이 부분은 컴포넌트가 마운트될 때 실행됩니다.
  return () => {
    // 이 부분은 컴포넌트가 언마운트될 때 실행됩니다.
  };
}, [dependencies]); // dependencies 배열의 값이 변경될 때 실행

📌 라이프사이클 단계별 사용 예시

1️⃣ 마운트(Mount)

컴포넌트가 처음 렌더링될 때 실행됩니다.

import React, { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    console.log('컴포넌트가 마운트되었습니다.');

    return () => {
      console.log('컴포넌트가 언마운트될 예정입니다.');
    };
  }, []); // 빈 배열: 한 번만 실행

  return <div>안녕하세요!</div>;
}

2️⃣ 업데이트(Update)

컴포넌트의 상태나 속성이 변경될 때 실행됩니다.

import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log(`Count가 ${count}로 업데이트되었습니다.`);
  }, [count]); // count가 변경될 때마다 실행

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

3️⃣ 언마운트(Unmount)

컴포넌트가 DOM에서 제거될 때 실행됩니다.

import React, { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    console.log('컴포넌트가 마운트되었습니다.');

    return () => {
      console.log('컴포넌트가 언마운트될 예정입니다.');
    };
  }, []); // 빈 배열: 한 번만 실행

  return <div>안녕하세요!</div>;
}

🧩 useEffect Hook 자세히 알아보기

React의 useEffect Hook은 컴포넌트가 마운트(DOM에 추가)되거나 업데이트, 언마운트(DOM에서 제거)될 때 발생하는 부수 효과(Side Effect)를 처리할 수 있도록 도와줍니다.

💡 주요 사용 사례

  1. 데이터 가져오기(Fetching data)
  2. 수동으로 DOM을 변경하는 작업
  3. 구독 설정 및 해제 (예: 웹소켓, 이벤트 리스너)
  4. 타이머 설정 및 해제

📜 useEffect 레퍼런스

useEffect(setup, dependencies?)

  • setup: 부수 효과를 처리하는 로직을 포함한 함수.
  • dependencies: 의존성 배열. 이 배열의 값이 변경될 때 setup 함수가 재실행됩니다. 빈 배열([])을 전달하면 마운트 시에만 실행됩니다.
import { useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [serverUrl, roomId]);
}

🔍 매개변수

1️⃣ setup 함수

  • setup 함수는 실행될 부수 효과 로직을 포함하며, 선택적으로 정리(clean-up) 함수를 반환할 수 있습니다.

2️⃣ dependencies 배열

  • 의존성 배열에 포함된 값이 변경될 때마다 setup 함수가 재실행됩니다.
import React, { useEffect } from 'react';

function ExampleComponent() {
  useEffect(() => {
    console.log('컴포넌트가 마운트되었습니다.');

    return () => {
      console.log('컴포넌트가 언마운트되었습니다.');
    };
  }, []); // 빈 배열: 마운트 시 한 번만 실행

  return <div>안녕하세요, 리액트!</div>;
}

🛠️ 실습

📝 실습 1: 데이터 조회

구현 목표:

JSON 데이터를 가져와 화면에 렌더링하기.

import { useEffect, useState } from "react";

export default function AppEffect() {
  const [list, setList] = useState([]);

  useEffect(() => {
    fetch('data/courses_all.json')
      .then((res) => res.json())
      .then((data) => {
        console.log('✅ 데이터 조회 성공');
        setList(data);
      });
  }, []);

  return (
    <>
      <h2 id="title">데이터 가져오기</h2>
      <ul>
        {list.map((item) => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </>
  );
}

🧩 추가 설명

  • useEffect는 데이터를 가져온 후 상태를 업데이트(setList)합니다.
  • 마운트 시에만 실행되므로 의존성 배열은 빈 배열([])로 설정됩니다.

📝 실습 2: 의존성 배열 사용

구현 목표:

필터링 조건에 따라 데이터를 다르게 조회하기.

import { useEffect, useState } from "react";

export default function AppEffect() {
  const [list, setList] = useState([]);
  const [filter, setFilter] = useState('all');

  useEffect(() => {
    fetch(`data/courses_${filter}.json`)
      .then((res) => res.json())
      .then((data) => {
        console.log('✅ 데이터 조회 성공');
        setList(data);
      });
  }, [filter]);

  return (
    <>
      <h2 id="title">데이터 가져오기</h2>
      <label htmlFor="all">전체</label>
      <input
        id="all"
        type="radio"
        value="all"
        checked={filter === 'all'}
        onChange={(e) => setFilter(e.target.value)}
      />
      <label htmlFor="favorite">좋아요</label>
      <input
        id="favorite"
        type="radio"
        value="favorite"
        checked={filter === 'favorite'}
        onChange={(e) => setFilter(e.target.value)}
      />
      <ul>
        {list.map((item) => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </>
  );
}

🧩 추가 설명

  • 의존성 배열에 filter를 추가하여, 필터 값이 변경될 때마다 새로운 데이터를 가져옵니다.
  • 상태 변화에 따른 동적인 데이터 업데이트를 다룰 때 유용합니다.

📝 실습 3: 마운트 해제

구현 목표:

컴포넌트가 언마운트될 때 정리 작업 수행하기.

import { useEffect, useState } from "react";

function Courses() {
  const [list, setList] = useState([]);
  const [filter, setFilter] = useState('all');

  useEffect(() => {
    fetch(`data/courses_${filter}.json`)
      .then((res) => res.json())
      .then((data) => {
        console.log('✅ 데이터 조회 성공');
        setList(data);
      });

    return () => {
      console.log('❎ 연결 해제~!');
    };
  }, [filter]);

  return (
    <>
      <label htmlFor="all">전체</label>
      <input
        id="all"
        type="radio"
        value="all"
        checked={filter === 'all'}
        onChange={(e) => setFilter(e.target.value)}
      />
      <label htmlFor="favorite">좋아요</label>
      <input
        id="favorite"
        type="radio"
        value="favorite"
        checked={filter === 'favorite'}
        onChange={(e) => setFilter(e.target.value)}
      />
      <ul>
        {list.map((item) => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </>
  );
}

export default function AppEffect() {
  const [show, setShow] = useState(true);

  return (
    <>
      <h2 id="title">데이터 가져오기</h2>
      <button onClick={() => setShow(!show)}>toggle</button>
      <hr />
      {show && <Courses />}
    </>
  );
}

🧩 추가 설명

  • return 문에서 정리 작업을 설정하면 컴포넌트가 DOM에서 제거될 때 해당 작업이 실행됩니다.
  • 이와 같은 정리는 이벤트 리스너 해제, 웹소켓 연결 종료 등에도 활용됩니다.

profile
개발자가 되고 싶은 정치학도생의 기술 블로그

0개의 댓글