← 전체 글로 돌아가기

웹 개발

배열 필터할 때 환경변수를 안전하게 쓰는 방법

환경변수는 런타임에 동적으로 정해진다. 배열 필터 같은 로직에서 쓸 때는 타입 체크와 기본값이 필수다.

배열을 필터링하거나 매핑할 때, 환경변수에서 읽은 값을 쓴다. 하지만 환경변수는 항상 문자열이고, 존재하지 않을 수 있고, 예상과 다른 값일 수 있다.

첫 번째: 환경변수를 먼저 파싱하고 검증한다

// 문제: 환경변수를 직접 쓴다
const minPrice = process.env.MIN_PRICE;
const items = products.filter(p => p.price >= minPrice); // string과 number 비교!

// 해결: 파싱과 검증
function getMinPrice() {
  const raw = process.env.MIN_PRICE;
  if (!raw) {
    return 0; // 기본값
  }

  const parsed = parseFloat(raw);
  if (isNaN(parsed) || parsed < 0) {
    console.warn(`Invalid MIN_PRICE: ${raw}, using default 0`);
    return 0;
  }

  return parsed;
}

const minPrice = getMinPrice();
const items = products.filter(p => p.price >= minPrice);

환경변수는 항상 문자열이므로, 필요한 타입으로 변환하고 유효성을 검사해야 한다.

두 번째: 환경변수 스키마를 정의한다

// zod를 쓴 예제
import { z } from 'zod';

const envSchema = z.object({
  MIN_PRICE: z.string().optional().transform(v => v ? parseFloat(v) : 0),
  MAX_PRICE: z.string().optional().transform(v => v ? parseFloat(v) : 10000),
  ALLOWED_CATEGORIES: z.string().optional().transform(v => v ? v.split(',') : []),
  DEBUG: z.string().optional().transform(v => v === 'true')
});

const config = envSchema.parse(process.env);

// 이제 config.MIN_PRICE는 number, config.ALLOWED_CATEGORIES는 string[]
const items = products.filter(p =>
  p.price >= config.MIN_PRICE &&
  p.price <= config.MAX_PRICE &&
  config.ALLOWED_CATEGORIES.length === 0 || config.ALLOWED_CATEGORIES.includes(p.category)
);

스키마로 환경변수를 한 곳에서 정의하고 관리한다.

세 번째: 배열 필터에서 null/undefined를 처리한다

const statusFilter = process.env.FILTER_STATUS?.split(',') || [];

// 문제: 빈 배열인지, 정의되지 않았는지 구분 안 함
const filtered = items.filter(item => {
  if (statusFilter.length === 0) return true; // 필터 없음
  return statusFilter.includes(item.status);
});

// 더 안전한 버전
function createStatusFilter() {
  const raw = process.env.FILTER_STATUS;
  if (!raw) {
    return null; // 필터 없음
  }

  return raw.split(',').filter(s => s.trim().length > 0);
}

const statusFilter = createStatusFilter();
const filtered = items.filter(item => {
  if (!statusFilter) return true; // 필터 없음
  return statusFilter.includes(item.status);
});

undefined와 빈 배열을 명확히 구분한다.

네 번째: 환경변수가 여러 개일 때 AND/OR 로직을 정한다

const config = {
  minPrice: parseFloat(process.env.MIN_PRICE || '0'),
  maxPrice: parseFloat(process.env.MAX_PRICE || '10000'),
  categories: (process.env.FILTER_CATEGORIES || '').split(',').filter(Boolean),
  tags: (process.env.FILTER_TAGS || '').split(',').filter(Boolean)
};

// AND 로직: 모든 조건을 만족해야 함
const filtered = items.filter(item => {
  if (item.price < config.minPrice || item.price > config.maxPrice) {
    return false; // 가격 범위 밖
  }

  if (config.categories.length > 0 && !config.categories.includes(item.category)) {
    return false; // 카테고리 아님
  }

  if (config.tags.length > 0 && !item.tags.some(t => config.tags.includes(t))) {
    return false; // 태그 매칭 없음
  }

  return true;
});

AND와 OR 로직을 명확히 정한다.

다섯 번째: 환경변수 변경을 감지하고 로그한다

// 앱 시작 시 설정을 출력
console.log('Configuration loaded:', {
  minPrice: config.minPrice,
  maxPrice: config.maxPrice,
  categories: config.categories.join(',') || 'all',
  tags: config.tags.join(',') || 'all'
});

// 또는 특정 변경 시 경고
if (config.minPrice > config.maxPrice) {
  console.warn('WARNING: MIN_PRICE > MAX_PRICE, results may be empty');
}

if (config.categories.length === 0) {
  console.warn('WARNING: No categories specified, filtering disabled');
}

설정 로그를 출력하면, 배포 후 예상과 다른 동작을 디버깅할 때 도움이 된다.

체크리스트

  1. 환경변수를 즉시 파싱하고 타입 변환한다
  2. 파싱 실패 시 기본값을 정한다
  3. zod나 env-validator 같은 라이브러리로 스키마를 정의한다
  4. 배열 필터에서 null/undefined/빈 배열을 구분한다
  5. AND/OR 로직을 명시적으로 정한다
  6. 앱 시작 시 최종 설정을 로깅한다
  7. 필터 결과가 예상과 다르면, 먼저 설정 로그를 확인한다

환경변수는 문자열이고, 런타임에만 알 수 있고, 배포 환경마다 다르다. 배열 필터에서 쓸 때는 예방적으로 타입 체크를 하자.