제9장 오류 처리
9.1 오류 처리 모델
DTP 구현은 "검출-통지-회복"의 3단계 오류 처리 모델을 따라야 한다:
- 검출: 예외 상황을 식별한다.
- 통지: 상대측 또는 상위에 오류 정보를 전송한다.
- 회복: 오류 유형에 따라 회복 조치를 취한다.
구현은 오류를 검출했을 때 통지 없이 조용히 프레임을 폐기해서는 안 된다.
9.2 오류 코드 체계
DTP는 다음 오류 코드 체계를 사용해야 한다. 오류 코드는 음이 아닌 정수여야 하며, 기능 모듈에 따라 8개 범위로 나뉜다:
| 범위 | 분류 | 처리 정책 |
|---|---|---|
| 1xxx | 프레임 처리 오류 | 프레임 폐기 + 송신측 통지 |
| 2xxx | 암호화 오류 | 프레임 폐기 + 송신측 통지 + 키 재협상 트리거 가능 |
| 3xxx | 약정 오류 | Fragment 폐기 + 송신측 통지 + 재협상 트리거 가능 |
| 4xxx | DAG 오류 | Fragment 거부 + 송신측 통지 또는 캐시 대기 |
| 5xxx | 세션 오류 | 세션 회복 시도 + 실패 시 닫고 상위 통지 |
| 6xxx | 재개 오류 | 전송 일시 중지 + 상위 애플리케이션 통지 |
| 7xxx | 버전 오류 | 버전 비호환 통지 전송 + 다운그레이드 처리 시도 |
| 8xxx | 권한 오류 | 작업 거부 + 요청측 통지 |
9.3 오류 코드 정의
구현은 다음 표준화된 오류 코드 정의를 사용해야 한다. 나열되지 않은 오류 코드를 사용해서는 안 된다. 확장이 필요한 경우 본 사양의 후속 버전에서 정의해야 한다.
9.3.1 프레임 처리 오류(1xxx)
| 오류 코드 | 이름 | 트리거 조건 |
|---|---|---|
| 1001 | FRAME_DESERIALIZATION_FAILED | 수신한 바이너리 데이터를 LogicalFrame으로 역직렬화할 수 없음 |
| 1002 | FRAME_INVALID_FORMAT | LogicalFrame 구조가 유효하지 않거나 필수 필드 누락 |
9.3.2 암호화 오류(2xxx)
| 오류 코드 | 이름 | 트리거 조건 |
|---|---|---|
| 2001 | DECRYPTION_FAILED | Payload 복호화 실패(잘못된 키 또는 데이터 손상) |
| 2002 | KEY_NOT_READY | CAP 키 교환이 아직 완료되지 않아 데이터 전송 거부 |
9.3.3 약정 오류(3xxx)
| 오류 코드 | 이름 | 트리거 조건 |
|---|---|---|
| 3001 | AGREEMENT_NOT_FOUND | Fragment가 알 수 없는 Agreement_ID를 참조 |
| 3002 | AGREEMENT_EXPIRED | 참조된 약정이 만료됨(validityPeriod 초과) |
| 3003 | AGREEMENT_NEGOTIATION_FAILED | 협상을 완료할 수 없음(타임아웃, 상대측의 재협상 거부 등) |
9.3.4 DAG 오류(4xxx)
| 오류 코드 | 이름 | 트리거 조건 |
|---|---|---|
| 4001 | DAG_CYCLE_DETECTED | Fragment 추가 시 DAG 순환이 형성됨 |
| 4002 | DAG_DEPENDENCY_UNRESOLVED | Fragment 의존이 캐시 타임아웃 내에 해석되지 않음 |
9.3.5 세션 오류(5xxx)
| 오류 코드 | 이름 | 트리거 조건 |
|---|---|---|
| 5001 | SESSION_NOT_FOUND | 참조된 Session_ID가 존재하지 않음 |
| 5002 | SESSION_TIMEOUT | 세션이 비활동으로 인해 타임아웃 |
| 5003 | SESSION_RESTORE_FAILED | 재연결 후 세션 회복 실패 |
9.3.6 재개 오류(6xxx)
| 오류 코드 | 이름 | 트리거 조건 |
|---|---|---|
| 6001 | BUFFER_FULL | 송신측 미확인 Fragment 캐시가 용량 상한에 도달 |
| 6002 | RETRANSMISSION_TIMEOUT | Fragment 재전송이 확인응답 없이 타임아웃(재전송 횟수 소진) |
9.3.7 버전 오류(7xxx)
| 오류 코드 | 이름 | 트리거 조건 |
|---|---|---|
| 7001 | VERSION_INCOMPATIBLE | 수신한 프레임의 프로토콜 버전이 자체 지원 버전보다 높음 |
9.3.8 권한 오류(8xxx)
| 오류 코드 | 이름 | 트리거 조건 |
|---|---|---|
| 8001 | PERMISSION_DENIED | 현재 역할이 해당 작업을 수행할 수 없음 |
| 8002 | OBSERVER_WRITE_DENIED | 관찰자가 쓰기 작업을 시도(관찰자는 읽기 전용) |
9.4 오류 고유성
구현은 오류 코드의 고유성을 보장해야 한다:
- 각 오류 코드는 고유한 오류 유형에 대응해야 한다.
- 서로 다른 오류 유형은 동일한 오류 코드를 공유해서는 안 된다.
- 구현은 본 사양에서 이미 할당한 오류 코드를 재정의해서는 안 된다.
9.5 오류 통지 메커니즘
9.5.1 ErrorNotificationFrame
오류 통지는 ControlFrame을 통해 전달되어야 하며, 정의는 다음과 같다:
interface ErrorNotificationFrame {
frameType: "control";
controlType: "error_notification";
errorCode: DTPErrorCode;
errorMessage: string;
relatedFrameId?: FragmentID;
relatedAgreementId?: AgreementID;
details?: Record<string, unknown>;
}
| 필드 | 규범적 요구사항 |
|---|---|
frameType | "control"이어야 한다 |
controlType | "error_notification"이어야 한다 |
errorCode | 9.3절에서 정의한 오류 코드 중 하나여야 한다 |
errorMessage | 사람이 읽을 수 있는 오류 설명이어야 한다. 상대측이 이해할 수 있는 언어를 사용해야 한다(권장) |
relatedFrameId | 선택. 특정 프레임에 의해 트리거된 오류일 때 해당 프레임의 fragmentId를 포함해야 한다 |
relatedAgreementId | 선택. 특정 약정과 관련된 오류일 때 Agreement_ID를 포함해야 한다 |
details | 선택. 디버깅용 추가 정보를 포함할 수 있다 |
9.5.2 오류 통지 전송
ErrorNotificationFrame은 일반 LogicalFrame 채널을 통해 전송되어야 하며, Payload는 암호화되어야 한다.
오류 자체로 인해 암호화가 불가능한 경우(예: KEY_NOT_READY 오류), 구현은 구현 정의의 대역 외(out-of-band) 채널을 통해 오류를 반환할 수 있으나, 암호화 채널에서 평문으로 오류를 전송해서는 안 된다.
9.6 핵심 오류 처리 흐름
9.6.1 역직렬화 실패(1001)
수신한 LogicalFrame을 역직렬화할 수 없을 때 수신측은 다음을 수행해야 한다:
- 해당 프레임을 폐기한다.
FRAME_DESERIALIZATION_FAILED오류 통지를 전송한다.- 역직렬화 실패로 인해 세션을 닫아서는 안 된다.
9.6.2 복호화 실패(2001)
수신한 Payload를 복호화할 수 없을 때 수신측은 다음을 수행해야 한다:
- 해당 프레임을 폐기한다.
DECRYPTION_FAILED오류 통지를 전송한다.- 연속된 복호화 실패 횟수를 계산한다.
- 연속 실패가 임곗값(권장 3회)을 초과하면 CAP 키 재교환을 트리거해야 한다(권장).
9.6.3 DAG 순환 검출(4001)
DAG 순환이 검출되면:
- 수신측은 해당 Fragment를 거부해야 한다.
DAG_CYCLE_DETECTED오류를 반환해야 한다.- Fragment를 DAG에 추가해서는 안 된다.
- 기존 Fragment의 상태에 영향을 주어서는 안 된다.
9.6.4 알 수 없는 약정(3001)
Fragment가 알 수 없는 Agreement_ID를 참조할 때:
- 수신측은 Fragment를 폐기해야 한다.
AGREEMENT_NOT_FOUND오류를 반환한다.- 재협상을 트리거할 수 있다(구현 정의).
9.6.5 키 미준비(2002)
CAP 키 교환이 아직 완료되지 않은 상태에서 데이터 전송을 시도할 때:
- DTP_Engine은 전송을 거부해야 한다.
- 상위 호출자에게
KEY_NOT_READY오류를 반환해야 한다. - 통신 채널에서 평문으로 응답해서는 안 된다.
9.6.6 캐시 가득(6001)
미확인 Fragment 캐시가 상한에 도달할 때:
- 송신측은 새 Fragment 전송을 일시 중지해야 한다.
- 상위 애플리케이션에
BUFFER_FULL통지를 전송한다. - 캐시 공간이 해제되었음을 확인한 후 전송을 재개한다.
- 이미 캐시된 Fragment를 폐기해서는 안 된다.
9.6.7 재전송 타임아웃(6002)
Fragment 재전송 횟수가 소진될 때:
- 송신측은 상위 애플리케이션에
RETRANSMISSION_TIMEOUT오류를 통지해야 한다. - 세션 일시 중지 또는 종료를 트리거해야 한다(권장).
- 무한 재전송해서는 안 된다.
9.6.8 버전 비호환(7001)
프로토콜 버전이 비호환인 프레임을 수신할 때(자세한 내용은 제10장 참조):
- 수신측은 해당 프레임을 처리해서는 안 된다.
VERSION_INCOMPATIBLE오류를 반환해야 한다.- 오류의
details필드에 자체 지원하는 최고 버전 번호를 포함해야 한다.
9.6.9 권한 거부(8001, 8002)
부적합한 역할 작업 시:
- DTP_Engine은 해당 작업을 거부해야 한다.
- 관찰자 쓰기 작업은
OBSERVER_WRITE_DENIED(8002)를 반환해야 한다. - 기타 권한 거부는
PERMISSION_DENIED(8001)를 반환해야 한다.
9.7 오류 회복 정책
구현은 오류 유형에 따라 해당 회복 정책을 취해야 한다:
| 오류 분류 | 회복 정책 |
|---|---|
| 1xxx 프레임 처리 | 프레임 폐기, 로그 기록, 후속 프레임 계속 수신 |
| 2xxx 암호화 | 단일 실패 시 프레임 폐기, 연속 실패 시 키 재협상 트리거 |
| 3xxx 약정 | Fragment 폐기, 재협상 트리거 가능 |
| 4xxx DAG | 순환: 거부; 미해석: 캐시 대기 |
| 5xxx 세션 | 세션 회복 시도, 실패 시 닫음 |
| 6xxx 재개 | 전송 일시 중지, 상대측 응답 또는 상위 개입 대기 |
| 7xxx 버전 | 버전 통지 전송, 다운그레이드 처리 시도 |
| 8xxx 권한 | 작업 거부, 자동 재시도해서는 안 됨 |
구현은 권한 오류(8xxx) 발생 시 동일한 작업을 자동으로 재시도해서는 안 된다.
