본문 바로가기
STUDY/react.js

[React] Lifecycle과 useEffect

by 히컵 2024. 8. 12.

 

 

Lifecycle(생명주기)란?

리액트의 컴포넌트는 

1. 생성(mount) → 2.업데이트 (update)  →  3.제거 (unmount)

의 생명주기를 갖는다. 

이미지 출처 코딩애플

 

컴포넌트에 Lifecycle 개념을 이용하여 컴포넌트 중간중간에 간섭을 할 수 있다. 간섭은 그냥 코드 실행이다.

'Detail 컴포넌트 등장 전에 이것 좀 해 줘'

'Detail 컴포넌트 사라지기 전에 이것 좀 해 줘'

'Detail 컴포넌트 업데이트 후에 이것 좀 해 줘'

이렇게 코드를 실행해 달라고 갈고리를 달아 간섭할 수 있고, 갈고리를 달아 코드를 넣어 주면 된다.

이미지 출처 코딩애플

그러면 페이지 장착시, 업데이트시, 제거시 코드 실행이 가능하다.

이 갈고리를 hook이라고 하고, 이러한 것들을 Lifecycle hook이라고 부른다.

 

 

Class Component Lifecycle hook (클래스형 컴포넌트 생명주기)

class Detail2 extends React.Component {
  componentDidMount(){
    //Detail2 컴포넌트가 로드되고 나서 실행할 코드
  }
  componentDidUpdate(){
    //Detail2 컴포넌트가 업데이트되고 나서 실행할 코드
  }
  componentWillUnmount(){
    //Detail2 컴포넌트가 삭제되기전에 실행할 코드 
  }
}

 

클래스 문법으로 컴포넌트를 만들 경우 그 안에 함수명(componentDidMount 등)을 써주면 각각 특정 Lifecycle에서 코드를 실행할 수 있다.

 

 

Functional Component Lifecycle hook (함수형 컴포넌트 생명주기)

import { useEffect } from "react";

function Detail(){
  useEffect( () => {
    //여기 작성한 코드는 컴포넌트 로드 & 업데이트 할 때마다 실행됨
    console.log('안녕')
  });
  
  return (생략)
}

export default Detail;

 

1. 상단에 { useEffect } 를 import 

2.  콜백함수 추가해서 코드 작성하면 컴포넌트가 mount & update시 코드가 실행된다.

이게 Lifecycle hook이다.

 

실행화면 콘솔창

페이지 로드시 콘솔창에 '안녕' 출력된다.

 

(참고) 안녕이 2번이나 출력되는 이유는?

리액트 상에서 디버깅용으로 useEffect 안에 있는 코드가 2번 실행된다.

실제 사이트상에서 발행할 때는 1번 동작하고, 콘솔창에서만 임의로 2번 실행되는 것이다. (신경 쓸 필요 없음)

1번만 출력되게 하려면 index.js에서 <React.StrictMode> 태그를 제거해 주면 된다.

 

import { useEffect, useState } from "react";

function Detail(props) {
  let [count, setCount] = useState(0);

  useEffect(() => {
    console.log("안녕");
  });

  return (
    <>
      { count }
      <buttononClick={() => { setCount(count + 1) }}>버튼</button>
    </>
  );
}

export default Detail;

 

버튼을 누르면 재렌더링되도록 코드를 작성했다.

재렌더링시에도 '안녕' 출력되는지 테스트해 보자.

실행화면

버튼을 누를 때마다(재렌더링될 때마다) useEffect 안에 있는 코드가 실행 되어 '안녕' 출력 횟수가 증가한 것을 확인할 수 있다.

 

 

💡 useEffect() 안에 있는 코드는 html이 모두 렌더링된 후에 동작한다.

useEffect(() => {
    
});
console.log("안녕");

사실 위와 같이 useEffect() 바깥에 적어도 컴포넌트 mount & update시 똑같이 실행된다.

컴포넌트가 mount & update시 funciton 안에 있는 코드도 다시 읽고 지나가기 때문이다. 

그러면 useEffect()가 왜 필요할까?

 

예를 들어 반복문을 돌려 1000번 실행해야 하는 코드가 있다고 가정해 보자.

function Detail(props) {
  for (let i=0 ; i <= 1000 ; i++) {
      console.log(i);
    }
  return (생략)
}

export default Detail;

▲ 반복문을 돌리고 나서 하단의 html 보여 준다.

 

function Detail() {

  useEffect(() => {
    for (let i=0 ; i <= 1000 ; i++) {
      console.log(i);
    }
  });

  return (생략)
}

export default Detail;

▲ html 보여주고 나서 useEffect 안에 있는 반복문을 돌린다.

 

이런식으로 코드의 실행 시점을 조절할 수 있기 때문에 html 렌더링이 더 빠른 사이트를 만들 수 있다.

컴포넌트의 핵심 기능은 html 렌더링이니 외에 나머지 기능들은 useEffect 안에 적는 것이 좋다.

(오래 걸리는 반복 연산, 서버에서 데이터를 가져오는 작업, 타이머 등등)

함수의 핵심 기능 외의 기능들을 프로그래밍 용어로 side effect라고 부른다.

 

 


 

페이지 방문 후 2초 후 박스가 사라지게 해 보자.

 

import { useEffect, useState } from "react";

function Detail() {
  let [alert, setAlert] = useState(true);

  useEffect(() => {
    setTimeout(() => {setAlert(false);}, 2000);
  });
  
  return (
    <>
      {alert === true 
      ? <div className="alert alert-warning">2초 이후 사라지기</div>
      : null}
    </>
  );
}

export default Detail;

 

더보기

자바스크립트로 n초 후에 코드를 실행하고 싶으면 setTimeout이라는 함수를 사용한다.

setTimeout( ()=>{  1초 후 실행할 코드 }, 1000);

 

1000이라고 숫자 적은 곳에 ms단위로 시간을 적어 주면 된다.

1000ms = 1초이므로 1초 후에 내부 코드를 실행해준다.

alert, setAlert 라는 state를 만들고 alert가 true면 div가 보이게 한 후, useEffect()를 사용해 2초 후에 state가 false로 바뀌게 하면서 div 창으로 사라지게 만들었다.

 

useEffect()의 두번째 인자 [ ]의 의미 : useEffect()에 넣을 수 있는 실행 조건

useEffect( () => {실행할 코드}, [변수] )
  • useEffect의 형태는 원래 useEffect(callback, dependencies)이다.
  • dependencise는  콜백함수의 코드 내부에서 참조되는 모든 반응형 값들이 포함된 배열로 구성된다. 반응형 값에는 props와 state, 모든 변수 및 컴포넌트 body에 직접적으로 선언된 함수들이 포함된다.

useEffect()의 둘째 파라미터에 [ ]을 넣을 수 있는데, 안에 변수나 state 같은 것들을 넣을 수 있다.

[ ] 안에 있는 변수나 state가 변할 때만 useEffect 안의 코드를 실행해 준다.

(참고) [ ]안에 state 여러개 넣을 수 있음. ex) [count, alert, ...]

 

useEffect(() => {
  setTimeout(() => {setAlert(false);}, 2000);
});

▲ mount, update시 setTimeout()이 실행된다.

 

useEffect(() => {
  setTimeout(() => {setAlert(false);}, 2000);
}, [count]);

▲ count라는 변수가 변할 때만 setTimeout()이 실행된다.

참고로 [count]가 있더라도 처음 mount될 때(컴포넌트 로드될 때) setTimeout() 실행한다.

 

useEffect( () => {실행할 코드}, [] )

[ ]안에 아무것도 안 넣으면 컴포넌트 mount시(로드시) 코드를 1회 실행하고 영영 실행해 주지 않는다.

 

 

clean up function

useEffect 동작하기 전에 특정 코드를 실행하고 싶으면 return () => {} 안에 넣을 수 있다.

useEffect(() => {
  그 다음 실행됨
  return () => {
    먼저 실행됨
  }
}, [count]);

useEffect 안에 있는 코드를 실행하기 전에 return () => {} 안에 있는 코드를 실행해 준다.

이것을 clean up function이라고 부른다.

 

📝왜 clean up function이 필요할까?

예를 들어 위에서 사용했던 useEffect 안에 dependcies를 작성하지 않았다고 가정해 보자. 그러면 렌더링될 때마다setTimeout 함수가 실행될 텐데 나중에는 타이머가 여러개 생겨서 버그를 일으킬 수 있다. 이럴 때 clean up function을 사용해서 타이머 함수를 사용하기 전에 기존 타이머들을 다 제거해 줄 수 있다.

useEffect(() => {
  let Timer = setTimeout(() => {setAlert(false);}, 2000);
  return () => {
    clearTimeout(Timer)
  }
}, []);

 

(참고1) clean up function에는 타이머 제거, socket 연결 요청 제거, ajax 요청 중단 등의 코드를 많이 작성한다.

(참고2) clean up function은 컴포넌트 mount시 실행되지 않고, unmount 시에 clean up fuunction 안에 있는 코드가 1회 실행된다.

 

 

 

 

📌 useEffect() 기본 문법 정리

1. 재렌더링마다 코드 실행하고 싶으면

useEffect(()=> { 실행할 코드 })

 

2. 컴포넌트 mount시(로드시)  1회만 코드 실행하고 싶으면

useEffect(()=> { 실행할 코드 }, [])

 

3. 컴포넌트 unmount시 1회만 코드 실행하고 싶으면

useEffect(()=> { 
  return () => {
    실행할 코드
  }
 }, [])

 

 

4.  useEffect안의 코드실행 전에 뭔가 실행하고 싶으면 언제나 return() => {}

useEffect(()=> { 
  return () => {
    실행할 코드
})
useEffect(()=> { 
  return () => {
    실행할 코드
  }
 }, [])

 

5. 특정 state 변경시에만 실행하려면 [state명]

useEffect(()=> { 
  실행할 코드
 }, [state])

 

 

 

 

 

 

* 이 포스팅은 코딩애플 강의를 토대로 작성하였습니다.