소개
안녕하세요! 프론트엔드 개발자 김동규입니다.
많은 개발자분들이 Next.js가 현재 개발 환경인지 배포 환경인지를 확인하는 방법은 잘 알고 계실거라고 생각해요. 저도 마찬가지였는데요.
최근 실무에서 Next.js로 만들어진 서비스에서 현재 상태가 빌드 중인지 확인해야만 하는 상황이 있었어요. 지금까지 개발해오면서 개발 환경, 테스트 환경, 배포 환경만 구분했지 빌드 상태를 확인해본 적은 없었어요.
이번 포스트에서는 제가 어떤 시도를 했고 결국에는 어떻게 문제를 해결했는지 이번 글을 통해 공유하려고 해요.
빌드 상태를 왜 알아야 하나요?
실무에서는 다수의 서비스가 지속적으로 개발되고 유지보수되고 있어요. 이러한 서비스들은 일반적으로 환경 변수를 사용하여 다양한 설정을 관리해요. 그러나 각 서비스마다 환경 변수를 개별적으로 관리하다 보면 일관성을 유지하기 어렵고, 변경 사항을 즉시 반영하기도 힘들어요. 이러한 문제를 해결하고자 환경 변수를 중앙에서 효율적으로 제어할 수 있는 플랫폼을 개발하여 운영을 하고 있어요.
해당 플랫폼에서는 환경 변수를 알려주는 API를 제공하고 있고, Next.js 서비스에서는 이 API를 next.config.js
파일에서 사용하고 있어요. 그러나 최근 도입한 로그 시스템을 통해 next dev
와 next start
명령어를 실행했을 때뿐만 아니라 next build
명령어를 실행할 때도 이 API가 호출되고 있다는 문제를 발견했어요.
next.config.js
파일은 next dev
, next build
, next start
명령어 모두에서 실행되기 때문에 이러한 비효율적인 API 호출이 발생했어요. 이 문제를 해결하기 위해서는 빌드 중에는 API를 호출하지 않도록 해야 해요. 이를 위해 현재 서비스가 빌드 중인지 확인하는 방법이 필요해요.
시도했던 방법들
NODE_ENV
Node.js 애플리케이션의 실행 환경을 지정하는 환경변수인 NODE_ENV
값으로 해결해보려고 했는데요.
아래의 표는 process.env.NODE_ENV
값을 next.config.js
파일에서 확인했을 때의 값이에요.
next dev | next build | next start |
development | production | production |
build와 start 명령어 때 동일한 값을 갖는 것을 확인할 수 있어요. 이러면 build 상태를 특정할 수 없어요. 이대로 포기해야 할까요?
아니요! 아직 방법 하나가 더 남았어요. 실행 스크립트에서 NODE_ENV 값을 수정하면 되지 않을까요?
{
"scripts": {
...,
"build": "NODE_ENV=build next build"
}
}
build를 실행해볼게요.
NODE_ENV 값이 "production"에서 "build"라는 값으로 변경되었어요! 하지만 Next.js 표준화되지 않은 값을 사용하고 있다고 경고를 하네요(원문 링크). 제공해주는 링크에서 전달하고자 하는 내용은 다음과 같아요.
다양한 라이브러리를 사용하여 운영중인 서비스에서 위와 같은 문제가 발생하다면 정말 큰일이겠죠? 아쉽지만 NODE_ENV는 사용하지 못했어요.
Next.js의 공식 문서에서도 NODE_ENV 값은 production, development, test 값을 가질 수 있다고 전달해주고 있어요(원문 링크).
Write file and Read file
이번에는 build 전에 json 파일을 생성하여 build 상태를 next.config.js
에 알려주는 방법이에요.
// updateEnv.js
const fs = require("fs");
const path = require("path");
// env.json 파일의 경로
const envFilePath = path.resolve(__dirname, "env.json");
// env.json 파일 읽기
fs.readFile(envFilePath, "utf8", (err, data) => {
if (err) {
console.error("Error reading env.json file:", err);
return;
}
// JSON 파싱
const envConfig = JSON.parse(data);
// status 값을 "build"로 변경
envConfig.status = "build";
// 변경된 내용을 다시 파일에 쓰기
fs.writeFile(envFilePath, JSON.stringify(envConfig, null, 2), "utf8", (err) => {
if (err) {
console.error("Error writing to env.json file:", err);
return;
}
console.log('Successfully updated env.json file with status "build".');
});
});
// package.json
{
"scripts": {
...,
"build": "node updateEnv.js && next build"
}
}
위와 같은 로직을 통해 build를 진행하게 되면 env.json 파일은 다음과 같이 작성되는데요.
{
"status": "build"
}
// next.config.mjs
import fs from "fs";
let envConfig = {};
try {
const data = fs.readFileSync("./env.json", "utf8");
envConfig = JSON.parse(data);
} catch (err) {
console.error("Error reading env.json file:", err);
}
console.log(`envConfig.status: ${envConfig.status}`);
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default nextConfig;
envConfig.status 값은 "build" 값을 가지고 있는 것을 확인할 수 있는데요. 벌써 정답을 찾은 느낌이에요. 너무 순조롭네요. 이 방식은 문제 해결에 도움이 되지만 최선이라고 할 수 없어요. 이유가 뭘까요?
Next.js로 개발된 서비스는 보통 서비스 개발자들이 관리해요. 하지만 위와 같은 기능은 플랫폼 개발자인 제 몫인데요. 그렇다면 해당 서비스에 새로 생긴 이 코드 로직은 누가 관리해야 하는 걸까요? 플랫폼 API와 밀접하게 연계되었으니 수많은 서비스에 사용되는 이 로직을 일일이 제가 관리해야 할까요? 아니면 서비스 개발자들에게 인수인계를 하고 관리 담당을 넘길까요? 하지만 그렇게 된다면 서비스 개발자들이 신경써야 하는 업무량이 조금이나마 늘어나지 않을까요?
제가 속한 팀은 최대한 서비스 개발자들이 비즈니스 업무에만 집중할 수 있도록 환경 조성에 신경쓰고 있어요. 저희 팀의 이념과 상반되어 파일을 쓰고 읽는 방법은 후순위로 두기로 했어요.
Webpack
next.config.js
파일에서는 webpack 옵션을 제공해요. 해당 옵션에서는 buildId 값을 제공해주는데요.
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config, { buildId, nextRuntime }) => {
console.log(`buildId: ${buildId}`);
return config;
},
};
export default nextConfig;
buildId는 next build
시에 생기는 .next
폴더 하위에 존재하는 BUILD_ID
값을 알려줘요. 하지만 문제는 next start
명령어를 실행할 때도 BUILD_ID
값이 존재해요. 이렇게되면 next build
와 next start
를 구분할수가 없어요.
흠... 어떻게 해야 서비스 개발자들에게 최소한의 영향을 미치면서 build 상태를 알아낼 수 있을까요?
process.argv
Node.js 애플리케이션이 실행될 때 명령 줄 인수를 배열 형태로 제공하는 process.argv
로 이 문제를 해결할 수 있었는데요. 사실 이 해결 방법은 파일을 읽고 쓰는 방법에서 떠올랐어요. env.json
파일을 제대로 제공하기 위해서는 build뿐 아니라 dev, start 명령어일 때도 status 값이 바뀌어야 해요. 각 명령어를 어떻게 가져올까 하다가 떠올랐죠!
process.argv 배열의 첫번째 요소는 Node.js 실행 경로, 두번째 요소는 Next.js 실행 경로고 세번째 요소부터 명령 줄 인수 값이에요.
출력해보면 세번째 요소의 값에서 "build" 값을 확인할 수 있어요. next dev
는 dev 값을 가지고 next start
는 start 값을 가져요.
이 정보를 토대로 저는 API를 사용하기 위해 제공하는 라이브러리 내에서 코드를 수정하여 배포했어요. 결국, 서비스 개발자들은 새로운 로직을 관리할 필요가 전혀 없는 방법으로 문제를 해결했어요.
마무리
process.argv
는 Node.js 개발을 하다보면 정말 자주 만나게 되는 속성이에요. 이번 경험을 통해 문제 해결을 위해 멀리 돌아가지 말고 가까운 곳부터 살펴보는 것이 중요하다는 점을 배웠어요.
트러블 슈팅 과정은 때로는 멀고 험할 수 있으며, 때로는 짧고 순조로울 수도 있어요. 물론 해결하지 못하는 문제도 분명히 있을 거에요. 과정과 결과 모두 중요하지만, 최선을 다해 꼭 해내겠다는 마음가짐을 갖는게 더 중요한 것 같아요.
감사합니다.