# 워크스루: 업무를 설명하면, 표준을 따르는 스키마가 나온다

> AI 에이전트로 처음부터 끝까지 — 로그인하고, 스레드형 게시판을 업무 용어로 설명하고, 팀 표준을 따르는 테이블·컬럼·물리명을 얻습니다.



아무 챗봇에나 게시판 스키마를 달라고 하면 하나 나옵니다. 문제는 *어떤* 스키마를 얻는
게 아니라 **팀이 이미 합의한** 스키마를 얻는 것입니다: 같은 약어, 같은 접미사, 같은
개념에 같은 단어 — 첫 번째 테이블과 열 번째 테이블에서 똑같이.

이 워크스루는 빈 프로젝트에서 표준을 준수하는 DDL까지 에이전트를 떠나지 않고 갑니다.
예제는 스레드형 게시판입니다 — 따라갈 만큼 작고, 대부분의 도구를 넘어뜨리는 한 가지
경우를 강제합니다: 다른 글에 답하는 글.

아래 출력은 전부 실제 실행에서 복사했습니다.

## 시작하기 전에

- Node.js 22 이상, `sqemo-mcp` **2.2.0 이상**
- 에이전트에 MCP 서버가 등록돼 있고 `npx sqemo-mcp login`을 마친 상태 —
  [AI 연동(MCP)](/ko/docs/mcp/) 참고
- [팀 표준](/ko/docs/naming-standards/)이 있는 워크스페이스. 이 워크스루는 단어사전에
  이미 *Customer*, *Order*, *Number*, *Content*, *Flag* 등 십여 개가 들어 있는 것을
  씁니다

## 1. 표준 위에서 프로젝트 시작

새 ERD는 단어사전이 비어 있어 물리명이 논리명 그대로 나옵니다. **생성 시점에**
프로젝트를 표준에 묶으면 단어사전·명명 규칙·도메인이 함께 옵니다:

```jsonc
list_workspaces
// → [{ workspaceId: "…", name: "Engineering", role: "owner",
//      glossary: { version: 4, … } }]

create_erd {
  name: "Threaded Discussion Board",
  workspaceId: "…",
  target: { server: true }
}
```

```jsonc
get_erd_overview
// → { glossaryLinked: true, dictionaryEntryCount: 28, domainCount: 15, … }
```

저 `glossaryLinked: true`가 핵심입니다. 이제부터 에이전트가 만드는 모든 이름은
지어내는 게 아니라 표준을 통해 풀립니다.

## 2. 업무 용어로 일을 설명

이제 동료에게 말하듯 에이전트에게 말합니다:

> 회원이 글을 올리고, 글은 다른 글에 대한 답글일 수 있고, 회원이 글에 댓글을 다는
> 게시판을 모델링해 줘.

에이전트는 **논리**명으로 `upsert_entity`와 `upsert_attribute`를 호출합니다. 컬럼명은
한 번도 치지 않습니다:

```jsonc
upsert_entity   { logicalName: "Post" }
// → { physicalName: "POST" }

upsert_attribute { logicalName: "Post Content", domain: "Content" }
// → { physicalName: "POST_CNTS" }

upsert_attribute { logicalName: "Delete Flag", domain: "Flag" }
// → { physicalName: "DELETE_YN" }
```

`Content` → `CNTS`, `Flag` → `YN`은 에이전트의 취향이 아닙니다. 팀 단어사전의
약어이고, 팀이 모델링한 다른 모든 테이블에 적용된 것과 같은 방식으로 적용됐습니다.

도메인이 데이터 타입을 실어 나르므로 `Content`는 나타나는 모든 곳에서
`varchar(1000)`입니다 — 테이블마다 컬럼 폭을 정하는 사람은 없습니다.

## 3. 글에 답하는 글

자기참조 외래키는 기본키의 이름을 그대로 쓸 수 없어 **역할 접두사(role prefix)** 를
받습니다. 접두사는 명명 규칙이고 — 다른 모든 단어처럼 — 단어사전을 통해 풀립니다:

```jsonc
upsert_relationship {
  sourceEntityId: "<Post>", targetEntityId: "<Post>",
  cardinality: "1:N", relationshipType: "nonIdentifying",
  constraintName: "FK_POST_PARENT"
}
```

```sql
`PARENT_POST_NO` varchar(20) COMMENT 'Parent Post Number'
```

접두사 기본값은 `Parent`라 위 FK는 설정 없이 `PARENT_POST_NO`로 나옵니다. 표준에서
한 번 바꾸면 — 앱에서 **명명(Naming) 탭 → 편집(Edit) → 자기참조 접두사(Self-reference
prefix)** — 연결된 모든 프로젝트의 모든 자기참조 FK가 따릅니다.

바꾸기 전에 알아 둘 두 가지:

- 접두사를 줄이고 싶으면 먼저 단어로 등록하세요(`Parent` → `PRNT`이면
  `PARENT_POST_NO` 대신 `PRNT_POST_NO`).
- 보통 컬럼명과 달리 **기존 자기참조 FK는 다음 편집 때 이름이 바뀝니다.** 이름이
  저장된 것이 아니라 파생되기 때문입니다. 나머지는 이미 가진 이름을 유지합니다.

이 기본값이 바뀌기 전에 만든 프로젝트는 발밑에서 움직이지 않습니다. 불러올 때 미설정
접두사가 명명 규칙에 기록됩니다: 이미 자기참조 관계가 있으면 `상위`(그래서 그 컬럼명이
그대로 유지됨), 없으면 `Parent`.

## 4. 표준에 아직 없는 단어

실제 모델링은 아무도 등록하지 않은 단어에 부딪힙니다. Sqemo는 조용히 약어를 지어내지
않고 표시합니다:

```jsonc
lint_erd
// → { code: "unknown-word", severity: "warning",
//      objectName: "Delete Flag",
//      message: "'Delete Flag' contains words not registered in the word list." }
```

`Delete`가 없어서 `DELETE_YN`이 줄여지지 않고 나왔습니다. 고치는 방법은 이름을
하드코딩하는 게 아니라 단어를 제안하는 것입니다:

```jsonc
propose_dictionary_word {
  logicalWord: "Delete", physicalWord: "DELETE", abbreviation: "DEL",
  note: "Found while modelling the discussion board"
}
// → { proposalId: "…", status: "pending", baseVersion: 4 }
```

표준 소유자가 앱에서 승인하면 약어가 연결된 모든 프로젝트로 전파됩니다. 챗봇이 할 수
없는 부분이 이것입니다: 에이전트는 **제안자**이지 권한자가 아닙니다.

## 5. 준수 검사

`check_naming`은 이미 있는 물리명을 표준이 생성했을 이름과 비교합니다:

```jsonc
check_naming {
  logicalName: "Customer Phone Number",
  physicalName: "CUSTOMER_PHONE_NUMBER"
}
// → { generatedPhysicalName: "CUST_TEL_NO",
//      compliant: false, providedMatches: false }
```

CI에 넣을 검사가 이것입니다. 에이전트 없이 프로젝트 파일에서 동작합니다:

```bash
npx sqemo-mcp lint schema.erd.json   # 위반이 있으면 종료 코드 1
```

## 6. 내보내기

```jsonc
export_sql { dialect: "mysql" }
```

```sql
CREATE TABLE `POST` (
  `POST_NO`        varchar(20)   NOT NULL COMMENT 'Post Number',
  `POST_CNTS`      varchar(1000) NOT NULL COMMENT 'Post Content',
  `DELETE_YN`      char(1)       NOT NULL DEFAULT 'N' COMMENT 'Delete Flag',
  `PARENT_POST_NO` varchar(20)            COMMENT 'Parent Post Number',
  PRIMARY KEY (`POST_NO`)
) COMMENT='An article on a board; may reply to another post';

ALTER TABLE `POST` ADD CONSTRAINT `FK_POST_PARENT`
  FOREIGN KEY (`PARENT_POST_NO`) REFERENCES `POST` (`POST_NO`);
```

논리명이 컬럼 코멘트로 살아남아, 업무 의미가 다이어그램에서 죽지 않고 데이터베이스까지
갑니다.

## 함정 하나

`defaultValue`는 원시 SQL이며 `DEFAULT` 뒤에 그대로 나갑니다. 문자열 리터럴은 직접
따옴표로 감싸세요:

```jsonc
upsert_attribute { logicalName: "Delete Flag", defaultValue: "'N'" }  // ✅
upsert_attribute { logicalName: "Delete Flag", defaultValue: "N" }    // ❌ DEFAULT N
```

따옴표 없는 값은 표현식으로 취급됩니다. `0`이나 `CURRENT_TIMESTAMP`에는 원하는
동작이고 — `N`에는 원하지 않는 동작입니다.

## 실제로 일어난 일

에이전트는 타이핑을 했습니다. 중요한 것은 아무것도 결정하지 않았습니다:

| 결정 | 결정한 쪽 |
|---|---|
| 어떤 테이블과 관계가 존재하는가 | 당신, 업무 용어로 |
| 약어·구분자·대소문자 | 팀 표준 |
| 데이터 타입 | 도메인 |
| 표준에 들어가는 새 단어 | 표준 소유자, 승인으로 |

스키마를 생성하는 것과 팀이 유지할 수 있는 스키마를 키우는 것의 차이가 그것입니다.
다음은: 단어사전과 규칙의 동작은 [명명 표준](/ko/docs/naming-standards/), 제안 목록과
역할은 [공유와 협업](/ko/docs/collaboration/) — 제안 목록은
[2분 영상](https://youtu.be/wHsoFbak6GY)으로도 있으니, 단어가 승인되는 과정을 읽기보다
보고 싶다면 그쪽으로.

---
Source: https://sqemo.com/ko/docs/ai-walkthrough/ · Updated 2026-08-29
