Turborepo + pnpm + Next.js 환경 구성기 (실전 예시: turborepo-pnpm-next-docker-letsgo)
monorepo로 여러 앱과 공용 패키지를 빠르게 운영하는 법
왜 이 조합인가
하나의 팀에서 여러 앱(웹, 어드민)을 개발하는데 생산성을 높이기 위해서 앱과 공용 패키지(UI, ESLint, Tailwind, TS 설정)을 한 저장소에서 관리하기 하기 위해서 Turborepo + pnpm 조합으로 모노레포을 구성합니다.
- Turborepo: 캐시/파이프라인으로 빌드 중복 제거
- pnpm: 빠른 설치 + 효율적인 의존성 관리
- Next.js(+Turbopack): 개발속도와 DX 강화
실제 코드를 아래 링크에서 확인 가능합니다.
[https://github.com/dosanHoon/turborepo-pnpm-next-docker-letsgo])
폴더 구조(요약)
root
├─ apps
│ ├─ client # Next.js 클라이언트
│ ├─ admin # Next.js 관리자
│ └─ storybook # UI 컴포넌트 카탈로그
├─ packages
│ ├─ ui # @on/ui : 공용 UI
│ ├─ eslint-config # @on/eslint-config
│ ├─ tailwind-config # @on/tailwind-config
│ └─ typescript-config # @on/typescript-config
├─ pnpm-workspace.yaml
├─ turbo.json
└─ deploy/ ... # 배포 스크립트/설정
워크스페이스 & 공통 설정
pnpm 워크스페이스
pnpm-workspace.yaml로 apps/*, packages/*를 워크스페이스로 묶어 로컬 패키지 즉시 참조가 가능합니다
(중복 설치↓, 버전 드리프트↓).
packages:
- "apps/*"
- "packages/*"
실무 팁: 공용 ESLint/TS/Tailwind 설정 패키지를 따로 두면, 새 앱을 추가할 때 구성 복붙이 필요 없고,
린팅/타입 규칙을 한 곳에서 바꿔 전체에 적용할 수 있습니다.
Turborepo 파이프라인
turbo.json에서 작업 간 의존성과 캐시 출력물을 명시해 빌드 낭비를 줄입니다(예: Next.js는 .next/**).
{
"globalDependencies": [".env"],
"tasks": {
"build": { "outputs": [".next/**"], "dependsOn": ["^build"] },
"dev": { "cache": false, "persistent": true },
"lint": { "dependsOn": ["^lint"] },
"check-types": { "dependsOn": ["^check-types"] }
}
}
앱 추가/연결 실전 스니펫
1) 공용 UI 패키지 사용
패키지 이름은 @on/ui로 가정합니다(레포 구조 기준).
// apps/client/src/app/page.tsx
import { Button } from "@on/ui";
export default function Page() {
return <Button>안녕하세요</Button>;
}
2) 공용 ESLint/TS/Tailwind 적용
packages/eslint-config→ 각 앱.eslintrc에서extends: ["@on/eslint-config"]packages/typescript-config→tsconfig.json에서extends: "@on/typescript-config/base.json"packages/tailwind-config→ 각 앱의tailwind.config.ts에서 preset 형태로 로드
“설정은 패키지로 모아 공용으로 사용하고, 앱은 비즈니스 로직에 집중”이 모노레포 생산성의 핵심입니다.
개발/빌드 명령어
# 루트에서
pnpm i
# 앱별 개발
pnpm -F client dev
pnpm -F admin dev
pnpm -F storybook dev
# 전체 빌드
pnpm turbo run build
# 특정 앱만 빌드
pnpm turbo run build --filter=client
-F(—filter)는 워크스페이스 중 특정 대상만 실행할 때 유용합니다.
Docker & 배포(레포 기준 Jenkins 예시)
deploy 폴더에 nginx 및 각 app 별 dockerfile을 모아두고 docker-compose로 배포합니다.
1. CI/CD ( 예 Jenkins ) 에서 인증서/키/환경변수를 주입하고,
2. dockerbuild.sh로 빌드/푸시 후 원격 서버에서
3. dockerpull.sh로 가져오는 흐름이 나옵니다.
# Jenkins 스크립트(요약)
sudo cat $CERT_FILE > ./deploy/nginx/cert.pem
sudo cat $KEY_FILE > ./deploy/nginx/key.pem
sudo cat $PEM_PASS_FILE > ./deploy/nginx/pem_pass
# 도커 레지스트리 환경 변수 주입
export NEXUS_URL=$NEXUS_URL
export NEXUS_USER=$NEXUS_USER
export NEXUS_PASS=$NEXUS_PASS
# env 환경변수 주입
touch .env
echo "NEXT_PUBLIC_API_HOST=${NEXT_PUBLIC_API_HOST}" >> .env
echo "NEXTAUTH_SECRET=${NEXTAUTH_SECRET}" >> .env
echo "NEXTAUTH_URL=${NEXTAUTH_URL}" >> .env
mv .env ./apps/client/
sudo sh ./deploy/dockerbuild.sh client $BUILD_ID dev
# 원격 서버에 compose/스크립트 복사 후
# dockerpull.sh client $BUILD_ID dev 실행
이 흐름의 포인트:
- 민감 정보는 CI에서만 주입 → 레포에 비밀이 남지 않음
- 앱별 이미지 태깅(
client $BUILD_ID dev) → 롤백·가시성 좋아짐 - Nginx + 앱 컨테이너 compose → 단일 호스트에서도 손쉽게 TLS/리버스프록시 구성 가능
해당 스니펫과 배포 아이디어는 레포 README에 정리되어 있습니다. (GitHub)
트러블슈팅 체크리스트
- 워크스페이스 링크가 안 잡혀요
→pnpm -F <pkg> build전에 루트에서pnpm i로 모든 패키지 링크를 최신화하세요. - 환경변경이 반영 안 돼요
→turbo.json의globalDependencies에.env가 포함되어 있는지 확인. - CI가 빨라지지 않아요
→ Turborepo 캐시의 저장 위치(원격 캐시/S3 등)와outputs설정이 실제 산출물 경로와 일치하는지 점검.
마무리
이 구조를 쓰면 앱 추가가 가벼워지고, 설정이 단일화되며, CI/CD도 깔끔해집니다.
다음 글에서는 이 구성을 Docker 멀티스테이지 + 원격 캐시로 더 빠르게 돌리는 팁을 공유하겠습니다.
참고
- 레포:
turborepo-pnpm-next-docker-letsgo(앱/패키지 구성, Jenkins 배포 플로우 정리) (GitHub)
#Turborepo #pnpm #Nextjs #Monorepo #Docker배포 #프론트엔드개발환경 #CICD #Nodejs #개발블로그 #웹개발팁
'개발관련' 카테고리의 다른 글
| Node.js 스트림 백프레셔 — FFmpeg stdin 파이핑 실전기 (0) | 2026.03.10 |
|---|---|
| WebP vs SVG, 그리고 Next.js에서의 실전 활용법 (2) | 2025.08.09 |
| 🎨 Figma 디자인을 코드로! Cursor IDE + MCP 연동 가이드 (0) | 2025.05.15 |
| how to vscode highlight HTML code syntax inside a string (0) | 2024.10.30 |
| 도커 볼륨 마운트 -v,--volume 컨테이너 경로를 호스트 경로 링크 마운트 하는 방법 (0) | 2024.02.28 |