Notion의 Theory & Technique : React.js 페이지 바로가기
라우트의 경로에 특정 값을 대입하는 방법은 두 가지가 존재한다: 1. params
2. query
를 사용하는 것이다. 먼저, route로 설정한 컴포넌트는 3가지의 props를 전달받게 된다:
history
의 push
, replace
를 통해 다른 경로로 이동하거나 앞 뒤 페이지로 전환할 수 있다.location
객체는 현재 경로에 대한 정보를 지니고 있고, URL 쿼리(about?foo=bar
형식)의 정보도 가지고 있다. 참고로 location.pathname
는 url의 path(즉 /foo 등의 경로)을 가리키고, location.search
는 queryString의 정보를 가리킨다. 여기서 queryString이란 path이후의 ? 다음에 나오는 id=1
과 같은 것을 의미한다.match
객체에는 어떤 route에 연결이 되었는지와 관련된 정보가 존재하고, param
(/about/:name
형식) 정보가 존재한다.URL 쿼리의 경우 컴포넌트 내에서 동적인 사용이 가능하고, params의 경우 사용 전에 반드시 route에서 지정을 해 주어야 한다.
먼저, params를 사용할 때는 다음과 같이 코드를 작성하면 된다:
src/shared/App.js
class App extends Component{ render(){ return( <React.Fragment> // 정확한 경로로 root-path+/가 호출되었을 때 Home 컴포넌트를 노출한다. <Route exact path="/" component={ Home }/> // /about 경로로 접근할 시 About 컴포넌트를 노출한다. <Route path="/about" component={ About } /> // /About 컴포넌트에 name이라는 params를 사용하는 것으로 설정한다. <Route path="/about/:name" component={ About }> </React.Fragment> ); } } export default App;
위와 같이 URL의 params를 설정 할 때는 :foo
의 형식으로 설정한다. 이렇게 선언해 주면 해당 컴포넌트에 foo라는 params가 생성된다.
src/pages/About.js
const About = ({ match }) => { return( <React.Fragment> // About Component의 본문 영역에 // name이라는 이름으로 선언한 params를 대입하여 확인한다. <h2>About { match.params.name }</h2> </React.Fragment> ); } export default About;
About 컴포넌트에 name이라는 params를 생성하였고, 그 값은 위와 같이 해당 컴포넌트에 선언해 확인 할 수 있다.
하지만 위와 같이 코드를 작성하는 경우는 동일한 컴포넌트에 대입한 path가 복수이므로 /about 경로를 통해 접근하는 경우 /about에 할당된 컴포넌트인 { About }과 /about/:name에 할당된 컴포넌트 { About }이 한 화면에 노출되게 된다. 이러한 경우, 상기한 바와 같이 Route 선언문에 <Route exact path="/about" component={ About }/>
과 같이 선언하여 정확한 경로로 접근하는 경우에만 해당 Component를 매치하거나, <Switch</Switch>
태그 내부에 해당 path를 매핑하여 매칭되는 첫 번째 Route만 노출하게 할 수 있다. 문법은 다음과 같다:
import React, { Component } from 'react'; import { Route, Switch } from 'react-router-dom'; import { Home, About } from 'pages'; class App extends Component{ render(){ return( // 복수의 elements를 반환하도록 작성하였으므로 wrapping해 준다. <React.Fragment> // Root path/의 경로로 정확하게 접근하는 경우 Home 컴포넌트를 노출한다. <Route exact path="/" component={ Home } /> // Switch 태그 내부에 매핑 route 작성을 시작한다. // 따라서 이 태그 내부에 위치하는 route들은 // 매칭되는 첫 번째 컴포넌트만 노출하고, // 하위의 결과는 화면단에 노출하지 않는다. <Switch> // 따라서 /about/:name \의 경로로 접근하는 경우 첫 번째 route만 매칭한다. <Route path="/about/:name" component={ About } /> <Route path="/about" component={ About } /> </Switch> </React.Fragement> ); } } export default App;
위의 태그 내부에 route를 매핑할 때에 주의할 점은, 중복되는 path를 통해 접근하는 경우 선언 순서에 따라 결과 값이 달라진다는 것이다. 사용자가 /about/:name의 경로로 접근하는 경우 name params를 가진 경로를 노출하고자 한다면 반드시 params가 포함된 Route 매핑을 전위에 작성해야 할 것이며, 이 params가 포함된 경로로 접근할 때에도 해당 /'about 통합 페이지를 노출하고자 한다면 /about 경로로 매핑된 route를 전위에 선언해 주면 된다.
react-router v3에는 URL Query를 해석하여 객체로 생성하는 기능을 자체적으로 포함하고 있었으나, 쿼리를 parsing하는 여러가지 방식을 개발자가 직접 선택하여 사용할 수 있도록 현재의 버전은 더 이상 이를 내장하지 않는다. 따라서, URL Query를 해석하기 위해서는 별도의 방법을 사용하여야 한다. URL Query의 해석 및 객체 생성은 자체적으로 구현할 수도 있지만, 라이브러리를 사용하는 편이 훨씬 간편하다. 이는 아래의 커맨드를 통해 설치할 수 있다.
$ yarn add query-string
위와 같이 설치가 완료되면, 다음과 같이 코드를 작성한다.
src/pages.About.js
import React from 'react'; // queryString 라이브러리를 import한다. import queryString from 'query-string'; // queryString 라이브러리의 var location, var match를 인자로 받는다. const About = ({ location, match }) => { // const로 query를 선언하며, // 이는 queryString을 var location과 any search로 parsing한다(쪼갠다). const query = queryString.parse(location.search); // parsing한 값을 확인하기 위해 console에 log를 출력하도록 한다. console.log(query); return( <React.Fragment> <h2> About { match.params.name } </h2> </React.Fragement> ); }; export default About;
RESULT
이제 /about/cheetah?detail=true
경로로 접속하고 F12를 눌러 개발자 모드에서 콘솔을 열어보자. detail의 값인 true가 객체에 생성된 것을 확인 할 수 있다.
또한 이 값에 따른 Conditional Rendering도 가능하다. 문법은 다음과 같다:
import React from 'react'; // queryString을 해석해서 객체로 만들기 위한 라이브러리를 import한다. import queryString from 'query-string'; // 인자로 var location, any match를 받으며 화살표 함수에 대입한다. const About =({ location, match })=> { //queryString, 즉 path 이후의 값을 파싱하여 const query에 대입한다. // 참고로 parsing된 내용은 String의 형태로 반환된다. const query = queryString.parse(location.search); // query.detail이 string 형태로 받은 반환 값이 정확히 'true'라면 // const detail에 query.detail 값을 대입한다. const detail = query.detail === 'true'; // div에 대입할 스타일을 const로 선언한다. const bodyStyle= { margin: '12px', fontSize: '1.1rem', textAlign: 'center', backgroundColor: 'magenta', paddingTop: '2px', paddingBottom: '16px', color: 'white' } // console에 const query에 대입된 값을 출력한다. (확인용) console.log(query); return( <div style= { bodyStyle }> // params로 넘겨받은 name을 본문에 포함시켜 출력한다. <h2>About { match.params.name }</h2> // const detail이 true일 때와 false일 때를 분리하여 // Ternary operator로 Conditional Rendering한다. { detail ? 'detail is true!' : 'detail is false!' } </div> ); } export default App;
단, URL Query를 사용할 때는 받아오는 값들이 전부 string(문자열)임에 주목하여야 한다. 비교 연산이 필요하거나 이에 따른 조건부 렌더링 구문 등이 필요한 경우에는 반드시 문자열 형태 또는, 알맞는 형변환을 거친 후 비교하여야 한다.
앱 내에서 다른 route로 이동할 때는, 페이지를 새로 고침 하는 <a href=""></a>
가 아닌 리액트 라우터 내 Link 컴포넌트를 사용한다. 이 <Link></Link>
컴포넌트를 사용하면 페이지를 새로 불러오는 것을 막고, 원하는 route로 화면 전환을 할 수 있다.
예시 코드는 다음과 같다:
src/components/Menu.js
import React from 'react'; import { Link } from 'react-router-dom'; // 일반적인 Component 선언법과 동일하게 화살표 함수 내부에 작성 하고, export한다. const Menu=()=>{ return( <React.Fragment> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/about">About</Link></li> <li><Link to="/about/foo?detail=true">About Foo True</Link></li> </ul> <hr/> </React.Fragment> ); }; export default Menu;
src/shared/App.js
... // *모든 path에 Menu를 노출하기 위하여 Switch 또는 exact를 사용하지 않음* <Route path="/" component={ Menu } /> ...
RESULT
이 컴포넌트에 전달되는 props들은 컴포넌트 내부의 DOM에도 전달되므로, 일반 DOM 구성 요소에 설정하는 것 처럼 className, style 또는 onClick등의 이벤트 전달이 가능하다.
NavLink는 Link와 유사하나, 설정한 URL이 활성화되면 특정 스타일 또는 클래스를 지정할 수 있다.
src/components/Menu.js
import React from 'react'; import { NavLink } from 'react-router-dom'; const Menu=()=>{ const activeStyle = { color: 'blue', fontSize: '1.2rem', fontStyle: 'italic' } return( <React.Fragment> <ul> <li><NavLink exact to="/" activeStyle={ activeStyle }>Home</NavLink></li> <li><NavLink exact to="/about" activeStyle={ activeStyle }>About :: Exact</NavLink></li> <li><NavLink to="/about/foo" activeStyle={ activeStyle }>About Foo:true</NavLink></li> </ul> </React.Fragment> ); }; export default Menu;
RESULT