본문으로 이동

카드와 보기

요약

이 문서는 순서 보기(sequence), 상태 카드(state), 테이블 카드(table), 클래스 카드(class, interface)의 선언과 장면 규칙을 정한다. 문서 하나는 카드 모형 하나이고, 그림의 모양은 보기가 정한다. 줄, 낱말, 이름, 값 형식, 머리, 오류 규칙은 그림 문법을 따르고, 이 문서는 카드와 보기마다 다른 점만 정한다. 그림 종류를 첫 문장으로 고르는 문장은 없다.

동기

순서, 상태, 스키마, 클래스를 서로 다른 그림 종류로 두면 같은 서비스를 설명하는 그림마다 원본을 따로 적어야 하고, 한 사건이 여러 그림에서 같은 시각에 일어난다는 사실을 표현할 수 없다. 카드를 한 번 선언하고 보기로 모양을 고르면 호출 순서, 스키마, 클래스가 한 문서에서 같은 이름과 같은 상태를 쓴다. 보기에 맞지 않는 줄과 카드는 줄 번호가 붙은 오류로 나온다.

예시

순서 보기

Plain text
thinkflow
title "OAuth 인가 코드와 PKCE"

person user "사용자"
box app "앱"
box auth "인가 서버"
external api "리소스 API"

view sequence {
  user app auth api
}

scene "인가와 토큰"
  user -> app "로그인 누름"
  app -> auth "authorize?code_challenge"
  note auth "challenge = S256(verifier)"
  auth -> app "redirect ?code" dashed
  app -> auth "token (code + verifier)"
  auth -> app "access_token" dashed
  app -> api "GET /me (Bearer)"
  1. 참여자는 보기 블록에 적은 순서대로 왼쪽부터 놓인다.
  2. 메시지는 장면에 적은 순서대로 위에서 아래로 놓인다. 문서의 모든 장면이 한 열에 놓이고, 탭은 어느 장면을 재생할지 고른다.
  3. 재생하면 박자마다 그 메시지 화살표가 밝아지고 점이 지난다.

한 문서의 여러 보기

Plain text
thinkflow
title "주문과 스키마"

person user "고객"
api create "POST /orders" {
  body "OrderRequest"
}
table orders "orders" {
  id bigint pk
  status text required
}

user -> create.body "주문"
create.body -> orders.id "저장"

view graph down "구조"
view sequence "호출 순서" {
  user create
}

scene "주문 한 건"
  user -> create.body "주문 요청" time=700ms
  create.body -> orders.id time=700ms
  1. 첫 이동은 그래프의 선 하나와 순서 보기의 메시지 하나로 함께 보인다. 시간은 먼저 놓인 투영이 한 번 정한다.
  2. 순서 보기는 API를 필드 없이 카드 머리(아이콘과 이름)로만 보이고, 필드는 같은 카드를 담은 그래프 보기가 보인다. 두 번째 이동은 순서 보기에 orders가 없어 그래프에서만 보인다.

상세 설계

순서 보기

줄 뜻
view sequence ["라벨"] { 참여자 이름들 } 순서 보기. 적은 순서가 왼쪽부터의 순서
a -> b "메시지" [dashed] [time=2s] 메시지 하나. 한 줄이 한 박자이고 한 행이다. dashed는 응답
a -> a "메시지" 자기 자신에게 보내는 메시지. 오른쪽으로 돌아 나오는 고리
a -> b "메시지" create, destroy 받는 참여자의 생성 또는 소멸
activate id, deactivate id 바로 앞 메시지 행에서 활성 구간을 시작하거나 끝낸다. 중첩은 나중에 연 구간부터 닫는다
note id "글" 바로 앞 메시지 행의 참여자 옆 메모
wait 2s 멈추는 박자
  • 참여자가 되는 카드는 person, box, external, store, queue, table, api, 클래스다. 상태, 갈림길, 격자, 차트, 추적은 참여자 모양이 없어 오류다.
  • 메시지는 장면 안에만 쓴다. 한 장면의 메시지는 그 장면의 첫 행부터 놓이고 다른 장면의 메시지는 그 장면에서 보이지 않는다(정지 장면과 마지막 모습도 같다). 메시지는 선이 아니라 행이다. 그래서 같은 두 참여자 사이 같은 방향 메시지를 여러 번 쓸 수 있고 a -> a도 쓸 수 있다. 메시지가 있는 박자에는 이동이 하나뿐이다(& 불가).
  • 메시지 글은 필수다. 화살표 위 라벨로 늘 보인다. 점에는 글 상자를 붙이지 않는다. 같은 글이 두 번 보이기 때문이다.
  • note는 메시지 줄 바로 다음 줄에만 쓰고, 그 메시지의 보내는 쪽이나 받는 쪽 참여자만 가리킨다. 박자를 만들지 않는다. 자기 자신에게 보내는 메시지 행이면 메모는 고리 반대편에 놓인다.
  • 참여자 순서가 처음 메시지를 보내는 순서와 다르면 경고다(declare participants in the order they first send: a, b). --strict에서는 오류가 된다. 경고는 순서가 처음 어긋난 참여자를 적은 보기 블록의 줄과 자리에 붙는다. 메시지를 보내지 않는 참여자는 보내는 참여자 뒤에 오면 된다.
  • 순서 보기 전용 줄(note, activate, deactivate, fragment, 이동의 dashed, create, destroy)은 그 카드가 어떤 순서 보기에든 놓여야 한다.
  • 순서 보기는 참여자 머리(아이콘과 이름)만 그린다. 카드 줄과 값 줄이 들어갈 자리가 없어서, 순서 보기에만 놓인 카드에 값 on=, show, clear를 쓰면 줄 번호가 붙은 오류다. 같은 카드를 그래프 보기에도 놓으면 그래프 보기가 그 카드와 값을 그리고, 순서 보기에는 머리만 보인다. 상태 알약(status)은 머리에 그려져 이 제한이 없다.

생성 메시지는 점선이며 참여자 머리에서 끝난다. 생명선은 그 머리 아래에서 시작한다. 소멸 메시지는 X 표식에서 끝나며 열린 활성 구간도 닫는다. 생성 전과 소멸 후 메시지, 같은 장면 안의 중복 생성, 짝 없는 활성 종료는 오류다. 생명주기와 활성 구간은 장면마다 따로 검사한다. 장면은 서로 이어지지 않는 대안이라 한 장면의 생성, 소멸, 열린 활성 구간이 다른 장면의 처음 상태에 영향을 주지 않는다. 어떤 장면에서 생성 메시지를 받는 참여자는 그 장면에서만 그 메시지 전까지 없고 다른 장면에서는 처음부터 있다. 같은 참여자를 둘 이상의 장면이 만들어도 된다. 활성 구간은 장면 끝에서 모두 닫혀야 한다. 메시지와 같은 행에서 여닫는 구간은 짧은 막대로 표시한다. 전체 호출 구조를 표시하며 재생 도중 참여자를 숨기는 애니메이션은 아니다.

시퀀스 제어 구획

장면 안에 fragment 종류 "이름" ... {를 쓴다. 종류는 alt, loop, par, opt다. alt와 par 안에는 branch "이름" { 대안을 둘 이상 적는다. loop와 opt 안에는 메시지를 바로 적는다. 구획의 대안이나 반복 본문 안에 다른 구획을 중첩할 수 있다. 구획 안의 메시지는 when=, wait=, reserve=를 쓸 수 없다(중첩한 구획도 같다). 반복과 병렬 대안의 출발 시각이 고정이라 조건과 예약이 처리할 시각이 없어서 그 줄의 오류다. 구획 밖의 메시지는 그대로 쓴다.

종류 필수 선택 사항 그림과 재생
alt choose="대안 이름" 모든 대안을 그리되 이름이 일치하는 대안만 재생한다. 선택한 대안을 제목에 표시한다
opt run=on 또는 run=off 본문을 그리되 on이면 한 번 재생하고 off이면 메시지와 대기를 모두 생략한다. 제목에 재생·생략을 표시한다
loop times=양의정수 본문을 한 번 그린다. 같은 메시지 선과 점을 지정한 횟수만큼 실제로 반복한다
par 없음 대안들이 동시에 시작해 각자 메시지 순서와 대기를 진행한다. 다음 메시지는 가장 늦은 대안이 끝난 뒤 시작한다

구획 안에는 메시지, 바로 앞 메시지에 붙인 note, wait, 중첩 구획을 쓴다. 생성·소멸 메시지와 activate·deactivate는 구획 밖에 쓴다. 선택하지 않은 대안과 생략한 opt 본문도 이름, 메시지, 메모, 중첩 구조를 모두 검증한다.

구획은 카드가 보여 주는 내용(show, clear, 값 줄)을 바꾸지 않는다(#179). 카드 내용은 구획을 접기 전의 박자 계획을 따라가므로 구획 앞, 사이, 뒤의 show와 값이 구획 박자에서도 그 자리의 내용으로 보이고, 구획으로 시작하는 장면, 중첩 구획, 건너뛴(run=off) 구획, 길이가 다른 대안, 구획 다음 장면도 같다. 건너뛴 구획뿐인 장면은 길이 0의 빈 박자 하나의 정지 모습을 갖는다.

choose와 opt의 run은 예제의 경로를 고정하는 선언이다. 조건식을 평가하거나 실제 서비스의 상태를 읽지 않는다. 반복은 종료 조건을 계산하지 않고 정해진 횟수를 실행한다. break, critical, ref와 표준의 모든 제어 연산자를 지원한다고 표시하지 않는다.

메시지마다 이동 시간과 기본 읽기 시간을 더하고, wait에는 선언한 대기 시간과 기본 읽기 시간을 더한다. 병렬 대안의 두 번째 메시지는 같은 번호의 다른 대안 메시지를 기다리지 않는다. 재생기는 구획을 해석하지 않고 생성 시 펼쳐 둔 출발 시각을 읽는다. 펼치기 전에 events 예산으로 메시지 출발·도착을 둘, 독립된 대기를 하나로 센다. 선택된 대안만 반복 횟수를 곱하고, 생략된 opt 본문은 실행 사건으로 세지 않는다. 기본 예산 300,000을 넘으면 출력 전에 오류를 낸다. 구획과 대안의 중첩 깊이는 64까지다. 네 구획과 생명주기를 모두 쓰는 원본은 sequence 예제다.

상태

줄 뜻
state id "이름" 상태
start id 처음 상태. 선언된 상태 하나를 가리키고, 채운 점에서 그 상태로 가는 선을 그린다. 없어도 된다
final id 끝 상태. 선언된 상태를 가리키고, 그 상태에서 겹원으로 가는 선을 그린다. 여럿 가능
a -> b "사건" [dashed] [quiet] 전이. 사건 글은 필수
group id "이름" [direction=right|down] {, } 상태 그룹
  • 자기 전이 a -> a "사건"는 상태 위에 고리로 그린다. 재시도와 대기처럼 같은 상태에 머무는 사건을 보이기 위해서다.
  • start는 없거나 하나다. 없으면 채운 점과 그 선을 그리지 않는다. 둘 이상이면 오류다. 같은 상태를 final로 두 번 적으면 오류다.
  • 채운 점과 겹원으로 가는 선은 이동 대상이 아니다.
  • 전이의 양 끝은 상태다. 그룹을 끝으로 쓰면 오류다.
  • 같은 두 상태 사이 같은 방향 전이는 하나다. 사건이 둘이면 "시작, 재개"처럼 한 글에 적는다.
  • 상태에는 show, clear, status를 쓸 수 없다. 이동은 전이를 따라간다.
  • 전이에 quiet를 쓰면 그 선을 처음 지나는 박자부터 그 장면 끝까지만 보이고, 한 번도 지나지 않으면 그림 검사 11번 경고가 난다. 다만 되돌아가는 전이가 배치를 크게 바꿀 수 있어 state 예제는 쓰지 않는다.

스키마

줄 뜻
table id "이름" {, } 테이블. 두 줄 사이에 열을 둔다
열 타입 [pk] [unique] [fk=테이블.열] [nullable 또는 required] [ondelete=정책] [from="다중성"] [to="다중성"] 테이블 안 열 하나. 선언 순서가 위에서 아래 순서
pk (열, ...), unique (열, ...) 테이블의 단일 또는 복합 기본 키·고유 키
fk (열, ...) -> 테이블 (열, ...) [ondelete=정책] [from="다중성"] [to="다중성"] 같은 순서의 열끼리 대응하는 외래 키 하나
light 테이블.열 테이블 열이나 테이블 전체를 밝힌다
show 테이블 "글" 테이블 아래 카드에 예시 행을 보인다
  • 테이블 이름은 영어 소문자로 시작하고 영어 소문자, 숫자, _만 쓴다(-는 쓰지 않는다). 열 이름은 영문자로 시작하고 영문자(대소문자), 숫자, _를 쓴다(userId, createdAt). 실제 데이터베이스의 이름을 그대로 적기 위해서다.
  • 타입은 영문자, 숫자, _만 든 낱말 하나(bigint)이거나, 그 밖의 기호나 공백이 있으면 따옴표 글("numeric(10, 2)")이다. varchar(255)처럼 따옴표 없이 기호를 쓰면 오류다.
  • 열 옆의 키 표기는 테이블 단위의 키 선언으로 합친다. 여러 열의 pk는 복합 기본 키 하나이며 별도 pk (...)와 함께 쓸 수 없다. 복합 unique는 열 조합의 고유성을 뜻한다. 중복 키, 같은 키 안의 중복 열, 존재하지 않는 열은 오류다.
  • 외래 키 선은 fk= 또는 fk (...)에서 생기며 기본 키나 고유 키의 전체 열을 가리켜야 한다. 양쪽 열 개수는 같고 순서대로 대응한다. orders -> users나 orders.user_id -> users.name처럼 직접 적은 선은 설명을 위한 연결이고 외래 키 제약을 만들지 않는다. 외래 키와 같은 열 쌍을 다시 연결하면 중복 오류다. 같은 테이블의 서로 다른 열을 잇는 자기 참조 외래 키는 카드 오른쪽을 도는 고리로 그린다. 같은 열 쌍을 자기 자신에 잇거나 한쪽만 열인 자기 선은 오류다.
  • 두 테이블을 잇는 선과 fk= 열에 from="0..*", to="1"로 각 끝의 다중성을 적는다. 클래스와 같은 음이 아닌 정수, *, 순서가 맞는 범위를 쓰며, 생략한 값은 추정하지 않는다. 직접 연결의 relation=association은 화살촉 없는 연관이고 테이블에는 다른 클래스 관계 종류를 쓰지 않는다.
  • nullable과 required는 각각 NULL 허용과 금지를 명시한다. 생략하면 표시하지 않는다. pk nullable과 nullable required는 오류다.
  • ondelete=restrict|cascade|set-null|no-action은 외래 키의 삭제 정책을 표시한다. 외래 키가 없는 열에는 쓸 수 없다. set-null은 참조하는 모든 열에 nullable이 있어야 한다. 타입은 표시용 텍스트다. 이 표기는 데이터베이스를 실행하거나 변경하지 않는다.
  • 한 열에 지정한 PK, FK, UNQ 표식을 모두 보인다. 명시한 NULL 또는 NOT NULL과 삭제 정책은 같은 열의 아래 보조 줄에 보인다.
  • 이동 payments -> orders는 적은 방향의 외래 키를 먼저 찾고, 없으면 반대 방향의 외래 키를 거꾸로 따라간다. 고른 방향의 외래 키가 둘 이상이면 payments.order_id -> orders.id처럼 열까지 적어야 한다.
  • 복합 키의 묶음 표식과 작성 예제는 데이터 관계 그림에 있다. 단일·복합 외래 키 모두 선 하나와 공통 이동·강조·다중성 표시를 사용한다.

클래스

class id "이름" [abstract] {와 interface id "이름" { 안에 field 이름 "타입", method 이름 "(인자): 반환 타입"을 적는다. 이름 구획, 속성 구획, 메서드 구획을 선으로 나눈다. 비어 있는 클래스도 허용한다. 제네릭과 언어별 타입은 따옴표 안에 원문으로 보존하며 특정 언어의 타입 검사나 코드 생성은 하지 않는다.

멤버의 visibility=public|private|protected|package는 각각 +, -, #, ~로 표시한다. 생략하면 가시성을 추정하지 않는다. static 멤버는 밑줄로 표시한다. 클래스와 메서드의 abstract는 {abstract}로 표시한다. 멤버 이름은 영문자로 시작하고 영문자, 숫자, _를 쓰며 클래스 안에서 중복될 수 없다. 인터페이스는 이름 위에 «interface»를 표시한다. 클래스 멤버는 연결점이 아니라서 선은 클래스 카드에 닿는다.

관계는 a -> b relation=종류 "설명"으로 선언한다. 설명과 relation=은 생략할 수 있으며 기본 관계는 연관이다. 상속은 같은 종류의 두 분류자를 잇는다. 구현은 클래스에서 인터페이스로 향한다. 순환 상속은 오류다. 자기 연관과 자기 합성은 재귀 구조를 나타내는 고리로 그린다. 그룹은 분류자를 묶는 경계이며 관계의 끝으로 쓸 수 없다.

관계 값 표시 방향
association 실선, 화살촉 없음 선언한 두 분류자
dependency 점선, 열린 화살촉 의존하는 쪽에서 제공하는 쪽
inheritance 실선, 빈 삼각형 하위에서 상위
realization 점선, 빈 삼각형 구현 클래스에서 인터페이스
aggregation 실선, 시작에 빈 마름모 전체에서 부분
composition 실선, 시작에 채운 마름모 전체에서 부분

연관·집합·합성 관계의 from="1", to="0..*"는 각각 출발과 도착 분류자 쪽에 다중성을 표시한다. 값은 음이 아닌 정수, *, 순서가 맞는 범위(0..1, 1..*)다. 생략한 다중성을 추정하지 않는다. 합성에서 부분 하나의 전체는 최대 하나이므로 from의 상한은 1을 넘을 수 없다. 상속·구현·의존에는 다중성을 붙이지 않는다. 관계 기호는 OMG UML 2.5.1의 분류자·의존·연관 표기를 기준으로 한다(2026-10-08 확인). 연관 클래스, 포트와 UML 메타모델 전체 검증은 이 계약에 포함되지 않는다. 여섯 관계와 다중성을 모두 쓰는 원본은 class 예제다.

카드와 보기 사이 규칙

카드 graph sequence plot time
person, box, external, store 구성원 참여자 오류 오류
queue 구성원 참여자 오류 오류
decision, state, grid 구성원 오류 오류 오류
table, api, class, interface 구성원 참여자(카드 머리만) 오류 오류
group 구성원 오류 오류 오류
chart 구성원(좁은 폭) 오류 정확히 하나 오류
trace 오류 오류 오류 정확히 하나
  • 표의 오류는 그 보기의 구성원 이름 자리에서 a chart card cannot be a sequence participant 같은 줄 번호가 붙은 오류라는 뜻이다. 문서 모양 규칙은 그림 문법가 정한다.
  • 줄 종류와 카드는 문서 하나에 섞을 수 있다. 보기에 적지 않은 카드는 기본 보기를 받는다(추적은 시간 보기, 남은 카드가 차트뿐이고 명시한 그래프 보기와 그룹이 없으면 차트 보기, 나머지는 그래프). 순서 보기에 적은 카드는 적힌 카드라 기본 그래프에 들어가지 않는다. 그 규칙은 그림 문법에 있다.
  • 차트의 줄은 차트에 있다.

요구사항

요구사항 검증 계획
클래스의 세 구획, 멤버 가시성·정적·추상 표시와 여섯 관계 기호와 양끝 다중성을 보인다. test/kinds.test.js(K-class), class 예제는 test/examples.test.js
클래스 관계의 끝 종류, 멤버 중복, 순환 상속, 닫지 않은 구획을 거부한다. test/kinds.test.js(K-class relations)
명시한 키, NULL 허용 여부와 삭제 정책을 모두 표시하고 서로 모순된 제약을 거부한다. 외래 키는 test/cards.test.js, schema 예제는 test/examples.test.js. NULL 허용과 삭제 정책의 표시와 모순 거부를 직접 확인하는 시험은 확인되지 않았다(검증 요구사항, 미완료)
순서 보기의 메시지, 구획, 생명주기와 참여자 순서 규칙을 줄 번호와 함께 알린다. 순서 보기에만 놓인 카드의 값 on=, show, clear는 줄 번호가 붙은 오류다. test/kinds.test.js(K-sequence), test/cards.test.js(순서 보기 전용 줄, 순서 보기에만 있는 카드, 참여자 순서)
예제 원본과 문서의 ```thinkflow 블록이 오류와 경고 없이 읽힌다. test/examples.test.js, ```thinkflow 블록은 thinkflow md --check. 설계 문서의 ```text 예시는 자동 시험이 읽지 않는다(검증 요구사항, 미완료)
순서 그림 메시지는 적은 순서대로 위에서 아래로 놓인다. test/kinds.test.js(K-sequence)
구획은 카드 내용을 바꾸지 않는다. 구획 앞, 사이, 뒤의 show와 값 줄이 남고, 구획으로 시작하는 장면과 건너뛴 구획뿐인 장면도 멈춘·움직이는 SVG와 HTML로 만들어진다(#179). test/kinds.test.js(K-sequence #179). 재생기가 구획 박자에서 카드를 칠하는 모습은 검증 요구사항, 미완료
외래 키 선은 두 열의 행 높이에 붙는다. test/cards.test.js
같은 테이블의 서로 다른 두 열을 잇는 자기 참조 외래 키가 열 줄에 붙는 고리로 그려지고, 같은 열이나 카드 전체를 잇는 자기 선은 오류다. examples/schema.thinkflow의 빌드(test/examples.test.js)와 test/cards.test.js(S4). 고리 경로의 기하를 직접 재는 시험은 아직 확인되지 않았다(검증 요구사항, 미완료)

단점

  • 순서 보기에서 병렬 메시지(&)를 쓸 수 없다. 동시에 일어나는 메시지도 한 행씩 적거나 par 구획을 쓴다.
  • 자기 참조 외래 키의 고리는 카드 오른쪽 면에 붙으므로 같은 열을 가리키는 다른 외래 키의 화살촉과 가까이 놓일 수 있다.

미해결 질문

  • SEQ-1: 조건·반복·병렬 구획을 메시지 재생과 어떻게 연결할 것인가.