NextJS 에서는 보통 .env
파일을 활용하여 환경변수를 작성하게 되는데,
이 때는 build time에 반영되어 literal 로 변경되어 들어가게 된다.
NEXT_PUBLIC_URL='http://naver.com'
// build 후에는 'http://naver.com' 값으로 들어감.
const HOST_URL = process.env.NEXT_PUBLIC_URL;
그렇게 되면 docker image로 만들 때 빌드 타임에 이미 환경변수가 literal로 들어간 상태가 된다.
dev용, real 용 image를 각각 만들어야하는 것이다.
사내 플랫폼에서는 하나의 image로 dev랑 real을 다 사용하기를 권장하고 있었다. 이렇게 하면 image를 하나로만 할 수 있으니 빌드를 한번만 하면 됐고, 그래서 더 빠르게, 같은 결과물이니 신뢰성 있게 사용할 수 있다는 장점이 있었다. 하지만 그렇게 하기 위해서는 runtime에 환경 변수를 수정할 수 있어야했다.
찾아보니 다른 회사 분들이 쓴 글들이 있었는데 client 단에서는 window
객체에 넣어서 사용하고, server 단에서는 .env
파일을 사용하여 할 수 있도록 하는 방식이었다. 그것이 가능하도록 꽤나 긴 코드들이 필요했는데 이것을 쉽게 할 수 있도록 제공하는 라이브러리가 있어서 사용했다.
이 next-runtime-env
라이브러리도 위 설명과 같은 방식으로 동작하는 걸로 보였다.
next 13, 14 버전을 지원하고, .env
친화적이라 사용하기도 편했다.
사용하는 방식은
app/layout.tsx
에 아래와 같이 head에 PublicEnvScript 를 넣어주면 된다.
// app/layout.tsx
import { PublicEnvScript } from 'next-runtime-env';
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<PublicEnvScript />
</head>
<body>
{children}
</body>
</html>
);
}
그리고 사용하는 곳에서는 client 단이라면 아래와 같이 NEXT_PUBLIC 이 붙은 환경변수로 사용하면된다.
// app/client-page.tsx
'use client';
import { env } from 'next-runtime-env';
export default function SomePage() {
const NEXT_PUBLIC_FOO = env('NEXT_PUBLIC_FOO');
return <main>NEXT_PUBLIC_FOO: {NEXT_PUBLIC_FOO}</main>;
}
server 단이라면 NEXT_PUBLIC 없는걸로 사용하면 된다. (있는 것도 사용 가능)
import { env } from 'next-runtime-env';
import styles from './page.module.css';
export default function ServerSide() {
return (
<main className={styles.main}>
<div className={styles.description}>
<p>
BAR: {env('BAR') /* This is the same as process.env.BAR */}
<br />
BAZ: {process.env.BAZ}
</p>
</div>
</main>
);
}
실행할 땐 NEXT_PUBLIC_FOO=foo-value BAR=bar-value BAZ=baz-value npm run start
이렇게 실행해주면 된다고 하는데, 저렇게 일일이 적어줄 수는 없으니 .env.dev
.env.prod
를 만들어서 사용하고, docker 에서 실행시킬 때 환경변수로 expose 하게 작성하였다.
아래는 dockerfile의 마지막 일부분이다.
CMD bash -c '\
if [ "$PHASE" = "production" ]; then \
echo "Loading production environment"; \
export $(cat .env.prod | xargs); \
elif [ "$PHASE" = "develop" ]; then \
echo "Loading development environment"; \
export $(cat .env.dev | xargs); \
else \
echo "No PHASE set or invalid phase, loading default .env"; \
export $(cat .env | xargs); \
fi; \
exec node server.js'
처음에는 저 부분을 sh 파일로 만들어서 ENTRYPOINT로 실행하도록 하려고 했으나 계속 deployment 실패가 나서, 기존처럼 CMD 형식으로 고쳐서 사용하였다.
이렇게 사내에서 권장하는 방법으로 배포할 수 있게 되었고, image를 한번만 빌드하여 더 빠르게 배포할 수 있게 되었다.
참고 글