LabHub

블로그

IB 시스템 아키텍처 — 딜 파이프라인부터 신디케이션까지

한국어English日本語

들어가며

은행 IT를 이야기할 때 대부분의 자료는 리테일 뱅킹(수신/여신/계정계)에 집중되어 있습니다. 그런데 투자은행(IB) 부문의 시스템은 전혀 다른 모양을 하고 있습니다. 계좌 수백만 개를 다루는 대신 딜(deal) 수십 건을 다루고, 초당 수천 건의 거래 대신 수개월짜리 워크플로를 다룹니다. 트랜잭션 볼륨은 작지만 딜 하나의 금액이 수천억 원에서 수조 원에 이르고, 미공개중요정보(MNPI)가 흐르는 곳이기 때문에 통제 실패의 비용이 어마어마합니다.

이 글은 IB 부문 시스템을 처음 설계하거나 유지보수하게 된 엔지니어를 위해, 업무 도메인 맵부터 딜 라이프사이클, 컨플릭트 체크, 차이니즈 월(정보교류차단)의 시스템적 구현, 북빌딩, 신디케이트론 관리, 문서 관리, 규제 보고까지를 하나의 그림으로 정리합니다. 한국 자본시장(자본시장법, 금융투자업규정) 맥락도 함께 다룹니다.

참고로 이 글은 시스템 설계 관점의 기술 자료이며, 투자 자문이나 법률 자문이 아닙니다. 규제 관련 내용은 반드시 소속 기관의 준법감시 부서와 확인하시기 바랍니다.

IB 업무 도메인 맵

IB 부문은 크게 다섯 개 도메인으로 나눌 수 있습니다.

+---------------------------------------------------------------+
|                     Investment Banking Division                |
|                                                               |
|  +-----------+  +-----------+  +-----------+                  |
|  |   ECM     |  |   DCM     |  |   M&A     |                  |
|  | (주식자본  |  | (부채자본  |  | (인수합병  |                  |
|  |  시장)    |  |  시장)    |  |  자문)    |                  |
|  | IPO/유상  |  | 회사채/   |  | 매수/매도  |                  |
|  | 증자/CB   |  | ABS/MTN  |  | 자문, 공정 |                  |
|  +-----------+  +-----------+  +-----------+                  |
|                                                               |
|  +------------------+  +---------------------------+          |
|  | Acquisition      |  | Loan Syndication          |          |
|  | Finance (인수금융)|  | (신디케이트론 주선/참여)     |          |
|  | LBO/브리지론     |  | 주선(Arranger)/대리(Agent) |          |
|  +------------------+  +---------------------------+          |
|                                                               |
|  공통 인프라: 딜 파이프라인 / 컨플릭트 체크 / 차이니즈 월         |
|              문서 관리 / 내부자 리스트 / 규제 보고 / CRM        |
+---------------------------------------------------------------+

각 도메인의 시스템 요구사항을 비교하면 다음과 같습니다.

도메인핵심 산출물핵심 시스템 기능라이프사이클 길이
ECMIPO, 유상증자, 전환사채북빌딩, 수요예측, 배정3~12개월
DCM회사채, ABS, MTN 프로그램발행 일정 관리, 프라이싱, 수요예측1~6개월
M&A매수/매도 자문, 밸류에이션딜룸(VDR), 문서 관리, 컨플릭트 체크6~18개월
인수금융LBO 대출, 브리지론한도/익스포저 관리, 신용 승인 워크플로3~9개월
신디케이션신디케이트론 주선참여기관 관리, 수수료 배분, 에이전트 업무대출 만기까지 수년

리테일과 가장 다른 점은 모든 도메인이 딜 단위로 움직인다는 것입니다. 시스템의 1급 객체(first-class object)는 계좌가 아니라 딜이고, 딜에는 팀, 문서, 승인, 정보 접근 권한이 모두 매달립니다.

딜 라이프사이클과 딜 파이프라인 관리

딜은 보통 다음 단계를 거칩니다.

[Origination]      [Execution]                    [Closing]      [Post-Close]
     |                  |                             |               |
 아이디어/피칭 --> 위임(Mandate) --> 실사(DD) --> 마케팅/북빌딩 --> 프라이싱
     |             |                |              |               |
     v             v                v              v               v
 컨플릭트 체크   엔게이지먼트 레터   딜룸 오픈      내부자 리스트    배정/결제
 (사전 스크리닝)  (계약 체결)       (VDR/문서)     (확대 관리)     수수료 정산
                                                                  |
                                                                  v
                                                            사후 관리/리그테이블

딜 파이프라인 관리 시스템(Deal Pipeline Management)의 핵심 요구사항은 다음과 같습니다.

단계 전이를 코드로 강제하는 것이 중요합니다. 스프레드시트로 파이프라인을 관리하면 컨플릭트 체크를 우회한 딜이 반드시 생깁니다.

컨플릭트 체크 — 딜을 받기 전에 막아야 한다

컨플릭트 체크(conflict check)는 새 딜을 수임하기 전에 기존 딜·고객 관계와 이해상충이 없는지 확인하는 절차입니다. 예를 들어 A사 매각 자문을 수임하려는데 이미 B사의 A사 인수 자문을 맡고 있다면 양쪽을 동시에 수임할 수 없습니다.

시스템 관점에서 컨플릭트 체크는 딜 생성 워크플로의 게이트입니다.

-- 컨플릭트 체크의 핵심 질의: 동일/관련 대상 기업에 대해
-- 활성 상태의 딜이 존재하는지 탐지
SELECT d.deal_id,
       d.deal_type,
       d.stage,
       c.company_name,
       r.relation_type        -- TARGET / ACQUIRER / ISSUER / SPONSOR
FROM   deal d
JOIN   deal_company_rel r ON r.deal_id = d.deal_id
JOIN   company c          ON c.company_id = r.company_id
WHERE  d.stage NOT IN ('CLOSED', 'DEAD')
AND    c.company_group_id IN (
         -- 신규 딜 대상 기업과 같은 기업집단까지 확장
         SELECT company_group_id FROM company
         WHERE  company_id = :new_deal_target_id
       );

실무에서 주의할 점:

차이니즈 월(정보교류차단)의 시스템적 구현

차이니즈 월은 IB 부문(private side, MNPI 보유)과 리서치/세일즈앤트레이딩 부문(public side) 사이의 정보 흐름을 차단하는 통제입니다. 한국에서는 자본시장법상 정보교류차단(이해상충 방지) 규제로 구현이 의무화되어 있습니다. 시스템적으로는 세 개 레이어로 구현합니다.

레이어 1 — 권한(접근 통제)

딜 정보는 기본적으로 deny-all이고, 딜 팀 멤버십에 의해서만 열립니다.

# 딜 단위 접근 정책 예시 (정책 엔진 입력)
policy:
  resource: deal/PRJ-FALCON
  default: deny
  rules:
    - effect: allow
      subjects:
        - group: deal-team/PRJ-FALCON        # 딜 팀 멤버
        - group: compliance-surveillance      # 준법감시(상시 열람권)
      actions: [read, write]
    - effect: allow
      subjects:
        - group: ib-management                # 부문 경영진
      actions: [read-summary]                 # 요약 정보만, 문서 접근 불가
    - effect: deny
      subjects:
        - group: research                     # 리서치 부문은 명시적 차단
        - group: sales-trading
      actions: [read, write, read-summary]
      override: true                          # allow보다 우선

핵심 설계 원칙:

레이어 2 — 워터마킹

딜 문서는 유출 시 출처를 추적할 수 있어야 합니다. 다운로드/열람 시점에 사용자별 워터마크를 동적으로 입힙니다.

# 문서 열람 시 동적 워터마크 적용 (개념 예시)
from pypdf import PdfReader, PdfWriter
from reportlab.pdfgen import canvas
from io import BytesIO
import datetime

def apply_watermark(src_pdf: bytes, user_id: str, deal_code: str) -> bytes:
    stamp_buf = BytesIO()
    c = canvas.Canvas(stamp_buf)
    text = f"{deal_code} / {user_id} / {datetime.datetime.utcnow().isoformat()}"
    c.setFont("Helvetica", 7)
    c.setFillColorRGB(0.6, 0.6, 0.6, alpha=0.4)
    # 대각선 반복 워터마크
    c.saveState()
    c.translate(300, 400)
    c.rotate(45)
    for y in range(-400, 400, 60):
        c.drawString(-250, y, text)
    c.restoreState()
    c.save()
    stamp_buf.seek(0)

    stamp = PdfReader(stamp_buf).pages[0]
    reader = PdfReader(BytesIO(src_pdf))
    writer = PdfWriter()
    for page in reader.pages:
        page.merge_page(stamp)
        writer.add_page(page)
    out = BytesIO()
    writer.write(out)
    return out.getvalue()

가시적 워터마크 외에 메타데이터/스테가노그래피 기반 비가시 워터마크를 병행하는 기관도 있습니다. 중요한 것은 워터마크 적용 이벤트 자체가 접근 로그와 연결되어야 한다는 점입니다.

레이어 3 — 로깅과 감시

MNPI 관리와 내부자 리스트

내부자 리스트(insider list)는 특정 딜의 미공개중요정보에 접근한 사람의 명부입니다. EU MAR(시장남용규제)는 발행사와 자문사에 내부자 리스트 작성을 명시적으로 의무화하고 있고, 한국에서도 미공개중요정보 이용행위 규제(자본시장법 제174조) 대응을 위해 동일한 통제를 운영합니다.

CREATE TABLE insider_list_entry (
    entry_id        BIGINT PRIMARY KEY,
    deal_id         BIGINT NOT NULL REFERENCES deal(deal_id),
    person_id       BIGINT NOT NULL REFERENCES person(person_id),
    reason          VARCHAR(200) NOT NULL,   -- 등재 사유 (딜 팀, 월 크로싱, 외부 자문 등)
    obtained_at     TIMESTAMP NOT NULL,      -- MNPI 접근 시작 시각
    notified_at     TIMESTAMP,               -- 본인 통지 시각 (의무 고지)
    removed_at      TIMESTAMP,               -- 리스트 제외 시각
    removed_reason  VARCHAR(200),
    created_by      VARCHAR(50) NOT NULL,
    created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- 내부자 리스트는 정정 이력까지 보존해야 하므로 UPDATE 금지,
-- 변경은 신규 row + 이전 row 무효화로 처리 (bitemporal 패턴)
CREATE INDEX idx_insider_deal ON insider_list_entry (deal_id, obtained_at);
CREATE INDEX idx_insider_person ON insider_list_entry (person_id, obtained_at);

운영 포인트:

북빌딩 시스템 — 수요예측과 배정

ECM/DCM 딜의 하이라이트는 북빌딩(bookbuilding)입니다. 기관투자자로부터 수요(가격, 수량)를 접수해 수요 곡선을 만들고, 발행가를 결정한 뒤 물량을 배정합니다. 한국 IPO에서는 기관 수요예측이 금융투자협회 규정에 따라 운영됩니다.

북빌딩 시스템의 핵심 모델은 주문(order)과 배정(allocation)입니다.

# 단순화한 배정 엔진 개념 예시
# 실제 배정은 정성 평가(앵커 투자자, 장기 보유 성향)가 결합된
# 심사 절차이며, 아래는 비례 배정 골격만 보여준다.
from dataclasses import dataclass

@dataclass
class Order:
    investor_id: str
    quantity: int          # 신청 수량
    limit_price: int       # 희망 가격 (원), 시장가는 None 대신 최고가 처리
    investor_grade: str    # 내부 등급: ANCHOR / LONG_ONLY / HEDGE / RETAIL_INST

GRADE_WEIGHT = {"ANCHOR": 1.5, "LONG_ONLY": 1.2, "HEDGE": 0.8, "RETAIL_INST": 1.0}

def build_demand_curve(orders, price_grid):
    """가격대별 누적 수요 곡선: 해당 가격 이상을 써낸 주문의 합"""
    return {
        p: sum(o.quantity for o in orders if o.limit_price >= p)
        for p in price_grid
    }

def allocate(orders, final_price, total_shares):
    eligible = [o for o in orders if o.limit_price >= final_price]
    weighted = sum(o.quantity * GRADE_WEIGHT[o.investor_grade] for o in eligible)
    allocations = {}
    remaining = total_shares
    for o in sorted(eligible, key=lambda x: GRADE_WEIGHT[x.investor_grade], reverse=True):
        share = int(total_shares * o.quantity * GRADE_WEIGHT[o.investor_grade] / weighted)
        share = min(share, o.quantity, remaining)
        allocations[o.investor_id] = share
        remaining -= share
    # 잔여 물량은 라운딩 보정 규칙으로 재배분 (생략)
    return allocations, remaining

시스템 설계에서 중요한 것들:

신디케이트론 관리 — 수년짜리 운영 시스템

신디케이트론은 여러 금융기관이 하나의 차주에게 공동으로 대출하는 구조입니다. 주선기관(arranger)이 딜을 조직하고, 에이전트 뱅크(agent bank)가 대출 기간 내내 이자 계산, 원리금 배분, 차주와 참여기관 간 통지를 담당합니다. LMA(유럽)/LSTA(미국) 표준 계약이 사실상의 업계 표준입니다.

시스템이 관리해야 하는 핵심 데이터:

영역내용
퍼실리티 구조Term Loan A/B, RCF 등 트랜치 구조와 한도
참여기관 지분기관별 커밋먼트 금액, 양도(assignment) 이력
인출/상환인출 요청, 이자 기간, 상환 스케줄
이자/수수료기준금리+마진, 약정수수료, 주선수수료, 에이전트수수료
배분이자/원금/수수료의 참여기관별 안분 계산과 지급

수수료 배분 계산 예시:

# 이자 기간 종료 시 참여기관별 배분 계산 (개념 예시)
from decimal import Decimal, ROUND_HALF_UP

def distribute_interest(total_interest: Decimal, participations: dict) -> dict:
    """participations: {institution_id: outstanding_amount}"""
    total_outstanding = sum(participations.values())
    result = {}
    allocated = Decimal("0")
    items = sorted(participations.items())
    for inst, amount in items[:-1]:
        share = (total_interest * amount / total_outstanding).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP)
        result[inst] = share
        allocated += share
    # 마지막 기관이 라운딩 잔차를 흡수 — 합계가 반드시 일치해야 함
    last_inst = items[-1][0]
    result[last_inst] = total_interest - allocated
    assert sum(result.values()) == total_interest
    return result

운영상 가장 까다로운 부분:

인수금융 한도와 익스포저 관리

인수금융(LBO 파이낸싱, 브리지론)은 은행 자기자본이 직접 노출되는 영역이라 한도 관리가 핵심입니다.

익스포저 집계 개념:
  총 익스포저(기업집단 G)
    = Σ 실행 잔액(집단 내 모든 차주)
    + Σ 미인출 약정 × 신용환산율(CCF)
    + Σ 언더라이팅 포지션(셀다운 미완료분)
  한도 체크는 신규 딜 승인 시점 + 일배치 양쪽에서 수행

딜 문서 관리 — 버전과 서명

M&A와 신디케이션은 문서가 산출물의 거의 전부입니다. 요구사항:

규제 보고와 한국 자본시장 맥락

한국에서 IB 업무는 자본시장과 금융투자업에 관한 법률(자본시장법)과 금융투자업규정의 적용을 받습니다. 시스템 관점에서 자주 만나는 보고/공시 의무는 다음과 같습니다(정확한 요건은 시점·기관 유형에 따라 다르므로 준법 부서 확인 필수).

설계 시사점은 명확합니다. 규제 보고를 별도 시스템의 일로 미루지 말고, 딜 시스템의 데이터 모델 자체가 보고 가능한 형태(불변 이력, 시점 조회)로 만들어져야 합니다.

CRM과의 통합

IB의 CRM은 영업 CRM과 결이 다릅니다. 커버리지 뱅커가 고객사 경영진과의 미팅, 피칭 이력, 딜 기회를 관리합니다. 통합 포인트:

데이터 모델 예시

핵심 엔터티의 관계를 ERD로 정리하면 다음과 같습니다.

+--------------+        +-------------------+        +--------------+
|   COMPANY    |        |       DEAL        |        |    PERSON    |
+--------------+        +-------------------+        +--------------+
| company_id PK|---+    | deal_id PK        |    +---| person_id PK |
| group_id     |   |    | code_name UQ      |    |   | dept         |
| lei          |   +--<-| deal_type         |    |   | side (pub/pr)|
| name         |        | stage             |    |   +--------------+
+--------------+        | expected_fee      |    |
       ^                | probability       |    |
       |                +-------------------+    |
       |                  |       |      |       |
+------+--------+         |       |      |       |
| DEAL_COMPANY_ |---------+       |      +-------+----------+
| REL           |                 |              |          |
| (TARGET/      |        +--------+-----+  +-----+------+  +-+----------+
|  ACQUIRER/    |        | DEAL_DOCUMENT|  | DEAL_TEAM  |  | INSIDER_   |
|  ISSUER...)   |        +--------------+  +------------+  | LIST_ENTRY |
+---------------+        | doc_id PK    |  | deal_id FK |  +------------+
                         | deal_id FK   |  | person_id  |  | deal_id FK |
+---------------+        | version      |  | role       |  | person_id  |
| ORDER (북빌딩) |        | status       |  | granted_at |  | obtained_at|
+---------------+        | sha256       |  | revoked_at |  | removed_at |
| order_id PK   |        +--------------+  +------------+  +------------+
| deal_id FK    |
| investor_id   |        +-----------------+   +----------------------+
| qty / price   |        | FACILITY (론)    |   | PARTICIPATION (지분) |
| superseded_by |        +-----------------+   +----------------------+
+---------------+        | facility_id PK  |---| facility_id FK       |
                         | deal_id FK      |   | institution_id       |
+---------------+        | tranche_type    |   | commitment_amt       |
| ALLOCATION    |        | limit_amt       |   | effective_from/to    |
+---------------+        | margin_bps      |   +----------------------+
| order_id FK   |        +-----------------+
| alloc_qty     |
| adjusted_by   |
+---------------+

모델링 원칙:

워크플로 엔진 설계

딜 라이프사이클, 월 크로싱, 신용 승인, 서명 추적은 전부 워크플로입니다. 직접 만들든 엔진(Camunda, Temporal 등)을 쓰든, IB 도메인에서 요구되는 특성은 다음과 같습니다.

# 딜 단계 전이를 명시적 상태기계로 강제하는 예시
from enum import Enum

class Stage(Enum):
    PITCH = "PITCH"
    CONFLICT_CHECK = "CONFLICT_CHECK"
    MANDATED = "MANDATED"
    DUE_DILIGENCE = "DUE_DILIGENCE"
    MARKETING = "MARKETING"
    PRICING = "PRICING"
    CLOSED = "CLOSED"
    DEAD = "DEAD"

TRANSITIONS = {
    Stage.PITCH:          {Stage.CONFLICT_CHECK, Stage.DEAD},
    Stage.CONFLICT_CHECK: {Stage.MANDATED, Stage.DEAD},
    Stage.MANDATED:       {Stage.DUE_DILIGENCE, Stage.DEAD},
    Stage.DUE_DILIGENCE:  {Stage.MARKETING, Stage.DEAD},
    Stage.MARKETING:      {Stage.PRICING, Stage.DEAD},
    Stage.PRICING:        {Stage.CLOSED, Stage.DEAD},
}

REQUIRED_APPROVALS = {
    (Stage.PITCH, Stage.CONFLICT_CHECK): ["team_head"],
    (Stage.CONFLICT_CHECK, Stage.MANDATED): ["compliance", "business_head"],
    (Stage.MARKETING, Stage.PRICING): ["syndicate_head", "compliance"],
}

def transition(deal, target: Stage, approvals: set, actor: str):
    if target not in TRANSITIONS[deal.stage]:
        raise ValueError(f"illegal transition {deal.stage} -> {target}")
    needed = set(REQUIRED_APPROVALS.get((deal.stage, target), []))
    if not needed.issubset(approvals):
        raise PermissionError(f"missing approvals: {needed - approvals}")
    audit_log(deal.deal_id, deal.stage, target, actor, approvals)
    deal.stage = target

추가로 필요한 것:

함정과 안티패턴

구축 체크리스트

마치며

IB 시스템의 본질은 거래 처리가 아니라 통제된 협업입니다. 소수의 고액 딜을 둘러싸고 정보 접근, 승인, 기록이 정확하게 통제되는 것이 시스템의 존재 이유입니다. 기능 요구사항(파이프라인, 북빌딩, 론 관리)은 비교적 평이하지만, 비기능 요구사항 — 불변 이력, 시점 조회, 권한의 기본 차단, 자동 회수 — 이 설계의 성패를 가릅니다. 새로 시스템을 만든다면 화면보다 데이터 모델과 권한 모델부터 설계하시기를 권합니다.

다시 한번, 본 글은 기술 자료이며 투자·법률 자문이 아닙니다.

참고 자료

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다