로컬에서 https 환경 만들기

2022-10-02


저번에 썼던 글에 이어서 Figma API 사용후기를 더 자세하게 다뤄보고자했지만, 워낙에 업무 범위가 계속 변경되다 보니 Figma API 작업은 다른 분에게 넘기게 되었고 작업은 다른 분이 하고있다. 코드의 결과는 리팩토링과 함께 정리되어 2주 쯤 후에 더 자세한 후기를 공유할 수 있을 것 같다.

개요

푸망에서 만든 심리테스트 컨텐츠를 다른 회사의 어플리케이션 또는 웹서비스에 컨텐츠를 주입하고, 사용자의 컨텐츠 플레이 정보로 성격와 입력 값을 받아 개인화된 서비스에 사용할 수 있는 서비스를 운영하고 있다.

최근 함께하게 된 대기업에서는 사용자의 데이터 처리 과정에서 로그인 후에 들어온 앱이지만, 컨텐츠는 푸망에서 제공하는 웹에서 플레이 되기 때문에, 데이터 처리의 주체가 푸망이 이다보니 푸망에서 서비스 로그인 절차가 필요해졌다. 로그인 후에 들어온 페이지에서 로그인을 한번 더 하라고하니 어색하지만 법이 가만두지 않기에 어쩔수 없었다.

OAuth2를 추가하는 작업은 크게 어려운 것은 아니지만 보안 관련한 이슈로 개발이 힘들어졌다.

  • 아직 개발 단계의 API임(redirect uri를 맘대로 못바꿈ㅋ)
  • 화이트 리스트에 등록된 IP에서만 사용할 것
  • HTTPS만 이용할 것

IP는 회사의 IP를 등록해두었으니 회사에서만 개발하면 상관없지만 HTTPS를 사용해야 하는 것은 문제가 되었다. 또 "혹시 리다이렉트 URI로 localhost:3000을 추가해주실 수 있나요?" 라고 말하자니 창피하다.

그래서 이번에는 local 환경에서도 HTTPS 환경에서의 테스트를 진행할 수 있도록 환경을 셋업한 사례를 설명해 보고자 한다.

준비

이 글에서 다루는 내용은 인프라 수준에서 HTTPS를 적용하는 것이다. 아래에서 DockerNginx만 알아두면 이해하는 데에 도움된다.(사실 몰라도 된다) AWS ALB는 푸망의 인프라는 AWS를 통해서 관리되기 때문에 HTTPS 적용 컨셉을 AWS에서 따왔다.

  • Docker(with docker-compose) 는 설치되어 있어야한다.
  • Nginx
  • AWS ALB(Application LoadBalancer)

방법

이번에 문제 해결 핵심은 기존 서버의 셋업은 변경되지 않는 선에서 HTTPS를 적용하는 것이다.

아래의 AWS 그림을 봐보자

이해하기 어려운 AWS

위의 그래프가 처음보는 사람이거나 네트워크를 잘모르는 사람은 갸우뚱 하는 이미지이지만 사용자들이 요청을 하면 ALB라는 친구가 알아서 원하는 특정 서버로 보내준다는 것만 이해하면 된다.

사실 이 작업에 위의 이미지는 그렇게 큰 도움이 되진 않지만, 있으면 왠진 간지가 날 것 같아 한번 퍼왔다.

아래가 로컬에서 만드려는 구조이다.

로컬 환경

이 환경은 도커로 2개의 컨테이너를 올릴 것이다.

  1. Nginx 컨테이너

    위의 AWS 구조에서 ALB의 역할을 담당한다.

    1. http 통신이 오는 경우 https 로 리다이렉트 한다.
    2. https 통신이 오는 경우 NextJS 서버로 보내준다.
  2. NextJS 컨테이너

    개발중인 로컬 서비스가 돌아가는 컨테이너

    1. yarn dev 라는 명령어를 수행하고 있다.

아래의 환경울 구성하기 위해서는 기존 NextJS 서버를 돌아가게 하기 위한 Dockerfile을 만들어주어야한다.

|
|...projects
| - scripts 
|      | - Dockerfile # 현재 프로젝트로 컨테이너를 올리기 위한 도커파일
| - proxy # Nginx 기능 관련 
|      | - docker-compose.yml # 도커 컨테이너 2개를 동시에 올려준다.
|      | - default.conf # Nginx 기능(http -> https 리다이렉트, https)
|      | - Dockerfile # Nginx 도커 이미지
|      | - cert.pem # https ssl conf(서버 인증서)
|      | - key.pem # https ssl conf(개인키 파일)
  1. nginx ssl 관련 설정을 준비한다. (cert.pem, key.pem)

    Nginxx SSL 인증서 설치/적용 가이드라는 블로그 글을 보면 도움이 될 것 같다.

    예전에 만들었던 샘플 nginx-proxy repository에도 파일이 있는데, 여기에 있는 파일을 그대로 사용해도 된다.

  2. 필요한 파일 작성

    scripts/Dockerfile(프로젝트 컨테이너)

    FROM node:16
    ENV TZ=Asia/Seoul
    ARG PROJECT_DIR=/web
    COPY package.json ${PROJECT_DIR}/package.json
    WORKDIR $PROJECT_DIR
    RUN yarn
    COPY . ./
    RUN yarn build
    
    EXPOSE 3000
    
    CMD ["yarn", "dev"]

    proxy/Dockerfile(Nginx 컨테이너)

    FROM nginx
    
    ARG PROJECT_DIRECTORY=/etc/nginx/conf.d
    COPY cert.pem ${PROJECT_DIRECTORY}/cert.pem
    COPY key.pem ${PROJECT_DIRECTORY}/key.pem
    COPY default.conf ${PROJECT_DIRECTORY}/default.conf

    proxy/docker-compose.yml

    version: '3.7'
    services:
      app: # 프로젝트 컨테이너
        build:
          context: ..
          dockerfile: scripts/Dockerfile
        volumes:
          - ..:/web
        ports:
          - 3000:3000 # nextjs 포트(굳이 열어둘 필요 없지만, localhost:3000으로 접속할 수 있다.)
      proxy: # nginx 컨테이너
        build: .
        ports:
          - 80:80 # http 프로토콜(뒤에 포트번호 없이도 진입된다.)
          - 443:443 # https 프로토콜(포트번호를 뒤에 붙일 필요 없다.)
        volumes:
          - ./default.conf:/etc/nginx/conf.d/default.conf
        depends_on:
          - app

    proxy/default.conf(Nginx 기능)

    server {
      listen 80;
      server_name local.mycomputer.com;
      return 301 https://$host$request_uri;
    }
    
    server {
      listen 443 ssl;
      server_name local.mycomputer.com; # 사용할 도매인 이름
      ssl_certificate /etc/nginx/conf.d/cert.pem;
      ssl_certificate_key /etc/nginx/conf.d/key.pem;
      location / {
        proxy_pass http://app:3000; # app은 docker-compose에서 정의한 서비스 이름이다.
        # header support
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        # web socket support (nextjs hot-reload를 활용하려면 필요하다)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
      }
    }
  3. 위의 작업이 완료되면 다음의 명령어를 입력한다.

docker-compose -f proxy/docker-compose.yml up

그럼 아래와 같이 컨테이너 2개에 대한 로그와 함께 확인할 수 있다.

명령어
  1. 가장 중요한 내용

설정을 적용해도 컴퓨터의 도메인 설정을 바꾸지 않으면 자꾸 외부 DNS에서 도메인을 찾기 때문에 연결이 안된다.

컴퓨터가 도메인 주소를 찾을 때, 컴퓨터 설정에서 한번, 컴퓨터의 내부망(와이파이나 공유기)에서 한번 그리고 외부에서 검색하기 때문에, 컴퓨터에서 도메인을 연결시켜주어야한다.

보통은 /etc/hosts(Mac 기준) 이런 이름의 파일에서 바꿀 수 있다.

##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
# 원하는 도메인을 입력하고 도메인이 컴퓨의 기본인 127.0.0.1을 가게 한다.
127.0.0.1       local.mycomputer.com # 도메인은 바꿀 수 있다.(google 써도 된다)
# 보통은 이 아래의 정보가 있다.
127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost

위와 같은 설정을 변경하면, local.mycomputer.com라는 도메인을 인터넷 주소창에 치면, 127.0.0.1 서버로 연결해준다. 그럼 내부에 올려둔 도커 컨테이너로 연결되고 도커 컨테이너의 Nginx가 우리가 개발중인 애플리케이션으로 연결해준다.

결론

위의 방법대로 하면 https를 아주 쉽게 다룰 수 있게 된다.

단점은

  1. 어렵다. (네트워크와 도커에 대한 지식이 필요하다)
  2. 도커의 자원 할당이 적당하지 않다면, 빌드 단계나 hot reload 단계에서 nextjs의 성능이 크게 제한된다.(느려졌다는 느낌이 확실히 있다.)

장점은

  1. 한번 해보고 나면 두고두고 써먹을 수 있는 지식이 된다.
  2. 실제 배포 환경과 같은 인프라 환경이라 사이드 이펙트를 기대할 수 없다.(오히려 QA하기 좋다)

예전에 페이스북 인증을 개발할 때 한번 해본적 있어서 수월하게 했는데, 당시에는 하루 이틀정도 시간을 써서 해결했던 기억이 난다.

첨언

  • 위 설정이 네트워크에 대한 지식이 없으면 어려운 편이라 web.dev에서 제공하는 로컬 개발에 HTTPS를 사용하는 방법을 이용하는 방법도 있다. 하지만 nextjs 서버를 실행할 때 커스텀 서버 설정이 필요하다.
  • 사실 Ngrok을 이용하면 https가 해결되지만, 도메인을 내가 지정할 수 없기 때문에 이 방법을 이용해야했다.