위코드에서 공부하며 정리한 내용입니다.
이전에 사용한 라우팅은 가장 기본적인 방식인 정적 라우팅이었습니다. 정적 라우팅은 라우터 컴포넌트에서 사용할 경로와 해당 경로로 접속 시 보여줄 컴포넌트를 미리 정의합니다.
<Route path="/" element={<Main />}/>
<Route path="/login" element={<Login />}/>
규모가 크고 복잡한 애플리케이션을 만든다면 이렇게 경로를 미리 설정하는 방식을 사용하기 어렵습니다. 예를 들어 블로그 포스팅 목록을 보여주는 리스트 페이지와 각 포스팅 내용을 보여주는 상세페이지가 있을 때 모든 페이지를 라우팅하는 것은 비효율적입니다.
<Route path="/" element={<Main />}/>
<Route path="/post/1" element={<첫번째 포스팅 />}/>
<Route path="/post/2" element={<두번째 포스팅 />}/>
..
<Route path="/post/100" element={<백번째 포스팅 />}/>
이런 문제를 해결하기 위해 동적 라우팅을 사용합니다. 동적 라우팅은 url 전체 형태를 미리 정의하지 않고 특정 규칙을 정의한 후 규칙에 부합한 url 이 있을 때 해당 엘리먼트가 보여줍니다.
path prop 에 :
기호를 사용해 경로/:문자열
형태로 설정하면 됩니다.
// 정적 라우팅
<Route path="/post/" element={<Detail />} />
// 동적 라우팅
<Route path="/post/: 문자열" element={<Detail />} />
<Route path="/post/: id" element={<Detail />} />
<Route path="/post/: value" element={<Detail />} />
여기서 id 와 같은 문자열을 path parameter 라고 합니다. path parameter 는 url 에 있는 값을 매개변수처럼 사용하는 것으로 이를 활용하면 페이지 별로 다른 UI 를 보여줄 수 있습니다.
useParams hook 을 사용하면 react-router-dom 에서 path parameter 를 가져와 활용할 수 있습니다. useParams hook 은 path parameter 의 값을 가져오고 state 처럼 path parameter 의 값이 바뀌면 컴포넌트를 리렌더링 해줍니다.
import { useParams } from "react-router-dom";
const params = useParams();
// 훅을 호출해 나온 반환값을 변수에 저장, 해당 값은 객체 형태
// 이때 객체 프로퍼티의 key 는 Route 에서 설정한 path parameter
// value 는 path parameter 에 실제 전달된 값
// /post/:id 이고 id 가 1이라면 { id : 1 } 값이 나오는 것
<h2>path parameter = {params.id}</h2>
// 이런 식으로 객체의 id 값을 넣어줄 수 있음
아래 API 주소로 데이터를 받아오고
https://reqres.in/api/users // 전체 유저 리스트 응답
https://reqres.in/api/users/[user의 ID] // 단일 유저 정보 응답
상세 페이지는 동적 라우팅을 통해 경로를 지정하고
<Route path="/" element={<List />} /> // 전체 리스트 페이지
<Route path="/detail/:id" element={<Detail />} /> // 유저 상세페이지, 패스 파라미터 id 로 지정
List 컴포넌트에서 유저 정보를 담을 state 를 선언하고 useEffect 와 fetch 를 이용해 데이터를 요청하고 응답 받습니다.
const [users, setUsers] = useState();
useEffect(() => {
fetch("")
.then((response)=>response.json())
.then((result)=>setUsers(result))
}, []);
console.log(users);
// 데이터가 들어왔는지, 어떤 형태인지 꼭 확인
// 확인 후 필요한 데이터 부분만 추출하게 아래처럼 변경
useEffect(() => {
fetch("")
.then((response)=>response.json())
.then((result)=>setUsers(result.data)) // result 값에서 data 에 해당하는 부분만 가져옴
}, []);
이제 users state 의 map 메서드를 사용해 ui 를 구현합니다. 리스트에서 특정 유저 클릭시 해당 유저 상세페이지로 이동하므로 링크 컴포넌트를 사용하고, 그 안의 to prop 에서 /detail/ 라우터로 이동하돼 /detail/ 뒤에 현재 클릭한 유저의 ID 를 path parameter 로 담아서 이동합니다.
{user.map(({ id, first_name, email, avatar })=>(
<Link key={id} to={`/detail/${id}`}>
<p>{first_name}</p>
<p>{email}</p>
<img alt="avatar" src={avatar} />
</Link>
))}
리스트 컴포넌트 전체 코드
// src/pages/List.js
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
const List = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('https://reqres.in/api/users')
.then((response) => response.json())
.then((result) => setUsers(result.data));
}, []);
return (
<section>
<nav>
{users.map(({ id, first_name, email, avatar }) => (
<Link key={id} to={`/detail/${id}`}>
<p>
<strong>{first_name}</strong>
</p>
<p>{email}</p>
<img alt="avatar" src={avatar} />
</Link>
))}
</nav>
</section>
);
};
export default List;
이제 유저 상세페이지 Detail 컴포넌트를 작업합니다. 유저 정보는 /api/users/[user의 ID] 를 통해서 가져오므로 해당 상세페이지의 유저 id 를 알고 있어야 합니다. 리스트 페이지에서 이동할 때 path parameter 에 user 의 id 를 포함해 라우팅 했으므로, 상세 페이지에서 useParams hook 을 이용해서 path parameter의 값을 가져온 뒤 이를 이용해 api 를 호출한 결과로 해당 UI 를 만듭니다.
우선 해당 훅을 호출하고
import { useParams } from "react-router-dom";
파람스 변수를 생성하고 유즈파람스 훅을 호출한 반환값을 할당하고
const params = useParams();
파람스 객체 안에서 id 프로퍼티의 value 를 userId 에 할당하고,
const userId = params.id
// 동적 라우팅 설정 시 path parameter 이름을 id 로 했으므로, id 로 접근
데이터를 담을 단일 유저 state 를 생성하고
cosnt [user, setUser] = useState([]);
useEffect 에 fetch 메소드를 사용해 데이터를 받아옵니다. 이때 위에서 정의한 userId 값을 사용합니다. 변수이므로 템플릿 리터럴을 이용해 입력합니다.
useEffect(()=>{
fetch(`https://reqres.in/api/users/${userId}`)
.then((response)=>response.json())
.then((result)=>setUser(result.data)) // result 미리 확인하고 필요한 부분만 담아주기, result 안의 data에 담긴값만 저장
}, [userId]);
여기서 의존성 배열에 userId 를 넣어줌으로서 userId 의 값이 변경될 때마다 새롭게 api 를 호출하고 user state 를 변경해 새로운 UI 를 그려주게 합니다. 의존성 배열을 비워두면, 컴포넌트가 처음 마운트 되었을 때만 동작하고 해당 컴포넌트 안에서 이전, 다음으로 넘어가는 버튼으로 userId 만 바뀌는 경우 데이터를 받아오지 않습니다.
이제 전달받은 내용으로 ui 를 그려주면 됩니다.
const { first_name, email, avatar } = user;
return (
<section className="detail">
<article>
<p>{first_name}</p>
<p>{email}</p>
<img alt="avatar" src={avatar} />
</article>
</section>
);
전체 코드 보기
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
const Detail = () => {
const params = useParams(); // 1
const userId = params.id; // 2
const [user, setUser] = useState({});
useEffect(() => {
fetch(`https://reqres.in/api/users/${userId}`) // 3
.then((response) => response.json())
.then((result) => setUser(result.data));
}, [userId]); // 4
const { first_name, email, avatar } = user;
return (
<section>
<article>
<p>
<strong>{first_name}</strong>
</p>
<p>{email}</p>
<img alt="avatar" src={avatar} />
</article>
</section>
);
};
export default Detail;