React 기본 (상태관리, 생명주기)

김현준·2024년 3월 23일

리액트

목록 보기
2/11
post-thumbnail

리액트에서 함수형 컴포넌트는 이름 그대로 그냥 함수다.
즉, 상태(state)가 바뀌면 전체 함수가 다시 실행되며, 그에 따라 UI도 새롭게 그려진다.
하지만 이때 단순히 화면을 다시 그리는 것만으로는 부족하다.
상태를 기억하고, 외부 작업(API 호출, 타이머 등)을 제대로 제어하려면 useStateuseEffect가 필요하다.


상태란 무엇이고 왜 필요한가?

유저는 리액트 앱에서 서버와 인터랙션을 한다.
예를 들어 회원가입, 로그인, 블로그 포스팅 같은 작업은 서버와 통신하며 DB에 정보가 저장된다.

하지만 댓글을 작성하기 직전, 즉 유저가 입력을 끝내기 전까지는 그 내용이 어디에 저장될까?

일반적으로 클라이언트 사이드의 메모리 위에 존재한다.

실시간으로 모든 입력값을 DB에 저장한다면, 유저의 조작 하나하나에 네트워크 요청이 발생해 성능 저하와 불편함이 생긴다.
따라서 클라이언트 쪽에서 입력값을 잠시 기억해야 하며, 이 역할을 state가 담당한다.


함수형 컴포넌트 = 렌더링 = 함수 실행

function MyComponent() {
  console.log("렌더링됨"); // 상태가 바뀔 때마다 다시 실행
  return <div>Hello</div>;
}
  • 상태가 바뀌면 전체 함수가 다시 실행됨
  • 함수 실행 후 DOM 반영 → 그 이후에 useEffect 실행

useState: 상태를 기억하고 UI를 갱신함

const [count, setCount] = useState(0);
setCount(count + 1); // 상태 변경 → 리렌더링
항목설명
역할상태 저장 및 변경
실행 시점컴포넌트 함수가 호출될 때마다 상태값을 React가 연결
상태 변경 시컴포넌트가 리렌더링됨
예시 용도입력값, 토글, 카운터, 체크 여부 등
  • 내부적으로 React가 상태값을 메모리에 따로 보관하고 있다가 다시 함수가 실행될 때 맞게 연결해줌.

useEffect: 생명주기 감시자

useEffect(() => {
  console.log("마운트됨");

  return () => {
    console.log("언마운트 또는 다음 effect 직전 정리");
  };
}, []);
항목설명
역할부수효과(Effect) 실행: DOM 조작, API 호출 등
실행 시점렌더링이 끝난 후 (DOM 반영 후)
의존성 배열(deps)변화 감지 대상 지정 → 값이 바뀔 때마다 다시 실행
cleanup 함수이전 effect 정리용 (언마운트 시에도 실행됨)
  • 화면이 바뀐 걸 기준으로 무언가 하고 싶을 때
    (예: API 호출, 스크롤 조정, 애니메이션 시작, 이벤트 등록 등)
  • DOM이 변하기 전에 실행되면 원하는 시점에 못 맞출 수 있기 때문에
    안전하게 DOM 업데이트가 끝난 뒤 실행됨.

예시: 요소의 크기를 측정할 때

useEffect(() => {
  const box = document.getElementById("box");
  console.log(box?.offsetHeight); // 박스의 실제 높이
}, []);
  • DOM이 그려지기 전에 실행된다면 offsetHeight가 0이 나오거나 잘못된 값이 나올 수 있다.

상태 변경과 useEffect 실행 흐름

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

  console.log("렌더링");

  useEffect(() => {
    console.log("🟢 effect 실행됨");

    return () => {
      console.log("🔴 cleanup 실행됨");
    };
  }, [count]);

  return <button onClick={() => setCount(c => c + 1)}>+</button>;
}

실행 순서 예시:

  1. count 초기값 0 → "렌더링" 출력
  2. DOM 반영됨 → "🟢 effect 실행됨" 출력
  3. 버튼 클릭 → setCount(1) → 상태 변경
  4. 컴포넌트 함수 재실행 → "렌더링" 출력
  5. 기존 effect 정리 → "🔴 cleanup 실행됨" 출력
  6. 새로운 effect 실행 → "🟢 effect 실행됨" 출력

useState vs useEffect 비교 정리

항목useStateuseEffect
실행 시점함수 실행 시렌더링이 끝난 후 (DOM 적용 후)
역할상태 저장 및 리렌더링 유발외부 작업 처리 (API, 타이머 등)
리렌더링 유발O (setState() 호출 시)X (내부에서 상태 변경하지 않으면 리렌더링 없음)
cleanup없음return으로 이전 effect 정리
주요 용도사용자 입력값, 토글, 내부 상태 관리비동기 작업, 타이머, 이벤트 등록, 웹소켓 등 관리

생명주기 흐름 요약

생명주기 단계설명관련 Hook
Mounting컴포넌트가 처음 나타날 때useEffect(() => { ... }, [])
Updatingprops 또는 state가 변경될 때useEffect(() => { ... }, [deps])
Unmounting컴포넌트가 사라질 때useEffect(() => { return () => {...} }, [])

클래스 컴포넌트와의 비교

생명주기 단계클래스형 메서드함수형 대응 방식
마운트componentDidMount()useEffect(() => {...}, [])
업데이트componentDidUpdate()useEffect(() => {...}, [deps])
언마운트componentWillUnmount()useEffect(() => { return () => {...} }, [])

함수형에서는 생명주기를 useEffect로 모두 커버할 수 있다.


실전에서 자주 쓰는 예시

1. API 호출

useEffect(() => {
  fetch('/api/data')
    .then(res => res.json())
    .then(data => setData(data));
}, []);

2. 타이머

useEffect(() => {
  const timer = setInterval(() => console.log('⏰'), 1000);
  return () => clearInterval(timer);
}, []);

3. 이벤트 등록/해제

useEffect(() => {
  const handleScroll = () => console.log("scroll!");
  window.addEventListener("scroll", handleScroll);

  return () => window.removeEventListener("scroll", handleScroll);
}, []);

정리

포인트내용
상태란?사용자 인터랙션 중 일시적으로 메모리에 저장되는 클라이언트 측 데이터
useState상태 저장 + 상태 변경 시 리렌더링 발생
useEffect외부 작업(API, 타이머 등)을 렌더링 이후 타이밍에 실행
전체 흐름상태 바뀜 → 함수 실행 → 화면 반영 → effect 실행
클래스형과의 차이useEffect 하나로 모든 생명주기 단계 제어 가능
profile
기록하자

0개의 댓글