Next.js 프로젝트에 local https 환경을 만들자 (feat. mkcert)
들어가기에 앞서..
대부분의 경우에 로컬 https 환경은 필수적이지 않습니다. 그러나 인증 문제나 CORS 등 일부 특수한 case에 따라 필요해지는 순간이 있는데, 이때 mkcert를 이용해 간단하게 로컬 https 환경을 조성하는 방법을 공유해보려고 합니다.
필요 기술
mkcert
dotenv
본문
[Step 1] mkcert 설치 및 인증
저는 mac에서 homebrew
를 사용하고 있기 때문에 터미널에서 아래와 같이 진행하였습니다
# 로컬에 mkcert 설치
$ brew install mkcert
# 로컬을 인증된 발급기관으로 추가 (ssl 공급자 인증을 위해 필요합니다)
$ mkcert -install
[Step 1.5] .env
파일 생성 및 환경변수 설정
- .env
파일 대신 하드 타입핑으로 진행해도 괜찮습니다.
저의 경우 프로젝트 레포를 멀티 테넌시로 구성해야 했기에 로컬 호스트 주소를 환경변수로 관리하게 되었습니다. 한번에 여러 프로젝트를 띄우고 있을 때 port 겹침을 방지하기 위해 개별적인 포트 번호를 사용합니다.
# 예시
LOCAL_HOST=local.example.com
PORT=4431
[Step 2] dotenv
라이브러리 추가
node 환경에서 환경변수 파일을 인식시키기 위해 dotenv 라이브러리를 설치해줍니다
$ npm i dotenv
[Step 3] setup
스크립트 작성
setup
스크립트에서는 https 인증서를 발행하고, pem 파일을 생성합니다.
동작 순서는 아래와 같습니다.
1단계 - .env
파일의 존재 유무 확인
2단계 - .env
파일 속 LOCAL_HOST
환경변수 존재 유무 확인
==== .env
없이 진행하셨다면 위 2단계는 생략하셔도 됩니다. ====
3단계 - /etc/hosts
파일에 해당 로컬 호스트를 추가합니다
4단계 - mkcert
로 인증서를 발행하고 pem 파일이 레포에 생성됩니다.
#!/bin/bash
# 환경 변수 파일 경로
ENV_FILE_NAME=".env"
# 환경 변수명
ENV_HOST_KEY="LOCAL_HOST"
# /etc/hosts 파일 경로
HOSTS_FILE_PATH="/etc/hosts"
###############################################################################
# 환경 변수 파일이 없을 경우 에러 메시지 출력 후 종료
if [ ! -f "$ENV_FILE_NAME" ]; then
echo "===================== 🚨 ERROR: NO ENV FILE ====================="
echo "== 환경 변수 파일이 없습니다"
echo "== $ENV_FILE_NAME 파일을 생성해주세요"
echo "====================================================================="
echo ""
exit 1
fi
echo "> ✅ 환경 변수 파일 $ENV_FILE_NAME 를 찾았습니다"
###############################################################################
# 환경 변수 파일에서 로컬 호스트 값 추출
LOCAL_HOST=$(grep ^"$ENV_HOST_KEY"= "$ENV_FILE_NAME" | cut -d '=' -f2)
# 환경 변수 파일에 로컬 호스트 값이 없을 경우 에러 메시지 출력 후 종료
if [ -z "$LOCAL_HOST" ]; then
echo ""
echo "============== 🚨 ERROR: $ENV_HOST_KEY NOT DECLARED ================"
echo "== $ENV_FILE_NAME 파일에 $ENV_HOST_KEY 환경변수를 설정해주세요"
echo "====================================================================="
echo ""
exit 1
fi
echo "> ✅ 환경 변수 $ENV_HOST_KEY 가 $ENV_FILE_NAME 에 선언되어 있습니다 (값: $LOCAL_HOST)"
###############################################################################
# /etc/hosts 파일에 해당 로컬 호스트가 이미 있을 경우
if grep -q "$LOCAL_HOST" "$HOSTS_FILE_PATH"; then
echo "> ✅ $HOSTS_FILE_PATH 에 $LOCAL_HOST 가 등록되어 있습니다"
else
# /etc/hosts 파일에 해당 로컬 호스트가 없을 경우 신규 추가
echo ""
echo "> 🚀 신규 로컬 호스트 [$LOCAL_HOST]를 $HOSTS_FILE_PATH 에 추가하기 위해 기기의 비밀번호(mac 비밀번호)를 입력하세요"
echo "127.0.0.1\t$LOCAL_HOST" | sudo tee -a "$HOSTS_FILE_PATH" >/dev/null
echo "> ✅ $HOSTS_FILE_PATH 에 신규 로컬 호스트 [$LOCAL_HOST]을 등록하였습니다"
fi
###############################################################################
# 로컬 호스트에 대한 https 인증서 생성
mkcert -key-file localhost-key.pem -cert-file localhost-cert.pem "$LOCAL_HOST"
[Step 4] start-local-server
스크립트 작성
공식 문서: Nextjs-Custom-Server
위 문서를 참고하여 커스텀 https 서버를 만들어보겠습니다.createServer
함수를 http
가 아닌 https
라이브러리에서 import 해야 함을 유의합니다.
[Step 3] 에서 생성한 인증서를 fs
라이브러리로 읽어옵니다.
/* eslint-disable @typescript-eslint/no-require-imports */
const { createServer } = require("https");
const { parse } = require("url");
const next = require("next");
const fs = require("fs");
require("dotenv").config();
const port = process.env.PORT;
const hostname = process.env.LOCAL_HOST;
const dev = process.env.NODE_ENV !== "production";
if (!port) throw new Error(" 🚨 PORT 환경변수가 설정되지 않았습니다!!");
if (!hostname) throw new Error(" 🚨 LOCAL_HOST 환경변수가 설정되지 않았습니다!!");
const app = next({ port, dev, hostname });
const handle = app.getRequestHandler();
console.log(" 🚀 로컬 서버 시작중...");
app.prepare().then(() => {
const localServer = createServer(
{
key: fs.readFileSync("localhost-key.pem"),
cert: fs.readFileSync("localhost-cert.pem"),
},
(req, res) => {
const parsedUrl = parse(req.url, true);
handle(req, res, parsedUrl);
}
);
localServer.listen(port);
console.log(` ✅ Ready: \t https://${hostname}:${port} \n`);
});
[Step 5] package.json
에 커맨드 추가
편의를 위해 package.json
에 만들어둔 script 실행문을 커맨드로 추가합니다.
"scripts": {
"setup": "sh src/scripts/setup.sh",
"dev": "node src/scripts/start-local-server.js",
...
},
[Step 6] local https server 실행
이제 모든 준비가 완료되었으니 동작을 확인해봅시다.setup
커맨드를 먼저 실행한 뒤, 인증서가 발급되면 dev
커맨드까지 실행합니다.
아래 사진과 같이 안전한 연결
까지 확인하시면 완전히 끝났습니다.
* 만약 안전하지 않은 연결
이 뜬다면 mkcert -install
을 다시 실행한 뒤 로컬 서버를 재실행해보세요. 시간이 지나면 자동으로 안전한 연결
로 변경될 수도 있습니다.
$ npm run setup
# '/etc/hosts' 파일에 접근하기 위해선 pc의 비밀번호가 필요합니다.
# 비밀번호를 입력하라는 화면이 노출되면 pc의 비밀번호를 입력하세요.
$ npm run dev

마무리
사실 Nextjs에서는 next dev
명령어 실행 시 --experimental-https
키를 통해 localhost를 바로 https로 실행하는 방법이 있긴 합니다. 🙂
그러나 shared cookie와 같이 domain이 중요한 상황이 올 때 https://localhost:PORT
로는 금방 한계를 느끼게 되는 것 같습니다.
프로젝트 세팅 시간을 줄이는데 도움이 되길 바라며 여기서 포스팅을 마치도록 하겠습니다.
예제 github repo도 같이 공유드립니다 local-https-example.github