docker-compose를 이용하여, nginx, next서버, gateway서버를 구성했을 때 발생할 수 있는 여러 문제들에 대한 해결과정.
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
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;
}
}
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;
}
}
위의 설정으로 인해 다음과 같은 문제가 발생했습니다.
NextAuth는 기본적으로 localhost/api/auth 경로를 포함한 URL을 호출합니다.
예를 들어, providers 목록을 호출하는 /api/auth/providers 등의 인증인가 처리를 위한 기본 url 경로.
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;
}
}
이 시점에서 .env.local 파일의 NEXTAUTH_URL을 http://localhost로 설정했습니다.
providers 호출 등 NextAuth의 내부적인 요청 URL에서 여전히 문제가 발생합니다.
이유는 위의 환경변수에 등록된 NEXTAUTH 기본 URL 경로 설정에서, localhost를 사용하기 때문입니다. 이는 nginx를 경유하지 않는 내부 호출이기 때문에 docker-compose에서 정의한 서버 이름을 명시해줘야 합니다.
문제가 되는 현상들을 해결하기 위해 서버 요청 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_URL을 http://nginx로 설정했습니다.
이와 같이 서버 요청을 모두 nginx로 타겟하도록 설정하여 문제가 해결되었습니다. 이를 통해 모든 요청이 nginx를 경유하여 일관된 요청 처리를 할 수 있게 되었습니다.
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.ts의 authorize 함수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
이러한 차이점을 이해하고 적절히 설정하면 클라이언트와 서버 간의 요청이 일관되게 처리될 수 있습니다.