Javascript

NextJS 앱 자체 호스팅: 실제 사례로 알아보는 최적의 방법

드리프트2 2024. 8. 9. 20:22

NextJS 앱 자체 호스팅: 실제 사례로 알아보는 최적의 방법

NextJS는 강력한 React 프레임워크로, 많은 개발자들이 선호하는 도구입니다. 하지만 이를 어떻게 배포하고 호스팅할 것인가는 항상 고민거리입니다. Vercel과 같은 플랫폼도 있지만, 비용이나 커스터마이징 측면에서 자체 호스팅을 고려하는 경우가 많습니다. 이번 글에서는 NextJS 앱을 자체 호스팅하는 다양한 방법을 실제 사례와 함께 상세히 알아보겠습니다.

1. Docker 컨테이너 활용

예시: 중소 규모의 전자상거래 웹사이트 "ShopEasy"

ShopEasy는 5만 명의 월간 활성 사용자를 보유한 전자상거래 플랫폼입니다. 그들은 NextJS로 개발한 웹사이트를 Docker와 Coolify를 사용해 관리하고 있습니다.

구체적인 설정:

  • DigitalOcean의 4GB RAM, 2 vCPU Droplet에 Coolify 설치
  • Dockerfile 생성:
    FROM node:14
    WORKDIR /app
    COPY package*.json ./
    RUN npm install
    COPY . .
    RUN npm run build
    CMD ["npm", "start"]
  • Coolify 대시보드에서 GitHub 저장소 연동
  • 환경 변수 설정 (예: DATABASE_URL, API_KEY 등)
  • 자동 배포 규칙 설정: main 브랜치에 push 시 자동 배포

장점:

  • 간편한 배포 및 롤백: Coolify 대시보드에서 원클릭으로 가능
  • 일관된 환경: 개발, 스테이징, 프로덕션 환경의 일관성 유지
  • 모니터링 용이: Coolify에서 제공하는 로그 및 리소스 사용량 모니터링

2. 클라우드 서비스 활용

예시: 대규모 소셜 미디어 플랫폼 "ConnectWorld"

ConnectWorld는 일일 활성 사용자 100만 명을 보유한 소셜 미디어 플랫폼입니다. 그들은 Google Cloud Platform의 서비스를 활용하여 NextJS 앱을 호스팅하고 있습니다.

구체적인 설정:

  • Cloud Build 설정 (cloudbuild.yaml):
    steps:
    - name: 'gcr.io/cloud-builders/docker'
      args: ['build', '-t', 'gcr.io/$PROJECT_ID/nextjs-app', '.']
    - name: 'gcr.io/cloud-builders/docker'
      args: ['push', 'gcr.io/$PROJECT_ID/nextjs-app']
    - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
      entrypoint: gcloud
      args: ['run', 'deploy', 'nextjs-app', '--image', 'gcr.io/$PROJECT_ID/nextjs-app', '--region', 'us-central1', '--platform', 'managed']
  • Cloud Run에 배포
  • Cloud CDN 설정: 정적 자산을 위한 CDN 구성
  • Cloud SQL: PostgreSQL 데이터베이스 설정

장점:

  • 높은 확장성: 트래픽 증가에 따라 자동으로 인스턴스 확장
  • 글로벌 CDN: 전 세계 사용자에게 빠른 콘텐츠 전달
  • 서버리스: 인프라 관리 부담 최소화

3. VPS와 Nginx, PM2 조합

예시: 개인 블로그 및 포트폴리오 웹사이트 "DevJourney"

프리랜서 개발자 Sarah는 자신의 블로그와 포트폴리오를 NextJS로 개발했습니다. 비용 효율성을 위해 VPS를 직접 관리하고 있습니다.

구체적인 설정:

  1. Linode에서 2GB RAM, 1 vCPU VPS 구매
  2. SSH로 서버 접속: ssh root@your_server_ip
  3. 기본 설정:
    apt update && apt upgrade -y
    adduser sarah
    usermod -aG sudo sarah
  4. Node.js 및 npm 설치:
    curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
    sudo apt-get install -y nodejs
  5. Nginx 설치 및 설정:
    sudo apt install nginx
    sudo nano /etc/nginx/sites-available/default
    Nginx 설정:
    server {
        listen 80;
        server_name devjourney.com;
        location / {
            proxy_pass http://localhost:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }
  6. PM2 설치 및 NextJS 앱 실행:
    sudo npm install -g pm2
    pm2 start npm --name "next-app" -- start
  7. Let's Encrypt로 SSL 설정:
    sudo apt install certbot python3-certbot-nginx
    sudo certbot --nginx -d devjourney.com
  8. GitHub Actions 설정 (.github/workflows/deploy.yml):
    name: Deploy to VPS
    on:
      push:
        branches: [ main ]
    jobs:
      deploy:
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@v2
        - name: Deploy to VPS
          uses: appleboy/ssh-action@master
          with:
            host: ${{ secrets.HOST }}
            username: ${{ secrets.USERNAME }}
            key: ${{ secrets.SSH_PRIVATE_KEY }}
            script: |
              cd /path/to/your/app
              git pull origin main
              npm install
              npm run build
              pm2 restart next-app

장점:

  • 저렴한 비용: 월 $10 미만의 호스팅 비용
  • 완전한 제어: 서버 설정에 대한 모든 권한
  • 학습 기회: 서버 관리 및 배포 과정에 대한 깊은 이해

4. Kubernetes 활용

예시: 대기업의 마이크로서비스 아키텍처 "EnterpriseApp"

EnterpriseApp은 글로벌 기업의 복잡한 비즈니스 프로세스를 관리하는 대규모 애플리케이션입니다. NextJS로 개발한 프론트엔드를 포함한 여러 마이크로서비스를 Kubernetes로 관리하고 있습니다.

구체적인 설정:

  1. AWS EKS 클러스터 생성:

    eksctl create cluster --name enterprise-cluster --region us-west-2 --nodegroup-name standard-workers --node-type t3.medium --nodes 3 --nodes-min 1 --nodes-max 4 --managed
  2. NextJS 앱을 위한 Dockerfile 생성:

    FROM node:14-alpine
    WORKDIR /app
    COPY package*.json ./
    RUN npm install
    COPY . .
    RUN npm run build
    CMD ["npm", "start"]
  3. Kubernetes 배포 설정 (deployment.yaml):

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nextjs-frontend
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: nextjs-frontend
      template:
        metadata:
          labels:
            app: nextjs-frontend
        spec:
          containers:
          - name: nextjs-frontend
            image: your-registry/nextjs-frontend:latest
            ports:
            - containerPort: 3000
  4. 서비스 및 인그레스 설정 (service.yamlingress.yaml):

    # service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: nextjs-frontend-service
    spec:
      selector:
        app: nextjs-frontend
      ports:
        - protocol: TCP
          port: 80
          targetPort: 3000

    # ingress.yaml
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: nextjs-frontend-ingress
      annotations:
        kubernetes.io/ingress.class: nginx
        cert-manager.io/cluster-issuer: "letsencrypt-prod"
    spec:
      tls:
      - hosts:
        - frontend.enterpriseapp.com
        secretName: nextjs-frontend-tls
      rules:
      - host: frontend.enterpriseapp.com
        http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nextjs-frontend-service
                port: 
                  number: 80
  5. 자동 스케일링 설정 (hpa.yaml):

    apiVersion: autoscaling/v2beta1
    kind: HorizontalPodAutoscaler
    metadata:
      name: nextjs-frontend-hpa
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: nextjs-frontend
      minReplicas: 3
      maxReplicas: 10
      metrics:
      - type: Resource
        resource:
          name: cpu
          targetAverageUtilization: 50
  6. CI/CD 파이프라인 구축 (예: GitLab CI):

    stages:
      - build
      - deploy
    
    build:
      stage: build
      image: docker:latest
      services:
        - docker:dind
      script:
        - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
        - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    
    deploy:
      stage: deploy
      image: bitnami/kubectl:latest
      script:
        - kubectl set image deployment/nextjs-frontend nextjs-frontend=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
        - kubectl rollout status deployment/nextjs-frontend

장점:

  • 높은 확장성: 트래픽 증가에 따라 자동으로 Pod 수 조절
  • 무중단 배포: 롤링 업데이트로 다운타임 없는 배포 가능
  • 리소스 효율성: 여러 마이크로서비스를 효율적으로 관리

5. Serverless 접근

예시: 이벤트 기반 웹 애플리케이션 "EventHub"

EventHub는 연간 몇 차례 대규모 이벤트 기간에 트래픽이 급증하는 웹 애플리케이션입니다. NextJS의 정적 생성 기능을 최대한 활용하여 Cloudflare Pages와 Workers를 사용한 서버리스 아키텍처를 채택했습니다.

구체적인 설정:

  1. NextJS 앱 최적화:

    • 가능한 많은 페이지를 정적으로 생성
    • 동적 콘텐츠는 클라이언트 사이드 렌더링 활용
  2. Cloudflare Pages 설정:

    • GitHub 저장소와 Cloudflare 계정 연결
    • 빌드 설정:
      Build command: npm run build && npm run export
      Output directory: out
  3. 동적 기능을 위한 Cloudflare Worker (api/events.js):

    addEventListener('fetch', event => {
      event.respondWith(handleRequest(event.request))
    })
    
    async function handleRequest(request) {
      // KV에서 이벤트 데이터 조회
      const events = await EVENT_KV.get('upcoming_events')
      return new Response(events, {
        headers: { 'Content-Type': 'application/json' },
      })
    }
  4. Cloudflare KV 설정:

    • Cloudflare 대시보드에서 KV 네임스페이스 생성
    • Worker에 KV 바인딩
  5. 배포 자동화 (.github/workflows/deploy.yml):

    name: Deploy to Cloudflare Pages
    on:
      push:
        branches: [main]
    jobs:
      deploy:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v2
          - name: Build
            run: |
              npm ci
              npm run build
              npm run export
          - name: Publish
            uses: cloudflare/wrangler-action@1.3.0
            with:
              apiToken: ${{ secrets.CF_API_TOKEN }}

장점:

  • 비용 효율성: 사용량 기반 과금으로 유휴 시간 동안 비용 최소화
  • 글로벌 배포: Cloudflare의 글로벌 네트워크로 빠른 콘텐츠 전달
  • 관리 용이성: 서버 관리 불필요, 개발에만 집중 가능

결론

NextJS 앱의 자체 호스팅 방식은 프로젝트의 규모, 복잡성, 예산, 그리고 팀의 기술적 역량에 따라 다양하게 선택할 수 있습니다. 소규모 프로젝트의 경우 VPS와 Nginx, PM2 조합이 비용 효율적이면서도 충분한 제어권을 제공합니다. 중간 규모의 프로젝트에서는 Docker와 클라우드 서비스 조합이 확장성과 관리 용이성을 제공할 수 있습니다. 대규모 프로젝트나 복잡한 마이크로서비스 아키텍처를 가진 경우 Kubernetes가 적합할 수 있으며, 트래픽 변동이 큰 프로젝트에는 서버리스 아키텍처가 좋은 선택이 될 수 있습니다.

각 방식의 장단점을 요약하면 다음과 같습니다:

  1. Docker 컨테이너 활용

    • 장점: 환경 일관성, 쉬운 배포 및 확장
    • 단점: 컨테이너 관리 학습 곡선
  2. 클라우드 서비스 활용

    • 장점: 높은 확장성, 관리형 서비스로 운영 부담 감소
    • 단점: 비용이 높을 수 있음, 특정 클라우드 제공업체에 종속
  3. VPS와 Nginx, PM2 조합

    • 장점: 저렴한 비용, 완전한 제어권
    • 단점: 서버 관리 지식 필요, 수동 확장
  4. Kubernetes 활용

    • 장점: 높은 확장성, 복잡한 아키텍처 관리에 적합
    • 단점: 높은 학습 곡선, 소규모 프로젝트에는 과도할 수 있음
  5. Serverless 접근

    • 장점: 자동 확장, 관리 부담 최소화, 사용량 기반 과금
    • 단점: 콜드 스타트 문제, 특정 서비스에 종속될 수 있음

프로젝트를 시작할 때는 현재의 요구사항뿐만 아니라 미래의 성장 가능성도 고려해야 합니다. 초기에는 간단한 VPS 설정으로 시작하고, 필요에 따라 Docker나 클라우드 서비스로 마이그레이션하는 전략도 좋은 방법입니다. 또는 처음부터 확장성을 고려해 Kubernetes나 서버리스 아키텍처를 채택할 수도 있습니다.

중요한 것은 선택한 방식에 대한 깊은 이해와 지속적인 학습입니다. NextJS의 유연성을 최대한 활용하면서, 프로젝트의 요구사항에 가장 적합한 호스팅 솔루션을 선택하는 것이 핵심입니다. 각 방식의 장단점을 잘 파악하고, 필요에 따라 여러 방식을 혼합해 사용하는 것도 좋은 전략이 될 수 있습니다.

마지막으로, 어떤 방식을 선택하든 보안, 성능 모니터링, 백업 전략 등의 기본적인 운영 관리 사항을 고려해야 합니다. 이러한 요소들이 잘 갖춰져 있을 때, NextJS 앱의 안정적이고 효율적인 운영이 가능해집니다.

NextJS 앱 자체 호스팅은 단순히 기술적인 선택을 넘어, 프로젝트의 성공을 위한 전략적 결정입니다. 각 프로젝트의 고유한 요구사항과 제약 조건을 고려하여, 가장 적합한 호스팅 전략을 수립하시기 바랍니다. 이를 통해 효율적이고 확장 가능한 NextJS 애플리케이션을 구축하고 운영할 수 있을 것입니다.