메인 콘텐츠로 건너뛰기
pnpm stub 명령어는 반복 작업을 줄이기 위한 코드 템플릿을 자동 생성합니다. Practice 스크립트나 새 Entity를 빠르게 만들 수 있어 개발 속도를 높입니다.

명령어

practice - Practice 스크립트 생성

임시 실험이나 데이터 처리를 위한 스크립트를 생성합니다.
pnpm stub practice <name>
예시:
pnpm stub practice test-api

# 생성됨:
# src/practices/p1-test-api.ts
# VS Code에서 자동으로 열림
# 실행 명령어가 클립보드에 복사됨
생성된 파일:
src/practices/p1-test-api.ts
import { Sonamu } from "sonamu";

console.clear();
console.log("p1-test-api.ts");

Sonamu.runScript(async () => {
  // TODO
});
실행:
# 클립보드에서 붙여넣기 (Cmd+V)
pnpm node -r dotenv/config --enable-source-maps dist/practices/p1-test-api.js

entity - Entity 생성

새로운 Entity를 생성합니다.
pnpm stub entity <entityId>
예시:
pnpm stub entity Product

# 생성됨:
# src/entities/Product.entity.ts
생성된 파일:
src/entities/Product.entity.ts
import type { EntityType } from "sonamu";

export const ProductEntity = {
  properties: [
    {
      name: "id",
      type: "int",
      primaryKey: true,
      autoIncrement: true,
    },
    {
      name: "title",
      type: "string",
      length: 255,
    },
    {
      name: "created_at",
      type: "datetime",
      default: "CURRENT_TIMESTAMP",
    },
  ],
} satisfies EntityType;

Practice 스크립트

사용 시나리오

Practice 스크립트는 다음과 같은 작업에 활용됩니다:
용도설명예시
API 테스트외부 API 호출 테스트p1-test-payment-api.ts
데이터 마이그레이션일회성 데이터 변환p2-migrate-user-data.ts
디버깅특정 로직 검증p3-debug-cache-issue.ts
데이터 생성테스트 데이터 생성p4-create-sample-posts.ts
분석데이터 통계 확인p5-analyze-user-activity.ts

순서 관리

Practice 파일은 자동으로 번호가 부여됩니다:
📁src/practices/
📄TSp1-first-practice.ts
📄TSp2-second-practice.ts
📄TSp3-third-practice.ts - 다음은 p4
새 practice를 생성하면 가장 큰 번호 + 1이 자동으로 할당됩니다.

실전 예제

1. API 테스트

src/practices/p1-test-payment-api.ts
import { Sonamu } from "sonamu";

console.clear();
console.log("p1-test-payment-api.ts");

Sonamu.runScript(async () => {
  const paymentService = new PaymentService();
  
  // 테스트 결제 시도
  const result = await paymentService.charge({
    amount: 10000,
    orderId: "TEST-001",
  });
  
  console.log("결제 결과:", result);
});

2. 데이터 마이그레이션

src/practices/p2-migrate-user-data.ts
import { Sonamu } from "sonamu";
import { UserModel } from "../models/User.model";

console.clear();
console.log("p2-migrate-user-data.ts");

Sonamu.runScript(async () => {
  // 모든 사용자 조회
  const users = await UserModel.findAll();
  
  for (const user of users) {
    // legacy 필드를 새 필드로 마이그레이션
    await UserModel.update(user.id, {
      new_field: transformData(user.legacy_field),
    });
    
    console.log(`✓ Migrated user ${user.id}`);
  }
  
  console.log(`\n✓ Total: ${users.length} users migrated`);
});

3. 배치 작업

src/practices/p3-send-welcome-emails.ts
import { Sonamu } from "sonamu";
import { UserModel } from "../models/User.model";

console.clear();
console.log("p3-send-welcome-emails.ts");

Sonamu.runScript(async () => {
  // 신규 가입 사용자
  const newUsers = await UserModel.findMany({
    where: { welcome_email_sent: false },
  });
  
  console.log(`Sending welcome emails to ${newUsers.length} users...`);
  
  for (const user of newUsers) {
    try {
      await sendWelcomeEmail(user.email);
      await UserModel.update(user.id, {
        welcome_email_sent: true,
      });
      console.log(`✓ Sent to ${user.email}`);
    } catch (error) {
      console.error(`✗ Failed to send to ${user.email}:`, error);
    }
  }
});

Entity 생성

기본 구조

stub entity로 생성된 Entity는 기본 필드들을 포함합니다:
export const ProductEntity = {
  properties: [
    { name: "id", type: "int", primaryKey: true, autoIncrement: true },
    { name: "title", type: "string", length: 255 },
    { name: "created_at", type: "datetime", default: "CURRENT_TIMESTAMP" },
  ],
} satisfies EntityType;

커스터마이징

생성 후 필요한 필드를 추가합니다:
export const ProductEntity = {
  properties: [
    // 기본 필드
    { name: "id", type: "int", primaryKey: true, autoIncrement: true },
    { name: "title", type: "string", length: 255 },
    
    // 추가 필드
    { name: "description", type: "text", nullable: true },
    { name: "price", type: "decimal", precision: 10, scale: 2 },
    { name: "stock", type: "int", default: 0 },
    { name: "category_id", type: "int" },
    
    // 타임스탬프
    { name: "created_at", type: "datetime", default: "CURRENT_TIMESTAMP" },
    { name: "updated_at", type: "datetime", onUpdate: "CURRENT_TIMESTAMP" },
  ],
  
  indexes: [
    { fields: ["category_id"] },
    { fields: ["title"], type: "fulltext" },
  ],
  
  belongsTo: [
    { entityId: "Category", as: "category" },
  ],
} satisfies EntityType;

개발 워크플로우

1. 아이디어 검증

# 빠르게 practice 생성
pnpm stub practice test-idea

# 코드 작성 및 실행
pnpm node -r dotenv/config --enable-source-maps dist/practices/p1-test-idea.js

# 동작 확인 후 삭제 또는 보관

2. Entity 추가

# Entity 생성
pnpm stub entity Order

# Entity 정의 작성
# src/entities/Order.entity.ts

# 마이그레이션 생성 및 적용
pnpm migrate run

# Model 스캐폴딩
pnpm scaffold model Order

3. 데이터 작업

# Practice 스크립트 생성
pnpm stub practice import-orders

# 데이터 처리 로직 작성
# src/practices/p1-import-orders.ts

# 실행
pnpm node -r dotenv/config --enable-source-maps dist/practices/p1-import-orders.js

실전 팁

1. Practice 스크립트 재사용

// ✅ 재사용 가능하도록 함수화
async function processUsers(filter: any) {
  const users = await UserModel.findMany({ where: filter });
  // 처리 로직...
}

Sonamu.runScript(async () => {
  // 다양한 필터로 재사용
  await processUsers({ role: "admin" });
  await processUsers({ role: "user" });
});

2. 에러 처리

Sonamu.runScript(async () => {
  try {
    await riskyOperation();
  } catch (error) {
    console.error("Error occurred:", error);
    process.exit(1);  // 실패 시 종료
  }
});

3. 진행 상황 표시

Sonamu.runScript(async () => {
  const users = await UserModel.findAll();
  
  for (let i = 0; i < users.length; i++) {
    await processUser(users[i]);
    
    // 진행률 표시
    const progress = Math.round(((i + 1) / users.length) * 100);
    console.log(`Progress: ${progress}% (${i + 1}/${users.length})`);
  }
});

4. Practice 정리

# 완료된 practice는 삭제
rm src/practices/p1-old-practice.ts

# 또는 보관용 디렉토리로 이동
mkdir src/practices/archived
mv src/practices/p1-*.ts src/practices/archived/

주의사항

Practice 스크립트 사용 시 주의사항:
  1. 프로덕션 데이터: Practice는 실제 DB를 사용합니다. 조심하세요!
  2. 백업: 중요한 데이터 작업 전에는 반드시 백업하세요.
  3. 트랜잭션: 데이터 변경 시 트랜잭션을 사용하세요.
  4. 테스트: 프로덕션 실행 전 개발 환경에서 먼저 테스트하세요.

다음 단계