리액트 기초, 동적 라우팅, useParams

라용·2022년 9월 15일
1

위코드 - 스터디로그

목록 보기
43/100

위코드에서 공부하며 정리한 내용입니다.

동적 라우팅 정의와 필요성

이전에 사용한 라우팅은 가장 기본적인 방식인 정적 라우팅이었습니다. 정적 라우팅은 라우터 컴포넌트에서 사용할 경로와 해당 경로로 접속 시 보여줄 컴포넌트를 미리 정의합니다.

<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 이 있을 때 해당 엘리먼트가 보여줍니다.

react-router-dom 활용한 동적 라우팅 구현, Path Parameter

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 를 보여줄 수 있습니다.

Path Parameter 활용

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;
profile
Today I Learned

0개의 댓글