훅 라이브러리는 React용 Hooks 및 유틸리티의 모음입니다. 즉, 기존 React 기능을 대체하거나 향상시키기 위한 사용자 정의 Hook 모음이라고 할 수 있습니다. React는 그 자체로도 매우 강력한 라이브러리이지만, React 커뮤니티에서 만들어진 훅을 사용하면 코드를 훨씬 명확하고 간결하게 작성할 수 있습니다. 또한 직접 만든 커스텀 훅을 라이브러리로 배포할 수도 있습니다.
이번 챕터에서는 다양한 훅 라이브러리 중에서도 유용하게 사용할 수 있는 몇 가지 훅 라이브러리에 대해 알아보도록 하겠습니다.
16.2 React Hook Form
16.2.1 React Hook Form이란?
React에서 form의 입력과 변경 사항을 처리하는 과정이 복잡해지고 다양한 유효성 검사를 해야할 경우 라이브러리를 사용하면 간편하게 처리하는 것이 가능합니다. React form 라이브러리 중에는 formik, react final form 등 다양한 라이브러리가 존재하지만 가장 인기있는 라이브러리는 React Hook Form입니다. React Hook Form의 특징을 살펴보며 가장 많이 사용되는 이유를 알아보겠습니다.
React Hook Form의 공식 문서에서는 성능 향상을 위해 만들어졌다고 이야기하고 있습니다. uncontrolled components(비제어 컴포넌트)를 기반으로 제작되었으며 사용자의 입력으로 인해 발생하는 수많은 리렌더링을 방지하는 것이 가능합니다. 비제어 컴포넌트 방식을 사용함으로써 ref를 통해 form 값을 가져오기 때문에 렌더링을 최소화할 수 있습니다. 물론, controlled components(제어 컴포넌트) 방식도 제공하고 있습니다. 하지만 사용자의 입력이 많거나 복잡하다면 코드의 상태 관리를 하는 것이 어려워지며 렌더링 발생 빈도가 증가하면서 성능에 영향을 미치게 됩니다.
또한 input 사용 시, 많이 사용되는 이벤트를 React Hook Form으로 직관적이고 간결하게 작성하는 것이 가능합니다. 그렇기 때문에 유지보수에도 용이하며 코드의 양을 줄일 수 있게 됩니다. 이는 다음 챕터에서 React Hook Form에서 제공하는 Hook을 살펴보며 자세하게 알아보도록 하겠습니다.
16.2.2 React Hook Form 설치방법
React 프로젝트를 생성하고 프로젝트 폴더로 이동한 뒤, npm 혹은 yarn 명령어를 입력하여 React Hook Form 라이브러리를 설치할 수 있습니다.
React Hook Form은 useForm Hook을 불러오는 것이 기본이며 사용하는 컴포넌트에서 import한 후, 사용하는 컴포넌트 함수 내부에서 호출하여 사용합니다.
16.2.3 React Hook Form 사용하여 validation 하기
이번에는 사용자로부터 입력받은 값을 validation 하는 예시를 통해 React Hook Form을 사용해보겠습니다.
React Hook Form에서 useForm을 불러와 컴포넌트에서 호출하면, register와 handleSubmit을 사용할 수 있습니다. register는 React Hook Form이 제공해주는 API로, validation 할 form 요소를 React Hook Form에 등록해주는 역할을 합니다. 객체를 반환하기 때문에 아래와 같이 스프레드 문법으로 작성해야 하고 첫 번째 인자로는 form 요소의 name으로 사용될 string 값을 넣어주면 됩니다.
validation을 통과한 값은 handleSubmit을 사용해서 받을 수 있습니다. 아래와 같이 form에 콜백 함수로 onSubmit을 달아주면 validation을 정상적으로 통과한 데이터를 받아 함수를 실행하여 콘솔이 출력되는 것을 볼 수 있습니다.
그림 16-1
이번에는 validation 조건을 추가해 보겠습니다. 만약 입력받는 나이를 20세부터 99세까지로 제한하고 싶다면, 이러한 조건을 register의 두 번째 인자로 작성하여 등록하면 됩니다.
조건을 충족하지 않는 숫자를 입력한 뒤 제출하니 콘솔이 찍지 않는 것으로 보아 validation이 잘 일어나고 있다는 것을 알 수 있습니다.
그림 16-2
min/max 이외에도 pattern, required, minLength/maxLength, validate 등 다양한 조건 타입이 있습니다.
조건 타입
기능
pattern
입력에 대한 정규표현식
required
필수 입력 값
min/max
허용 최소값/허용 최대값
minLength/maxLength
허용 최소 길이/허용 최대 길이
validate
custom validation 함수
validation 조건에 맞지 않으면 submit 되지 않는 것을 확인했으니, 마지막으로 validation error가 발생했을 때 어떻게 사용자에게 에러 메시지를 보여줄 수 있는지 알아보겠습니다.
위 코드와 같이 useForm에서 errors를 불러온 뒤, 해당 폼에서 에러가 났을 때 formState의 error 값이 들어 오게 코드를 작성하면 validation이 실패했을 때 사용에게 보여줄 오류 메시지를 설정할 수 있습니다.
그림 16-3
16.2.4 React Hook Form 사용하여 회원가입 validation 하기
회원가입 validation 구현을 통해 React Hook Form을 사용 전과 후를 비교해보며 React Hook Form에 대해 자세히 알아보겠습니다.
React Hook Form을 사용하지 않고 회원가입 form validation을 구현할 경우, 위 코드와 같습니다. 회원가입 기능을 구현하기 위해서는 사용자에게 이메일, 이름, 비밀번호, 비밀번호 확인 등 여러가지의 입력값을 받아야 합니다. 위 코드에서는 state를 사용하여 입력값을 관리하고 있습니다. 하지만 입력받아야 하는 요소의 개수가 많아질수록 다수의 state를 관리하는 것에는 번거로움이 있으며 코드의 양이 늘어나기 때문에 이를 방지하기 위해 React Hook Form을 사용하여 구현해보겠습니다.
⓵ 이메일, 이름, 비밀번호, 비밀번호 확인 입력창을 각각 만들어줍니다. 필수 입력 값이기 때문에 required를 true로 설정한 후, 입력하지 않았을 경우 설정한 message를 출력합니다.
⓶ 해당하는 이름에 일치하는 errors가 있을 경우, 에러 메시지를 출력합니다.
⓷ mode는 사용자가 form을 submit하기 전에 유효성 검사를 실행할 수 있도록 합니다. onChange 이벤트가 발생할 때마다 유효성 검사를 실행합니다.
⓸ watch는 'password'라는 name을 가지는 input element를 관찰합니다. 만약 ‘password’와 ‘passwordConfirm’이 일치하지 않을 경우 에러 메시지를 반환합니다.
⓹ 파라미터로 data가 들어오며 submit시, data가 console에 출력되는 것을 확인할 수 있습니다.
🚨
useForm()의 mode
사용 가능한 값은 아래와 같으며, 각각의 속성에 대한 설명은 react-hook-form 공식 문서에서 확인할 수 있습니다.
실행화면은 위와 같습니다. 입력값이 유효성 검사를 통과하지 못할 경우 에러 메시지를 출력하며, 유효성 검사에 통과하여 제출하면 data를 제대로 출력하는 것을 확인할 수 있습니다.
위처럼 React Hook Form을 활용하여 회원가입 form validation을 구현해보았습니다. input마다 onChange 이벤트에 입력값의 state와 비교하는 함수를 연결하는 것도 가능하지만, React Hook Form을 사용함으로써 코드의 양이 줄어든 것을 확인할 수 있습니다.
16.3 react-router-dom에서 지원하는 Hook
16.3.1 react-router-dom이란?
일반적으로 브라우저는 사용자가 링크를 클릭하거나 양식을 제출하거나 뒤로가기 버튼을 클릭하면 서버에 요청을 보내 페이지를 새로고침 합니다. 하지만 react-router-dom의 내장 컴포넌트인 BrowserRouter, Routes, Route, Link 등을 활용하여 페이지를 새로고침 하지 않고도 URL 주소를 변경할 수 있습니다. Router를 이용하여 URL의 링크에 따라 컴포넌트를 생성할 수 있습니다. React Router는 사용자가 입력한 주소를 감지하는 역할을 하며, 여러 환경에서 동작할 수 있도록 여러 종류의 Router 컴포넌트를 제공하는데 그 중 BrowserRouter 컴포넌트를 이용하여 간단하게 라우팅하는 예제를 살펴보고 넘어가겠습니다.
먼저 코드에서 볼 수 있듯이 BrowserRouter 컴포넌트 안에 Routes 컴포넌트를 작성합니다. Routes 컴포넌트는 여러 개의 Route를 가질 수 있고 Route의 중첩도 가능한데, 그 중 규칙이 일치하는 Route 1개를 렌더링 합니다. 또한 Route의 path 속성에는 경로를, element 속성에는 연결하고자 하는 컴포넌트를 작성할 수 있습니다.
위의 Router 예제 코드를 통해 간단하게 라우팅 하는 법을 알아보았습니다. 지금부터는 본격적으로 react-router-dom이 지원하는 대표적인 Hook 몇 가지를 소개하겠습니다.
16.3.2 react-router-dom 설치방법
React 프로젝트를 생성하고 프로젝트 폴더로 이동한 뒤, npm 혹은 yarn 명령어를 입력하여 react-router-dom 라이브러리를 설치할 수 있습니다.
16.3.3 useLocation
useLocation은 브라우저에 내장된 window.location 객체를 기반으로 하는 React Router의 location 객체를 반환합니다. 사용자가 링크를 클릭하거나 화면을 이동하는 등 현재 위치가 변경 되었을 때 수행하고 싶은 부수적인 효과(Side Effect)가 있을 경우 유용하게 사용할 수 있습니다.
💡
Side Effect란?
React 컴포넌트가 화면에 렌더링된 이후에 비동기적으로 처리되어야 하는 부수적인 효과
현재 URL 주소에 대한 정보를 useLocation Hook이 반환하는 location 객체를 통해 알 수 있습니다. 아래의 예제 코드에서 간단하게 console.log로 확인해보겠습니다.
그림 16-6 위의 예시 코드를 실행한 console 화면
위의 예시 코드를 npm start 명령어를 입력하여 실행하고 URL에 /home을 더하면 콘솔창에서 useLocation에서 반환된 location 객체를 확인할 수 있습니다. 아래에서 조금 더 구체적인 location 객체 예시를 살펴보겠습니다.
위의 Location 객체 예시에서 pathname, search, hash 값을 더하면 브라우저에서 사용자에게 보여지는 URL 주소(/home?event=instagram#product)를 알 수 있습니다. 또한 state와 key는 Router 마다 다른 값을 가질 수 있습니다. location.state는 컴포넌트 간 props를 전달하는 것 처럼 16.3.4에서 다룰 useNavigate를 이용하여 전달하려는 데이터를 Router 주소와 함께 객체 형태로 전달할 수 있고, 해당 데이터를 전달 받는 컴포넌트에서 useLocation을 이용하여 location.state의 형태로 사용할 수 있습니다.
16.3.4 useNavigate
useNavigate 훅을 호출하면 특정 경로를 가진 페이지로 리다이렉트 시킬 수 있는 함수를 반환합니다. 반환된 navigate 함수에 이동하려는 페이지 경로를 인자로 전달하여 특정 페이지로 이동할 수 있습니다.
홈페이지에서 버튼을 클릭했을 때 프로필 페이지로 이동하도록 하는 간단한 예제 코드를 살펴보겠습니다.
먼저, Route 컴포넌트에 path와 element props를 넣어 특정 경로와 컴포넌트를 연결합니다.
React Router는 버전 5에서 버전 6으로 업그레이드를 하면서 useHistory 훅을 useNavigate 훅으로 교체했습니다. 훅의 이름만 바뀐 것이 아니라 특정 경로의 페이지로 이동하기 위해 호출하는 부분인 history.push 와 history.replace도 변경된 것을 확인할 수 있습니다.
React Router의 메인 컨셉 중 하나는 React Router가 제공하는 기능을 통해 기록 스택(history stack)을 조작하는 것입니다. useNavigate 훅에서도 기록 스택을 조작할 수 있습니다.
navigate 함수 두번째 인자에 { replace: true } 를 전달하면 replace 액션이 실행되어 기록 스택의 가장 상단 페이지를 이동하려는 페이지로 교체합니다.
navigate 함수에 숫자를 인자로 전달하면 해당 숫자만큼 전/후에 방문했던 페이지로 이동하게 됩니다. 예를 들어, navigate(-1)은 직전에 방문했던 페이지로 이동합니다.
💡
기록 스택(history stack)이란?
DOM의 window 객체는 history 객체를 통해 브라우저의 세션 기록에 접근하는 다양한 방법을 제공합니다. history 객체 내 back(), forward(), go() 메서드를 사용해 브라우저 기록을 조작할 수 있습니다. 브라우저 기록은 제일 최근에 방문한 페이지가 기록 제일 상단에 있고, 뒤로 가기 버튼을 누르면 바로 이전 페이지를 다시 방문할 수 있는 스택의 형태로 구현되어 있습니다. 기록 스택을 토대로 우리는 브라우저에서 뒤로 가기 버튼을 계속 누르고 있으면 방문 기록(그림 3-1)을 확인할 수 있습니다.
그림 16-7
추가적으로, navigate 함수의 두번째 인자로 state 프로퍼티를 넘겨서 이동하는 페이지에게 데이터를 전달할 수 있습니다.
16.3.5 useParams
useParams 훅을 호출하면 현재 페이지 URL의 파라미터 값이 담긴 객체가 반환됩니다. 이때, 반환된 객체는 Route 컴포넌트의 path props로 설정한 URL 중 : 뒤에 오는 동적 파라미터 이름(예. :nickname)을 키값으로 가집니다.
실제로 반환된 객체의 키와 값을 확인하기 위해 16.3.4 useNavigate에서 선언한 Route를 다시 보겠습니다.
Profile 컴포넌트의 경로가 profile/:nickname 으로 설정되었고, 동적 파라미터의 이름이 nickname인 것을 확인할 수 있습니다.
그림 16-8 실행 결과
그림 16-9. useParams 훅 호출 시 반환되는 객체
useParams 훅을 호출한 결과 { nickname: “hook” } 객체가 반환되어 url에서 동적 파라미터 nickname 값을 얻을 수 있습니다. 따라서, 닉네임 hook이 잘 출력되는 것을 확인할 수 있습니다.
16.3.6 장단점
React Router를 이용하여 SPA(Single Page Application)로 웹 사이트를 개발하면 단 하나의 HTML 문서를 이용하여 서버에 요청하지 않고도 페이지를 이동할 수 있기 때문에 서버의 부하를 줄일 수 있고 필요한 리소스만 부분적으로 로딩하여 UX(User Experience)의 관점에서 성능이 좋습니다. 하지만 MPA(Multiple Page Application)에 비해 JavaScript 파일의 의존도가 높아 최초 로딩이 비교적 느리고, 검색 엔진 최적화(SEO)에 불리하기 때문에 검색 노출이 중요한 웹 애플리케이션의 경우 검색 엔진 최적화를 위해 추가로 대응해주어야 한다는 단점이 있습니다.
16.4. React Query에서 지원하는 Hook
그림 16-9
16.4.1 React Query이란?
React Query는 서버에서 클라이언트로 데이터를 가져오거나, 동기화 및 업데이트를 쉽게 만들어주고 캐싱 기능까지 지원해주며 비동기 로직을 보다 쉽게 다루게 해주는 리액트 라이브러리 입니다.
❓
캐싱이란?
동일한 데이터를 반복해서 가져오는 것을 줄이기 위해 메모리에 저장해두는 행위
React Query를 사용하는 이유는 무엇일까요? 서버에서 받아온 데이터는 언제까지 신선(fresh)한 상태라고 보장할 수 없습니다. 시간이 흘러 유저가 상호작용하여 데이터가 변하였을 수도 있고, 서버에서 변경되서 새로운 정보를 받아와야할 수도 있습니다. 클라이언트에서 해당 로직들을 일일히 구현하는 일은 부담이 될 수 있습니다. 여기서 React Query는 최신의 데이터를 간편하게 유지하는 다양한 방법을 제공합니다. 또한 React Hook 처럼 사용될 수 있습니다.
16.4.2 React Query 설치방법
React 프로젝트를 생성하고 프로젝트 폴더로 이동한 뒤, npm 또는 yarn 명령어를 입력하여 React Query 라이브러리를 설치할 수 있습니다.
16.4.3 React Query 셋팅 및 사용방법
위와 같이 컴포넌트 최상단에서 new QueryClient 생성자 함수를 사용하여 queryClient 인스턴스를 생성하고 <QueryClientProvider> 컴포넌트에 client prop로 전달하여 줌으로써 초기 셋팅은 끝납니다.
16.4.4 useQuery
useQuery는 비동기 통신을 이용해 GET 요청으로 데이터를 불러 오는데 사용됩니다.비동기 통신이 끝나면 isLoading은 true에서 false로 바뀌고 data에 요청한 데이터가 담기게 되며 error 발생했을 시 error에 반환해줍니다.
useQuery에 들어가는 인자 값
queryKey: useQuery마다 부여되는 고유 Key 입니다. 단순 문자열을 할 수 있고 배열의 형태로도 사용될 수 있습니다.
queryFn: 쿼리가 데이터를 요청하는 데 사용할 함수입니다. fetch나 axios를 사용하여 promise를 반환하는 함수가 와야합니다.
options: 생략가능한 인자로 옵션을 추가할 때 사용됩니다. enable, retry 등등 많은 옵션들이 있습니다.
아래는 실제 사용 예시입니다.
React-Query를 사용한다면 처음 비동기 통신을 한번만 보내기 위해 useEffect를 쓸 필요가 없습니다. 또한 받아 온 데이터를 보여주기 위해, 로딩 상태를 지정하기 위해 useState를 사용할 필요가 없습니다. React-Query는 이 모든걸 useQuery Hook으로 간단하게 만들어줍니다.
16.4.4 useMutation
useQuery가 GET 요청으로 데이터를 불러 오는데 사용된다면 useMutation은 React Query를 이용해 서버에 데이터 변경 작업을 요청할 때 사용합니다. isLoading 과 data는 useQuery와 같이 완료 여부와, 데이터를 반환하지만 mutate은 mutationFn으로 제공된 함수를 작동시키는 트리거 함수입니다.
useMutation에 들어가는 인자 값
mutationFn: 데이터를 수정하고 업데이트하고 삭제하는데 사용할 함수입니다. fetch나 axios를 사용하여 promise를 반환하는 함수가 와야합니다.
options: useMutation에도 많은 옵션이 있지만 많이 쓰는 4가지 옵션이 있습니다.
onMutate: Promise 객체를 반환하는 Callback 함수로써 mutation 함수가 실행되기 전에 실행되고 mutation 함수와 동일한 변수가 제공됩니다.
onError: mutation이 실패한 경우 실행할 Callback 함수 입니다.
onSettled: mutation이 실패 또는 성공 여부에 관계 없이 실행할 Callback 함수 입니다.
onSuccess: mutation이 성공한 경우 실행할 Callback 함수 입니다.
아래는 실제 사용 예시입니다.
useMutation를 사용해 생성, 업데이트, 삭제 등의 처리 할 수 있고, queryClient가 검색할 수 있는 Query key라면 queryClient.invalidateQueries(queryKey)를 통해 데이터를 손쉽게 갱신 해 줄 수 있습니다. 데이터를 갱신하는 방법은 이 밖에도 여러가지 방법이 있지만 여기서는 invalidateQueries 만 다루어 보았습니다.
이 밖에도 React-Query의 가능성은 무궁무진합니다. 해당 라이브러리에 대해 조금 더 알고 싶다면 공식 사이트를 방문해 주세요.
React는 UI, 라이프사이클, State 등 다양한 측면에서 유용한 Hook들을 만들어 라이브러리로 배포하고 있습니다. react-use 라이브러리는 매우 다양한 Hook들을 지원하기 때문에 보다 편리한 개발을 진행할 수 있습니다. 이번 챕터에서는 React use에서 제공하는 다양한 Hook 중에서 몇 가지 유용한 Hook들에 대해 알아보도록 하겠습니다.
React 프로젝트 생성 후 프로젝트 폴더로 이동한 뒤, npm 혹은 yarn 명령어를 입력하여 React use 라이브러리를 설치할 수 있습니다.
16.5.3 useUpdateEffect
useEffect Hook은 React에서 특정 함수가 업데이트 될 때 함수를 호출할 경우 사용되는 Hook입니다. useUpdateEffect Hook은 useEffect와 같이 특정 함수가 업데이트 될 때 사용할 수 있는데, useUpdateEffect 는 최초 마운트 시 실행이 되지 않는다는 차이점을 가지고 있습니다. 따라서 마운트가 될 때 실행을 건너뛰고 싶을 경우 유용하게 사용할 수 있으며, 사용법은 useEffect와 동일합니다.
아래 예제를 통해 useEffect와 useUpdateEffect의 차이점에 대해 좀 더 알아보도록 하겠습니다.
그림 16-10
그림 16-11
useEffect를 사용하게 되면 화면이 처음 렌더링 될 때에도 함수가 실행되기 때문에 console에 현재 input값과 ‘첫 마운트 시에 실행됩니다.’ 라는 문구가 출력이 된 것을 확인할 수 있습니다.
그림 16-12
그림 16-13
반면 useUpdateEffect를 사용하는 경우, 화면이 처음 렌더링 될 때에는 실행이 되지 않기 때문에 console창에 아무것도 출력이 되지 않고, 값이 update 되는 경우에만 console에 내용을 출력해 주는 것을 확인해 볼 수 있습니다.
16.5.4 useDebounce
debounce는 함수를 여러 번 호출하고 마지막 호출에서 일정 시간이 지난 후 해당 함수의 기능이 동작하는 기법을 말합니다. 검색처럼 입력하는 글자에 따라 결과를 출력해 주고 싶을 때, 사용자가 단어를 입력할 때마다 API 요청을 보낸다면 불필요한 API 요청이 생기게 되고, 서버에 부하가 생길 수 있습니다. 이럴 경우 debounce 기법을 사용하게 된다면 서버의 부하를 줄일 수 있게 됩니다.
debounce를 구현하는 방법에는 커스텀 훅 만들기와 같은 다양한 방법이 있지만, react-use의 useDebounce를 사용하게 되면 보다 쉽게 debounce의 구현이 가능합니다.
useDebounce는 첫 번째 인자로 debounce 될 때 호출될 함수, 두 번째 인자로 지연 단위, 세 번째 인자로 변화를 감지할 값의 배열을 가집니다. 지연 단위는 ms(밀리초) 단위로 작성해 주어야 합니다.
16.5.5 useLocalStorage
LocalStorage는 key와 value로 이루어진 데이터를 저장할 수 있는 웹 스토리지 입니다. 개발자 도구를 열어 “애플리케이션” 탭으로 들어가 접근이 가능하며, LocalStorage는 해당 브라우저를 닫더라도 데이터가 계속해서 유지되는 특성이 있습니다. 이러한 특성으로 인해 로그인 상태를 유지하는 기능을 구현하기도 합니다.
이러한 LocalStorage는 react-use의 useLocalStorage를 사용하여 좀 더 간편하게 관리 할 수 있는데 간단하게 useLocalStorage 의 형태와 사용 예제에 대해 알아보도록 하겠습니다.
useLocalStorage는 위와 같이 useState와 비슷한 형태를 띄는데 먼저 value는 LocalStorage의 value값이 들어가고 setValue는 value를 바꾸어주는 역할을 하는 콜백함수이며, removeValue는 LocalStorage에 있는 값을 비워주는 역할을 하는 콜백함수 입니다. useLocalStorage 의 첫 번째 인자는 LocalStorage의 key값이 들어가고 두 번째 인자는 LocalStorage의 값이 비어있다면 초기로 세팅해줄 값을 적어주는데 이는 적어주어도 되고 적어주지 않아도 되는 옵션값입니다.
useLocalStorage 훅 사용 예제
예제와 같이 코드를 작성하면 위와 같이 셋팅이 되는데 useLocalStorage 의 두 번째 인자에 “기본값” 이라고 작성했기 때문에 초기의 LocalStorage의 값이 “기본값”으로 설정되어 있는 것을 확인할 수 있습니다. 버튼을 클릭하여 value를 변경해보겠습니다.
위의 이미지와 같이 LocalStorage에 있던 “기본값”이 포도가 된 걸 확인할 수 있으며 이에 따라 화면에 렌더링 되던 “기본값”이라는 텍스트 또한 “포도”로 바뀐 것을 확인할 수 있습니다. LocalStorage의 값을 삭제하는 버튼을 클릭해보겠습니다.
위와 같이 LocalStorage의 key와 value가 모두 삭제되고 화면에 렌더링 되어지는 Value값 또한 지워지는 것을 확인할 수 있습니다.
이처럼 useLocalStorage 을 활용한다면 LocalStorage의 값을 추가하고, 삭제하고, 변경하는 것을 좀 더 간편하게 할 수 있습니다.
16.5.6 useClickAway
useClickAway Hook은 특정 UI의 외부를 클릭했을 때 함수를 실행할 수 있게 해주는 Hook입니다. 예를 들어 모달창이 활성화가 되어있는 상태에서 닫기 버튼이 아닌 외부를 클릭하여 모달창을 닫고싶은 경우가 있을 수 있습니다. 이럴 때 useClickAway를 사용하면 이를 더욱 쉽게 구현할 수 있는데 예제를 통해 useClickAway 의 기본구조와 사용법에 대해 알아보고 모달창을 닫는 기능을 구현해보도록 하겠습니다.
useClickAway의 기본형태는 위와 같으며 아래의 예제를 통해 자세한 용법에 대해 알아보겠습니다.
App.jsx
예제는 위의 이미지와 같이 분홍박스 바깥 부분을 클릭하면 콘솔 창에 메시지가 출력되고 분홍박스를 클릭하면 아무 일도 일어나지 않는 간단한 예제입니다. 이를 통해 알 수 있는 것은 useClickAway의 첫 번째 인자에는 외부 영역 이외의 요소를 가리키며 두 번째 인자에는 ref의 외부 요소가 클릭 되었을 때 실행될 함수를 가리킵니다. 그리고 세 번째 인자에는 원하는 이벤트 종류를 넣게 되는데 세 번째 인자를 생략할 경우 기본적으로 ‘click’ 이벤트시 트리거되도록 설정이 됩니다.
또한 위의 예제처럼 두 번째 인자에서 바로 함수를 선언하여 사용할 수도 있습니다.지금까지useClickAway의 기본형태에 대해 알아보았으니 이어지는 예제에서 모달창을 닫는 기능을 구현해보도록 하겠습니다.
App.jsx
예제와 같이 코드를 작성하면 위와 같이 모달창을 띄울 수 있는 버튼이 생성됩니다. 버튼을 누르게 되면 모달창이 활성화가 되며 “예”를 누르면 모달창이 닫히게 됩니다. 현재의 상태에서는 모달창의 외부를 클릭하여도 모달창은 그대로 남아있는 것을 알 수 있는데 이는 사용자 편의성을 고려해보았을 때 약간은 불편하다고 생각이 들 수도 있습니다. 따라서 모달창의 외부를 클릭하였을 때에도 모달창이 닫힐 수 있게 useClickAway 를 활용하여 코드를 개선해보겠습니다.
App.jsx
앞선 예제를 위와 같이 변경해주면 모달창의 외부를 클릭하여도 모달창이 닫히는 것을 확인할 수 있습니다.
이처럼 useClickAway 를 활용하면 특정 UI의 외부 요소가 클릭 되었을 때 콜백 함수를 실행시킬 수 있고 클릭뿐만 아니라 다양한 event를 적용할 수 있습니다.
16.6 자신이 만든 커스텀 훅으로 훅 라이브러리 배포하는 방법
지금까지와 같이 우리는 배포되어 있는 훅 라이브러리를 가져와서 사용할 수 있을 뿐만 아니라, 15장에서 직접 만들어 본 ‘커스텀 훅’을 npm을 통해 배포할 수 있습니다. 16.6장에서는 15.4.1장에서 만든 useTitle 훅을 이용하여 커스텀 훅을 배포하는 과정을 살펴보겠습니다.
로그인이 완료되었다면 배포하고자 하는 커스텀 훅이 존재하는 폴더로 이동해야 합니다. 현재 useTitle 훅은 src/useTitle/ 폴더에 존재하므로 아래와 같은 명령어를 통해 터미널의 디렉토리를 이동해야 합니다.
터미널의 현재 디렉토리가 커스텀 훅이 존재하는 폴더를 가리킨다면 이제 배포를 위한 package.json 파일을 생성해야 합니다. 아래의 명령어를 입력하여 package.json 파일을 초기화합니다.
위의 명령어를 실행시키면 아래의 그림과 같이 초기값들을 설정할 수 있습니다.
그림 16-15
이 중 다른 값들은 기본 값으로 설정하거나 설정하지 않아도 관계 없지만 name과 main(entry point)의 값들은 반드시 설정해주어야 합니다.
name에는 배포하고자 하는 라이브러리의 이름이 들어가야 합니다. 추후 npm을 통해 배포를 할 때 기존에 존재하는 다른 라이브러리와 같은 이름을 가지고 있다면 에러가 발생하게 됩니다. 그리고 main에는 커스텀 훅이 정의되어 있는 파일의 이름이 들어가야 합니다. 이를 통해 추후 라이브러리를 install하고, 사용할 때 해당 파일을 참조할 수 있게 됩니다.
기본적인 값들의 설정이 끝났으면 커스텀 훅의 종속성을 설정해주어야 합니다. 이때 기존의 Dependency가 아닌, 생성하고자 하는 패키지가 다른 호스트 패키지에서 사용될 때 필요한 종속성을 의미하는 peerDependency라는 키에 커스텀 훅이 종속되는 것들을 입력해주어야 합니다. 현재 패키지의 경우, useEffect를 사용하므로, 아래와 같이 리액트에 대한 종속성만 입력해주면 됩니다.
그림 16-16
package.json의 설정이 완료 되었다면 이제 배포를 시작할 수 있습니다. 터미널에 아래와 같은 명령어를 입력하면 npm을 통해 배포를 시작할 수 있습니다.
그림 16-17
정상적으로 배포가 완료 되었다면 npm 홈페이지에서 배포한 라이브러리를 찾아볼 수 있습니다. 현재 배포한 라이브러리의 이름은 use-document-title-react이므로 이를 npm 홈페이지에 검색하면 아래와 같이 정상적으로 배포된 것을 확인할 수 있습니다.
그림 16-18
정상적으로 배포가 완료되었으니 해당 라이브러리가 정상적으로 동작하는지 확인해 볼 필요가 있습니다. react 프로젝트의 최상위 폴더 즉, package.json 파일이 존재하는 위치로 이동하여 아래의 명령어를 실행하면 직접 제작하여 배포한 커스텀 훅을 설치할 수 있습니다.
해당 명령어를 실행하면 package.json에 라이브러리가 정상적으로 설치된 것을 확인할 수 있습니다.
그림 16-19
여기까지 진행되었다면 이제 훅을 사용하고자 하는 곳에서 해당 라이브러리를 import 할 수 있습니다. use-document-title-react의 경우 useTitle을 return 하였으므로 아래와 같이 import 할 수 있습니다.
그림 16-20
이제 react를 실행시키면 useTitle이 정상적으로 동작하는 것을 확인할 수 있습니다.
import { useParams } from "react-router-dom";
function Profile() {
// url에서 동적 파라미터 nickname 값을 얻을 수 있습니다.
const {nickname} = useParams();
// console.log(useParams());
return <h2>{nickname}님의 프로필 페이지입니다.</h2>;
}
export default Profile;
npm install @tanstack/react-query
npm으로 react-query 설치하기
yarn add @tanstack/react-query
yarn으로 react-query 설치하기
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.StrictMode>
);
index.js
import { useQuery } from "@tanstack/react-query";
// useQuery가 return 하는 객체에는 이 세가지 말고도 여러 속성값이 있습니다.
const { data, isLoading, error } = useQuery(queryKey, queryFn, options);
import { useMutation } from "@tanstack/react-query";
// useMutation이 return 하는 객체에는 이 세가지 말고도 여러 속성값이 있습니다.
const { data, isLoading, mutate } = useMutation(mutationFn, options);