그런데 생각해보면, 단일페이지로만 웹페이지를 구성하는 것에는 상당한 제약이 따르기 마련이다. 결국 다중페이지로 나갈 수 밖에 없는 부분이 있는데, 이를 가능하게 하는 기법을 'routing' 이라고 하며, 리액트에서 지원하는 'routin'기법을 위해서는 이를 위한 추가 라이브러리를 설치함으로 이러한 환경을 구축할 수 있다.
http://localhost3000/
http://localhost3000/page1
http://localhost3000/page2
http://localhost3000/page2/innnerpage1....
yarn add react-router-dom
리덕스를 사용하기 위해서는 2개의 라이브러리를 설치했다면, 라우터는 하나만 설치하면 된다. 이후 라우터를 관리하기 위한 별도의 폴더를 생성해준다.
src > shared 폴더 > Route.js 파일을 생성한다.
src > pages 폴더 > 다중페이지를 위한 컴포넌트들을 기록한다.
이후에, 라우터 사용을 위해서 3가지를 임포트한다.
import { BrowserRouter, Routes, Route } from "react-router-dom";
그 후, 임포트한 순서대로 태그를 생성해주면 된다.
import Home from "../pages/Home";
import page1 from "../pages/page1";
import page2 from "../pages/page2";
import page3 from "../pages/page3";
const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/page1" element={<page1/>}/>
<Route path="/page2" element={<page2/>}/>
<Route path="/page3" element={<page3/>}/>
</Routes>
</BrowserRouter>
)
}
export default Router
간단하다. 생성한 컴포넌트들(page1~3) <Route>
에 연결해주면된다. 이때 경로를 만들어주는 path 속성과 어떤 컴포넌트들이 연결되는지 알려주는 element 속성을 정의해주면 된다. 그리고 이때 컴포넌트를 연결해준다는 것은 해당 컴포넌트를 연결한다는 것이기 때문에 상단에 해당 컴포넌트들을 임포트 해주어야 한다.
import Router from './shared/Router'
function App() {
return <Router />
}
export default App
라우터에서 적용한 경로를 적용하기 위해서 App.js로 이동해서 라우터 컴포넌트(Router.js)를 연결해주고, App.js의 return으로 <Router />
태그를 기록해 주면 끝이다. 이후 yarn start로 프로젝트를 활성화 시켜주면, path="/"로 설정했던 화면이 처음으로 뜨게 될 것이다.
그런데 <Router />
를 받아올 때 중요한 것은 react-router-dom의 <Router />
가 아니라, 위에서 설정한 shared 폴더 > Route.js에서 작성한 <Router />
를 가져와야 한다.
기존의 HTML의 a과 같은 기능을 리액트 안에서 구현되게 하기 위해서는 Router Hook을 사용함으로 가능하다. 그러나 기억할 것은 HTML의 a태그는 새로고침과 같이 DOM을 새롭게 그린다는 점이고, 리액트는 새로고침없이 변경된 컴포넌트를 화면에 적용시킨다는 점이다. 즉 이동함의 기능은 동일하지만 동작하는 방식이 다른 것이다. 하나는 새로고침이 있고, 다른 하나는 새로고침이 없다.
import { useNavigate } from 'react-router-dom'
function Home() {
const navigate = useNavigate();
return (
<div>
<p><button onClick={()=> {navigate("/page1")}}>page1</button></p>
<p><button onClick={()=> {navigate("/page2")}}>page2</button></p>
<p><button onClick={()=> {navigate("/page3")}}>page3</button></p>
</div>
)}
export default Home
HOOK을 사용하기 위해 함수를 간단하게 변수로 만들어주고, 만들어준 변수를 버튼의 onclick={} 속성에 넣어주면 된다. 이때 navigate가 동작하는 원리는 shared 폴더 > Route.js에서 작성해준 각각의 페이지들의 path 속성값을 기록해주면 된다.
엄밀하게 말해서 Link는 훅은 아니지만, useNavigate과 유사한 기능을 수행하기에 여기에 함께 언급하였다.
import { Link } from 'react-router-dom'
...
return (
<div>
<p><Link to="/page1">page1</Link></p>
<p><Link to="/page2">page2</Link></p>
<p><Link to="/page3">page3</Link></p>
</div>
)
...
또 다른 사용법으로는 <Link>
가 있다. 외형상의 모습으로는 Link가 완벽하게 a태그와 닮았다. HTML에서도 다른 페이지로 이동하는 방법이 2가지였는데, <button onClick={}>
을 통해서 서버에서 설정한 경로로 이동하는 방법이 있었고, <a href="">
를 통해서 이동했던 방법이 있었던 것과 같이 말이다. 그래서일까? <Link>
에는 <a href="">
와 같이 하이퍼링크가 생겨났다는 파란색글자와 밑줄이 생성된 것을 볼 수 있다.
useLocation는 사용자에게 해당 페이지의 정보를 제공해주는 HOOK이다. Hash값, pathname 등을 알려주는 기능을 제공함으로 페이지 안에서 조건부 렌더링 등을 활용할 수 있다. 해당 내용은 심화과정에서 사용하기에 일단은 기억만 해두자.
예를 들어서 현재 TODO-lists를 통해서 해당 과정의 연습을 진행하고 있다. 그렇다면, todo 목록에 있는 각각의 아이템마다 상세페이지를 만들어 줄 수 있고, 이동할 수 있다는 말이기도 하다. 그런데 이를 만들 때, todo 목록에 있는 각각의 아이템 마다 페이지를 작성한다는 것은 개발자의 소양이 부족한 것이기에, 하나의 페이지를 활용하여 이를 재사용해보자.
이를 위해서 todoList를 별도의 파일로 먼저 분리시켰다.
// shared 폴더 > data.js
export const data = [
{ id:1, todo:"1 안녕"},
{ id:2, todo:"2 안녕"},
{ id:3, todo:"3 안녕"},
{ id:4, todo:"4 안녕"},
{ id:5, todo:"5 안녕"},
{ id:6, todo:"6 안녕"},
{ id:7, todo:"7 안녕"},
{ id:8, todo:"8 안녕"},
{ id:9, todo:"9 안녕"},
]
분리했다는 것은 사용할 부분에서 임포트 해줘야 한다는 말이 된다. 그후 data.map()을 사용하면 분리시키기 이전과 동일하게 사용할 수 있다.
import { data } from '../shared/data';
먼저 경로를 설정해주어야 하니, Router.js로 이동하자. 현재 우리는 data 가운데 id 값을 이용해서 이동할 것이기에, 경로 설정을 아래와 같이 기록했다. (사실 아래와 같이 입력하면 아무런 값이나 기록해도 이동은 되는데, 우리가 하고 싶은 것은 data와 페이지를 일치시켜주는 것이다.)
<Route path="/Works/:id" element={<Work/>}/>
경로를 설정했다면, 이제 링크를 만들어줄 파일로 돌아와서 기존의 <Link>
나 navigate
에 백틱을 사용해서 기록해주면, 각각의 페이지로 이동할 수 있는 조건을 만들어줄 수 있다.
...
{data.map(el=> {return (
<div key={el.id}>{el.id}
<Link to={`/Works/${el.id}`}>{el.todo}</Link></div>
<div key={el.id}>{el.id}
<button onClick={()=>navigate(`/Works/${el.id}`)}>{el.todo}</button></div>
)}
위의 하이퍼링크는 <Link>
로 생성한 페이지 이동에 대한 설정이고, 아래의 버튼은 navigate
을 통해서 생성한 경로이다. 그리고 리액트에서 Map() 매서드를 사용하려면 고유한 키값을 설정해야 하는데 해당 부분이 위의 코드에 기록된 key={el.id}
부분이다.
그리고 각각의 아이템들이 키값을 가지고 있다는 것은 개발자로 하여금 다양한 작업을 가능하게 하는데 해당 부분은 아래의 글에서 이어가도록 하겠다.
이를 수행하기 위해서 useParams()를 소개하고자 한다. useParams()는 라우터 라이브러리에서 제공하는 매서드로 URL에 포함되어 있는 key:value를 객체형식으로 반환해준다.
그래서 위에서 경로를 설정하면서 path="/Works/:id"
를 입력하고, 해당 내용으로 /Works/${el.id}
를 준 것이었다. 바로 useParams()를 사용하기 위함이었다.
const param = useParams();
console.log(param);
const findData = data.find((el)=>{
return el.id === Number(param.id)
});
console.log(findData);
url로 실려온 id 정보를 알 수 있다는 것은 data에서 해당 id를 포함한 객체의 값을 찾을 수 있다는 것을 의미한다. 그리고 이를 통해서 화면에 해당 정보를 반환하여 표시할 수 있다는 것을 의미하기도 한다.
return (
<>
<p><button onClick={() => { navigate("/Works") }}>Works</button></p>
<div>{findData.id}</div>
<div>{findData.todo}</div>
</>
)
이제 실전프로젝트를 위한 준비가 끝냈다. 바로 리덕스와 라우터를 이용해서 TODO-List와 상세페이지를 만들어보자.
Editor. EDWIN
data. 23/03/06