Appearance
백엔드 아키텍처
apps/api NestJS REST API 구조입니다.
기술 스택
| 영역 | 기술 |
|---|---|
| 프레임워크 | NestJS 11 |
| HTTP | Express (@nestjs/platform-express) |
| ORM | Prisma 7 (@prisma/client) |
| DB | PostgreSQL 17 (Docker) |
| 인증 | Passport (Google OAuth 2.0, JWT) |
| 테스트 | Jest + Supertest |
| 공유 타입 | @repo/api workspace 패키지 |
진입점
apps/api/src/main.ts:
typescript
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
app.use(cookieParser());
app.enableCors({
origin: ['http://localhost:3001'],
credentials: true,
});
await app.listen(3000);- 포트: 3000
- CORS: web 앱(
http://localhost:3001)만 허용, 쿠키(credentials) 지원
모듈 구조
src/
├── main.ts
├── app.module.ts
├── prisma/
│ ├── prisma.module.ts # @Global() PrismaService
│ └── prisma.service.ts
├── auth/
│ ├── auth.module.ts
│ ├── auth.controller.ts
│ ├── auth.service.ts
│ ├── guards/jwt-auth.guard.ts
│ └── strategies/
│ ├── google.strategy.ts
│ └── jwt.strategy.ts
├── users/
│ ├── users.module.ts
│ ├── users.controller.ts
│ ├── users.service.ts
│ └── dtos/user.dto.ts
├── workspaces/
│ ├── workspaces.module.ts
│ ├── workspaces.controller.ts
│ ├── workspaces.service.ts
│ ├── dtos/
│ └── utils/slug.ts
└── links/ # CRUD 예제 (레거시 스캐폴딩)Prisma
스키마: apps/api/prisma/schema.prisma
클라이언트 출력: apps/api/generated/prisma/
설정: apps/api/prisma.config.ts
데이터 모델 (요약)
워크스페이스 바로 아래에 노드가 붙는 플랫 구조입니다.
prisma
model User { ... }
model Workspace {
id, name, slug, ownerId
members WorkspaceMember[]
nodes Node[]
}
model WorkspaceMember {
workspaceId, userId, role // owner, admin, editor, viewer 등
}
model Node {
id String @id // 클라이언트 UUID
workspaceId Int
sortOrder Int
kind String // text 등
data Json // CanvasNodeState 전체
}상세 ERD는 apps/api/README.md를 참고하세요.
마이그레이션은 apps/api/prisma/migrations/에 관리됩니다.
DB 초기화 (Docker)
bash
pnpm docker:db:setup
# = prisma generate + migrate deploy로컬(Docker 없이):
bash
cd apps/api
DATABASE_URL=postgresql://postgres:example_password@localhost:5432/mydb \
pnpm exec prisma generate
DATABASE_URL=postgresql://postgres:example_password@localhost:5432/mydb \
pnpm exec prisma migrate deploy인증 흐름
- web
/login에서VITE_API_URL/auth/google로 이동 - Google OAuth 완료 후 JWT를
access_tokenhttpOnly 쿠키로 설정 - 이후 API 요청은
credentials: 'include'로 쿠키 전송 JwtStrategy가 쿠키에서 JWT 추출,JwtAuthGuard로 보호 엔드포인트 검증
API 엔드포인트
인증·사용자
| 메서드 | 경로 | 인증 | 설명 |
|---|---|---|---|
| GET | /auth/google | — | Google OAuth 시작 |
| GET | /auth/google/callback | — | OAuth 콜백, 쿠키 설정 후 리다이렉트 |
| POST | /auth/logout | — | access_token 쿠키 삭제 |
| GET | /users/me | JWT | 현재 로그인 사용자 조회 |
워크스페이스·노드 (JWT 필수)
| 메서드 | 경로 | 설명 |
|---|---|---|
| POST | /workspaces | 워크스페이스 생성 (owner 멤버십 자동 추가) |
| GET | /workspaces | 내 워크스페이스 목록 |
| GET | /workspaces/:id | 워크스페이스 조회 (멤버만) |
| GET | /workspaces/:id/nodes | 노드 문서 조회 { nodes, nodeOrder } |
| PUT | /workspaces/:id/nodes | 노드 문서 동기화 (upsert + 삭제) |
접근 제어
- 워크스페이스 미존재 → 404
- 멤버가 아님 → 403
syncNodes는nodeOrder순서대로sortOrder를 갱신하고, payload에 없는 노드는 삭제합니다.
links 모듈은 NestJS 스캐폴딩 예제로 남아 있으며, DTO/엔티티 타입은 @repo/api 패키지에서 import합니다.
공유 패키지 (@repo/api)
packages/api/src/:
links/
├── dto/
│ ├── create-link.dto.ts
│ └── update-link.dto.ts
└── entities/
└── link.entity.tsUser·Workspace DTO는 현재 apps/api/src/에 api 앱 로컬로 정의되어 있습니다.
환경 변수
| 변수 | 설명 |
|---|---|
DATABASE_URL | PostgreSQL 연결 문자열 |
NODE_ENV | development / production |
GOOGLE_CLIENT_ID | Google OAuth 클라이언트 ID |
GOOGLE_CLIENT_SECRET | Google OAuth 클라이언트 시크릿 |
JWT_SECRET | JWT 서명 시크릿 |
Docker dev 환경:
env
DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@db:5432/mydb
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
JWT_SECRET=...Google Cloud Console에서 OAuth 리디렉션 URI를 http://localhost:3000/auth/google/callback로 등록해야 합니다.
Docker
- Dockerfile:
apps/api/Dockerfile.dev - DB healthcheck 후 api 기동 (
depends_on: condition: service_healthy) - 최초 기동 후
pnpm docker:db:setup으로 스키마 적용 - DB 브라우징:
pnpm docker:prisma:studio→ http://localhost:5555
현재 상태 & 향후 확장
구현됨
- Google OAuth 로그인 + JWT 쿠키 세션
- User 조회 (
/users/me) - Workspace CRUD (목록·생성·조회)
- Node 문서 동기화 (워크스페이스 직속, JSON
data필드) - WorkspaceMember 기반 접근 제어
향후
- 초대(Invitation), 역할별 세분 권한
- 노드 단위 PATCH / 실시간 협업(WebSocket)
- 이미지 업로드 등 자산 API
테스트
bash
pnpm --filter api test # unit
pnpm --filter api test:e2e # e2e