Tools mentioned in this article
Open the browser-based tool while you read and try the workflow immediately.
“이 테이블, 어디에 연결되어 있었지?”
“이 user_id는 어떤 테이블을 참조하고 있지?”, “어라, 이 테이블은 아무 데서도 참조되지 않나?”—원본 SQL을 읽으며 관계를 추적하는 것은 머릿속으로 퍼즐을 맞추는 것과 같습니다.
**SQL to ER 다이어그램 변환 도구**는 CREATE TABLE 문을 받아 테이블·컬럼·기본키·외래키를 Mermaid ER 다이어그램으로 즉시 렌더링합니다. 모든 처리가 브라우저 안에서 이루어지며, SQL이 외부로 나가는 일은 없습니다.

이 글에서는 DDL 파서가 실제로 어떻게 동작하는지 내부를 살펴보고, 그 구조 때문에 알아둬야 할 지원 범위를 설명합니다.
사용법
CREATE TABLE users (
id BIGINT PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
total_amount INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
이것을 붙여넣으면 다음과 같은 Mermaid erDiagram 코드가 생성됩니다.
erDiagram
users {
BIGINT id PK
VARCHAR name
}
orders {
BIGINT id PK
BIGINT user_id
INTEGER total_amount
}
orders }|--|| users : "user_id -> id"
생성된 코드를 GitHub README나 풀 리퀘스트에 그대로 붙여넣으면 자동으로 렌더링됩니다.
파서의 원리: 정규표현식으로 DDL 분해하기
본격적인 SQL 파싱 라이브러리 대신, 이 도구는 정규표현식 기반의 경량 파서(SqlParser.parseDdl)를 사용합니다. 처리는 3단계로 이루어집니다.
1단계: 주석 제거와 문장 분할
const cleanSql = sql
.replace(/--.*$/gm, '') // 줄 주석 제거
.replace(/\/\*[\s\S]*?\*\//g, '') // 블록 주석 제거
.trim();
const statements = cleanSql.split(';').map(s => s.trim()).filter(Boolean);
문장을 단순히 ;로 분할하기 때문에, 기본값 안에 세미콜론이 포함된 DDL은 올바르게 파싱되지 않습니다—실무에서는 거의 발생하지 않는 케이스입니다.
2단계: 괄호 바깥의 쉼표로 컬럼 정의 분할하기
CREATE TABLE 본문을 단순히 ,로 분할하면 DECIMAL(10, 2) 같은 타입 정의 안의 쉼표에서도 끊어져 버립니다. 그래서 파서는 괄호의 깊이를 추적하며, 깊이가 0일 때의 쉼표만 구분자로 취급합니다.
private static splitByCommaOutsideParens(text: string): string[] {
let depth = 0;
// '(' 에서 depth++, ')' 에서 depth--, depth === 0 인 쉼표만 구분자로 취급
for (let i = 0; i < text.length; i++) {
if (text[i] === '(') depth++;
else if (text[i] === ')') depth--;
if (text[i] === ',' && depth === 0) { /* 여기서 분할 */ }
}
}
이는 JSON⇔CSV 변환 도구의 CSV 파서에서 쓴 것과 같은 발상입니다—상태를 가진 1글자씩의 스캔. SQL 컬럼 정의와 따옴표로 감싼 CSV 필드는 겉보기엔 다르지만, “특정 조건에서는 구분자가 무효화된다”는 성질을 공유합니다.
3단계: 각 줄을 PRIMARY KEY·FOREIGN KEY·컬럼 정의로 분류
분할된 각 줄을 정규표현식으로 패턴 매칭하여 3종류로 분류합니다.
// 테이블 레벨 기본키. PRIMARY KEY (id, tenant_id) 같은 복합키도 지원
const pkMatch = trimmed.match(/PRIMARY KEY\s*\(([\s\w,`"]+)\)/i);
// 테이블 레벨 외래키
const fkMatch = trimmed.match(
/FOREIGN KEY\s*\(([\s\w`"]+)\)\s*REFERENCES\s*(?:['\`"]?(\w+)['\`"]?\.)?['\`"]?(\w+)['\`"]?\s*\(([\s\w`"]+)\)/i
);
// 컬럼 정의. 끝에 인라인 REFERENCES 절이 있는 경우도 포착
const colMatch = trimmed.match(/^['`"]?(\w+)['`"]?\s+(\w+(?:\([\w\s,]+\))?)(.*)$/i);
REFERENCES 정규표현식은 컬럼 정의 끝의 인라인 외래키와 테이블 레벨의 FOREIGN KEY 절 양쪽 모두에 매칭됩니다. 복합 기본키도 지원되어, PRIMARY KEY (id, tenant_id) 같은 정의는 두 컬럼 모두에 PK 표시가 붙습니다.
출력: 까마귀발 표기법
도구가 생성하는 관계 라인은 까마귀발 표기법을 사용합니다.
orders }|--|| users : "user_id -> id"
}|(다)에서 ||(1)로 향하는 화살표로 “여러 주문이 한 사용자에 속한다”는 1대다 관계를 나타냅니다. 1대1, 다대다, 식별/비식별 관계를 포함한 이 표기법의 전체 가이드는 Mermaid ER 다이어그램 문법 레퍼런스를 참고하세요.
Mermaid의 erDiagram 문법은 컬럼 타입에 괄호를 포함할 수 없는 사양이므로, 길이 지정은 자동으로 생략됩니다—VARCHAR(255)는 VARCHAR로 축약됩니다(type.replace(/\([^)]*\)/g, '')).
지원하지 않는 DDL 패턴
정규표현식 기반의 경량 파서이기 때문에—브라우저에서 빠르게 동작하도록 번들 크기를 억제하기 위한 의도적인 트레이드오프—다음 패턴은 인식되지 않습니다.
| 패턴 | 지원 여부 |
|---|---|
CREATE TABLE 안의 PRIMARY KEY/FOREIGN KEY(테이블·컬럼 레벨 모두) | ✅ 지원 |
복합 기본키 PRIMARY KEY (a, b) | ✅ 지원 |
ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY | ❌ 미지원(CREATE TABLE 문 밖에 있기 때문) |
CREATE TABLE IF NOT EXISTS | ✅ 지원 |
스키마 한정 schema.table_name | ✅ 지원(테이블명 부분만 추출) |
-- 줄 주석 / /* */ 블록 주석 | ✅ 파싱 전에 제거 |
일부 마이그레이션 도구는 외래키를 별도의 ALTER TABLE 문으로 나중에 추가하는 스타일을 씁니다(Rails의 ActiveRecord가 대표적). 그런 경우, 외래키 줄을 일시적으로 CREATE TABLE 본문으로 옮기거나, 붙여넣기 전에 해당하는 FOREIGN KEY (...) REFERENCES ... 구문을 추가하면 관계가 인식됩니다.
생성된 다이어그램을 검토할 때 체크포인트
- 각 테이블에 기본키가 설정되어 있는가
- 외래키의 방향이 의도한 대로인가
- 어디에서도 참조되지 않는 고립된 테이블이 없는가
- 중간 테이블이 다대다 관계를 올바르게 표현하고 있는가
풀 리퀘스트 리뷰에서는 SQL diff만 보는 것보다 ER 다이어그램을 첨부하는 쪽이 변경의 영향 범위를 훨씬 잘 전달합니다. 출력이 일반 텍스트(Mermaid)이므로 리뷰 코멘트에 그대로 붙여넣을 수 있습니다.
자주 묻는 질문
MySQL이나 PostgreSQL의 DDL에서도 사용할 수 있나요?
CREATE TABLE을 중심으로 한 일반적인 DDL이라면 사용할 수 있습니다. 방언 고유의 타입이나 옵션(ENGINE=InnoDB 등)은 단순히 무시되며, 테이블·컬럼·키 추출에는 영향을 주지 않습니다.
외래키가 없는 SQL이어도 다이어그램이 되나요?
외래키 제약이 없어도 테이블과 컬럼 목록으로 시각화됩니다. 다만 관계 라인은 FOREIGN KEY/REFERENCES 정보를 바탕으로 만들어지므로, 관계를 명확히 표시하고 싶다면 DDL에 포함시켜 주세요.
ALTER TABLE로 추가한 외래키도 인식되나요?
현재 파서는 CREATE TABLE 문 내부만 분석하므로, 별도의 ALTER TABLE ... ADD CONSTRAINT 문으로 추가된 외래키는 인식되지 않습니다. 인식시키고 싶다면 해당 FOREIGN KEY (...) REFERENCES ... 구문을 일시적으로 CREATE TABLE 쪽에 포함시킨 뒤 붙여넣으세요.
생성한 다이어그램을 문서에 붙여넣을 수 있나요?
네. Mermaid 형식으로 출력되므로 GitHub README나 설계 문서에 붙여넣으면 코드에 가까운 형태로 구성도를 관리할 수 있습니다. 까마귀발 표기법을 읽는 법은 Mermaid ER 다이어그램 문법 레퍼런스를 참고하세요.
정리
- 파싱은 정규표현식 기반의 경량 구현. 괄호 깊이를 보고 쉼표를 분할하는 처리가 핵심
- PRIMARY KEY·FOREIGN KEY(테이블/컬럼 레벨 모두)·복합 기본키를 지원
ALTER TABLE로 나중에 추가한 외래키는 미지원. 마이그레이션 스타일에 따라 수정이 필요할 수 있음- 생성 코드는 Mermaid의 까마귀발 표기법. 읽는 법은 별도 레퍼런스 글 참고
손에 있는 SQL을 도구에 맡기고, 지금까지 보이지 않던 테이블 간의 관계를 확인해보세요.