훅은 리액트 버전 16.8에서 새롭게 등장한 개념이다.
최근에는 리액트로 개발을 할 때 대부분 훅을 사용하기 때문에 훅에 대해 잘 이해하는 것이 중요하다.
Hook란?
리액트 컴포넌트에는 두 가지 종류가 있다. 하나는 클래스 컴포넌트, 하나는 함수 컴포넌트
컴포넌트에는 state라는 중요한 개념이 등장한다.
이 state를 이용해서 렌더링에 필요한 데이터를 관리하게 된다.
클래스 컴포넌트에서는 생성자에서 이 state를 정의하고 setState 함수를 통해 state를 업데이트한다.
이처럼 class 컴포넌트는 state와 관련된 기능 뿐만 아니라 컴포넌트의 생명주기 함수들까지 모두 명확하게 정의되어 있기 떄문에 잘 가져다 쓰기만 하면된다.
하지만 기존 함수 컴포넌트는 클래스 컴포넌트와 다르게 코드도 굉장히 간결하고 별도로 스테이트를 정의해서 사용하거나 컴포넌트의 생명주기에 맞춰 어떤 코드가 실행되도록 할 수 없었다.
따라서 함수 컴포넌트에 이런 기능을 지원하기 위해 나온 것이 Hook이다.
Hook을 사용하면 함수 컴포넌트도 클래스 컴포넌트의 기능을 모두 동일하게 구현할 수 있게 되는 것이다.
지금부터 대표적인 훅들에 대해서 하나씩 알아보도록 하겠다.
먼저 가장 대표적이고 많이 사용되는 훅인 useState훅이다.
함수 컴포넌트에서는 기본적으로 state라는 것을 제공하지 않기 때문에 class 컴포넌트처럼 state를 사용하고 싶다면 useState hook을 사용해야 한다.
import React, { useState } from "react";
function Counter(props) {
var count = 0;
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => count++}>
클릭
</button>
</div>
);
}
간단한 예제 코드를 한번 보겠다.
위에 코드에는 카운터라는 함수 컴포넌트가 등장한다.
카운터 컴포넌트는 버튼을 클릭하면 카운트를 하나씩 증가시키고 현재 카운트를 보여주는 단순한 컴포넌트이다.
그런데 만약 이처럼 카운트를 함수의 변수로 선언해서 사용하게 되면 버튼 클릭 시 카운트 값을 증가시킬 수는 있지만 재렌더링이 일어나지 않아 새로운 카운트 값이 화면에 표시되지 않게 된다.
따라서 이런 경우에는 state를 사용해서 값이 바뀔 때마다 재렌더링 되도록 해야 하는데 함수 컴포넌트에는 해당 기능이 따로 없기 때문에 useState를 사용하여 state를 선언하고 업데이트해야 한다.
useState() 사용법
const [변수명, set함수명] = useState(초기값);
useState를 노출할 때에는 파라미터로 선언할 state의 초기값이 들어간다.
클래스 컴포넌트 생성자에서 state를 선언할 때 초기값을 넣어주는 것과 동일한 것이라고 보면 된다.
이렇게 초기값을 넣어 useState를 노출하면 return 값으로 배열이 나온다.
return된 배열에는 두 가지 항목이 들어 있는데 첫 번째 항목은 state로 선언된 변수이고 두 번째 항목은 state 함수의 set함수이다.
useEffect
- Side effect를 수행하기 위한 Hook
Side effect = 효과, 영향 -> 다른 컴포넌트에 영향을 미칠 수 있으며, 렌더링 중에는 작업이 완료될 수 없기 때문
리액트의 함수 컴포넌트에서 Side effect를 실행할 수 있게 해주는 Hook
useEffect는 클래스 컴포넌트에서 제공하는 생명 주기 함수인 컴포넌트 디드 마운트 컴포넌트 디드 업데이트 그리고 컴포넌트 윌 업마운트와 동일한 기능을 하나로 통합해서 제공한다.
그래서 useEffect hook만으로 이러한 생명 주기 함수와 동일한 기능을 수행할 수 있다.
ex) 서버에서 데이터를 받아오거나 수동으로 DOM을 변경하는 등의 작업을 의미
useEffect() 사용법
useEffect(이펙트 함수, 의존성 배열);
첫번째 파라미터로는 이펙트 함수가 들어가고 두번째 파라미터로는 의존성 배열이 들어간다.
의존성 배열은 말 그대로 이 이펙트가 의존하고 있는 배열인데 배열 안에 있는 변수 중에 하나라도 값이 변경되었을 때 이펙트 함수가 실행된다.
기본적으로 이펙트 함수는 처음 컴포넌트가 렌더링된 이후와 업데이트로 인한 재렌더링 이후에 실행된다.
만약 이펙트 함수가 mount와 unmount시에 단 한번씩만 실행되게 하고싶으면 의존성 배열에 빈 배열을 넣으면 된다.
useEffect(이펙트 함수, []);
이렇게 해당 이펙트가 props나 state에 있는 어떤 값에도 의존하지 않는 것이 되므로 여러 번 실행되지 않는다.
그리고 의존성 배열은 생략할 수도 있는데 생략하게 되면 컴포넌트가 업데이트 될 때마다 호출된다.
useEffect(이펙트 함수);
또한 useEffectHook은 하나의 컴포넌트의 여러 개를 사용할 수 있다.
useMemo() Hook
useMemo 훅은 memoized value를 리턴하는 훅이다.
useMemo와 useCallback 훅에서는 memoization이라는 개념이 나온다.
컴퓨터 분야에서 memoization은 최적화를 위해서 사용하는 개념이다.
비용이 높은, 즉 연산량이 많이 드는 함수의 호출 결과를 저장해 두었다가 같은 입력값으로 함수를 호출하면 새로 함수를 호출하지 않고 이전에 저장해 놨던 호출 결과를 바로 반환하는 것이다. 이렇게 하면 결과적으로 함수 호출 결과를 받기까지 걸리는 시간도 짧아질 뿐더러 불필요한 중복 연산도 하지 않기 때문에 컴퓨터의 자원을 적게 쓰게 된다.
메모이제이션이 된 결과 값을 영어로는 Memoized Value라고 부른다.
메모이제이션의 메모는 우리가 흔히 메모하다 라고 표현하는 것과 비슷한 맥락이라고 생각하면 된다.
메모를 해두었다가 나중에 다시 사용한다는 의미와 비슷
useMemo() 사용법
const memoizedValue = useMemo(
() => {
//연산량이 높은 작업을 수행하여 결과를 반환
return computeExpensiveValue(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
useMemoHook은 파라미터로 memoizedValue를 생성하는 create함수와 의존성 배열을 받는다.
메모이제이션의 개념처럼 의존성 배열에 들어있는 변수가 변했을 경우에만 새로 create함수를 노출하여 결과값을 반환하며 그렇지 않은 경우에는 기존함수의 결과값을 그대로 반환한다.
useMemo훅을 사용하면 컴포넌트가 다시 렌더링될 때마다 연산량이 높은 작업을 반복하는 것을 피할 수 있다.
useMemo훅을 사용할 때 기억해야 할 점은 useMemo로 전달된 함수는 렌더링이 일어나는 동안 실행된다는 점이다.
그렇기 때문에 일반적으로 렌더링이 일어나는 동안 실행되서는 안될 작업을 useMemo의 함수에 넣으면 안된다.
예를 들면 useEffectHook에서 실행되어야 할 사이드 이펙트 같은 것이 있다.
서버에서 데이터를 받아오거나 수동으로 DOM을 변경하는 작업 등은 렌더링이 일어나는 동안 실행되서는 안되기 때문에 useMemo훅 함수에 넣으면 안되고 useEffectHook을 사용해야 한다.
useCallbackHook
useMemo() Hook과 유사하지만 값이 아닌 함수를 반환한다.
컴포넌트가 렌더링 될 때마다 매번 함수를 새로 정의하는 것이 아니라 의존성 배열의 값이 바뀐 경우에만 함수를 새로 정의해서 리턴해주는 것이다.
useCallback() 사용법
const memoizedCallback = useCallback(
() => {
doSomething(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
useMemoHook과 마찬가지로 함수와 의존성 배열을 파라미터로 받는다.
useCallbackHook에서는 파라미터로 받는 이 함수를 callback이라고 부른다.
의존성 배열에 있는 변수 중 하나라도 변경되면 메모이제이션된 콜백 함수를 반환한다.
의존성 배열에 따라 메모이즈드 값을 반환한다는 점에서 useMemoHook과 완전히 동일하다.
useRef()
useRef훅은 레퍼런스를 사용하기 위한 훅이다.
여기서 레퍼런스란 무엇일까?
React에서 레퍼런스란 특정 컴포넌트에 접근할 수 있는 객체를 의미한다.
그리고 useRefHook은 바로 이 레퍼런스 객체를 반환한다.
레퍼런스 객체에는 current라는 속성이 있는데 이것은 현재 레퍼런스하고 있는 엘리먼트를 의미한다고 보면 된다.
useRef() 사용법
const refContainer = useRef(초기값);
파라미터로 초기값을 넣으면 해당 초기값으로 초기화된 레퍼런스 객체를 반환한다.
useRefHook은 변경 가능한 current라는 속성을 가진 하나의 상자라고 생각하면 된다.
Hook 규칙
1. Hook은 무조건 최상위 레벨에서만 호출해야 한다.
여기에서 말하는 최상위 레벨은 React 함수 컴포넌트의 최상위 레벨을 의미한다.
따라서 반복문이나 조건문 또는 중첩된 함수들 안에서 Hook을 호출하면 안된다는 뜻이다.
2. 리액트 함수 컴포넌트에서만 Hook을 호출해야 한다.
일반적으로 JavaScript 함수에서 Hook을 호출하면 안된다.
Hook은 React 함수 컴포넌트에서 호출하거나 직접 만든 커스텀 Hook에서만 호출할 수 있다.
이 규칙에 따라 React 컴포넌트에 있는 state와 관련된 모든 로직은 소스 코드를 통해 명확하게 확인이 가능해야 한다.
Hook의 규칙을 따르도록 강제해주는 플러그인(ESLint 플러그인)
JavaScript 코드에서 발견되는 문제 패턴을 식별하기 위한 정적 코드 분석 도구이다.
React 컴포넌트가 Hook의 규칙을 따르는지 아닌지 분석이 가능.
'React' 카테고리의 다른 글
[리액트] Props 정의 실습 (0) | 2024.05.08 |
---|---|
[리액트] Rendering Elements의 정의, 특징 (0) | 2024.05.08 |
[리액트] JSX란? (정의, 역할, 장점 및 사용법) (4) | 2024.05.07 |
[리액트] npx create-react-app 가 실행되지 않을 때 (0) | 2024.05.06 |
[리액트] HTML란 무엇인가 (0) | 2024.05.06 |