컴포넌트에서 보여줘야 하는 값이 사용자 interaction에 따라 동적으로 달라지는 경우가 있습니다. 리액트에서는 '상태 관리'라는 방법을 통해 이를 구현하는데요. 가변적인 데이터를 '상태(state)'라고 정의하고, 이 '상태'가 변하는대로 뷰를 업데이트 합니다.
리액트 16.8 버전 이전에는, 함수로 상태를 관리할 수 없었습니다. 그러나 리액트 16.8에서 hooks라는 기능이 도입되면서, 함수형 컴포넌트로도 상태를 관리할 수 있게 되었습니다. 아래에서 살펴볼 useState
는 이 hooks 중 하나입니다.
참고: 리액트 컴포넌트의 형태에는 클래스형, 함수형 두 가지가 있습니다. 클래스형 컴포넌트와 함수형 컴포넌트의 차이는, 이를 잘 설명한 블로그가 있어 아래에 주소를 남깁니다.
https://overreacted.io/ko/how-are-function-components-different-from-classes/
버튼을 누르면 숫자가 1씩 증감하는 Counter 컴포넌트를 만들어 보며 상태 개념을 이해해 보겠습니다. Counter 컴포넌트를 만들고, 이를 App 컴포넌트에서 렌더링하겠습니다.
Counter.js
import React, { useState } from 'react'
function Counter() {
return (
<div>
<h1>0</h1>
<button>+1</button>
<button>-1</button>
</div>
)
}
export default Counter;
App.js
import logo from './logo.svg';
import './App.css';
import Counter from './Counter';
function App() {
return (
<Counter />
);
}
export default App;
결과는 아래와 같습니다. 아직 이벤트 설정을 하지 않았기 때문에, 버튼을 눌러도 아무런 변화가 없습니다.
이제는 <Counter />
컴포넌트에서 이벤트를 설정해 보겠습니다. 우리가 원하는 View는, +1
이나 -1
버튼을 클릭하는 이벤트가 발생하면 버튼 위의 숫자가 동적으로 변하며(=페이지 새로고침 없이 변하며) 1씩 더해지거나 감소하는 모습입니다.
Counter.js
import React, { useState } from 'react'
function Counter() {
const onIncrease = () => {
console.log('+1')
}
const onDecrease = () => {
console.log('-1')
}
return (
<div>
<h1>0</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
)
}
export default Counter;
<Counter />
컴포넌트에 두 가지 변화가 생겼네요.
(1) return (...) 이전에 이벤트 발생 시 호출할 두 가지 함수가 정의되어 있습니다(onIncrease, onDecrease).
(2) <button />
에 onClick={...}
로 click 이벤트를 감지할 시 어떤 함수를 호출할지 설정했습니다.
<button>
에서onClick={onIncrease()}
,onClick={onDecrease()}
과 같이()
를 적지 않은 것에 주목해 주시기 바랍니다.
onClick={} 에서 이벤트를 설정하기 위해서는 함수 타입의 값을 입력 해 주셔야지(onIncrease
,onDecrease
), 함수를 호출 하시면 안됩니다(onIncrease()
,onDecrease()
).
()
가 붙을 경우 컴포넌트가 브라우저에 렌더링 됨과 동시에onIncrease
,onDecrease
함수가 호출됩니다. 그리고 버튼을 클릭해도 함수가 호출되지 않습니다. 우리가 원하는 것은 버튼을 누를 때마다 함수가 호출되는 것입니다.
여기까지 저장하고 chrome 개발자 도구의 콘솔 창을 보면, 버튼을 누를 때마다 '+1' 혹은 '-1'이 출력되는 것을 볼 수 있습니다.
이제, 본격적으로 상태를 정의하고 버튼 클릭에 따라 숫자가 변하는 모습을 구현해 봅시다.
Counter.js
import React, { useState } from 'react'
function Counter() {
const [number, setNumber] = useState(0) //[현재 상태, Setter 함수]
//기본값이 0인 number라는 상태를 생성
//setNumber는 number라는 상태를 바꿔주는 함수
const onIncrease = () => {
setNumber(number + 1) //number를 (...)와 같이 변경한다.
}
const onDecrease = () => {
setNumber(number - 1)
}
return (
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
)
}
export default Counter;
<Counter />
컴포넌트의 코드에 또 변화가 생겼습니다.
(1) const [number, setNumber] = useState(0)
코드가 추가되었음.
(2) onIncrease
, onDecrease
함수의 내용이 바뀌었음.
(3) JSX 코드 부분에서 <h1>0</h1>
이 <h1>{name}</h1>
으로 바뀌었음.
하나씩 살펴보죠.
(1)은, 화면에서 버튼을 누르면 변화하는 숫자를 number
라는 상태로 정의하고, 그 초기값을 설정한 부분입니다. 방법은 useState()
의 괄호 안에 값을 적어주는 것입니다. 여기서는 숫자 0으로 초기값을 정했습니다. 또한, setNumber는 버튼이 클릭될 때마다 number를 변경시키는 함수입니다.
원래는 이렇게 작성해야 하는 코드입니다만, '배열 비구조화 할당'이라는 방법을 사용하여 저렇게 한 줄로 간결하게 작성할 수 있습니다(이는 Velopert님께서 블로그에 알기 쉽게 설명해 주셨기에, 내용을 알고 싶으시다면 여기를 클릭해 공부해 봅시다!
const numberState = useState(0); const number = numberState[0]; const setNumber = numberState[1];
(2)는, onIncrease/onDecrease 함수의 동작 내용을 재정의한 것입니다. setNumber로 number라는 상태의 value를 기존 number의 value에 1을 더하거나 뺀 값으로 변화시켜주는 부분입니다. number = number + 1
또는 number = number - 1
해 주는 부분입니다.
(3)은, <h1>
태그 안의 내용으로 맨 위에 정의한 number
상태를 넣은 것입니다. 정의된 상태는 이렇게 JSX에서 {}
안에 사용할 수 있습니다.
이제, 브라우저로 돌아가 버튼을 눌러봅시다. 화면에 있는 숫자가 잘 변하는 것을 확인할 수 있습니다.
<Counter />
컴포넌트에서 Setter함수(setNumber)를 사용했는데요, 이 Setter 함수는 두 가지 방식으로 사용할 수 있습니다.
(1) setNumber(number + 1)
과 같이, 아예 어떠한 값으로 바꿀 목적으로 다음 상태를 넣어줄 수도 있습니다.
(2) setNumber(prevNumber => prevNumber + 1)
과 같이, '이러이러한 로직으로 상태를 업데이트할 것이다' 는 의미에서 함수 를 넣어줄 수도 있습니다. 이런 방식을 함수형 업데이트라고 합니다.
그래서 아래와 같이 작성해도 컴포넌트는 제대로 작동하는 것을 볼 수 있습니다.
Counter.js
import React, { useState } from 'react'
function Counter() {
const [number, setNumber] = useState(0)
const onIncrease = () => {
setNumber(prevNumber => prevNumber + 1)
}
const onDecrease = () => {
setNumber(prevNumber => prevNumber - 1)
}
return (
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
)
}
export default Counter;
함수형 업데이트는 컴포넌트의 성능 최적화와 관련이 있습니다. 이에 관련한 내용은 뒤에서 다루기로 하고, 지금은 이런 개념이 있다는 것만 인지하면 되겠습니다!
이번 장의 내용을 정리하겠습니다. 이번 장에서는 함수형 컴포넌트에서 동적으로 변하는 값의 상태 관리법을 알아보았습니다.
useState()
를 사용한다.[현재 상태, Setter 함수] = useState('초기값')
의 형태로 정의한다.onClick
과 같이 JSX 안에서 on...
으로 이벤트를 설정할 때 함수를 호출(onIncrease(), onDecrease())하는 것이 아니라, 함수 타입의 값(onIncrease, onDecrease)을 입력해야 한다.