본문 바로가기

React

20210607 React04 : Routing(path, id, queryString 방식) URLSearchParams, query-string 패키지, Switch-NotFound, JSX링크로 라우팅 이동(RRD : Link, NavLink), JS로 라우팅 이동(RRD: history, withRouter, Redirect)

React 04



Dynamic Routing


  • URL을 동적으로 입력하는 방법

Path에 id값 사용 방식


  • Router에서 Route에 연결한 라우트 Component의 경우 path에 :이름을 붙여서 url을 표현 하게 되면 해당 Component 함수에서 받아오는 인자 (props)의 경우 객체이다.
  • 그 객체는 해당 객체의 match에 params 안에 path로 지정한 이름으로 key를 가지는 key-value의 구조로 가지게 됨
  • 자동으로 params 객체에 이름: '1' 의 item을 가지게 됨
  • :를 사용하는 경우 필수 요소라서 /profile/:id의 표현인 경우 /profile를 포함한다고 인식하고, 이처럼 따로 라우트 처리를 해줘야 함
// id라는 이름으로 path를 붙인 경우 -> 라우트 처리
import { BrowserRouter, Route } from "react-router-dom";
import "./App.css";
import About from "./pages/About";
import Home from "./pages/Home";
import Profile from "./pages/Profile";

function App() {
  return (
    <BrowserRouter>
      <Route path="/" exact component={Home} />
      <Route path="/profile" exact component={Profile} />
      <Route path="/profile/:id" component={Profile} />
      <Route path="/about" component={About} />
    </BrowserRouter>
  );
}

export default App;
export default function Profile(props) {
  console.log(props);
  const id = props.match.params.id;
  console.log(id, typeof id);
  // 1, string
  return (
    <div>
      <h2>Profile 페이지 입니다.</h2>
      {id && <p>id 는 {id} 입니다.</p>}
    </div>
  );
}

Path에 QueryString을 사용 방식


  • ?이름=값으로 url 표현 부분
  • 이것을 Query String이라고 함
  • 필수가 아니고 옵션이라 라우트 처리가 필요 없음(즉, path에 표시해서 지정해줄 필요가 없음)
  • 해당 url 표현 부분의 경우 Route에 연결한 컴포넌트 함수가 받는 인자 객체 안 location에 search에 string으로 들어가 있음
  • search가 담고 있는 내용이 string이라서 직접적으로 사용하려면 이름, 값 부분을 가져올수 있게 가공을 해야하는데 방법이 2가지가 있음
    • 브라우저의 내장 객체인 URLSearchParams를 활용함

URLSearchPrams


  • URLSearchParams 객체를 분석할 search를 넣어 주면서 new 키워드로 생성하여 사용하고 해당 객체를 받아서 console에 찍어보면 별다른 데이터가 바로 보이진 않고, 해당 객체에서 지원하는 method를 활용해야 한다.
  • get() method를 통해서 가지고 오고 싶은 search부분의 이름 부분을 인자로 넣어 값을 가져 올 수 있음
  • 단점
    • 브라우저의 내장 전역 객체이기 때문에 브라우저에 따라 지원 유무가 다르다.
    • key를 무조건 제공해야 꺼내 쓸수 있다.
// 기본적으로 URLSearchParams를 제공하기 때문에 import 필요 없음
export default function About(props) {
  console.log(props);
  const searchParams = props.location.search;
  const obj = new URLSearchParams(searchParams);
  console.log(obj); // {}
  console.log(obj.get("name")); // kim
  return <div>About 페이지 입니다.</div>;
}

query-string 패키지


  • 설치 : npm i query-string
  • URLSearchParams의 단점을 극복시켜 줌
  • queryString을 key-value 객체 형태로 만들어줌
  • parse 함수를 사용함
// 외부 패키지 이기 때문에 import 필요
import queryString from "query-string";
export default function About(props) {
  console.log(props);
  const searchParams = props.location.search;
  const query = queryString.parse(searchParams);
  console.log(query); // {name: "kim"}
  return (
    <div>
      <h2>About 페이지 입니다.</h2>
      {query.name && <p>name은 {query.name} 입니다.</p>}
    </div>
  );
}



Switch로 NotFound 구현하기


Switch


  • React-Router-Dom에 있는 컴포넌트 임
  • 여러 Route 중 순서대로 먼저 맞는 하나만 보여줌
  • exact를 뺄 수 있는 로직을 만들 수 있음 (순서대로 실행되기 때문에 가장 넓은 범위를 제일 나중에 작성하면 됨)
    • / path의 경우에는 무조건 exact를 넣어주어야 함(모든 경로에 포함이 되어 있기 때문에)
  • 가장 마지막에 어디 path에도 맞지 않으면 보여지는 컴보넌트를 설정해서, Not Found 페이지를 만들 수 있음
// App.js
// Switch 컴포넌트 import
import { BrowserRouter, Route, Switch } from "react-router-dom";
import "./App.css";
import About from "./pages/About";
import Home from "./pages/Home";
import NotFound from "./pages/NotFound";
import Profile from "./pages/Profile";

function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/profile/:id" component={Profile} />
        <Route path="/profile" component={Profile} />
        <Route path="/about" component={About} />
        <Route path="/" exact component={Home} />
        <Route component={NotFound} />
      </Switch>
    </BrowserRouter>
  );
}

export default App;
// NotFound.jsx
export default function NotFound() {
  return <div>페이지를 찾을 수 없습니다.</div>;
}



JSX링크로 라우팅 이동하기

  • 앱 내부에서 페이지 이동하는 것

a 태그 사용 방식 (기존 HTML을 통한 이동 방식)

  • 기존 html 이동방식은 a 태그를 사용
  • 이미 다운로드된 파일을 이용해서 페이지를 이동하는 것이 아닌, 일반적인 사이트 처럼 주소 url의 page를 찾는 기능을 수행함으로서 새롭게 앱을 다시 다운받아 새로고침하게 됨
function App() {
  return (
    <BrowserRouter>
      <a href="/">Home</a>
      <Switch>
        <Route path="/profile/:id" component={Profile} />
        <Route path="/profile" component={Profile} />
        <Route path="/about" component={About} />
        <Route path="/" exact component={Home} />
        <Route component={NotFound} />
      </Switch>
    </BrowserRouter>
  );
}

Link Component (링크 컴포넌트 - react-router-dom)

  • 내부의 앱이 가진 페이지에서 맞는 페이지를 이동하여 출력 함
  • Link 컴포넌트는 react-router-dom 에서 제공함
  • 사실 출력하여 dev tool로 보면 a태그로 되어 있음 (단, 실제로 서버에서 페이지를 가져와서 이동하는게 아닌, 앱 내에서 페이지를 찾아 도와줌)
  • 참고) 주소창으로 path를 찍으면 새로고침 됨
function App() {
  return (
    <BrowserRouter>
      <Links />
      <Switch>
        <Route path="/profile/:id" component={Profile} />
        <Route path="/profile" component={Profile} />
        <Route path="/about" component={About} />
        <Route path="/" exact component={Home} />
        <Route component={NotFound} />
      </Switch>
    </BrowserRouter>
  );
}
  • Link 컴포넌트에서는 a 태그의 href와 달리 to라는 속성 키워드로 사용됨
// src-> components-> Links.jsx
import { Link } from "react-router-dom";

export default function Links() {
  return (
    <ul>
      <li>
        <Link to="/">Home</Link>
      </li>
      <li>
        <Link to="/profile">Proflie</Link>
      </li>
      <li>
        <Link to="/profile/1">Profile/1</Link>
      </li>
      <li>
        <Link to="/about">About</Link>
      </li>
      <li>
        <Link to="/about?name=kim">About?name=kim</Link>
      </li>
    </ul>
  );
}

NavLink를 사용하는 방식

  • react-router-dom에서 제공하는 컴포넌트임
  • Link 컴포넌트와 달리, activeClassName, activeStyle처럼 active 상태에 대한 스타일 지정이 가능함
    • 현재 주소에 대한 match 상태를 client적으로 표현하는데 유용함
  • 현재 브라우저 url 상태와 지정한 경로가 맞으면 여러 스타일링을 표시함
  • 브라우저 url과 현재 지정한 경로가 맞는지를 체크하는 matching 알고리즘이고 Route의 path처럼 동작하기 때문에 exact가 있음

  • 주의) queryString도 포함관계로 인식해서 따로 구분할 수 있는 로직이 필요함
    • NavLink의 isActive 속성을 이용하는데, isActive의 경우 true, false 값중에 true를 받아야 해당 activeStyle이 나타남
    • isActive 로 callback을 넣으면, matchlocation이라는 두개의 인자를 받아 사용할 수 있음
    • location의 경우 search에 queryString이 달리기 때문에 search 값이 지정한 값인지를 체크하여 true, false를 return하여 isActive에 할당하면 queryString도 구분이 된다.
      • (하지만, queryString만 체크하기 때문에 원래 path도 체크해주어야 한다.)
    • 원래 path도 체크하기 위해서, match 인자를 사용함
    • match의 경우 브라우저의 url과 해당 link에서 지정한 to가 일치하는지를 확인하여 일치하면 일치한다는 정보를 담은 객체를 return 함, 일치하지 않으면 null을 내보냄
import { NavLink } from "react-router-dom";

// active시 지정할 스타일링 변수
const activeStyle = { color: "green" };

// activeStyle -> 스타일링 연결
// isActive -> Active에 대한 로직
// match -> path 조건
// location -> qeuryStrig 조건

export default function Links() {
  return (
    <ul>
      <li>
        <NavLink to="/" exact activeStyle={activeStyle}>
          Home
        </NavLink>
      </li>
      <li>
        <NavLink to="/profile" exact activeStyle={activeStyle}>
          Proflie
        </NavLink>
      </li>
      <li>
        <NavLink to="/profile/1" activeStyle={activeStyle}>
          Profile/1
        </NavLink>
      </li>
      <li>
        <NavLink
          to="/about"
          activeStyle={activeStyle}
          isActive={(match, location) => {
            return match !== null && location.search === "";
          }}
        >
          About
        </NavLink>
      </li>
      <li>
        <NavLink
          to="/about?name=kim"
          activeStyle={activeStyle}
          isActive={(match, location) => {
            return match !== null && location.search === "?name=kim";
          }}
        >
          About?name=kim
        </NavLink>
      </li>
    </ul>
  );
}



JS로 라우팅 이동하기


  • react-route-dom에서 제공하는 함수를 이용한 방법
  • Route에서 페이지 요청시 받아오는 인자로 props를 받아오는데, 해당 props는 객체로 history, location, match 등의 정보를 가지고 있는데 history 객체를 이용하여 페이지 이동을 할 수 있음
  • history에 push 메서드를 활용하여, 안에 경로를 넣으면 해당 페이지로 이동하게 됨
// Login page route
// 로그인 하기 버튼을 누르고 1초뒤 home 페이지로 이동함
export default function Login(props) {
  console.log(props); // history, location, match 등이 들어옴
  function login() {
    setTimeout(() => {
      props.history.push("/"); // history 객체에서 제공하는 메서드들을 활용해서 페이지 이동이 가능함(새로고침 없는 페이지)
    }, 1000);
  }
  return (
    <div>
      <h2>Login 페이지 입니다.</h2>
      <button onClick={login}>로그인하기</button>
    </div>
  );
}


따로 기능을 분리하여 component로 관리하는 경우


  • porps에 대한 기능을 사용하려면 page가 바뀔때 받았던 props를 하위 component에 계속 전달 시켜야 함
  • 이러한 경우 나중에 복잡해 지면 문제가 생김
// login page
import LoginButton from "../components/LoginButton";
export default function Login(props) {
  return (
    <div>
      <h2>Login 페이지 입니다.</h2>
      <LoginButton {...props} />
    </div>
  );
}
// logtin button component
export default function LoginButton(props) {
  function Login() {
    setTimeout(() => {
      props.history.push("/");
    }, 1000);
  }
  return <button onClick={Login}>로그인 하기</button>;
}


해결방법 : HOC (Higher Order Component)의 withRouter 함수


  • react-router-dom에서 제공하는 HOC인 withRouter 메서드를 사용
    • 기존 상위 page component에서 하위componet로 props를 뿌리지 않고, 해당 기능을 사용할 component에서 withRouter 메서드로 해당 출력 component function을 한번 묶어서 export 처리 하면 됨
    • 즉, props를 활용하는 component에게 Router에서 props를 계속 전달할 필요 없이 바로 props를 받는것 처럼 사용이 가능함
import { withRouter } from "react-router-dom";
export default withRouter(function LoginButton(props) {
  function Login() {
    setTimeout(() => {
      props.history.push("/");
    }, 1000);
  }
  return <button onClick={Login}>로그인 하기</button>;
});



Redirect Component


  • react-router-dom에서 지원함
  • JSX 형태로 사용
  • Redirect 컴포넌트는 render가 되면 지정한 경로로 이동하게 됨
  • Route component에서는 render 속성을 지원함 isLogin 변수에 따라서 홈으로 갈지, Login component를 보여줄지 정할 수 있음
const isLogin = false;

function App() {
  return (
    <BrowserRouter>
      <Links />
      <NavLinks />
      <Switch>
        <Route
          path="/login"
          render={() => (isLogin ? <Redirect to="/" /> : <Login />)}
        />
        <Route path="/profile/:id" component={Profile} />
        <Route path="/profile" component={Profile} />
        <Route path="/about" component={About} />
        <Route path="/" exact component={Home} />
        <Route component={NotFound} />
      </Switch>
    </BrowserRouter>
  );
}

export default App;