Next.js 프로젝트를 완료한 후 배포환경을 설정하던 도중 예상치 못한 난관에 마주했다.
Google Cloud Platform의 App Engine을 사용해 배포하는데
문제는 project-dev서비스와 project-prd서비스로 나누어 각각 환경변수를 다르게 사용하려했다.
dev서비스에는 .env.development 파일을, prd서비스에는 .env.production파일을 사용하도록 하려는데
지금까지 시도해본 모든 방법, 설정은 모두 한쪽만 다르게 설정해내지 못했다.
내가 생각한 순서도에 따르면 다음과 같다.
"scripts": {
"dev": "next dev",
"lint": "next lint",
"start:dev": "APP_ENV=development next start",
"start": "APP_ENV=production next start",
"build:dev": "APP_ENV=development next build",
"build": "APP_ENV=production next build",
"deploy:dev": "gcloud app deploy --project='project-dev' -q --appyaml=dev_app.yaml",
"deploy": "gcloud app deploy --project='project-prd' -q --appyaml=prd_app.yaml"
}
deploy를 통해서 GCP App Engine에 업로드
runtime: nodejs18
service: next-project-develop
entrypoint: "npm run start:dev"
env_variables:
APP_ENV: "development"
App Engine "next-project-develop"에서 app.yaml을 읽어 빌드 -> entrypoint를 읽어 npm run start:dev 실행
const dotenv = require("dotenv");
const path = require("path");
const nextConfig =()=> {
const envPath = path.resolve(__dirname, `.env.${process.env.APP_ENV}`);
const envConfig = dotenv.config({ path: envPath }).parsed;
return {
env: envConfig,
reactStrictMode: false,
swcMinify: true,
};
}
module.exports = nextConfig();
이 과정에서 next.config.js를 읽는데 env는 APP_ENV이름에 따라 주입되도록 했으니
development는 .env.development를 주입하고
production은 .env.production을 주입해야 맞다.
로컬에서 npm run build:dev, npm start:dev를 실행했을때 완벽히 내가 원하는 결과물이 도출된다.
또한 :dev를 제외하고 실행하면 production이 잘 주입된다.
문제는 App Engine Service에서는 둘다 production만 주입된다는 점
원인.
아무래도 빌드자체가 npm run build만 실행되는듯 보인다.
시도방법
1. next.config.js에서 phase를 이용
마찬가지로 app engine에서 next start명령을 하기때문에 production으로 인식한다.
2. .env파일명 변경
의미가 없었다. .env, .env.local, .env.development, .env.production, .env.test 등
지원되는 모든 .env파일명을 시도해봤다.
3. yaml에서 env valuables 직접설정
주입되지 않는다. 원인은 파악중.
해결방법
1. next.config.ts에서 output, distDir을 사용해서 빌드파일만 분리 한다.
const dotenv = require("dotenv");
const { PHASE_PRODUCTION_BUILD } = require("next/dist/shared/lib/constants");
const path = require("path");
const nextConfig = (phase) => {
const envPath = path.resolve(__dirname, `.env.${process.env.APP_ENV}`);
const envConfig = dotenv.config({ path: envPath }).parsed;
if (phase === PHASE_PRODUCTION_BUILD) {
return {
env: envConfig,
reactStrictMode: false,
swcMinify: true,
output: "export",
distDir: "../build",
};
} else {
return {
env: envConfig,
reactStrictMode: false,
swcMinify: true,
};
}
};
module.exports = nextConfig;
설정된 내용은 다음과 같다.
1. local docker develop환경을 위해 phase값을 읽어 PHASE_PRODUCTION_BUILD인 경우에만 output, distDir을 사용
2. static으로 사용해도 무방한(b2b admin) 서비스이기 때문에 output을 사용해 html추출
3. 루트프로젝트 바깥으로 build(static build html)추출
2. 빌드를 시도한다.
"scripts": {
"dev": "next dev",
"lint": "next lint",
"start:dev": "APP_ENV=development node app.js",
"start": "node app.js",
"build:dev": "APP_ENV=development next build",
"build": "APP_ENV=production next build",
},
development 배포 시에는 npm run build:dev
production 배포 시에는 npm run build
3. 루트프로젝트 바깥으로 디렉토리를 이동해 파일을 설정한다.
바깥에서도 간단히 설정해줄 것이 있다.
우선 app_dev.yaml, app_prd.yaml을 생성해 각각 서비스명만 다르게 작성한다.
runtime: nodejs18 # or another supported version
service: next-project-develop
이후 app.js를 하나 생성해준다.
const express = require("express");
const app = express();
const server = require("http").createServer(app);
app.get("/", function (req, res) {
res.sendFile(__dirname + "/build/index.html", {
etag: false,
lastModified: false,
});
});
app.use(
"/",
express.static(__dirname + "/build", { etag: false, lastModified: false })
);
app.get("/*", function (req, res) {
res.sendFile(__dirname + "/build/index.html", {
etag: false,
lastModified: false,
});
});
server.listen(8080, function () {
console.log("listening on port 8080");
});
추출된 static build 를 실행해주는 파일이다.
4. 마지막으로 package.json을 다음과같이 작성하고 npm install, deploy해준다.
{
"name": "barofactory",
"version": "1.0.0",
"description": "",
"main": "app.js",
"homepage": ".",
"scripts": {
"start": "node app.js",
"deploy:dev": "gcloud app deploy --project='project-dev' -q --appyaml=dev_app.yaml",
"deploy": "gcloud app deploy --project='project-prd' -q --appyaml=prd_app.yaml"
},
"author": "",
"engines": {
"node": ">=8"
},
"license": "ISC",
"dependencies": {
"express": "^4.17.1"
}
}
start는 app.js를 사용해서 하도록하고
각각 배포될 GCP AppEngine 저장소를 분기해 명령어를 작성해 두었다.
빌드 된 파일들을 development쪽으로 배포하려면 npm deploy:dev
production으로 배포하려면 npm deploy를 실행하면 드디어 환경변수도 정상 작동하는 것을 볼 수 있다.
주의할 점으로는 빌드 되어 추출된 파일만 업로드 하므로
환경변수 적용이 빌드가 dev로 되었다면 dev가 배포되고 production으로 되었다면 production으로 빌드된다.
요점은 빌드를 조심하자 라는 것이다.
이번 고민을 해결하면서 하나 더 장점으로 얻어가는 것이 있다면
기존 배포방식에서 용량이 4~500MB였고 시간은 약 배포 당 15분 소요되었다.
새 배포방식에서는 용량이 8.1MB로 고정됐고 시간은 약 배포당 8분가량으로 획기적인 단축이 이뤄졌다.
Server Side Rendering이 무조건 적으로 필요하지 않다면
가볍고 빠른 배포, 수정이 가능한 이 방법이 좋을 것 같다는 생각이 든다.
다음엔 이 귀찮은 배포를 자동으로 해결해줄 CI/CD를 알아볼 예정이다.
해결완료!!
'고민, 이슈 정리 > 이슈, 버그' 카테고리의 다른 글
[JavaScript] Global Locale 다국어 지원하기 (3) | 2025.04.11 |
---|---|
[해결]moment.js deprecated이슈로 인해 datetime 라이브러리 교체 (feat. day.js) (0) | 2023.08.29 |
[해결] Invalid date cross-browsing 날짜객체 에러(크로스 브라우징) (0) | 2023.08.24 |
[미해결] React Infinite UpScrolling (0) | 2023.05.30 |
[해결] Socket.io 웹뷰 접속지연 (0) | 2023.05.16 |