안녕하세요! 단일 호스트에서 원활하게 작동하도록 NestJS를 구성하는 방법을 공유하게 되어 기쁩니다. 하지만 먼저 오랫동안 프런트엔드와 백엔드를 모두 관리하기 위해 이 설정이 제가 최고의 선택이었던 이유를 설명하겠습니다.
Next.js는 새로운 프로젝트를 시작할 때 강력한 힘을 발휘합니다. 기본 제공 라우팅, 서버 측 렌더링(SSR), 캐싱과 같은 기능이 포함되어 있어 원활한 실행에 도움이 됩니다. 또한 Next.js에는 자체 내부 API 기능이 있어 프레임워크 내에서 바로 캐싱 및 데이터 준비와 같은 작업을 관리할 수 있습니다. 즉, 인프라 설정에 시간을 덜 쓰고 앱 구축에 더 집중할 수 있습니다.
하지만 때로는 서버에 더 강력한 기능이 필요할 때가 있습니다. 이것이 바로 Nest.js가 개입하는 곳입니다. 이 프레임워크는 매우 강력하여 백엔드와 프런트엔드 사이의 미들웨어 업무를 처리할 수 있을 뿐만 아니라 그 자체로 강력한 백엔드 솔루션 역할을 할 수도 있습니다. 따라서 이 경우 NestJS는 Next.js에 추가되어 프런트엔드와 백엔드에 단일 프로그래밍 언어를 사용할 수 있게 해줍니다.
간단히 말하면 엄청나게 편리합니다. git pull과 docker-compose up -d만 사용하면 준비가 완료됩니다. CORS나 포트 저글링에 대해 걱정할 필요가 없습니다. 또한 배송 프로세스를 간소화하여 모든 것이 더욱 원활하고 효율적으로 실행되도록 합니다. 단점으로는 부하가 많이 걸리는 대규모 프로젝트에는 적합하지 않다는 점을 지적할 수 있습니다.
파일: ./docker-compose.yml
services: nginx: image: nginx:alpine ports: - "80:80" volumes: - "./docker/nginx/conf.d:/etc/nginx/conf.d" depends_on: - frontend - backend networks: - internal-network - external-network frontend: image: ${FRONTEND_IMAGE} restart: always networks: - internal-network backend: image: ${BACKEND_IMAGE} environment: NODE_ENV: ${NODE_ENV} POSTGRES_HOST: ${POSTGRES_HOST} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} depends_on: - postgres restart: always networks: - internal-network postgres: image: postgres:12.1-alpine container_name: postgres volumes: - "./docker/postgres:/var/lib/postgresql/data" environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} ports: - "5432:5432" networks: internal-network: driver: bridge external-network: driver: bridge
간단히 말하면 엄청나게 편리합니다. git pull과 docker-compose up -d만 사용하면 준비가 완료됩니다. CORS나 포트 저글링에 대해 걱정할 필요가 없습니다. 또한 배송 프로세스를 간소화하여 모든 것이 더욱 원활하고 효율적으로 실행되도록 합니다. 단점으로는 부하가 많이 걸리는 대규모 프로젝트에는 적합하지 않다는 점을 지적할 수 있습니다.
개발 모드의 경우 백엔드와 프런트엔드를 위한 컨테이너 서비스가 필요하지 않습니다. 왜냐하면 로컬에서 실행하기 때문입니다.
파일: ./docker-compose.dev.yml
version: '3' services: nginx: image: nginx:alpine ports: - "80:80" volumes: - "./docker/nginx/conf.d:/etc/nginx/conf.d" postgres: image: postgres:12.1-alpine container_name: postgres volumes: - "./docker/postgres:/var/lib/postgresql/data" environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: postgres ports: - "5432:5432"
파일: ./backend/Dockerfile
FROM node:18-alpine AS deps RUN apk add --no-cache libc6-compat WORKDIR /app COPY package.json package-lock.json ./ RUN npm install FROM node:18-alpine AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . ENV NEXT_TELEMETRY_DISABLED 1 RUN npm run build FROM node:18-alpine AS runner WORKDIR /app ENV NODE_ENV production ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/package.json ./package.json RUN mkdir -p /app/backups && chown -R nextjs:nodejs /app/backups && chmod -R 777 /app/backups USER nextjs EXPOSE 3010 ENV PORT 3010 CMD ["node", "dist/src/main"] ## 5. Docker file for frontend File: ./frontend/Dockerfile FROM node:18-alpine AS deps RUN apk add --no-cache libc6-compat WORKDIR /app COPY package.json package-lock.json ./ RUN npm install FROM node:18-alpine AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . ENV NEXT_TELEMETRY_DISABLED 1 RUN npm run build FROM node:18-alpine AS runner WORKDIR /app ENV NODE_ENV production ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next COPY --from=builder --chown=nextjs:nodejs /app/public ./public COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/package.json ./package.json USER nextjs EXPOSE 3000 ENV PORT 3000 CMD ["npm", "start"]
이 단계에서는 Next.js 프런트엔드와 Nest.js 백엔드에 대한 역방향 프록시 역할을 하도록 Nginx를 구성합니다. Nginx 구성을 사용하면 프런트엔드와 백엔드 간에 요청을 원활하게 라우팅하는 동시에 동일한 호스트에서 요청을 처리할 수 있습니다.
파일: /docker/nginx/conf.d/default.conf
server { listen 80; location / { proxy_pass http://host.docker.internal:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /api { proxy_pass http://host.docker.internal:3010; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
이 구성은 포트 80에서 수신 대기하고 일반 트래픽을 포트 3000의 Next.js 프런트엔드로 라우팅하는 반면, /api에 대한 모든 요청은 포트 3010의 Nest.js 백엔드로 전달됩니다.
동일한 호스트를 사용하므로 /apipath에서 NestJ를 사용할 수 있어야 합니다. 이를 위해서는 setGlobalPrefix — API가 필요합니다.
파일: ./backend/src/main.js
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule, { cors: true }); app.setGlobalPrefix('api'); await app.listen(3010); } bootstrap();
프런트엔드에는 구성이 필요하지 않지만 모든 서버 요청은 /api 경로를 기준으로 호출되어야 한다는 점만 고려하면 됩니다.
CD 프론트엔드
npm 실행 개발
CD ../백엔드
npm 실행 시작:dev
CD ../
docker-compose -f docker-compose.dev.yml up -d
이제 브라우저에서 localhost를 열어 웹사이트를 확인할 수 있습니다. 이 예에서는 서버에 1개의 요청이 있고 클라이언트에 또 다른 요청이 있습니다. 이 두 요청은 모두 Next.J에서 호출되고 Nest.J에서 처리됩니다.
이 문서에서는 Docker 레지스트리 및 GitHub Actions를 사용하여 서버에 프로젝트를 배포하는 방법을 살펴봅니다. 프로세스는 Docker 레지스트리에서 백엔드와 프런트엔드 모두에 대한 Docker 이미지를 생성하는 것으로 시작됩니다. 그런 다음 GitHub 저장소를 설정하고 원활한 배포를 위해 필요한 비밀을 구성해야 합니다.
DOCKERHUB_USERNAME
DOCKERHUB_TOKEN
DOCKER_FRONTEND_IMAGE
DOCKER_BACKEND_IMAGE
REMOTE_SERVER_HOST
REMOTE_SERVER_USERNAME
REMOTE_SERVER_SSH_KEY
REMOTE_SERVER_SSH_PORT
백엔드와 프런트엔드에 하나의 저장소를 사용하는 것의 단점은 무언가를 푸시할 때마다 두 이미지가 모두 다시 빌드된다는 것입니다. 이를 최적화하기 위해 다음 조건을 사용할 수 있습니다.
if: contains(github.event_name, ‘push’) && !startsWith(github.event.head_commit.message, ‘frontend’)
if: contains(github.event_name, ‘push’) && !startsWith(github.event.head_commit.message, ‘backend’)
커밋 메시지를 지정하여 원하는 이미지만 다시 빌드하는 것이 가능합니다.
파일: ./github/workflows/deploy.yml
name: deploy nextjs and nestjs to GITHUB on: push: branches: [ "main" ] jobs: build-and-push-frontend: runs-on: ubuntu-latest if: contains(github.event_name, 'push') && !startsWith(github.event.head_commit.message, 'backend') steps: - name: Checkout uses: actions/checkout@v3 - name: Login to Docker Hub uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push frontend to Docker Hub uses: docker/build-push-action@v2 with: context: frontend file: frontend/Dockerfile push: true tags: ${{ secrets.DOCKER_FRONTEND_IMAGE }}:latest - name: SSH into the remote server and deploy frontend uses: appleboy/ssh-action@master with: host: ${{ secrets.REMOTE_SERVER_HOST }} username: ${{ secrets.REMOTE_SERVER_USERNAME }} password: ${{ secrets.REMOTE_SERVER_SSH_KEY }} port: ${{ secrets.REMOTE_SERVER_SSH_PORT }} script: | cd website/ docker rmi -f ${{ secrets.DOCKER_FRONTEND_IMAGE }}:latest docker-compose down docker-compose up -d build-and-push-backend: runs-on: ubuntu-latest if: contains(github.event_name, 'push') && !startsWith(github.event.head_commit.message, 'frontend') steps: - name: Checkout uses: actions/checkout@v3 - name: Login to Docker Hub uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push backend to Docker Hub uses: docker/build-push-action@v2 with: context: backend file: backend/Dockerfile push: true tags: ${{ secrets.DOCKER_BACKEND_IMAGE }}:latest - name: SSH into the remote server and deploy backend uses: appleboy/ssh-action@master with: host: ${{ secrets.REMOTE_SERVER_HOST }} username: ${{ secrets.REMOTE_SERVER_USERNAME }} password: ${{ secrets.REMOTE_SERVER_SSH_KEY }} port: ${{ secrets.REMOTE_SERVER_SSH_PORT }} script: | cd website/ docker rmi -f ${{ secrets.DOCKER_BACKEND_IMAGE }}:latest docker-compose down docker-compose up -d=직업: 빌드 및 푸시 프런트엔드: 실행: 우분투 최신 if: 포함(github.event_name, 'push') && !startsWith(github.event.head_commit.message, 'backend') 단계: - 이름 : 체크아웃 용도: actions/checkout@v3 - 이름: Docker Hub에 로그인합니다. 용도: docker/login-action@v1 와 함께: 사용자 이름: ${{ secrets.DOCKERHUB_USERNAME }} 비밀번호: ${{ secrets.DOCKERHUB_TOKEN }} - 이름: 프런트엔드를 빌드하고 Docker Hub에 푸시합니다. 용도: docker/build-push-action@v2 와 함께: 컨텍스트: 프론트엔드 파일: 프론트엔드/Dockerfile 푸시: 사실 태그: ${{ secrets.DOCKER_FRONTEND_IMAGE }}:최신 - 이름: SSH를 통해 원격 서버에 접속하고 프런트엔드 배포 용도: appleboy/ssh-action@master 와 함께: 호스트: ${{ secrets.REMOTE_SERVER_HOST }} 사용자 이름: ${{ secrets.REMOTE_SERVER_USERNAME }} 비밀번호: ${{ secrets.REMOTE_SERVER_SSH_KEY }} 포트: ${{ secrets.REMOTE_SERVER_SSH_PORT }} 스크립트: | CD 웹사이트/ docker rmi -f ${{ secrets.DOCKER_FRONTEND_IMAGE }}:최신 도커 작성 도커-작성 -d 빌드 및 푸시 백엔드: 실행: 우분투 최신 if: 포함(github.event_name, 'push') && !startsWith(github.event.head_commit.message, 'frontend') 단계: - 이름 : 체크아웃 용도: actions/checkout@v3 - 이름: Docker Hub에 로그인합니다. 용도: docker/login-action@v1 와 함께: 사용자 이름: ${{ secrets.DOCKERHUB_USERNAME }} 비밀번호: ${{ secrets.DOCKERHUB_TOKEN }} - 이름: 백엔드를 빌드하고 Docker Hub에 푸시합니다. 용도: docker/build-push-action@v2 와 함께: 컨텍스트: 백엔드 파일: 백엔드/Dockerfile 푸시: 사실 태그: ${{ secrets.DOCKER_BACKEND_IMAGE }}:최신 - 이름: SSH를 통해 원격 서버에 접속하고 백엔드 배포 용도: appleboy/ssh-action@master 와 함께: 호스트: ${{ secrets.REMOTE_SERVER_HOST }} 사용자 이름: ${{ secrets.REMOTE_SERVER_USERNAME }} 비밀번호: ${{ secrets.REMOTE_SERVER_SSH_KEY }} 포트: ${{ secrets.REMOTE_SERVER_SSH_PORT }} 스크립트: | CD 웹사이트/ docker rmi -f ${{ secrets.DOCKER_BACKEND_IMAGE }}:최신 도커 작성 도커-작성 -d=
저장소: https://github.com/xvandevx/blog-examples/tree/main/nextjs-nestjs-deploy
이 문서는 Next.js와 Nest.js를 단일 서버에 함께 배포하여 간소화된 설정을 원하는 개발자를 위한 솔루션으로 만드는 실습 가이드입니다. 프런트엔드용 Next.js와 백엔드용 Nest.js의 장점을 결합하여 Docker 및 GitHub Actions를 사용하여 애플리케이션의 두 부분을 효율적으로 관리하는 방법을 보여주었습니다. 배포 프로세스를 단순화하여 여러 구성을 저글링하는 대신 앱 구축에 집중할 수 있습니다. 번거로움을 최소화하면서 풀 스택 프로젝트를 빠르게 시작하고 실행하려는 사람들에게 적합합니다.
부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.
Copyright© 2022 湘ICP备2022001581号-3