GDGoC/FrontEnd

[Frontend] #04. React - State와 Hooks

Opal1031 2026. 5. 6. 16:21

State와 Hooks

React Hooks를 사용하여 함수형 컴포넌트에서 상태 관리와 부수 효과를 처리할 수 있다.


1️⃣ State (useState)

  • State는 컴포넌트 내부에서 관리되는 동적인 데이터다.
  • State가 변경되면 React는 자동으로 컴포넌트를 리렌더링한다.
  • 일반 변수는 변경되어도 UI가 업데이트되지 않지만, State는 변경 시 자동으로 화면을 갱신한다.
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); // 초기값 0

  return (
    <div>
      <p>현재 카운트: {count}</p>
      <button onClick={() => setCount(count + 1)}>증가</button>
      <button onClick={() => setCount(count - 1)}>감소</button>
    </div>
  );
}

 

State의 특징

  • 내부 데이터: 컴포넌트 내부에서만 관리
  • 변경 가능: setState 함수를 통해 값 변경
  • 추적: State 값이 변하면 UI가 자동으로 업데이트
  • 비동기성: setState는 즉시 반영되지 않고, 나중에 일괄 처리됨

 

State의 비동기성

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

const handleClick = () => {
  setCount(count + 1);
  console.log(count); // 아직 이전 값 출력
};

 

함수형 업데이트

  • 이전 상태를 기반으로 새로운 상태를 계산할 때 사용
  • 연속적인 상태 업데이트가 필요할 때 유용
const handleClick = () => {
  setCount(prev => prev + 1);
  setCount(prev => prev + 1);
  setCount(prev => prev + 1);
  // count는 3 증가
};

 


2️⃣ useEffect

  • useEffect는 렌더링 후 부수 효과(side effect)를 처리하는 Hook이다.
  • 부수 효과란 렌더링 과정과는 별개의 작업을 의미한다.
    • 콘솔 로그 출력
    • 서버에서 데이터 불러오기
    • 타이머 사용
    • DOM 직접 조작
    • 외부 라이브러리 호출
import { useState, useEffect } from 'react';

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

  // count가 변경될 때마다 실행
  useEffect(() => {
    console.log(`count가 ${count}로 변경되었습니다.`);
  }, [count]); // 의존성 배열

  return (
    <div>
      <p>현재 값: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

 

의존성 배열

의존성 배열 실행 시점
[] (빈 배열) 컴포넌트 마운트 시 1회만 실행
[state] 해당 state가 변경될 때마다 실행
생략 매 렌더링마다 실행 (성능 저하 가능)

 

마운트 시 1회만 실행

useEffect(() => {
  console.log("컴포넌트가 마운트되었습니다.");
  // 초기 데이터 로딩 등에 사용
}, []);

 

useEffect 무한 루프 주의

// ❌ 잘못된 예: 무한 루프 발생
function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(prev => prev + 1); // count 변경
  }, [count]); // count가 변경될 때마다 실행 → 무한 반복

  return <p>{count}</p>;
}

// ✅ 올바른 예: 빈 배열로 1회만 실행
useEffect(() => {
  setCount(prev => prev + 1);
}, []);

 

cleanup 함수

  • useEffect에서 반환하는 함수는 컴포넌트가 언마운트되거나 다음 effect가 실행되기 전에 호출된다.
  • 타이머 해제, 구독 취소 등에 사용
useEffect(() => {
  const timer = setInterval(() => {
    console.log('1초마다 실행');
  }, 1000);

  // cleanup 함수
  return () => {
    clearInterval(timer);
    console.log('타이머 해제');
  };
}, []);

 


3️⃣ 이벤트 처리

  • React에서 이벤트는 camelCase로 작성한다 (onClick, onChange).
  • 이벤트 핸들러는 함수 참조를 전달해야 한다.
  • 인자가 있는 함수를 전달할 때는 화살표 함수로 감싸야 한다.
function Button() {
  const handleClick = () => {
    alert('버튼 클릭!');
  };

  return <button onClick={handleClick}>클릭</button>;
}

 

인자가 있는 이벤트 핸들러

function Greeting() {
  const sayHello = (name) => {
    alert(`안녕, ${name}!`);
  };

  // ❌ 잘못된 예: 렌더링 시 즉시 실행
  // return <button onClick={sayHello('민서')}>인사</button>;

  // ✅ 올바른 예: 클릭 시 실행
  return <button onClick={() => sayHello('민서')}>인사</button>;
}

 

이벤트 객체 (e)

  • 이벤트 핸들러는 자동으로 이벤트 객체 e를 받는다.
  • e.target으로 이벤트가 발생한 요소에 접근할 수 있다.
function InputExample() {
  const [text, setText] = useState('');

  const handleChange = (e) => {
    setText(e.target.value); // 입력값 가져오기
  };

  return (
    <div>
      <input type="text" onChange={handleChange} placeholder="입력하세요" />
      <p>입력값: {text}</p>
    </div>
  );
}

 

이벤트 전파 (Bubbling)

  • 이벤트는 자식에서 부모로 전파된다.
  • e.stopPropagation()으로 전파를 막을 수 있다.
function App() {
  const handleCloseMenu = () => console.log("메뉴 닫힘");
  const handleLogin = (e) => {
    e.stopPropagation(); // 부모로 이벤트 전달 차단
    console.log("로그인 클릭");
  };

  return (
    <div onClick={handleCloseMenu}>
      <button onClick={handleLogin}>로그인</button>
    </div>
  );
}

 


4️⃣ 조건부 렌더링

  • 조건에 따라 다른 UI를 렌더링할 수 있다.
  • 주로 삼항 연산자를 사용한다.
function LoginButton({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <button>로그아웃</button>
      ) : (
        <button>로그인</button>
      )}
    </div>
  );
}

 

조건부 렌더링 예제

function App() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  return (
    <div>
      {isLoggedIn ? <p>환영합니다!</p> : <p>로그인해주세요</p>}
      <button onClick={() => setIsLoggedIn(!isLoggedIn)}>
        {isLoggedIn ? "로그아웃" : "로그인"}
      </button>
    </div>
  );
}

 

&& 연산자를 이용한 조건부 렌더링

function Notification({ hasUnread }) {
  return (
    <div>
      {hasUnread && <p>읽지 않은 메시지가 있습니다.</p>}
    </div>
  );
}

'GDGoC > FrontEnd' 카테고리의 다른 글

[Frontend] #06. React - 비동기 통신  (0) 2026.05.06
[Frontend] #05. React - React Router와 전역 상태 관리  (0) 2026.05.06
[Frontend] #03. React 기초  (0) 2026.05.06
[Frontend] #02. JavaScript  (0) 2026.05.06
[Frontend] #01. HTML / CSS  (0) 2026.05.06