docker-compose 와 next-auth

Jindolph·2024년 8월 4일

블로그 포스트: Docker Compose와 Nginx를 이용한 Next.js 및 NextAuth 설정 트러블슈팅

docker-compose를 이용하여, nginx, next서버, gateway서버를 구성했을 때 발생할 수 있는 여러 문제들에 대한 해결과정.


1. 서버 구성

Docker Compose를 이용하여 nginx, frontend, gateway 서버를 구성합니다.

version: '3.7'
services:
  frontend:
    image: node:16-alpine
    container_name: nextjs-app
    working_dir: /app
    volumes:
      - .:/app
      - /app/node_modules
    expose:
      - "3000"
    command: npm run dev
    environment:
      - NODE_ENV=development
      - NEXTAUTH_URL=http://localhost

  gateway:
    image: node:16-alpine
    container_name: api-server
    working_dir: /app
    volumes:
      - ./api:/app
    expose:
      - "3001"
    command: npm start
    environment:
      - NODE_ENV=development

  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - frontend
      - gateway

2. nginx 설정

2-1. Frontend 서버 설정

nginx 설정 파일에서 frontend 서버를 localhost로 요청받도록 설정합니다.

server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://frontend: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;
    }
}

2-2. Gateway 서버 설정

nginx 설정 파일에서 gateway 서버를 localhost/api 경로로 요청받도록 설정합니다.

server {
    listen 80;
    server_name localhost;

    location /api/ {
        proxy_pass http://gateway:3001;
        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;
    }
}

3. 문제 발생

위의 설정으로 인해 다음과 같은 문제가 발생했습니다.

3-1. NextAuth 기본 호출 경로 문제

NextAuth는 기본적으로 localhost/api/auth 경로를 포함한 URL을 호출합니다.
예를 들어, providers 목록을 호출하는 /api/auth/providers 등의 인증인가 처리를 위한 기본 url 경로.

3-2. nginx에서 NextAuth 기본 호출 경로 프록시 설정

nginx에서 NextAuth 기본 호출 경로를 frontend로 프록시하도록 설정합니다.

server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://frontend: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;
    }

    location /api/ {
        proxy_pass http://gateway:3001;
        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;
    }

    location /api/auth {
        proxy_pass http://frontend: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;
    }
}

4. 여전히 발생하는 포트 문제

이 시점에서 .env.local 파일의 NEXTAUTH_URLhttp://localhost로 설정했습니다.

4-1. NextAuth의 내부적인 요청 URL 문제

providers 호출 등 NextAuth의 내부적인 요청 URL에서 여전히 문제가 발생합니다.
이유는 위의 환경변수에 등록된 NEXTAUTH 기본 URL 경로 설정에서, localhost를 사용하기 때문입니다. 이는 nginx를 경유하지 않는 내부 호출이기 때문에 docker-compose에서 정의한 서버 이름을 명시해줘야 합니다.

5. 문제 해결: 서버 요청 base URL 설정

문제가 되는 현상들을 해결하기 위해 서버 요청 base URL을 전부 nginx로 타겟하도록 정의해야 합니다.

예시 1: fetch 등의 타겟 URL 설정

import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';

export default NextAuth({
  providers: [
    Providers.Credentials({
      name: 'Credentials',
      credentials: {
        email: { label: 'Email', type: 'email' },
        password: { label: 'Password', type: 'password' },
      },
      async authorize(credentials) {
        try {
          const res = await fetch('http://nginx/api/auth/verify', {
            method: 'POST',
            body: JSON.stringify(credentials),
            headers: { 'Content-Type': 'application/json' },
          });
          if (!res.ok) {
            throw new Error('Failed to fetch');
          }
          const user = await res.json();
          if (user) {
            return user;
          }
          return null;
        } catch (error) {
          console.error('Authorize Error:', error);
          return null;
        }
      },
    }),
  ],
});

예시 2: .env.local 파일의 NEXTAUTH_URLhttp://nginx로 설정했습니다.

이와 같이 서버 요청을 모두 nginx로 타겟하도록 설정하여 문제가 해결되었습니다. 이를 통해 모든 요청이 nginx를 경유하여 일관된 요청 처리를 할 수 있게 되었습니다.


추가내용!!

Next.js의 특성으로 인한 트러블슈팅 경험입니다.

Next.js의 클라이언트와 서버 컴포넌트의 이러한 차이점을 주의하세요.
클라이언트 컴포넌트는 클라이언트사이드에서 요청을 보내기 때문에 fetch 등이 localhost로 나갑니다.(정상적으로 nginx 경유함.)
반면에, 서버 컴포넌트는 서버사이드에서 요청을 보내기 때문에 nginx를 경유하지 않고 컨테이너간 네트워크 통신이 이루어집니다.

클라이언트 컴포넌트

  • 브라우저에서 실행: 클라이언트 컴포넌트는 브라우저에서 실행되므로, fetch 요청은 브라우저의 컨텍스트 내에서 실행됩니다.
  • 예시: SignUp 페이지
const handleSubmit = async (e) => {
  e.preventDefault();
  const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/signup`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ username, email, password }),
  });
};

서버 컴포넌트

  • 서버 측에서 실행: 서버 컴포넌트는 서버 측에서 실행되므로, fetch 요청은 서버의 컨텍스트 내에서 실행됩니다.
  • 예시: auth.tsauthorize 함수
async authorize(credentials) {
  const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/auth/verify`, {
    method: 'POST',
    body: JSON.stringify(credentials),
    headers: { 'Content-Type': 'application/json' },
  });
}

LoginForm에서 요청처리를 할때 next-auth의 signIn을 사용하게 되면, 내부적으로 next-auth 옵션에서 정의한 Provider의 authorize를 사용합니다.
이러한 인증인가 과정에서 /api/auth/ 경로의 여러 요청이 처리됩니다.
GET /api/auth/providers
POST /api/auth/signin/credentials
POST /api/auth/callback/credentials

이러한 차이점을 이해하고 적절히 설정하면 클라이언트와 서버 간의 요청이 일관되게 처리될 수 있습니다.

profile
Hello World!

0개의 댓글