휴먼스케이프 Software engineer Covy입니다.
본 포스트에서는 React v16.8부터 새로 도입된 기능인 Hooks, 그 중에서도 프론트엔드 개발을 하면서 많이 쓰일 useState와 useRef에 대해서 알아보도록 하겠습니다. Hooks에 대한 전반적인 설명은 이곳을 참고하시면 좋습니다.
리액트를 다루는 기술
Similarity between useState and useRef
useState와 useRef의 기능상의 공통점은 함수형 컴포넌트에서 동적으로 상태 관리를 할 수 있게 해준다는 점입니다. 간단한 예시로, 버튼이 클릭 되었을때 주어진 state를 “Humanscape!” 라는 string으로 변환하는 동일한 프로그램을 useState와 useRef를 통해 아래와 같이 다르게 구현할 수 있습니다.
[functionality implemented by “useState”]
[functionality implemented by “useRef”]
useState와 useRef 모두 주어진 state를 변화시킬 수 있다는 것을 알아보았습니다. 두 hook의 차이에 대해서 알아보기 전에 이미 위의 코드를 유심하게 살펴본 분들은 아마 아래와 같은 의문을 가질 수 있습니다.
“왜 useState의 구현에서는 “Humanscape!”라는 글씨를 화면에 띄워서 값이 변한 것을 확인했는데, useRef의 구현에서는 console에 log를 찍어 확인하지…?”
제가 왜 이렇게 보여드렸는지 이미 알고 계신 분들이나 이러한 의문을 가지신 분들은 어느정도 두 hook의 차이에 대해서 인지하고 있는 것입니다. 아래에서 자세히 설명하도록 하겠습니다.
Difference between useState and useRef
useRef를 사용한 구현에서 글씨를 화면에 띄우지 않은 것은 useState와는 다르게 useRef는 state를 변화시킨 후에 component를 re-render하지 않기 때문입니다. 다음은 React 공식 문서의 Hooks API Reference에 나와있는 내용입니다.
Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a callback ref instead.
따라서 useRef를 사용한 구현에서 state를 변화시키더라도 변화 후에 re-render가 되지 않아 initial value로 나타날 것이기 때문에 rendering을 할 수는 있지만 변화했다는 의미가 존재하지 않아 따로 화면에 띄우지 않은 것입니다.
반면, useState의 경우 선언한 state가 setter function에 의해 update될 경우, re-rendering process가 진행됩니다. 다음은 React 공식 문서의 Hooks API Reference에 나와있는 내용입니다.
The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
결과적으로 각각의 state에서 이용할 hook을 설계할 때, 주로 state의 rendering 여부를 바탕으로 결정하는 것이 좋습니다. Rendering이 필요한 state의 경우 useState를 이용하는 것이 간편하게 상태관리를 할 수 있으며, rendering이 필요하지 않은 state의 경우 useRef를 쓰는 것이 간단하게 코드를 작성하실 수 있습니다.
Using useRef for naming
앞서 설명드린 내용은 useRef를 상태관리라는 목적으로 사용한 경우였습니다. 하지만 useState와는 다르게 useRef는 각 요소에 이름을 짓는 용도로도 사용할 수 있습니다. 다음은 useRef를 통해 상위 component에서 하위 component 요소에 접근하여 input text box를 focusing 하는 코드입니다.
[parent component]
[child component]
useRef를 통해 특정 요소에 접근하는 방법을 알려드렸습니다. 이렇게 useState와 useRef의 이용에 있어 공통점과 차이점을 위주로 설명을 드렸습니다. 다음은 실제 코드 속에서 useState와 useRef 사용의 차이점을 설명하도록 하겠습니다.
Different usage of useState vs useRef in code
다음과 같은 프로그램을 작성하고 싶다고 합시다.
화면에 1초마다 1씩 증가하는 숫자를 띄우며, 이 숫자의 가시성을 조절할 수 있는 버튼이 존재합니다. 숫자가 mount될 때는 0부터 차례로 증가하며, unmount될 때는 현재 화면의 숫자를 alert 해줍니다.
눈치 빠르신 분들은 이 코드를 왜 예시로 들었는지 벌써 아실 수 있습니다.
“화면에 1초마다 1씩 증가하는 숫자를 띄우며” 라고 했으니 렌더링에 관여한 state를 선언해야 하니까 useState를 사용해야 하고, mount와 unmount의 순간에 동작이 진행해야 하므로 useEffect를 사용하며 2번째 parameter로 빈 배열 []를 넣어주면 되겠구나!
라고 생각하셨으면 대략적으로 다음과 같이 코드를 작성하셨을 가능성이 높습니다.
[App.js]
[Counter.js]
여기까지 오셔서 npm start로 react를 실행시켜보면 렌더링, 가시성 조절은 잘 동작하는데 비해서 unmount를 할 때의 alert값이 0으로 나타남을 확인하실 수 있습니다.
잠시 생각을 거치신 여러분은 또 이렇게 생각하실 수 있습니다.
아! useEffect가 초기에 함수를 생성할 때 return 부분에 counter를 0을 넣고 이후에 재생성하지 않기 때문에 실제로 unmount에서도 0이 나타나는구나! 그렇다면 useEffect 함수가 counter가 변할 때마다 재 생성 해주면 되겠다!
이렇게 생각하셔서 useEffect의 2번째 parameter로 [counter]를 넣으신다면 또 counter가 변하는 매 1초마다 cleanup함수가 반환되어 alert가 1초마다 발생하게 됩니다.
해결방법부터 알려드리자면 Counter.js를 다음과 같이 변화시키면 됩니다.
[Counter.js]
이 코드와 이전의 코드의 유일한 차이점은 useState대신에 useRef를 사용하여 alert를 진행한 것입니다. 이것이 차이를 일으키는 이유는 useRef를 통해 반환된 객체는 component의 생애주기 내내 변화하는 값을 가리키고 있기 때문입니다. 다음은 React 공식 문서의 Hooks API Reference에 나와있는 내용입니다.
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
코드에서 useState와 useRef 이용의 차이에 대해 알아보았습니다. 같은 용도로 두 hook을 사용할 수 있지만 프로그래밍을 진행할 때 자신이 원하는 기능을 더 잘 수행할 수 있는 것을 골라서 사용하는 것이 좋습니다. 마지막으로 두 hooks에 대한 정리를 진행하겠습니다.
Conclusion
useState와 useRef 모두 상태관리를 위해 사용할 수 있습니다. 다만 useState의 경우 state변화 후에 re-rendering을 진행하는 반면 useRef는 진행하지 않습니다. 이러한 특성에 맞추어 렌더링이 필요한 state의 경우에는 useState를 사용하며 그렇지 않은 경우 useRef를 사용하는 것이 좋습니다. 이외에도 useRef는 이름을 지어 접근하는 용도와 생애주기 내내 변화하는 값을 가리키고 있다는 차별점을 가지고 있습니다. 프로그래밍에 앞서 두 기능을 돌아보면서 어떤 것이 원하는 기능을 구현하는데 적합할지 생각해보는 것이 좋습니다.
Get to know us better! Join our official channels below.
Telegram(EN) : t.me/Humanscape KakaoTalk(KR) : open.kakao.com/o/gqbUQEM Website : humanscape.io Medium : medium.com/humanscape-ico Facebook : www.facebook.com/humanscape Twitter : twitter.com/Humanscape_io Reddit : https://www.reddit.com/r/Humanscape_official Bitcointalk announcement : https://bit.ly/2rVsP4T Email : support@humanscape.io
Reference
[1] https://reactjs.org/docs/hooks-reference.html
[2] https://stackoverflow.com/questions/56455887/react-usestate-or-useref
[3] Velopert, 리액트를 다루는 기술