요약

  • RFC 3382는 서로 다른 유형의 이름 붙은 멤버들을 하나의 값 안에 담기 위해, 중첩 컬렉션과 미래의 선택적 멤버까지 포함하는 선택적 IPPcollection구문을 추가했다.
  • 이 멤버들의 순서는 명시적으로 권위적이지 않았다. 프린터(Printer)는 멤버들을 다른 순서로 반환할 수 있었으므로, 의미는 바이트 위치가 아니라 고유한 이름과 컬렉션 경계에 붙어 있어야 했다.

클라이언트가 세 개의 멤버를 가진 컬렉션을 보낸다고 하자. 프린터는 같은 세 개의 멤버를 반환하지만, 첫 번째는 세 번째로, 세 번째는 두 번째로 바뀌어 있다. 클라이언트가 위치를 식별자처럼 취급해 왔다면 이 교환은 손상된 것처럼 보인다. RFC 3382 아래에서는, 프린터가 정확히 프로토콜이 허용한 대로 행동했을 수 있다. 안정적인 사실은 멤버 이름, 그 구문, 그 값, 그리고 그들이 하나로 속해 있음을 말하는 경계다.

2002년 9월에 Standards Track으로 발행된 RFC 3382는 인터넷 인쇄 프로토콜(IPP)의 구조적 빈틈을 메웠다. IPP에는 이미 많은 익숙한 스칼라 값과 반복 값에 대한 구문이 있었지만, 서로 다른 유형의 속성들을 하나의 값으로 묶는 일반적인 방법은 없었다. RFC는 새 구조물을 PostScript 사전이나 Java Map에 비유했다. 이 비교는 이름 붙은 멤버십에 관한 것이었지, 프로그래밍 언어의 객체 모델 전체를 IPP로 들여오려는 것이 아니었다.

필요성은 평범하게 들린다: 하나의 대상을 여러 관련 필드로 기술하는 것이다. 예컨대 물리적 매체는 크기, 유형, 색상 또는 출처 정보를 하나의 개념적 단위로 옮겨야 할 수 있다. 평면적인 목록도 각 사실을 담을 수 있지만, 그런 단위가 여러 개 나타날 때 어떤 값이 어느 인스턴스에 속하는지 말하기가 어려워진다. 컬렉션이 그 경계를 제공했다.

RFC 3382는 컬렉션을 하나 이상의 멤버 속성으로 정의했다. 각 멤버는 이름과 자체 구문을 가졌다. 하나는 정수, 다른 하나는 키워드, 또 다른 하나는 범위일 수 있고, 또 다른 하나는 그 자체로 컬렉션이나 컬렉션의 집합일 수 있다. 외부 컨테이너는 이러한 유형들을 지우지 않았다. 서로 다른 값들이 하나의 구조에 참여한다고 선언되면서도 각각 구별된 채로 있을 수 있게 했다.

그러나 이 묶음은 순서 계약을 만들지 않았다. RFC는 멤버 속성이 어떤 순서로든 나타날 수 있다고 명시했다. 프린터는 그것들을 내부에 저장했다가 요청과 다른 순서로 반환할 수 있었다. 이것은 사소한 직렬화 각주가 아니었다. 의미상의 권위가 어디에 사는지를 결정한 것이다. 소비자는 '두 번째 값이 항상 높이를 뜻한다'거나 '마지막 값이 항상 색상을 뜻한다'고 말하는 대신, 이름으로 멤버를 해석해야 했다.

따라서 이름은 더 무거운 부담을 짊어졌다. 실제 컬렉션 속성을 정의하는 문서는 컬렉션의 이름, 단일 값인지 다중 값인지, 나타날 수 있는 맥락, 지원되는 멤버가 어떻게 광고되는지, 각 멤버의 이름, 요구 수준, 구문, 의미론, 제약 조건, 지원 값과 기본값을 명시해야 했다. 컨테이너 구문은 구조가 들어갈 자리를 만들었을 뿐, 그 구조를 정의할 책임을 문서로부터 면제시키지 않았다.

멤버 이름은 하나의 컬렉션 정의 안에서 고유해야 했다. 같은 이름이 다른 컬렉션이나 더 넓은 IPP 속성 네임스페이스의 다른 곳에 나타나는 것은 정당했다. 컬렉션 경계가 범위를 제공했기 때문이다. 이것이 유용한 비대칭이다: 고유성은 재사용을 허용할 만큼 충분히 지역적이면서, 하나의 컨테이너 안에서는 이름 기반 조회를 모호하지 않게 할 만큼 충분히 엄격했다.

같은 멤버 이름을 두 번 담은 컬렉션 값은 잘못된 형식이었다. 클라이언트는 그것을 보내지 말아야 했고, 프린터도 그것을 반환하지 말아야 했다. 수신자의 허용된 행동은 또한 중요한 한계를 드러냈다. 프린터는 요청을 잘못된 것으로 거부할 수도 있고, 구현에 따라 요청을 수락하면서 중복 중 하나만 사용할 수도 있었다. RFC 3382는 이식 가능한 '첫 번째 승리' 또는 '마지막 승리' 규칙을 세우지 않았다. 고유성이 깨지면 직렬화된 순서가 의미를 안전하게 복구할 수 없었다.

이 경계는 구문 분석만큼이나 보안과 운영에도 중요하다. 두 구성 요소가 서로 다른 중복 값을 선택하면, 검증은 한 값을 대상으로 이루어지지만 실행은 다른 값을 사용할 수 있다. RFC는 그런 현대적 공격 패턴을 기술하지 않았으며, 발행 자체가 특정 제품에 대해 아무것도 말하지 않는다. 그러나 그 적합성 규칙은 올바른 방향을 가리킨다: 생산자는 모호성을 만들어서는 안 되고, 소비자는 프로토콜이 약속한 적 없는 위치 규칙을 발명해서는 안 된다.

중첩은 모델을 더 표현력 있게 만들었다. 멤버는 다른 컬렉션을 담을 수 있었고, 멤버는 컬렉션의 집합일 수도 있었다. 컬렉션 전체에 대한 단일 전역 길이 제한은 없었지만, 모든 멤버는 여전히 자체 구문의 제약을 받았다. 그 결과는 무한한 자유 형식의 문서가 아니었다. 잎(leaf)은 여전히 IPP 값 규칙을 따르고, 정의는 여전히 지원되는 문법을 열거해야 하는 재귀적 구조였다.

확장도 마찬가지로 의도적이었다. 컬렉션 정의는 선택적 멤버를 포함할 수 있었고, 정의 규칙이 허용하는 곳에서 이후의 문서들이 더 많은 선택적 멤버를 추가할 수 있었다. 오래된 구현은 새 멤버를 이해하는 척할 필요가 없었다. 실패를 올바른 컨테이너에 묶어 둔 채, 지원되지 않는 것이 무엇인지 식별하는 규율 있는 방법이 필요했을 뿐이다.

RFC 3382는 이것을 Unsupported Attributes Group을 통해 해결했다. 멤버가 지원되지 않거나 충돌할 때, 그 멤버들은 컬렉션 안에 담겨 반환될 수 있었으며, 무엇이 실패했는지에 대한 경계가 유지되었다. 멤버를 무관한 최상위 오류로 평면화하면 맥락이 사라진다. 같은 이름이 여러 범위에 존재할 수 있으므로, 그 컬렉션을 둘러싼 문맥이 어느 용례가 문제를 일으켰는지 설명해 주었다.

와이어 인코딩도 이러한 경계를 강화했다.begin-collection,member-name,end-collection태그가 구조를 감쌌다. 설계는 구식 IPP 파서들을 고려했으므로, 컬렉션에 익숙하지 않은 파서가 멤버 이름을 평범한 최상위 속성으로 가볍게 취급하지 않았다. 여기서의 호환성이란 레거시 구현이 갑자기 새 의미론을 이해하게 되었다는 뜻이 아니었다. 확장이 프레임을 갖춰 두었기 때문에, 무지가 오해로 굳어질 가능성이 낮아졌다는 뜻이었다.

RFC의media-col과job-sheet-col자료는 예시적이었다. 필수 및 선택적 멤버, 지원 멤버 속성, 중첩을 포함하여 저자들이 컬렉션을 어떻게 정의할 수 있는지를 보여주었다. 그 자체로 그 예시들을 배포된 속성으로 표준화하지는 않았다. 후속 명세가 여전히 정의 작업을 수행해야 했다. 예시 이름을 페이로드에 복사한다고 상호 운용 가능한 계약이 생기지 않았다.

이 점이 RFC 3382를 바로 옆의 두 IPP 확장과 구분한다. RFC 3380은 작업 속성 갱신의 원자적 수락 또는 거부를 다루었고, RFC 3381은 정렬(collation)과 관측 경계에 따라 해석이 달라지는 진행 카운터를 다루었다. RFC 3382는 더 근본적인 표현의 질문을 물었다: 여러 유형화된 값이 자신들의 물리적 순서를 의미의 일부로 만들지 않으면서 하나의 단위로 이동할 수 있는가?

그 답은 IPP/1.1 인코딩 및 전송 문서인 RFC 2910과, 모델 및 의미론 문서인 RFC 2911을 모두 갱신했다. 더 앞선 RFC 2565와 RFC 2566은 IPP/1.0 대응물을 정의했고, RFC 2567은 요구 사항을 기록했으며, RFC 2568은 설계 목표와 프로토콜 구조를 설명했다. 이후 RFC 8010과 RFC 8011이 인코딩과 모델을 통합했다. 이 연표는 그 메커니즘의 위치를 알려줄 뿐이다. 채택 여부, 제품 행동, 성공적인 인쇄를 증명하지는 않는다.

멤버십과 순서의 분리가 오래 지속되는 까닭은, 많은 시스템이 실수로 표현(presentation)이 식별자를 대신하도록 만들기 때문이다. 디코더가 필드를 배열로 읽고, 애플리케이션이 그 배열을 렌더링하면, 곧 세 번째 원소가 어느 명세도 부여한 적 없는 의미를 얻게 된다. 이 배열은 직렬화기가 출력을 정규화하거나, 새 선택적 멤버가 삽입되거나, 중개자가 맵으로부터 컬렉션을 재구성할 때까지 작동한다.

RFC 3382의 규칙은 이런 표류를 끊는다. 순서가 바뀔 수 있다면 테스트는 순서를 섞어야 한다. 이름이 멤버를 정의한다면 중복 이름은 모호성으로서 거부되거나 격리되어야 한다. 중첩이 범위를 제공한다면 로그와 오류 보고는 중첩 컬렉션으로 들어가는 경로를 보존해야 한다. 선택적 확장이 허용된다면, 알 수 없는 멤버는 가장 가까운 익숙한 위치에 조용히 묶이는 것이 아니라 눈에 보이는 채로 알 수 없는 상태로 남아야 한다.

Lu Heng의 최소 초기 명세(minimum-initial-specification) 렌즈는 이 설계의 절약을 설명하는 데 도움이 된다. RFC는 모든 미래의 구조화된 속성을 표준화하려 하지 않았다. 재사용 가능한 가장 작은 컨테이너, 구체적인 컬렉션을 정의하는 문서들의 의무, 그리고 기반 모델을 다시 쓰지 않고 선택적 멤버가 나타날 수 있게 하는 규칙을 표준화했다. 미래의 결정은 새 정의 안에 지역화되어 남았다.

그의 현실 계층(reality-layers) 렌즈는 그에 상응하는 경고를 제공한다. 직렬화된 순서는 하나의 표현 계층이다. 멤버 식별, 컬렉션 멤버십, 지원 상태, 검증된 의미, 최종 장치 동작은 서로 다른 계층이다. 깨끗하게 디코딩된 컬렉션은 모든 멤버가 지원되었다는 것도, 요청이 수락되었다는 것도, 물리적 작업이 성공했다는 것도 증명하지 않는다. 그룹화 구문은 구조에 대한 증거이지 결과에 대한 증명서가 아니다.

따라서 RFC 3382의 역사적 성과는 몇 개의 값에 괄호를 쳤다는 것이 아니었다. IPP에 관계를 보존하면서 표현이 이동하는 것을 허용하도록 가르친 것이다. 컬렉션은 멤버들을 함께 유지했다. 고유한 이름과 명시적인 경계가 그 멤버들을 이해 가능하게 만들었다. 순서는 처음부터 그래야 했던 것으로 남을 수 있었다: 하나의 인코딩에 부수적인 속성으로.

출처