File-base Routing

kim98111·2022년 3월 7일
0

NextJS

목록 보기
4/8
post-thumbnail

Routing

NextJS를 이용하는 경우 프로젝트 내 pages라는 폴더를 통해서 추가적인 코드나 패키지 설치 없이도 Routing 기능을 사용할 수 있는 것을 앞에서 살펴보았습니다.

중첩된 경로

위에서 살펴본 것처럼 pages 폴더 내 "서브 폴더"로 파일을 중첩하여 저장하는 방식으로 구조를 작성하여 "중첩된 경로"을 구현할 수 있습니다.

> pages 폴더
    index.js  -> index.html 문서 생성, "our-domain.com/" 요청받으면 index.html 문서를 응답으로 전달
    > news 폴더
        index.js  -> news.html 문서 생성, "our-domain.com/news" 요청받으면 news.html 문서를 응답으로 전달
        abc.js  -> news/abc.html 문서 생성, "our-domain.com/news/abc" 요청받으면 news/abc.html 문서를 응답으로 전달
        > article 폴더
            index.js  -> news/article.html 문서 생성, "our-domain/news/article" 요청받으면 news/article.html 문서를 응답으로 전달
            qwe.js  -> news/article/qwe.html 문서 생성, "our-domain.com/news/article/qwe"요청받으면 news/article/qwe.html 문서를 응답으로 전달
        ,,,

위 pages 폴더 구조일 때 pages 폴더 내 index.js로 생성한 index.html 문서는 요청한 URL 경로가 /일 때 응답으로 전달되고, news 폴더 내 index.js로 생성한 news.html문서는 요청한 URL 경로가 /news일 때 전달됩니다.
pages/news/article/qwe.js 파일로 생성한 news/article/qwe.html 문서는 요청한 URL 경로가 /news/article/qwe일 때 응답으로 전달합니다.

즉, 서브 폴더의 이름 또한 URL 경로가 되며, 서브 폴더 내부에 파일을 작성하여 중첩 라우팅을 구현할 수 있습니다.

동적 라우팅

동적 라우팅이란 하나의 Route를 재사용하는 것입니다. 즉, 동적으로 결정되는 URL의 경로에 따라 컴포넌트에서 렌더링될 정보를 결정하는 방식으로 하나의 Route를 재사용하는 것입니다.

NextJS에서 동적 라우팅을 구현하기 위해서는 pages 폴더 내 파일명을 [,,,].js처럼 작성해주어야 합니다. [,,,]안에 작성된 것이 경로 파라미터가 되고, 요청한 URL 경로에 의해 동적으로 결정된 경로값이 할당됩니다.

즉, [경로파라미터].js형식으로 파일을 생성하면 해당 파일의 컴포넌트는 동적 라우팅으로 동작하는 페이지 컴포넌트 파일이 됩니다.

> pages
    [userId].js  -> [userId].html 문서 생성, "our-domain.com/어떤값"일 때 [userId].html 문서가 응답으로 전달
    > news
        [newsId].js  -> news/[newsId].html 문서 생성, "ouer-domain.com/news/어떤값"일 때 news/[newsId].html 문서를 응답으로 전달
        ,,,

위 Pages 구조를 갖는 NextJS 프로젝트의 경우 [,,,].js 형식으로 작성된 파일들은 모두 동적 라우팅으로 동작하는 페이지 컴포넌트 파일을 의미합니다. [,,,] 안에 작성한 것은 경로 파라미터로 동작하며, 요청한 URL 경로에 동적으로 동적으로 결정된 경로값이 경로 파라미터에 할당됩니다.

"our-domain.com/a"를 요청시 서버는 [userId].html문서를 응답으로 전달해줍니다. 이때 userId 경로 파라미터에는 a가 할당됩니다.

"our-domain.com/qqq"를 요청시 서버는 [userId].html문서를 응답으로 전달해줍니다. 이때 userId 경로 파라미터에는 qqq가 할당됩니다.

"our-domain.com/news/abc"를 요청시 서버는 news/[newsId].html문서를 응답으로 전달해줍니다. 이때 newsId 경로 파라미터에는 abc가 할당됩니다.

"our-domain.com/news/pop"를 요청시 서버는 news/[newsId].html문서를 응답으로 전달해줍니다. 이때 newsId 경로 파라미터에는 pop가 할당됩니다.

[useId].js파일과 [userId.js]파일에서는 경로 파라미터가 할당받은 경로값을 통해 렌더링될 데이터를 결정해야 하므로, 각 컴포넌트에서는 경로 파라미터가 할당받은 값을 취득할 수 있어야 합니다.

useRouter 훅으로 경로 파라미터 값 취득

우리는 NextJS가 자체적으로 갖고 있는 next/router 이라는 패키지에서 "useRouter"라는 커스텀 훅을 사용하여 경로 파라미터 값을 가져올 수 있습니다.

useRouter 훅을 호출하면 반환되는 "객체"를 통해서 경로 파라미터 값을 취득할 수 있습니다. 반환되는 객체의 "query"라는 프로퍼티에 객체가 바인딩되어 있습니다.

query 객체의 프로퍼티 키로 대괄호안에 작성한 파일이름, 즉 경로 파라미터가 존재하고, 프로퍼티 값으로는 동적으로 결정된 경로값이 존재합니다.

예를 들어, "our-domain.com/a" 요청으로 전달받은 [userId].html문서는 pages/[userId].js를 통해서 생성합니다. 우리는 pages/[userId].js 파일에 작성된 페이지 컴포넌트 내에서 아래와 같은 코드로 경로 파라미터 값을 취득하여 렌더링될 데이터를 결정할 수 있습니다.

// pagse/[userId].js
import { useRouter } from 'next/router';

const User = () => {
    // useRouter 훅을 호출하여 router 객체 생성
    const router = useRouter();
    
    // 반환된 router 객체의 query 프로퍼티에 바인딩된 객체를 통해서 취득
    // query 객체에는 경로 파라미터와 동일한 이름으로 프로퍼티가 존재
    // router.query -> Object { userId: "a" }
    const userId = router.query.userId;
    
    // userId를 통해 렌더링할 데이터를 결정,,,
    
    return (
        <>
            <h1>The User Page</h1>
            <p>{userId}</P>
        </>
    );
}

export default User;

즉, router.query 객체파일명, 즉 경로 파라미터인 userId 프로퍼티 키로 동적으로 결정된 경로값인 a를 취득할 수 있습니다.

경로 변경하기

React는 SPA로서 하나의 HTML 파일을 사용합니다. 그렇기 때문에 새로운 페이지에 대한 요청을 보내지 않고 사용자와 상호작용이 가능합니다. 일반적인 React App에서는 react-router-dom의 Link, NavLink 컴포넌트를 사용하여 링크를 만들었습니다. 해당 컴포넌트들은 새로운 페이지에 대한 요청을 보내지 않으면서 경로만 변경시켜줌으로서 React App을 새로 시작하지 않고 계속 머물 수 있도록 합니다. 이는 기존 상태를 보존하면서 사용자에게는 더 나은 UX를 제공해줍니다.

NextJS 또한 하나의 HTML 파일을 사용합니다(SPA). 이때 서버로부터 전달받는 HTML 파일은 기존 React App에서 사용하는 빈 HTML 파일이 아닌 서버측에서 미리 관련된 콘텐츠를 갖고 있는 HTML 파일입니다 .

즉, NextJS 또한 SPA로서 동작해야 하기 위해서 새로운 페이지에 대한 요청을 보내지 않고 경로를 변경하여 기존 상태를 보존하면서 사용자에게도 더 나은 UX를 제공해주어야 합니다.

이를 위해 NextJS가 자체적으로 갖고 있는 next/link패키지에 존재하는 "Link 컴포넌트"를 사용합니다. Link 컴포넌트는 export default를 사용했기 때문에 {,,,}을 사용하여 import 하지 않습니다.

// news/index.js
import Link from 'next/link';

const NewsPage = () => {
    return (
        <>
            <h1>The News Page</h1>
            <ul>
                <Link href="/news/a">
                    Go to page A!!
                </Link>
                <Link href="/news/b">
                    Go to page B!!
                </Link>
            </ul>
        <>
    );
};

export default NewsPage;

참고로 Link 컴포넌트는 a 엘리먼트를 반환하는 컴포넌트입니다. 내부에서 새로운 HTML 파일의 요청을 보내지 않는 로직이 포함되어 있습니다. 이를 통해 SPA에 계속 머무르면서 기존 상태를 잃지도 않고, SPA 내부에 존재하는 컴포넌트를 URL 경로에 따라 렌더링을 하게 됩니다.

useRouter 훅으로 경로 변경하기

next/link의 Link 컴포넌트를 통한 경로를 변경하는 방법 말고 직접 코드를 작성하여 경로를 변경할 수 있습니다. 이는 react-router-dom이 제공하는 useNavigate 커스텀 훅이 반환하는 navigation 함수를 통해 경로를 이동할 수 있었습니다.

NextJS에서는 자체적으로 갖고 있는 next/router 패키지의 "useRouter 커스텀 훅"을 사용합니다. useRouter 훅을 호출하면 router 객체가 반환됩니다. 반환된 router 객체에는 이전에 살펴본 것처럼 query라는 프로퍼티에 바인딩된 객체를 통해서 동적으로 결정된 경로값을 가져올 수 있었습니다.

경로를 변경하기 위해서는 useRouter 훅이 반환한 객체를 통해 push, replace, back 메서드를 호출하는 방식으로 가능합니다.

  • router.push("이동할 경로") : 인수로 전달한 경로로 이동하며 URL 정보가 History 스택에 쌓이게 됩니다.

  • router.replace("이동할 경로") : 인수로 전달한 경로로 이동하며 URL 정보가 History 스택에 쌓이지 않게 됩니다.

  • router.back() : 직전 페이지로 이동합니다.

router.push(); vs router.replace();

push와 replace 메서드 둘 다 인수로 전달한 경로로 변경해줍니다. 이때 두 메서드의 차이점은 History stack의 동작에 있습니다.

push 메서드의 경우 인수로 전달한 경로로 변경을 하고 변경한 경로의 정보를 History stack에 push 합니다. 이 경우 뒤로가기 버튼을 눌렀을 때 직전 경로로 이동이 가능합니다.

만약 현재 URL이 "our-domain.com/"일 때 router.push("/a");을 실행하면 경로가 "our-domain.com/a"로 변경이 됩니다. 그리고 History stack에는 이전 정보인 "/"위에 "/a"가 쌓이게 됩니다. 즉, 뒤로가기 버튼을 누르면 경로인 "our-domain.com/"의 페이지를 보게 됩니다.

만약 현재 URL이 "our-domain.com/"일 때 router.replace("/a");을 실행하면 경로가 "our-domain.com/a"로 변경됩니다. 이때 History Stack에는 변경된 URL인 "/a"의 정보가 쌓이지 않습니다. 즉, 현재 경로가 "our-domain.com/a"로 변경되더라고 History stack에는 "/"에 대한 정보만이 존재합니다. 이는 브라우저에서 뒤로가기 버튼을 눌른다면 "/"이전 페이지로 이동하게 됩니다.

정리하자면 push와 replace 둘 다 경로를 변경하는 동작을 하지만, push의 경우 변경할 URL 정보가 History stack에 쌓이게되고, replace의 경우 변경할 URL 정보가 History Stack에 쌓이지 않는다는 차이점이 존재합니다.

profile
Frontend Dev

0개의 댓글