리액트 기초, 동적 라우팅 과제 리뷰, useParams, Link, useEffect

라용·2022년 9월 16일
1

위코드 - 스터디로그

목록 보기
45/100
post-custom-banner

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

동적 라우팅을 직접 구현해보는 과제입니다. 페이지 구성과 구현해야 할 동작은 아래 이미지와 같습니다.

step 1

몬스터 카드가 나열된 리스트 형식의 Monsters 컴포넌트에서 해당 몬스터를 클릭 했을 때 /detail/[몬스터 id] 로 이동하게 합니다. (path parameter 사용) 이동한 몬스터 디테일 페이지에서 해당 url 의 path parameter 값을 추출하고 그 값을 활용해 해당 페이지의 api 를 호출해 화면에 그려줍니다.

라우터의 디테일 페이지 경로에 path parameter 를 추가하고,

<Routes>
    <Route path="/" element={<Monsters />} />
    <Route path="/detail/:id" element={<Monster />} />
</Routes>

Monsters 컴포넌트에서 useEffect 를 활용해 전체 데이터를 받고 CardList 컴포넌트에 props 로 전달합니다.

import React, { useState, useEffect } from "react";
import CardList from "./CardList/CardList";
import "./Monsters.scss";

function Monsters() {
  const [monsters, setMonsters] = useState([]);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then((res) => res.json())
      .then((res) => setMonsters(res)); // 데이터 받을 때 콘솔 찍어보기!
  }, []); // 의존성 배열을 비워서 한번만 데이터를 받아옴

  return (
    <div className="monsters">
      <h1>Assignment - Dynamic Routing</h1>
      <CardList monsters={monsters} />
    </div>
  );
}

export default Monsters;

CardList 컴포넌트에서 props 로 받은 데이터를 map 함수로 돌려 리스트 화면을 만듭니다. 이때 Link 를 사용해서 /detail/몬스터 ID] 로 이동하게 연결합니다.

import React from "react";
import { Link } from "react-router-dom";
import "./CardList.scss";

function CardList({ monsters }) {
  return (
    <div className="cardList">
      {monsters.map(({ id, name, email }) => {
        return (
          <Link key={id} to={`/detail/${id}`}>
            <div className="card">
              <img
                src={`https://robohash.org/${id}?set=set2&size=180x180`}
                alt="user avatar"
              />
              <h2>{name}</h2>
              <p>{email}</p>
            </div>
          </Link>
        );
      })}
    </div>
  );
}

export default CardList;

이렇게 이동한 Monster 페이지에서 useParams 훅을 사용해 해당 url 의 path parameter 값을 추출하고 (id 값) 그 값으로 해당 몬스터의 데이터를 받습니다. (fetch 의 api 주소의 패스파라미터 값에 id 값을 넣어서)

import React, { useState, useEffect } from "react";
import { useParams, Link } from "react-router-dom";
import Card from "../../components/Card/Card";
import "./Monster.scss";

function Monster() {
  const params = useParams(); // 현재 url 의 pathparameter 값 추출
  const userId = params.id; // 해당 id 값을 변수에 저장
  const [monster, setMonster] = useState({});

  useEffect(() => {
    fetch(`https://jsonplaceholder.typicode.com/users/${userId}`) // userId 값으로 데이터 호출
      .then((res) => res.json())
      .then((res) => setMonster(res));
  }, [userId]); // 이후 버튼 액션을 고려해 userId 를 의존성 배열에 넣음

  return (
    <article className="monster">
      <div className="btnWrapper">
        <button>Back to Monsters List</button>
      </div>
      <Card monster={monster} /> // 카드 이미지는 별도 컴포넌트 그려줌
      <div className="btnWrapper">
        <button>Previous</button>
        <button>Next</button>
      </div>
    </article>
  );
}

export default Monster;

step 2

디테일 페이지 monster 컴포넌트에서 Link 를 활용해 Back to Monster List 버튼을 누르면 "/" 경로로 이동하고, Preview, Next 버튼을 누르면 각각 이전 다음 몬스터에 대한 정보가 나오게 합니다. 기능 추가는 바로 위 코드에서 Link 만 추가하면 됩니다.

import React, { useState, useEffect } from "react";
import { useParams, Link } from "react-router-dom";
import Card from "../../components/Card/Card";
import "./Monster.scss";

function Monster() {
  const params = useParams();
  const userId = params.id;
  const [monster, setMonster] = useState({});

  useEffect(() => {
    fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
      .then((res) => res.json())
      .then((res) => setMonster(res));
  }, [userId]); // 이전 이후 버튼으로 이동시 userId 변화에 따라 데이터를 새로 받아옴

  return (
    <article className="monster">
      <div className="btnWrapper">
        <Link to={"/"}>
          <button>Back to Monsters List</button>
        </Link>
      </div>
      <Card monster={monster} />
      <div className="btnWrapper">
        <Link to={`/detail/${parseInt(userId) - 1}`}>
          <button>Previous</button>
        </Link>
        <Link to={`/detail/${parseInt(userId) + 1}`}>
          <button>Next</button>
        </Link>
      </div>
    </article>
  );
}

export default Monster;

막혔던 지점

  • 리스트 페이지에서 보이는 카드와 상세 페이지에서 보이는 카드는 같은 Card 컴포넌트를 사용했는데, 두가지 경로로 props를 받으니 두번째 상세 페이지에서 데이터를 받아 화면에 그려주지 못했습니다. 일단 리스트 페이지에서는 Card 컴포넌트를 사용하지 않고 해결했습니다. 생각해보면 카드컴포넌트를 재사용하는 것이 맞을텐데, 데이터를 받는 방식에 문제가 있을까 싶기도 합니다.

카드 컴포넌트 코드

import React from "react";
import "./Card.scss";

function Card({ monster }) {
  const { id, name, email } = monster;
  return (
    <div className="card">
      <img
        src={`https://robohash.org/${id}?set=set2&size=180x180`}
        alt="user avatar"
      />
      <h2>{name}</h2>
      <p>{email}</p>
    </div>
  );
}

export default Card;
  • 동적라우팅과 쿼리스트링을 동시에 공부하고 동적라우팅 과제를 하다보니 페이지 전과 후로 이동하는 개념이 쿼리스트링과 헷갈렸습니다. 현재 과제에서는 리소스 자체를 이동해야 하므로 Link 로 사용했고, 다음 페이지로 이동할 때 userId + 1 란 연산을 처리하는 데 userId 가 문자열이란 것을 인식하지 못해 해맸습니다. 이후 parseInt 메서드로 userId 를 숫자로 변환해 연산 문제를 해결했습니다.
profile
Today I Learned
post-custom-banner

0개의 댓글