메인 콘텐츠로 건너뛰기
Sonamu는 Entity와 API가 변경되면 자동으로 타입 정의와 Service 코드를 생성하여 프론트엔드 프로젝트에 동기화합니다. sync.targets로 동기화할 프론트엔드를 지정합니다.

기본 구조

import { defineConfig } from "sonamu";

export default defineConfig({
  sync: {
    targets: ["web"],  // web 폴더로 동기화
  },
  // ...
});

targets

동기화할 프론트엔드 프로젝트 목록을 지정합니다. 타입: string[] (필수)
export default defineConfig({
  sync: {
    targets: ["web", "app"],  // 2개 프로젝트에 동기화
  },
});

동작 방식

Entity나 API가 변경되면 Sonamu는 다음 파일들을 자동으로 생성하여 각 target 디렉토리에 동기화합니다:
  1. 타입 정의 ({entity}.types.ts)
    • Entity 필드에서 생성된 TypeScript 타입
    • Zod 검증 스키마
    • Subset 타입
  2. Service 클래스 ({entity}.service.ts)
    • API 호출을 위한 타입 안전한 메서드
    • TanStack Query 통합 준비

디렉토리 구조

단일 프론트엔드

가장 일반적인 경우로, 웹 프론트엔드 하나만 사용합니다.
export default defineConfig({
  sync: {
    targets: ["web"],
  },
  // ...
});
프로젝트 구조:

동기화되는 위치: web/src/services/

다중 프론트엔드

웹과 앱(React Native, Flutter 등)을 동시에 개발하는 경우:
export default defineConfig({
  sync: {
    targets: ["web", "app"],
  },
  // ...
});
프로젝트 구조:

동기화되는 위치:
  • web/src/services/
  • app/src/services/
각 프론트엔드는 동일한 타입과 Service를 공유하므로, API 변경 시 모든 클라이언트가 자동으로 업데이트됩니다.

target 디렉토리 위치

기본적으로 Sonamu는 프로젝트 루트에서 target 이름과 동일한 디렉토리를 찾습니다.

커스텀 경로

target 디렉토리가 다른 위치에 있다면 상대 경로를 사용할 수 있습니다:
export default defineConfig({
  sync: {
    targets: ["../frontend/web", "../mobile/app"],
  },
});
프로젝트 구조:

동기화 확인

Entity를 수정하고 저장하면 콘솔에 동기화 메시지가 표시됩니다:
 Syncing to targets...
 web/src/services/user.types.ts
 web/src/services/user.service.ts
 app/src/services/user.types.ts
 app/src/services/user.service.ts

 All files are synced!

생성되는 파일

각 Entity마다 2개의 파일이 생성됩니다: user.types.ts:
// 자동 생성된 타입
export type UserSaved = {
  id: number;
  email: string;
  name: string;
  createdAt: Date;
};

export type UserForm = {
  email: string;
  name: string;
};

// Zod 스키마
export const UserFormSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1),
});
user.service.ts:
// 자동 생성된 Service
export class UserService {
  static async list(params: UserListParams): Promise<UserSaved[]> {
    return apiClient.get("/api/user/list", { params });
  }
  
  static async detail(id: number): Promise<UserSaved> {
    return apiClient.get(`/api/user/${id}/detail`);
  }
  
  static async save(form: UserForm): Promise<UserSaved> {
    return apiClient.post("/api/user/save", form);
  }
}

동기화 비활성화

프론트엔드가 없거나 동기화가 필요 없다면 빈 배열을 지정합니다:
export default defineConfig({
  sync: {
    targets: [],  // 동기화 하지 않음
  },
  // ...
});
API 전용 프로젝트(예: 마이크로서비스)에서는 동기화를 비활성화할 수 있습니다.

실전 예시

기본 웹 프로젝트

import { defineConfig } from "sonamu";

export default defineConfig({
  projectName: "MyApp",
  
  sync: {
    targets: ["web"],
  },
  
  // ...
});
프로젝트 구조:

웹 + 모바일 앱

import { defineConfig } from "sonamu";

export default defineConfig({
  projectName: "EcommerceApp",
  
  sync: {
    targets: ["web", "mobile"],
  },
  
  // ...
});
프로젝트 구조:

Monorepo 구조

import { defineConfig } from "sonamu";

export default defineConfig({
  projectName: "MonorepoApp",
  
  sync: {
    targets: [
      "../packages/web-client",
      "../packages/admin-panel",
      "../packages/mobile-app",
    ],
  },
  
  // ...
});
프로젝트 구조:

동기화 주의사항

1. 자동 생성 파일 수정 금지

동기화된 파일은 자동으로 생성되므로 직접 수정하면 안 됩니다.
// ❌ 수정하지 마세요!
// web/src/services/user.service.ts

export class UserService {
  static async list() {
    // 여기에 코드를 추가하면 다음 동기화 시 사라집니다!
  }
}
대신 래퍼 클래스 사용:
// ✅ 별도 파일로 관리
// web/src/services/user-wrapper.ts

import { UserService } from "./user.service";

export class UserServiceWrapper {
  static async listActiveUsers() {
    const users = await UserService.list();
    return users.filter(u => u.status === "active");
  }
}

2. Git 관리

동기화된 파일은 Git에 커밋하세요.
# ❌ .gitignore에 추가하지 마세요
# web/src/services/

# ✅ Git에 포함시켜야 합니다
이유:
  • 팀원들이 백엔드를 실행하지 않아도 프론트엔드 개발 가능
  • CI/CD에서 Sonamu 없이 프론트엔드만 빌드 가능

3. target 디렉토리 존재 확인

target으로 지정한 디렉토리가 존재해야 합니다.
# 디렉토리가 없으면 에러 발생
 Error: Sync target 'web' not found at ../web
해결:
# target 디렉토리 생성
mkdir web
mkdir app

프론트엔드 설정

동기화된 Service를 사용하려면 프론트엔드에서 약간의 설정이 필요합니다.

1. API 클라이언트 설정

web/src/lib/api-client.ts:
import axios from "axios";

export const apiClient = axios.create({
  baseURL: import.meta.env.VITE_API_URL ?? "http://localhost:1028",
  withCredentials: true,
});

2. Service 사용

import { UserService } from "@/services/user.service";

// API 호출
const users = await UserService.list();
const user = await UserService.detail(1);
프론트엔드 통합 가이드

다음 단계

동기화 대상 설정을 완료했다면: