MIJI DApp Server Docs

개발 환경 설정 가이드 (DEV_README.md)

본 문서는 PLAN.md (v8) 및 SCHEMA.md (v6)를 기반으로, NestJS 서버의 초기 환경 설정, CI/CD 파이프라인, 그리고 운영 전략의 세부 사항을 정의합니다.

1. 환경 변수 명세 (/server/.env.example)

NestJS 서버(server/)의 루트에 .env 파일이 필요합니다. (Value는 실제 배포 시점에 채웁니다.)

# -——————————–
# NestJS & Server
# -——————————–
NODE_ENV=development
SERVER_PORT=3000

# -——————————–
# Auth & Security (A-120)
# -——————————–
# 콤마(,)로 구분된 복수의 키를 지원 (키 회전용)
GLOBAL_SECRET_KEY=primary_key,secondary_key
# Throttler (Rate Limit) - 1분에 60개 요청
THROTTLE_TTL=60
THROTTLE_LIMIT=60

# -——————————–
# Databases (P1-1, P1-3)
# -——————————–
# MongoDB Atlas (M0) 연결 문자열
DB_URI=mongodb+srv://…
# Redis (로컬 Docker 또는 ElastiCache)
REDIS_HOST=localhost
REDIS_PORT=6379

# -——————————–
# AWS Credentials (P1-2, M-050)
# -——————————–
# (권장) EC2 Instance Role을 사용하면 아래 2개는 필요 없음. 로컬 개발용.
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=ap-northeast-2
S3_BUCKET_NAME=miji-nft-assets-dev

# -——————————–
# Blockchain & Secrets (P1-2, B-100)
# -——————————–
# (핵심) 서버 지갑 PK가 저장된 Secrets Manager의 ARN
PK_SECRET_ARN=arn:aws:secretsmanager:ap-northeast-2:….
# (핵심) PK 캐시 TTL (초) (M1 제안)
PK_CACHE_TTL_SECONDS=300

# -——————————–
# Chain RPC Endpoints (B-100)
# -——————————–
# 체인 ID를 키로 사용 (e.g., Infura, Alchemy)
CHAIN_RPC_URL_43114=[https://api.avax.network/ext/bc/C/rpc](https://api.avax.network/ext/bc/C/rpc)
CHAIN_RPC_URL_137=[https://polygon-rpc.com](https://polygon-rpc.com)

# -——————————–
# Webhooks (H-200)
# -——————————–
# (B2) Miji 서버가 수신할 기본 웹훅 URL
WEBHOOK_URL_DEFAULT=[https://api.miji.service.com/v1/webhook/nft](https://api.miji.service.com/v1/webhook/nft)
# (B2) 웹훅 서명 인증을 위한 비밀키
WEBHOOK_SECRET=whsec_…

2. Secrets Manager 구조 (P1-2)

제안서(M1)에 따라 PK는 5분(300초)간 인메모리 캐싱합니다.

PK_SECRET_ARN 환경변수가 가리키는 Secret은 다음 JSON 구조를 따르는 것을 권장합니다. MVP에서는 단일 지갑(default_wallet_pk)만 사용합니다.

{
“default_wallet_pk”: “0xabc123…”
}

3. Bull 큐 설정 (B1, B2)

PLAN.md (v8)에 따라 3개의 큐를 @nestjs/bull로 등록합니다. 각 큐는 작업의 중요도와 성격에 따라 다른 기본 옵션을 가집니다.

큐 이름 PLAN.md ID 목적 기본 Job 옵션 (제안)
minting-queue (T-110) (핵심) 민팅 트랜잭션 처리 attempts: 3, backoff: { type: ‘exponential’, delay: 5000 } (5초 후 재시도)
deployment-queue (C-130) (핵심) 컨트랙트 배포 처리 attempts: 2, backoff: { type: ‘exponential’, delay: 10000 } (배포는 재시도 간격 길게)
webhook-queue (H-200) (B2) 외부 알림 발송 attempts: 5, backoff: { type: ‘exponential’, delay: 10000 } (10초 후 재시도)

4. 배포 파이프라인 (Dockerfile)

t4g.small (ARM64) 단일 플랫폼 배포를 전제로 합니다. **멀티 스테이지 빌드(Multi-stage Build)**를 사용하며, 컨트랙트 아티팩트(ABI/Bytecode)를 최종 이미지에 포함시킵니다.

/server/Dockerfile (초안)

# -——————————–
# Stage 1: Builder
# -——————————–
FROM node:18-alpine AS builder

WORKDIR /app

# 모노레포 루트의 package.json, pnpm-lock.yaml 등을 복사
COPY package.json pnpm-lock.yaml ./
COPY pnpm-workspace.yaml ./

# server/ 와 contracts/ 의 package.json 복사
COPY server/package.json ./server/
COPY contracts/package.json ./contracts/

# 의존성 설치 (pnpm 사용)
RUN npm install -g pnpm
RUN pnpm install --frozen-lockfile

# 전체 소스 코드 복사
COPY . .

# (★수정★) 컨트랙트 빌드 (ABI/Bytecode 생성)
RUN pnpm --filter contracts build
# NestJS 서버 애플리케이션 빌드
RUN pnpm --filter server build

# -——————————–
# Stage 2: Final Runtime
# -——————————–
FROM node:18-alpine

WORKDIR /app

# Builder 스테이지에서 빌드 결과물만 복사
COPY --from=builder /app/server/dist ./dist

# (★수정★) 컨트랙트 아티팩트(ABI/Bytecode) 복사
COPY --from=builder /app/contracts/artifacts ./artifacts

# (중요) Production 의존성만 복사 (또는 재설치)
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/server/node_modules ./server/node_modules

# NestJS 서버 실행
CMD [“node”, “dist/main.js”]

5. 컨트랙트 배포 전략

배경: PLAN.md (v8)의 핵심 요구사항은 “새 NFT 컬렉션을 위한 동적 컨트랙트 배포 API(C-130)”입니다.

워크플로우:

  1. 사전 준비: 1인 개발자가 contracts/ 디렉토리에 미리 검증된 MijiNft.sol 템플릿을 개발합니다.
  2. 빌드 시점: Dockerfile (섹션 4)이 CI/CD 파이프라인에서 pnpm --filter contracts build를 실행하여, ABI와 Bytecode가 담긴 artifacts/ 폴더를 생성하고, 이 artifacts/ 폴더를 최종 서버 이미지에 포함시킵니다.
  3. API 호출 (C-130): 클라이언트(Miji)가 새 컬렉션이 필요할 때 POST /contracts/deploy API를 호출합니다. (DTO: name, symbol, chainId 등)
  4. 큐(Queue) 삽입: ContractsController는 DeployedContract 문서를 DEPLOYING 상태로 DB에 저장하고, deployment-queue (섹션 3)에 작업을 추가한 뒤 즉시 응답합니다. (HTTP 타임아웃 방지)
  5. 비동기 배포 (Processor):
    • ContractsProcessor가 큐에서 작업을 꺼냅니다.
    • fs.readFileSync 등을 사용해 서버 로컬(./artifacts/contracts/MijiNft.sol/MijiNft.json)에 저장된 ABI와 Bytecode를 읽어옵니다.
    • ethers.ContractFactory와 BlockchainService.getSigner()를 이용해 이 템플릿을 클라이언트가 요청한 name, symbol로 배포합니다.
    • 배포가 완료되면 DeployedContract DB의 status를 ACTIVE로 업데이트합니다.

6. 테스트 인프라 (P4-5)

로컬 개발(pnpm run start:dev) 및 E2E 테스트(pnpm test:e2e)를 위한 인프라 구성입니다.

  1. DB (MongoDB): Atlas의 Dev용 M0 클러스터를 사용합니다. .env 파일에 DB_URI를 설정하여 연결합니다.
  2. 큐 (Redis): 로컬 테스트의 편의성을 위해 Docker Compose를 사용합니다.

/docker-compose.yml (로컬 테스트용)

version: ‘3.8’
services:
# 1. NestJS API 서버
api:
build:
context: .
dockerfile: server/Dockerfile # (4번에서 정의한 Dockerfile)
ports:
- “3000:3000”
env_file:
- server/.env # 로컬 .env 파일 사용
depends_on:
- redis

# 2. Redis 서버 (큐/캐시용)
redis:
image: redis:7-alpine
ports:
- “6379:6379”
volumes:
- redis_data:/data # Redis 데이터 영속성

volumes:
redis_data:

7. 인증/보안 디테일 (A-120)

PLAN.md (v8)의 AuthGuard 외에 두 가지를 추가합니다.

  1. 키 회전(Key Rotation): .env의 GLOBAL_SECRET_KEY를 key1,key2처럼 콤마로 구분합니다. AuthGuard는 key1 또는 key2 중 하나라도 일치하면 인증을 통과시킵니다. (새 키 배포 시 무중단 회전 가능)
  2. Rate Limiting: Nginx 대신 NestJS 내장에서 처리하는 것이 MVP에 더 간단합니다. @nestjs/throttler 모듈을 app.module.ts에 글로벌하게 적용합니다.
    // app.module.ts
    import { ThrottlerModule } from ‘@nestjs/throttler’;

    @Module({
    imports: [
    ThrottlerModule.forRoot([{
    ttl: 60, // 1분
    limit: 60, // 60회
    }]),
    // …
    ],
    })
    export class AppModule {}

8. 메타데이터 규칙 (M-100)

SCHEMA.md (v6)를 기반으로 DTO(Data Transfer Object)에서 최소한의 구조를 검증해야 합니다. Miji 정의서(Source: “아발란체 디앱 개발, 티켓 쿠폰 메타데이터 상세”)의 누락 필드를 반영하여 DTO를 수정합니다.

9. Webhook 대상 규격 (H-200)

(B2) webhook-queue가 Miji 서버(WEBHOOK_URL_DEFAULT)로 전송할 페이로드(Payload)와 헤더 규격을 제안합니다.

10. 운영 모니터링 (P1-5)

  1. 로그 포맷 (JSON): pino (nestjs-pino) 라이브러리를 사용하여 모든 로그를 JSON 형식으로 출력합니다. (CloudWatch가 파싱하기 쉬움)
    • 모든 로그에 timestamp, level, context (NestJS 모듈명), message가 포함되어야 합니다.
    • HttpExceptionFilter는 에러 발생 시 trace_id와 함께 에러 객체를 로깅합니다.
  2. 큐 모니터링: NestJS에 @bull-board/nestjs 라이브러리를 추가합니다.
    • /admin/queues 같은 엔드포인트를 (AuthGuard 적용하여) 열어둡니다.
    • 1인 개발자가 Redis 큐(minting-queue, deployment-queue, webhook-queue)에 작업이 얼마나 쌓였는지, 실패한 작업은 무엇인지 시각적으로 모니터링할 수 있습니다.