C4 모델과 C4-PlantUML을 이용한 소프트웨어 구조 다이어그램 만들기

안녕하세요. LINE에서 테크니컬 라이터로 일하고 있는 강정일입니다. 모두 건강하게 잘 지내고 계신가요? 올해는 ‘새해 복 많이 받으세요.’라는 말보다 COVID-19 때문에 건강을 먼저 여쭙게 되는 것 같습니다. COVID-19로 많은 것이 변하면서 컨퍼런스 행사도 대부분 오프라인에서 온라인으로 변경해 진행되고 있습니다. 제가 이전에 방문기를 작성했던 Write the Docs 컨퍼런스도 2020년엔 온라인으로 진행되어 원격으로 시청했습니다. 이번 글에선 Write the Docs Portland 2020 온라인 발표 중 하나를 소개하고 이 내용에 대해 이야기해볼까 합니다.

발표 내용과 제 생각을 공유하기 전에 소프트웨어 구조 다이어그램에 대한 경험을 함께 떠올려 보면 좋을 것 같습니다. 소프트웨어 구조를 파악할 때 글보다는 소프트웨어 구조 다이어그램과 같은 그림을 통해 훨씬 빠르게 이해한 경험이 많으실 겁니다. 그래서 동료와 협업하거나 파트너와 회의할 때 소프트웨어 구조 다이어그램을 많이 작성하게 됩니다. 그런데 소프트웨어 구조 다이어그램 작성에 익숙하지 않다면 막상 그리려고 해도 그리는 게 쉽지 않습니다. 어떤 도구를 써서 어떻게 시작해야 할지도 고민되고 그리기 시작한 뒤에도 이렇게 그리는 게 맞는지 자문할 때가 많으셨을 겁니다.

이번 글에서 소개할 발표는 Avi Flax 님의 Set your data free with model-based architecture diagramming 세션으로, 저와 여러분의 고민을 조금이나마 해소할 수 있을 것 같아 이에 대한 생각을 공유하려고 합니다.

이 글은 다음과 같은 순서로 진행하겠습니다.

 

데이터/텍스트 기반 다이어그래밍

발표자인 Avi 님은 내부용 소프트웨어 구조 문서를 작성하는 데에 많은 경험을 갖고 있었습니다. 그는 소프트웨어 구조에 대한 사람들의 질문은 대부분 다음 질문으로 귀결된다고 이야기했습니다.

소프트웨어에 어떤 시스템이 있고 각 시스템은 서로 어떤 관계인가?

이 질문에 대해서 대부분 ‘소프트웨어 구조 다이어그램’으로 답할 수 있었다고 합니다. 그런데 일반적으로 소프트웨어 구조 다이어그램이라고 하면 아래와 같이 화이트보드에 그려진 혼란스러운 그림을 상상한다고 말했습니다. 그래서 ‘(잘 그려진 일부 다이어그램만) 답할 수 있다(can answer).’라고 말했습니다.

그는 잘 그린 다이어그램은 소프트웨어 구조를 이해하거나 구조와 관련해서 커뮤니케이션하는 데 매우 효과적이라고 이야기하면서 이를 위해 소프트웨어 구조 다이어그램을 그릴 때 모델링 영역과 일러스트레이팅 영역을 구분해야 한다고 말했습니다. 소프트웨어 구조 다이어그램에서 모델링과 일러스트레이팅은 다음과 같이 정의할 수 있습니다.

  • 모델링
    특정 모델(이론)을 기반으로 소프트웨어 구조에 어떤 요소들이 있고 각 요소가 어떤 관계를 맺고 있는지 파악하는 것

  • 일러스트레이팅
    모델링으로 파악한 요소와 그 관계를 시각적으로 표현하는 것

모델링 결과는 소프트웨어 구조 다이어그램 작성 과정의 중간 콘텐츠라고 볼 수 있고, 일러스트레이팅은 이를 시각적으로 나타내는 다이어그램 프레젠테이션 기법이라고 말할 수 있습니다. 아래와 같이 이 둘을 구분해 소프트웨어 구조 다이어그램을 그릴 수 있도록 만드는 것이 이 발표의 핵심 내용이었습니다. 

Avi 님은 소프트웨어 구조 다이어그램의 작성 구조를 왜 위와 같이 만들어야 하는지에 대해서 아래와 같은 장점을 들어 설명했습니다.

  • 협업하기 쉬워짐(Collaboration)
  • 변경 사항을 쉽게 동기화할 수 있는 구조가 됨(Consistency)
  • 콘텐츠에 집중할 수 있음(Focus)
  • 텍스트 기반으로 작업할 수 있음(Text, Docs as Code와 같은 전략 적용 가능)
  • 소프트웨어 구조 모델을 데이터로 관리할 수 있음(Data)

그리고 이를 위해 우리가 무엇을 해야 하는지도 설명했습니다. 제 경험과 함께 그 이야기를 풀어 보겠습니다.

 

소프트웨어 구조 다이어그램 작성 경험

Avi 님만큼은 아니겠지만 저 역시 테크니컬 라이터로 일하면서 소프트웨어 구조 다이어그램을 많이 그렸습니다. 소프트웨어 프로젝트의 문서 산출물은 개발 프로세스와 배포 범위 등에 따라 그 종류와 성격이 달라지고(참고), 그에 따라 문서에 포함되는 다이어그램의 성격도 달라집니다. 따라서 제 문서 작성 경험을 소프트웨어 설계 단계에 필요한 문서와 구현 후 단계에 필요한 문서로 나누어 이야기해 보겠습니다. 

 

설계 단계의 다이어그램

먼저, 설계 문서를 작성한 경험입니다. 저는 문제가 발생하면 사용자의 생명이 위험해지는 것과 같은 치명적인 결과를 초래하는 자동차 전장 제품에 들어가는 소프트웨어의 설계 문서를 작성한 적이 있습니다. 소프트웨어 설계 문서로 제품의 안정성을 검증하기도 했기 때문에 다른 문서 프로젝트보다 중요도가 높았습니다. 소프트웨어 설계 단계의 산출물인 소프트웨어 요구 사항 명세서(SRS, Software Requirements Specification)와 소프트웨어 구조 문서(SAD, Software Architecture Document), 소프트웨어 설계 문서(SDD, Software Design Document)를 작성하고 관리했으며, 소프트웨어 설계자 및 각 모듈 개발자와 협업해 설계 문서를 작성할 때는 각 이해관계자에게 필요한 관점 즉, 구조 뷰1에 맞춘 UML(Unified Modeling Language) 다이어그램도 만들어야 했습니다.

이런 설계 문서를 작성하면서 느꼈던 점은 설계 단계에서 작성하는 다이어그램은 구조 뷰와 UML의 스펙이 명확하기 때문에 일단 작성이 완료되면 작성자나 독자 입장에서 모호한 부분이 없다는 것입니다(소프트웨어 아키텍처 문서화(Documenting Software Architecture) 책 참고). 하지만 다이어그램을 이해하기 위해서 알아야 할 것이 많아 진입 장벽이 높았고 그런 규칙과 기준을 만족하는 다이어그램을 그리려면 신경을 많이 쓰거나 복잡하고 비싼 모델링 도구를 써야 했습니다. 이런 이유로 앞서 보여드렸던 이미지처럼 화이트보드에 다이어그램을 그리는 것을 선호하는 사람도 많았습니다. 간편하고 쉽기 때문입니다. 

 

구현 후 단계의 다이어그램

소프트웨어 구현 후에 작성하는 개발자 가이드나 API 레퍼런스 문서에서도 다이어그램을 작성했습니다. 독자의 이해를 돕기 위해 개요 절과 같은 개념(concept) 타입 콘텐츠에 설명과 함께 소프트웨어의 사용 방법이나 동작 구조 혹은 내부 구성 요소를 나타내는 다이어그램을 작성했습니다. 사용자의 동작을 요구하는 태스크(task) 타입 콘텐츠에는 시퀀스 다이어그램이나 액티비티 다이어그램을, 레퍼런스 타입 콘텐츠나 디자인 가이드라인에는 상태 천이 다이어그램을 글과 함께 작성하곤 했습니다. 가이드나 API 레퍼런스 문서를 작성할 때는(물론 몇몇 UML을 사용한 다이어그램을 포함시키기도 했지만) 소프트웨어의 구조나 구성을 표현할 때 특별한 기준이나 규칙 없이 네모 박스를 쌓거나 내부에 포함시키는 형태로 만들기도 했고 구성 요소 사이에 임의로 선을 긋기도 했습니다. 이런 방식에선 UML 같은 복잡한 표현 명세를 알아야 할 필요도 없었고 Microsoft PowerPoint나 Microsoft Visio, Gliffy, Lucidchart와 같은 도구로 아래와 같은 다이어그램을 쉽게 그릴 수 있었습니다.

하지만 위와 같이 작성자의 자의적인 기준에 따라 그린 다이어그램은 협업자 또는 그림을 보는 독자가 같은 관점이나 기준으로 그림을 이해한다는 보장이 없었습니다. 다시 말하면 모델링과 일러스트레이팅에 대한 명확한 기준이 없었기 때문에 불확실한 표현이 많았습니다. 특정 문서 안에서 다이어그램 표기 형태를 통일한다고 하더라도 다른 문서에서는 그 기준이 달라지는 경우가 있었고, 대량의 문서나 문서 세트를 여럿이서 작업할 때 각자 다이어그램을 그리는 스타일이 달라서 그리는 규칙이나 작업 수준을 맞추기가 힘들 때도 있었습니다.

 

소프트웨어 구조 다이어그램 작성 방법이 갖춰야 할 특징

위와 같은 경험들은 소프트웨어 구조 다이어그램을 어떻게 작성해야 하는지 이해하는 데 큰 도움이 되었습니다. 저는 이런 경험에 기반해 소프트웨어 구조 다이어그램 작성 방법이 아래와 같은 특징을 갖춰야 한다고 생각했습니다.

  • 다이어그램을 작성하고 유지 보수하는 데 드는 비용과 시간이 적어야 한다.  
  • 일관된 구조 뷰와 정확한 표기 기준에 맞춰 그릴 수 있어야 한다.
  • 구조 뷰나 적용 모델은 소프트웨어 개발 관계자라면 누구나 이해하고 그릴 수 있도록 어렵거나 복잡하지 않아야 한다.

이런 특징을 갖춘 다이어그램 작성 방법에 대해 많이 고민했는데요. 다행히 2018년에 문서 엔지니어이자 동료 테크니컬 라이터인 전정은 님(주석 분석기를 이용한 간단한 API 문서화 방법이란 글을 남기셨죠!)과 협업하면서 PlantUML을 접할 수 있었습니다. 

 

텍스트 기반의 다이어그램 작성 오픈소스, PlantUML

PlantUML은 UML 다이어그램을 포함해 10여 개 정도의 다이어그램을 텍스트 기반으로 빠르게 작성할 수 있게 도와주는 오픈 소스 프로젝트입니다. 이를 이용해 Docs as Code 개념과 같은 전략을 다이어그램에 적용할 수 있었고, 현재도 문서를 작성할 때 잘 사용하고 있습니다. 다음은 텍스트 기반으로 다이어그램을 생성한 예입니다.

예제 1 – CLOVA

!include ../../../styles/PlantUML_StyleSheet.puml
 
actor User as user
participant Client as client #9EA0CA
participant CIC as cic #05D686
participant CLOVA as clova #05D686
 
user ->> client: "Play classical music"
 
activate client
  client ->> cic: Sends the event message of user request\n("Play classical music")
deactivate client
 
activate  cic
  cic ->> clova: Request user speech recognition
deactivate cic
 
activate clova
  clova -> clova: Recognize speech
 
  clova -> clova: Analyze semantics
 
  clova -->> cic: Send semantics analysis result
deactivate clova
 
activate cic
  cic -->> client: Sends the requested result as directive\n(guidance speech, audio information, play directs, etc.)
deactivate cic
 
activate client
  client -> client: Handle a directive
  client -->> user: Reporting handled results
deactivate client

예제 2 – LINE 로그인

@startuml
/' Formatting '/
title Register new user with access tokens [✅SECURE]
hide footbox
!include ../../plantuml-stylesheet.puml
 
actor user as "User"
participant app as "Client\n(your app)"
participant SDK as "LINE SDK"
participant server as "Your server"
participant LINEp as "LINE Platform"
 
user -> app : Log in with LINE
activate app
app -> SDK : Log in
activate SDK
app <-- SDK : Success (accessToken)
deactivate SDK
app -> server : accessToken
note right of app
  The client never sends
  user profile information,
  only the accessToken.
end note
activate server
server -> LINEp : [[/en/reference/line-login/#verify-access-token GET /oauth2/v2.1/verify (accessToken)]]
activate LINEp
server <-- LINEp : 200 OK (client_id, expires_in)
deactivate LINEp
server <-- server : Check: \n- client_id == LINE Login channel ID? \n- expires_in > 0?
note right of server #LightSkyBlue
  ✅ If the accessToken is
  valid, the **server** can
  use this accessToken to
  retrieve the user profile.
end note
server -> LINEp : [[/en/reference/line-login/#get-user-profile GET /v2/profile (accessToken)]]
activate LINEp
server <-- LINEp : 200 OK (User profile)
note right of server #LightSkyBlue
  ✅ The **server** can use this
  user profile information
  because it comes directly
  from the LINE Platform.
end note
deactivate LINEp
server <-- server : Create new user (User profile)
app <-- server: Success
deactivate server
user <-- app : You're logged in!
deactivate app
 
@enduml

예제 3 - GDC

@startuml
skinparam monochrome true
skinparam shadowing false
skinparam BackgroundColor White
skinparam RectangleBackgroundColor White
 
rectangle "User Info" as Info
rectangle "Game App" as App
rectangle "User Group" as Group
rectangle "Sales Info" as Sales
rectangle "Properties" as Property
rectangle "User List" as List
 
App "1" -- "0...*" Info
App "1" -- "0...*" Sales
App "1" -- "0...*" Group
Group "1" -- "1" Property
Group "1" -- "0..*" List
@enduml

PlantUML 덕분에 다이어그램 표기법을 맞출 수 있어서 일러스트레이팅에 관련된 많은 작업을 효율적으로 처리할 수 있게 되었습니다. 특히, 다이어그램을 업데이트할 때 그림을 새로 그릴 필요 없이 텍스트로 내용만 업데이트하면 되어 유지 보수 비용도 현저하게 줄었습니다. Jenkins와 같은 CI(Continuous Integration) 도구로 다이어그램 빌드를 자동화하거나 웹에서 PlantUML 데이터를 로드해 동적으로 다이어그램이 렌더링되는 구조를 적용해 다이어그램 콘텐츠를 업데이트하면 거의 실시간으로 다이어그램이 변하도록 만들 수도 있습니다. 이렇게 콘텐츠를 만드는 모델링 작업과 다이어그램을 표현하는 일러스트레이팅 작업을 분리하면 큰 장점을 얻을 수 있기 때문에 Avi 님도 발표에서 Microsoft Visio나 Gliffy, Lucidchart와 같이 직접 그리는 형태의 도구보다는 텍스트나 데이터 기반으로 다이어그램을 그리는 도구인 PlantUML이나 Mermaid와 같은 도구를 추천했습니다.

PlantUML을 사용해 소프트웨어 구조 다이어그램을 생성하는 과정을 그림으로 나타내면 아래와 같다고 할 수 있습니다.

 

PlantUML로도 채울 수 없었던 점

PlantUML은 다이어그램 표현 방식을 통일하고 다이어그램을 효율적으로 생성할 수 있다는 점에서 많은 이점을 주었습니다. 하지만 여전히 채워지지 않는 부분이 있었습니다. PlantUML은 주로 일러스트레이팅 과정에 많은 영향을 주는 도구이기 때문에 중간 콘텐츠를 생성하는 방법은 여전히 전형적인 모델을 이용해야 합니다. 즉, 사람이 소프트웨어 구조를 모델링해서 데이터로 바꾸는 방법론이나 모델은 기존 이론을 이용해야 하기 때문에 여전히 조금 어렵고 복잡했습니다. 이 점이 앞서 언급했던 다이어그램 작업의 특징 중 세 번째 항목에 걸렸습니다. 이런 제약 때문에 주로 개념 타입 콘텐츠에 추가하는 시스템의 구조나 구성을 보여주는 다이어그램을 작성할 때는 PlantUML을 사용하지 않았습니다.

저는 UML보다 쉽고 간편하며 협업하기 좋은 모델과 이를 표현하는 도구가 필요했습니다. 그러던 중 Avi 님의 발표를 통해 C4 모델을 알게 되었고 이 모델이 비교적 쉽고 간결하다고 느꼈습니다. 발표에서 이와 관련된 내용을 깊이 다루고 있지 않기 때문에 이번 글에서 C4 모델과 이를 표현하는 프레젠테이션 도구인 C4-PlantUML에 대해서 좀 더 깊이 이야기해보려고 합니다.

 

C4 모델

발표에서 가장 관심이 갔던 내용은 C4 모델이었습니다. C4 모델은 Simon Brown이라는 사람이 UML과 4+1 아키텍처 뷰 모델을 기반으로 만든 모델입니다. 시스템 콘텍스트(Context)와 컨테이너(Container), 컴포넌트(Component), 그리고 코드(Code) 순서로 다이어그램의 수준(level)을 달리하여 소프트웨어를 작은 단위로 분해해가는 방식으로 모델링하는 기법입니다. C4는 각 수준에 공통으로 들어가는 알파벳 'C'의 개수를 의미합니다. 이 모델은 널리 알려진 애자일 방법론과 같이 소프트웨어 개발을 기민하게 진행하는 상황에서 소프트웨어의 구조를 빠르고 효율적으로 공유하고 지속적으로 업데이트할 때 사용하도록 권장하고 있으며, 정적인 뷰(static view)의 소프트웨어 구조를 나타내는 데 적합합니다.

C4 모델은 지도 개념에 빗대서 설명할 수 있습니다. 아시다시피 지도는 척도에 따라 국가와 대륙이 보이는 지도에서 동네와 거리가 보이는 지도까지 보이는 규모와 상세한 정도를 달리 설정할 수 있습니다. C4 모델은 이렇게 보이는 다이어그램의 수준을 조절하는 방식으로 소프트웨어의 구조를 나타냅니다. 'abstraction-first' 접근 방식으로 제일 높은 수준인 시스템 컨텍스트에서 하위 수준으로 소프트웨어를 분해해 나가면서 소프트웨어 구조를 모델링합니다. 다음은 C4 모델에 대해 Simon Brown이 자세히 설명하는 영상입니다.

영상의 내용을 요약할 겸 C4 모델의 내용을 정리해보면 우선 추상화하는 대상에 다음과 같은 것이 있습니다.

  • 사용자(person)
    사용자나 역할 등을 표현하는 요소이며 소프트웨어 시스템의 사용자를 나타냅니다.

  • 소프트웨어 시스템
    최상위 추상화 요소로 시스템이 어떤 가치를 제공하는지 나타내며 보유하거나 개발하고 있는 소프트웨어와 연동되는 소프트웨어를 나타낼 때 사용합니다.

  • 컨테이너
    소프트웨어 시스템의 내부를 표현하는 추상화 요소로 애플리케이션이나 데이터 저장과 관련된 솔루션을 나타낼 때 사용합니다. Docker의 컨테이너 개념과는 다르며 서버 애플리케이션이나 클라이언트 애플리케이션(웹, 모바일, PC), CLI 애플리케이션 또는 배치 프로세스, 데이터베이스, Blob 스토리지, 파일 시스템, 쉘 스크립트 등과 같은 것을 나타냅니다. 주로 독립적으로 배포할 수 있는 소프트웨어 단위로 구분합니다.

  • 컴포넌트
    컨테이너 내부를 표현하는 추상화 요소로 기능 단위로 묶을 수 있는 모듈이나 인터페이스의 집합을 나타낼 때 사용합니다. Java나 C#에 빗대어 설명하면 인터페이스나 패키지를 구현하기 위해 구현한 클래스의 집합이라고 생각하면 됩니다. 독립적으로 배포할 수 있는 소프트웨어 단위가 아닌 것으로 구분하면 됩니다.

  • 관계
    위 추상화 요소 사이의 의존성이나 데이터 흐름 등을 나타냅니다.

이제 위 추상화 대상을 다이어그램의 수준에 맞게 표현하면 됩니다. C4 모델에 별도로 강제하는 표기 규칙은 없습니다. 다만 다음과 같은 가이드라인은 제시하고 있습니다.

  • 다이어그램 표기 가이드라인
    • 다이어그램은 제목을 표시하고 수준을 알 수 있게 작성합니다(예. System context diagram of my software).
    • 되도록 다이어그램의 범례를 표시합니다.
    • 약어나 두문자어(낱말의 머리글자를 모아서 만든 준말)는 독자가 이해할 수 있게 표기합니다.

  • 요소 표기 가이드라인
    • 모든 요소는 타입이 무엇인지 명확히 구분합니다.
    • 모든 요소에 짧은 설명이 있어야 합니다.
    • 컨테이너와 컴포넌트는 사용 혹은 기반 기술이 무엇인지 표기합니다.

  • 관계 표기 가이드라인
    • 되도록이면 단방향 선으로 관계를 표시합니다.
    • 모든 선에 설명을 작성합니다.
    • 컨테이너 간에 데이터를 주고받기 위해 어떤 기술이나 프로토콜을 사용했는지 표기합니다.

이제 추상화 대상과 가이드라인을 토대로 각 다이어그램의 수준에 맞춰 다이어그램을 그려 보겠습니다. 이해를 돕기 위해 CLOVA AiCall을 예로 들겠습니다. 먼저 최상위 수준인 시스템 컨텍스트 다이어그램은 큰 그림을 그리는 단계로 대상 시스템과 연동하는 다른 시스템 또는 사용자를 나열하고 이들 간의 관계를 대략적으로 표현하는 다이어그램입니다. 기술자가 아니더라도 전체 맥락과 내용을 이해할 수 있게 작성해야 합니다.

1. 소프트웨어 구조를 표현하려는 대상 시스템을 그린 다음 간략하게 설명합니다.
2. 시스템을 직접 사용하거나 시스템과 연동되는 요소인 사용자 또는 외부 시스템을 간략한 설명과 함께 추가합니다.
3. 각 요소가 어떤 관계를 맺고 있는지 선으로 표현하고 설명을 작성합니다. A 요소에서 B 요소로 향하는 선의 설명을 작성한다고 가정할 때 영문 작성 기준으로 A 요소를 주어로, B 요소를 목적어나 간접 목적어로 둔다고 생각하면 좋습니다.
4. 시스템 컨텍스트 다이어그램은 최상위 수준의 다이어그램으로 단순히 대상 시스템과 직접 관련된 일부 요소만 국소적으로 표현하기보단 내용을 전혀 몰라도 전체 맥락을 살필 수 있는 범위로 나타내는 것이 좋습니다. 또한 범례도 넣어야 합니다.

다음은 컨테이너 다이어그램입니다. 시스템 컨텍스트 다이어그램에서 CLOVA AiCall 내부를 들여다본다고 생각하면 쉽습니다. 일단, CLOVA AiCall의 경계를 그린 다음 시스템 컨텍스트 다이어그램에서 CLOVA AiCall과 관계를 맺고 있던 시스템 요소를 그립니다.

그런 다음 시스템 컨텍스트 다이어그램에서 수행한 것과 같이 개별 배포할 수 있는 단위의 소프트웨어 요소를 컨테이너 타입으로 나열하고 각 컨테이너와 외부 시스템 또는 사용자와의 관계를 작성합니다. 이때 중요한 것은 시스템 컨텍스트 다이어그램과 달리 어떤 기술을 사용하는지 각 요소와 관계를 표현하는 선에 표시하거나 설명해야 합니다. 이 다이어그램은 고 수준 설계(high level design) 단계의 다이어그램이며 소프트웨어 설계자와 개발자, 그리고 프로젝트 운영자에게 유용합니다. 다음은 컨테이너 다이어그램을 마저 그려본 예입니다.

컴포넌트 다이어그램은 컨테이너 다이어그램에서 표현한 각 컨테이너를 확대하여 나타낸 다이어그램입니다. 설계 초기 단계나 개발자 가이드, API 레퍼런스 문서와 같은 곳에서는 잘 사용하지 않습니다. 소프트웨어 설계자와 소프트웨어 개발자가 어떤 구성 모듈(component)을 만들어야 하고, 각 모듈이 어떤 일을 수행해야 하는지, 어떤 의존 관계가 있는지 나타냅니다. 

컨테이너 다이어그램을 그릴 때와 마찬가지로 대상 컴포넌트의 경계를 그린 다음 컨테이너 다이어그램에서 대상 컴포넌트와 관계를 맺고 있던 컨테이너 요소를 그립니다. AiCall 인터페이스 요소를 예로 들어 설명하면 아래와 같이 컴포넌트 다이어그램을 준비할 수 있습니다.

마지막으로 기능 단위로 구분한 모듈을 해당 컨테이너의 컴포넌트로 간주하고 나열해서 이들의 구체적인 역할과 관계를 컨테이너 다이어그램보다 구체적으로, 사용 기술과 구현 정보와 함께 나타냅니다. 다음은 AiCall 인터페이스 컨테이너의 컴포넌트 다이어그램을 그려본 예입니다.

C4 모델에서 코드 수준의 다이어그램은 매우 구체적인 구현을 나타내는 클래스 다이어그램이나 개체 관계 다이어그램과 유사합니다. 코드 수준의 다이어그램은 필요에 따라 그릴 수도 있고 생략할 수도 있는데요. 매우 중요하거나 복잡한 컴포넌트가 아니라면 대체로 그리지 않는 것을 권장한다고 합니다.

C4 모델로 소프트웨어 구조를 모델링하고 이를 다이어그램으로 그려보았습니다. 이제 모델링한 결과를 중간 산출물(텍스트)로 남기고 이를 토대로 다이어그램을 그려주는 도구에 대해 알아볼 시간입니다.

 

텍스트 기반 C4 모델 다이어그램 작성 도구, C4-PlantUML

Avi 님은 C4 모델을 소개하면서 이를 텍스트 기반으로 그릴 수 있는 도구도 함께 소개했습니다. 바로 Structurizr와 C4-PlantUML, 그리고 Kroki입니다. 그런데 Kroki는 C4-PlantUML을 이용하기 때문에 실제로 Structurizr과 C4-PlantUML만이 C4 모델 다이어그램을 그릴 수 있는 도구라고 볼 수 있습니다.

Structurizr는 C4 모델 다이어그램 작성 솔루션을 제공하는 유료 서비스이지만 해당 서비스에서 사용한 다이어그램용 DSL(Domain Specific Language)과 빌드용 CLI 명령은 오픈 소스로 제공하고 있습니다. 이 DSL 기반으로 작성된 소프트웨어 아키텍처 데이터를 이용해 다양한 포맷으로 다이어그램을 만들 수 있고, DSL의 명세를 잘 활용하면 다이어그램의 레이아웃이나 스타일도 세세하게 조정할 수 있습니다. 이런 점이 Structurizr의 장점입니다.

C4-PlantUML은 앞서 설명드린 PlantUML의 라이브러리 같은 개념으로 PlantUML에 C4 모델 다이어그램을 작성할 수 있도록 확장한 명세를 제공합니다. Structurizr과 다르게 다이어그램의 레이아웃이 거의 자동으로 결정되며 스타일도 정해진 스타일을 사용해야 하지만, 기존 PlantUML을 사용하는 환경에 어렵지 않게 적용할 수 있고 업데이트도 꾸준히 되고 있습니다.

Structurizr과 C4-PlantUML는 각각 DSL 포맷이나 PlantUML 마크업으로 작성된 텍스트 기반의 데이터를 기반으로 다이어그램을 만들어 냅니다. 따라서, 소프트웨어 구조 다이어그램을 그리는 프로세스에서 모델링은 C4 모델을 적용해 소프트웨어 구조를 DSL이나 C4-PlantUML 포맷으로 만들어 내는 것이라고 할 수 있습니다.

저는 2021년 1월 현재 작업 도구로 Microsoft Visual Studio Code를 사용하고 있습니다. Structurizr보다 PlantUML 플러그인이 잘 동작하기도 하고, 앞서 말씀드렸듯 계속 PlantUML을 써왔기 때문에 C4 모델 다이어그램의 일러스트레이팅 도구로 PlantUML을 사용하고 있습니다. 물론, 다이어그램을 좀 더 세세하게 다루거나 데이터를 범용적으로 사용할 수 있게 YAML과 같은 포맷으로 다이어그램 콘텐츠를 다뤄야 한다면 Structurizr DSL을 이용하는 쪽으로 전향할지도 모르겠습니다.

C4-PlantUML을 사용하는 방법은, 먼저 C4-PlantUML 저장소를 복제(clone)하거나 다운로드한 후 작업하려는 PlantUML 파일 맨 윗줄에 아래와 같이 C4-PlantUML 파일을 로드하는 구문을 추가하면 됩니다.

@startuml
!include path/to/C4-PlantUML/C4.puml
...

항상 최신 버전을 사용하고 싶다면 아래와 같이 인터넷 URI를 입력해도 됩니다.

@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4.puml
...

또한 C4.puml과 C4_Context.pumlC4_Container.pumlC4_Component.puml 파일을 구분해서 로드할 수도 있습니다. 정확한 설명을 찾을 수는 없었지만, 아마도 각 다이어그램의 수준에 따라 표현되는 요소의 범위가 다르기 때문에 쓸데없는 데이터 로드를 방지하기 위함인 것 같습니다. 예를 들면, 범례와 같은 것도 로드한 파일에 따라서 해당 수준에 등장할 수 있는 요소만 범례 항목에 표시됩니다. 따라서 시스템 컨텍스트나 컨테이너 다이어그램을 만든다면 해당 파일만 로드하는 것이 좋을 것 같습니다.

이제 그렸던 다이어그램을 C4-PlantUML 문법에 맞춰 다이어그램 콘텐츠로 남길 시간입니다. PlantUML과 C4 모델에 대해 어느 정도 이해하고 있다면 그 외 문법을 비롯한 사용법은 C4-PlantUML GitHub 저장소의 README.md를 읽고 30분에서 1시간 정도 써 보는 것만으로 충분히 익힐 수 있습니다. 예상하셨겠지만 앞서 보여드린 예제도 모두 C4-PlantUML을 이용해 만든 다이어그램입니다. 이를 살펴볼 수 있도록 아래와 같이 예제를 준비했습니다.

시스템 컨텍스트 예제

@startuml
 
!include ../C4-PlantUML/C4_Context.puml
!define ICONS https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/
 
!include ICONS/font-awesome-5/users.puml
!include ICONS/font-awesome-5/user_tie.puml
!include ICONS/material/contact_phone.puml
 
LAYOUT_AS_SKETCH()
LAYOUT_LEFT_RIGHT()
LAYOUT_WITH_LEGEND()
 
Person_Ext(user, "Clients", "Clients who are using OOO service", "users")
Boundary(new, "Customer service using CLOVA AiCall") {
 
  Boundary(legacy, "Legacy customer service"){
    System_Ext(cc, "Contact Center", "Contact point, switches customer calls.")
    Person_Ext(counselor, "Customer counselors", "Interacts with customers to handle complaints, process orders, and provide information about OOO service.", "contact_phone")
    System_Ext(ars, "ARS", "Interacts with customers when counselors are not available.")
 
    Rel(user, cc, "Makes a call")
    Rel(cc, counselor, "Connects and forwards a call to")
    Rel(cc, ars, "Connects and forwards a call to")
  }
 
  System(ccai, "CLOVA AiCall", "Interacts with customers using CLOVA AI tech.")
   
  Rel(cc, ccai, "Connects and forwards a call to")
   
}
 
Person_Ext(manager, "Manager", "Makes a decision from statistics provided by CLOVA AiCall", "user_tie")
Rel(manager, ccai, "Retrives statistics from")
 
@enduml

컨테이너 예제

@startuml
 
!include ../C4-PlantUML/C4_Container.puml
!define ICONS https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/
 
!include ICONS/font-awesome-5/user_tie.puml
 
LAYOUT_AS_SKETCH()
LAYOUT_LEFT_RIGHT()
LAYOUT_WITH_LEGEND()
 
System_Ext(cc, "Contact Center", "Contact point, switches customer calls.")
Person_Ext(manager, "Manager", "Makes a decision from statistics provided by CLOVA AiCall", "user_tie")
 
System_Boundary(clova_ai_call, "CLOVA AiCall") {
 
  Container(pa, "Protocol Adapter", "SIP, gRPC client", "Converts voice streams into supported format.")
  Container(vsg, "Voice Streaming Gateway", "gRPC server", "Manages call sessions and handle voice streaming.")
  Container(cic, "CIC", "CIC Protocol", "Processes AI requests")
  Container(interface, "AiCall Interface", "Kafka, REST API", "Provides interfaces for AiCall console")
  Container(console, "AiCall Console", "Web app", "Provides UI for retrieving statistics.")
  ContainerDb(db, "Database", "Redis, MySQL", "Stores activities, events, and call information")
       
  Rel(cc, pa, "Forwards voice stream data to", "SIP trunk")
  Rel_L(pa, vsg, "Forwards converted voice stream to", "gRPC call")
 
  Rel(vsg, cic, "Requests voice recognition and processing corresponding works to", "REST/HTTPS 2.0")
  Rel(vsg, interface, "Sends event log to", "REST API call")
  Rel(manager, console, "Retrieves statistics from", "HTTP")
  Rel(console, interface, "Queries statistic data from", "REST API call")
 
  Rel_L(interface, db, "Reads from and writes to", "JDBC")
}
 
@enduml

컴포넌트 예제

@startuml
 
!include ../C4-PlantUML/C4_Component.puml
!define ICONS https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/
 
!include ICONS/govicons/user_suit.puml
!include ICONS/font-awesome-5/user_tie.puml
!include ICONS/material/dialer_sip.puml
 
LAYOUT_AS_SKETCH()
LAYOUT_WITH_LEGEND()
 
Container_Ext(vsg, "Voice Streaming Gateway", "gRPC server", "Manages call sessions and handle voice streaming.")
Container_Ext(console, "AiCall Console", "Web app", "Provides UI for retrieving statistics.")
ContainerDb_Ext(db, "Database", "Redis, MySQL", "Stores activities, events, and call information")
 
Container_Boundary(ai_call_interface, "AiCall Interface") {
 
  Component(signin, "Sign In Controller", "Spring MVC REST Controller", "Allows users to sign in to AiCall Console.")
  Component(query, "Query Controller", "Spring MVC REST Controller", "Allows users to Retrieves statistics data with query.")
  Component(log, "Event Log Controller", "Spring MVC REST Controller", "Allows other components to store their events logs to DB.")
  Component(auth, "Auth Component", "Spring Bean", "Provides functionality related to authentication")
  Component(db_control, "DB Controller", "Spring Bean", "Provides functionality related to DB access.")
 
}
 
Rel(vsg, log, "Makes API calls to", "JSON/HTTPS")
Rel_R(console, signin, "Makes API calls to", "JSON/HTTPS")
Rel_R(console, query, "Makes API calls to", "JSON/HTTPS")
Rel(signin, auth, "Uses")
Rel(query, db_control, "Uses")
Rel(log, db_control, "Uses")
Rel(auth, db_control, "Uses")
Rel(auth, db_control, "Uses")
Rel(db_control, db, "Reads from and writes to", "JDBC")
 
@enduml

 

맺음말

글을 정신없이 전개했는데 이제 정리해야 할 것 같습니다. 얼핏 보면 이 글은 공식적이고 전문적인 문서에 들어가는 다이어그램을 그리는 데에는 도움이 되지만 평소 업무에는 활용하기 어려울 것 같다고 생각할 수 있습니다. 하지만 조금 더 생각해 보면 다이어그램은 문서 작업뿐 아니라 평소에도 많이 그리고 활용한다는 것을 알 수 있습니다. 소프트웨어에 대한 생각을 동료와 공유하고 협의할 때나 외부 관계자에게 대략적인 구조를 설명할 때, 새로운 멤버가 합류해 관련 내용을 알려줘야 할 때와 같은 상황들 말입니다. 회의 중에, 혹은 대화하면서 즉석으로 보드나 종이에 다이어그램을 그리고 이걸 사진으로 찍어서 위키와 같은 협업 도구에 올리는 것도 많이 봐 왔습니다.

이처럼 일상에서 다이어그램을 많이 활용하는 이유는 무엇일까요? 얼마 전 발행됐던 이 블로그에서 언급됐던 것처럼 우리가 글 쓰는 것에 많은 부담을 느끼는 것도 이유가 되겠지만, 그보다는 다이어그램으로 내용을 좀 더 직관적으로 전달할 수 있기 때문이 아닐까 생각합니다. 그럴 때 4+1 아키텍처 뷰나 UML과 같이 다소 어렵고 복잡한 이론과 규칙을 이해할 필요는 없으면서도 손으로 즉석에서 만든 다이어그램보다는 기준과 체계가 잘 잡혀있는 C4 모델로 다이어그램을 만들어 보면 어떨까요? 손으로 그리는 것만큼 빠르고 쉽진 않겠지만 훨씬 명확하고 협업하기 쉬우며 지속해서 사용할 수 있는 소프트웨어 구조 다이어그램을 만들 수 있을 것이라고 생각합니다.

지금까지 소프트웨어 구조 다이어그램을 그릴 때 활용할 수 있는 C4 모델과 C4-PlantUML를 소개했습니다. 연초이니 만큼 시작하는 프로젝트도 많고 새롭게 합류하는 동료도 많아서 다이어그램을 그릴 계기가 많을 텐데요. 이 글에서 소개한 내용이 여러분께서 동료 또는 고객과 함께 생각을 공유하고 업무를 진행할 때 조금이나마 도움이 되길 바라겠습니다.

 


 

  1. 구조 뷰(architecture view): 건물의 구조를 평면도와 배치도, 입면도, 단면도, 조감도와 같이 여러 관점으로 나누어 표현하듯 소프트웨어의 구조도 각 이해관계자의 관심사 결정에 필요한 부분을 잘 나타낼 수 있도록 다양한 관점으로 나눠서 표현합니다. 소프트웨어 구조를 들여다보는 기준이라고 할 수 있습니다.