리액트 앱을 구축할 때는 앱에서 사용하는 데이터가 시간이 지남에 따라 바뀌리라 예상한다. 랜더링을 모두 서버에서 수행하든, 모바일 앱이나 브라우저에서 전부 수행하든, 애플리케이션의 사용자 인터페이스는 랜더링 시점의 현재 데이터, 즉 상태를 나타내야 한다. 때로는 앱 전체에서 여러 컴포넌트가 이런 데이터를 사용하기도 하지만, 때로는 컴포넌트가 앱 전체에 걸친 큰 상태 스토어(State Store)를 사용하지 않고 상태를 자신만의 비밀로 유지할 수도 있다. 2장에서는 상태를 비공개로 유지하고 주변의 다른 컴포넌트를 고려하지 않고 자신만 신경 쓰는 컴포넌트에 집중 한다.
그림 2.1은 리액트의 작업을 매우 기본적인 수준에서 보여준다. 리액트는 현재 상태를 사용해 UI를 랜더링 해야만 한다. 상태가 변경되면 리액트는 UI를 다시 랜더링해야 한다. 그림은 인사 메시지에 이름을 덧붙이는 모습을 보여준다. 이름 값이 변경되면 리액트는 메시지에서 새 이름을 보여주도록 UI를 갱신한다. 우리는 보통 상태와 UI가 동기화되기를 원한다. (물론 상태 전환(예를 들어 최신 데이터를 가져오는 등.) 중에 동기화를 지연시킬 수도 있다.)
리액튼는 컴포넌트 내의 값을 추적하고 상태와 UI를 동기화하기 위해 몇가지 함수, 즉 훅을 제공한다. 단일 값인 경우 useState 훅을 사용하는데, 이번 장에서는 이 훅을 살펴볼 것이다.
훅을 호출하는 방법, 훅이 반환하는 값, 상태를 갱신해 리액트가 UI를 갱신하는 방법을 살펴보자. 종종 작업을 수행하기 위해 컴포넌트에서 하나 이상의 상태가 필요한 경우도 있기 때문에 여러 값을 처리하기 위해 useState를 여러 번 호출하는 방법도 살펴본다. 이번 장은 단순히 useState를 여러 번 호출하는 방법도 살펴본다. 이번 장은 단순히 useState API를 문서화하는 것이 목표가 아니다.(API 문서라면 공식 리액트 문서를 참고하면 된다.) 우리는 useState 훅을 논의하면서 함수형 컴포넌트가 무엇인지와 함수형 컴포넌트가 어떻게 작동하는지를 더 잘 이해할 수 있도록 도울 것이다. 이를 위해 코드 리스트가 진화함에 따라 사용된 핵심 개념을 검토하면서 이번 장을 마무리한다.
코드 리스트에 관해 이야기 하자. 여기서는 이 책 전반에 걸쳐 주로 예제로 작용할 앱을 사용해 시작한다. 이 예제는 리액트 훅을 사용해 일반적인 코딩 문제를 해결하는 상호아을 보여주는 일관된 배경으로 작용한다. 앱을 설정하기 위해 약간의 정리 작업이 필요하지만, 일단 정리가 끝나고 나면 이번 장의 나머지 부분에서는 한 컴포넌트에만 집중할 수 있다.
2.1 예약 관리 앱 설정하기
재미잇고 전문적인 회사에서는 회의실, AV 장비, 기술자 시간, 탁구대 등 많은 자원을 직원들이 예약할 수 있다. 어느날 상사가 회사 네트워크를 위한 앱의 뼈대를 설정해 직원들이 자원을 예약할 수 있게 하라고 요구했다. 그림 2.2 처럼 Bookings(예약), Bookables(예약 가능 자원), Users(사용자)라는 세 가지 페이지가 있는 앱을 만들어야 한다.
이 앱은 기술적으로는 단일 페이지 애플리케이션 (single-page-application(SPA))이며, 각 페이지는 실제로는 컴포넌트다. 하지만 사용자 관점에서는 페이지 사이를 전환하기 때문에 앞으로는 이를 계속 페이지라고 부를 것이다. 이번 절의 끝에서는 각 페이지를 표시하고 링크로 이동할 수 있게 된다. 이때 프로젝트 폴더에는 그림 2.3과 비슷한 public과 src 폴더가 들어 있게 된다.
components 폴더 내부의 하위 폴더가 어떻게 세 가지 페이지에 해당하는 지 볼 수 있다. 그림과 같은 모양의 앱을 만들기 위해 해야 할 작업은 여섯 가지다.
create-react-app을 사용해 예약 앱의 뼈대를 생성하라.
create-react-app이 생성한 파일 중 사용하지 않을 파일을 제거하라.
public과 src 폴더에 남아 있는 4개의 파일을 수정하라.
npm을 통해 몇 가지 패키지를 설치하라.
앱에 표시할 일부 데이터를 제공하기 위해 데이터베이스 파일을 추가하라.
각 페이지에 대한 하위 폴더를 만들고 페이지 컴포넌트를 넣어라.
이 작업을 하는 대신, 이 책에서 계속 사용할 예약 예제 앱의 코드 예제를 깃허브에서 찾을 수 있다. https://github.com/jrlarsen/react-hooks-in-action 에서 코드의 진화에 따른 브랜치가 설정된 저장소를 볼 수 있다. 예제 앱에 대한 코드 리스트에는 깃허브 리포지터리에서 체크아웃 할 브랜치의 이름이 포함되어 있다. 예를 들어, 리포지터리를 클론한 다음, 첫 브랜치의 코드를 체크아웃하려면 다음 명령을 입력하라
git checkout 0201-pages
다음 명령으로 프로젝트의 의존 관계를 설치할 수 있다.
npm i
다음 명령으로 프로젝트를 실행할 수 있다.
npm start
여기까지 실행한 경우 2.2.절로 넘어가도 좋다.
손을 더럽혀 가면서 앱을 처음부터 만들어 보고 싶은 독자라면, 가장 먼저 필요한 것은 리액트 앱이다.
2.1.1. create-react-app으로 앱 뼈대 생성하기
리액트의 create-react-app 유틸리티는 미리 설정된 린트(lint) 및 컴파일 작업 흐름이 설정된 프로젝트를 생성한다. 또한 이 프로젝트에는 앱을 계속 발전시키면서 작업하는 동안 사용할 수 있는 완벽한 개발 서버도 포함된다. create-react-app을 사용해 react-hooks-in-action이라는 새 리액트 프로젝트를 생성하자. 실행하기 전에 npm으로 create-react-app을 설치할 필요는 없다. npx 명령을 사용해 직접 리포지터리에서 앱을 실행할 수 있다.
npx create-react-app react-hooks-in-action
명령이 실행되는 데 약간 시간이 걸리며, react-hooks-in-action 폴더에 많은 파일이 생성돼야 한다. create-react-app 명령을 실행하면 컴퓨터가 npm을 사용해 파일을 설치한다. 얀(yarn)을 설치했다면 create-react-app이 얀을 사용하기 때문에 package-lock.json 대신 yarn.lock 파일이 생성된다.(npx는 npm을 설치할 때 함께 제공되는 편리한 명령이다. 저자인 캣 마친 미디엄의 'npx소개'라는 포스트 http://mng.bz/RX2j에서 npx의 배경에 대해 설명한다.
앱에 설치된 파일이 전부 다 필요하지는 않기 때문에 파일을 몇 개 빠르게 삭제하자. react-hooks-in-action 폴더 내부의 public 폴더에서 index.html을 제외한 모든 파일을 제거하라. src 폴더에서는 App.css, App.js, index.js만 남기고 모든 파일을 제거하라. 그림 2.4는 제거해야 할 파일을 강조해 보여준다.
그림 2.5는 puiblic과 src 폴더에 남아 있는 네 가지 중요한 파일을 보여준다. 이들은 이 책 전반에서 구축할 컴포넌트를 가져와 앱을 실행하기 위해 사용된다.
이 네 파일은 리액트 데모 페이지로 설정되어 있지 우리 예약 앱을 위한 것은 아니다. 따라서 약간 손볼 필요가 있다.
2.1.2.네 가지 핵심 파일 수정하기
4개의 작은 작업자 파일은 앱을 시작하고 실행시킨다. 각 파일은 다음과 같다.
/public/index.html : 앱을 포함하는 웹 페이지
/src/App.css : 페이지의 엘리먼트를 구성하는 스타일
/src/components/App.js : 다른 모든 컴포넌트를 포함할 루트 컴포넌트
/src/index.js : App 컴포넌트를 가져와 index.html 페이지에 렌더링 하는 파일
index.html
public 폴더 내부의 index.html 파일을 편집하라. create-react-app에 의해 생성된 많은 준비 코드를 삭제할 수 있다. 단, id가 root인 div 앨리먼트를 유지해야 한다. 이것은 앱의 컨테이너 앨리먼트다. 리액트는 App 컴포넌트를 이 div 앨리먼트에 랜더링한다. 이때 페이지 제목도 리스트 2.1에 나와 있는 대로 설정할 수 있다.
이제 웹 페이지에 필요한 모든 것을 갖췄다. App 컴포넌트는 div 안에 표시되며, 예약 가능한 항목, 예약, 사용자와 이들에 해당하는 페이지에 대한 컴포넌트는 모두 App 컴포넌트에 의해 관리된다.
App.css
이 책은 css를 알려주기 위한 책이 아니므로 스타일 코드에 초점을 맞추지 않는다. 때로 컴포넌트 이벤트와 함께 css가 쓰일 수도 있다. 그리고 이런 경우와 관련있는 스타일은 이 책에서 그때그때 강조해 표시할 것이다. 시간이 지나면서 스타일시트도 개발되므로, 관심이 있는 독자는 저장소를 살펴보라. 최초의 스타일은 Branch:0201-pages, 파일 : /src/App.css에서 찾을 수 있다. (프로젝트 전체에서 css가 어떻게 발전하는지에 대해서는 특별히 관심이 없지만 이 책의 자바스크립트 코드 변화를 따라가고는 싶다면, 완성된 프로젝트에서 App.css 파일을 가져와서 사용하면 된다.)
이 스타일은 각 페이지의 주요 컴포넌트를 배치하기 위해 css 격자 프로퍼티를 사용하며, css 변수를 일부 사용해 텍스트나 배경의 공통 색상을 정의한다.
App.js
App 컴포넌트는 우리 애플리케이션의 루트 컴포넌트다. 그림 2.6에서 볼 수 있듯이 이 컴포넌트는 링크와 사용자 피커 드롭다운이 포함된 헤더를 표시한다.
리스트 2.2 처럼 App 컴포넌트는 3개의 주요 페이지에 이르는 경로도 설정한다. 라우터는 URL을 페이지 컴포넌트와 매치시켜서 사용자에게 적절한 페이지를 표시한다. App.js 파일은 새로운 components 폴더로 옮겨졌다. 이 파일은 이번 장에서 생성할 여러 컴포넌트를 임포트한다.
주목해야 할 점은 목록 상단에 import React from "react" 구문이 없다는 것이다. 이전에는 JSX를 일반 자바스크립트로 변환할 때 리액트 컴포넌트가 작동하도록 하기 위해 이런 구문이 필요 했다. 그러나 create-react-app과 같은 리액트르 컴파일하는 도구는 최신 버전의 리액트에서 JSX를 변환할 때 import 문 없이도 작동할 수 있다. 이 변경사항에 대해 자세히 알고 싶은 독자는 리액트 블로그 (http://mng.bz/2ew8) 를 살펴보라.
이 앱은 리액트 라우터 버전 6을 사용해 세 페이지를 관리한다. 현재 리액트 라우터 6은 2023년 상반기 6.11이 최신 버전이다. 리액트 라우터를 다음과 같이 설치할 수 있다.
npm i history react-router-dom
리액트 라우터에 대한 자세한 정보를 깃허브 페이지 (https://github.com/ReactTraining/react-router) 에서 볼 수 있다. 우리는 Link 컴포넌트를 사용해 헤더에서 페이지 링크를 표시하고, Route 엘리먼트를 사용해 매치되는 URL에 따라 페이지 컴포넌트를 표시한다. 예를 들어, 사용자가 /bookings를 방문하면 BookingsPage 컴포넌트가 표시된다.
현재는 리액트 라우터에 대해 걱정할 필요가 없다. 리액트 라우터는 링크에 대해 어떤 페이지 컴포넌트를 표시할 지 관리할 뿐이다. 우리는 커스텀 훅을 사용해 매치되는 URL과 쿼리 문자열 파라미터(query string parameters)에 접근할 수 있게 해주는 리액트 라우터의 기능을 더 사용할 것이다.
App 컴포넌트는 세가지 페이지 컴포넌트(BookablesPage, BookingsPage, UserPage)와 UserPicker 컴포넌트도 임포트한다. 이 네가지 컴포넌트를 만들고자 한다.
Index.js
리액트에는 애플리케이션 시작점 역할을 할 자바스크립트 파일이 필요하다.src 폴더 안의 index.js 파일을 리스트 2.3처럼 변경하라. 이 파일은 App 컴포넌트를 가져와 index.html 파일에서 본 root div에 랜더링한다.
// src/index.js
import ReactDOM from "react-dom";
import App from "./components/App";
ReactDOM.render(
<App />,
document.getElementById("root")
);
그럼 이제 기존의 네 가지 파일을 수정했다! 여전이 App 컴포넌트가 가져올 페이지 컴포넌트들과 헤더에서 끌 UserPicker 드롭다운을 만들어야 한다. 먼저 앱에 표시할 예약 기능 항목과 사용자가 필요하다. 몇 가지 데이터를 넣어보자.
2.1.3. 애플리케이션에 필요한 데이터베이스 파일 추가하기
우리 애플리케이션에는 사용자, 예약 가능 항목, 예약 데이터 같은 몇 가지 데이터가 필요하다. 우선 static.json 이라는 JSON 파일에서 모든 데이터를 임포트 하는 것으로 시작하자. 목록에 표시할 몇가지 예약 가능한 항목과 사용자만 필요하므로 초기 데이터 파일은 그리 복잡하지 않다. 데이터를 리스트 2.4에서 볼 수 있다.(깃허브에서 리스트 2.4에 표시된 브랜치의 해당 파일을 복사해 올 수 있다.)
예약 가능 자원의 목록은 방금 본 객체의 배열이며, bookables 프로퍼티에 대입된다. 각 예약 가능 객체는 id, group, title, notes 프로퍼티를 포함한다. 이 책의 코드 리포지터리에 있는 데이터는 약간 더 긴 notes를 포함하지만 구조는 동일하다. 각 예약 가능 객체는 예약할 수 있는 날짜와 세션도 포함된다.
사용자는 다음과 같은 구조의 객체로 저장된다.
{
"id": 1,
"name": "Mark",
"img": "user1.png",
"title": "Envisioning Sculptor",
"notes": "With the company for 15 years, Mark has consistently sculpted innovative and compelling narratives for enforwarding the mutual ethos of all stakeholders."
},
앱에 기능을 추가하면서, 각 기능을 캡슐화하는 컴포넌트를 사용해 리액트 훅이 제공하는 기법을 사용하는 방법을 보여줄 것이다. 우리는 컴포넌트를 자신이 포함된 페이지와 연관된 폴더 안에 위치시킨다. 우리는 컴포넌트를 자신이 포함된 페이지와 연관된 폴더 안에 위치시킨다. components 폴더 안에 Bookables, Bookings, Users라는 폴더를 만들어라. 리스트 2.5처럼 골격 앱을 본따서 구조적으로 동일한 플레이스홀더 페이지를 세 가지 만들고, 각각을 BookablesPage, BookingsPage, UsersPage라고 부르자.
예약 앱에서 훅을 탐구하기 위한 모든 컴포넌트를 만들었다. create-react-app의 개발 서버를 시작해서 우리가 만든 앱이 작동하는 지 테스트해 보자.
npm start
모든 것이 잘 작동하면 세 페이지 사이를 이동할 수 있고, 각각이 자신이 어떤 페이지인지를 'Bookables!', 'Bookings!', 'Users!'라는 제목으로 표시해 준다. 데이터베이스에서 예약 가능 자원 목록을 표시하게 만들어서 Bookables 페이지를 달래보자.