제3장 프로토콜 아키텍처
3.1 프로토콜 계층
DTP 구현은 다음 계층 구조에 따라 조직되어야 한다:
+-----------------------------------------------+
| 응용 계층 (Application Layer) |
| iFay / coFay / Personal Data Heap |
| 단말 애플리케이션 |
+-----------------------------------------------+
| DTP 프로토콜 계층 (DTP Protocol Layer)|
| DTP_Engine (DTP_Master / DTP_Slave) |
| - Agreement Manager |
| - Frame Codec |
| - DAG Manager |
| - Encryption Module |
| - Session Manager |
| - Resume Manager |
+-----------------------------------------------+
| 어댑터 계층 (Adapter Layer) |
| Transport_Adapter |
+-----------------------------------------------+
| 전송 계층 (Transport Layer) |
| BLE / WebSocket / TCP / RTSP / ... |
+-----------------------------------------------+
구현은 계층을 건너뛴 호출을 해서는 안 된다(예: 응용 계층은 어댑터 계층 인터페이스에 직접 접근해서는 안 된다).
3.2 핵심 컴포넌트
DTP_Engine은 다음 6개의 핵심 컴포넌트를 포함해야 한다:
3.2.1 Agreement Manager(협상 관리자)
Agreement Manager는 다음 능력을 제공해야 한다:
- 협상 흐름 관리: Request_Frame과 Response_Frame의 송수신을 처리한다.
- 약정 생명주기 관리: 약정의
negotiating에서terminated까지의 상태 전이를 유지한다. - 고유 식별자 생성: 달성된 각 약정에 대해 RFC 4122를 따르는 UUID v4를 Agreement_ID로 생성한다.
- 다중 약정 동시성 지원: 단일 세션에서 여러 활성 약정을 동시에 유지할 수 있어야 한다.
3.2.2 Frame Codec(프레임 인코딩/디코딩기)
Frame Codec은 다음 능력을 제공해야 한다:
- 직렬화: LogicalFrame 객체를 바이너리 바이트 시퀀스로 인코딩한다.
- 역직렬화: 바이너리 바이트 시퀀스를 LogicalFrame 객체로 디코딩한다.
- 왕복 일관성: 임의의 유효한 LogicalFrame 객체에 대해 직렬화 후 다시 역직렬화하면 원본 객체와 동등한 LogicalFrame이 생성되어야 한다.
- 포맷 출력(Pretty Printer): LogicalFrame을 사람이 읽을 수 있는 텍스트로 변환하는 능력을 제공해야 하며, 프레임 헤더의 모든 핵심 필드를 포함해야 한다.
3.2.3 DAG Manager(DAG 관리자)
DAG Manager는 다음 능력을 제공해야 한다:
- 순환 검출: Fragment가 DAG에 추가되기 전에 순환이 형성되지 않음을 검증해야 한다. 순환이 검출되면 해당 Fragment를 거부하고
DAG_CYCLE_DETECTED오류(4001)를 반환해야 한다. - 의존 해석: Fragment가 선언한 의존 대상이 아직 도착하지 않았을 때 해당 Fragment를 "의존 해석 대기" 상태로 표시하고 캐시해야 한다.
- 지연 해석: 의존 대상이 되는 Fragment가 도착하면 이전에 캐시된 Fragment를 자동으로 해석해야 한다.
- 관계 유형 지원:
derived_from,annotates,supersedes세 가지 관계 유형을 지원해야 한다.
3.2.4 Encryption Module(암호화 모듈)
Encryption Module은 다음 능력을 제공해야 한다:
- 페이로드 암호화: CAP에서 사전 협상한 키를 사용하여 Payload를 암호화한다.
- 페이로드 복호화: CAP에서 사전 협상한 키를 사용하여 수신한 Payload를 복호화한다.
- 키 준비 상태 검사: 암호화 작업 전에 CAP가 키 교환을 완료했는지 검증해야 한다.
- 암호화 메타데이터 생성: 프레임 헤더에 평문 형태로 암호화 메타데이터(알고리즘 식별자와 키 버전 번호)를 포함해야 한다.
3.2.5 Session Manager(세션 관리자)
Session Manager는 다음 능력을 제공해야 한다:
- 세션 생명주기 관리: 세션의 수립, 유지, 종료를 담당한다.
- 상태 영속화: 하위 연결이 중단될 때 세션 상태를 영속화해야 한다.
- 상태 복구: 연결이 회복되고 CAP 재검증이 통과한 후 이전 세션 상태를 복구할 수 있어야 한다.
- 타임아웃 검출: 세션 유휴 타임아웃 검출을 구현해야 한다.
3.2.6 Resume Manager(재개 관리자)
Resume Manager는 다음 능력을 제공해야 한다:
- 시퀀스 번호 할당: 전송되는 각 Fragment에 단조 증가하는 시퀀스 번호를 할당한다.
- 미확인 캐시: 수신측이 아직 확인하지 않은 Fragment를 로컬에 캐시한다.
- 단절 지점 보고: 연결이 회복될 때 상대측에 성공적으로 수신한 최고 시퀀스 번호를 보고한다.
- 캐시 관리: 캐시 용량 상한 검출을 구현해야 한다. 캐시가 상한에 도달하면 전송을 일시 중지하고
BUFFER_FULL오류(6001)를 반환해야 한다.
3.3 상태 머신
DTP_Engine은 다음 상태 머신을 구현해야 한다:
[Idle]
|
| 연결 요청 수신
v
[WaitingForCAP]
|
| CAP 검증 + 키 교환 완료
v
[SessionEstablished]
|
| Request_Frame 발의 또는 수신
v
[Negotiating]
|
| Agreement 달성
v
[Transmitting]
|
| 연결 중단
v
[Suspended]
|
| 연결 회복 + CAP 재검증
v
[Resuming]
|
| 재개 핸드셰이크 완료
v
[Transmitting]
완전한 상태 전이 규칙은 다음 표를 따라야 한다:
| 현재 상태 | 트리거 이벤트 | 목표 상태 | 비고 |
|---|---|---|---|
Idle | 연결 요청 수신 | WaitingForCAP | |
WaitingForCAP | CAP 검증 + 키 교환 완료 | SessionEstablished | |
WaitingForCAP | CAP 실패 또는 타임아웃 | Idle | 관련 자원을 해제해야 한다 |
SessionEstablished | Request_Frame 발의 또는 수신 | Negotiating | |
SessionEstablished | 세션 타임아웃 | Idle | 세션을 닫아야 한다 |
Negotiating | Agreement 달성 | Transmitting | |
Negotiating | 협상 실패 또는 거부됨 | SessionEstablished | |
Transmitting | Fragment 지속 전송 | Transmitting | 자체 루프 |
Transmitting | adjustment 유형 Request_Frame 수신 | Negotiating | |
Transmitting | 하위 연결 단절 | Suspended | 세션 상태를 영속화해야 한다 |
Transmitting | Agreement 종료 및 다른 활성 약정 없음 | SessionEstablished | |
Suspended | 연결 회복 및 CAP 재검증 통과 | Resuming | |
Suspended | 세션 타임아웃 | Idle | 자원을 해제해야 한다 |
Resuming | 재개 핸드셰이크 완료 | Transmitting | |
Resuming | 복구 실패 | Idle | 세션을 닫아야 한다 |
구현은 위 표에 나열되지 않은 상태 전이를 도입해서는 안 된다.
3.4 Transport_Adapter 인터페이스
Transport_Adapter는 다음 인터페이스를 제공해야 한다:
interface Transport_Adapter {
// 연결 관리
connect(endpoint: TransportEndpoint): Connection;
disconnect(connectionId: ConnectionID): void;
// 데이터 전송
send(connectionId: ConnectionID, data: Uint8Array): void;
onReceive(handler: (connectionId: ConnectionID, data: Uint8Array) => void): void;
// 상태 이벤트
onConnectionStateChange(handler: (connectionId: ConnectionID, state: ConnectionState) => void): void;
}
Transport_Adapter의 구체 구현은 다음 규범적 요구사항을 충족해야 한다:
send()작업은 비차단(non-blocking)이어야 한다.- 하위 연결 상태가 변경되면
onConnectionStateChange콜백을 통해 DTP_Engine에 알려야 한다. - 지원하는 각 전송 프로토콜(BLE, WebSocket, TCP, RTSP)마다 통일된 인터페이스 구현을 제공해야 한다.
- DTP_Engine이 전달한 바이너리 데이터를 수정해서는 안 된다.
3.5 주종 상호작용 시퀀스
완전한 DTP 상호작용은 다음 5단계를 따라야 한다:
단계 1: CAP 사전 처리
DTP_Engine은 CAP가 신원 검증과 키 교환을 완료할 때까지 대기해야 한다. 이 단계에서 DTP는 어떠한 데이터 프레임도 전송해서는 안 된다.
단계 2: DTP 세션 수립
CAP 완료 후, DTP_Engine은 고유한 Session_ID(UUID v4)를 생성하고 SessionEstablished 상태로 진입해야 한다.
단계 3: 협상
단계 3a(데이터 수집 협상): Master는 requestType = collection인 Request_Frame을 전송할 수 있으며, Slave는 Response_Frame을 통해 accepted, rejected 또는 counter_proposal로 회신해야 한다.
단계 3b(데이터 주입 협상): Slave는 requestType = injection인 Request_Frame을 전송할 수 있으며, Master는 Response_Frame을 통해 회신해야 한다.
단계 4: 데이터 전송
Agreement가 달성된 후, 송신측은 데이터 프레임을 통해 Fragment를 전송해야 하며, 각 Fragment는 자신이 속한 Agreement_ID를 포함해야 한다(또는 컨텍스트를 상속하기 위해 생략, 제4.5절 참조).
단계 5: 연결 중단과 회복
하위 연결이 중단되면 DTP_Engine은 Suspended 상태로 진입하고 세션을 영속화해야 한다. 연결이 회복된 후 CAP 재검증을 거쳐 Resuming 상태로 진입하여 재개 핸드셰이크를 완료해야 한다.
