❓ HOOK이란?

React Hook(훅)은 함수형 컴포넌트에서 상태(state)나 생명주기(lifecycle) 기능 등을 사용할 수 있게 해주는 기능입니다.
Hook은 함수형 컴포넌트에서만 사용할 수 있으며, use라는 접두사로 시작하는 함수 이름으로 작성합니다.
Hook을 사용하면 컴포넌트 로직을 재사용하기 쉬워지므로 코드의 가독성과 유지보수성을 높일 수 있습니다.

❶ State란?

  • 리액트에서 state는 컴포넌트의 상태를 나타내는 데이터입니다.
  • state는 컴포넌트 내부에서 변경될 수 있는 데이터로, 일반적으로 사용자 인터페이스와 관련된 데이터를 저장합니다.
  • 리액트의 클래스 컴포넌트에서는 state를 클래스 내부에서 정의하고, this.state를 사용하여 값을 읽거나 변경합니다.

❷ State가 필요한 이유

state는 위에서 설명한 것과 같이 리액트 컴포넌트에서 필요한 데이터를 저장하고, 관리하기 위한 방법 중 하나입니다.

React는 *단방향 데이터 바인딩을 사용하여 컴포넌트 간의 데이터 흐름을 관리합니다.

부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하면, 자식 컴포넌트는 해당 데이터를 props로 받아 사용합니다.
이렇게 전달된 데이터는 자식 컴포넌트에서는 읽기 전용으로 사용됩니다.
그러나 컴포넌트 내부에서 동적으로 변경되어야 하는 데이터는 state를 사용하여 관리합니다.

state는 컴포넌트 내부에서 변경 가능한 데이터로, 일반적으로 사용자 인터페이스와 관련된 데이터를 저장합니다.
사용자의 입력, API 응답, 타이머 등에 의해 변경될 수 있으며, 이러한 변경사항이 발생하면 리액트는 해당 컴포넌트를 다시 렌더링하여 변경된 상태를 화면에 반영합니다.

용어 정리
❶ 단방향 데이터 바인딩
단방향 데이터 바인딩은 데이터의 흐름이 한 방향으로만 흐르는 데이터 바인딩 방식을 의미합니다.
React에서는 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때, 단방향 데이터 바인딩을 사용합니다.

부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때는 props를 사용합니다.
props는 읽기 전용으로, 자식 컴포넌트에서는 props를 받아 사용할 뿐, 직접 변경할 수 없습니다.

❷ props
props는 React 컴포넌트에서 사용되는 데이터를 전달하는 방법 중 하나입니다.
props는 부모 컴포넌트에서 자식 컴포넌트로 전달되며, 자식 컴포넌트에서는 props를 사용하여 데이터를 읽을 수 있습니다.

props는 컴포넌트의 속성(Property)을 의미하며, 컴포넌트가 생성될 때 정적으로 할당됩니다.
즉, 컴포넌트가 생성된 이후에는 props를 변경할 수 없습니다.

예를 들어, 버튼을 클릭할 때마다 숫자가 증가하는 컴포넌트를 만들어보겠습니다.

import React, { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

위 코드에서 count state가 버튼 클릭에 따라 변경됩니다. 이때, useState 훅을 사용하여 state를 정의하고, setCount 함수를 사용하여 state를 변경합니다.

따라서, state는 컴포넌트 내부에서 변경 가능한 데이터를 저장하고, 이러한 변경사항이 발생할 때마다 컴포넌트를 다시 렌더링하여 변경된 상태를 화면에 반영하기 위해 필요합니다.


❷ State의 불변성

React에서 state의 불변성(Immutability)은 매우 중요한 개념 중 하나입니다.
state를 변경할 때는 원본 state를 직접 수정하지 않고, 새로운 객체를 생성하여 변경합니다.
이렇게 하면 예측 가능하고 안정적인 동작을 보장할 수 있으며, 성능 향상에도 도움이 됩니다.
(state를 직접 변경하면, React가 내부적으로 state의 변경 여부를 판단하는 방식과 충돌이 발생할 수 있습니다.)

React는 state의 변경 여부를 판단하기 위해, 이전 state 객체와 현재 state 객체를 비교하여 차이가 있는 부분만 업데이트합니다.
그러나 직접 state를 변경하면, React가 변경된 부분을 감지하지 못하고, 렌더링이 제대로 이루어지지 않을 수 있습니다.

따라서, state를 변경할 때는 불변성을 유지해야 합니다.
객체나 배열을 변경할 때는 기존 객체나 배열을 변경하지 않고, 새로운 객체나 배열을 생성하여 변경합니다.

예를 들어, 다음과 같이 count state를 변경하는 코드를 작성할 수 있습니다.

import React, { useState } from 'react';

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

  const incrementCount = () => {
    setCount(prevCount => prevCount + 1);
  }

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={incrementCount}>
        Click me
      </button>
    </div>
  );
}

위 코드에서는 setCount 함수에 콜백 함수를 전달하여, 이전 state 값을 받아와서 새로운 state 값을 생성합니다. 이렇게 하면, 이전 state 객체를 직접 변경하지 않고, 새로운 state 객체를 생성하므로, 불변성을 유지할 수 있습니다.


❸ 원시타입 state 다루기

원시 타입(state)은 문자열, 숫자, 불리언, null, undefined와 같은 단순한 값들을 말합니다.
React에서 원시 타입의 state를 다루는 방법은 객체 타입의 state를 다루는 방법과 크게 다르지 않습니다.

클래스 컴포넌트에서는 state를 클래스 내부에서 정의하고, this.setState 메서드를 사용하여 값을 읽거나 변경합니다.
원시 타입의 state를 다룰 때는, 새로운 값을 할당하는 것으로 변경할 수 있습니다.

예를 들어, 다음 코드에서 count state는 숫자 타입입니다.

import React from 'react';

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

함수 컴포넌트에서는 useState 훅을 사용하여 state를 관리합니다.
원시 타입의 state를 다룰 때는, useState 훅으로 상태값을 정의하고, 이전 상태값을 이용하여 새로운 값을 할당하는 것으로 변경할 수 있습니다.

import React, { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>
        Click me
      </button>
    </div>
  );
}

위 코드에서는 count state가 숫자 타입이며, setCount 함수로 count state를 변경합니다.
함수로 변경할 때에는 이전 state 값을 받아와서 새로운 값을 할당합니다.

따라서, 원시 타입의 state를 다룰 때에도 객체 타입의 state를 다룰 때와 마찬가지로 값을 변경하는 것으로 변경할 수 있습니다.


❹ 참조타입 state 다루기

참조 타입(state)은 객체, 배열, 함수와 같이 복잡한 값들을 말합니다.
React에서 참조 타입의 state를 다루는 방법은 원시 타입의 state를 다루는 방법과 비슷하지만, 객체나 배열의 불변성(Immutability)을 유지해야 한다는 점에서 차이가 있습니다.

클래스 컴포넌트에서는 state를 클래스 내부에서 정의하고, this.setState 메서드를 사용하여 값을 읽거나 변경합니다.
참조 타입의 state를 다룰 때는, 새로운 객체나 배열을 생성하여 변경합니다.

예를 들어, 다음과 같은 코드에서 list state는 배열 타입입니다.

import React from 'react';

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      list: [1, 2, 3]
    };
  }

  addItem = () => {
    this.setState(prevState => ({
      list: [...prevState.list, 4]
    }));
  }

  render() {
    return (
      <div>
        <ul>
          {this.state.list.map(item => <li key={item}>{item}</li>)}
        </ul>
        <button onClick={this.addItem}>Add item</button>
      </div>
    );
  }
}

위 코드에서는 list state를 배열 타입으로 정의하고, addItem 메서드에서는 새로운 배열을 생성하여 list state를 업데이트합니다.
이때, 이전 배열을 직접 변경하지 않고, 전개 연산자(spread operator)를 사용하여 새로운 배열을 생성합니다.

함수 컴포넌트에서는 useState 훅을 사용하여 state를 관리합니다. 참조 타입의 state를 다룰 때는, useState 훅으로 상태값을 정의하고, 이전 상태값을 이용하여 새로운 객체나 배열을 생성하여 변경합니다.

import React, { useState } from 'react';

function MyComponent(props) {
  const [list, setList] = useState([1, 2, 3]);

  const addItem = () => {
    setList(prevList => [...prevList, 4]);
  }

  return (
    <div>
      <ul>
        {list.map(item => <li key={item}>{item}</li>)}
      </ul>
      <button onClick={addItem}>Add item</button>
    </div>
  );
}

위 코드에서는 list state를 배열 타입으로 정의하고, addItem 함수에서는 새로운 배열을 생성하여 list state를 업데이트합니다. 이때, useState 훅에서 제공하는 setList 함수의 콜백 함수에서 이전 state 값을 받아와서, 이전 배열을 직접 변경하지 않고 새로운 배열을 생성합니다.

따라서, 참조 타입의 state를 다룰 때에는 불변성을 유지하면서, 새로운 객체나 배열을 생성하여 값을 변경해야 합니다.


❺ State와 props의 차이

State와 props는 React에서 데이터를 다루는 방법 중 두 가지입니다. 둘 다 컴포넌트 내부에서 사용되는 데이터를 관리하며, 컴포넌트의 렌더링 결과에 영향을 줍니다. 그러나 둘 간에는 몇 가지 차이점이 있습니다.

1. 정의 방식

  • State
    • state는 컴포넌트 내부에서 정의하며, 컴포넌트 내에서 변경이 가능합니다. state는 컴포넌트의 상태를 나타내며, 상태에 따라 컴포넌트의 렌더링 결과가 달라집니다.
  • Props
    • props는 부모 컴포넌트에서 자식 컴포넌트로 전달되는 값으로, 자식 컴포넌트에서는 변경이 불가능합니다.
    • props는 컴포넌트의 속성(Property)을 나타내며, 부모 컴포넌트에서 값을 전달하고, 자식 컴포넌트에서는 값을 읽어 사용합니다.

2. 변경

  • State
    • state는 컴포넌트 내에서 변경이 가능합니다. state가 변경되면, 컴포넌트가 다시 렌더링됩니다.
  • Props
    • props는 자식 컴포넌트에서 변경이 불가능합니다. props가 변경되면, 자식 컴포넌트는 다시 렌더링됩니다.

3. 사용 방법

  • State
    • state는 this.state와 this.setState 메서드를 사용하여 정의하고, 변경합니다. state는 컴포넌트 내에서만 사용할 수 있으며, 자식 컴포넌트에 props로 전달될 수 있습니다.
  • Props
    • props는 부모 컴포넌트에서 자식 컴포넌트로 전달됩니다. 자식 컴포넌트에서는 props를 함수 인자로 받아 사용하거나, this.props로 사용할 수 있습니다.

4. 업데이트 주기

  • State: state는 컴포넌트가 마운트될 때, 언마운트될 때, 그리고 this.setState 메서드를 호출하여 업데이트될 때 업데이트 주기를 가집니다.
  • Props: props는 부모 컴포넌트가 전달하는 값이므로, 부모 컴포넌트가 변경될 때마다 업데이트 주기를 가집니다.

따라서, State와 props는 모두 컴포넌트에서 사용되는 데이터를 관리하는 방법이지만, 정의 방식, 변경 가능성, 사용 방법, 업데이트 주기 등에서 차이가 있습니다.


❻ state 끌어올리기

React에서 여러 컴포넌트가 상호작용할 때, 공통으로 사용하는 데이터를 상위 컴포넌트의 state에 저장하여 관리하는 것을 State 끌어올리기(Lifting State Up)라고 합니다.

State 끌어올리기는 컴포넌트 간에 데이터를 공유하거나, 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 사용됩니다.
상위 컴포넌트에서 state를 관리하면, 여러 하위 컴포넌트에서 공유하거나 사용할 수 있으므로, 코드의 재사용성과 유지보수성을 높일 수 있습니다.

예를 들어, 다음과 같은 코드에서는 상위 컴포넌트인 App 컴포넌트에서 count state를 관리하고, 이 값을 하위 컴포넌트인 Counter 컴포넌트로 전달하여 사용합니다.

import React, { useState } from 'react';

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

  const incrementCount = () => {
    setCount(count + 1);
  }

  const decrementCount = () => {
    setCount(count - 1);
  }

  return (
    <div>
      <Counter count={count} onIncrement={incrementCount} onDecrement={decrementCount} />
    </div>
  );
}

function Counter(props) {
  return (
    <div>
      <p>Count: {props.count}</p>
      <button onClick={props.onIncrement}>+</button>
      <button onClick={props.onDecrement}>-</button>
    </div>
  );
}

위 코드에서는 App 컴포넌트에서 count state를 정의하고, 이 값을 Counter 컴포넌트의 count props로 전달하여 사용합니다.
Counter 컴포넌트에서는 전달받은 props를 사용하여 화면에 표시하고, 버튼 클릭 이벤트에 대한 콜백 함수를 실행합니다.
이렇게 하면, count state를 하위 컴포넌트에서 사용할 수 있으며, 상위 컴포넌트에서 count state를 관리할 수 있습니다.

State 끌어올리기를 통해 컴포넌트 간의 데이터 공유를 구현하면, 코드의 재사용성과 유지보수성을 높일 수 있습니다. 그러나, 상위 컴포넌트에서 모든 state를 관리하면, 컴포넌트 구조가 복잡해지고, state의 변경이 많아지면 성능 이슈가 발생할 수 있으므로, 적절한 범위에서 state를 관리해야 합니다.

profile
#UXUI #코린이

0개의 댓글