middleware의 matcher에 변수를 사용할 수 없는 이유

Next.js

문제

특정 페이지에서 JavaScript가 실행되지 않고 화면이 깨지는 현상이 발생했다. 흥미로운 점은 middleware matcher에 정의된 여러 경로 중 /signup 페이지에서만 이 문제가 나타났다는 것이다.

Image.png

export const config = {
  matcher: [
    '/',
    '/account',
    Path.ITEMS, // 여기에 변수 사용!
    '/blog/:path*',
    '/user/:path*',
    '/signin',
    '/signup',
    // ...
  ]
};

원인

Next.js 공식 문서의 설명

Next.js 공식 문서에 따르면, middleware matcher의 값은 빌드 시점에 정적으로 분석할 수 있어야 하며, 변수와 같은 동적 값은 무시된다고 설명되어 있다.

처음에는 Path.ITEMS를 config 바로 위에 const로 선언했기 때문에 문제가 없을 것이라 생각했다. 하지만 실제로는 문제가 발생했고, 그 이유를 찾기 위해 Next.js의 소스 코드를 분석해보았다.

Next.js의 Config 분석 과정

아래는 Next.js의 실제 코드 중 일부이며, SSG나 middleware등의 설정을 분석할 때 다음과 같은 과정을 거친다

export async function getPageStaticInfo(params: {
  nextConfig: Partial<NextConfig>
}): Promise<PageStaticInfo> {
  const fileContent = (await tryToReadFile(pageFilePath, !isDev)) || ''
  if (/runtime|getStaticProps|getServerSideProps|matcher/.test(fileContent)) {
    const swcAST = await parseModule(pageFilePath, fileContent)
    const config = tryToExtractExportedConstValue(swcAST, 'config') || {}
    // ...
  }
}

이 코드에서 중요한 점은 다음과 같다.

  1. 설정 파일을 먼저 읽음
  2. config라는 이름의 변수를 찾음
  3. AST(Abstract Syntax Tree)를 생성하고 파싱

더 자세히 살펴보면, Next.js는 const 선언만을 허용한다

export function extractExportedConstValue(
  module: Module,
  exportedName: string
): any {
  for (const moduleItem of module.body) {
    if (!isExportDeclaration(moduleItem)) continue;

    const declaration = moduleItem.declaration
    if (!isVariableDeclaration(declaration)) continue;

    if (declaration.kind !== 'const') continue;
    // ...
  }
}

이 코드는 const로 선언되지 않은 값들은 추출하지 않는다는 것을 보여준다.

SWC's AST와 정적 분석

Next.js는 SWC(Speedy Web Compiler)를 사용하여 코드를 파싱하고 AST를 생성한다. 이 과정에서 변수를 사용한 설정은 정적 분석이 어렵기 때문에, 빌드 타임에 최적화하기 어려워진다.

정적 분석이 가능한 올바른 설정 예시:

// ✅ 좋은 예
export const config = {
  matcher: [
    '/',
    '/account',
    '/items', // 직접 문자열 사용
    '/blog/:path*',
  ]
};

남은 의문점

왜 다른 페이지들은 정상 작동하고 /signup 페이지에서만 문제가 발생했는지에 대해서는 알아내지 못했다. 이는 아마도 Next.js의 빌드 프로세스나 코드 분할(code splitting) 과정에서의 특이점과 관련이 있을 것으로 추측된다.

결론

Next.js middleware matcher에서 변수를 사용할 때는 주의가 필요하다. 빌드 타임 최적화와 정적 분석을 위해 직접적인 문자열을 사용해야 한다.