안녕하세요. IT 엘도라도 에 오신 것을 환영합니다.
글을 쓰는 것은 귀찮지만 다시 찾아보는 것은 더 귀찮습니다.
완전한 나만의 것으로 만들기 위해 지식을 차곡차곡 저장해 보아요.   포스팅 둘러보기 ▼

회사 프로젝트 작업 일지

[오픈갤러리] Next.js, Django REST Framework 환경 구축 (feat. 기술 스택 전환)

피그브라더 2022. 1. 25. 20:06

목차
⦁ 구체적인 기술 스택 선택 (Next.js, Django REST Framework)
⦁ Next.js 프로젝트 생성 및 관련 패키지 설치
⦁ Next.js 프로젝트 Docker 세팅 (로컬 환경)
⦁ Next.js 프로젝트 Docker 세팅 (배포 환경)

⦁ Django REST Framework 세팅
⦁ 기존 인증 기능과의 호환성 확보

 

▎구체적인 기술 스택 선택 (Next.js, Django REST Framework)

모든 준비가 완료되었으니, 이제 React로 기술 스택을 전환할 때다. 우리는 프론트 엔드 기술 스택으로 React를 선택하였다. 그 이유에 대해서는 이 포스팅에서 설명하였다. 그런데 React를 선택했다고 끝이 아니다. 순수한 React로 개발하면 검색 엔진 최적화(SEO)에 아주 불리하기 때문에, 서버 사이드 렌더링을 위한 도구를 선택해야 했다. 선택지는 크게 두 가지였는데, 하나는 최근에 출시된 React 18의 기능을 이용하는 것이고, 다른 하나는 React 기반의 Next.js 프레임워크를 이용하는 것이다.

 

 

결론적으로 우리는 Next.js를 선택하였다. 물론 최근에 React 18도 반응이 아주 뜨겁지만, 지금 당장 기술 스택을 전환해야 하는 우리로서는 아무래도 너무 최근에 출시되어 커뮤니티가 부족할지도 모르는 React 18을 이용하기에 약간 부담스러웠다. 또한 Next.js도 원한다면 React 18과 함께 사용할 수 있도록 지원하고 있고, Next.js 자체의 인기가 상당하는 점도 선택의 이유 중 하나이다. 그리고 일단 기술 스택 전환을 리드하게 될 필자가 이미 Next.js에 익숙한 상태였다는 점도 무시하지 못할 이유였다.

 

다음으로, 최근 웹 개발 시장에서 가장 핫한 TypeScript를 도입할 것인가에 대해서도 고민하였다. 결론적으로, 처음에는 우선 TypeScript를 사용하지 않기로 하였다. 사실 필자는 TypeScript를 좋아해서 사용하고 싶었지만, 새롭게 도입할 기술인 React, Next.js, Django REST Framework를 전부 단시간에 빠르게 익혀야 하는 동료 개발자분들의 입장에서 TypeScript까지 익히는 건 너무 부담이 크다고 판단했다. 따라서 TypeScript는 동료 개발자분들이 새롭게 도입하는 기술들에 어느 정도 익숙해지고 나면 점진적으로 도입하기로 하였다.

 

또한, 코드 문법 검사 및 코드 포맷팅을 위한 도구로는 ESLint와 Prettier를 모두 사용하기로 하였다. 개인적으로 필자는 '예쁜 코드'를 아주 중요시하기도 하고, 현재 회사의 문화 자체가 그렇기도 해서 이는 필연적인 선택이었다. ESLint와 Prettier를 동시에 적용하는 방법에 대해서는 이 포스팅을 읽어보자.

 

 

마지막으로, 백 엔드에서 새롭게 개발할 API들에는 Django REST Framework(이하 DRF)를 이용하기로 하였다. 만약 백 엔드 프레임워크 자체를 Django가 아닌 다른 걸로 바꾼다면 일이 너무 커지기 때문에, 우선은 Django를 그대로 사용하되 이제부터는 진정한 의미의 '백 엔드'를 구현하기 위해 DRF를 선택한 것이다. 이로 인해 기존에 편하게 API들을 구현할 때와는 달리 REST API 컨벤션 등을 조금 더 많이 신경 쓰게 될 것이다. 기본적으로 DRF는 Django의 부속품이기 때문에 Django에 익숙하면 금방 배울 수 있다는 장점도 있다.

 

 

참고로 DRF에서 사용할 인증(Authentication) 방식으로는 세션 기반 인증(SessionAuthentication)을 선택하였다. 기존에 Django 풀 스택으로 개발하던 서비스에서 세션 기반 인증을 사용하고 있었기 때문에, 인증에 대한 무리한 추가 개발 없이 점진적인 기술 스택 전환을 이뤄내려면 이러한 선택이 옳다고 판단하였다.


▎Next.js 프로젝트 생성 및 관련 패키지 설치

먼저, 다음과 같이 create-next-app 패키지를 이용하여 Next.js 프로젝트를 생성하였다. (프로젝트명 : nextjs)

npx create-next-app@latest

그리고 .gitignore 파일에 다음 내용을 추가하였다. (Node.js 프로젝트 및 Next.js 관련 파일)

# Environment Variables
.env*

# Node.js Dependencies
node_modules/

# Next.js
.next/

# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Vercel
.vercel

다음으로, 우리는 CSS 대신 Sass를 사용할 것이므로 다음과 같이 sass 패키지를 설치하였다.

npm install sass

마지막으로 코드 문법 검사 및 코드 포맷팅을 위한 환경을 구축하기 위해 ESLint 및 Prettier의 설치 및 설정을 진행하였다(IDE 설정 포함). 구체적인 설치 및 설정 방법은 이 포스팅을 읽어보자.


▎Next.js 프로젝트 Docker 세팅 (로컬 환경)

로컬 환경 전용 Dockerfile 파일은 다음과 같이 작성하였다. Next.js 프로젝트의 소스 코드를 Next.js 컨테이너로 옮기고, Node.js 패키지들을 설치하고, 개발 환경을 위한 Next.js 서버를 실행하도록 설정하였다.

FROM node:16

# Copy Source Codes
COPY . /nextjs
WORKDIR /nextjs

# Install Node.js Packages
RUN npm install

# Run Front-end Server (for Development)
CMD ["npm", "run", "dev"]

COPY 명령어를 수행할 때 제외할 대상은 다음과 같이 .dockerignore 파일을 이용하여 명시하였다.

.next
node_modules

마지막으로, 로컬 환경 전용 docker-compose 설정 파일에 Next.js 컨테이너에 대한 설정을 작성하였다. 호스트 포트와 컨테이너 포트를 맵핑하고, Next.js 서버의 자동 리로드를 위해 볼륨을 마운트 하도록 설정하였다. 단, 볼륨 연결 시에는 .next 폴더와 node_modules 폴더를 제외해주도록 하였다. 다음을 참고하자.

nextjs:
  build:
    context: ./nextjs
    dockerfile: ./Dockerfile.local
    image: nextjs-image
    container_name: nextjs-container
    ports:
      - "3000:3000"
    volumes:
      - ./nextjs:/nextjs
      - /nextjs/.next
      - /nextjs/node_modules

참고로 Windows 10 환경 기준, Next.js 컨테이너의 볼륨 맵핑이 정상적으로 이뤄지고 호스트에서 소스 코드를 수정하더라도 Next.js 서버가 자동으로 리로드 되지 않는 문제가 있었다. 이는 next.config.js 파일에 다음과 같은 설정을 작성해줌으로써 해결할 수 있었다.

module.exports = {
  reactStrictMode: true,
  webpackDevMiddleware: (config) => {
    config.watchOptions = {
      poll: 1000,
      aggregateTimeout: 300
    };
    return config;
  }
};

▎Next.js 프로젝트 Docker 세팅 (배포 환경)

배포 환경 전용 Dockerfile 파일은 다음과 같이 작성하였다. Next.js 프로젝트의 소스 코드를 Next.js 컨테이너로 옮기고, Node.js 패키지들을 (배포 환경에 필요한 것만) 설치하고, 번들링을 위한 빌드를 실행하도록 설정하였다.

FROM node:16

# Copy Source Codes
COPY . /nextjs
WORKDIR /nextjs

# Install Node.js Packages
RUN npm install --production

# Bundle Next.js Application
RUN npm run build

# Copy Entrypoint Script
COPY docker-entrypoint.production.sh /docker-entrypoint.production.sh
RUN chmod +x /docker-entrypoint.production.sh

다음으로, 배포 환경 전용 docker-compose 설정 파일에 Next.js 컨테이너에 대한 설정을 작성하였다. ECR에 Next.js 리포지토리를 만들고, 이미지의 이름을 해당 리포지토리의 경로로 지정하였다. 다음을 참고하자.

nextjs:
  build:
    context: ./nextjs
    dockerfile: ./Dockerfile.production
  image: ECR 리포지토리의 경로
  container_name: nextjs-container
  entrypoint:
    - /docker-entrypoint.production.sh

그리고 docker-entrypoint.production.sh 파일에는 다음과 같이 배포 환경을 위한 Next.js 서버를 실행하는 명령어를 작성하였다.

#!/bin/bash

# Run Front-end Server
npm run start

또한, Nginx의 설정 파일도 수정해줄 필요가 있었다. 점진적인 적용을 위해서는 특정 prefix로 시작하는 URL에 대한 요청을 Next.js 컨테이너로 포워딩해줘야 하기 때문이다. 또한 _next로 시작하는 URL에 대한 요청도 마찬가지로 처리하였다. 다음을 참고하자.

upstream nextjs {
  ip_hash;
  server nextjs:3000;
}

server {
  # ... 다른 설정 ...

  location /prefix {
    proxy_pass http://nextjs;
    # ... proxy 관련 설정 ...
  }

  location /_next {
    proxy_pass http://nextjs;
    # ... proxy 관련 설정 ...
  }
  
  # .. 다른 설정 ...
}

마지막으로, ECS 파라미터 파일의 내용을 수정하여 Next.js 컨테이너의 메모리 제한도 적절히 설정해줬다.


▎Django REST Framework 세팅

DRF를 사용하기 위해 다음과 같이 두 개의 Python 패키지를 requirements 파일에 추가하였다.

djangorestframework==3.13.1  # DRF
django-cors-headers==3.10.1  # CORS 설정

다음으로, 위 두 패키지를 활용하기 위해 다음과 같은 설정들을 진행하였다. 자세한 내용은 생략한다.

  • INSTALLED_APPS 설정에 'rest_framework', 'corsheaders' 추가
  • REST_FRAMEWORK 설정 추가
    • DEFAULT_AUTHENTICATION_CLASSES 필드를 SessionAuthentication으로 지정
  • MIDDLEWARE 설정에 'corsheaders.middleware.CorsMiddleware' 추가
  • CORS_ORIGIN_WHITELIST, CORS_ALLOW_CREDENTIALS 설정 추가

▎기존 인증 기능과의 호환성 확보

기존 서비스는 세션 기반의 인증 방식을 사용한다. 따라서 DRF에서도 세션 기반 인증 방식을 채택했다. 보통 프론트 엔드와 백 엔드를 분리하면 JWT 등의 토큰을 기반으로 한 인증 방식을 많이 채택하는데, 우리는 이렇게 하면 기존 인증 기능과의 호환성 확보가 매우 까다로워져서 새로운 기술의 점진적인 도입이 어렵다 판단하였다.

 

결론적으로, 다음과 같은 전략을 통해 기존 인증 기능과의 호환성을 확보하였다.

  • DRF : 현재 쿠키에 저장되어 있는 세션 ID를 기반으로 로그인된 유저의 정보를 조회하는 API를 구현한다.
  • Next.js : 최초 렌더링 시 클라이언트 사이드에서 해당 API를 요청하고, 그 결과에 해당하는 유저의 정보를 전역 상태에 저장하도록 한다. 이후 로그인된 유저의 정보는 이 전역 상태를 참조하도록 한다.
  • 결론적으로, 기존 서비스에서 로그인을 완료한 유저는 새로 개발할 Next.js 페이지에서도 로그인된 것으로 인식된다. 만약 로그인되지 않은 유저라면 로그인을 할 수 있도록 기존 서비스의 로그인 페이지로 가는 링크를 삽입해주면 될 것이다.

 

이때, 클라이언트 사이드에서 API를 요청하기 위한 패키지로는 axios를 채택하였으며, 조금 더 진보된 기능의 클라이언트 사이드 렌더링을 적극 활용하기 위해 swr 패키지도 채택하였다. useSWR() 함수가 사용할 fetcher() 함수는 axios를 이용하여 구현하였다.

 

Axios

Promise based HTTP client for the browser and node.js Axios is a simple promise based HTTP client for the browser and node.js. Axios provides a simple to use library in a small package with a very extensible interface.

axios-http.com

 

데이터 가져오기를 위한 React Hooks – SWR

데이터 가져오기를 위한 React Hooks Suspense Pagination

swr.vercel.app

다음으로, 전역 상태의 관리를 위한 패키지로는 recoil을 채택하였다. 원래 많이들 사용하는 redux를 채택할까 했는데, 이렇게 되면 동료 개발자분들의 신기술 학습 부담이 더욱 커질 것을 우려하여 러닝 커브가 낮은 recoil을 채택하게 되었다. 실제로 recoil은 1시간 내로 기본적인 활용 방법을 익힐 수 있을 정도로 단순하다. 또한, 원한다면 나중에 redux로 바꾸는 것은 크게 어렵지 않다 판단했다.

 

Recoil

A state management library for React.

recoiljs.org