문서: README 전면 개편 + 상세 ARCHITECTURE.md 신규 (산출물)

- README.md: 전체 현황 반영 개편 — 실측 지표(4,661파일·33만LOC·1,478svc·506배치·
  101테이블·1,173만행·패리티≈89%), 스택·Oracle계보, 빠른시작(빌드/거래/ISO왕복/
  클리어링파일), 포털 7화면(#해시), 저장소 구조, 문서 인덱스, 핵심 설계제약.
- docs/ARCHITECTURE.md: 상세 아키텍처 — 런타임 토폴로지(모듈서버 패턴·tmsrv/XA),
  11 도메인 분해표, 6단 XA 체인·브랜치 격리 규율, 데이터모델(정합성 불변식),
  외부연동 ISO8583 전문처리 다이어그램, 빌드시스템, Oracle 계보 3단, 관측·검증.
- 구 architecture.md → ARCHITECTURE.md 로 대체(원문 architecture.legacy.md 보존).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hyeongwoo 2026-07-23 15:06:00 +09:00
parent 12812a6c33
commit e834692abb
4 changed files with 364 additions and 292 deletions

177
docs/ARCHITECTURE.md Normal file
View file

@ -0,0 +1,177 @@
# 아키텍처 — acquire-core-x
카드 매입·정산 시스템의 런타임 구조·도메인 분해·트랜잭션 흐름·데이터 모델·외부 연동·
빌드 시스템을 기술한다. 상용 Tuxedo/Pro\*C/Oracle 스택을 오픈 등가물(Enduro/X·ECPG·
PostgreSQL)로 재현했으며, **모듈 서버 패턴**으로 1,478 온라인 서비스를 11개 모듈 서버가
광고(advertise)한다.
---
## 1. 런타임 토폴로지
```
┌─────────────────── db 컨테이너 ──────────────────┐ ┌──────────────────────── app 컨테이너 (Enduro/X) ────────────────────────┐
│ PostgreSQL 15 │ │ ndrxd (TP 모니터 데몬) │
│ - max_prepared_transactions=100 (XA prepare) │◀──┤ ├─ tmsrv XA 트랜잭션 매니저 (RM1=ECPG/PG, prepare/commit/rollback) │
│ - 101 테이블 / ~1,173만 행 │XA │ ├─ ac_svr 매입 (ACQUIRE, ACQ_CANCEL, ACQ_PRESENT … ≈133 svc) │
│ │ │ ├─ au_svr 승인/한도 (AUTH, TOKENIZE, AUTH_3DS, LIMIT_DEC …) │
└───────────────────────────────────────────────────┘ │ ├─ st_svr 정산/수수료 (SETTLE, ST_MDR, ST_FEEDECOMP, ST_FUNDING …) │
│ ├─ py_svr 지급 (PAY_APPROVE, PAY_FILEGEN …) │
브라우저 ─HTTP→ acq_httpgw :8090 ─tpcall→ ndrxd ──────▶│ ├─ lg_svr 원장 (LG_POST, LG_BALCHK, LG_SETTLEPOST …) │
(업무 포털 게이트웨이) │ ├─ cl_svr 마감 (CL_DAILY, CL_LOCK …) │
│ ├─ rc_svr 대사 (RECONCILE, RC_3WAY …) │
외부 카드망 ─ISO8583/TCP:9500─ acq_extsw ◀──socket────│ ├─ vl_svr 정합성검증 (VL_SALES, VL_RULE …) │
(acq_netdrv 왕복 드라이버) │ ├─ mm_svr 마스터 (MM_ONBOARD, MM_KYC_CHECK, MM_MERCH_REG …) │
│ ├─ mg_svr 전문게이트웨이 (MG_ISO8583, MG_CLEARING_INQ …) │
acqdrv ─tpbegin/tpcall/tpcommit→ ndrxd ──────────────▶│ └─ cm_svr 공통 (CM_SEQ, CM_LOG, CM_FX …) │
(매입 체인 드라이버) │ 각 서버가 svc/*.pgc 를 buildserver 로 링크·advertise │
│ EXEC SQL (ECPG) → libndrxxaecpg XA 스위치 → tmsrv → PostgreSQL │
└────────────────────────────────────────────────────────────────────────┘
```
- **모듈 서버 패턴**: 각 도메인이 서버 바이너리 1종(`<mod>_svr`)이고, 그 모듈의
`svc/*.pgc` 서비스 전부를 `tpsvrinit` 에서 `tpadvertise` 한다. 서버 카피 min/max=4
(모듈내 tpcall 체인 자기교착 회피).
- **XA 조율**: `tmsrv`(RM1) 가 PostgreSQL 을 XA 리소스로 관리. 서비스는
`EXEC SQL CONNECT` 를 쓰지 않는다 — XA 스위치가 `tpopen()` 에서 커넥션을 연다.
- **부팅**: `entrypoint.sh``build.sh`(빌드+ndrxconfig 생성) → `xadmin start`
`acq_httpgw`(:8090) + `acq_extsw`(:9500) 기동.
---
## 2. 도메인 분해 (11 모듈)
| 모듈 | 도메인 | 대표 서비스 | 주요 테이블 |
|---|---|---|---|
| **ac** | 매입 (acquiring) | ACQUIRE·ACQ_CANCEL·ACQ_PRESENT·ACQ_DISPUTE_*·AC_DCC | purchase·approval·ac_cancel·ac_dispute·ac_dcc·cl_presentment |
| **au** | 승인/한도 | AUTH·TOKENIZE·AUTH_3DS·AUTH_VERIFY·LIMIT_DEC·FRAUD_DETECT | au_authorization·card_limit·card_vault·au_3ds·au_verify·au_fraud_log |
| **st** | 정산/수수료 | SETTLE·ST_MDR·ST_FEEDECOMP·ST_NETSETTLE·ST_FUNDING·ST_FUNDINSTR | settlement·st_fee_component·st_net_settlement·st_merchant_funding·st_funding_instr |
| **py** | 지급 | PAY_APPROVE·PAY_FILEGEN·PAY_ACCTCHK | py_payout·py_file·py_account·py_ledger |
| **lg** | 원장 | LG_POST·LG_BALCHK·LG_SETTLEPOST·LG_REVERSE | ledger·lg_ledger_line·lg_voucher·lg_account·lg_trial_bal |
| **cl** | 마감 | CL_DAILY·CL_LOCK·CL_APPR* | cl_close_log·cl_period_lock·cl_snapshot·cl_clearing_file |
| **rc** | 대사 | RECONCILE·RC_3WAY·RC_EXCEPTION | rc_match_log·rc_diff·rc_exception·rc_3way |
| **vl** | 정합성검증 | VL_SALES·VL_RULE·VL_SUSPECT | vl_check_log·vl_rule·vl_suspect |
| **mm** | 마스터 | MM_ONBOARD·MM_KYC_CHECK·MM_MERCH_REG·MM_FEERATE_SET | merchant·mm_application·mm_kyc·mm_merch_mcc·mm_terminal·mm_mcc |
| **mg** | 전문게이트웨이 | MG_ISO8583·MG_CLEARING_INQ·MG_SEND·MG_ROUTE | mg_channel·mg_msg_log·mg_clearing·mg_netlog·mg_queue |
| **cm** | 공통 | CM_SEQ·CM_LOG·CM_FX·CM_PARAM | cm_seq·cm_log·cm_fx_rate·cm_param·cm_holiday |
모듈간 호출: 552 노드·41 모듈간 경로(`cl→vl→cm`, `ac→st→lg`, `au→mg` 등).
전체 그래프는 포털 `#arch` 또는 `tools/gen_callgraph.py` 산출.
---
## 3. 트랜잭션 흐름 — 6단 XA 체인
매입 접수 한 건이 **하나의 글로벌 트랜잭션** 안에서 6개 프로그램·6개 XA 브랜치를 관통해
2PC 로 원자 커밋한다:
```
acqdrv: tpbegin
└─ ACQUIRE (ac) 매입 접수·한도점검·purchase/approval 적재
└─ RECONCILE (rc) 대사 (미커밋 매입은 전문값으로 판정)
└─ SETTLE (st) 정산 확정·settlement 적재
└─ ST_MDR (st) MDR 수수료 원장 기표
└─ LG_SETTLEPOST (lg) 정산 원장 전기
└─ LG_BALCHK (lg) 차대변 균형 검증
acqdrv: tpcommit → tmsrv: prepare(모든 브랜치) → commit → COMMIT OK
```
### XA 브랜치 격리 규율 (핵심 제약)
`tpcall` 로 호출한 서비스는 **다른 XA 브랜치**다 → 호출측이 방금 INSERT/UPDATE 한
**미커밋 행을 볼 수 없다**. 따라서:
1. 판정에 필요한 값은 **UBF 전문(`T_*` 필드)으로 실어 보낸다.**
2. 피호출자는 `sqlcode=100`(미존재)을 정상 흐름으로 처리(FAIL 금지).
3. **owner-writes**: 각 테이블 쓰기 소유자를 하나로 둔다. 형제 브랜치가 같은 행을
UPDATE 하면 같은 트랜잭션이라 락이 안 풀려 타임아웃까지 교착 → 이 규율로 방지.
> 이 격리 의미론이 Spring `@Transactional` 경계와 달라, 잘못 옮기면 교착·이중전기가
> 그대로 재현된다 — Forge 마이그레이션의 핵심 난제.
---
## 4. 데이터 모델 (101 테이블, 도메인별)
- **거래 원장**: purchase(135만)·approval·ledger(258만)·settlement(129만)·merchant_summary
- **수수료·정산 경제**: st_fee_component(3층 분해)·st_scheme_fee·st_interchange_rate(MCC×카드×채널)·
st_net_settlement(BIN 순액)·st_merchant_funding(펀딩 항등식)·st_reserve·st_cb_fee
- **클리어링**: cl_presentment·mg_clearing(ISO8583 메시지클래스)·cl_clearing_file
- **카드보안**: card_vault(토큰·PAN미저장)·au_3ds·au_verify
- **분쟁**: ac_dispute(라이프사이클)·ac_retrieval
- **마스터·생애주기**: merchant·mm_application(온보딩)·mm_kyc·mm_terminal·mm_mcc
- **외부연동**: mg_netlog(전문왕복)·mg_channel·mg_msg_log
- **회계**: lg_account(계정과목 suspense/clearing)·lg_ledger_line(복식부기)·lg_trial_bal
- **공통**: cm_seq·cm_log(감사)·cm_fx_rate·cm_param
**정합성 불변식(실측 검증)**: 복식부기 차대변 균형(DR=CR), 수수료 3층 항등
(interchange+scheme+markup=mdr_total, 129만/129만), 펀딩 항등
(총매출−환불−채그백−수수료−리저브=입금, 35.5만/35.5만).
---
## 5. 외부 연동 · 전문처리 (ISO 8583)
실제 카드망(밴사/VAN) 인터페이스를 **실제 프로토콜**로 구현 — 마이그레이션 최난이도 계층.
```
매입사 외부 카드망 (시뮬레이터)
───── ──────────────────────
acq_netdrv acq_extsw (:9500)
│ 실매입건 → iso_build(0200) │
│ MTI + 비트맵(64bit) + DE │
│ (PAN·금액·STAN·MID·TID·통화 …) │
│ ── 길이접두 프레이밍 + hex ──socket──▶ │ iso_parse → 승인판정
│ │ iso_build(0210) DE38 승인번호·DE39 응답코드
│ ◀────────── 0210 응답 ────────socket── │
│ iso_parse → resp_code·approval·RTT │
└─ mg_netlog 적재 │
```
- **코덱**: `common/acq_iso8583.c` — 1차 비트맵 + DE 인코딩/파싱(고정·LLVAR),
`iso_build`/`iso_parse` 왕복 무손실.
- **왕복 실측**: 200건 승인 198/거절 2/오류 0, 0200→0210, 평균 RTT 0.3ms.
- **클리어링 파일**: `acq_clrfile` 가 cl_presentment → 고정폭 정산파일
(HD 헤더 + DT 레코드 + TR 트레일러·체크섬)을 `/app/out/CLR_BC_YYYYMMDD.dat` 로 생성,
cl_clearing_file 등록. 실제 SFTP 배치 파일교환 대역.
---
## 6. 빌드 시스템
`app/build.sh` 는 **모듈을 자동 발견**해 빌드하고 `ndrxconfig.xml` 을 생성한다:
1. `mkfldhdr acq.fd` — UBF 필드 헤더 생성.
2. `common/*.c``libacqcommon.a` (ISO코덱·luhn·fee·bizday·seq).
3. 모듈별: `dbio/*.pgc``lib<mod>dbio.a`, `svc/*.pgc``lib<mod>svc.a`,
`<mod>_svr.pgc``buildserver``<mod>_svr` 서버 바이너리, `batch/*.pgc` → 배치.
4. `clients/*.c``buildclient` (libacqcommon.a 링크 = ISO 코덱 사용).
5. `ndrxconfig.xml` 자동 생성 (tmsrv + 모듈서버 11종, min/max=4).
ECPG 전처리(`.pgc → .c`)는 gcc 와 동일한 `-I` 경로로 수행(copybook 해석).
새 모듈은 `app/src/<mod>/{<mod>_svr.pgc,svc,dbio,batch}` 를 두고 재빌드하면 자동 편입.
---
## 7. Oracle 계보 (As-Is → 현행 → To-Be)
```
As-Is (상용) 현행 (이 저장소) To-Be (Forge 목표)
Oracle 19c + Pro*C → PostgreSQL + ECPG → PostgreSQL + Spring Boot
Tuxedo/ATMI Enduro/X (open ATMI) @Service / REST·메시지
PL/SQL 패키지 서비스 인라인 @Transactional
legacy-oracle/*.pc app/src/**/*.pgc (target)
```
`legacy-oracle/` 의 Oracle Pro\*C/PL-SQL 역사 원본(9본, 컴파일 제외)이 현행 ECPG
이식본과 1:1 대조된다. DB 엔진 이관(Oracle→PG)은 완료 상태로 두고, Forge 는 남은
**애플리케이션 계층 이관**(ATMI·임베디드SQL·UBF·XA·ISO8583)에 집중한다.
상세: [ORACLE_PROVENANCE.md](ORACLE_PROVENANCE.md).
---
## 8. 관측 · 검증
- **포털**(`acq_httpgw` :8090): 7개 운영·분석 화면, 전부 실측 데이터 조회.
- **자동 지표**: `tools/gen_metrics.py`·`gen_callgraph.py` 가 소스에서 규모·호출그래프·
Oracle 방언을 집계 → 포털/문서 반영("이 숫자 진짜냐"에 코드로 답).
- **런타임 검증**: 6단 체인 COMMIT OK·미결 2PC 0, 체인 전수 스모크 477/477,
ISO 8583 왕복 200건, 정합성 불변식 100%.

View file

@ -0,0 +1,73 @@
# 아키텍처 — acquire-core-x
## 1. 런타임 토폴로지
```
┌──────────────────────────── app 컨테이너 (Enduro/X) ────────────────────────────┐
acqdrv ──▶ │ ndrxd (TP 모니터) │
(client) │ ├─ tmsrv (RM1 = PostgreSQL, XA 조율: prepare/commit/rollback) │
tpbegin │ ├─ cconfsrv / tpevsrv / cpmsrv (시스템) │
tpcall ───▶│ ├─ acq_online : ACQUIRE, CANCEL, CORRECT … (매입 온라인 tpservice) │
tpcommit │ ├─ rc_online : RECONCILE, REMATCH … (대사) │
│ ├─ st_online : SETTLE, FEECALC … (정산·수수료) │
│ └─ *_batch : 커서 배치 (마감/집계) │
│ │ EXEC SQL (ECPG, XA 스위치 libndrxxaecpg) │
└────────┼──────────────────────────────────────────────────────────────────────┘
PostgreSQL (db 컨테이너, max_prepared_transactions=100)
merchant / purchase / approval / settlement / ledger …
```
## 2. 트랜잭션 모델 (핵심 — 진짜 XA 2PC)
클라이언트가 `tpbegin()`으로 **글로벌 트랜잭션** 시작 → `tpcall("ACQUIRE")`
ACQUIRE 서비스가 `EXEC SQL INSERT purchase` (자동으로 XA `xa_start`로 글로벌 tx에 편입) →
같은 tx 안에서 `tpcall("RECONCILE")``tpcall("SETTLE")` → 클라이언트 `tpcommit()`
`tmsrv`**xa_prepare → xa_commit** (2단계 커밋)으로 모든 서비스의 DB 작업을 원자적으로 확정.
`tpabort()` 시 전부 롤백 → DB에 아무것도 남지 않음.
이것이 레거시 Tuxedo/ProFrame 시스템의 표준 패턴이며, 마이그레이션 도구가 실제로 마주할 idiom이다.
## 3. 모듈 (11) — acquire-core-full 도메인 계승
| 코드 | 모듈 | 대표 서비스/배치 |
|---|---|---|
| ac | 매입(acquiring) | ACQUIRE, DDC, EDI청구, 취소/정정, 부분/할부/해외, 미매입추출 |
| au | 승인/한도 | AUTH중계, 한도차감/복원, 대행승인, 이상거래탐지 |
| rc | 대사(reconcile) | RECONCILE(승인-매입-입금 3-way), tolerance, 재대사, 반송 |
| st | 정산/수수료 | SETTLE, MDR/VAN수수료, netting, 원천징수, T+n |
| py | 지급(payment) | 지급파일생성, 결과반영, 재처리, 이체연계 |
| lg | 원장(ledger) | 복식전표, 잔액검증, 전기, 역분개, 마감연계 |
| cl | 마감(closing) | 일/월/분기 마감, 스냅샷, 재마감 |
| mm | 마스터 | 가맹점/수수료율/BIN/한도 마스터 |
| vl | 정합성검증 | 매출/카드/한도/금액 정합성, 이상탐지 |
| mg | 전문게이트웨이 | 전문 수/송신, ISO8583 파싱, STAN채번, 라우팅 |
| cm | 공통 | 공통코드, 영업일, 금액유틸, 채번, Luhn/CRC |
## 4. 파일 아키타입 (2000본 구성 근거)
| 아키타입 | 형식 | 개수(목표) | 성격 |
|---|---|---|---|
| online service | `.pgc` (`tpservice` + EXEC SQL) | ~350 | tpcall 진입점, XA |
| dbio | `.pgc` (EXEC SQL 함수) | ~300 | 서비스가 호출하는 DB 접근 계층 |
| batch | `.pgc`/`.c` (`main`, 커서) | ~180 | 마감/집계, XA |
| common util | `.c` | ~120 | leaf 유틸 |
| copybook/header | `.h`, UBF 필드테이블 `.fd` | ~600 | 구조체·FML 필드 |
| config | ndrxconfig.xml, app.ini, UBFTAB, XA RM | ~50 | TP/서버/RM 설정 |
| shell/run | `.sh` | ~180 | 기동/배치 스크립트 |
| schema/seed | `.sql` | ~50 | 스키마·시드 |
| **합계** | | **~2,030** | |
## 5. 마이그레이션 타깃 매핑 (포지 → Spring Boot)
| 레거시 (Enduro/X) | Spring Boot |
|---|---|
| `tpservice`/`tpadvertise` | `@Service` 빈 + 메서드 |
| `tpcall(SVC)` | 빈 주입 호출 (동기) |
| UBF `Bget/Bchg` (전문버퍼) | DTO/record |
| `tpbegin/tpcommit` (XA) | `@Transactional` (JTA/단일DB) |
| `EXEC SQL` / ECPG dbio | MyBatis/JPA repository |
| 커서 배치 | Spring Batch |
| 고정길이 전문 | 코덱(fixed-length) |
| ndrxconfig 서비스 등록 | 컴포넌트 스캔/라우팅 |
| `schema.sql` | Flyway |

View file

@ -1,73 +1,4 @@
# 아키텍처 — acquire-core-x
# (구) 아키텍처 문서
## 1. 런타임 토폴로지
```
┌──────────────────────────── app 컨테이너 (Enduro/X) ────────────────────────────┐
acqdrv ──▶ │ ndrxd (TP 모니터) │
(client) │ ├─ tmsrv (RM1 = PostgreSQL, XA 조율: prepare/commit/rollback) │
tpbegin │ ├─ cconfsrv / tpevsrv / cpmsrv (시스템) │
tpcall ───▶│ ├─ acq_online : ACQUIRE, CANCEL, CORRECT … (매입 온라인 tpservice) │
tpcommit │ ├─ rc_online : RECONCILE, REMATCH … (대사) │
│ ├─ st_online : SETTLE, FEECALC … (정산·수수료) │
│ └─ *_batch : 커서 배치 (마감/집계) │
│ │ EXEC SQL (ECPG, XA 스위치 libndrxxaecpg) │
└────────┼──────────────────────────────────────────────────────────────────────┘
PostgreSQL (db 컨테이너, max_prepared_transactions=100)
merchant / purchase / approval / settlement / ledger …
```
## 2. 트랜잭션 모델 (핵심 — 진짜 XA 2PC)
클라이언트가 `tpbegin()`으로 **글로벌 트랜잭션** 시작 → `tpcall("ACQUIRE")`
ACQUIRE 서비스가 `EXEC SQL INSERT purchase` (자동으로 XA `xa_start`로 글로벌 tx에 편입) →
같은 tx 안에서 `tpcall("RECONCILE")``tpcall("SETTLE")` → 클라이언트 `tpcommit()`
`tmsrv`**xa_prepare → xa_commit** (2단계 커밋)으로 모든 서비스의 DB 작업을 원자적으로 확정.
`tpabort()` 시 전부 롤백 → DB에 아무것도 남지 않음.
이것이 레거시 Tuxedo/ProFrame 시스템의 표준 패턴이며, 마이그레이션 도구가 실제로 마주할 idiom이다.
## 3. 모듈 (11) — acquire-core-full 도메인 계승
| 코드 | 모듈 | 대표 서비스/배치 |
|---|---|---|
| ac | 매입(acquiring) | ACQUIRE, DDC, EDI청구, 취소/정정, 부분/할부/해외, 미매입추출 |
| au | 승인/한도 | AUTH중계, 한도차감/복원, 대행승인, 이상거래탐지 |
| rc | 대사(reconcile) | RECONCILE(승인-매입-입금 3-way), tolerance, 재대사, 반송 |
| st | 정산/수수료 | SETTLE, MDR/VAN수수료, netting, 원천징수, T+n |
| py | 지급(payment) | 지급파일생성, 결과반영, 재처리, 이체연계 |
| lg | 원장(ledger) | 복식전표, 잔액검증, 전기, 역분개, 마감연계 |
| cl | 마감(closing) | 일/월/분기 마감, 스냅샷, 재마감 |
| mm | 마스터 | 가맹점/수수료율/BIN/한도 마스터 |
| vl | 정합성검증 | 매출/카드/한도/금액 정합성, 이상탐지 |
| mg | 전문게이트웨이 | 전문 수/송신, ISO8583 파싱, STAN채번, 라우팅 |
| cm | 공통 | 공통코드, 영업일, 금액유틸, 채번, Luhn/CRC |
## 4. 파일 아키타입 (2000본 구성 근거)
| 아키타입 | 형식 | 개수(목표) | 성격 |
|---|---|---|---|
| online service | `.pgc` (`tpservice` + EXEC SQL) | ~350 | tpcall 진입점, XA |
| dbio | `.pgc` (EXEC SQL 함수) | ~300 | 서비스가 호출하는 DB 접근 계층 |
| batch | `.pgc`/`.c` (`main`, 커서) | ~180 | 마감/집계, XA |
| common util | `.c` | ~120 | leaf 유틸 |
| copybook/header | `.h`, UBF 필드테이블 `.fd` | ~600 | 구조체·FML 필드 |
| config | ndrxconfig.xml, app.ini, UBFTAB, XA RM | ~50 | TP/서버/RM 설정 |
| shell/run | `.sh` | ~180 | 기동/배치 스크립트 |
| schema/seed | `.sql` | ~50 | 스키마·시드 |
| **합계** | | **~2,030** | |
## 5. 마이그레이션 타깃 매핑 (포지 → Spring Boot)
| 레거시 (Enduro/X) | Spring Boot |
|---|---|
| `tpservice`/`tpadvertise` | `@Service` 빈 + 메서드 |
| `tpcall(SVC)` | 빈 주입 호출 (동기) |
| UBF `Bget/Bchg` (전문버퍼) | DTO/record |
| `tpbegin/tpcommit` (XA) | `@Transactional` (JTA/단일DB) |
| `EXEC SQL` / ECPG dbio | MyBatis/JPA repository |
| 커서 배치 | Spring Batch |
| 고정길이 전문 | 코덱(fixed-length) |
| ndrxconfig 서비스 등록 | 컴포넌트 스캔/라우팅 |
| `schema.sql` | Flyway |
> 이 문서는 초기 토폴로지 기준이라 낡았습니다. 최신 상세 문서는 **[ARCHITECTURE.md](ARCHITECTURE.md)** 를 참조하세요.
> (원문은 참고용으로 architecture.legacy.md 에 보존)