[개인 프로젝트 Lottodolist] Nest.JS + NextJS 모노레포 만들기

규갓 God Gyu·2025년 3월 26일
0

프로젝트

목록 보기
78/81

next + nest를 이용한 풀스텍 프로젝트를 진행해보려한다
기획 내용은 이전 블로그에도 자세히 적진 않았지만 거기에 적어놨고기획 내용은 이전 블로그에도 자세히 적진 않았지만 거기에 적어놨고
일단 따로 레포지토리를 팔지, 모노레포로 팔지부터 고민이 들어가야하는데,

모노 레포?

Monolithic Repository의 줄임말

여러 프로젝트, 패키지를 단일 Git 저장소에서 관리하는 방식

즉, 프론트엔드, 백, 공용 코드 등을 하나의 저장소에서 함께 관리하는 것

  • 코드 공유 & 재사용 쉬움
  • 통합 테스트 간편
  • 일괄된 개발 환경
  • 의존성 관리 간소화

그러나 단점으론

  • 저장소 크기 증가
  • 빌드 시간 증가
  • 접근 제어 어려움(모든 코드 개발자 접근 가능)

이러한 모노레포에 대한 도구도 있다고 하는데

Turborepo

장점

  • Next.js와 같은 Vercel 생태계와 매우 잘 통합
  • 빌드 캐싱 뛰어나 개발 속도 빠름
  • 설정 간단 학습 곡선 낮음
  • JS/TS 프로젝트에 최적화

단점

  • 생태계 덜 성숙
  • JS/TS에 특화되어 다른 언어 지원 제한적

다른 것들도 찾아봤는데 엄청나게 큰 차이는 없는데 Next Nest 함께 사용하기 적합하고 학습 곡선도 낮아서 채택하기로 하였다 + 빌드 캐싱 개발 과정 빨라짐

pnpm

패키지 매니저는 pnpm을 채택할건데

디스크 공간 효율성 뛰어나고 의존성 설치 속도빠르고 npm보다 더 효율적인 성능제공 + turborepo와 함께 사용되어서 채택하였다.

turborepo 설치

pnpm dlx create-turbo@latest 이것만 설치해도 기본 모노레포 구조 자동 생성해줌

{DD2F35B3-6457-4B1C-AD79-0C546F24D651}.png

이렇게 알아서 세팅해줬는데

{2FF01BFA-DF76-4CAF-B72E-476DD7849A79}.png

엄청 많은 실행되는 주소가 떴다.

백과 프론트를 같이 실행해야하니 한번에 실행해주는 도움을 주는 부분인 것 같고, 초기화를 시켜야해서

# 불필요한 예제 앱 제거 (create-turbo가 생성한 기본 앱들)
rm -rf apps/docs
rm -rf apps/web

# 필요한 디렉토리 구조 생성
mkdir -p apps/web       # Next.js 프론트엔드
mkdir -p apps/api       # NestJS 백엔드
mkdir -p packages/ui    # 공유 UI 컴포넌트
mkdir -p packages/types # 공유 타입 정의
mkdir -p packages/database # 데이터베이스 관련 패키지

일단 구조가 이렇게 짜지는게 맞는지 아직 모르겠지만 세팅은 챗봇의 도움을 받자 하나하나 공부하면서 검색하면서 하기엔 챗봇이 너무 잘되어있다.

# apps/web 디렉토리로 이동
cd apps/web

# Next.js 프로젝트 생성
pnpm dlx create-next-app@latest . --typescript --tailwind --eslint --app --src-dir --import-alias="@/*"

그리고 기본 옵션인 next의 tailwind와 app router ts 추가

+turbopack까지

{58E307BC-D8CA-44F2-8EBD-EECC892D8480}.png

이후 패키지에서 이름 변경

pnpm add -g @nestjs/cli

cd apps/api
nest new . --package-manager pnpm

이후 루트 디렉토리에서 nest 설치 + pnpm까지

여기서도 마찬가지로

@lottodolist/api

로 이름 package.json의 name 수정

여기까지 프론트와 백 구축했고 이제 재사용성을 위한 type, ui, database 패키지도 추가해보려한다

ui쪽은

cd packages/ui
pnpm init

{
  "name": "@lottodolist/ui",
  "version": "0.0.0",
  "private": true,
  "main": "./index.tsx",
  "types": "./index.tsx",
  "exports": {
    "./*": "./src/*.tsx"
  },
  "scripts": {
    "lint": "eslint . --max-warnings 0",
    "generate:component": "turbo gen react-component",
    "check-types": "tsc --noEmit",
    "build": "tsup index.tsx --format esm,cjs --dts"
  },
  "devDependencies": {
    "@repo/eslint-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@turbo/gen": "^2.4.4",
    "@types/node": "^22.13.10",
    "@types/react": "19.0.10",
    "@types/react-dom": "19.0.4",
    "eslint": "^9.22.0",
    "typescript": "5.8.2"
  },
  "dependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  }
}

여기선 main, types, build만 추가해주고

types에선

pnpm init

pnpm add -D typescript

까지 해준 후

{
  "name": "@lottodolist/types",
  "version": "1.0.0",
  "description": "",
  "main": "./index.ts",
  "types": "./index.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "tsup index.ts --format esm,cjs --dts"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "typescript": "^5.8.2"
  }
}

.ts로 변경시켜준다

database는

pnpm add prisma @prisma/client
pnpm add -D typescript

prisma를 typeORM보다 많이 쓴데서 일단 추가는 시켰는데 왜 채택해야하는지 자세히 안알아봤기 떄문에 추 후 다룰 예정

{
"name": "lottodolist",
"version": "0.0.1",
"private": true,
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"db:generate": "turbo run db:generate",
"db:push": "turbo run db:push"
},
"devDependencies": {
"eslint": "^8.48.0",
"prettier": "^3.0.3",
"tsup": "^7.2.0",
"turbo": "latest"
},
"packageManager": "pnpm@8.6.10"
}

그리고 root의 package.json에서도 db관련된 명령어 추가

루트 디렉토리에 turbo.json도 있었는데 용도는

  • 작업 파이프라인 정의:어떤 작업(build, lint, test등) 이 어떻게 실행되어야 하는지 정의
  • 의존성 관리:특정 작업이 다른 작업에 의존하는지 설정
  • 캐싱 설정: 어떤 작업이 캐싱되어야 하는지, 캐싱의 조건은 무엇인지 정의
  • 출력물 지정: 각 작업이 생성하는 출력물 지정하여 캐싱 최적화

세팅을 하면서 pipeline을 쓰라고 권장했으나, 이전 버전의 문법이여서 tasks를 사용하였다.

{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": ["**/.env.*local"],
  "ui": "tui",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["$TURBO_DEFAULT$", ".env*"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "lint": {
      "dependsOn": ["^lint"]
    },
    "check-types": {
      "dependsOn": ["^check-types"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "db:generate": {
      "cache": false
    },
    "db:push": {
      "cache": false
    }
  }
}

이후 ORM 도구로 Prisma를 추천받았는데, TypeORM만 강의로 사용해보았다.

장단점을 비교해보면

Prisma

  • 타입 안정성: 자동으로 데이터베이스 스키마에서 TS 타입을 생성해줘서 타입 안정성 매우 뛰어남(자동? 이미 여기서 끝 ㅋㅋ)
  • 직관적 API : 쿼리 작성 간결하고 읽기 쉬움
  • 스키마 마이그레이션: 마이그레이션 도구가 내장되어 있어 스키마 변경 관리 용이
  • Prisma Studio: GUI로 데이터 쉽게 탐색하고 수정할 수 있는 도구
  • Next.js와 통합 원활

그러나

  • 러닝커브
  • 복잡한 쿼리의 경우 직접 SQL 작성해야할수도

정도다

여기서 GUI는 Graphical User Interface 즉 그래픽 사용자 인터페이스를 의미

TypeORM

  • 타입 안정성
  • 설정 복잡성(여기서 안끌림)
  • 마이그레이션 도구(직관적이지 않음)

그리고 최근엔 Prisma를 풀스택 프레임워크에서 더 많이 선호한다고 하니 Prisma채택!

pnpm add prisma @prisma/client
pnpm add -D typescript

이렇게 설치해주고

// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client { provider = "prisma-client-js" }

datasource db { provider = "postgresql" url = env("DATABASE_URL") }

기본 내용은 pnpm prisma init으로 세팅해놨다

Prisma CLI를 사용하여 Prisma 프로젝트 초기화하는 명령

근데 pnpm prisma init으로 설치하면 이미 설치된 prisma 패키지를 ㅅ ㅏ용하려고 하고,

pnpm dlx prisma init으로 설치하면 임시 다운로드로 prisma 설치안되어있어도 사용가능하다는 차이가 있음

이후 공용 패키지로 사용할 것들을 프론트와 백에 각각 추가

프론트

cd apps/web
pnpm add @lottodolist/ui @lottodolist/types @lottodolist/database

cd ../api
pnpm add @lottodolist/types @lottodolist/database

여기까지가 말도 많고 탈도 많았던 기본 세팅 끝..!

내용은 서서히 구체화 해볼 예정……..

profile
웹 개발자 되고 시포용

0개의 댓글