Next.js는 ‘리액트 프레임워크(React Framework)’ 입니다. React 기반으로 웹사이트를 만들거나 웹앱을 만들 때 간단하게 만들 수 있도록 필요한 기능들을 갖춰 놓은 도구죠.
한편 Headless CMS는 백엔드(backend) 부분만 있고 프론트엔드 부분은 없는 CMS를 말합니다. 예를 들어 워드프레스라면, 관리자 페이지만 있고 테마(theme)는 없는 상태를 말한다고 할 수 있겠죠. 이럴 경우 프론트엔드 부분은 (Headless) CMS에서 제공하는 API를 이용해서 직접 만드는 방식을 취하게 됩니다.
이 글에서는 Next.js를 가지고 이런 Headless CMS의 프론트엔드를 간단하게 한번 만들어 보기로 하겠습니다.
Next.js
Next.js는 리액트 기반의 프레임워크입니다. 리액트로 해야 할 일들을 미리 만들어 둬서 사용자가 좀 더 쉽게 쓸 수 있도록 했다고 보면 됩니다.
그 중 것들 몇몇만 뽑아보면 다음과 같습니다:
- 설정이 필요 없음. 별다른 설정 없이 바로 사용할 수 있습니다.
- 서버측 렌더링 지원. 서버측 렌더링 처리를 프레임워크 차원에서 지원합니다.
- 웹앱 및 정적 웹사이트 방식. 웹앱으로 만들 수도 있고 Jekyll과 같은 정적 웹사이트로 만들 수도 있습니다.
- 쉬운 배포. 배포가 아주 간단합니다. 특히 Now에 배포할 경우 간단하게 서버리스(serverless) 방식으로 처리할 수 있습니다.
프로젝트 시작하기
프로젝트는 다음과 같이 간단하게 시작할 수 있습니다. 프로젝트 디렉터리를 만들고 필요한 라이브러리를 패키지로 추가하면 됩니다.
$ mkdir nextjs-wp && cd nextjs-wp
$ yarn init -y
$ yarn add react react-dom next
$ mkdir pages
맨 마지막에서 pages 디렉터리를 추가한 것에 유의합니다. Next.js에서는 페이지 개념으로 라우팅(routing)을 관리하며 이때 이 pages 디렉터리 아래에 놓이는 React 컴포넌트들이 각각의 페이지를 구성하는 관례를 따릅니다.
그런 다음 package.json 파일을 열어 다음 NPM 스크립트를 추가합니다.
{
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
}
이제 명령행에서 yarn dev
명령을 입력하면 개발 환경에서 서버가 실행됩니다.
별다른 문제 없다면 http://localhost:3000 으로 접속하여 작동을 확인할 수 있을 것입니다.
404 오류 페이지가 뜬다구요? 맞습니다. 아직 아무런 페이지도 만들지 않았기에 당연합니다.
Index 페이지 만들기
그럼 이제 페이지를 하나 만들어 볼까요? 방금 전 만든 pages 디렉터리 아래에 index.js 파일을 하나 만들고 그 내용을 다음과 같이 입력합니다.
const Index = () =>
<h1>Hello, Next.js!</h1>
export default Index
익숙한가요? 맞습니다. 그야말로 그냥 React 컴포넌트입니다. 여기서는 무상태(stateless) 함수로 간단하게 만들었습니다.
이제 확인하면 브라우저에 Hello, Next.js!가 출력 될 것입니다.
REST API로부터 데이터 가져오기
여기서는 워드프레스(WordPress)를 Headless CMS로 사용하기로 하겠습니다. 워드프레스 한국어 사이트에서 REST API로 포스트 목록을 가져와 Index 페이지에 출력해 보기로 합니다.
import Link from 'next/link'
import fetch from 'cross-fetch'
const Index = ({ posts }) => (
<ul>
{posts.map(({ id, title }) =>
<li key={id}>
<Link href={`/post?id=${id}`}><a>{title.rendered}</a></Link>
</li>
)}
</ul>
)
Index.getInitialProps = async ({ req }) => {
const res = await fetch('https://ko.wordpress.org/wp-json/wp/v2/posts')
const data = await res.json()
return {posts: data}
}
export default Index
코드는 간단합니다. 우선 Link를 처리하기 위해 Next.js에서 제공하는 next/link
패키지를 추가했습니다. 여기서 Link 컴포넌트는 리액트 프로젝트에서 흔히 사용하는 React Router 라이브러리와 유사한 기능을 수행하는 Next.js의 컴포넌트입니다.
또 fetch 처리를 위해 cross-fetch 라이브러리를 추가했습니다.
그런 다음 Next.js에서 제공하는 콜백함수인 getInitialProps()
함수에서 REST API를 호출하여 결과값을 받습니다. 함수 이름에서도 알 수 있듯 이 함수의 반환값은 컴포넌트 프로퍼티(props)의 초기값으로 지정됩니다.
한 가지 유의할 점은 이 때 getInitialProps()
함수의 동작입니다. 이 함수는 브라우저의 첫 번째 호출 시점에서는 서버측에서 호출되고 이어지는 히스토리(history) 라우팅 시에는 클라이언트측에서 호출됩니다. 따라서 맨 처음 브라우저에 URL을 입력하거나 브라우저를 갱신(refresh)한 경우에는 서버측에서 fetch가 이뤄져서 렌더링된 결과값이 클라이언트측으로 전달됩니다.
Link 컴포넌트 속 href 속성 값도 유의합니다. 여기서 지정한 경로(path)가 나중에 포스트 페이지를 만들어 호출할 때 대응되는 경로가 됩니다.
결과는 아래와 같습니다. 워드프레스 한국어 사이트에서 최신글 목록을 가져와서 제목을 보여주고 있습니다.
데이터 가져오기에 관한 더 자세한 내용은 Getting Started – Next.js Documentation 문서를 참조 바랍니다.
Single Post 페이지 만들기
이번엔 목록에서 제목을 클릭하면 개별 포스트가 출력되도록 Post 페이지를 한번 만들어 보기로 하겠습니다. 원리는 앞서 Index 페이지 때와 똑같습니다. pages 디렉터리 아래에 이번엔 post.js 파일을 하나 만듭니다. 내용은 다음과 같습니다.
import { withRouter } from 'next/router'
import fetch from 'cross-fetch'
const Post = withRouter(({ post }) => (
<article>
<h1>{post.title.rendered}</h1>
<div className="body" dangerouslySetInnerHTML={{__html: post.content.rendered}} />
</article>
))
Post.getInitialProps = async ({ query }) => {
const res = await fetch('https://ko.wordpress.org/wp-json/wp/v2/posts/'+query.id)
const data = await res.json()
return {post: data}
}
export default Post
마찬가지로 여기서도 getInitialProps()
함수에서 데이터를 추출합니다.
한편 여기서는 Next.js에서 제공하는 withRouter라는 고차함수(HoC)를 사용하고 있는데, 이는 앞선 목록 페이지의 Link로부터 쿼리값을 전달받기 위함입니다. 여기서는 포스트의 id값을 쿼리로 받아 그 값으로 REST API를 호출하는 방식을 취했습니다.
이제 목록에서 제목을 클릭하면 해당 제목의 포스트 내용이 화면에 표시되는 것을 확인할 수 있을 것입니다.
Next.js의 기능을 이보다 더 많이 있지만 기본적인 개념은 얼추 다룬 듯해서 다음은 배포(deployment)로 넘어 가겠습니다.
사이트 배포하기
사이트 배포 역시 통상적인 React 앱 혹은 자바스크립트 앱의 배포 방식과 전혀 다를 게 없습니다.
하지만 여기서는 Next.js 개발사인 ZEIT에서 제공하는 서버리스(serverless) 클라우드 서비스인 Now에 한번 배포 해 보기로 하겠습니다.
Now는 자바스크립트 앱이나 정적인(static) 웹사이트를 클릭 몇 번으로 간단하게 클라우드에 배포할 수 있게 만들어주는 클라우드 호스팅 서비스입니다. Now를 이용하면, 사이트에서 소개하는 것처럼, 앱이나 웹사이트를 간단하게 배포하고 CDN이나 DNS 처리도 간편하게 할 수 있습니다.
(Now 서비스에 가입하고 프로그램(데스크톱 버전 또는 CLI 프로그램)을 내려 받아 설치하는 부분은 생략합니다.)
배포를 위해 다음과 같이 설정 파일(now.json
)을 하나 프로젝트의 루트 디렉터리에 추가합니다.
{
"version": 2,
"builds": [
{ "src": "package.json", "use": "@now/next" }
]
}
이 설정 파일은 Now에 프로그램을 배포할 때 빌드하는 방법을 지시하는 설정입니다. 설정값에 관한 더 자세한 내용은 Deployment Configuration (now.json) – ZEIT Documentation 문서를 참조하세요!
이제 명령행에서 now
명령을 내립니다. 프로그램(사이트)을 Now에 배포하라는 명령입니다.
$ now
> Deploying ~/hacks/nextjs-wp under my-account
> https://nextjs-wp-5fsg0yk73.now.sh [v2] [in clipboard] [4s]
> Success! Deployment ready [4s]
잠시 기다리면 성공적으로 배포가 완료되었다는 메시지가 나옵니다. 이걸로 끝입니다. 이제 화면에 표시된 URL로 접속해 확인해 보면 서비스가 잘 배포되어 작동되고 있는 것을 확인할 수 있을 것입니다.
마무리
웹에서 프론트엔드의 비중이 커지고 CMS는 그야말로 콘텐츠 관리 본연의 기능에만 충실한 Headless 방식을 취하는 게 최근의 트렌드죠. 그에 따라 CMS도 headless 방식의 CMS 솔루션이나 서비스들이 많이 등장하고 있구요.
React나 Vue 또는 오늘 소개한 Next.js 같은 도구들은 Headless CMS와 함께 사용할 경우 아주 간단하면서도 유연하게 원하는 결과를 만들어 낼 수 있는 좋은 솔루션이 되지 않을까 생각합니다.
※ 지면 관계상 소개하지 못한 나머지 코드 부분은 GitHub에 올려 두었으니 관심있는 분들은 참고 바랍니다.