쇼핑몰 프로젝트(Router)

코딩애플 님의 강의를 바탕으로한 글입니다:)

페이지 나누는 방법

라우팅: 페이지 나누기
= react-router-dom 라이브러리 이용

yarn add react-router-dom@5 또는 npn install react-router-dom@5

react-router-dom 초기셋팅법

  1. index.js 에 import {BrowserRouter} from 'react-router-dom'; 입력
    (import 부분의 경로에 ./ 가 아닌 그냥 이름이 적혀있다면 라이브러리 이름이라 생각하면 된다.)
<BrowserRouter></BrowserRouter><App/>을 감싸준다.


잠깐) HashRouter도 사용이 가능하고, 이 라우터는 라우팅을 안전하게 할 수 있게 도와준다. 이 라우터를 사용할 경우 사이트 주소 뒤에 #이 붙는데 # 뒤에 적는 것은 서버로 전달이 되지 않는다. (ex. localhost:3000/#/~~) 그래서 실수로 서버에게 요청하지 않게 하려면 안전하게 #을 붙여준다. 따라서, 리액트가 알아서 라우팅을 잘해준다.반면 BrowserRouter는 라우팅을 리액트가 아니라 서버에게 요청할 수도 있어서 위험함으로 잘못하면 있지도 않은 페이지를 서버에 요청을 해서 404 Page Not Found 이런 에러도 뜰 수 있다. 서버에서 서버 라우팅을 방지하는 API를 작성해야하고 이는 백엔드 개발자와 상의하면 된다.

페이지 나누기

  • 메인 페이지
  • 상품 상세 페이지
  1. import 하기 ({} 속 단어들은 HTML의 태그로 컴포넌트와 같다.)

  2. 아래와 같은 코드처럼 작성.
    ((참고)BrowserRouter태그는 index.js에서 App.js 자체를 감싸놨으므로 아래 코드와 같이 별도로 또 안묶어도 되긴 한다.)
    exact 의 경우 정확히 path의 경로대로 작성하여야 페이지가 나타나게 해준다.
    만약 없을시 / 와 /detail 에서 /는 공통적으로 들어가므로 메인페이지와 상세페이지가 동시에 뜨게 된다.

잠깐) path 경로대로 입력 시 컴포넌트를 띄울 수 있을 수 있으므로 가독성이 좋다.
일반적으로는 Route안에 HTML코드가 작성되지만 위에서 작성한 것과 같은 코드가 보기 좋다.

<Route path="/어쩌구" component={Card} ></Route> 
<Route path="/어쩌구"> <Card/> </Route> 

그리고 React-Router의 특징은 각각 페이지마다 다른 HTML파일을 보여주는 것이 아니다.
내부의 내용을 갈아치워서 보여주는 식으로 마치 페이지가 바뀌는 것처럼 보이는 것이다.
왜냐하면 파일을 보면 index.html 은 하나만 존재한다.

클릭시 페이지 변환

지금까지는 / 또는 /detail 을 직접 주소창에 입력하였지만, 버튼UI를 만들어 클릭시 페이지가 변환되게 설정할 수 있다.

앞서 react-router-dom을 import 해올때 {} 안에 태그들을 사용할 수 있었고, 여기서는

<Link></Link>

를 사용하면 된다.


네비게이션 바에 속해 있는 버튼을 Home과 Detail로 작문한 뒤,

<Link to ="경로"></Link>

로 설정해준다.

잠깐) 하지만 아래와 같은 오류가 나와서 잠깐 고민을 했다.

Invariant failed: You should not use <Link> outside a <Router>

해석을 간단하게 하면 내가 지금 사용한 링크 태그가 라우터 바깥에서 사용되고 있다는 것이다.
여기서 주의할 점은 라우터와 관련된 태그를 사용할 시 브라우저라우터 안에서 사용되어야 한다는 것이다. 나의 코드에서 네비게이션 태그가 브라우저 라우터 바깥에 존재하였고, 안으로 코드를 짜주니 잘 해결되었다.

  • BrowserRouter - history API를 사용해 URL과 UI를 동기화하는 라우터
  • Route - 컴포넌트의 속성에 설정된 URL과 현재 경로가 일치하면 해당하는 컴포넌트, 함수를 렌더링.
    - Link - 'a'태그와 비슷한 태그. to속성에 설정된 링크로 이동. 기록이 history스택에 저장.
  • Switch - 자식 컴포넌트 Route또는 Redirect중 매치되는 첫 번째 요소를 렌더. Switch를 사용하면 BrowserRouter만 사용할 때와 다르게 하나의 매칭되는 요소만 렌더링한다는 점을 보장. 사용하지 않을 경우 매칭되는 모두를 렌더링.

다른 방법

버튼을 클릭할때 모든 것에 링크를 걸어 이동하는 것은 비효율적이다.
예를 들어 코드 실행 중간에 페이지를 이동시키고 싶은 경우도 많고 그럴 경우엔 페이지 이동 함수를 사용하면 된다.

따라서 예시로 Detail 페이지에 뒤로가기 버튼을 만들어 보자.
useHistory라는 기능을 import 해오고 history 변수에 저장한다.
useHistory는 일종의 Hook 으로, 페이지 이동 내역, 유용한 함수가 저장되어 있다.

그리고 history.goBack(); 을 사용하면 뒤로가기 기능이 구현된다.

그 이외에도 많은 유용한 함수로 페이지 이동에 대한 여러 방법이 있다.
그 중 가장 많이 사용되는 함수가 .push('이동할경로') 이다. 말 그대로 특정 이동 경로로 이동시켜 주는 것이다.

Switch 컴포넌트

Switch 컴포넌트는 exact 를 써준 것과 같은 효과가 나타난다. 보통 메인페이지를 "/"로 설정하고 나머지 페이지들을 "/detail" 과 같은 형식으로 지정하는데, 만약 exact 또는 Switch을 사용하지 않는다면 "/"만을 인식하고 이를 포함한 모든 컴포넌트들이 전부 렌더링 될 수 있다.
따라서 Switch는 Route로 생성된 자식컴포넌트들 중에서 첫번째 Route를 렌더링 해줍니다.
참고: https://velog.io/@hoon_dev/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0Route-Link-Switch-5

잠깐)

상세페이지에 상품 정보 또한 shoes state 의 데이터에서 받아와보자.
App은 상위 컴포넌트로 Detail에 props 를 통해 데이터를 보내 바인딩해줄 수 있다.

중요!!!
애초에 shoes state 또한 Detail 컴포넌트에 만들어서 쓰면 되지 않을까?
데이터는 항상 위에서 아래로 흘러야 한다.
컴포넌트들마다 각자 필요한 데이터들을 만들어놓고 props 없이 사용하는 것은 좋지만, 만약 다른 컴포넌트들의 데이터가 필요할 경우 서로서로 데이터를 받아오려고 난리가 날 것이다.
그래서 가장 좋은 관습은 상위 컴포넌트가 중요 데이터를 다 가지고 있고 하위 컴포넌트에서 데이터를 받아가는 것이 가장 좋은 방법이다.

URL 파라미터

상세페이지 3개 만들기
일단 URL 주소부터 생각해보면 아래와 같이 상세피이지를 만들 수 있다.

/detail/0으로 접속하면 0번째 상품의 상세페이지
/detail/1으로 접속하면 1번째 상품의 상세페이지
/detail/2으로 접속하면 2번째 상품의 상세페이지

하지만, 상세피이지가 많으면 하드코딩 방식으로는 위와 같은 작성은 한계가 있다.
따라서 사용하는 것이
/detail/:id : URL 파라미터 문법으로, 아무문자나 다 받겠다는 URL 작명법
1. 콜론 뒤에 맘대로 작명
2. 여러개 사용 가능

그렇다면 실제로 0이면 0번째 상품, 1이면 1번째 상품이 나오게 데이터 바인딩이 필요하다.
따라서, 아래와 같은 개념으로 생각해보면 데이터 바인딩이 이루어질 것이다.

실제로 이를 구현시켜줄 수 있게 해주는 것이 있는데, 그것이 useParams()이다.

맨위에 import를 이용해 useParams를 가져오고 변수에 저장한다.

useParams()함수는 현재 URL에 적힌 모든 파라미터를 {파라미터1,파라미터2...} 과 같이 저장해준다.
그 것을 destructuring 문법을 이용해서 따로따로 변수로 저장한 것이다.
따라서 id라는 변수는 :id 자리에 있던 숫자를 의미 한다.
ex) /detail/1로 접속하면 id라는 변수는 1이 되고
/detail/100 으로 접속하면 id라는 변수는 100

잠깐)

/detail/0 은 0번째 상품인 A를 보여준다. 하지만, 메인페이지에서 가격 졍렬 같은 정렬 기능으로 0번째 상품이 B로 바뀌는 것과 같이 shoes state 배열의 순서가 바뀐다면? 하지만 0번째 상품은 항상 A가 보이게 해야 한다. 따라서 데이터바인딩을 할때 영구번호를 사용하면 된다. 상품정보가 담겨있는 Data.js를 보면 상품 객체마다 id가 존재한다.

이를 해결하기 위해 find()함수를 이용하면 된다.
이는 Array 안에서 원하는 자료를 찾고 싶을 때 사용한다.
쉽게 말해, 배열안에 영구적인 id번호가 [0,1,2] 그대로 상품이 정렬되있다면 좋겠지만, [2,0,1]과 같이 섞여 있어도 find()함수를 이용해 찾는 것이다.

  1. find()는 array 뒤에 붙일 수 있으며, 안에 콜백함수가 들어간다.
  2. 콜백함수 내의 파라미터 x는 array 안에 있던 하나하나의 데이터를 의미.
  3. return 오른쪽엔 조건식을 적을 수 있는데, 이게 참인 데이터만 새로운 변수에 저장.
  4. 조건식엔 현재 URL의 /:id에 적힌 값과 상품의 영구번호 (x.id)가 같은지 비교.

즉, 내가 현재 /detail/0 라고 검색하여서, A라는 상품을 보고싶다. 이때 useParams()를 통해 0이라는 파라미터가 id 변수에 저장된다. 그리고 그 값이 상품(x)의 영구번호인 0과동일하다면 return 해주는 것이다. 다시 한번 말하지만, 배열 속 0번째 상품이라고 상품 A가 뽑히는 것이 아니라, 영구번호가 0이여서 뽑히는 것이다. 정렬 기능으로 인해 0번째 상품은 A가 아닌 다른 상품이 올 수도 있는 것이다.

참고!

지금은 프론트엔드에서 모든 데이터를 다루고 있어서 어려운 + 반복문스러운 find() 함수를 사용한 것이지만
실제 개발할 땐 그냥 서버에 id : 0인 상품데이터를 Ajax로 요청하는 경우가 많다.
그럼 저렇게 find() 어쩌구를 쓰는게 아니라 ajax 요청하는 코드가 들어가있겠고
ajax 요청을 성공하면 {} 중괄호 안에 깔끔하게 상품데이터가 하나만 딱 들어올 것이다.

좋은 웹페이지 즐겨찾기