웹 개발
배열 필터할 때 환경변수를 안전하게 쓰는 방법
환경변수는 런타임에 동적으로 정해진다. 배열 필터 같은 로직에서 쓸 때는 타입 체크와 기본값이 필수다.
배열을 필터링하거나 매핑할 때, 환경변수에서 읽은 값을 쓴다. 하지만 환경변수는 항상 문자열이고, 존재하지 않을 수 있고, 예상과 다른 값일 수 있다.
첫 번째: 환경변수를 먼저 파싱하고 검증한다
// 문제: 환경변수를 직접 쓴다
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');
}
설정 로그를 출력하면, 배포 후 예상과 다른 동작을 디버깅할 때 도움이 된다.
체크리스트
- 환경변수를 즉시 파싱하고 타입 변환한다
- 파싱 실패 시 기본값을 정한다
- zod나 env-validator 같은 라이브러리로 스키마를 정의한다
- 배열 필터에서 null/undefined/빈 배열을 구분한다
- AND/OR 로직을 명시적으로 정한다
- 앱 시작 시 최종 설정을 로깅한다
- 필터 결과가 예상과 다르면, 먼저 설정 로그를 확인한다
환경변수는 문자열이고, 런타임에만 알 수 있고, 배포 환경마다 다르다. 배열 필터에서 쓸 때는 예방적으로 타입 체크를 하자.