출처
해당 내용은 ZeroCho님의 리액트 무료 강좌 중 ReactRouter를 정리한 내용입니다.
코드 : https://github.com/ZeroCho/react-webgame/tree/master/react-router
포인트
- 웹사이트 개발시 역할
- React에서의 역할
- React 연동방법
- 그 외 정보는 공식 문서 참고
React Router 도입하기
설치
npm i react-router
- react-router 뼈대. 웹, 앱 둘다 사용 가능
npm i react-router-dom
- 웹에서 사용할 때 설치.
- 실제로는 react-router-dom만 사용
- react-router는 react-router-dom이 내부적으로 사용
Router 컴포넌트
Router 컴포넌트를 사용하면 각각 따로만들었던 컴포넌트를 하나의 페이지에서 동시에 사용 가능하다.
컴포넌트의 최상위를 <..Router>로 감싸줘야한다.
- Route : 페이지들 만듬
- path : 페이지 주소
- component : 보여줄 컴포넌트
const Games = () => {
return (
<BrowserRouter>
<div>
<Route path="/3_NumberBaseball" component={NumberBaseball}></Route>
<Route path="/5_leture" component={RSP}></Route>
<Route path="/6_lecture" component={Lotto}></Route>
</div>
</BrowserRouter>
);
}
Link와 BrowserRouter
Link 컴포넌트
실제로 페이지가 여러 개가 있는게 아니라 react-router만 알고있는 가상으로 만들어낸 페이지이다.
페이지 전환처럼 보이기 위해서 Link 컴포넌트를 사용한다.
<Link to='/number-baseball'>숫자야구</Link>
- Route로 정의한 path를 to에 넣어준다.
- html의 <a></a>와 동일한 기능이다.
링크 클릭시 'http://localhost:8080/lotto-generator'로 주소가 바뀐다.
배치
공통적인 layout은 `<Route>` 바깥으로 빼야한다.
<BrowserRouter>
<div>
<Link to='/number-baseball'>숫자야구</Link>
<Link to='/rock-scissors-paper'>가위바위보</Link>
<Link to='lotto-generator'>로또생성기</Link>
</div>
<div>
<Route path="/number-baseball" component={NumberBaseball} />
<Route path="/rock-scissors-paper" component={RSP} />
<Route path="/lotto-generator" component={Lotto} />
</div>
</BrowserRouter>
이렇게 배치할 경우
- 첫번째 div에 있는 부분은 페이지가 변경되도 바뀌지 않는다.
- 두번째 div에 있는 부분은 페이지가 변경되면 바뀐다.
해시 라우터, params, with Router
해시 라우터
해시 라우터 사용시 링크를 클릭하면 주소 중간에 #가 들어간다.
- ex) http://localhost:8080/#/number-baseball
장점 : 새로고침을 해도 화면이 보인다.
단점 : 서버가 해석하지 못해서 실무에서는 잘 사용되지않는다. 서버가 해석하지 못하면 검색 엔진에 뜨지 않는다.
- url에 #가 있으면 서버가 해석하지 못하고, 브라우저(프론트엔드)에서 처리한다.
- 검색엔진에 뜨게하려면 따로 서버에 세팅해야한다.
따라서 주소 이상해도 되고, 검색 엔진에 뜨지 않아도 되면 사용해도 된다.
dynamic Route matching1
Route가 계속 추가되면 추가해야할 만큼 일일히 쳐줘야한다. => 너무 방대해짐
route가 늘어나는 것을 하나로 합칠 수 있다.
효율적으로 Route들을 관리 가능
param
:는 param이라고 불린다.
/game/으로 시작하는 모든 Link는 <Route path="/game/:name >" 하나로 매칭할 수 있다.
ex)<Route path="/game/:name >"로 선언한 경우
- name부분은 동적으로 변경된다.
- <Link to="/game/...">
- ...은 아무거나
- /game/으로 시작하는 모든 Link가 이 Route에 매칭된다.
withRouter
<Route>내 정의된 component에서 this.props에는 자동으로 history, location, match가 생기며, 이는 <Route>가 자동으로 넣어주는 것이다.
<Route>와 연결이 안된 컴포넌트에서 history, location, match를 사용하고 싶으면 withRouter를 사용하면 된다.
import { withRouter } from 'react-router-dom';
class GameMatcher extends Component {}
export default withRouter(GameMatcher);
location, match, history
history
페이지 넘나든 내역을 가지고있다. 라우터 이동에 사용하며, goBack, goForward... 함수를 사용할 수 있다.
내부적으로 브라우저의 history를 사용한다.
기본 브라우저의 동작과 달리 실제로 페이지가 바뀌는게 아니기 때문에 히스토리를 관리할 수 있는게 존재한다.
match
동적 주소 라우팅시 params정보가 존재
match > params에 param 정보가 들어있다. 이걸로 분기처리를 할 수 있다.
location
주소(pathname), hash, search가 들어있다. 나중에 배움.
dynamic Route matching2
this.props.match.params.name를 이용해 분기 처리
하나의 라우터로 여러개의 페이지를 처리
class GameMatcher extends Component {
render() {
if(this.props.match.params.name === 'number-baseball') {
return <NumberBaseball />
} else if (this.props.match.params.name === 'rock-scissors-paper') {
return <RSP />
} else if (this.props.match.params.name === 'lottoe-generator') {
return <Lotto />
}
return (
<div>
일치하는 게임이 없습니다.
</div>
);
}
}
queryString과 URLSeatchParam
queryString
<Link to="">에 ?붙이고 key=value형식으로 정의하는 것
여러개일경우 &로 구분
주소로 데이터를 전달하는 가장 쉬운 방법
ex) <Link to='/game/number-baseball?query=10&hello=zerocho&bye=react'>
이 정보는 <Route>내 정의된 component에서 this.props.location.search에 쿼리스트링이 존재한다.
URLSeatchParam
queryString을 편하게 파싱하기 위해 사용한다.
브라우저에서 제공.
let urlSearchParams = new URLSearchParams(this.props.location.search.slice(1)); console.log(urlSearchParams.get('hello'));
render props, switch, exact
하위 컴포넌트로 props 넘기는 방법
첫 번째 방법 : 함수로 감싸기
<Route path="/game/:name" component={() => <GameMatcher props="12345" />} />
두 번째 방법 : render를 사용해 부모의 props 넘기기
<Route path="/game/:name" render={(props) => <GameMatcher props={props} />} />
=> props를 그대로 넘기는 게 목적이면 2번 방법을 추천.
동시에 라우트가 여러 개 뜨는 것을 막아보자.
switch
<Route>가 동적 라우팅과 정적 라우팅이 같이 있을 때 정적 라우팅으로 접근하면 화면이 어떻게 나올까?
ex) /game/exact로 접근시 두 라우터가 모두 렌더링된다.
<BrowserRouter>
<div>
<Route path="/game/:name" component={GameMatcher} />
<Route path="/game/exact" component={GameMatcher} />
</div>
</BrowserRouter>
이를 개선하기 위해 <Switch>를 사용하면 첫번째로 렌더링되는 것 하나만 렌더링한다.
<BrowserRouter>
<div>
<Switch>
<Route path="/game/:name" component={GameMatcher} />
<Route path="/game/exact" component={GameMatcher} />
</Switch>
</div>
</BrowserRouter>
exact
<Route>에 매칭되는 라우터가 여러 개일 때는 매칭되는 라우터가 모두 렌더링된다.
- 기본적으로 상위주소도 항상 일치한다고 생각한다.
ex) /game/number-baseball로 접근시 두 라우터가 모두 나온다. => /도 일치한다고 생각한다.
<BrowserRouter>
<div>
<Route path="/" component={GameMatcher} />
<Route path="/game/:name" component={GameMatcher} />
</div>
</BrowserRouter>
이런 경우, <Switch>로도 해결이 되지 않는다.
- 위의 경우 /가 더 위에 있기때문에 / 라우트만 렌더링된다.
- 원래 원하는 건 아래 라우트로 렌더링되는 것.
exact를 사용해 적힌 주소와 정확히 일치하는 라우트만 렌더링한다.
그래서 아래의 경우 /에 붙여서, 진짜 주소가 /인 경우만 렌더링되게한다.
<BrowserRouter>
<div>
<Switch>
<Route exact path="/" component={GameMatcher} />
<Route path="/game/:name" component={GameMatcher} />
</Switch>
</div>
</BrowserRouter>