UML의 현행판인 2.5.1은 다이어그램을 열네 종류 정의한다. 이 글의 목록과 이름, 문법은 모두 2.x 기준이다 — 1.x와는 구성이 다르다. 그런데 실제 설계 문서나 PR에서 만나는 것은 손에 꼽는다. 무엇이 사라졌고, 남은 것은 무엇을 묻는 도구인지 정리한다.

예제는 전부 mermaid다. 이 사이트가 불러오는 11.4.0에서 파싱을 확인하고 실었으니 그대로 복사해 쓰면 된다.

어디서 왔고 어디서 멈췄나

1990년대 초에는 객체지향 설계 방법론이 난립했다. Booch, Rumbaugh의 OMT, Jacobson의 OOSE가 각자 표기법을 들고 경쟁했고, 같은 모양의 그림이 진영마다 다른 뜻이었다. 그 셋이 Rational에 모여 표기법을 합친 것이 UML이다. 1994–95년에 시작해 1996년 UML Partners 컨소시엄(HP·DEC·IBM·마이크로소프트 등)으로 커졌고, 1997년 11월 OMG 표준이 됐다. 애초 목적이 새 방법론이 아니라 표기법 통일이었다는 점이 이 글 내내 중요하다.

1.x 시절(1.3–1.5)은 부분 개정이었고, 명세가 모호하고 일관성이 없다는 비판을 받았다. 그 비판에 답한 것이 2005년의 2.0이다. 메타모델을 재정비한 대규모 개정이었고, 지금 말하는 열넷도 이 2.x 체계다. 이후 2.2–2.4.1은 수정 위주였고, 2.5(2015)는 기능이 아니라 명세 자체를 정리한 판이다 — Superstructure와 Infrastructure로 갈라져 있던 문서를 하나로 합쳤다.

현행판은 2017년 12월의 2.5.1이고, 그 뒤로 새 버전이 없다. OMG 명세 페이지에 다음 버전 계획도 없다. 대신 OMG의 무게는 2025년 9월 채택된 SysML 2.0으로 옮겨 갔는데, UML 프로파일로 얹던 v1과 달리 KerML이라는 새 메타모델 위에 다시 지은 것이다. UML 3.0을 기다릴 일은 없다는 뜻이다.

UML 2.x가 정의한 열넷

갈래다이어그램무엇을 그리나
구조Class클래스와 관계(상속·합성·연관)
구조Object특정 시점의 인스턴스 스냅샷
구조Package패키지·네임스페이스의 의존 방향
구조Component배포 단위 모듈과 제공/요구 인터페이스
구조Composite Structure클래스 내부의 파트와 포트
구조Deployment노드(장비)와 거기 올라가는 산출물
구조ProfileUML 자체를 확장하는 스테레오타입 정의
행위Use Case액터와 시스템이 제공하는 기능 단위
행위Activity작업의 흐름, 분기와 병렬
행위State Machine객체의 상태와 전이 규칙
행위Sequence시간 순서에 따른 메시지 교환
행위Communication같은 상호작용을 객체 배치 중심으로
행위Interaction Overview상호작용 조각들을 흐름도로 엮은 것
행위Timing시간 축 위의 상태 변화

열네 이름과 구조·행위 두 갈래 분류는 명세 2.5.1 원문(부록 A)에서 확인한 것이다. Communication은 UML 1.x의 Collaboration이 이름만 바뀐 것이다.

사라진 것들

아래는 내가 최근 몇 년간 설계 문서나 PR에서 실물을 본 적 없는 것들이다.

다이어그램왜 안 쓰나
Object특정 순간의 스냅샷이라 그 순간이 지나면 틀린 그림이 된다
Composite Structure클래스 내부 배치를 그리는데, 내부는 리팩터링마다 바뀐다
ProfileUML 도구로 모델링하던 시절의 확장 장치다. 도구를 안 쓰면 쓸 일이 없다
Communication시퀀스와 같은 정보를 담는데 순서를 번호(1.1, 1.2)로 따라가야 한다
Interaction Overview시퀀스 조각을 흐름도로 엮는 메타 다이어그램. 한 장에 담길 만큼 단순한 적이 없다
Timing임베디드·프로토콜에는 여전히 맞지만 텍스트로 그릴 도구가 마땅치 않다
Use Case타원과 막대 인간 그림보다 이슈 트래커의 티켓 목록이 이겼다

Component와 Deployment는 기호만 죽고 관점은 남았다. 막대사탕·소켓 기호와 3D 큐브는 안 쓰지만, “모듈이 어떤 인터페이스로 붙나”, “어디에 배포되고 무엇이 경계를 넘나”는 여전히 그린다. 다만 C4 식 블록과 화살표, 클라우드 아이콘으로 그린다.

여기 나오는 C4는 기호가 아니라 축척이다. Simon Brown이 정리한 방식으로, 같은 시스템을 네 배율로 그린다 — 시스템 맥락(바깥 사용자·외부 시스템과의 관계), 컨테이너(앱·서비스·DB 같은 실행 단위), 컴포넌트(한 컨테이너 안의 모듈), 코드. 실제로는 위 두 단계만 그리고 멈추는 경우가 많다. 표기법을 정하지 않는다는 점이 UML과 결정적으로 다르다 — 블록과 화살표에 “무엇이고 무엇으로 통신하는지” 라벨만 달면 된다. 배울 기호가 없으니 그림 앞에서 문법을 설명할 일도 없다.

사라진 쪽에는 공통점이 있다. 갱신 비용이 정보 가치보다 크거나(Object, Composite Structure), 문법부터 설명해야 하거나(Component의 볼-앤-소켓, Timing), 전제가 된 세상이 바뀌었다(Deployment의 고정된 장비 대수).

그리고 결정적인 축이 하나 더 있다. 텍스트로 못 쓰는 다이어그램은 리뷰를 통과하지 못한다. 이미지 파일은 PR에서 무엇이 바뀌었는지 볼 수 없어 저장소 밖 위키에서 썩는다. 살아남은 목록이 mermaid·PlantUML이 지원하는 목록과 겹치는 것은 우연이 아니다. LLM에 붙여 넣고 돌려받는 형식도 텍스트라 이 경향은 더 굳었다. AI가 다이어그램을 없앤 것이 아니라, 이미 기울어 있던 축을 한 번 더 눌렀다.

남아 있는 것들

다이어그램답하는 질문요즘 형태
시퀀스호출이 어떤 순서로 오가나mermaid·PlantUML 그대로
상태 머신이 상태에서 그 이벤트가 와도 되나그림 또는 상태 전이 표
클래스핵심 타입과 관계가 어떻게 되나반쯤 남았다 — 설명용이거나 자동 생성
ERD테이블 관계와 카디널리티가 어떻게 되나mermaid ERD, DBML
활동흐름의 분기와 병렬은사실상 순서도
패키지·컴포넌트의존 방향이 맞나, 순환은 없나의존성 그래프, C4 블록
배치어디에 뜨고 무엇이 경계를 넘나클라우드 아키텍처 그림, IaC

샘플

시퀀스

컴포넌트가 둘 이상이고 왕복이 있으면 값이 있다. 장애 회고에서 “이 시점에 토큰이 이미 만료였다”를 짚을 때, 글로 세 문단인 내용이 그림 하나로 끝난다. 참여자가 예닐곱을 넘으면 가로로 늘어지니 그때는 두 장으로 자른다.

그리려는 것 — 로그인 한 번이 클라이언트에서 출발해 게이트웨이와 인증 서비스, 세션 저장소를 거쳐 토큰으로 돌아오는 왕복. 누가 누구를 아는지가 아니라 어느 순서로 부르는지가 요점이다.

sequenceDiagram
    participant C as 클라이언트
    participant G as API 게이트웨이
    participant A as 인증 서비스
    participant R as 세션 저장소
    C->>G: POST /login
    G->>A: 자격 증명 검증
    A->>R: 세션 기록
    R-->>A: OK
    A-->>G: 액세스 토큰
    G-->>C: 200 + 토큰

위 그림을 그린 코드다.

```mermaid
sequenceDiagram
    participant C as 클라이언트
    participant G as API 게이트웨이
    participant A as 인증 서비스
    participant R as 세션 저장소
    C->>G: POST /login
    G->>A: 자격 증명 검증
    A->>R: 세션 기록
    R-->>A: OK
    A-->>G: 액세스 토큰
    G-->>C: 200 + 토큰
```

상태 머신

세션·주문·연결처럼 잘못된 전이가 곧 사고인 대상에 쓴다. TCP 연결의 상태 머신과 같은 종류다.

그리려는 것 — 접속한 클라이언트 하나의 생애. 연결과 인증을 지나 로비·방·게임 사이를 오가고, 어느 지점에 있든 연결이 끊기면 끝난다. 각 상태에서 무엇이 허용되는지가 요점이다.

stateDiagram-v2
    [*] --> Connecting
    Connecting --> Handshake : TCP 연결
    Handshake --> [*] : 인증 실패
    Handshake --> Active : 인증 성공
    state Active {
        [*] --> InLobby
        InLobby --> InRoom : 방 입장
        InRoom --> InGame : 게임 시작
        InGame --> InLobby : 게임 종료
    }
    Active --> [*] : 연결 끊김

위 그림을 그린 코드다.

```mermaid
stateDiagram-v2
    [*] --> Connecting
    Connecting --> Handshake : TCP 연결
    Handshake --> [*] : 인증 실패
    Handshake --> Active : 인증 성공
    state Active {
        [*] --> InLobby
        InLobby --> InRoom : 방 입장
        InRoom --> InGame : 게임 시작
        InGame --> InLobby : 게임 종료
    }
    Active --> [*] : 연결 끊김
```

Active로 묶은 것이 요점이다. 로비·방·게임 각각에서 “연결 끊김” 화살표를 빼면 셋인데 묶으면 하나다. 거미줄이 되는 주된 이유는 상태 수가 아니라 어디서든 올 수 있는 공통 이벤트(취소, 타임아웃, 연결 끊김)다. 그것부터 상위 상태로 올린다.

그래도 전이가 스무 개를 넘으면 그림을 포기하고 표로 간다. 표는 한 줄이 switch의 한 case나 전이 맵의 한 항목과 1:1로 붙고, 빈 칸이 곧 검토 안 된 조합이라 누락이 드러난다.

마지막 열의 가드(guard) 는 이벤트가 와도 그것만으로는 부족하고 추가로 참이어야 전이가 일어나는 조건이다. 정원이 찬 방에 입장 요청이 오면 이벤트는 왔지만 전이는 없다. 같은 칸에 적은 부수 효과는 전이하면서 함께 하는 일이다 — 퇴장을 남들에게 알리거나 세션 자원을 정리하는 것.

현재 상태이벤트다음 상태가드 / 부수 효과
InLobbyJoinRoomReqInRoom방 인원 < 정원
InRoomLeaveRoomReqInLobby퇴장 브로드캐스트
InRoomStartGameReqInGame방장만 허용
아무 상태DisconnectTerminating세션 리소스 정리

클래스 — 남았다고 말하기 애매한 쪽

남은 것들 중 가장 약하다. 코드가 이미 텍스트이고 IDE가 타입을 따라가 주기 때문에, 손으로 그린 클래스 그림은 대개 코드의 중복이다. 멤버를 충실히 적을수록 코드보다 정보가 적으면서 갱신 부담만 남는다.

그래서 남는 자리는 둘뿐이다. 설명용(구조를 남에게 보여 줄 때)과 자동 생성(도구가 뽑아 주는, 한 번 읽고 버리는 그림).

이 사이트가 그 증거다. 디자인 패턴 15편은 전부 클래스 사이의 관계를 다루는데 그중 클래스 다이어그램으로 그린 편은 하나도 없다 — 열다섯 편 모두 블록과 화살표(graph)를 썼다. 그리는 쪽이 편하고 보는 쪽도 배울 기호가 없다.

굳이 그린다면 핵심 타입 서넛과 관계만 남긴다.

그리려는 것 — 세션과 방이라는 두 타입, 그리고 “세션이 방에 들어간다”는 관계 하나. 멤버는 대표적인 것만 남겼다.

classDiagram
    class Session {
        +SessionId id
        +State state
        +handle(Packet p)
    }
    class Room {
        +RoomId id
        +broadcast(Packet p)
    }
    Session --> Room : joins

위 그림을 그린 코드다.

```mermaid
classDiagram
    class Session {
        +SessionId id
        +State state
        +handle(Packet p)
    }
    class Room {
        +RoomId id
        +broadcast(Packet p)
    }
    Session --> Room : joins
```

ERD

관계의 개수 규칙을 적는 그림이다 — 한쪽 하나에 다른 쪽이 몇 개까지 붙을 수 있고, 0개가 허용되는가. 흔히 카디널리티(cardinality)라 부른다. 이 규칙은 코드를 읽어서는 잘 안 보인다. 코드에는 List<OrderItem> items 한 줄로 나타날 뿐, 그 리스트가 비어도 되는지는 타입에 적혀 있지 않다. 어긋나면 품목 없는 주문이 저장되는 식의 버그가 된다. 스키마를 바꾸기 전과 새 사람에게 도메인을 설명할 때 쓴다.

그리려는 것 — 회원 한 명은 주문을 0개 이상 가질 수 있고, 주문 하나에는 품목이 1개 이상 들어간다는 관계.

상품(ITEM)과 주문은 여러 대 여러라 직접 잇지 못한다. 그래서 ORDER_ITEM이 둘 사이에 끼어 각각 “하나 대 여럿”으로 풀어 준다 — 교차 테이블이라 부르는 그것이다.

까마귀 발(crow’s foot) 표기를 쓴다. ||가 정확히 하나, o가 0(선택), {가 여럿이다. 그래서 ||--o{는 “하나 대 0개 이상”, ||--|{는 “하나 대 1개 이상”으로 읽는다.

erDiagram
    USER ||--o{ ORDER : places
    ORDER ||--|{ ORDER_ITEM : contains
    ITEM ||--o{ ORDER_ITEM : "appears in"

위 그림을 그린 코드다.

```mermaid
erDiagram
    USER ||--o{ ORDER : places
    ORDER ||--|{ ORDER_ITEM : contains
    ITEM ||--o{ ORDER_ITEM : "appears in"
```

활동 — 사실상 순서도

UML이 순서도에 더한 것은 셋이다. 흐름을 여러 갈래로 동시에 쪼갰다가(fork) 모두 끝날 때까지 기다려 다시 합치는 것(join), 수영장 레인처럼 구역을 나눠 어느 주체가 그 일을 하는지 표시하는 것(swimlane), 그리고 제어뿐 아니라 데이터가 어떤 상태로 넘어가는지 함께 적는 것이다. 실무에서는 mermaid flowchart로 충분하고, 담당이 갈리면 subgraph로 묶어 레인을 대신한다. 한 함수 안의 로직을 이걸로 그리면 코드를 두 번 쓰는 셈이니 여러 주체가 얽힐 때만 그린다.

그리려는 것 — 주문 한 건이 접수된 뒤 재고 확인에서 갈라지고, 통과하면 결제를 거쳐 완료까지 가는 절차. 분기와 그 결과가 요점이다.

flowchart TD
    A[주문 요청 수신] --> B{재고 확인}
    B -- 충분 --> C[재고 차감]
    B -- 부족 --> D[주문 취소 알림]
    C --> E[결제 승인 요청]
    E --> F[주문 완료]

위 그림을 그린 코드다.

```mermaid
flowchart TD
    A[주문 요청 수신] --> B{재고 확인}
    B -- 충분 --> C[재고 차감]
    B -- 부족 --> D[주문 취소 알림]
    C --> E[결제 승인 요청]
    E --> F[주문 완료]
```

패키지·컴포넌트

모듈 경계와 의존 방향은 클래스가 수백 번 바뀌어도 그대로라 가장 오래 사는 그림이다. 모놀리스를 쪼갤 때, 순환 참조를 잡을 때 쓴다.

이 그림이 말하는 것은 화살표의 방향 하나다. 계층을 위에서 아래로 놓고 의존은 아래로만 흐르게 한다. 붉은 점선은 실제 의존이 아니라 생기면 안 되는 방향이다 — 인프라가 도메인을 직접 부르는 순간 둘이 서로를 참조하게 되고(순환), 그때부터 따로 빌드할 수도 따로 테스트할 수도 없다.

flowchart TD
    subgraph L1[표현 계층]
        api["api — HTTP 핸들러"]
    end
    subgraph L2[도메인 계층]
        order["order — 주문 규칙"]
        user["user — 회원 규칙"]
    end
    subgraph L3[인프라 계층]
        db["db — 영속화"]
        mq["mq — 메시지 발행"]
    end
    api --> order
    api --> user
    order --> db
    order --> mq
    user --> db
    db -- "이 화살표가 생기면 위반" --> order
    linkStyle 5 stroke:#d33,stroke-width:2px,stroke-dasharray:6 4

위 그림을 그린 코드다.

```mermaid
flowchart TD
    subgraph L1[표현 계층]
        api["api — HTTP 핸들러"]
    end
    subgraph L2[도메인 계층]
        order["order — 주문 규칙"]
        user["user — 회원 규칙"]
    end
    subgraph L3[인프라 계층]
        db["db — 영속화"]
        mq["mq — 메시지 발행"]
    end
    api --> order
    api --> user
    order --> db
    order --> mq
    user --> db
    db -- "이 화살표가 생기면 위반" --> order
    linkStyle 5 stroke:#d33,stroke-width:2px,stroke-dasharray:6 4
```

linkStyle 5의 5는 위에서부터 0으로 세는 화살표 순번이다. 중간에 화살표를 하나 끼워 넣으면 번호가 밀려 엉뚱한 선이 빨개진다.

허용된 화살표만 그린 그림보다 금지선을 같이 그린 그림이 리뷰에서 쓸모 있다. 합의한 규칙이 그림에 남기 때문이다.

다만 손으로 그린 그림은 실제 빌드와 어긋날 수 있다. 대조는 빌드 도구가 대신해 준다. CMake는 의존성 그래프를 Graphviz 형식으로 뽑는다.

cmake -S . -B build --graphviz=build/deps.dot

작은 예제 프로젝트에 돌려 나온 결과다. 실행 파일 둘이 같은 정적 라이브러리에 붙어 있다.

"node0" [ label = "test_count", shape = egg ];
"node1" [ label = "textstat_lib", shape = octagon ];
"node0" -> "node1"
"node2" [ label = "textstat", shape = egg ];
"node2" -> "node1"

Gradle의 dependencies, npm의 npm ls도 같은 자리를 메운다. 그림과 이 그래프가 어긋나면 둘 중 하나가 틀린 것이고, 대개 틀린 쪽은 그림이다.

배치

단일 장애점, 가용 영역 간 지연, 퍼블릭과 프라이빗의 경계는 논리 설계만 봐서는 안 보인다. 이건 UML로 그리지 않는다 — 클라우드 벤더 아이콘 세트나 IaC 코드가 이미 그 자리를 차지했다. 다만 기호 없이도 경계와 중복은 그려진다.

그리려는 것 — 바깥에서 들어오는 길이 퍼블릭 서브넷의 로드 밸런서 하나뿐이고, 앱과 DB는 프라이빗에 있다는 것. 그리고 앱은 두 대라 하나가 죽어도 되지만 쓰기를 받는 DB는 한 대라는 것. 그림을 그리는 값은 대개 여기 있다 — 단일 장애점이 눈에 보인다.

flowchart TD
    user((외부 사용자))
    subgraph vpc[VPC]
        subgraph pub[퍼블릭 서브넷]
            lb[로드 밸런서]
        end
        subgraph priv[프라이빗 서브넷]
            app1[앱 인스턴스 A]
            app2[앱 인스턴스 B]
            db[(주 DB)]
            rep[(읽기 복제본)]
        end
    end
    user --> lb
    lb --> app1
    lb --> app2
    app1 --> db
    app2 --> db
    db -. 복제 .-> rep

위 그림을 그린 코드다.

```mermaid
flowchart TD
    user((외부 사용자))
    subgraph vpc[VPC]
        subgraph pub[퍼블릭 서브넷]
            lb[로드 밸런서]
        end
        subgraph priv[프라이빗 서브넷]
            app1[앱 인스턴스 A]
            app2[앱 인스턴스 B]
            db[(주 DB)]
            rep[(읽기 복제본)]
        end
    end
    user --> lb
    lb --> app1
    lb --> app2
    app1 --> db
    app2 --> db
    db -. 복제 .-> rep
```

상태 머신과 순서도, 무엇이 다른가

둘 다 상자와 화살표라 헷갈린다. 갈리는 지점은 상자가 무엇이냐다.

상태 머신순서도(활동)
상자머무는 자리 (명사)하는 일 (동사)
화살표이벤트가 와서 생기는 전이앞 일이 끝나 넘어가는 흐름
주인공대상 하나의 생애일의 절차
진행이벤트가 올 때까지 머문다끝나면 저절로 다음으로 간다

같은 주문을 양쪽으로 그려 보면 분명해진다. 상태 머신은 결제대기 → 결제완료 → 배송중처럼 주문 한 건의 생애를 그리고, 순서도는 주문 접수 → 재고 확인 → 결제 승인처럼 해야 할 일의 절차를 그린다.

고를 때 쓰는 질문은 하나다. 같은 입력이 어디서 오느냐에 따라 다르게 처리돼야 하는가. 이미 취소된 주문에 결제 성공 웹훅이 뒤늦게 도착하는 상황 — 이걸 막는 건 상태 머신의 일이다. 순서도는 “무엇을 하는가”만 말할 뿐 “지금 이건 하면 안 된다”를 말하지 못한다.

정리

다이어그램을 고를 때 “무슨 다이어그램을 그릴까”로 시작하면 UML 목록을 뒤지게 된다. “무엇을 묻고 있나”로 시작하면 답은 위의 일곱 줄 중 하나로 좁혀진다. 기호의 엄밀함은 잃었지만 그 자리에 코드와 함께 커밋되고 리뷰되는 그림이 들어왔다. 나쁘지 않은 거래다.

참고

  • UML 2.5 Diagrams Overview — uml-diagrams.org — 다이어그램 이름과 구조/행위 분류, Communication이 UML 1.x의 Collaboration이라는 사실 확인
  • Unified Modeling Language — Wikipedia — 다이어그램 14종(구조 7 · 행위 7) 교차 확인, 그리고 method wars·Three Amigos·OMG 채택(1997-11)·2.0(2005)·2.5의 명세 통합까지 역사 확인
  • UML 2.5.1 명세 (OMG, PDF) — 부록 A의 구조/행위 분류와 열네 다이어그램 이름을 원문에서 확인
  • UML — OMG 명세 페이지 — 버전별 발행일(2.0 = 2005-07, 현행 2.5.1 = 2017-12)과 이후 개정 계획이 없다는 점 확인
  • SysML — OMG 명세 페이지 — SysML 2.0 채택 시점(2025-09)과 KerML 기반이라는 점 확인
  • C4 model — 네 단계(시스템 맥락·컨테이너·컴포넌트·코드)의 이름과 “표기법을 정하지 않는다”는 입장 확인
  • Mermaid 문서 — 지원 다이어그램 종류와 키워드 확인. 예제는 mermaid 11.4.0에서 파싱을 확인했다