이번 블로깅에선 Next.js의 초기세팅에서부터 기본적인 페이지를 만드는 것을 해보려한다. 이 블로깅은 React문법을 기본적으로 알고 있다고 가정하고 진행하려 한다.
Next.js를 위한 프로젝트를 만들기 위해선 아래와 같은 명령어를 터미널에 입력하면 된다.
npx create-next-app@latest
그러면 다음과 같은 명령어가 나온다.
? What is your project named? › my-app
화살표 옆에 본인 프로젝트의 이름을 적으면 된다.
이번 연습 프로젝트는 test
라는 이름으로 하겠다.
그러고 나면 TS를 쓸건지 Router를 쓴건지 등등 물어보는데, 본인 프로젝트에 맞게 Yes/No를 입력해주면 다음과 같이 프로젝트 기본 세팅이 완성된다.
그리고 프로젝트를 실행하고 싶다면 다음과 같은 명령어를 터미널에 입력하면 된다.
npm run dev
만약 에러가 뜰 경우, 아래와 같은 명령어를 입력해보고 다시해보자.
npm install next --save
이로써 기본적인 기본세팅은 끝났다.
프로젝트의 구조는 다음과 같는데, 각각이 어떤 일을 하는지 중요한 파일 위주로 간단하게 확인해보자.
layout.js
layout.js는 page.js를 감싸는 컴포넌트라고 생각하면 된다. 좀 더 자세한 이야기는 page.js를 다룰때 이야기 해보겠다.
page.js
이는 말 그대로 페이지 전체에 대한 컴포넌트이다.
그렇다면 layout.js는 페이지를 감싼다는 말인가?? 이해가 가지 않는다. 이 둘을 구분 짓는 이유는 무엇이란 말인가.
layout.js와 page.js
이해를 쉽게 하기 위해 파일을 위와 같이 구성해보았다.
const Layout = ({children}) =>{
return(<>
<div> layout.js</div>
{children}
</>)
}
export default Layout;
const Example = () =>{
return (
<>
<div className="wrapper">page.js</div>
</>
)
}
export default Example;
위와 같이 파일을 만들면 아래와 같은 페이지가 완성이 된다. layout.js가 page.js를 감싸고 있다는 뜻은 이 뜻이다.
그렇다면 이짓을 왜 할까? 이따 라우팅 설명할 때 한번 더 설명하겠지만 간단히 집어보자면, 공통된 UI가 있을 경우가 있기 떄문에 코드의 간략화를 위해 이러한 짓을 한다. 라우팅 이야기 할때 좀 더 자세히 다루어보기로 하고, layout.js
는 page.js
를 감싸는 것이고, page.js는 페이지 주 내용이 들어가는 파일
을 말하는구나 정도로 알면 될 것 같다.
globals.css
모든 페이지에 적용되는 CSS파일
xxx.module.css
특정 페이지에 적용되는 CSS파일
node_modules
프로젝트의 구동에 필요한 모든 라이브러리 보관하는 공간
package.json
구동되는 라이브러리의 버전들과 터미널 명령어를 정리해두는 파일 (기록용 파일)
이정도가 프로젝트 실행시 기본이 되는 파일로 알아두면 굉장히 유용할 것이다.
Next.js는 라우팅 방법이 매우 편하다.
리액트에서 라우팅할 때는 어떠한 방법을 사용했는지 한번 보자.
const Router = () => (
<BrowserRouter>
<Routes>
<Route exact path='/main' element={<Main />} />
<Route exact path='/' element={<Intro />} />
<Route exact path='/login' element={<Login />} />
...
</BrowserRouter>
);
이렇게 컴포넌트와 주소를 연결하는 과정이 필요했다. 하지만 Next.js의 경우 이 부분이 매우 편리하다. 이렇게 할 필요 없이 파일만 만들면 알아서 라우팅이 된다.
만드는 방법은 폴더를 만들고 거기에 page.js
를 만들면 된다. Next.js의 경우 약속 같은게 있는데, 우리가 HTML에서 초기 페이지를 index.html로 하는 것처럼 전체적인 페이지가 될 파일 이름은 page.js로 해야한다.
이때 폴더 명이 라우팅 주소가 된다.
예시를 들어보자.
본인이 /page2 라는 주소에 페이지를 만들고 싶다면 다음과 같이 폴더를 만들면 된다.
const Example = () =>{
return(<>Example 페이지로 이동 성공</>)
}
export default Example;
컴포넌트 이름은 아무거나 해도 괜찮다. 하지만 당연하게도 export default를 해주어야 한다.
이렇게 만들었다면 이제 주소를 입력해보자.
잘 나오는 것을 확인할 수 있다.
그렇다면 page2/page3로 하고 싶다면 어떻게 해야할까?
폴더 명을 page2/page3으로 해야할까? 절대 아니다.
눈치 빠른 사람들은 알겠지만 폴더 구조
에 따라 라우팅
이 정해진다.
예를 들어 app 폴더 안에 page2라는 폴더가 있고, 그 안에 page3라는 폴더가 있다고 가정해보자. 다음과 같이 말이다.
const Example = () =>{
return(<>page3 페이지로 이동 성공</>)
}
export default Example;
이러한 구조라면 page3에 있는 page.js를 페이지로 출력하기 위한 라우팅은 /page2/page3
가 된다.
정리하면 app폴더에 있는 page.js가 최상위 폴더인 아무것도 없는 주소 /
가 되는 것이고 그 아래로 폴더를 타고 타고 들어가는 라우팅의 형태를 취한다.
이를 통해 기존에 있던 React의 라우팅을 하지 않고 자동으로 이루어질 수 있다. 하지만 딱 보기에도 프로젝트 크기가 커질 시에 폴더 구조가 굉장히 많아져서 복잡할 가능성이 있어보인다.
추가적으로 layout.js도 이에 적용이 가능하다.
layout.js는 해당 폴더에 있는 page.js를 감싸는 파일이라고 했다...
이 점이 Next.js만의 라우팅 방식에 있어서 활용도가 있다.
이를 예시를 통해 알아보자.
만약 위와 같은 폴더 구조(라우팅 구조)
가 있다고 가정해보자.
만약 page3
과 page4
에 공통된 UI
를 넣고 싶다면 어떻게 하면 될까? 이때 layout.js
를 활용하면 된다. 어떤 폴더에 layout.js
를 활용해야 될까?
바로 그 상위 폴더인 layout.js
가 된다.
페이지가 이동했다고 해서 layout.js가 사라지는 것은 아니다. 상위폴더의 layout.js는 사라지지 않고 안에 페이지를 품고 있는 형태이다.
한번 만들어보자.
const Layout = ({children}) =>{
return(<>
<p>page2의 layout.js</p>
{children}
</>)
}
export default Layout;
이렇게 만들었다고 가정해보자. 방금 본인이 한 말처럼 품고 있는 형태라면 두 페이지에서 위의 코드가 나와야 한다. 결과를 한번 봐보자.
두 페이지에서 page2의 layout.js가 품어서 적용되는 것을 볼 수 있다.
위를 통해 Routing이 폴더 계층 구조를 따른다는 것을 알았다.
이제는 좀 더 어려운 버전을 배워보자.
우리가 프로젝트를 만든때 아래와 같은 페이지가 필요할 수 있다.
위 페이지는 유튜브에 한 영상을 틀었을때 나오는 페이지이다. 이를 위해선 동영상마다 페이지를 만드는 것이 아니라, 전체적인 틀을 가진 페이지를 하나 만들고 그 부분에서 영상이나 제목, 유튜버 등 유동적인 부분을 정해주면 된다.
이러한 라우팅은 어떤식으로 이루어질까?
이는 아래와 같이 주소에 유동적인 주소 부분을 넣는 것이다.
이를 통해 위와 같은 페이지 개발이 가능한데, 본인이 가져온 유튜브 영상 페이지의 주소를 자세히 봐보자.
www.youtube.com/watch
영상 id
이런식으로 페이지를 만들어서, 유튜브 영상에 대한 정보를 주소에 담아서 나타내고 있다.
이렇게 보내면 페이지 코드에서는 주소에 담겨 있는 영상 id를 알아차리고, 이를 데이터베이스에서 가져오면 우리가 앞써 고민한 페이지를 만들어 낼 수 있다.
어떻게 만들까?
이때 쓰는 방법이 Dynamic route
이다.
이를 구현하는 방법은 매우 간단하다.
위 사진처럼 유동적으로 바꿀 주소를 [이름] 으로 바꿔주면 된다. 대괄호 안에 있는 변수명은 이후에 이 값을 가져올때 사용되니, 의미있는 이름으로 하는 것을 추천한다.
그렇게 되면 주소를 입력할때 listName이라는 부분에 어떤 값이 들어간다면 이 페이지로 이동하게 된다.
이렇게 detail/어떤 값
을 만들어서 주소를 입력하면 listName이라는 변수에 어떤 값이 넣어진 상태로 그 페이지로 이동한다.
그렇다면 listName이라는 변수는 어떤식으로 받아올까? 그냥 사용하면 될까?
그것은 아니다. 전달방식은 props
의 형태로 전달해준다.
props의 params이라는 변수에 넣어서 값을 보내준다.
그래서 props.params.변수명
을 통해 주소에 입력된 값을 받아올 수 있다.
이를 통해서 예시로 보여준 유튜브 영상 같은 페이지를 만들어낼 수 있다.
그 영상에 대한 고유 id값을 받아와서 그 id를 통해 데이터베이스에서 제목, 영상, 유튜버 등 을 가져오면 되니깐 말이다.
아래는 이를 이용한 예시코드이다. MongoDB를 이용하여 고유한 id를 통해 값을 불러오는 코드이다. 상세히 볼 필요는 없고, 이런식으로 주소의 값을 보내서 유동적인 페이지를 만들어낼 수 있다 정도로 이해하면 좋을 것 같다.
import {connectDB} from "/util/database"
import { ObjectId } from "mongodb";
const Detail = async(props) =>{
const client = await connectDB;
const db = client.db("forum")
let result = await db.collection('post').findOne({_id:new ObjectId(props.params.listName)});
return(
<>
<h4>상세페이지</h4>
<h4>{result.title}</h4>
<p>{result.content}</p>
</>
)
}
export default Detail;
자 이제 라우팅 주소에 값을 넣어 보내는 방법, Dynamic Route
에 대해 알아보았다. 그러면 주소에 값을 넣어서 이동하는 방법이 필요하다. 그 방법에는 여러가지가 있는데 useRouter를 상당히 많이 쓴다.
이를 사용하는 방법은 간단한다.
예시 코드를 보면서 공부해보자.
'use client'
import { useRouter } from "next/navigation"
export default function DetailLink(props){
let router = useRouter();
return(
<>
<button onClick={()=>{router.push(`원하는 주소`)}}>
원하는 주소로 이동
</button>
</>
)
}
우선 useRouter
를 import해주어야 한다. 이후에 이에 대한 객체를 하나 만든다. 그리고 만들어준 useRouter의 객체를 통해 페이지를 이동해주면 된다.
이때 그냥 이동하는 것이 아니라 함수를 활용해야 한다.
- push
이는 인자로 페이지 주소를 넣어주면 그 페이지로 이동한다.- back
이는 이전 페이지로 이동한다.- forward
이는 앞 페이지로 이동한다.- refresh
이는 페이지를 새로고침 해준다.- prefetch
이는 다른 것보다 조금 어려운 내용인데, 주소에 해당하는 페이지를 미리 로드해주는 함수이다. 그래서 페이지로 이동할때 매우 빠르게 이동할 수 있게 해준다.
난 위의 함수 같은거 필요없고, 그냥 페이지 이동만 할꺼야 라고 한다면 Link태그를 쓰는 것도 추천한다.
사용 방법은 다음과 같다.
<Link href={'이동할 주소'}>클릭할 영역(컴포넌트)</Link>
이를 사용하면 client 컴포넌트로 하지 않아도 된다. 그래서 페이지 로딩을 좀 더 빠르게 할 수 있다는 장점이 있다.
여기에는 위에서 설명한 prefetch가 전부 적용되어있다. 이동하지 않을 페이지도 모두 로드해놓고 대기한다. 그래서 자원 낭비를 일으킬 수 있다.
이를 위해 Link태그는 이를 개선하기 위해 아래와 같이 prefetch
속성을 지원하고 있다. false
로 설정할 경우 미리 로드하는 것을 방지한다.
<Link href={'이동할 주소'} prefetch={false}>링크</Link>
이번 블로깅에선 프로젝트를 생성하고, 실행하는 것을 처음에 간단하게 배웠다. 이후에 생성된 중요파일 및 폴더가 하는 일에 대해 간략하게 알아보았다.
또한 이번 블로깅에서 핵심이라고 될 수 있는 라우팅에 대해 구조부터 차근차근 알아보았다. layout.js와 page.js의 이해도
와 라우팅의 구조가 폴더의 구조랑 동일하다는 점
이 이번 블로깅에서 핵심이라고 생각이 든다.
이 블로깅을 통해 이 부분에 대해 이해가 갔으면 좋겠다.