동적 라우팅 - Path parameter

React에는 자체적인 라우팅 기능이 없다.
그래서 react-router-dom의 BrowserRouter와 Route 컴포넌트를 이용한다.
그 외 부가 기능으로 Path, Query parameter도 사용할 수 있다.

지금까지 해온 라우팅 방법으로는 완전히 정해진 경우에 대해서만 경로를 표현할 수 있었다.

"/"         => <App />
"/users"    => <Users />
"/products" => <Products />

  • 위의 url을 살표보면 마지막에 특정 id값이 들어가고 그 값에따라
    서로 다른 상세 페이지 정보가 화면에 그려진다.
    id값에 따라 무수히 많은 url이 나타날 것이고, 점점 많아진다면
    모든 url에 대해 미리 경로의 형태와 갯수를 결정할 수 없게 된다.

  • 즉, URL에 들어갈 id를 변수처럼 다뤄야 할 필요성이 있다.

  • 이처럼 동적일 수 있는 경로에 대하여 라우팅 하는 것을 동적 라우팅 이라고 부른다.

  • 라우트 경로에 특정 값을 넣어 해당하는 페이지로 이동할 수 있게 하는것을 동적 라우팅 이라고 한다.

  • Path Parameter, Query Parameter를 사용한다.

  • 주로 상품 목록 페이지에서 상품 상세 페이지로 이동할 때 활용되는 기능이다.

특정 resource를 식별하고 싶을때 Path Param,
정렬이나 필터링을 해야할때 Query Param 을 쓴다고 한다.

Path Parameter

localhost:3000/product/2
localhost:3000/product/45
localhost:3000/product/125

2, 45, 125와 같이 라우트 경로 끝에 들어가는 각기 다른 id 값들을 저장하는 매개변수를 Path Parameter 라고 한다.


Path Parameter 는 Routes 컴포넌트에서 다음과 같이 정의된다.

<Router>
  <Switch>
    <Route exact path='/product/:id' component={productDetail} />
  </Switch>
</Router>

: 는 Path Parameter가 올 것임을 의미한다.
id는 해당 Path Parameter의 이름을 의미 한다. 변수 명을 짓듯, 임의의 이름을 지정할 수 있다.

history, match, location 객체

React Router 에서 제공하는 history, match, location 객체를 사용하여 위의 id 값을 가져올 수 있다.
**Routes.jsRoute 컴포넌트의 component 프로퍼티에 직접 연결되어 있는 하위 컴포넌트**는 history, location, match 3가지 객체를 props 를 통해 제공 받는다.





자식 컴포넌트에서 콘솔찍어보면,

const {history, location, match} = this.props;

console.log(history);
console.log(history);
console.log(history);

  • history 객체는 페이지 이동을 위한 여러 메서드들을 담고있다. (ex, push)
  • location 객체는 현재 url 경로에 관한 정보를 담고있다.
  • match 객체는 Path Parameter 에 관한 정보를 담고있다..

우리가 페이지 이동을 위해 사용하던 push 함수도 history 객체가 제공하는 기능이다.
(ex. onClick={ () ⇒ this.props.history.push('/main') } )

But, 직접 Route의 component 프로퍼티에 직접 연결이 되지 않은 자식 컴포넌트에서는 history, location, match 객체를 제공받지 못한다.

withRouter 함수를 이용한다. 인자로 컴포넌트를 받고, 해당 컴포넌트에 3가지 객체를 추가한 컴포넌트를 반환한다.

import { withRouter } from 'react-router-dom';

class 자식컴포넌트 extends React.Component {
	render() {
		console.log(this.props); // { history: {}, location:{}, match:{}, ... }
		return(
			...
		)
	}
}

export default withRouter(자식컴포넌트);


this.props.match.params.id

Path Parameter 로 명시해둔 값은 match 객체에 담긴다.
match 함수를 이용해 id 값을 가져온다.

class ProductDetail extends React.Component {
	...
	render() {
		console.log(this.props.match.params.id) // 1
		return (
			...
		);	
	}
}

그 id에 해당하는 정보를 fetch 해온다.

componentDidMount() {
	fetch(`${API}/${this.props.match.params.id}`)
	.then(res => res.json())
	.then(res => ...);

정리

  1. Card.js에서 Link나 onClick 을 이용해 경로로 보내준다. (경로 + id로)
    (ex. <Link to="/monsters/${this.props.id}"> )
    router에 직접 연결되지 않은 요소에서 보내주기때문에 withRouter 연결돼 있어야 함.

  2. Route 설정해준다.
    <Route exact path="/monsters/:id" component={MonsterDetail} />

  3. MonsterDetail.js(부모)에서 componentDidMount() {
    fetch("https://jsonplaceholder.typicode.com/users/" + this.props.match.params.id).then(res => ~생략~state저장.
    }

  4. 데이터를 저장한 state를 다시 card.js로 넘겨준다.
    그에 해당하는 카드, 정보가 render된다.

monsterDetail.js

import React, { Component } from "react";
import Card from "./Components/Card/Card";
import "./MonsterDetail.scss";

class MonsterDetail extends Component {
  state = {
    monster: {},
  };

  componentDidMount() {
    fetch(
      `https://jsonplaceholder.typicode.com/users/${this.props.match.params.productId}`
    )
      .then((res) => res.json())
      .then((res) => this.setState({ monster: res }));
  }

  render() {
    const { monster } = this.state;
    return monster.name ? (
      <div className="url-parameters">
        <div className="btn-wrapper">
          <button>Back to Monsters List</button>
        </div>
        <Card
          key={monster.id}
          id={monster.id}
          name={monster.name}
          email={monster.email}
        />
        <div className="btn-wrapper">
          <button>Previous</button>
          <button>Next</button>
        </div>
      </div>
    ) : null;
  }
}

export default MonsterDetail;

Card.js

import React, { Component } from "react";
import { withRouter } from "react-router-dom";
import "./Card.scss";

class Card extends Component {
  render() {
    return (
      <div
        className="card-container"
        onClick={() =>
          this.props.history.push(`/monsters/${this.props.id}`)
        }
      >
        <img
          src={`https://robohash.org/${this.props.id}?set=set2&size=180x180`}
          alt=""
        />
        <h2>{this.props.name}</h2>
        <p>{this.props.email}</p>
      </div>
    );
  }
}

export default withRouter(Card);

Route.js

import React, { Component } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";

import Pagination from "./Lectures/Pagination/Users";
import UrlParameters from "./Lectures/UrlParameters/Monsters";
import MonsterDetail from "./Lectures/UrlParameters/MonsterDetail";
import StateProps from "./Lectures/StateProps/StateProps";
import OnChange from "./Lectures/OnChange/OnChange";
import MenuTab from "./Lectures/MenuTab/MenuTab";
import Lifecycle from "./Lectures/LifeCycle";

export default class Routes extends Component {
  render() {
    return (
      <Router>
        <Switch>
          
          <Route exact path="/StateProps" component={StateProps} />
          <Route exact path="/onChange" component={OnChange} />
          <Route exact path="/MenuTab" component={MenuTab} />
          <Route exact path="/monsters" component={UrlParameters} />
          <Route
            exact
            path="/monsters/:productId"
            component={MonsterDetail}
          />
          <Route exact path="/Pagination" component={Pagination} />
          <Route exact path="/lifecycle" component={Lifecycle} />
        </Switch>
      </Router>
    );
  }
}

좋은 웹페이지 즐겨찾기