개요
현재 Next.js 13 이상 버전을 사용하고 있다면, 일반적으로 배포에 제약이 없다면 Vercel을 통해 배포를 한다면 큰 문제가 없을 수 있습니다. 하지만 사내 배포 환경을 이용하기 위해 K8S 환경 내 CloudNative Buildpack을 통해 배포하거나 별도 배포 환경을 위해 Dockerfile을 작성하여 이미지를 만드는 경우는 고려해야할 점이 있었습니다.
현재 개발 배포 환경은 Next.js 13 App Router, PNPM, turborepo, Hashcorp Vault, Dockerfile, Tekton CI/CD를 이용하고 있습니다. 브랜치 전략을 통한 개발, QA, 라이브 환경 등 다양한 환경이 CI/CD에 적용되어 있고 때로는 개발환경에서 사용한 이미지를 그대로 QA에 그리고 라이브에서 사용해야할 일이 꽤나 빈번하게 있습니다. 이 때마다 이미지를 새롭게 만들지 않고 환경 변수를 교체하여 사용하기 위해 import-meta-env를 고려하게 됐습니다.
기존환경 및 방향성
Dev환경만 구성했을 때 .env와 Hashcorp Vault에서 주입하여 만드는 Dockerfile에 환경 변수에 종속적인 이미지를 만들고 있었습니다. 환경 변수 정보를 runtime-env인 import-meta-env를 이용해서 주입하여 이미지를 만들땐 key값만 갖도록하고 runtime에서 value를 넣어주는 방식을 통해 하나의 이미지를 재활용할 수 있도록 하는 방향으로 변경할 예정입니다.
적용
import-meta-env에서 환경별 예제를 잘 갖고 있습니다.현재 사용하려는 패키지는 Next.js 기반이고 GitHub에 관련 예제를 포함하고 있습니다.
1. 모듈 설치
pnpm add -D @import-meta-env/unplugin
pnpm add @import-meta-env/cli
2. .env.example.public 생성
// .env.example.public
NEXT_PUBLIC_HELLO=
HELLO=
3. next.config.js webpack에 @import-meta-env/unplugin 적용
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// ...
webpack: (config) => {
config.plugins.push(
require("@import-meta-env/unplugin").webpack({
example: ".env.example.public",
}),
);
return config;
},
};
module.exports = nextConfig;
4.globalThis에 runtime으로 적용할 import_meta_env_placeholder 작성
// /app/layout.tsx
import Script from "next/script";
...
return (
...
<Script
dangerouslySetInnerHTML={{
__html: `globalThis.import_meta_env=JSON.parse('"import_meta_env_placeholder"')`,
}}
/>
...
)
* 로컬 환경에서는 .env.development.local과 같은 파일을 사용해야하고, 빌드된 결과물을 production으로 실행하면 해당 스크립트가 CI/CD에서 지정한 env로 변경됩니다.
5.process.env.NEXT_PUBLIC_HELLO를 import.meta.env.NEXT_PUBLIC_HELLO로 변경
- process.env.NEXT_PUBLIC_HELLO
+ import.meta.env.NEXT_PUBLIC_HELLO
6.npx import-meta-env
npx import-meta-env -x .env.example
<script>
- globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"')
+ globalThis.import_meta_env = JSON.parse('{"NEXT_PUBLIC_HELLO":"production"}');
</script>
CI 환경(K8S YAML)에서 import-meta-env를 위와 같이 실행시켜주면 런타임에서 import_meta_env_placeholder 결과값이 기대했던 env로 변경됩니다.
이슈
중간에 두 가지 이슈가 생겼었는데,
첫째로 로컬 docker에서는 잘 작동했지만 K8S Harbor에 올린 docker image를 실행하면 npx import-meta-env를 실행할 수 없었습니다. 원인은 컨테이너에 빌드 결과물을 복사 해올 때 유저와 그룹을를 직접 생성하여 chown 권한을 줬었는데 알고보니 K8S에서 해당 유저를 갖고 있어서 permission 문제가 있었습니다.
adduser --uid $UID
addgroup --gid $GID
두번째로, Next.js 13 App Router에서 만날 수 있는 문제입니다. layout.tsx, page.tsx, route.tsx는 전부 dynamic auto 상태입니다.
export const dynamic = 'auto'
// 'auto' | 'force-dynamic' | 'error' | 'force-static'
auto는 가능한 많이 캐싱하기 위해 동적으로 변하는 값을 차단하는데 기본 옵션으로 설정되어 있죠. 과거 getServerSideProps와 같이 이용하기 위해서는 export const dynamic = 'force-dynamic'을 설정하지 않으면 동적으로 환경변수가 변하지 않았던 오류가 있었습니다.
참조
https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config
https://github.com/runtime-env/import-meta-env/tree/main/packages/examples/create-next-app-example
https://import-meta-env.org/
'software engineering > frontend' 카테고리의 다른 글
Preact 그리고 Signals (0) | 2024.02.14 |
---|---|
Zustand Persist (부제: localstorage 쉽게 저장하기) (1) | 2024.02.12 |
JSX Conditional rendering && 와 삼항연산자 (0) | 2023.12.31 |
React TypeScript Cheatsheet (0) | 2023.12.19 |
UX 라이팅 (0) | 2023.11.19 |