React Project7. Router 3 : URL 파라미터로 상세페이지 100개 만들기

Steve·2021년 5월 22일
0

URL 파라미터에 대하여
URL 뒤에 뭘 적든간에 이리로 안내해주세요~ 라는 뜻
상품 3개에 해당하는 각각의 상세페이지를 만들어주고 싶다면..

일단 컴포넌트에 실제 상품명 데이터바인딩 ㄱㄱ

상위컴포넌트의 state를 하위컴포넌트에 보내려면 2가지 스텝 앞에서 배웠었다.
1) state작명={state명}
2) 하위 컴포넌트의 파라미터로 props 써주고 데이터바인딩 할때마다 props.붙여줌

기술질문

Q. 애초에 shoes와 같은 state를 하위컴포넌트에 넣으면 되는거 아닌가? 그럼 props 안써도 되자나?
A. 데이터는 항상 위에서 아래로 흘러야한다.
만약 하위컴포넌트에 넣는다고 하면, 최상위 컴포넌트의 다른 하위컴포넌트에서 state가 필요할 경우 골치아파짐.
결론) 상위 컴포넌트가 중요 데이터를 모두 갖고 있게 한다.
하위컴퍼넌트는 데이터를 항상 props로 받아서 사용한다.
state 만들 땐 state를 필요로하는 컴포넌트들 중 가장 최상위 컴포넌트에 보관!!

상세페이지 3개 만들기

/detail/0 -> 0번째 상품의 상세페이지
/detail/1 -> 1번째 상품의 상세페이지
/detail/2 -> 2번째 상품의 상세페이지

(App.js 파일)

function App(){
  return (
    <div>
      <나머지HTML/>
        <Route path="/detail/0">
          <Detail shoes={shoes}/>
        </Route>
        <Route path="/detail/1">
          <Detail shoes={shoes}/>
        </Route>
        <Route path="/detail/2">
          <Detail shoes={shoes}/>
        </Route>
    </div>
  )
}

위 반복문을 아래처럼 줄여준다.

(App.js 파일)

function App(){
  return (
    <div>
      <Route exact path="/">
          <div className="background">
            <h1>20% Season Off</h1>
            <div>
              This is a simple hero unit, a simple jumbotron-style component for
              calling extra attention to featured content or information.
            </div>
            <button className="learn_button">Learn more</button>
          </div>

          <div className="container">
            <div className="row">
              {/* map을 쓸때, jsx안에서  js문법을 쓸때 중괄호좀 써라  */}
              {shoes.map((shoe, i) => (
                <Card key={i} shoes={shoes[i]} i={i} />
              ))}
            </div>
          </div>

          <button
            onClick={() => {
              let list = shoes.sort((a, b) => a.price - b.price);
              setShoes([...list]);
              // state의 값을 다시 재정비해줘야함. 그 역할 setShoes
              // 유레카 이걸 안해서 바로바로 버튼을 눌러도 적용이 안됐었음
            }}
          >
            가격순 정렬하기!!
          </button>
        </Route>
        <Route path="/detail/:id">
          <Detail shoes={shoes}/>
        </Route>
    </div>
  )
}

💢왜 정렬버튼 제대로 눌렀는데 바로바로 적용이 안되고 새로고침을 해야 작동하나?💢 이런 이상한 에러가 있었음

해결 : setShoes로 state값을 재정비해주는 과정을 안거치고 그냥 onClick버튼을 냅다 눌러버림. 안돼서 빡침!!

let list = shoes.sort((a, b) => a.price - b.price);
            setShoes([...list]); // 왜 깊은 복사를 하는지 설명할 수 있어야할듯.
리액트 대관습. state를 그대로 가져다 쓰지 않는다.
깊은 복사를 하면 또다른 메모리가 생겨 기존의 것과 독립적으로 활동함.

:id 의 의미
id 자리에 아무 문자나 입력하면 Detail 컴포넌트를 보여달라~ 라는 뜻.
/detail/123123 이처럼 아무거나 입력해도 Detail컴포넌트 보여줌

/detail/:id/name 과같이 2개이상 추가 가능함.

문제는 각각 URL 접속시 상품명을 다르게 보여줘야 하는데..

:id자리에 있던 숫자만 불러올수 있으면된다
이때는 useParams()라는 훅을 사용한다

(Detail.js 파일)

import React from 'react';

function Detail(props){
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-6">
          <img src="https://codingapple1.github.io/shop/shoes1.jpg" width="100%" />
        </div>
        <div className="col-md-6 mt-4">
          <h4 className="pt-5">{props.shoes[:id자리에 있던숫자].title}</h4>
          <p>{props.shoes[:id자리에 있던숫자].content}</p>
          <p>{props.shoes[:id자리에 있던숫자].price}</p>
          <button className="btn btn-danger">주문하기</button> 
        </div>
      </div>
  </div>  
  )
};

export default Detail 

▲ 그래서 :id 자리에 입력한 숫자를 저기에 집어넣고 싶습니다.
useParams() 라는 훅을 사용.

(Detail.js 파일)

import React from 'react';
import { useHistory, **useParams** } from 'react-router-dom';

function Detail(props){

  let { id } = useParams(); // 1번 코드
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-6">
          <img src="https://codingapple1.github.io/shop/shoes1.jpg" width="100%" />
        </div>
        <div className="col-md-6 mt-4">
          <h4 className="pt-5">{props.shoes[:id자리에 있던숫자].title}</h4>
          <p>{props.shoes[:id자리에 있던숫자].content}</p>
          <p>{props.shoes[:id자리에 있던숫자].price}</p>
          <button className="btn btn-danger">주문하기</button> 
        </div>
      </div>
  </div>  
  )
};

export default Detail 

1번코드의 id라는 변수는 :id 자리에 있던 숫자를 의미함
/detail/1로 접속하면 id라는 변수는 1이 되고
/detail/100 으로 접속하면 id라는 변수는 이제 100이 되됨

리액트에서 상세페이지 만드는 패턴

(Detail.js 파일)


import React from 'react';
import { useHistory, useParams } from 'react-router-dom';

function Detail(props){

  let { id } = useParams();
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-6">
          <img src="https://codingapple1.github.io/shop/shoes1.jpg" width="100%" />
        </div>
        <div className="col-md-6 mt-4">
          <h4 className="pt-5">{props.shoes[id].title}</h4>
          <p>{props.shoes[id].content}</p>
          <p>{props.shoes[id].price}</p>
          <button className="btn btn-danger">주문하기</button> 
        </div>
      </div>
  </div>  
  )
};

export default Detail

자료의 순서가 변경되면 상세페이지도 고장나는 문제 어떻게 해결할까?

그니까 지금 /detail/0으로 접속하면 0번째 상품이 보입니다. (Black 이라는 상품)

근데 메인페이지나 다른 페이지에서 상품 순서를 가격순으로 변경하는 기능을 만들어버렸다고 가정합시다.

그래서 shoes라는 상품 데이터가 가격 순으로 변경이 되었습니다.

그럼 0번째 상품이 Red Knit 이라는 상품으로 변합니다.

그럼 이제 /detail/0으로 접속하면 0번째 상품, 즉 Red Knit 이라는 상품이 보이게 되는 것입니다.

이런 일종의 버그같은 것을 어떻게 해결하면 좋을까?

  (Detail.js 파일)

import React from 'react';
import { useHistory, useParams } from 'react-router-dom';

function Detail(props){

  let { id } = useParams();
  let 찾은상품 = props.shoes.find((상품) => 상품.id == id);
// 조건에 부합하는 배열의 한 요소(여기서는 객체)를 리턴한다.
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-6">
          <img src="https://codingapple1.github.io/shop/shoes1.jpg" width="100%" />
        </div>
        <div className="col-md-6 mt-4">
          <h4 className="pt-5">{찾은상품.title}</h4>
          <p>{찾은상품.content}</p>
          <p>{찾은상품.price}</p>
          <button className="btn btn-danger">주문하기</button> 
        </div>
      </div>
  </div>  
  )
};

export default Detail 

find() 라는 ES6 신문법이 있습니다. Array 안에서 원하는 자료를 찾고싶을 때 사용합니다.

filter() 함수, 그냥 반복문 이런거 쓰셔도 전혀상관없습니다.

  1. find()는 array 뒤에 붙일 수 있으며, 안에 콜백함수가 들어갑니다.

  2. 콜백함수 내의 파라미터는 (상품이라고 적은거) array 안에 있던 하나하나의 데이터를 의미합니다.

  3. arrow function 조건식에 참인 데이터만 새로운 변수에 저장해줌.

  4. 조건식엔 그리고 그걸 현재 URL의 /:id에 적힌 값과 상품의 영구번호 (상품.id)가 같은지 비교하고 있는 겁니다.

그래서 /detail/0으로 접속시 찾은상품이라는 변수를 출력해보시면 아마 영구번호가 id : 0인 데이터가 나올겁니다.

/detail/1로 접속시 찾은상품이라는 변수는 영구번호가 id : 1인 데이터일겁니다.

그래서 찾은상품이라는 변수를 이용해서 상품명, 가격 HTML 부분에 데이터바인딩을 했을 뿐입니다.

성공!

지금은 프론트엔드에서 모든 데이터를 다루고 있어서 어려운 + 반복문스러운 find() 함수를 사용한 것이지만

실제 개발할 땐 그냥 서버에 id : 0인 상품데이터를 Ajax로 요청하는 경우가 많을겁니다.

그럼 저렇게 find() 어쩌구를 쓰는게 아니라 ajax 요청하는 코드가 들어가있겠고

ajax 요청을 성공하면 {} 중괄호 안에 깔끔하게 상품데이터가 하나만 딱 들어옴
ajax에 대해선 다음에 더 자세히 알아보자!

profile
Front-Dev

0개의 댓글