TIL 231113 - React의 주요 hook 정리

송용승·2023년 11월 13일
1

TIL

목록 보기
14/29
post-thumbnail

Today I Learned

오늘은 React 숙련주차에 접어들어 새로 익힌 리액트의 주요 hook 들에 대해 정리해보도록 하겠다.

useState

statestate를 변경하는 업데이트 함수를 리턴하는 함수로, React hook 중에서 가장 기본적이고, 가장 많이 사용하는 hook이다.

const [state, setState] = useState(초기값);

// value === 변경될 state값
setState(value); // 일반 업데이트
//or
setState((prevState) => value) // 함수형 업데이트 

위와 같이 사용하는게 기본으로, setState 함수를 호출해 state 를 변경하면 해당 state 가 선언된 상위 컴포넌트나 props 로 받는 하위 컴포넌트가 리렌더링된다. 이 때 state 를 변경하는 방법에는 일반적인 방법과 함수형 업데이트의 두 방법이 있는데, 함수형 업데이트를 간단히 짚어보자.

state 의 함수형 업데이트

일반적인 state 업데이트는 배치(batch)형식으로 업데이트 된다.

배치 업데이트란 리액트가 업데이트 될 state 를 모아서 한 번에 업데이트 하는 것을 말한다. 아래의 예를 보자.

setCount(count + 1);
setCount(count + 1);
setCount(count + 1);

위와 같이 state 업데이트를 하는 경우, 세 번 업데이트 될 것 같지만 배치 업데이트로 인해 count 는 1 만 증가한다. 다음으로는 함수형 업데이트의 예를 보자.

setCount((prevCount) => prevCount + 1);
setCount((prevCount) => prevCount + 1);
setCount((prevCount) => prevCount + 1);

함수형 업데이트를 위와 같이 세 번 호출하면, 그 호출을 모아 순차적으로 실행시킨다. 리액트가 이런 식으로 상태 업데이트를 처리하는 이유는 불필요한 리렌더링을 줄여 성능을 높이기 위해서이다.


useEffect

dependencies array 내의 Reactive value 가 변경될때마다 callback 함수 내의 내용을 실행하는 훅이다. 바꿔 말해

렌더링 시에 특정 작업을 수행해야 할 때 사용하는 훅이다.

function UseEffect() {
  const [value, setValue] = useState("");
  useEffect(() => {
    console.log("hello useEffect!");
  }, []);

위의 코드에서는 콘솔 창에 로그를 띄워주는데, 이 효과는 페이지를 처음으로 로드할 때에만 일어난다. 왜일까?

dependencies array

useEffect(()=>{}, []);

useEffect 호출부에서 두 번째 인자를 보면 배열이 하나 있는데, 이 배열을 dependencies array 라 한다. 이 배열의 내용에 따라 useEffect 내부 함수의 실행 시기가 달라진다. 배열 안에는 Reactive value 를 담을 수 있는데, Reactive value 는 비단 propsstate 뿐 아니라 이들을 이용해 계산한 값 또한 포함된다. 어찌 보면 당연한데, propsstate 가 변해서 리렌더링이 일어나므로 계산한 값 또한 달라지게 되는 것이다. 배열의 내용에 따른 차이를 간단히 살펴보자.

  • [value, value2, ... ] → 이 경우 배열 내부의 Reactive value 가 업데이트 될 때마다useEffect 내부 함수가 호출된다.
  • [] → Reactive value 가 없다. 즉 내부 함수를 호출할 방법이 없는 것이다. 이런 경우 내부 함수는 컴포넌트가 처음 mount 될 때에만 호출된다.
  • 생략 → 배열 자체가 생략된 경우이다. 이런 경우 컴포넌트가 리렌더링 될 때마다 내부 함수가 실행된다. 조심해야 하는 경우이다.

cleanup function

useEffect 훅에는 컴포넌트가 마운트 되고 리렌더링 될 때마다 실행되는 함수도 있지만 반대로 컴포넌트가 unmount 될 때 실행하는 함수도 정의할 수 있다. 이걸 cleanup function이라 하는데, 이 함수에 대해서는 아직 배운 바가 별로 없어 다음에 다뤄보도록 하겠다.


지금까지 살펴본 hook들은 실제로 리액트 앱이 원하는 대로 동작하는 데에 필수적인 것들이다. 물론 더 많이 있겠지만 지금까지 배운 내용들에서 분류를 하자면 그렇게 나눌 수 있을 것 같다는 이야기다.

이 다음부터 설명할 hook 들은 리액트로 다양한 층위의 컴포넌트를 구성하고 사용하는데 도움이 될 것들이다.


useRef

이 hook은 리액트 hook 의 네이밍 패턴인 use 어떤 값을 reference로 삼는다는 의미에서 Ref 를 붙인 hook으로, 용도는 크게 두 가지로 나눌 수 있다.

  • 기억해 둘 특정 값을 저장
  • document 메소드보다 편하게 DOM 객체에 접근 (물론, DOM 객체도 값이므로 엄밀히는 하나라고 할 수도 있다.)

기본적인 용법은 아래와 같다

// 값 저장
const ref = useRef("초기값");
console.log(ref); // {current: '초기값'}

ref.current = "변경값";
console.log(ref); // {current: '변경값'}


// DOM 요소 접근
const idRef = useRef(null);
useEffect (() => {
	idRef.current.focus(); // 컴포넌트가 마운트 될 때 idRef가 참조하는 DOM 요소에 `focus`
}, [])

return (
	<> 
		<div> 
			아이디 : <input type="text" ref={idRef} /> <!-- ref 속성에 idRef를 전달  -->
		</div>
	</>
);

document.querySelector('#id') 와 같이 번거롭게 접근하지 않아도 된다는 점도 좋지만, 이 외에도 useRef hook 을 사용하는 장점은 더 있다.

저장공간

  • state와 비교했을 때 값을 저장한다는 점은 같지만 state는 변화가 일어나면 다시 렌더링이 일어나고 컴포넌트 그 과정에서 내부 변수도 초기화 된다.
  • ref에 저장한 값은 렌더링을 일으키지 않는다. 즉, ref의 값 변화가 일어나도 렌더링으로 인해 내부 변수들이 초기화 되는 것을 막을 수 있다.

useContext

props drilling으로 발생하는 문제에 대처하기 위한 대책으로 만들어진 hook이다. props drilling이란 상위 컴포넌트에서 계층 차이가 많이 나는 하위 컴포넌트가 필요로 하는 변수를 props 로 전달하기 위해 계속해서 하위 컴포넌트로 같은 props 를 전달하는 현상을 일컫는데, 이런 방식의 문제점이 뭘까?

  • 한 두 계층 아래의 컴포넌트에게 props 를 전달하는 것은 문제가 아니다. 하지만 컴포넌트의 계층이 수십개가 된다면 어떨까? 작성할 코드가 늘어나는데다가 이 props 를 필요하는 컴포넌트가 아니면 쓸 일도 없기 때문에 무의미한 코드라 할 수 있다.
  • 열심히 drilling 하던 중 무언가 문제가 발생했다면 어떨까? 하다못해 오타만이라도 발생하면 지금까지 props 를 전달받은 하위 컴포넌트를 하나하나 들여다보며 수정해야한다. 파악하기도 어렵다.

이런 배경에서 useContext 가 등장한 것이다. 이 hook 의 경우 사용 과정에서 여러 파일을 오락가락 해야하므로 순서로 코드를 대신하겠다.

  1. context 를 저장할 js파일을 하나 만들고
  2. createContext 함수를 호출하고
  3. 그 인자로 초기값을 설정해준 다음
  4. 해당 js 파일을 export 한다.
  5. context에 담을 데이터를 가진 컴포넌트에서 해당 js 파일을 import 하고
  6. MyComponentName.Provider 라는 React Element로 하위 컴포넌트를 감싸고
  7. value 속성에 object 형태로 변수를 넣어준다.
  8. 하위 컴포넌트에서 이 데이터에 접근하려면
  9. 우선 useContext(MyComponentName) 의 값을 새로운 식별자myData에 할당하고
  10. 필요한 부분에서 myData.propertyName 을 통해 접근할 수 있다.
  • 주의할 점! context를 설정한 상위 컴포넌트에서 context 에 포함되어있는 데이터의 값을 바꾸면 해당 컴포넌트의 하위 컴포넌트가 모두 다 리렌더링된다.

후속 글이 나온다면 그 때에는 프로젝트 진행 과정에서 익힌 다른 hook 들을 다뤄보겠다.


profile
웹 프론트엔드 개발을 익히고 있습니다.

0개의 댓글