개발 slecs

SEO DB 모듈을 빌드 타임에서 런타임 전용으로 전환한 방법

목차

SEO 메타데이터를 관리하는 데이터베이스 모듈을 런타임에만 로드해야 하는 상황을 마주쳤다. 빌드 단계에서 불필요하게 실행되는 import를 피하기 위해 new Function() 패턴을 도입하면서 설정과 레이아웃 코드를 정리한 작업이다.

왜 이 문제가 발생했나

Next.js의 App Router 구조에서는 개발자가 명시하지 않아도 빌드 중에 특정 코드가 서버에서 실행된다. 특히 layout.tsxpage.tsx 같은 라우트 파일에서 top-level import는 빌드 시점에 평가되는데, SEO DB 모듈이 런타임 환경(예: 데이터베이스 연결, API 호출)에 의존하면 빌드가 실패하거나 예기치 않은 side effect가 발생한다.

일반적으로 이런 문제는 다음 상황에서 나타난다:
- 모듈이 파일 시스템이나 네트워크에 접근하는 초기화 코드를 포함
- 환경 변수(개발 vs 프로덕션)에 따라 다르게 동작
- DB 연결 풀이 필요한 경우

new Function()으로 런타임 로드 구현

기존에는 아마도 이렇게 직접 import했을 것이다:

// ❌ 문제 있는 방식 - 빌드 시 실행됨
import seoDB from './seo_db.mjs';

이를 동적으로 변경했다:

// ✅ 해결책 - 런타임에만 실행됨
const seoDB = await new Function('return import("./seo_db.mjs")')();

new Function()으로 감싸면 함수 몸체의 코드가 즉시 평가되지 않고, 실제 호출 시점(런타임)에 실행된다. 빌드 단계에서 번들러가 정적 분석을 할 수 없으므로 이 import는 스킵된다.

방식 실행 시점 용도
정적 import 빌드 타임 순수 함수, 상수, 타입 정의
동적 import() 런타임 (lazy) 큰 번들, 조건부 로드
new Function() + import() 런타임 (강제) 빌드 타임 실행 방지 필수

변경 파일의 역할

next.config.ts: 번들러 설정과 모듈 해석 방식을 조정했을 가능성이 높다. SEO DB의 .mjs 확장자 처리나, 특정 모듈을 번들에 포함하지 않도록 설정했을 수 있다.

src/app/layout.tsx: 루트 레이아웃에서 페이지마다 필요한 SEO 메타데이터를 동적으로 생성하는 부분이다. 아마 generateMetadata() 함수나 비슷한 동적 메타데이터 로직에서 SEO DB를 호출하는데, 이제 런타임에 안전하게 로드하도록 변경한 것 같다.

트레이드오프와 고민

이 방식의 장점은 명확하다: 빌드 안정성을 확보하고, 런타임 환경과 완전히 분리된다. 단점도 있다.

  • 코드 복잡도 증가: 문자열 기반 함수 생성은 정적 분석 도구(IDE, 타입 체커)가 이해하기 어렵다
  • 성능 약간의 오버헤드: 매 호출마다 함수 인스턴스를 생성하므로, 적어도 처음 로드는 약간 느리다
  • 디버깅 난이도: 스택 트레이스가 복잡해질 수 있다

처음에는 동적 import(import())만으로도 해결될지 고민했을 텐데, 번들러가 여전히 정적 분석으로 감지하는 경우가 있어서 new Function()까지 필요했던 것 같다.

앞으로의 고려 사항

비슷한 문제가 다시 나타날 때는, 먼저 "이 모듈이 정말 런타임에만 필요한가?"를 묻는 게 좋다. 가능하면 빌드와 런타임을 분명히 구분하거나, mock/stub 데이터를 제공해서 빌드 단계를 우회하는 방법도 있다. 하지만 SEO DB처럼 동적 데이터가 필수라면, 이 패턴이 현실적인 선택지다. 팀원들과 이 코드를 리뷰할 때 왜 이런 방식이 필요한지 명확히 남겨두면, 나중에 유지보수할 때 무분별한 리팩토링을 피할 수 있다.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.