📚 1인 개발자의 B2B SaaS 개발기 #1

왜 NestJS + Prisma를 선택했나 — B2B SaaS 백엔드 기술 선택기

Express vs NestJS, TypeORM vs Prisma. B2B SaaS 백엔드를 만들면서 기술 스택을 고르기까지의 과정과 첫 커밋 이야기.

왜 NestJS + Prisma를 선택했나

2025년 11월 18일. nest new 명령어를 치고 첫 커밋을 올렸다.

git commit -m "initial commit"

이렇게 시작된 프로젝트가 4개월 뒤에는 커밋 1,283개, 테이블 30개, API 100개가 넘는 B2B SaaS 플랫폼이 될 줄은 몰랐다.

오늘은 그 첫날 이야기를 써보려고 한다. 기술 스택을 고르는 과정에서 뭘 고민했고, 왜 이 조합이 되었는지.


📋 프로젝트 배경

만들려는 건 B2B SaaS 플랫폼이었다. 핵심 요구사항을 정리하면:

  • 멀티테넌트: 테넌트(고객사)마다 독립된 데이터
  • 역할 기반 접근 제어: 관리자/운영자/엔드유저/외부 뷰어 — 4가지 역할
  • 복잡한 도메인 로직: 등급 시스템, 진행 트랙, 성과 지표 추적
  • 실시간성: 사용자 활동 기록, 실시간 상태 갱신
  • 관리 포탈 2개: 시스템 관리용 + 테넌트 관리용

1인 개발이었다. 기획, 설계, 백엔드, 프론트엔드, QA까지 전부 혼자. 그래서 “혼자서도 빠르게 만들 수 있는 스택”이 중요했다.


🛠️ 후보군 비교

Express vs NestJS

사실 Express로 시작할 뻔했다. 가볍고, 자유롭고, 레퍼런스도 많으니까.

근데 프로젝트 스펙을 보면서 생각이 바뀌었다:

기준ExpressNestJS
구조 강제성없음 (자유)있음 (모듈/컨트롤러/서비스)
DI (의존성 주입)직접 구현내장
데코레이터없음@Controller, @Injectable
테스트직접 세팅Testing Module 내장
Swagger미들웨어@nestjs/swagger 통합

프로젝트 초기에 자유도가 높은 프레임워크를 고르면 생기는 일

Express의 자유도는 프로젝트가 커지면 독이 된다. 파일 하나에 라우터+비즈니스 로직+DB 쿼리가 다 들어가는 코드를 이미 겪어봤다.

NestJS는 “어디에 뭘 놓을지”를 프레임워크가 정해준다. 1인 개발에서 이건 엄청난 장점이다. 구조 고민에 쓸 시간을 기능 구현에 쓸 수 있으니까.

결정 요인: API 100개 이상 예상 + 4역할 인증 + 테스트 필수 → 구조가 잡힌 NestJS

TypeORM vs Prisma

ORM 선택은 좀 더 고민했다.

기준TypeORMPrisma
스키마 정의데코레이터 (코드)schema.prisma (선언형)
마이그레이션자동 생성 (불안정)명시적 (prisma migrate)
타입 안전성부분적완전 (자동 생성)
쿼리 빌더QueryBuilderFluent API
러닝 커브보통낮음

TypeORM을 써본 적 있는데, 마이그레이션이 불안정한 게 가장 큰 문제였다. synchronize: true로 개발하다가 프로덕션에서 데이터 날린 경험이 있다면 공감할 거다.

Prisma는 schema.prisma 파일 하나에 모든 테이블이 선언형으로 정의된다. 이게 좋은 이유:

// schema.prisma — 테이블 구조가 한눈에 보인다
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  role      Role     @default(USER)
  tenant    Tenant   @relation(fields: [tenantId], references: [id])
  tenantId  Int
  createdAt DateTime @default(now())
}

model Tenant {
  id    Int    @id @default(autoincrement())
  name  String
  code  String @unique
  users User[]
}

이걸 TypeORM으로 쓰면 데코레이터 떡칠이 되는데, Prisma는 읽기만 해도 관계가 보인다.

결정 요인: 30개 테이블 예상 + 마이그레이션 안정성 + 타입 자동 생성 → Prisma


🔨 첫날: initial commit

11월 18일. nest new로 프로젝트를 생성했다.

nest new server
cd server
pnpm add prisma @prisma/client
npx prisma init

첫 커밋의 파일 목록:

.gitignore
.prettierrc
README.md
eslint.config.mjs
nest-cli.json
package.json
pnpm-lock.yaml
src/app.controller.ts
src/app.module.ts
src/app.service.ts
src/main.ts

NestJS starter의 기본 파일들. 아직 아무것도 없는 상태다.

그런데 여기서부터 9일 후에 첫 번째 큰 결정을 내리게 된다.


📐 9일 후: 마스터 문서 3,000줄

11월 27일. 코드를 한 줄도 안 치고 문서만 썼다.

docs/마스터.md                    — 709줄
docs/MVP_EPR_정의.md              — 132줄
docs/erd.md                       — 393줄
docs/필요_정의사항_명확화.md      — 1,029줄
docs/우선순위정의.md              — 223줄

총 2,486줄의 설계 문서. 코드보다 문서가 먼저였다.

왜 이렇게 했냐면, 클라이언트 머릿속에 있는 것을 하나씩 꺼내서 정리해야 했기 때문이다.

이전 프로젝트에서 겪은 교훈이 있었다. 설계 없이 바로 코딩에 들어가면 개발 중간에 “이건 이런 뜻이 아니었는데”가 반복된다. 요구사항이 바뀌는 게 아니라, 처음부터 서로 다르게 이해하고 있었던 거다. 그래서 이번엔 클라이언트와 비즈니스 관점 차이를 최대한 줄이고 시작하자는 원칙을 세웠다.

등급 30개, 성과 지표 5개, 진행 트랙, 작업 그룹 생성/완료/이어하기 — 머릿속으로만 돌리면 반드시 빠뜨리는 게 생긴다.

설계 없이 코딩 시작하면 일어나는 일

특히 필요_정의사항_명확화.md 1,029줄은 “이거 구현할 때 뭘 결정해야 하는지” 를 전부 리스트업한 문서다:

  • 등급 조정은 실시간으로 할 건가, 배치로 할 건가?
  • 진행 트랙 완료 기준은 누적인가, 연속인가?
  • 외부 뷰어 인증은 토큰 기반인가, 세션 기반인가?

이 결정들을 코딩하면서 하면 코드를 뒤엎게 된다. 미리 하면 코드가 한 방향으로 간다.

4개월 후 회고하자면, 이 9일이 프로젝트에서 가장 가치 있는 시간이었다. 나중에 v2.0으로 전면 재작성할 때도 이 문서가 기준이 됐다.

다만, 솔직하게 말하면 — 문서화로 클라이언트의 비즈니스 관점을 정확히 옮겨 담는 데는 성공했다. 하지만 프로토타입을 보여주고 나면 새로운 아이디어가 떠오르고, 비즈니스 관점 자체가 바뀌는 건 막을 수 없었다. 그건 문서의 한계가 아니라 제품 개발의 본질이다.


💡 첫날 배운 것

  1. 프레임워크 선택은 프로젝트 규모로 결정한다. 작은 프로젝트면 Express, 큰 프로젝트면 NestJS. “익숙함”보다 “맞음”이 중요하다.

  2. ORM은 마이그레이션 전략으로 고른다. 쿼리 빌더 문법은 익숙해지면 다 비슷한데, 마이그레이션은 한번 잘못 고르면 프로덕션에서 터진다.

  3. 설계 문서를 먼저 쓰면 코드가 빨라진다. 문서 2,486줄 쓰는 데 9일 걸렸지만, 그 덕에 첫 번째 API까지는 2주 만에 도달했다.


📊 오늘의 숫자

항목
커밋1개 (initial commit)
코드NestJS 기본 파일 12개
의존성NestJS 코어 + Prisma
설계 문서0줄 (9일 후 2,486줄)

🔜 다음에 할 것

  • 마스터 문서 기반으로 도메인 모델링 시작
  • Prisma 스키마 설계 — 30개 테이블의 탄생기
  • 유즈케이스 정의 — API가 될 것들

다음 글: #2 도메인 모델링 첫날 — 핵심 엔티티 정의