개발/리액트

리액트 state

whatd0udo 2024. 3. 13. 21:18

생활코딩 state 유튜브 강좌

리액트 state

React에서 상태(state)는 컴포넌트의 데이터를 추적하는 데 사용되며, 이 데이터는 시간이 지남에 따라 변경될 수 있습니다. 상태는 사용자 인터랙션 또는 시스템 이벤트로 인해 변경되며, 상태가 변경되면 React는 컴포넌트를 자동으로 다시 렌더링하여 UI를 최신 상태로 유지합니다.

클래스 컴포넌트에서의 State

클래스 컴포넌트에서 상태를 사용하기 위해, 일반적으로 constructor에서 this.state를 초기화하고, setState 메서드를 사용하여 상태를 업데이트합니다.

class Counter extends React.Component {
  constructor(props) {
    super(props);
    // 상태 초기화
    this.state = {
      count: 0
    };
  }

  handleClick = () => {
    // 상태 업데이트
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>{this.state.count}</p>
        <button onClick={this.handleClick}>클릭</button>
      </div>
    );
  }
}

함수 컴포넌트에서의 State (Hooks 사용)

React 16.8부터 Hooks를 도입하여 함수 컴포넌트에서도 상태 관리가 가능해졌습니다. useState 훅을 사용하여 함수 컴포넌트 내에서 상태를 선언하고 업데이트할 수 있습니다.

import React, { useState } from 'react';

function Counter() {
  // 상태 초기화 및 상태 업데이트 함수 선언
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>클릭</button>
    </div>
  );
}

상태 업데이트 주의사항

  • 비동기적인 setState: setState 메서드는 비동기적으로 동작할 수 있으므로, 이전 상태에 기반해 상태를 업데이트할 때는 함수를 인자로 전달하는 것이 안전합니다.
  • 상태 불변성 유지: 상태를 업데이트할 때는 상태를 직접 수정하지 말고, 새로운 상태 객체를 생성하여 업데이트해야 합니다. 이는 React가 상태 변화를 효율적으로 감지하고, 컴포넌트를 적절히 리렌더링할 수 있도록 합니다.
  • 상태 분할: 복잡한 상태 로직은 여러 상태 변수로 분할하여 관리하는 것이 좋습니다.

React의 상태 관리는 컴포넌트의 동적인 데이터를 효과적으로 관리하고, 반응형 UI를 구현하는 데 중요한 역할을 합니다.

Props (속성)

  • 외부로부터 전달: props는 부모 컴포넌트로부터 자식 컴포넌트에 전달되는 데이터입니다. 이를 통해 컴포넌트를 재사용하고 다양한 상황에서의 UI를 표현할 수 있습니다.
  • 읽기 전용: props는 컴포넌트 내에서 변경할 수 없습니다. 부모 컴포넌트에서 전달된 props를 자식 컴포넌트가 직접 수정하는 것은 React의 데이터 흐름에 어긋납니다. 만약 props에 기반한 상태를 변경하고 싶다면, 이를 state에 복사한 후 state를 변경해야 합니다.
  • 컴포넌트의 재사용성 증가: 다른 props를 전달함으로써 동일한 컴포넌트를 다양한 방식으로 사용할 수 있습니다.

State (상태)

  • 컴포넌트 내부에서 관리: state는 컴포넌트 내부에서 선언되고 관리됩니다. 컴포넌트의 상태는 사용자의 인터랙션 또는 시간의 경과와 같은 요인에 따라 변경될 수 있습니다.
  • 변경 가능: state는 setState 함수(클래스 컴포넌트) 또는 useState 훅(함수 컴포넌트)을 사용하여 업데이트할 수 있습니다. state가 변경되면 컴포넌트는 자동으로 리렌더링됩니다.
  • 컴포넌트의 동적인 데이터 관리: state는 컴포넌트가 스스로 관리해야 하는 데이터를 위한 것입니다. 예를 들어, 사용자로부터 입력을 받아야 하는 폼, 리스트의 아이템을 추가하거나 삭제하는 기능 등이 이에 해당합니다.

Props와 State의 차이점

  • 데이터의 소유권: props는 부모 컴포넌트가 자식 컴포넌트에게 전달하는 데이터입니다. 반면, state는 컴포넌트 자체가 소유하고 관리하는 데이터입니다.
  • 변경 가능성: props는 읽기 전용으로, 컴포넌트 내부에서는 변경할 수 없습니다. state는 컴포넌트 내에서 업데이트 가능합니다.

이러한 차이점을 이해하는 것은 React에서 효과적으로 데이터를 관리하고, 예측 가능한 방식으로 UI를 업데이트하는 데 중요합니다.

useState 훅을 사용하여 상태를 관리하며, 여러 사용자 정의 컴포넌트 (Header, Nav, Article)를 통해 간단한 웹 페이지를 구성 예시

// React의 useState 훅을 import합니다. useState는 함수 컴포넌트에서 상태 관리를 가능하게 합니다.
import {useState} from 'react';
import './App.css'; // App 컴포넌트에 적용할 CSS 스타일을 import합니다.

// Header 컴포넌트: 사이트의 제목과 홈 링크를 표시합니다.
function Header(props){
  // 클릭 이벤트가 발생했을 때 호출될 함수를 props로 받습니다. 이 함수는 나중에 App 컴포넌트에서 정의됩니다.
  return <header>
    <h1>
      {/* a 태그의 클릭 이벤트를 캡처하고, 기본 동작(페이지 이동)을 방지한 뒤, props로 받은 onChangeMode 함수를 호출합니다. */}
      <a href="/" onClick={(event)=>{
        event.preventDefault();
        props.onChangeMode();
      }}>{props.title}</a> {/* props.title을 통해 전달받은 제목을 표시합니다. */}
    </h1>
  </header>
}

// Nav 컴포넌트: 사이트의 내비게이션(메뉴)를 구성합니다.
function Nav(props){
  const lis = [] // 내비게이션 메뉴 항목을 저장할 배열입니다.
  // props로 전달받은 topics 배열을 순회하며, 각 항목에 대한 링크를 생성합니다.
  for (let i= 0; i<props.topics.length; i++){
    let t = props.topics[i]; // 현재 주제(topic)을 t에 저장합니다.
    // 각 주제에 대한 링크를 생성하고, 클릭 시 props.onChangeMode 함수를 호출합니다.
    // 이 때, 링크의 id 속성을 이용해 각 항목의 고유 id를 전달합니다.
    lis.push(<li key={t.id}>
      <a id={t.id} href={'/read/'+t.id} onClick={(event)=>{
        event.preventDefault();
        props.onChangeMode(t.id);
      }}>{t.title}</a>
    </li>)
  }
  return <nav>
    <ol>
      {lis} // 내비게이션 메뉴의 리스트 아이템들을 렌더링합니다.
    </ol>
  </nav>
}

// Article 컴포넌트: 사이트의 주요 콘텐츠(기사)를 표시합니다.
function Article(props){
  // 주어진 제목(props.title)과 본문(props.body)을 표시합니다.
  return <article>
    <h2>{props.title}</h2>
    {props.body}
  </article>
}

// App 컴포넌트: 애플리케이션의 최상위 컴포넌트입니다.
function App() {
  // mode 상태를 'WELCOME'으로 초기화합니다. 이 상태는 사이트의 현재 모드(예: 환영 메시지, 주제 읽기 등)를 추적합니다.
  const [mode, setMode] = useState('WELCOME');
  // id 상태를 null로 초기화합니다. 이 상태는 사용자가 선택한 주제의 id를 저장합니다.
  const [id, setId] = useState(null);
  
  // 사이트에 표시할 주제들을 정의합니다.
  const topics = [
    {id:1, title:'html', body:'html is...'},
    {id:2, title:'css', body:'css is...'},
    {id:3, title:'javascript', body:'javascript is...'}
  ]

  // 현재 mode 상태에 따라 표시할 컨텐츠를 결정합니다.
  let content = null;
  if(mode === 'welcome'){
    // mode가 'welcome'일 때, 환영 메시지를 표시합니다.
    content = <Article title="Welcome" body="Hello, React"></Article>
  } else if (mode === 'read'){
    // mode가 'read'일 때, 선택된 주제의 콘텐츠를 표시합니다.
    let title, body = null;
    for(let i=0; i<topics.length; i++){
      // 사용자가 선택한 주제를 찾아 해당 제목과 본문을 content에 할당합니다.
      if(topics[i].id === id){
        title = topics[i].title;
        body = topics[i].body;
      }
    }
    content = <Article title={title} body={body}></Article>
  }
   
  return (
    <div>
      <Header title="WEB" onChangeMode={()=>{
        setMode('welcome'); // Header 컴포넌트에서 클릭 이벤트 발생 시 mode를 'welcome'으로 설정합니다.
      }}></Header>
      <Nav topics={topics} onChangeMode={(_id)=>{
        setMode('read'); // Nav 컴포넌트에서 항목 클릭 시 mode를 'read'로, 선택된 id를 업데이트합니다.
        setId(_id);
      }}></Nav>
      {content} // 현재 mode에 따라 결정된 컨텐츠를 렌더링합니다.
    </div>
  );
}

export default App;

이 코드에서는 다음과 같은 React의 핵심 개념들을 사용하고 있습니다:

- Props `Header`, `Nav`, `Article` 컴포넌트에 데이터와 이벤트 핸들러 함수를 전달하는 데 사용됩니다. Props를 통해 컴포넌트 간에 데이터를 "내려보내고"(`props`로 데이터 전달), 컴포넌트 이벤트에 대한 응답을 "올려보내는"(`props`의 함수 호출) 방식으로 컴포넌트 트리에서 데이터 흐름을 관리합니다.
- State `useState` 훅을 사용하여 `App` 컴포넌트의 내부 상태(`mode`와 `id`)를 관리합니다. State의 변화는 컴포넌트의 리렌더링을 유발하여, 사용자 인터랙션에 따라 동적으로 UI가 갱신됩니다.
- 이벤트 핸들링 `onClick` 이벤트 핸들러를 통해 사용자의 클릭 액션을 감지하고, 이에 따라 애플리케이션의 상태를 업데이트합니다. 이벤트 객체의 `preventDefault` 메서드를 호출하여 브라우저의 기본 동작(예: 링크 클릭 시 페이지 이동)을 방지합니다.

위 코드는 React 애플리케이션의 구조와 데이터 흐름을 이해하는 데 도움이 되는 좋은 예제입니다. 사용자 정의 컴포넌트를 정의하고, props와 state를 사용하여 동적인 데이터를 관리하며, 이벤트를 처리하는 방법을 보여줍니다.