사실 이전부터 monorepo를 적용해보고 싶다는 생각은 있었지만, 명분이 없었다.
새로운 프로젝트를 시작하기에도, 시작하더라도 굳이 Monorepo를?!
그러던 중 진행하고 있던 프로젝트가 잠시 휴식기었고, 팀원들에게 허락을 받고 리팩토링을 진행하던 중, 어드민 페이지를 간단하게라도 지금 만들어야겠다는 생각이 들었다.
이때까지는 데이터가 많지 않아 간단하게 상수로 진행했었는데, supabase도 사용해보고 싶었으니, 개인적인 욕구를 충족하면서도 어드민 페이지는 만들어두면 팀원들이 사용하기 편할 것이라고 생각했다.
처음에는 단순히 기존 프로젝트에 /admin 라우트를 추가하려고 했다. 하지만 약간의 문제가 있었다.
layout.ts (Next.js app router 환경)에 고정된 Header 컴포넌트가 있었는데, 어드민에는 필요 없는 요소였다...하하
간단(?)하게 app 하위 구조만 좀 변경할까 했지만, 아직은 로그인 기능이 없는 서비스라서 인증/인가는 우선순위가 낮았다. 어드민을 만들면서 불필요한 공수는 줄이고 싶었기에, 결국 독립된 프로젝트로 분리하는 게 더 낫다고 판단했다.
어차피 프로젝트를 분리한다면, 예전부터 고민하던 monorepo로 전환해볼까?
그럼 기존 UI와 공통 feature도 재사용할 수 있을텐데?
망설일 이유가 없었다. 바로 실행에 옮겼다🚀
Next.js에서 monorepo 환경을 참고해, 아래와 같은 구조로 재편했다.
packages/ui: button, input 등 공통 컴포넌트packages/shared: web/admin에서 공통적으로 사용되는 constants, types 등packages/supabase: DB 접근 전용 관리projects
|_ apps
| |_ web // 기존 서비스 프로젝트
| |_ admin // 새로 만든 어드민 프로젝트
|_ packages
| |_ eslint-config
| |_ typescript-config
| |_ supabase
| |_ ui // 공통 ui 라이브러리
| |_ shared // 공통 features
shared는 처음엔 FSD 구조를 고민했지만, constants와 types 정도만 필요해서 layered 형식을 적용했다.
구조와 변경된 alias를 모두 처리했는데...생각지 못했던 배포에서 막혔다😱
Project Settings > Build and Deployment에서 설정을 통해
monorepo 환경에 맞도록 변경했지만 문제가 발생했다.

패키지 설치를 하지 못하고 요청을 기다리다가, 결국 설치 에러가 발생했다.

1번 방법이 빌드에 실패해서,
Root Directory를 기존 설정으로 두고 Framework Settings 값을 아래 이미지와 같이 변경했다.
Install Command가 프로젝트에 맞도록 변경된다고 했는데, npm으로 설치를 해서 직접 pnpm install로 지정해주었다.

1번에서와 다르게 패키지들은 설치되었으나, 패키지 설치 후 package.json에 next 패키지가 없다는 오류가 발생한다.
이것은 당연하게도 프로젝트 루트의 package.json에는 next가 없기 때문이다..

Next.js build error in monorepo 같은 일반적인 키워드 대신, no next js version detected in monorepo와 같이 실제 에러메시지를 넣어 검색해봤다.
그러자 무려 4일 전에 올라온 같은 이슈를 발견했지만, 해결 방법에 대한 내용은 없었다.
🔗 Vercel “No Next.js version detected” for Next 15 app in pnpm monorepo
추가적으로 커뮤니티 내에서 검색한 결과 아래 글을 발견하고, 댓글에서 해결 방법을 확인할 수 있었다.
🔗 No Next.js version detected
나의 실수가 있었다./apps/web하위에 남아있던package-lock.json이 루트의pnpm-lock.yaml과 충돌했다. 해당 파일을 지우니 해결됐다.
나도 같은 상황이었다. npm에서 pnpm으로 패키지 매니저를 변경하면서 package-lock.json이 그대로 남아있었다.
동일하게 삭제 후 문제가 해결됐다👻
혹시 눈치 채었다면, 시도2: Framework Settings 변경 에서 이상하다고 느꼈을 것이다.
"Install Command가 프로젝트에 맞도록 변경된다고 했는데, npm으로 설치를 해서 직접 pnpm install로 지정해주었다."
💡교훈: 패키지 매니저를 바꾸면, 기존 lock 파일도 반드시 지우자.
처음에는 불필요한 공수는 줄이고 싶었지만, packages를 분리하면서 기존 설정 파일들을 재사용 가능하도록 구성하는 과정은 조금 번거로웠다.
예를 들어 Typescript, ESLint, Tailwind 설정...
반면, 새로운 공통 package인 Supabase를 추가하는 과정은 수월하게 진행됐다.
아직 admin 개발을 본격적으로 시작하진 않았지만, 기대하는 효과는 다음과 같다.
packages/shared에서 관리 가능packages/supabase 에서만 관리해 일관성 유지