XionWCFM의 로고 이미지

언제나 런타임 세이프하게 환경변수를 관리하는 방법

frontend
XionWCFM의 로고 이미지
유길종(XionWCFM)
2025.01.10. 15:00
오늘은 환경변수를 런타임 세이프하게 관리하는 방법을 다루어보고자 합니다.
서비스가 성장하며 다양한 서드파티 툴들을 사용하면 할수록 환경변수를 관리하는 일의 중요성은 더더욱 커지는데요
개발환경에서는 환경변수를 설정해두는 것이 자연스럽지만 다양한 배포환경에서 한번도 빼먹지 않고 환경변수를 잘 설정하는 것은 개발자 개개인의 꼼꼼함에 심하게 의존하게 되는 일 중 하나입니다.
또한 여러명의 개발자들이 각자의 개발환경에서 개발을 진행하다보니 환경변수에 대한 커뮤니케이션 비용이 생기기도 했는데요
그렇다보니 환경변수를 안전하게 관리할 방법을 고민하게 되었습니다.
이번에는 환경변수를 안전하게 관리하기 위한 도구인 @t3-oss 오픈소스 라이브러리를 도입한 경험을 공유해보고자 합니다.

t3-oss/env-core ?

이 라이브러리의 메인 컨셉은 zod와 같은 런타임 밸리데이터를 통해 런타임에 환경변수들이 제대로 주입되었는지를 검증하는 것입니다.
따라서 환경변수가 제대로 설정되지 않은 런타임에는 오류를 Throw하며 환경변수의 누락을 조기에 식별할 수 있습니다.
개인적으로는 이 오류 Throw를 통해 여러번의 환경변수 실수를 잡아낼 수 있었어서 매우 만족스러운 부분이었습니다.
컨셉자체가 간단한만큼 라이브러리의 구현체 역시도 매우 간단합니다. t3-oss/env-core의 구현체는 단 300여줄의 단일 파일로 구현되어있습니다.

적용방법

pnpm add @t3-oss/env-core
t3-oss는 자주 사용되는 프레임워크들에 대한 일류지원을 포함하기도 합니다. nextjs의 경우에는 t3-oss/env-nextjs 패키지가 있기도합니다.
다만 core를 사용해도 무방하기 때문에 저는 core를 선호하는 편입니다.
import { createEnv } from "@t3-oss/env-core";
import { z } from "zod";
 
export const env = createEnv({
  client: {
    NEXT_PUBLIC_GSC_ID: z.string().min(1),
  },
  server: {},
  shared: {
    NODE_ENV: z.enum(["development", "production", "test"]),
  },
  runtimeEnv: {
    NODE_ENV: process.env.NODE_ENV,
    NEXT_PUBLIC_GSC_ID: process.env.NEXT_PUBLIC_GSC_ID,
  },
  clientPrefix: "NEXT_PUBLIC", // 클라이언트에서도 노출되어야하는 환경변수 프리픽스를 적습니다. next.js는 NEXT_PUBLIC 프리픽스를 사용합니다.
  emptyStringAsUndefined: true, // 빈 문자열을 undefined로 처리하는 옵션입니다.
});
 
이렇게 작성해줄 수 있습니다.
이제 process.env. 으로 접근하는대신 해당 env 변수를 직접 import 하여 사용하면 끝입니다.

테스트 환경에서도 환경변수 주입이 필요합니다.

해당 툴을 도입하고 난 뒤 잘 통과되고 있던 테스트들이 실패하는 케이스가 발생했습니다.
이는 테스트 환경에서 적절하게 환경변수가 주입되지 않아 발생하는 문제였는데요
다행히 현재 사용하고 있는 vitest의 경우 환경변수 주입을 위한 configuration을 제공하고 있었습니다.
테스트 거짓음성 문제를 해결하기 위해 환경변수 관리를 도와주는 dotenv를 추가적으로 도입했습니다.
pnpm add -D dotenv
import { config } from "dotenv";
import { defineConfig } from "vitest/config";
 
export default defineConfig({
  test: {
    // ... otheroptions
    env: {
      ...config({ path: process.env.CI ? "./.env" : "./.env.local" }).parsed,
    },
  },
});
사실 테스트 환경에서의 환경변수 주입 필요성은 실제로 적용해보기 전까지는 전혀 예상하지 못했던 부분이었는데요
비교적 적은 비용으로 해결할 수 있는 문제였기 때문에 다행히 도입에 차질이 생기지는 않았습니다.

도입효과 체감

해당 방법을 도입한 이후 빌드 프로세스에서 환경변수 누락을 잡아내어 디버깅 비용을 절감하기도 했습니다.
저는 환경변수를 설정했다고 생각했지만 실제로 환경변수가 빌드타임에 주입되지 않은 경우가 있었기 때문인데요
Invalid environment variables: { GOOGLE_CLIENT_SECRET: [ 'Required' ], JWT_SECRET: [ 'Required' ] }
빌드 타임에 다음과 같은 오류를 받게되어 어떤 환경변수가 누락되었는지를 파악하고 미리 환경변수가 누락된 상태로 프로젝트가 배포되는 것을 방지할 수 있었습니다.

마치며

t3-oss의 경우 도입의 난이도에 비해 장기적으로 프로덕트의 안정성을 크게 끌어올려주는 도구라고 생각을 합니다. 환경 변수 관리에 대한 고민이 있는 팀이라면 도입을 추천드리고 싶습니다.
그럼 이번 포스트는 여기서 마무리하도록 하겠습니다. 읽어주셔서 감사합니다.
이런 글은 어때요?
XionWCFM을 나타내는 캐릭터 이미지
유길종(XionWCFM)
무언가를 쉽게 설명 해낼 때 쾌감을 느끼는 사람입니다. 현재는 프론트엔드 개발자로 일하고 있습니다.
© 2024 YuGilJong. All rights reservedAnimated icons by Lordicon.com