[Next.js 2.0] 간단한 React 전용 서버사이드 프레임워크, 기초부터 본격적으로 파보기


Next.js, 작년부터 존재함을 인지해왔고, 뭔가 멋지다는것도 알고있었지만 그 동안 딱히 필요성을 못 느껴서 오랫동안, 아주 오랫동안 미뤄왔습니다. (이 포스트를 보시는 여러분들중 일부도 그러하리라 생각합니다) 그런데 어느새 벌써 2.0이 런칭되었고 왠지 사용을 해봐야 할 것 같습니다.

Next.js 란 무엇인가?

독자 여러분의 일분는 이미 익숙하시겠지만, 모르시는 분들을 위하여 설명을 드리자면 Universal 리액트 어플리케이션의 서버렌더링을 쉽게 구현 할 수 있게 도와주는 간단한 프레임워크입니다.

자, Universal 이란, 어디서든 작동한다는 의미입니다.서버에서 미리 사전 작업을 좀 해놓고, 클라이언트에게 던져줘서, 클라이언트가 좀 더 작업을하게 하고, 양쪽에서 코드와 뷰를 공유합니다.

서버사이드 렌더링을 한다는게, 그렇게 복잡한 컨셉은 아니지만, 실제로 구현하려고 하면 복잡함이 밀려옵니다. 특히나, 그냥 렌더링하는것까지는 쉽지만 비동기 작업 처리를 어떻게 할까.. 생각하다보면 힘들어집니다. 거기에 라우터도 붙고 하면.. 음… 초반에 삽질이 좀 많이 필요합니다. (적어도 저는 그렇게 느꼈습니다)

Next.js 는 이 작업을 한 명령어로 해결해줍니다.

“아니 뭐 보일러플레이트 사용하면 되는거 아닌가요?”

보일러플레이트는.. 사실 상 보일러플레이트 메인테이너가 이런식으로 구현 할 수 있다 라고 참고하라고 하는 의미가 크다고 생각합니다. 실제로 실무에서 사용하려고 하면 불필요한 것들이 딸려와서 골치아플수도있습니다.

거기에 보일러 플레이트 파일구조는? 좀 복잡할 확률이 큽니다. 많은 분들이 이미 알고있겠지만 리액트 프로젝트의 파일 구조는 개발자마다 다 다릅니다 – (저도 저만의 구조화 방식이 있습니다. 다른 개발자의 프로젝트 구조가 괜찮아 보인다고 느껴져도, 경험이 쌓여 이뤄진 구조이기 때문에 쉽게 걷어낼 수 없습니다. 아, 그리고 이미 프로젝트가 진행되면 당연히 구조 바꾸기 힘들겠죠)

여하튼, Next.js 는 이 작업을 쉽게해줍니다. 심지어 Next.js 를 익히는것은 그리 어렵지 않습니다. 전체 README 를 10~20분내에 읽을수 있을정도입니다.

게다가 코드 스플릿팅도 자동으로, 아주 간단하게 할 수 있게 해줍니다.

“코드 스플릿팅이 뭐죠?”

일반적인 SPA 는 한 파일로 결합되어있어서 예를들어 그저 About 페이지를 보고싶었을 뿐인데도 페이지를 로딩하면서 설정 페이지에 대한 정보도 다운로드하게됩니다. 이 부분을, 새 페이지 불러오지 않으면서 유동적으로 해결해주는것이 바로 코드스플릿팅입니다. 참고로 Webpack2 이후로는 이 작업은 꽤 쉬워졌습니다.

“이거 create-react-app 같은건가요?”

맞다고 할 수도 있고 아니라고 할 수도 있겠군요. 일단 둘 다 여러분들을 편하게 해주지만 create-react-app 은 프로젝트 구조를 아주 자유롭게 설정할수있지만, Next.js 는 서버사이드 렌더링과 자동화된 코드스플리팅을 위하여 약간의 정형화된 구조를 따라야합니다 (각 라우트에 해당하는 파일들은 소문자로 pages 디렉토리에 넣어야해요. 나머지는 자유입니다).

기능을 좀 더 살펴보자면,

  • 간단한 클라이언트 사이드 라우팅
  • Hot Module Replacement를 지원하는 Webpack 기반 작업환경
  • Express 나 그 어떤 Node.js 서버와 함께 사용 가능
  • Babel / Webpack 환경설정 커스터마이징 가능
  • 등등..

정도가 있겠군요.

 

자.. 궁금하죠? 한번 시작해봅시다!

앞으로 사용되는 코드는 https://github.com/vlpt-playground/nextjs-tutorial-basic 에서 열람 할 수있습니다.

 

환경 설정

Next.js 는 Mac, Windows, Linux 에서 동일하게 작동합니다. 여러분들에게 필요한건 Node.js 만 있으면 돼요.

하지만 이 포스트에서는 bash 터미널을 Git Bash Simulator 를 사용하는것을 권고합니다.

추가적으로 이 포스트에서는 npm 대신 yarn 을 사용할것입니다. yarn 이 없으신분들은 설치를 하시거나, npm 을 사용하셔도 무방합니다. 하지만, yarn 은 훨씬 빠릅니다!

mkdir hello-next
cd hello-next
yarn init -y
yarn add react react-dom next
mkdir pages

 

그 다음, package.json 파일을 열어 다음과 같이 스크립트를 추가해주세요.

그 다음, pages 디렉토리 내부에 저희의 첫 페이지를 만들어볼게요.

 

index.js 파일을 만들어서 다음과 같이 코드를 작성해보세요.

코드에서 import React from 'react'; 를 할 필요 없습니다. 함수형 컴포넌트가 아닌 클래스 형 컴포넌트도 동일한데요, 만약에 여러분들이 class Index extends Component 와 같은 형식으로 하는걸 선호한다면, import { Component } from 'react';  는 선언 해주어야합니다. 혹은 class Index extends React.Component 로 작성해도 되겠구요.

그 다음, 이 명령어를 통해 개발 서버를 실행해봅시다:

yarn run dev

와우! 벌써 끝났어요! 파일을 수정해보세요. 바로바로 업데이트 되죠? Hot Module Replacement 가 적용되어있어서 파일을 수정해도 페이지를 리로딩하지 않고 변화를 적용해줍니다.

 

페이지 라우팅

Next.js 는 라우터를 내장하고 있어요. 여러 페이지를 만들어서 라우팅 하는 방법을 알아볼게요.

우선 pages 디렉토리에 about.js 파일을 생성해서 다음과 같이 컴포넌트를 만드세요.

그 다음에 Index 페이지에서 링크를 달아줄게요.

주의 하실 점은 <Link> 컴포넌트를 사용 할 때, react-router 에선 to 를 썼겠지만 여기선 href 라는 점과, 이 컴포넌트 내부에 문자열이 아닌 컴포넌트 혹은 엘리먼트가 있어야합니다.

추가적으로 스타일링을 하실땐, 그 내부의 엘리먼트에 style 혹은 className 을 설정하면 되겠습니다. 꼭 a 태그가 아니여도 되구요, 만약에 컴포넌트를 넣으신다면 해당 컴포넌트가 onClick props 를 전달받아서 실행하도록 해야해요.

여기까지 저장하고나면, 클라이언트 사이드 라우팅도 정상적으로 작동할겁니다.

 

한번 스타일링 예제도 살펴볼까요?

 

공용 컴포넌트 만들기

자 이제 페이지 이외의 컴포넌트들도 만들어봅시다.

각 페이지에서 보여지는 헤더 컴포넌트를 만들어볼까요?

components 디렉토리를 만들어서 내부에 Header.js 라는 파일을 만드시고 다음과 같이 코드를 입력하세요.

그 다음엔, about 페이지, index 페이지 각각 페이지에서 Header.js 를 불러온다음에 상단에 넣으면 되겠습니다.

페이지 관련 컴포넌트들은 꼭 pages/ 디렉토리에 넣어야 하는 반면, 다른것들은 꼭 components 디렉토리에 넣지 않아도, 상대 경로를 사용하여 불러올 수 있습니다.

 

근데… 헤더 컴포넌트가 필요한 모든 페이지마다 다 불러오는건 솔직히 좀 구리잖아요? 공용으로 불러올 컴포넌트가 얼마나 될 지 모르는데..

그러므로, Layout 이란 컴포넌트를 만들어서 이를 좀 더 제대로 해결해봅시다.

 

components 디렉토리에 Layout.js 파일을 만들어서 다음과 같이 입력해주세요 .

다음 작업은 좀 예상 되시죠?

 

기존에 index.js 와 about.js 에서 Header 를 지워주고 Layout 을 불러와서 감싸주겠습니다.

그래.. 이런식으로 해야 좀 말이 되죠.

 

라우트 – 유동적인 주소 사용하기

라우트를 static 한 라우트만 다룰게아니라, 좀 더 깊게 파봅시다.

쿼리 파라미터

쿼리 파라미터는 /search?keyword=something 의 형태입니다.

생각보다 쉬워요, props 의 url.query.keyword 를 읽어주면됩니다.

 

pages 디렉토리에 search.js 파일을 만들어서 다음과 같이 적어보세요:

직접 주소를 쳐서 keyword 값을 설정해보세요:

* url props 를 보면 push 메소드도 딸려서 옵니다. 이 메소드를 사용하여 원하는곳으로 라우팅을 할 수 있어요.

Pathname 파라미터

pathname 파라미터는 /post/:id 에서 id 부분을 칭하는데요. Next.js 에서는 이걸 하려면 커스텀 서버 API 를 사용해야합니다.  그리 복잡하지는 않지만 포스트가 길어질것같아서 다음에 다뤄보도록 하겠습니다 🙂

이게 쉽게 자체적으로는 안되는 이유는 Next.js 에서 서버렌더링을 하기 때문에 구조상 /post/13 이런식으로 요청이 들어오면 pages/post/13.js 파일을 렌더링하려고 시도하기 때문이에요.

 

 

외부 데이터 가져오기

솔직히 여기가 가장 기대되는 부분이자 중요한 부분 아닐까요? 이걸 해보기 위해서 여기까지 달려왔습니다 – (?)

Next.js 에선 이 부분을 클래스형 컴포넌트에 getInitialProps 라는 메소드를 설정하여 해결합니다. 이 메소드가 서버사이드에서도 실행될수있고, 클라이언트 사이드에서도 실행될수있는거죠. 심지어 필요에 따라 prefetch 도 될 수 있습니다. 아주 유용하죠!

일단 기본 사용법부터 알아볼까요?

사용법 살펴보기

pages 에 ssr-test.js 라는 페이지를 만드세요.

getInitialProps 에서 실행한 메소드에서 리턴하는 값이 해당 컴포넌트의  props 로 전달된답니다.

파라미터로는 서버측의 req 가 들어갑니다.그렇기에 client 에서의 req 는 undefined 이겠죠.

 

그 다음엔 헤더 컴포넌트에 SSR 테스트 링크를 추가하세요

한번 테스팅을 해볼까요?

주소로 직접 들어가면 server 에서 실행했다고 하지만 링크 컴포넌트를 눌러서 들어가면 client 에서 실행을 했다고 하죠.

 

자, 이제 기본적인 사용법을 이해했으니 진짜로 데이터를 fetching 해봅시다.

테스트용으로 제공되는 REST API 인 JSONPlaceholder 를 사용해볼게요. 여기 API 들 중에서 https://jsonplaceholder.typicode.com/users 를 사용하겠습니다 (10명의 유저정보를 불러오는 API 입니다)

axios 를 통한 ajax 요청

우리는 axios 를 사용하여 ajax 요청을 하도록 하겠습니다.

우선 axios 를 설치하세요.

yarn add axios

그 다음에, ssr-test.js 파일을 수정하세요.

결과를 살펴볼까요?

오호.. 아주 잘 됩니다.

prefetch 기능 사용하기

여기서 더 멋진 기능은 prefetch 기능인 것 같습니다.

이건 Link 컴포넌트를 사용해서 이뤄지는건데요. 링크 컴포넌트를 렌더링할때 <Link prefetch href="..."> 형식으로 prefetch 값을 전달해주면 데이터를 먼저 불러온다음에 라우팅을 시작합니다.

Header.js 컴포넌트를 다음과 같이 수정해보세요:

이렇게하면 링크를 누를때 prefetching 이 됩니다.

 

Head 설정

Next.js 내부에 react-helmet 같은 녀석이 내장되어있습니다. 한번 사용해볼까요?

index.js 페이지에 “안녕하세요” 라는 페이지 제목을 달아보겠습니다.

어때요? 꽤나 직관적이죠?

 

만약에 여러페이지에서 공통적으로 사용하는 헤더를 설정할 경우에는 pages 디렉토리에 _document.js 파일을 만든 뒤 다음과 같이 작성하시면 됩니다:

이렇게 될 경우엔 이 값을 기본적으로 설정하고 Head 컴포넌트가 사용된 페이지의 경우엔 이 기본값 위에 덮어씌웁니다. (상단에 flush 가 있는 이유는 스타일링 섹션에서 다시 다룰것입니다)

 

 

설정 커스터마이징 하기

소개부분에서도 잠깐 설명했었지만, webpack 및 babel 설정을 커스터마이징 할 수 있습니다.

webpack의 경우엔 최상위 디렉토리에 다음과 같이 next.config.js 파일을 만들어서 설정하시면 됩니다:

매뉴얼에 따르면 불러올수있는 파일형식을 추가하기 위해서 loader를 추가하는건 좋지 않다고 합니다. 그 이유는 클라이언트쪽 코드만 웹팩으로 번들되기 때문에 서버사이드렌더링에서 잘 작동하지 않기 때문이라는데요, 그 대신 바벨 플러그인을 사용하라고 권고합니다. 예를들어 svg 파일을 불러오기 위해서 babel-plugin-inline-react-svg 를 사용한다던지 말이죠.

바벨 설정도 최상위 디렉토리에 .babelrc 파일을 만드셔서 설정하면 되겠습니다:

그럼 스타일링은 어떻게?

스타일링에 있어선 가장 추천하는 방식은 styled-jsx 를 사용하는것입니다. JS 내부에 CSS를 작성 할 수 있게 해주는 모듈인데요, 이것 또한 내장되어있기때문에 추가적으로 불러올필요가 없습니다. 한번 사용해볼까요?

잘 되는군요. 아까 _document.js 파일에서 flush() 했던거, 기억 나시나요? 우선 styled-jsx 는 기본적으로 클라이언트 사이드에서 스타일링이 이뤄집니다. flush() 함수는 이 스타일링 작업을 서버사이드에서 사전작업을 해주는데요, 서버사이드에서 꼭 실행하지 않아도 클라이언트 쪽에서 컴포넌트가 불러와지면서 스타일링을 하기 때문에 상관은 없지만 클라이언트가 스타일을 만들기위해 필요한 연산을 아껴줍니다. 결국 더 빠른 페이지로딩을 할 수 있게 되겠죠.

아! 그리고 styled-components 를 선호하신다면 이 페이지를 참고해주세요.

저의 경우엔 SASS 를 사용하는걸 선호하는데, 아무래도 나중에 사용하게된다면 아예 sass 파일을 빌드한다음에 _document.js 에서 불러와서 사용하게 될 것 같군요.

 

Deployment

프로젝트를 완성하고 릴리즈 할때는 또 다른 명령어를 사용합니다. 그리고 Next.js 개발사 zeit 에서는 쉽게 프로젝트를 올릴 수 있는 now 라는 서비스도 제공해주는데요, 한번 사용하는 방법을 알아보겠습니다.

우선 now 를 글로벌 모듈로 설치하세요.

yarn global add now

 

package.json 파일에 다음과 같이 스크립트를 추가해주세요.

주의: “main”: “index.js” 가 있다면 지워주세요.

만약에 여러분들이 프로덕션 서버를 실행 하실 땐 yarn run build -> yarn run start 를 하시면 되겠습니다.

 

이 프로젝트를 now 에 deploy 하기위해선 now 명령어를 입력하세요.

(가장 처음 실행할땐 이메일 인증 과정이 필요합니다)

 

https://hello-next-rtiolxoqyw.now.sh 링크가 생성되었습니다! 들어가면 우리가 만든 프로젝트를 볼 수 있습니다.

now 는 매달에 20회까지 무료로 deploy 할 수 있으며 각 파일당 1MB 가 넘지 않아야됩니다.

도메인의 경우엔 무료일경우엔 alias 만 설정할수있는데요, 다음과 같이 실행하시면 됩니다.

이제 https://hello-next.now.sh/ 이 주소로 들어갈 수 있게됩니다!

 

유료 플랜을 사용하면 여러 제한이 풀리게 되는데, 여기를 참조해보세요.

 

마무리

 

생각보다 괜찮은 프레임워크이군요. 리액트로 서버사이드 렌더링을 하며 코드 스플릿팅을 하는게, Next 를 사용하지 않아도 충분히 가능한 작업이긴 하지만 해당 환경에 react-router, react-hot-loader, redux 등 까지 적용하다보면 적당한 가이드 예시가 없다면 초반에 삽질이 무수히, 무수히 필요합니다. 반면 이 Next.js 는 이 모든작업을 매우매우 간단하게 해결해주고 심지어 실용성까지 있으니.. 굉장한 프레임워크인것같습니다.

하지만 기존 프로젝트에 이를 도입한다면, 구조상 다른점들이 좀 있어서 조금 시간이 걸릴 것 같습니다 – react-router 를 걷어내야하니까요.

코드 스플리팅과 서버사이드 렌더링을 어중간하게 하느니 이걸로 하는게 낫겠다 싶은 생각이 드는군요.

이번 포스트에서 아직 다루지 않은 내용들은 https://github.com/zeit/next.js/tree/master/examples 여기를 참조해보세요.

 

조만간 올라올 포스트에서는 Next.js 에서 커스텀 서버 사용하기 및 Redux 적용하는 방법에 대해서 다뤄보겠습니다.

 

Reference

  • 권형주

    와대박 잘보고 갑니다. 다음 포스팅이 기다려져요!

  • ghost_plan

    React 초보잡니다.
    먼저 좋은 강의를 줄기차게 이어가시는 velopert 님께 감사의 인사드립니다.
    velopert 님께서 “저만의 구조화 방식 “이 있다고 하셨는데 그 React 프로젝트 구조에 대해 알고 싶습니다.

  • deprecated w

    안녕하세요
    react component의
    render()
    return (
    )

    이부분에서
    render과 return 사이에 this.props.something은 사용 불가능한가요?

    이런식으루요
    render()
    const a = this.props.something
    return (
    {a}
    )

    오류는 안뜨느데 안돼서 ㅠㅠ 헷갈복잡하네요 답변부탁드릴게요 ㅠㅠ

  • Pete Kim

    진짜 선구자이신가요? 궁서체입니다. 정말 감사합니다-

  • DaekyuLee

    항상 유용한 강좌 감사드립니다. 많이 배우고 있습니다.
    이번에 next.js 를 활용해서 프로젝트 진행중에 있는데 redux 적용하는 방법 기대 하고 있습니다 ㅎㅎ
    행복하세요~

  • Pete Kim

    아.. 저도 next.js에 redux 적용하는 방법을 기대하고 있습니다. 화이팅하겠습니다. =)

  • Hyeungshik Jung

    와 README 보다 훨씬 낫네요. 감사합니다!!

  • madnomad

    이어지는 연재 기대하고 있을게요!

  • kekeke

    궁금한점이 있어 글 남깁니다.
    ‘https://jsonplaceholder.typicode.com/users’ 로 통신하는 예제에서 render부분에 콘솔로 console.log(“this.props.users”, this.props.users)를 찍어보면 서버를 실행하는 터미널창과 브라우저 콘솔창 두곳에 다 찍히게 됩니다.
    그 이유가 궁금합니다.

  • Benjamin Park

    설명이 이해하기 매우 쉬웠습니다. 간단 명료하게 설명해주셔서 감사합니다.
    SSR 해볼만 한 것 같다. 결국 리액트를 잘해야 한다. 그리고 리액트 보다 javascript를 잘해야 한다. 는 것을 많이 느낍니다ㅎㅎ