메인 콘텐츠로 건너뛰기
Sonamu는 파일 변경에 따라 관련 파일을 자동으로 재생성합니다. 이 문서는 각 파일 변경 시 어떤 파일이 재생성되는지 명확하게 설명합니다.

재생성 흐름도

Entity 저장 시

.entity.json 파일을 저장하면 다음 파일들이 재생성됩니다.

재생성되는 파일

1

1. {Entity}.types.ts

Entity별 TypeScript 타입과 Zod 스키마위치: api/src/application/{entity}/{entity}.types.ts생성 조건: Entity가 처음 생성될 때 (이후 자동 재생성 안됨)
user.types.ts
// Base 타입
export type User = {
  id: number;
  email: string;
  // ...
};

// Zod 스키마
export const User = z.object({
  id: z.number(),
  email: z.string(),
  // ...
});
2

2. sonamu.generated.ts

프로젝트 전체의 베이스 타입과 Enum위치: api/src/application/sonamu.generated.ts항상 재생성: ✅
// 모든 Entity의 Enum
export const UserRole = z.enum(["admin", "normal"]);

// Subset 타입
export type UserSubsetKey = "A" | "P" | "SS";
export type UserSubsetMapping = { ... };

// Model exports
export * from "./user/user.model";
3

3. sonamu.generated.sso.ts

Subset 쿼리 함수들위치: api/src/application/sonamu.generated.sso.ts항상 재생성: ✅
export const userSubsetQueries = {
  A: (puri) => puri.table("users").select([...]),
  P: (puri) => puri.table("users").leftJoin(...).select([...]),
};

export const userLoaderQueries = { ... };
4

4. 타겟 복사

위 파일들이 타겟 프로젝트에 복사됨위치:
  • web/src/services/user/user.types.ts
  • web/src/services/sonamu.generated.ts
  • web/src/services/sonamu.generated.sso.ts
  • app/src/services/... (동일)

트리거 예시

{
  "id": "User",
  "table": "users",
  "props": [
    { "name": "id", "type": "integer" },
    { "name": "email", "type": "string" }
  ],
  "subsets": {
    "A": ["id", "email"]
  },
  "enums": {
    "UserRole": {
      "admin": "관리자",
      "normal": "일반 사용자"
    }
  }
}
{entity}.types.ts처음 Entity 생성 시에만 자동 생성됩니다. 이후에는 수동으로 관리해야 합니다.이미 존재하는 user.types.ts를 재생성하려면:
rm api/src/application/user/user.types.ts
# Entity 다시 저장

Model 저장 시

.model.ts 파일을 저장하면 다음 파일들이 재생성됩니다.

재생성되는 파일

1

1. services.generated.ts

API 클라이언트 함수와 TanStack Query hooks위치:
  • web/src/services/services.generated.ts
  • app/src/services/services.generated.ts
항상 재생성: ✅
// Axios 클라이언트
export async function findUserById(id: number): Promise<User> { ... }

// TanStack Query hooks
export function useUserById(id: number) { ... }
export function useSaveUser() { ... }
생성 기준: Model의 @api 데코레이터가 있는 메서드
2

2. sonamu.generated.http

REST Client용 HTTP 테스트 파일위치: api/sonamu.generated.http항상 재생성: ✅
### User.findById
GET http://localhost:3000/user/findById?id=1

### User.save
POST http://localhost:3000/user/save
Content-Type: application/json

{ "params": [...] }
3

3. queries.generated.ts

SSR용 Query Options위치: web/src/queries.generated.ts항상 재생성: ✅
export const userQueries = {
  findById: (id: number) => queryOptions({
    queryKey: ["User", "findById", id],
    queryFn: () => findUserById(id),
  }),
};
4

4. entry-server.generated.tsx

SSR Entry Server 코드위치: web/src/entry-server.generated.tsx항상 재생성: ✅
export async function loader({ params }) {
  const queryClient = new QueryClient();
  
  // SSR 데이터 프리페칭
  if (params.userId) {
    await queryClient.prefetchQuery(
      userQueries.findById(Number(params.userId))
    );
  }
  
  return { dehydratedState: dehydrate(queryClient) };
}

트리거 예시

class UserModelClass extends BaseModelClass {
  @api({ httpMethod: "GET", clients: ["axios", "tanstack-query"] })
  async findById(id: number): Promise<User> {
    // ...
  }

  @api({ httpMethod: "POST", clients: ["axios", "tanstack-mutation"] })
  async save(params: UserSaveParams[]): Promise<number[]> {
    // ...
  }
}

Types 저장 시

.types.ts 파일을 저장하면 타겟에 복사됩니다.

재생성되는 파일

1

타겟 복사

수정된 Types 파일이 타겟 프로젝트에 복사위치:
  • web/src/services/{entity}/{entity}.types.ts
  • app/src/services/{entity}/{entity}.types.ts
항상 복사: ✅import 변환: sonamu./sonamu.shared

트리거 예시

import { z } from "zod";

// 커스텀 타입 추가
export const UserLoginParams = z.object({
  email: z.string().email(),
  password: z.string().min(8),
});

export type UserLoginParams = z.infer<typeof UserLoginParams>;

Config 저장 시

sonamu.config.ts 파일을 저장하면 환경 변수가 동기화됩니다.

재생성되는 파일

1

.sonamu.env

API 서버 정보를 담은 환경 변수 파일위치:
  • web/.sonamu.env
  • app/.sonamu.env
항상 재생성: ✅
API_HOST=localhost
API_PORT=3000

트리거 예시

export default {
  server: {
    listen: {
      host: "0.0.0.0",  // 변경
      port: 4000,       // 변경
    },
  },
};

i18n 파일 저장 시

src/i18n/**/*.ts 파일을 저장하면 SD 파일이 재생성됩니다.

재생성되는 파일

1

1. Locale 파일 복사

i18n 파일이 타겟에 복사됨위치:
  • web/src/i18n/{locale}.ts
  • app/src/i18n/{locale}.ts
2

2. sd.generated.ts

Sonamu Dictionary 파일 생성위치:
  • api/src/sd.generated.ts
  • web/src/sd.generated.ts
  • app/src/sd.generated.ts
항상 재생성: ✅
export const SD = {
  User: "사용자",
  user: {
    id: "ID",
    email: "이메일",
  },
} as const;

재생성 매트릭스

파일 변경과 재생성 관계를 한눈에 보여주는 표입니다.
변경 파일.types.tssonamu.generated.tssonamu.generated.sso.tsservices.generated.tssonamu.generated.httpqueries.generated.tsentry-server.generated.tsx.sonamu.envsd.generated.ts
.entity.json✅ (첫 생성)------
.model.ts-----
.types.ts복사--------
sonamu.config.ts--------
i18n/.ts--------
범례:
  • ✅ = 항상 재생성
  • ✅ (첫 생성) = 첫 생성 시에만
  • 복사 = 타겟에 복사
    • = 재생성 안됨

재생성 방지하기

생성된 파일을 수정하면 다음 재생성 시 덮어씌워집니다.

❌ 잘못된 방법

services.generated.ts 직접 수정
// ❌ 이렇게 하지 마세요!
export async function findUserById(id: number): Promise<User> {
  // 커스텀 로직 추가
  console.log("Finding user:", id);
  
  const { data } = await axios.get("/user/findById", { params: { id } });
  return data;
}
// → Model 변경 시 덮어씌워짐 💥

✅ 올바른 방법

services/user/user.custom.ts
import { findUserById } from "../services.generated";

// 래퍼 함수 생성
export async function findUserByIdWithLog(id: number) {
  console.log("Finding user:", id);
  return findUserById(id);
}
장점: 생성 파일 건드리지 않음

재생성 강제하기

파일이 재생성되지 않을 때 강제로 재생성하는 방법입니다.

Checksum 초기화

# Checksum 파일 삭제
rm .sonamu-checksums.json

# 전체 재동기화
pnpm sonamu sync

특정 파일 재생성

# Entity 파일 재저장 (Sonamu UI에서)
# 또는 파일 내용 변경 후 저장

# Model 파일 재저장
# → services.generated.ts 등 자동 재생성

강제 Overwrite

import { Sonamu } from "sonamu";

// overwrite 옵션으로 강제 재생성
await Sonamu.syncer.generateTemplate(
  "services",
  {},
  { overwrite: true }  // 기존 파일 덮어쓰기
);

재생성 최적화

재생성 시간을 줄이는 팁입니다.
Types만 수정했다면 Entity를 다시 저장할 필요 없음
  • Types 변경 → 타겟 복사만 (빠름)
  • Entity 변경 → 모든 스키마 재생성 (느림)
사용하지 않는 타겟을 sonamu.config.ts에서 제거
export default {
  sync: {
    targets: ["web"],  // app 제거
  },
};
필요한 메서드에만 @api 추가
  • 내부 메서드는 @api 불필요
  • API 수가 적을수록 빠른 재생성
Node.js v22+ 사용 및 SSD 활용
{
  "engines": {
    "node": ">=22.0.0"
  }
}

다음 단계