[{"data":1,"prerenderedAt":20},["ShallowReactive",2],{"$f5uB0m0rAdDTKlvvGJpe9Gf5fRvCnUWB8S4OCyhbu4Zw":3},{"uuid":4,"title":5,"summary":6,"content":7,"createdAt":8,"tags":9},"0bbfc97f-f39f-41bd-b382-f3effd4c2277","Next.js 'output: export' 환경에서 GitHub Pages 경로 문제 해결기: basePath와 정적 자산 로딩","Next.js 앱을 'output: export'로 GitHub Pages에 정적 배포했을 때, 'basePath' 설정에도 불구하고 AI 모델과 스티커 같은 'public' 디렉토리 내 자산의 경로가 어긋나 로딩에 실패하는 문제를 겪었다. 이 글은 그 원인을 파악하고 해결한 과정을 다룬다.","최근 HDomi\u002Fframe-pick 프로젝트를 GitHub Pages에 배포하는 과정에서 예상치 못한 경로 문제를 마주했습니다. Frame Pick은 유튜브 썸네일 생성 및 편집기로, 100% 클라이언트 사이드에서 동작하며 Next.js 16 (App Router)과 TypeScript를 기반으로 합니다. 서버 비용을 0으로 만들기 위해 `next.config.ts`에서 `output: 'export'` 옵션을 설정하여 완전한 정적 HTML 파일들로 빌드하고, GitHub Pages의 저장소 이름을 따라 `basePath: '\u002Fframe-pick'`을 지정했습니다. 배포 후 웹 애플리케이션에 접속했을 때, UI는 정상적으로 로드되는 것처럼 보였지만, AI 누끼 모델인 `public\u002Fmodels\u002Fu2netp.onnx`와 스티커 이미지들 (`public\u002Fstickers\u002F*.svg`)이 로딩되지 않아 핵심 기능들이 동작하지 않았습니다. 개발자 도구의 네트워크 탭에서는 해당 자산들에 대한 404 Not Found 에러가 발생하고 있었습니다.\n\n## 초기 진단: `basePath` 설정 검토\n\n문제의 원인을 파악하기 위해 가장 먼저 `next.config.ts` 파일을 확인했습니다. Next.js의 `basePath` 옵션은 애플리케이션이 루트 경로가 아닌 서브 경로, 예를 들어 `https:\u002F\u002Fusername.github.io\u002Fframe-pick`와 같이 배포될 때 필요한 설정입니다. 이 옵션이 제대로 설정되어 있다면 Next.js는 모든 내부 링크와 자신이 생성하는 자산(JavaScript, CSS 번들 등)의 URL에 이 `basePath`를 자동으로 접두사로 붙여줍니다. 제 `next.config.ts`에는 `basePath: '\u002Fframe-pick'`가 명확히 설정되어 있었고, 실제로 `_next`로 시작하는 Next.js 코어 자산들(예: `\u002F_next\u002Fstatic\u002Fchunks\u002F...`)은 `https:\u002F\u002Fusername.github.io\u002Fframe-pick\u002F_next\u002F...`와 같이 `basePath`가 적용된 올바른 경로로 정상적으로 로드되는 것을 확인했습니다. 이는 `basePath` 설정 자체는 문제가 없음을 의미했습니다.\n\n## 원인 파악: 프로그램적 자산 로딩의 맹점\n\n핵심은 `public` 디렉토리 내에 직접 위치한 자산들이었습니다. 브라우저 개발자 도구의 네트워크 탭을 자세히 살펴보니, AI 모델 파일(`u2netp.onnx`)이나 스티커 SVG 파일들에 대한 요청 경로가 `https:\u002F\u002Fusername.github.io\u002Fmodels\u002Fu2netp.onnx` 또는 `https:\u002F\u002Fusername.github.io\u002Fstickers\u002Farrows\u002Farrow_curve.svg`와 같이 `basePath`인 `\u002Fframe-pick`이 누락된 채 요청되고 있었습니다. 즉, 브라우저는 `basePath`를 무시하고 웹사이트의 루트 도메인(`https:\u002F\u002Fusername.github.io`)을 기준으로 절대 경로를 해석하고 있었습니다.\n\n이러한 현상이 발생하는 이유는 `public` 디렉토리의 자산들이 Next.js의 `\u003CImage>` 컴포넌트나 `\u003CLink>` 컴포넌트처럼 프레임워크의 경로 처리 로직을 통하는 방식이 아니었기 때문입니다. 대신 Fabric.js의 `loadSVGFromURL` 함수나 `onnxruntime-web`의 모델 로딩 함수 등 클라이언트 측 JavaScript 코드에서 `\u002Fmodels\u002Fu2netp.onnx`와 같이 슬래시(`\u002F`)로 시작하는 절대 경로 문자열을 직접 구성하여 자산을 `fetch`하거나 `new Image().src` 등으로 로드하고 있었습니다. Next.js의 `basePath`는 Next.js가 직접 번들링하거나 라우팅하는 경로에는 적용되지만, 클라이언트 사이드 JavaScript 코드에서 `public` 디렉토리의 자산을 직접 절대 경로로 참조할 때는 자동으로 `basePath`를 붙여주지 않습니다. 이 경우, 브라우저는 현재 문서의 `origin`을 기준으로 절대 경로를 해석하므로, `basePath`가 적용되지 않은 잘못된 URL이 생성되었던 것입니다.\n\n## 해결 과정: `basePath` 명시적 적용\n\n이 문제를 해결하기 위해서는 클라이언트 측 코드에서 `public` 디렉토리의 자산을 참조할 때 `basePath`를 명시적으로 붙여주어야 합니다. Next.js는 `next.config.ts`에 `basePath`가 설정되어 있을 경우, 빌드 시 `process.env.NEXT_PUBLIC_BASE_PATH` 환경 변수에 해당 값을 주입해 줍니다. 이 환경 변수는 클라이언트 코드에서도 접근 가능합니다.\n\n따라서, `public` 디렉토리 내 자산 경로를 사용할 때 `process.env.NEXT_PUBLIC_BASE_PATH`를 접두사로 붙여주는 방식으로 코드를 수정했습니다. 예를 들어, AI 모델 파일을 로드하는 부분과 Fabric.js로 SVG 스티커를 로드하는 부분에 이 변경을 적용했습니다.\n\n```typescript\n\u002F\u002F 수정 전 (오류 발생 예시)\nconst modelPath = '\u002Fmodels\u002Fu2netp.onnx';\n\u002F\u002F Fabric.js SVG 로딩 예시\n\u002F\u002F fabric.loadSVGFromURL('\u002Fstickers\u002Farrows\u002Farrow_curve.svg', ...);\n\n\u002F\u002F 수정 후 (basePath 적용)\n\u002F\u002F process.env.NEXT_PUBLIC_BASE_PATH는 next.config.ts의 basePath 값으로 자동 주입됩니다.\nconst basePath = process.env.NEXT_PUBLIC_BASE_PATH || '';\nconst modelPath = `${basePath}\u002Fmodels\u002Fu2netp.onnx`;\n\u002F\u002F Fabric.js SVG 로딩 예시\n\u002F\u002F fabric.loadSVGFromURL(`${basePath}\u002Fstickers\u002Farrows\u002Farrow_curve.svg`, (objects, options) => { ... });\n```\n\n이 변경 사항을 적용하고 다시 배포하자, AI 모델과 스티커 이미지가 `https:\u002F\u002Fusername.github.io\u002Fframe-pick\u002Fmodels\u002Fu2netp.onnx`와 같이 올바른 경로로 로드되기 시작했으며, 애플리케이션의 모든 기능이 정상적으로 동작함을 확인할 수 있었습니다. 클라이언트 JavaScript가 `public` 디렉토리의 자산에 접근할 때 `basePath`가 포함된 완전한 경로를 사용하게 된 것입니다.\n\n## 다음에 할 것\n\n현재는 `process.env.NEXT_PUBLIC_BASE_PATH`를 직접 사용하는 방식으로 문제를 해결했지만, 추후 이러한 경로 생성을 위한 유틸리티 함수를 만들거나, `next\u002Fconfig`의 `publicRuntimeConfig`를 활용하여 경로 관리를 좀 더 중앙집중화하는 방안을 고려해볼 수 있습니다. 또한, GitHub Pages 환경에서 커스텀 도메인을 사용하는 경우 `basePath` 설정 없이 루트 경로에 배포할 수도 있으므로, 배포 환경의 변화에 유연하게 대응할 수 있도록 경로 관리 전략을 좀 더 명확히 문서화할 필요가 있습니다.","2026-08-20T14:02:08.955+09:00",[10,11,12,13,14,15,16,17,18,19],"Deployment","Fabric-js","GitHub Pages","Next-js","ONNX Runtime","Path Resolution","Static Export","TypeScript","basePath","개발기",1788152655815]