with one click
api-contract
Cross-stack API 계약 동기화. NestJS DTO ↔ Flutter DTO 일치성 관리.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Cross-stack API 계약 동기화. NestJS DTO ↔ Flutter DTO 일치성 관리.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Figma MCP 로 디자인을 픽셀 퍼펙트로 구현. 토큰 추출 → 테마 매핑 → 정확한 레이아웃 → 스크린샷 diff 검증 루프. Flutter/React 공용.
Flutter 외부/BaaS API 연동 풀 파이프라인. 계약 → retrofit 코드젠 → dio 인터셉터(auth/retry) → Result 매핑 → repository → 오프라인 캐시. dio-retrofit 스킬 심화.
TanStack Query v5 패턴 (React). 서버 상태 패칭/캐시/뮤테이션/낙관적 업데이트.
Supabase BaaS 연동 패턴 (Flutter). 인증/DB/리얼타임/스토리지/RLS.
Auth.js v5 (NextAuth) 패턴. 세션/미들웨어/Server Action 가드.
Dio+Retrofit API 통신 패턴. API 클라이언트, 인터셉터 작성 시 자동 참조.
| name | api-contract |
| description | Cross-stack API 계약 동기화. NestJS DTO ↔ Flutter DTO 일치성 관리. |
| globs | contracts/**, server/src/**/dto/**, client/data/remote/**, client/data/dto/**, client/domain/entity/**, docs/api/** |
크로스-스택 피처는 contracts/<domain>.contract.md 가 계약의 원본이다.
서버 DTO 작성 전에 반드시 확인:
contracts/<domain>.contract.md 가 있으면 → 그 필드/타입/enum 그대로 구현.
계약에 없는 필드 추가 금지 — 필요하면 메인 세션에 inbox 로 계약 수정 요청.contracts/README.md 형식).
팀 멤버라면 메인에게 계약 작성 요청이 원칙 (멤버는 contracts/ read-only)./api-sync 의 기준.contracts/ 가 없는 레거시 프로젝트는 아래 기존 흐름 (NestJS DTO = 원본) 사용.
contracts/*.contract.md → Prisma Schema → NestJS DTO → Flutter DTO → Flutter Entity
↑ ↑ ↑ ↑ ↑
계약 원본 DB 테이블 API 응답 형태 JSON 역직렬화 앱 도메인 모델
// server/src/game/dto/roll-dice-response.dto.ts
import { ApiProperty } from '@nestjs/swagger';
export class TileEventDto {
@ApiProperty({ enum: ['BUY_OPTION', 'PAY_TOLL', 'GOLDEN_KEY', 'WARP', 'ISLAND'] })
type: string;
@ApiProperty({ required: false })
data?: Record<string, unknown>;
}
export class RollDiceResponseDto {
@ApiProperty({ minimum: 1, maximum: 6 })
dice: number;
@ApiProperty()
newPosition: number;
@ApiProperty()
passedStart: boolean;
@ApiProperty({ required: false })
startBonus?: number;
@ApiProperty({ required: false })
event?: TileEventDto;
@ApiProperty()
diceBalance: number;
}
// client/data/remote/dto/roll_dice_response_dto.dart
import 'package:freezed_annotation/freezed_annotation.dart';
part 'roll_dice_response_dto.freezed.dart';
part 'roll_dice_response_dto.g.dart';
@freezed
class RollDiceResponseDto with _$RollDiceResponseDto {
const factory RollDiceResponseDto({
required int dice,
required int newPosition,
required bool passedStart,
int? startBonus,
TileEventDto? event,
required int diceBalance,
}) = _RollDiceResponseDto;
factory RollDiceResponseDto.fromJson(Map<String, dynamic> json) =>
_$RollDiceResponseDtoFromJson(json);
}
@freezed
class TileEventDto with _$TileEventDto {
const factory TileEventDto({
required String type,
Map<String, dynamic>? data,
}) = _TileEventDto;
factory TileEventDto.fromJson(Map<String, dynamic> json) =>
_$TileEventDtoFromJson(json);
}
// client/domain/entity/roll_result.dart
import 'package:freezed_annotation/freezed_annotation.dart';
part 'roll_result.freezed.dart';
@freezed
class RollResult with _$RollResult {
const factory RollResult({
required int dice,
required int newPosition,
required bool passedStart,
int? startBonus,
TileEvent? event,
required int diceBalance,
}) = _RollResult;
}
// client/data/remote/dto/roll_dice_response_dto.dart (extension)
extension RollDiceResponseDtoX on RollDiceResponseDto {
RollResult toEntity() => RollResult(
dice: dice,
newPosition: newPosition,
passedStart: passedStart,
startBonus: startBonus,
event: event?.toEntity(),
diceBalance: diceBalance,
);
}
| NestJS (서버) | Flutter DTO | Flutter Entity | 비고 |
|---|---|---|---|
| camelCase | camelCase | camelCase | JSON key 동일 |
| snake_case DB | @ApiProperty | @JsonKey(name:) | DB↔API 변환은 서버 |
string | String | String | |
number | int / double | int / double | 정수/실수 구분 |
boolean | bool | bool | |
Date (ISO) | DateTime | DateTime | JSON: ISO 8601 문자열 |
enum | String + enum | domain enum | DTO는 String, Entity에서 enum 변환 |
T? optional | T? nullable | T? nullable | |
T[] array | List<T> | List<T> |
docs/api/
├── auth.md # POST /auth/login, /auth/refresh, /auth/logout
├── board.md # GET /boards, POST /board/roll, /board/buy, etc.
├── game.md # 게임 이벤트 상세
└── _template.md # API 문서 템플릿
각 API 문서에 포함할 항목:
# NestJS Swagger에서 자동 생성된 OpenAPI JSON과 Flutter DTO 대조
# 1. NestJS에서 swagger.json 추출
curl http://localhost:3000/api-json > docs/api/swagger.json
# 2. Flutter DTO 파일 목록과 대조
# → 누락된 DTO, 필드 불일치 확인
GOLDEN_KEY, PAY_TOLL)docs/api/ 문서는 API 변경 시 즉시 갱신