1. SPA

(1) SPA (Single Page Application) - 페이지가 한 개인 애플리케이션(html파일이 한 개)

(2) Westagram-frontend : login.html, main.html - 페이지 수만큼 html 파일이 존재(MPA)

(3) 리액트 프로젝트에서 .html 파일의 개수는? 1개 >>> SPA(Single Page Application)

(4) 한 개의 웹페이지(html) 안에서 여러 개의 페이지를 보여주는 방법은? >>> Routing

 

 

2. Routing

(1) 라우팅(Routing)이란 다른 경로(url 주소)에 따라 다른 View(화면)를 보여주는 것 입니다.(웹에서 경로라는건 url을 뜻한다)

(2) 리액트 자체에는 이러한 기능이 내장되어있지 않습니다.

(3) 리액트가 Framework 가 아닌 Library 로 분류되는 이유입니다.

(4) 그렇기 때문에 서드파티 라이브러리(Third-party Library)인 React-router를 사용합니다.

(React-router는 리액트의 라우팅 기능을 위해 가장 많이 사용되는 라이브러리이다.)

 

 

 

 

3. React Router

Create React App(CRA)에 특별히 routing을 위한 로직이 들어있지 않기 때문에, 가장 인기 있는 routing solution인 react-router 를 추가해서 routing을 구현하도록 하겠습니다.

 

 

3-1. react-router 설치

npm install react-router-dom --save 

 

리액트 라우터 돔을 설치

 

--save는 패키지.json의 디펜던시에 추가 패키지 정보를 저장합니다.

 

 

 

3-2. Routes 컴포넌트 구현하기

 

<예시>

import React from 'react'; //Node_modules안의 react에서 React를 가져온다.
import {
  BrowserRouter as Router, //BrowserRouter를 Router라고 부르겠다.
  Switch,
  Route,
} from 'react-router-dom'; //리액트라우터돔에서 BrowserRouter, switch,route를 가져온다.

import Login from './pages/Login/Login';
import Signup from './pages/Signup/Signup';
import Main from './pages/Main/Main';

class Routes extends React.Component {
  render() {
    return (
      <Router>
        <Nav />
        <Switch>
          <Route exact path="/" component={Login} />
          <Route exact path="/signup" component={Signup}/>
          <Route exact path="/main" component={Main} />
        </Switch>
        <Footer />
      </Router>
    )
  }
}

export default Routes;

 

 

태그랑 비슷하게 생겼는데 대문자로 시작하는건 컴포넌트다.

ex) <Nav />

 

소문자로하면 그냥 태그로 인식한다.

 

 

 

 

 

 

직접  구현하기

 

 

<Routes.js>

import React from "react";
import { BrowserRouter as Router, Switch, Route } form "react-router-dom";

import Login from "./pages/Login/Login"
import Main from "./pages/Main/Main"

class Routes extends React.Component {
  render(){
    return(
      <Router>
        <Switch>
          <Route path="/" component={Login}/>
          <Route path="/main" component={Main}/>
        </Switch>
      </Router>
    )
  }
}

export default Routes;

 

import할 때 라이브러리 -> 컴포넌트 -> 기타나머지 파일(css) 순서로 해주는게 좋다.

 

path="/"는 뒤에 추가적인 주소를 안붙였을 때 나오는 주소이다.

component={}는 해당경로에 들어갔을 때 나오는 컴포넌트를 넣으면 된다.

 

 

 

리액트는 그냥 가져왔는데 (import React from "react";)

리액트라우터돔은 약간 객체처럼 {}로 가져왔다.

 

export는 두가지 종류가 있다.

named export vs default export

 

named export는 익스포트할 때 

export { Routes } ; 

이런 형식이다. 이렇게 하면 임포트할 때도 중괄호를 쳐서 가져와야한다. 

import { Routes } from "./Routes"

 

default export는 중괄호를 칠필요는 없다.

export default Routes;

 

named export는 여러개를 내보내야할 때 쓴다.

default export는 하나만 가능.

 

컴포넌트를 익스포트할 땐 보통 디폴트익스폴트하는게 편해서 디폴트익스포트를 쓴다.

 

 

 

 

<index.js>

import React from "react";
import ReactDOM form "react-dom";
import Routes from "./Routes"

import "./styles/common.css"

ReactDOM.render(<Routes />,document.getElementById("root"));

 

 

 

<Login.js>

import React from "react";

class Login extends React.Component {
  render(){
    return <h1>Hello Login</h1>
  }
}

export default Login;

<Main.js>

import React from "react";

class Main extends React.Component {
  render(){
    return <h1>Hello Main</h1>
  }
}

export default Main;

 

 

<localhost:3000>

 

 

 

main으로 옮기기 위해서 localhost:3000/main을 쳐도 안들어가진다. 

왜냐하면 Routes.js의 Route에 exact를 안 붙였기 때문이다.

import React from "react";
import { BrowserRouter as Router, Switch, Route } form "react-router-dom";

import Login from "./pages/Login/Login"
import Main from "./pages/Main/Main"

class Routes extends React.Component {
  render(){
    return(
      <Router>
        <Switch>
          <Route exact path="/" component={Login}/>
          <Route exact path="/main" component={Main}/>
        </Switch>
      </Router>
    )
  }
}

export default Routes;

위의 path에 /가 있고 아래에도 /main으로 /가 들어간다.

이렇게 됏을 때 exact를 안넣으면 어 /main 제일 앞에 /가 있네? 하고 무조건 login으로 이동해버린다.

 

이렇게 되면 /main으로 이동할 수 없으니 exact라는 속성을 넣어준다.

 

 

<localhost:3000/main>

exact를 입력하니 정상적으로 이동한다.

 

 

이제 버튼을 눌러 경로를 바꾸게 해보자. route를 이동하는 방법은 

1.<link>컴포넌트를 사용하는 방법

2.withRouterHOC로 구현하는 방법이 있다.

 

1번

<Login.js>

import React from "react";
import {Link} from "react-router-dom"

class Login extends React.Component {
  render(){
    return <Link to="/main">Go To Main Page</Link>
  }
}

export default Login;

 

링크의 to속성에 경로를 적어준다.

 

react-router-dom 에서 제공하는 <Link> 컴포넌트는 DOM에서 <a> 로 변환(Compile) 됩니다.

 

그런데 이럴꺼면 a태그 쓰지 굳이 link컴포넌트를 임포트해와서 쓸까?

 

a태그로 이동을 하게되면은 서버에다가 다시 파일을 요청한다. 그러면 html파일을 다시 받아오는 거다.

그렇기떄문에 전체파일을 한 번 다시 다 받아온다고 보면 된다.

 

그런데 화면에서 특정부분만 바꿔주면 될 수도 있다. ex)Nav나 footer같은 경우는 항상고정되고 내부안의 내용만 바뀌는 경우 굳이 nav와 footer까지 굳이 다시 서버에 요청을 해서 다 받아오고 html을 다시 받아오기 위한 요청을 보내고 할 필요가 없다.

 

link컴포넌트를 쓰면은 결과적으로 a로 컴파일은 되는데 여기서 컴파일된 a태그는 새롭게 파일을 받아오는 요청을 보내지 않는다.

단순히 경로만 바꿔준다.

 

그래서 불필요한 요청 필요없는 부분까지 다시 랜더링하지않고 효율적으로 바뀌는 부분만 바뀔 수 있게 해주는 기능이 있다.

 

<a> vs <link>

<a> 외부사이트로 이동하는 경우

<link> 프로젝트 내에서 페이지 전환하는 경우

 

 

 

2번 withRouterHOC

 

 

Nav.js

import React from 'react'

class Nav extends React.Component {
  render() {
    return {
      <nav>
        <button>LoginPage</button>
        <button>MainPage</button>
      </nav>
    }
  }
}

export default Nav;

 

 

 

<Routes.js>

import React from "react";
import { BrowserRouter as Router, Switch, Route } form "react-router-dom";
import Login from "./pages/Login/Login"
import Main from "./pages/Main/Main"
import Nav from "./components/Nav"

class Routes extends React.Component {
  render(){
    return(
      <Router>
        <Nav /> // 추가
        <Switch>
          <Route exact path="/" component={Login}/>
          <Route exact path="/main" component={Main}/>
        </Switch>
      </Router>
    )
  }
}

export default Routes;

 

<Nav />는 스위치 바깥이니까 무슨 경로든 계속 나오고 있다.

 

 

 

 

 

 

<예시>

import React from 'react';
import { withRouter } from 'react-router-dom';

class Login extends React.Component {

  goToMain = () => {
    this.props.history.push('/main');
  }

  // 실제 활용 예시
  // goToMain = () => {
  //   if(response.message === "valid user"){
  //     this.props.history.push('/main');
  //   } else {
  //     alert("너 우리 회원 아님. 가입 먼저 해주세요")
  //     this.props.history.push('/signup');
  //   }
  // }

  render() {
    return (
      <div>
        <button
          className="loginBtn"
          onClick={this.goToMain}
        >
          로그인
        </button>
      </div>
    )
  }
}

export default withRouter(Login);

 

임포트에서 withRouter라는걸 가져온다.

근데 얘는 어떤 얘냐면 HOC라는 애다.

(High Order Component) 쉽게말해서 컴포넌트를 감싸는 컴포넌트(한계층위의 컴포넌트)이다.

 

그냥 컴포넌트를 감싸서 컴포넌트를 내보내주는 또다른 한계층위의 컴포넌트 이런 정도로 이해하면 된다.

 

무슨 기능을 하냐면 

 

  goToMain = () => {
    this.props.history.push('/main');
  }

 

클래스를 쓰면 this를 많이 쓰는데 클래스안에서  매서드를 만들 때 this를 쓸때는 무조건 애로우평션으로 적어줘야한다.

왜냐면 그냥 function으로 쓰게되면은 this가 계속 시시각각 변할 수 있다.

하지만 애로우펑션으로 쓰면은 여기서 this는 계속 Login이라는 컴퍼넌트가 되는 것이다.

 

this는 Login컴퍼넌트다. 그러면 .으로 이 컴퍼넌트 안에 들어갓다. 그러면 이 login이라는 컴퍼넌트가 최종적으로  객체로 만들어진다는 것이다.

 

props안에 들어갓다. 이 컴포넌트 안에 있는 어떤 property(속성)인 것 같다. 그리고나서 props안에 또 . 하고 들어갓다.

그러면 프롭스도 객체다.

 

히스토리도 객체다.

 

푸쉬는 메소드(함수)

 

우리는 this안에 있는 props라는 객체에 들어가서 history라는 객체에 들어가서 push라는 매소드를 호출해서 뭔가 동작을 일으킬거다.

 

 

 

 

 

<Nav.js>

 

console.log(this.props) 

맨위 Object는 this.props가 객체라는 것 

 

props를 열어보면 아무것도 없다.

__proto__라는건 기본적으로 자바스크립트 객체에 들어가 있는 것이다

 

그 외 나머지 요소는하나도 없다.

 

 

<Nav.js>

console.log("Nav props", this.props) 

<Login.js>

console.log("Login Props", this.props)

Nav props는 아무것도 안들어와 있다.

 

반면 login props는 history, location, match 이런 값들이 들어와 있다. 

히스토리도 객체인데 그 안을 보면 push가 들어가 있는걸 볼 수 있다. f는 펑션이라는 뜻이다.

props안에 history라는 객체가 들어가 있고 그 안에 push라는 메소드가 있다. 

 

근데 왜 Nav에만 없고 Login에만 있을까?

Login은 Routes.js파일에서 Switch안에 Route안에 들어가 있다.

반면에 Nav는 그 바깥에 나와있다.

 

Route안에 들어가 있는 컴포넌트들은 props안에 history, location, match와 같은 것을 주입받는 것이다.

 

그래서 Nav도 props로 history,location,match같은걸 주입받기 위해서 쓰는게 withRouterHOC다. 

 

import React from 'react'
import {withRouter} from "react-router-dom";

class Nav extends React.Component {
  render() {
    comsole.log("Nav props", this.props);
    return {
      <nav>
        <button>LoginPage</button>
        <button>MainPage</button>
      </nav>
    }
  }
}

export default withRouter(Nav);

 

withRouterHOC를 적용한 후 다시 콘솔 로그를 찍으면 history,location,match가 들어와 있다.

 

라우트안에 안들어가 있는 애들중에서 history,location,match가 필요할 때는 withRouter로 감싸줘야하는 것이다.

 

 

<Nav.js>

import React from 'react'
import {withRouter} from "react-router-dom";

class Nav extends React.Component {
  goToMain = () => {
    this.props.history.push("/main");   //push의 인자로 경로를 넣어줬다.
  };
  
  goToLogin = () => {
    this.props.history.push("/");
  }
  
  render() {
    comsole.log("Nav props", this.props);
    return {
      <nav>
        <button Onclick = {this.goToLogin}>LoginPage</button>
        <button Onclick = {this.goToMain}>MainPage</button>
      </nav>
    }
  }
}

export default withRouter(Nav);

 

 

 

Go To LoginPage를 누르면 로그인 페이지로 이동하고 

Go To MainPage를 누르면 메인 페이지로 이동한다.

 

이걸로 index.html페이지라는 하나의 페이지 안에서 경로에 따라서 다른화면을 보여주게 되는 spa를 리액트라우터로 구현한 것이다.

 

링크대신 withRouterHOC를 사용하는 이유는 함수로 쓸 수 있기 떄문이다.

함수로 사용하면 조건문을 넣을 수 있다. 특정조건이면 호출을해서 이동시켜야 하는 경우 this.props.history.push를 이용해서 하는 것이다.

 

3-4-3. 두 가지 방법의 활용법

  1. <Link>
    • 클릭 시 바로 이동하는 로직 구현 시에 사용합니다.
    • ex. Nav Bar, Aside Menu, 아이템 리스트 페이지에서 아이템 클릭 시 > 상세 페이지로 이동
  2. withRouterHOC
    • 페이지 전환 시 추가로 처리해야 하는 로직이 있는 경우 withRouterHOC 방법으로 구현합니다.
    • ex. 로그인 버튼 클릭 시
      • Backend API로 데이터(User Info) 전송
      • User Data 인증 / 인가(이 유저가 우리 유저가 맞는지 확인하고 맞으면 허락해주는 것)
      • response message
      • Case 1 : 회원 가입되어 있는 사용자 > Main 페이지로 이동
      • Case 2 : 회원 가입이 되어 있지 않은 사용자 > Signup 페이지로 이동

 

 

 

'React > Today I learned' 카테고리의 다른 글

state,props,event  (0) 2021.07.01
Sass  (0) 2021.06.30
[React] 비동기, 동기  (0) 2021.06.27
[React] why react?, JSX, Node.js, CRA  (0) 2021.06.27
[React] key값을 써야하는 이유  (0) 2021.06.26

+ Recent posts