본문 바로가기
카테고리 없음

누구를 위한 문서인가...

by eoruadl 2026. 2. 13.

대부분 회사에서 혼자 일을 하다 보니 문서의 중요성을 깨닫기까지 오래 걸렸다.

작년에 프론트엔드 작업을 하며 백엔드 개발자와 소통을 하는데 정리되지 않은 API 명세서로 인해 불필요한 소통을 하게 되는 경험을 하면서 문서의 중요성을 깨닫게 되었다.

 

전체 개발 프로세스의 작은 부분이지만 제대로 갖춰지지 않은 문서로 인해 생산성이 떨어지는 것을 경험하고 어떻게 하면 소통이 잘되는 문서를 작성할 수 있을까... 란 고민을 하기도 했다.

 

이번에 개발을 하기 전 작성되는 설계 문서를 알아가고 이커머스 도메인에 대한 문서를 작성하며 느낀 점을 공유하고자 한다.


- 요구사항 정리 -

요구사항 정리 문서를 작성하며 이 문서는 어느정도의 depth를 가지고 작성을 해야 할지 고민하게 됐다.

상세하게 작성을 하려면 개발적인 내용들이 들어가줘야하고 개발적인 내용이 들어가면 비개발자들이 이해하기 힘들 것 이라고 생각했다.

 

예를 들어 좋아요 기능에 대한 기능 정의를

 

- product_id와 user_id를 복합 UK로 설정하여 하나의 상품에 대해 좋아요가 중복되지 않도록 한다.

- 상태값에 가까운 좋아요는 Hard Delete를 통해 좋아요를 취소한다.

 

라고 하면 비개발자 입장에선 이해하기 힘들 수 있다.

 

그래서 개발자가 아닌 사용자 관점에서 요구사항을 정리하는 게 개발자와 비개발자 간의 소통에 유리하다고 생각한다.

개발적인 내용들은 다른 문서를 통해 표현을 하면 된다.

 

예를 들어 시퀀스 다이어그램, 클래스 다이어그램 등을 통해 표현하면 된다.


- 시퀀스 다이어그램 -

시퀀스 다이어그램은 객체의 역할과 책임을 표현하고 시간의 흐름에 따라 각각의 객체들이 어떻게 상호작용하는지를 표현하는 다이어그램이다.

 

추상적으로 흐름만 나타내면 될지 고민을 했는데 메서드와 어떤 파라미터가 사용되어 데이터가 흘러가는지 좀 더 자세히 표현하고 싶어 구체적으로 작성을 해보았다. 상황에 따라 추상적으로 표현하는 게 더 효과적일 수 있을 것 같다고 생각한다.

 

요청이 들어오고 데이터가 어떻게 흘러갈지를 고민하다보니 어떤 객체가 어떤 역할을 해야 할지 조금씩 보였다.

 

작성한 주문 요청 기능에 대한 시퀀스 다이어그램은 아래와 같다.

 

sequenceDiagram
    participant User
    participant OC as OrderController
    participant OS as OrderService
    participant PS as ProductService
    participant PIS as ProductInventoryService
    participant PR as ProductRepository
    participant PIR as ProductInventoryRepository
    participant OR as OrderRepository

    User ->> OC: POST /api/v1/orders
    OC ->> OS: createOrder(userId, items)
    
    loop 상품별 재고 확인 및 스냅샷 생성
        OS ->> PS: getProduct(productId)
        PS ->> PR: findById(productId)

        alt 상품 있음
            PR -->> OS: ProductModel
            OS ->> PIS: getProductStock(productId)
            PIS ->> PIR: findByProductId(productId)
            PIR -->> OS: ProductInventoryModel

        alt 재고 충분
            OS ->> PIS: decreaseStock(productId, quantity)
            PIS ->> PIR: decreaseStockByProductId(productId, quantity)
            PIR -->> OS: 재고 차감 (stock - quantity)
            OS ->> OS: 주문 상세(Snapshot) 생성
        else 재고 부족
            OS -->> OC: 예외 발생 (Out of Stock)
            OC -->> User: 400 Bad Request
        end

        else 상품 없음
            PR -->> PS: null
            PS -->> OS: 예외 발생 (Not Found)
            OS -->> OC: 예외 발생 (Not Found)
            OC -->> User: 404 Not Found
        end
    end

    OS ->> OR: save(OrderModel)
    OR -->> OS: Saved OrderModel
    OS -->> OC: 생성된 주문 정보
    OC -->> User: 주문 생성 성공 (201 Created)

 

처음엔 ProductInventory를 사용하지 않고 Product에 수량을 넣어 재고를 관리하려고 했다.

생각해 보니 수량은 계속해서 변하는 상태값이었고 Product에는 변하지 않는 정보를 관리하도록 하는 게 좋다고 생각했다.

그래서 ProductInventory로 수량의 상태를 책임지도록 역할을 분리하였다.

 

그리고 예외 처리에 대한 흐름을 어느정도로 표현해야 할지 고민했지만 각 도메인의 핵심 부분만 표현하도록 하였다.

각 기능의 핵심 내용에 대한 예외가 아닌 내용을 표현하면 다이어그램이 복잡해져 중요한 부분을 놓칠 수 있다.


- 클래스 다이어그램 -

클래스 다이어그램은 각각의 클래스들이 어떤 관계를 가지고 있는지 나타내는 정적인 설계도이다.

 

아래는 주문과 관련된 클래스들의 관계를 나타낸 다이어그램이다.

 

classDiagram
%% 주문
class Order {
  +String orderId
  +String userId
  +Decimal totalAmount
  +OrderStatus status
  +Date orderDate
  +Date createdAt
  +Date updatedAt
  +calculateTotal() Decimal
  +updateStatus(OrderStatus status) void
}

%% 주문 상세
class OrderItem {
  +String orderItemId
  +String orderId
  +String productId
  +Integer quantity
  +Decimal priceAtOrder
  +String productNameSnapshot
  +String productDescriptionSnapshot
  +getSubtotal() Decimal
}

%% 주문 상태
  class OrderStatus {
  <>
  PENDING
  PROCESSING
  SHIPPED
  DELIVERED
  CANCELLED
  }
  
  Order "1" *-- "1..*" OrderItem : 포함
  Order "1" -- "1" OrderStatus : 상태 보유

 

주문 시 사용된 상품의 정보를 OrderItem 클래스를 통해 스냅샷으로 저장해서 상품 정보가 변경되거나 삭제되더라도 주문 당시의 데이터를 보존하여 관리한다는 것과 OrderStatus를 통해 주문의 상태값을 enum 타입으로 관리한다는 것을 확인할 수 있다.

 

이처럼 클래스 다이어그램을 통해 객체 간의 의존성과 관계를 그림으로 파악할 수 있다.


- ERD -

ERD는 엔티티 관계 모델로 데이터베이스에 데이터를 어떤 구조로 저장할지 시각적으로 나타낸 데이터베이스 설계도이다.

 

테이블에 어떤 데이터가 들어갈지 테이블간의 카디널리티는 어떻게 정의할지를 작성하는 용도이다.

 

아래는 ERD로 표현한 전체적인 관계이다.

 

erDiagram
    %% 사용자 엔티티
    USER {
        BIGINT userId PK
        VARCHAR(100) name "NOT NULL"
        VARCHAR(100) password "NOT NULL"
        VARCHAR(100) email UK "NOT NULL"
        DATE birthDate
        BOOLEAN isDeleted "NOT NULL"
        DATETIME createdAt "NOT NULL"
        DATETIME updatedAt "NOT NULL"
        DATETIME deletedAt
    }

    %% 좋아요 엔티티
    LIKE {
        BIGINT likeId PK
        BIGINT productId UK "NOT NULL"
        BIGINT userId UK "NOT NULL"
        DATETIME createdAt "NOT NULL"
    }

    %% 브랜드 엔티티
    BRAND {
        BIGINT brandId PK
        VARCHAR(100) name UK "NOT NULL"
        TEXT description
        VARCHAR(255) logoImagePath
        VARCHAR(100) businessNumber UK
        VARCHAR(100) contactEmail UK
        VARCHAR(100) contactPhone UK
        TEXT address
        BOOLEAN isDeleted "NOT NULL"
        DATETIME createdAt "NOT NULL"
        DATETIME updatedAt "NOT NULL"
        DATETIME deletedAt
    }

    %% 상품 엔티티
    PRODUCT {
        BIGINT productId PK
        BIGINT brandId "NOT NULL"
        VARCHAR(100) name "NOT NULL"
        TEXT description
        DECIMAL price "NOT NULL"
        INT likeCount "NOT NULL"
        VARCHAR(255) imageUrl
        BOOLEAN isDeleted "NOT NULL"
        DATETIME createdAt "NOT NULL"
        DATETIME updatedAt "NOT NULL"
        DATETIME deletedAt
    }

    %% 상품 재고 엔티티
    PRODUCT_INVENTORY {
        BIGINT productId PK "NOT NULL"
        INT stock "NOT NULL"
        DATETIME updatedAt "NOT NULL"
    }

    %% 주문 엔티티
    ORDER {
        BIGINT orderId PK
        BIGINT userId "NOT NULL"
        DECIMAL totalAmount "NOT NULL"
        VARCHAR(100) orderStatus "NOT NULL"
        DATETIME orderDate "NOT NULL"
        DATETIME createdAt "NOT NULL"
        DATETIME updatedAt "NOT NULL"
    }

    %% 주문 상세 엔티티
    ORDER_ITEM {
        BIGINT orderItemId PK
        BIGINT orderId "NOT NULL"
        BIGINT brandId "NOT NULL"
        BIGINT productId "NOT NULL"
        INT quantity "NOT NULL"
        DECIMAL priceAtOrder "NOT NULL"
        VARCHAR(100) productNameSnapshot "NOT NULL"
        TEXT productDescriptionSnapshot
        VARCHAR(255) imageUrlSnapshot
    }

    %% 관계 정의
    BRAND ||--o{ PRODUCT : "소유"
    PRODUCT ||--|| PRODUCT_INVENTORY : "재고 보유"
    direction LR
    USER ||--o{ LIKE : "좋아요 등록"
    PRODUCT ||--o{ LIKE : "좋아요 받음"
    USER ||--o{ ORDER : "주문 생성"
    ORDER ||--|{ ORDER_ITEM : "포함"
    PRODUCT ||--o{ ORDER_ITEM : "참조됨"

 

이번에 시퀀스 다이어그램, 클래스 다이어그램 그리고 ERD를 마지막으로 작성했는데 ERD를 처음에 작성을 했다면 설계하기 좀 더 쉬워지지 않았을까 하는 생각을 했다.

 

데이터를 정의하는데 가장 중요한 문서가 아닐까 하고 생각한다.


이번에 다이어그램에 대해 공부하면서 다이어그램에도 종류가 많다는 것을 이번에 처음 알았다.

시퀀스 다이어그램, 클래스 다이어그램 등 기본적인 것들은 알고 있었지만 C4 다이어그램, 스테이트 머신 다이어그램, 컴포넌트 다이어그램 등 다양한 종류가 있다는 걸 알았다.

 

각각의 문서, 다이어그램은 작성되는 목적이 다 다르고 적절한 상황에 어떤 도구를 사용하는지가 중요한 것 같다.

그리고 다이어그램을 그리다보니 다이어그램만 그리고 있는 나 자신을 발견했다.

다이어그램을 잘 그리는 것도 중요하지만 해당 문서를 보는 사람들을 잘 이해시키기 위한 도구로써 다이어그램이 존재한다고 생각한다. 해당 다이어그램을 이해시킬 수 있도록 글로도 잘 설명하고 표현해 낼 수 있어야 한다고 느꼈다.

 

다양한 다이어그램을 실무에도 적용시켜보고 다양한 상황에서 사용해 보며 경험치를 많이 쌓아야겠다.

 

누구를 위한 문서인가... 를 고민하다보면 어느 정도의 깊이를 가지고 작성해야 할지, 어떤 도구를 사용해야 할지.. 이런 고민들을 하게 되는데 많은 경험을 쌓아 적절한 판단을 내릴 수 있는 사람이 되고 싶다.

 

댓글