본문 바로가기

[2020-01-21] nginx로 next.js 앱 배포하기 (초보)

웹챗 SDK를 운영계에 배포하기 위해 nginx로 reverse proxy 설정을 해야할 필요가 생겼다.

 

클라우드 디스크 교체 작업 동안 운영계 서버를 쓸 수 없으니 일단 개인 ec2에서 하나의 서버에서 reverse proxy 설정을 해보고 운영계에 도전하기로 했다.

 

대략적인 구성은 다음과 같다.

 

외부에서는 http://my_ec2_host/sdk 로 접근하면 nginx는 reverse proxy로 도커로 띄운 http://my_ec2_host:4001/로 프록시 패스해준다.

 

1. nginx.conf

RHEL7 기준 default로 /etc/nginx 내에 위치한 nginx.conf를 다음과 같이 수정했다.

server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        # 번들링된 정적 파일 프록시
        location /_next/static {
            proxy_pass http://my_next_js_app:4001/_next/static;
        }

        # api route 프록시
        location /api {
            proxy_pass http://my_next_js_app:4001/api;

            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 / {
            # 이미지 프록시
            location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc|woff2)$ {
                proxy_pass http://my_next_js_app:4001;
            }

            # cors 해제
            if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' 'http://my_next_js_app:4001';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain; charset=utf-8';
                add_header 'Content-Length' 0;
                return 204;
            }
            if ($request_method = 'POST') {
                add_header 'Access-Control-Allow-Origin' 'http://my_next_js_app:4001';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
            }
            if ($request_method = 'GET') {
                add_header 'Access-Control-Allow-Origin' 'http://my_next_js_app:4001';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
            }
        }

        # 페이지 프록시
        location /sdk {
            proxy_pass http://my_next_js_app:4001/;

            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;

            if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain; charset=utf-8';
                add_header 'Content-Length' 0;
                return 204;
            }
            if ($request_method = 'POST') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
            }
            if ($request_method = 'GET') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
            }
        }
        
        # Next.js 자체에서 404 에러를 띄우므로 주석처리
        # error_page 404 /404.html;
        #     location = /40x.html {
        # }

        # error_page 500 502 503 504 /50x.html;
        #     location = /50x.html {
        # }
    }

번들링된 /_next/static과 api route를 신경써줘야 한다! 같은 서버에서 띄웠으므로 cors를 해제해주었다.

 

2. Next.js configuration

nginx에서 api route또한 proxy pass 해주었으므로 config에서 api route또한 http://my_ec2_host/api로 접근할 수 있게끔 해준다.

 

/* /config/env/production.js */
module.exports = {
    // ...
    api_domain: "http://my_ec2_host/api"
};

 

 

아주 초보적인 방법으로 기존의 docker로만 띄워서 4001번으로 접근했던 것을 서브 도메인으로 접근할 수 있도록 변경 끝!

3. Improvement

위에서는 하나의 컨테이너만 띄워져 있어서 Next.js 내 이미지를 받아오는 데 크게 문제가 없지만 만약 여러 개의 웹 서비스를 nginx가 동시에 처리하고 있다면 이미지 path 또한 nginx 에서 proxy pass 해주면 좋을 거 같다!

 

혹은 싱글 페이지가 아닌 link로 연결된 다수의 페이지가 있을 경우 또한 next.js의 assetPrefix를 활용하여 효율적으로 관리할 수 있다.

 

지금은 아주 초보적인 방법으로 접근했는데 실제 운영계에서는 다수의 컨테이너와 서비스들을 nginx가 처리하고 있어서 이를 처리하려면 모두 나눠야 할거 같다.

 

그건 다음에 성공하고 나서 기록해둬야지 ㅇ<-<

 

참고했던 URL들

더보기