본문 바로가기

React

React - 화면 이동하기(Router, Link, Navigate)

  • React는 모든 것을 component단위로 만들고 코드를 관리한다.
  • 복잡한 페이지를 component로 쪼개로 개발하면 빠르고, 쉽고, 여럿이 동시에 만들 수 있는 장점이 있다.
  • React에서는 이 component단위로 화면을 이동시킬 수 있다.
  • HTML에서는 화면을 이동할 때, 다른 HTML파일로의 이동만이 가능했던 점이 다르다.

 

 

React Router v6

npm install react-router-dom@6
  • React Component로 페이지를 나누고, 이동할 수 있게 해주는 라이브러리이다.
  • 위 명령어로 설치한다.
<Routes>
  <Route path="/" element={<HomePage/>} />
  <Route path="courses/" element={<CourseListPage/>} />
  <Route path="courses/1" element={<CoursePage/>} />
  <Route path="*" element={<NotFoundPage/>} />
</Routes>
  • 위와같이 컴포넌트단위로 페이지를 나눈다.
<Link to="/">홈페이지</Link>
<Link to="/courses">수업탐색</Link>
<Link to="/question">커뮤니티</Link>
  • 그리고 이렇게 페이지를 이동시킬 수 있다.

 

React Router의 핵심 컴포넌트

1. Router

  • React Router에서 사용하는 모든 데이터(현재 주소, 페이지 기록 등)를 가지고 있다.
  • Router가 없으면 React Router를 쓸 수 없다.
  • Router컴포넌트는 내부적으로 context.provider와 같기때문에, React Router의 기능을 사용하기위해서는 반드시 Router 컴포넌트 안에서 사용해야한다.
  • 보통 최상위 컴포넌트를 Router로 감싸서 프로젝트 전체에서 React Router를 사용할 수 있게 만든다.

2. Routes, Route

<Routes>
  <Route path="/" element={<HomePage/>} />
  <Route path="courses/" element={<CourseListPage/>} />
  <Route path="courses/1" element={<CoursePage/>} />
  <Route path="*" element={<NotFoundPage/>} />
</Routes>
  • switch - case 문과 같이 url이 들어오면, 위에서부터 하나씩 검사한다.
  • path와 url이 일치하면, 해당 Route의 element prop의 컴포넌트로 이동한다.
  • Route 컴포넌트는 Routes 컴포넌트 내부에 사용해야한다.
  • Routes 컴포넌느 내부에는 Route 컴포넌트만 사용할 수 있다.

3. Link

React Router에서 a태그 대신 사용하는 컴포넌트로, 페이지를 이동시킬 수 있다.

<Link to="/">홈페이지</Link>
<Link to="/courses">수업탐색</Link>
<Link to="/question">커뮤니티</Link>

Routes로 페이지 나누기

function Main() {
  return (
    <BrowserRouter>
      <App>
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="courses" element={<CourseListPage />} />
          <Route
            path="courses/react-frontend-development"
            element={<CoursePage />}
          />
          <Route path="wishlist" element={<WishlistPage />} />
        </Routes>
      </App>
    </BrowserRouter>
  );
}
  • "/" 경로로 들어오면, HomePage 컴포넌트가 렌더링된다.
  • path prop으로 경로를 지정하고, element prop으로 보여줄 컴포넌트를 지정한다.
  • element prop에는 컴포넌트 함수가 아닌, jsx를 넘겨줘야한다는 점을 기억하자.

 

  • Routes컴포넌트는 이렇게 여러개의 Route를 포함한다.
  • Route를 렌더링 할 때, 리액트는 Routes안에 있는 Route를 순서대로 검사한다.
  • 일치하는 경로를 찾으면, element prop으로 지정한 컴포넌트를 렌더링한다.
  • 여기서 Routes, Route는 react fragment처럼 아무 dom도 가지지않는다.

Link로 페이지 이동하기

import { Link } from 'react-router-dom';

function Nav() {
  return (
    <div className={styles.nav}>
      <Container className={styles.container}>
        <Link to="/">
          <img src={logoImg} alt="Codethat Logo" />
        </Link>
        <ul className={styles.menu}>
          <li>
            <Link to="/courses">카탈로그</Link>
          </li>
          <li>커뮤니티</li>
          <li>
            <UserMenu />
          </li>
        </ul>
      </Container>
    </div>
  );
}
  • Link 를 import하고, 클릭시 페이지를 이동시켜야겠다고 생각한 노드를 Link컴포넌트로 감싸서 사용한다.
  • Link컴포넌트는 to 라는 pro에 이동시킬 경로를 지정해줄 수 있다.
  • 위 예시코드에서는 <Link to="/courses"> 라고 작성된 부분이 있는데, 맨 앞에 "/"를 붙여준 것은 절대경로라는 의미이다.
  • 맨 앞에 "/"를 붙여주지않으면, 웹브라우저가 현재주서의 맨 끝에 "/courses" 를 붙인 곳으로 이동시키려한다.
<Link to={`/courses/${course.slug}`}>{course.title}</Link>
  • 위와같이 템플릿문자열을 사용해서 to prop의 값을 적어줄 수 있다.

navLink 네비게이션 구현하기

  • Navigation Link 로, 메뉴에서 사용하는 Link라는 뜻이다.
  • Link와, navLink의 차이점은 navLink는 style prop으로 함수를 지정해줄 수 있다는 점이다.
  • NavLink의 style prop으로 지정된 함수는 불린타입의 값을 파라미터로 받는데, 이 값은 '현재 위치해있는 페이지의 경로가 해당 navLink의 path 경로와 같은지' 에대한 불린값이다.
  • 그러니까, 해당 메뉴에 내가 들어와있다면 그 navLink를 어떤 스타일로 보여줄지를 함수 안에 적을 수 있다.
  • 이 함수는 react inline style객체를 리턴하면 되고 아래와같이 만들 수 있다.
function getLinkStyle({ isActive }) {
  return {
    textDecoration: isActive ? 'underline' : undefined,
  };
}


function Nav() {
  return (
    <div className={styles.nav}>
      <Container className={styles.container}>
        <Link to="/">
          <img src={logoImg} alt="Codethat Logo" />
        </Link>
        <ul className={styles.menu}>
          <li>
            <NavLink to="/courses" style={getLinkStyle}>
              카탈로그
            </NavLink>
          </li>
          <li>
            <NavLink to="/questions" style={getLinkStyle}>
              커뮤니티
            </NavLink>
          </li>
          <li>
            <UserMenu />
          </li>
        </ul>
      </Container>
    </div>
  );
}

하위 페이지 나누기

  • Route로 페이지를 나누는 것이 복잡한 경우, Route를 중첩해서 코드를 조금 줄일 수 있다.
function Main() {
  return (
    <BrowserRouter>
      <App>
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="courses">
            <Route index element={<CourseListPage />} />
            <Route path="react-frontend-development" element={<CoursePage />} />
          </Route>
          <Route path="wishlist" element={<WishlistPage />} />
          <Route path="questions" element={<QuestionListPage />} />
          <Route path="questions/*" element={<QuestionPage />} />
        </Routes>
      </App>
    </BrowserRouter>
  );
}
  • 상위 Route의 path에 "courses"를 주었기때문에, 하위 Route들의 path의 앞에는 "courses"가 생략되었다.
  • 그리고 상위 Route의 path로 접근시 바로 보여줄 Route는 path가 아닌, index 를 prop으로 준다.

Outlet 컴포넌트

하위 Route 여러개에서 공통된 디자인을 보여주고 싶을 때(공통된 레이아웃을 지정해주고 싶을 때) 사용한다.

function App() {
  return (
    <>
      <Nav className={styles.nav} />
      <div className={styles.body}>
        <Outlet />
      </div>
      <Footer className={styles.footer} />
    </>
  );
}
function Main() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App />}>
          <Route index element={<HomePage />} />
          <Route path="courses">
            <Route index element={<CourseListPage />} />
            <Route path="react-frontend-development" element={<CoursePage />} />
          </Route>
          <Route path="wishlist" element={<WishlistPage />} />
          <Route path="questions" element={<QuestionListPage />} />
          <Route path="questions/*" element={<QuestionPage />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}
  • Main함수의 상위 Route컴포넌트의 element prop으로 App컴포넌트를 주고있다.
  • App 컴포넌트에서는, Outlet 컴포넌트를 div에 넣고있다.
  • App컴포넌트의 안에 Routes컴포넌트를 넣은거랑 뭐가 다른지 잘 모르겠다. 찾아보자.

useParams 로 동적인 경로 만들기

function Main() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App />}>
          <Route index element={<HomePage />} />
          <Route path="courses">
            <Route index element={<CourseListPage />} />
            {/* 이 부분 */}
            <Route path="react-frontend-development" element={<CoursePage />} />
          </Route>
          <Route path="wishlist" element={<WishlistPage />} />
          <Route path="questions">
            <Route index element={<QuestionListPage />} />
            <Route path="*" element={<QuestionPage />} />
          </Route>
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

위 코드의 Route path를 동적으로 만들어볼것이다.

function Main() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App />}>
          <Route index element={<HomePage />} />
          <Route path="courses">
            <Route index element={<CourseListPage />} />
            {/* 이 부분 */}
            <Route path=":courseSlug" element={<CoursePage />} />
          </Route>
          <Route path="wishlist" element={<WishlistPage />} />
          <Route path="questions">
            <Route index element={<QuestionListPage />} />
            <Route path="*" element={<QuestionPage />} />
          </Route>
        </Route>
      </Routes>
    </BrowserRouter>
  );
}
  • 위와 같이 작성하면, courseSlug라는 변수로 페이지의 경로를 받아올 수 있다.
  • React Router에서는 이 것을 파라미터라고 부른다.
function CoursePage() {
 /* 이 부분 */
  const { courseSlug } = useParams();
  const navigate = useNavigate();
  const course = getCourseBySlug(courseSlug);
  const courseColor = getCourseColor(course?.code);
...
}
  • CoursePage 컴포넌트에서는 useParams를 사용해서 파라미터를 받을 수 있다.
  • useParams는 현재 경로의 파라미터들이 저장되어있는 객체이다.
  • Main에서 사용한 courseSlug라는 변수명(내가 임의로 정한 이름이다.)을 따라서 coursePage에서 꺼내볼 수 있다.

없는 페이지 처리

function Main() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App />}>
          <Route index element={<HomePage />} />
          <Route path="courses">
            <Route index element={<CourseListPage />} />
            <Route path=":courseSlug" element={<CoursePage />} />
          </Route>
          <Route path="wishlist" element={<WishlistPage />} />
          <Route path="questions">
            <Route index element={<QuestionListPage />} />
            <Route path=":questionId" element={<QuestionPage />} />
          </Route>
          <Route path="*" element={<NotFoundPage />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}
  • Route는 위에서부터 차례대로 검사를 시작한다고 했으니, 맨 마지막에 path="*" 을 가진 Route를 추가해주므로 존재하지 않는 url을 입력할 시, NotFoundPage 컴포넌트를 렌더링시킬 수 있다.

navigate로 리다이렉트하기

어떤 이유로, 다른페이지로 이동시키는 것을 redirect라고 부른다.

if (!course) {
  return <Navigate to="/courses" />;
}
  • 이렇게 사용할 수 있는데, Navigate컴포넌트가 렌더링되면 React Router는 to prop에 지정된 경로로 이동시켜준다.

useSearchParams로 쿼리 사용하기

const [searchParams, setSearchParams] = useSearchParams();
const initKeyword = searchParams.get('keyword');
  • useSearchParams는, React Router에서 제공하는 커스텀훅이다.
  • React Router에서 쿼리파라미터의 값을 가져오고 싶을 때 이 훅을 사용할 수 있다.

useNavigate로 페이지 이동하기

  • 위에서 <Navigate> 컴포넌트를 리턴하면, React Router가 해당 컴포넌트를 렌더링할 때 지정된 경로로 이동한다고 배웠다.
  • 만약 return문에서가 아닌 경우(렌더링하는 도중에 이동시킬 것이 아닌 경우) 코드상으로 페이지를 이동시키고 싶다면 useNavigate 훅을 사용할 수 있다.
import { Navigate, useParams, useNavigate } from 'react-router-dom';
...

function CoursePage() {
  const navigate = useNavigate();
  
   const handleAddWishlistClick = () => {
    addWishlist(course?.slug);
    navigate('/wishlist');
  };
  
  return (
    <>
      <div className={styles.header} style={headerStyle}>
      ...
          <Button variant="round" onClick={handleAddWishlistClick}>
            + 코스 담기
          </Button>
      ...     
      </div>
    </>
  );
}

export default CoursePage;

Link, Navigate, useNavigate 의 차이

Link

  • 사용자가 클릭해서 페이지를 이동할 때 사용한다.
  • 하이퍼링크 텍스트, 페이지 이동버튼, 이미지 등에 사용한다.

Navigate

  • 특정 경로에서 렌더링시점에 다른 페이지로 이동시키고 싶을 때 사용한다.
  • 쇼핑몰에서 회원전용 페이지에 로그인 없이 접근시, 로그인페이지로 redirect시킬 때

useNavigate

  • 특정코드의 실행이 끝나고 페이지를 이동시키고 싶을 때 사용한다.
  • 쇼핑몰의 장바구니에 담기버튼클릭으로 리퀘스트를 보낸 후 페이지를 이동시킬 때
  • 결제버튼을 누르고 모든 결제가 완료된 후 페이지를 이동시킬 때
  • 리다이렉트된 로그인 페이지에서 로그인을 완료한 후, 처음 진입했던 페이지로 이동시킬 때