SPA(Single Page Application)는 말 그대로 한 개의 페이지로 이루어진 어플리케이션이라는 의미이다. 전통적인 웹 페이지는 다음과 같이 구성되어 있다.
기존에는 사용자가 다른 페이지로 이동할 때마다 새로운 html을 받아오고, 페이지를 로딩할 때 마다 서버에 리소스를 전달 받아 해석한 뒤 화면에 보여주었다. 요즘은 웹에서 제공되는 정보가 많기 때문에 새로운 화면을 보여 주어야 할 때마다 서버측에서 모든 뷰를 준비한다면 성능상의 문제가 발생할 수 있다. 이러한 방식은 비효율적이다.
그래서 React 같은 라이브러리 혹은 프레임워크를 사용하여 View렌더링을 사용자의 브라우저가 담당하도록 하고, 우선 어플리케이션을 브라우저에 불러와서 실행시킨 후에 사용자와의 인터랙션이 발생하면 필요한 부분만 자바스크립트를 사용하여 업데이트 해준다. 만약 새로운 데이터가 필요하다면 서버 API를 호출하여 필요한 데이터만 새로 불어와 어플리케이션에서 사용할 수 있다.
싱글페이지라고 화면이 한 종류일까? 절대 그렇지 않다. 요즘은 웹에서 제공되는 정보의 양도 어마어마하고 페이지 수도 엄청나게 많다.
다른 조수에 다른 화면을 보여주는 것을 Routing이라고 한다. 리액트 자체에 이 기능이 내장되어 있지는 않다. 그 대신 브라우저의 API를 직접 사용하여 관리학나, 라이브러리를 사용하여 쉽게 구현할 수 있다.
하지만 이러한 SPA에도 단점이 있다.
앱의 규모가 커지면 자바스크립트 파일이 너무 커진다는 것이다. 페이지 로딩시 사용자가 실제로 방문하지 않을 수 도있는 페이지의 스크립트도 불러오기 때문이다. 하지만 코드 스플리팅(code splitting)을 트래픽과 로딩 속도를 개선 할 수 있다.
Create React App (CRA)에는 Routing을 위한 로직이 들어있지 않기 때문에 가장 인기 있는 routing solution인 react-router를 추가해서 routing을 구현한다.
npm install react-router-dom --save
react-router를 사용하기 위해서는 CRA로 만든 앱에 src/App.js에서 대신에 routing을 설정한 컴포넌트로 대치해야 한다.
ReactDOM.render(<Routes />, document.getElementById('root'));
import React from 'react'; import { BrowserRouter as Router, Route, Switch, } from 'react-router-dom'; import Home from './Pages/Home'; import Signup from './Pages/Signup'; class Routes extends React.Component { render() { return ( <Router> <Switch> <Route exact path="/" component={Home} /> <Route exact path="/signup" component={Signup} /> </Switch> </Router> ) } } export default Routes;
Route이동하는 방법은 두가지가 있다.
Routes에서 설정한 path로 이동하도록 구현하려면 Link 컴포넌트를 사용한다.
import React from 'react'; import { Link } from 'react-router-dom'; class Login extends React.Component { render() { return ( <div> <Link to="/signup">회원가입</Link> </div> ) } } export default Login;
하지만 react-router-dom에서 제공하는 Link 컴포넌트는 dom 에서 <a>
로 변환되므로, a태그를 사용하고 싶지 않다면 Link를 사용하지 않아도 된다.
예를들어, <button>
컴포넌트를 만들어 놓았을 경우 Link 컴포넌트를 사용하면 <a>
태그 처럼 밑줄이 생긴다.
Link를 사용하지 않고, 요소에 onClick 이벤트를 달아서 이동하고 싶은 곳으로 넘기는 방법도 있다.
다음 로직을 보면 goTOSignup이라는 event handler에서 구현한 것처럼 this.props의 history에 접근해서 이동할 수 있다.
여기서 받은 history의 push메서드에 이동할 path를 인자로 넘겨주면, 해당 Routes로 이동할 수 있다.
import React from 'react'; import { withRouter } from 'react-router-dom' class Login extends React.Component { goToSignup() { this.props.history.push('/signup'); } render() { return ( <div> <div class="btn signup-btn" onClick={this.goToSignup.bind(this)} 회원가입 </div> </div> ) } } export default withRouter(Login);
위 컴포넌트에서 props에 route정보(history)를 받으려면 export하는 class에 withRouter로 감싸주어야 한다. 이렇게 withRouter같이 해당 컴포넌트를 감싸주는 것을 higher-order component(이하 HOC)라고 한다.
HOC는 react 고급 기능이다. 기능이라기보다는 컴포넌트의 공통부분을 구현하는 패턴이라고 생각하면 된다. 간단히 설명하면 HOC는 함수이다. 컴포넌트를 인자로 받고, 컴포넌트를 return하는 함수이다.