> For the complete documentation index, see [llms.txt](https://yamoo9.gitbook.io/learning-react-app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yamoo9.gitbook.io/learning-react-app/learning-history/props-vs-state.md).

# Props vs State

## 전달 속성과 상태의 차이점 <a href="#difference-between-props-and-state" id="difference-between-props-and-state"></a>

컴포넌트는 외부로부터 속성(props)을 전달 받아 사용할 수 있지만, 이 속성은 읽기 전용(Readonly)으로 값을 업데이트 할 수 없습니다. `props`는 부모에서 자식으로 단방향(One-way) 데이터 흐름을 가집니다.

![상위(부모) 컴포넌트로부터 자식 컴포넌트로 전달 되는 One-way Flow](/files/-MV-kdfuHCdZDXjbrNFN)

하지만 UI는 사용자의 요구에 의해 수시로 변합니다. 그러므로 앱 화면을 업데이트 하려면? 외부로부터 전달 받은 속성은 읽기 전용으로 변경할 수 없기 때문에, 변경 가능한 데이터를 컴포넌트가 가질 필요가 있습니다. 업데이트를 목적으로 컴포넌트 스스로 가지는 데이터를 "상태(`state`)"라 부릅니다.

`state`는 컴포넌트가 소유한 로컬 데이터로 적용 범위(scope)는 현재 컴포넌트에 한정됩니다. 필요한 경우, 컴포넌트는 자신의 `state`를 초기화 하거나, 업데이트 할 수 있습니다.  `state`가 업데이트 되면 React는 UI를 다시 렌더링합니다.

![](/files/-MV-lNMYJw-kXuY1R6Mp)

| 콘셉트     | 설명                       | 특징      | 비고              |
| ------- | ------------------------ | ------- | --------------- |
| `props` | 상위 컴포넌트로부터 전달 된 속성       | 읽기 전용   | 속성 객체           |
| `state` | 컴포넌트 자신이 가지는 상태 (로컬 데이터) | 읽기 / 쓰기 | 다양한 유형의 값 설정 가능 |

## 상태가 없는 컴포넌트  <a href="#stateless-component" id="stateless-component"></a>

일반적으로 함수 컴포넌트는 상태를 가지지 않습니다. 함수는 클래스가 아니므로 소유(`this`) 멤버를 가지지 못합니다.

```jsx
function CountDisplay(props) {
  // 전달 받은 props의 count 값을 출력하는 일만 처리
  return <output>{props.count}</output>
}
```

{% hint style="info" %}
React v16.8 부터는 함수에서 상태를 관리할 수 있는 기능이 추가됩니다. 추후 관련 주제를 다뤄보겠습니다.
{% endhint %}

클래스 컴포넌트라고 반드시 상태를 가지는 것은 아닙니다. 아래 작성된 컴포넌트는 상태가 없는 컴포넌트 입니다.

```jsx
class Diagram extends React.Component {
  render() {
    const { caption, children } = this.props
    
    return (
      <figure className="diagram">
        {children}
        <figcaption>{caption}</figcaption>
      </figure>
    )
  }
}
```

이처럼 상태를 가지지 않는 컴포넌트는 자체적으로 상태를 가져 앱을 업데이트 할 목적이 없는 경우 사용합니다. 상위 컴포넌트로부터 전달 받은 속성 값이 변경될 때 수동적으로 업데이트 됩니다.

{% hint style="info" %}

#### 상태가 없다면? 클래스 보다 함&#xC218;*!*

상태를 가지지 않은 컴포넌트로 클래스를 사용해도 무방하나, 함수 유형 작성이 간결합니다. 작성이 간결하고 관리가 수월해 상태를 가질 필요가 없는 컴포넌트인 경우 함수로 작성하는 경우가 일반적입니다.

```jsx
const Diagram = ({caption, children}) => (
  <figure className="diagram">
    {children}
    <figcaption>{caption}</figcaption>
  </figure>
)
```

{% endhint %}

## 상태를 소유한 컴포넌트 <a href="#stateful-component" id="stateful-component"></a>

상태를 가지는 컴포넌트의 특징은 하위에 상태를 가지지 않는 컴포넌트를 포함하며 자신의 상태를 하위 컴포넌트에 제공합니다. 예를 들어 Counter 컴포넌트는 `count` 상태를 소유하고 있고, 상태를 CountDisplay 컴포넌트에 전달합니다.

```jsx
class Counter extends React.Component {
  constructor(props) {

    // 컴포넌트가 소유한 상태
    this.state = {
      count: 9
    }
  }
  
  render() {
    <div className="counter">
      {/* 컴포넌트의 상태 this.state.count를 전달 */}
      <CountDisplay count={this.state.count} />
    </div>
  }
}
```

{% hint style="info" %}

#### 클래스 필드 (Class Field)

[클래스 필드](https://github.com/tc39/proposal-class-fields) 선언을 사용하면 클래스 컴포넌트의 상태를 보다 손쉽게 설정할 수 있습니다. 이 방법을 사용하면 생성자(constructor) 메서드를 사용하지 않아도 됩니다. 클래스 필드는 ES 표준 명세에 제안된 문법이지만 아직 표준으로 채택되지는 않았습니다. 하지만 [Babel 플러그인](https://babeljs.io/docs/en/babel-plugin-proposal-class-properties)을 사용하면 문제 없이 컴파일 됩니다.

```jsx
// React.Component 추출 
import { Component } from 'react'

class Counter extends Component {
    // 클래스 필드 구문을 사용한 상태 정의
  state = {
    count: 9
  }
  
  // ...
}
```

{% endhint %}

## 함수 vs 클래스 컴포넌트 <a href="#function-vs-class-component" id="function-vs-class-component"></a>

React 컴포넌트는 함수와 클래스 유형으로 컴포넌트를 제작할 수 있습니다. 각 컴포넌트 유형은 개념 상 유사하지만, 함수 컴포넌트와 클래스 컴포넌트의 쓰임새는 다소 다릅니다. 가장 큰 차이로 클래스 컴포넌트에 있는 [라이프 사이클 (Life Cycle)](https://ko.reactjs.org/docs/state-and-lifecycle.html#adding-lifecycle-methods-to-a-class) 개념이 함수 컴포넌트에는 존재하지 않습니다. 컴포넌트 라이프 사이클 메서드는 추후 다뤄볼 것입니다.

| 컴포넌트 유형                | 설명                                                                  | 비고                               |
| ---------------------- | ------------------------------------------------------------------- | -------------------------------- |
| <p>함수형 컴포넌트</p><p></p> | <p>소유(<code>this</code>) 멤버를 가질 수 없으며, 상태(state)를 가지지 못함</p><p></p> | <p>React v16.8+ <br>상태 관리 가능</p> |
| 클래스 컴포넌트               | 소유(this) 멤버를 가질 수 있고, 상태, 라이프 사이클 메서드 설정 가능                         |                                  |

## 불변 객체, 상태 업데이트 <a href="#immutable-and-set-state" id="immutable-and-set-state"></a>

클래스 컴포넌트의 상태 (`state`)에 접근하는 방법은 다음과 같습니다.

```jsx
this.state.activeIndex
```

그렇다면 state 속성 값을 업데이트 하려면 어떻게 해야 할까요? 다음과 같이 작성하면 될까요?

```jsx
this.state.activeIndex += 9
```

**아쉽게도 정답은 '아닙니다'. 위와 같은 방법을 React는 허용하지 않습니다.**

React의 `state`는 [불변(Immutable) 객체](https://ko.wikipedia.org/wiki/%EB%B6%88%EB%B3%80%EA%B0%9D%EC%B2%B4)입니다. 즉, 객체에 변형을 가해서는 안됩니다. 변형이 된다면 더 이상 불변이 아니기 때문입니다. 그런 이유로 앞에서 다룬 방법은 불변 객체를 관리하는 방법이 아닙니다. React는 `state`에 **변형을 가하는 대신, 비교를 통해 교체하는 방법을 사용해 불변 객체를 유지**합니다.

{% hint style="info" %}

#### 왜 React는 불변 객체를 사용할까요?

일반적으로 객체가 참조를 통해 공유되어 있다면 상태가 언제든지 변경 될 가능성도 커져 문제가 됩니다. 반면 불변 객체는 객체를 복제할 때 객체 전체가 아니라 단순 참조만 복사하고 끝냅니다. 참조는 보통 객체 그 자체보다 훨씬 작아서(전형적으로 포인터 크기), 메모리가 절감되며 프로그램 성능 향상에 도움이 됩니다.
{% endhint %}

React는 불변 객체인 상태를 관리하기 위한 방법을 [setState()](https://ko.reactjs.org/docs/react-component.html#setstate) 메서드로 제공합니다. setState() 메서드를 사용하면 `state`를 비교하되, 불변 객체를 유지하면서 상태를 업데이트 합니다.

```jsx
this.setState({
  activeIndex: this.state.activeIndex + 1
})
```

일반적으로는 객체를 전달해 상태를 업데이트 하지만, 상태 업데이트 전 계산이 필요한 경우 함수를 전달하기도 합니다.

```jsx
this.setState((prevState, props) => {
  // 새로운 상태를 반환하기 전 계산 처리
  const changeIndex = props.changeIndex * 2
  
  // 계산 이후 새로운 상태 객체 반환
  return {
    activeIndex: prevState.activeIndex + changeIndex
  }
})
```

또는 구조 분해 할당을 사용해 이전 상태 객체에서 속성 값을 추출해  계산 된 새로운 상태 객체를 반환할 수도 있습니다.

```jsx
this.setState(({ activeIndex }) => ({
  activeIndex: activeIndex + 1
}))
```

## 상태 업데이트 이후 콜백 <a href="#callback-after-status-update" id="callback-after-status-update"></a>

컴포넌트 상태 업데이트가 이뤄진 후, 뭔가를 수행해야 한다면? 콜백(Callback) 함수를 설정해 처리할 수 있습니다.

```javascript
this.setState(
  // 상태 업데이트
  { activeIndex: this.state.activeIndex + 1 },
  // 콜백 함수 설정 (옵션)
  () => console.log('state.activeIndex가 업데이트 되었습니다.')
)
```

{% hint style="info" %}

#### 언제 상태 업데이트 이후 콜백을 사용해야 할까요?

React 컴포넌트 상태 업데이트는 동기 처리가 아니라, 비동기 처리됩니다. 즉, 상태 업데이트는 즉시 발생하지 않습니다. 따라서 상태가 업데이트되기 전에 코드 일부가 실행되는 시나리오에 직면하게 됩니다.&#x20;

이러한 문제를 해결하기 위해 콜백 함수를 제공하는 것입니다. 2번째 인자로 설정 된 콜백 함수는 업데이트가 완료 된 후 실행됩니다. 그러므로 비동기 요청 또는 오류 발생과 같은 상황에서 컴포넌트에 뭔가를 수행해야 할 때 콜백을 사용할 수 있습니다. ([참고](https://upmostly.com/tutorials/how-to-use-the-setstate-callback-in-react))
{% endhint %}

예를 들어 나이(age)를 업데이트 한 이후, 나이를 검사해서 미성년자 유무를 가려야 한다고 합시다. 이런 경우 콜백 함수를 전달해 처리할 수 있습니다. 나이가 변경되서 미성년자가 아니라면 마실 수 있어요! 🍺

```jsx
updateAge = (value) => {
  // 상태 업데이트 후에 this.checkAge 메서드 실행
  this.setState({ age: value}, this.checkAge)
}

// 상태가 업데이트 된 이후, 나이 체크 후 결과 명시
checkAge = () => {
  const { age } = this.state
  if (age >= 19) {
    console.log('미성년자가 아니니 🍺 마실 수 있어요!')
  } else {
    console.warn('미성년자라 🍺 마실 수 없네요.')
  }
}
```

![](/files/-MV-jikHhiFkw7jnBj-3)
