웹 시리얼 API

진행 중 문서

초안 커뮤니티 그룹 보고서

최근 게시된 버전:
없음
최근 편집 초안:
https://wicg.github.io/serial/
편집자:
GH에서 기여자 보기
피드백:
GitHub wicg/serial (풀 리퀘스트, 새 이슈, 열린 이슈)

요약

Serial API는 웹사이트가 자바스크립트로 시리얼 장치에서 읽고 쓸 수 있도록 하는 방법을 제공합니다. 이 API는 문서가 마이크로컨트롤러, 3D 프린터, 기타 시리얼 장치와 같은 장치와 통신할 수 있게 하여 웹과 물리적 세계를 연결합니다. 또한 설명서도 함께 제공됩니다.

문서 상태

본 명세는 Web Platform Incubator Community Group에서 발행하였습니다. W3C 표준이 아니며, W3C 표준 트랙에도 포함되어 있지 않습니다. W3C Community Contributor License Agreement (CLA)에 따라 제한적 옵트아웃 및 기타 조건이 적용됨을 참고하세요. W3C Community and Business Groups에 대해 더 알아보세요.

본 문서는 진행 중인 작업입니다. 모든 기여를 환영합니다.

본 명세에 대한 논의는 GitHub Issues를 사용하는 것이 권장됩니다.

1. Navigator 인터페이스에 대한 확장

WebIDL[Exposed=Window, SecureContext]
partial interface Navigator {
  [SameObject] readonly attribute Serial serial;
};

1.1 serial 속성

가져올 때, serial 속성은 항상 동일한 Serial 객체 인스턴스를 반환합니다.

2. WorkerNavigator 인터페이스에 대한 확장

WebIDL[Exposed=DedicatedWorker, SecureContext]
partial interface WorkerNavigator {
  [SameObject] readonly attribute Serial serial;
};

2.1 serial 속성

가져올 때, serial 속성은 항상 동일한 Serial 객체 인스턴스를 반환합니다.

3. Serial 인터페이스

WebIDL[Exposed=(DedicatedWorker, Window), SecureContext]
interface Serial : EventTarget {
  attribute EventHandler onconnect;
  attribute EventHandler ondisconnect;
  Promise<sequence<SerialPort>> getPorts();
  [Exposed=Window] Promise<SerialPort> requestPort(optional SerialPortRequestOptions options = {});
};

3.1 requestPort() 메서드

requestPort() 메서드 단계는 다음과 같습니다:

  1. promise새로운 Promise로 설정합니다.
  2. this관련 전역 객체에 연결된 연관된 Document해당 정책 제어 기능 "serial"을 사용할 수 없다면, reject promise를 "SecurityError" DOMException와 함께 반환합니다.
  3. 관련 전역 객체this와 연결되어 있고 임시 활성화가 없다면, reject promise를 "SecurityError" DOMException와 함께 반환합니다.
  4. 만약 options["filters"]가 존재한다면, 각 filter에 대해 다음 단계를 수행합니다:
    1. 만약 filter["bluetoothServiceClassId"] 가 존재한다면:
      1. filter["usbVendorId"] 가 있으면, reject promiseTypeError와 함께 반환합니다.
      2. filter["usbProductId"] 가 있으면, reject promiseTypeError와 함께 반환합니다.
    2. 만약 filter["usbVendorId"]가 없다면, reject promiseTypeError와 함께 반환합니다.
      참고
      이 검사 규칙은 SerialPortFilter가 비어 있을 수 없고 usbProductId가 지정되면, usbVendorId도 지정되어야 함을 의미합니다.
  5. 다음 단계를 병렬로 실행합니다:
    1. allPorts를 빈 리스트로 설정합니다.
    2. 시스템에 등록된 각 Bluetooth 장치에 대해:
      1. 장치가 지원하는 각 BluetoothServiceUUID를 순회합니다:
        1. uuid차단된 Bluetooth 서비스 클래스 UUID가 아니면:
    3. 각 Bluetooth가 아닌 시리얼 포트에 대해:
      1. port를 해당 포트를 나타내는 SerialPort로 설정합니다.
      2. 추가 portallPorts에 추가합니다.
    4. 사용자에게 사이트가 시리얼 포트에 접근할 수 있도록 허용할 것을 요청하며, allPortsoptions["filters"]가 있을 시 필터와 일치하는 포트, 그렇지 않으면 모든 포트 목록을 표시합니다.
    5. 사용자가 포트를 선택하지 않은 경우, 전역 작업을 큐에 추가하여 관련 전역 객체에서 reject promise를 "NotFoundError" DOMException와 함께 반환하고 이 단계를 중단합니다.
    6. port를 사용자가 선택한 SerialPort로 설정합니다.
    7. 전역 작업을 큐에 추가하여 관련 전역 객체에서 resolve promiseport와 함께 반환합니다.
  6. promise를 반환합니다.

시리얼 포트는 사용 가능하려면 유선 시리얼 포트가 시스템에 물리적으로 연결되어 있거나, 무선 시리얼 포트가 해당 포트를 호스팅하는 무선 장치가 시스템에 등록되어 있는 경우입니다.

3.1.1 SerialPortRequestOptions 딕셔너리

WebIDLdictionary SerialPortRequestOptions {
  sequence<SerialPortFilter> filters;
  sequence<BluetoothServiceUUID> allowedBluetoothServiceClassIds;
};
filters 멤버
시리얼 포트용 필터
allowedBluetoothServiceClassIds 멤버
BluetoothServiceUUID 값들의 목록. Bluetooth 포트가 사용자에게 표시되는 포트 목록에서 제외되며, 해당 서비스 클래스 ID가 이 목록에 있지 않으면 표시되지 않습니다.

3.1.2 SerialPortFilter 딕셔너리

WebIDLdictionary SerialPortFilter {
  unsigned short usbVendorId;
  unsigned short usbProductId;
  BluetoothServiceUUID bluetoothServiceClassId;
};
usbVendorId 멤버
USB 공급업체 ID
usbProductId 멤버
USB 제품 ID
bluetoothServiceClassId 멤버
Bluetooth 서비스 클래스 ID

시리얼 포트 portfilter일치하면 true를 반환합니다. 단계는 다음과 같습니다:

  1. infoport.getInfo() 호출 값으로 설정합니다.
  2. 만약 filter["bluetoothServiceClassId"]가 있다면:
    1. 시리얼 포트가 Bluetooth 장치 일부가 아니라면 false 반환
    2. filter["bluetoothServiceClassId"]와 info["bluetoothServiceClassId"]가 같으면 true 반환
    3. 그 외는 false 반환
  3. filter["usbVendorId"]가 없으면 true 반환
  4. 시리얼 포트가 USB 장치 일부가 아니면 false 반환
  5. info["usbVendorId"]와 filter["usbVendorId"]가 다르면 false 반환
  6. filter["usbProductId"]가 없으면 true 반환
  7. info["usbProductId"]와 filter["usbProductId"]가 다르면 false 반환
  8. 그 외는 true 반환

시리얼 포트 portSerialPortFilter 시퀀스에서 하나라도 일치하면 true를 반환합니다.

  1. 시퀀스의 각 filter에 대해, 다음 하위 단계를 수행합니다:
    1. 만약 port필터와 일치하는 filter라면, true를 반환합니다.
  2. false를 반환합니다.

3.2 getPorts() 메서드

getPorts() 메서드 단계는 다음과 같습니다:

  1. promise새로운 Promise로 설정합니다.
  2. this관련 전역 객체에 연결된 연관된 Document해당 정책 제어 기능 "serial"을 사용할 수 없다면, reject promise를 "SecurityError" DOMException와 함께 반환합니다.
  3. 다음 단계를 병렬로 실행합니다:
    1. availablePorts를, 이전 requestPort() 호출 결과로 사용자가 사이트에 접근을 허락한 사용 가능한 시리얼 포트들의 시퀀스로 설정합니다.
    2. ports를, availablePorts에 해당하는 SerialPort의 시퀀스로 설정합니다.
    3. 전역 작업을 큐에 추가하여 관련 전역 객체에서 resolve promiseports와 함께 반환합니다.
  4. promise를 반환합니다.

3.3 onconnect 속성

onconnect이벤트 핸들러 IDL 속성입니다. connect 이벤트 타입에 해당합니다.

3.4 ondisconnect 속성

ondisconnect이벤트 핸들러 IDL 속성입니다. disconnect 이벤트 타입에 해당합니다.

4. SerialPort 인터페이스

WebIDL[Exposed=(DedicatedWorker,Window), SecureContext]
interface SerialPort : EventTarget {
  attribute EventHandler onconnect;
  attribute EventHandler ondisconnect;
  readonly attribute boolean connected;
  readonly attribute ReadableStream readable;
  readonly attribute WritableStream writable;

  SerialPortInfo getInfo();

  Promise<undefined> open(SerialOptions options);
  Promise<undefined> setSignals(optional SerialOutputSignals signals = {});
  Promise<SerialInputSignals> getSignals();
  Promise<undefined> close();
  Promise<undefined> forget();
};

이 인터페이스의 메서드는 일반적으로 비동기적으로 완료되며, 작업을 시리얼 포트 태스크 소스에 큐잉합니다.

SerialPort에 대한 부모 가져오기 알고리즘은, Serial 인스턴스를 반환합니다. 이는 Navigator 객체의 serial getter로 반환되는 것과 같습니다. (해당 SerialPort관련 전역 객체 사용)

SerialPort의 인스턴스는 아래 표에 설명된 내부 슬롯들과 함께 생성됩니다:

내부 슬롯 초기 값 설명 (비규범)
[[state]] "closed" SerialPort의 활성 상태를 추적
[[bufferSize]] undefined 송수신을 위해 버퍼링할 데이터의 양
[[connected]] false 시리얼 포트의 논리적 연결 상태를 나타내는 플래그
[[readable]] null 포트에서 데이터를 수신하는 ReadableStream
[[readFatal]] false 포트에서 치명적인 읽기 오류가 발생했음을 나타내는 플래그
[[writable]] null 포트로 데이터를 전송하는 WritableStream
[[writeFatal]] false 포트에서 치명적인 쓰기 오류가 발생했음을 나타내는 플래그
[[pendingClosePromise]] null Promise로, readablewritable이 닫힐 때까지 대기하는 용도

4.1 onconnect 속성

onconnect이벤트 핸들러 IDL 속성입니다. connect 이벤트 타입에 해당합니다.

사용자가 이전에 requestPort()를 호출한 결과로 사이트가 접근을 허용받은 시리얼 포트가 논리적으로 연결되면, 다음 단계를 수행합니다:

  1. port를 포트를 나타내는 SerialPort로 설정합니다.
  2. port.[[connected]]true로 설정합니다.
  3. 이벤트를 발송합니다. 이벤트 이름은 connect이고 대상은 port이며, bubbles 속성은 true로 초기화됩니다.

시리얼 포트가 논리적으로 연결되었다고 판단되는 경우는, 유선 시리얼 포트의 경우 시스템에 물리적으로 연결된 경우거나, 무선 시리얼 포트의 경우 시스템이 무선 장치(예: 활성 Bluetooth L2CAP 채널)에 적극적으로 연결된 경우입니다.

4.2 ondisconnect 속성

ondisconnect이벤트 핸들러 IDL 속성입니다. disconnect 이벤트 타입에 해당합니다.

사용자가 이전에 requestPort()를 호출한 결과로 사이트가 접근을 허용받은 시리얼 포트가 더 이상 논리적으로 연결되어 있지 않다면, 다음 단계를 수행합니다:

  1. port를 포트를 나타내는 SerialPort로 설정합니다.
  2. port.[[connected]]false로 설정합니다.
  3. 이벤트를 발송합니다. 이벤트 이름은 disconnect이고 대상은 port이며, bubbles 속성은 true로 초기화됩니다.

4.3 getInfo() 메서드

getInfo() 메서드 단계는 다음과 같습니다:
  1. info를 빈 순서가 있는 맵으로 설정합니다.
  2. 포트가 USB 장치의 일부라면, 다음 단계를 수행합니다:
    1. info["usbVendorId"]를 장치의 vendor ID 값으로 설정
    2. info["usbProductId"]를 장치의 product ID 값으로 설정
  3. 포트가 Bluetooth 장치의 서비스라면, 다음 단계를 수행합니다:
    1. info["bluetoothServiceClassId"] 를 Bluetooth 서비스의 서비스 클래스 UUID로 설정
  4. info를 반환합니다.

4.3.1 SerialPortInfo 딕셔너리

WebIDLdictionary SerialPortInfo {
  unsigned short usbVendorId;
  unsigned short usbProductId;
  BluetoothServiceUUID bluetoothServiceClassId;
};
usbVendorId 멤버
포트가 USB 장치의 일부일 경우 이 멤버는 해당 장치의 16비트 vendor ID입니다. 그렇지 않으면 undefined입니다.
usbProductId 멤버
포트가 USB 장치의 일부일 경우 이 멤버는 해당 장치의 16비트 product ID입니다. 그렇지 않으면 undefined입니다.
bluetoothServiceClassId 멤버
포트가 Bluetooth 장치의 서비스라면 이 멤버는 서비스 클래스 UUID를 포함하는 BluetoothServiceUUID입니다. 그렇지 않으면 undefined입니다.

4.4 open() 메서드

open() 메서드 단계는 다음과 같습니다:

  1. promise새로운 promise로 설정합니다.
  2. this.[[state]]"closed"가 아니라면, promise를 "InvalidStateError" DOMException과 함께 reject 하고 promise를 반환합니다.
  3. options["dataBits"]가 7 또는 8이 아니면, promiseTypeError와 함께 reject 하고, promise를 반환합니다.
  4. options["stopBits"]가 1 또는 2가 아니면, promiseTypeError와 함께 reject 하고, promise를 반환합니다.
  5. options["bufferSize"]가 0이면, promiseTypeError와 함께 reject 하고, promise를 반환합니다.
  6. 선택적으로, options["bufferSize"]가 구현체에서 지원 가능한 크기보다 크면, promiseTypeError와 함께 reject 하고, promise를 반환합니다.
  7. this.[[state]]"opening"으로 설정합니다.
  8. 병렬로 다음을 수행합니다.
    1. 운영체제에 options에 명시된(또는 기본값인) 연결 파라미터를 사용해 시리얼 포트를 열도록 요청합니다.
    2. 실패할 경우, 글로벌 태스크를 큐잉하여 해당 전역 객체에서, serial port task source를 사용해 reject promise를 "NetworkError" DOMException과 함께 하고, 단계를 중단합니다.
    3. this.[[state]]"opened"로 설정합니다.
    4. this.[[bufferSize]]options["bufferSize"]로 설정합니다.
    5. 글로벌 태스크를 큐잉하여 해당 전역 객체에서, serial port task source를 사용해 resolve promiseundefined와 함께 합니다.
  9. promise를 반환합니다.

4.4.1 SerialOptions 딕셔너리

WebIDLdictionary SerialOptions {
  [EnforceRange] required unsigned long baudRate;
  [EnforceRange] octet dataBits = 8;
  [EnforceRange] octet stopBits = 1;
  ParityType parity = "none";
  [EnforceRange] unsigned long bufferSize = 255;
  FlowControlType flowControl = "none";
};
baudRate 멤버
시리얼 통신을 시작할 때 사용할 양수, 0보다 큰 통신 속도 값입니다.
참고
baudRate는 이 딕셔너리의 유일한 필수 멤버입니다. 다른 연결 파라미터에는 일반적인 기본값이 존재하지만, 개발자는 연결하려는 특정 장치에 맞는 값을 반드시 확인해야 하며 설명서를 참고할 필요가 있습니다. 일부 값이 자주 사용되긴 하지만 표준 통신 속도가 정해져 있지 않습니다. 이 파라미터를 필수로 하는 것은 이 명세에서 임의의 기본값을 설정할 경우의 혼란을 줄여주기 위함입니다.
dataBits 멤버
프레임마다 사용하는 데이터 비트 수입니다. 7 또는 8이어야 합니다.
stopBits 멤버
프레임 끝에 사용하는 정지 비트 수입니다. 1 또는 2여야 합니다.
parity 멤버
패리티 모드입니다.
bufferSize 멤버
읽기 및 쓰기 버퍼로 생성해야 하는 크기의 양수, 0보다 큰 값입니다.
flowControl 멤버
플로우 제어 모드입니다.
4.4.1.1 ParityType 열거형
WebIDLenum ParityType {
  "none",
  "even",
  "odd"
};
none
각 데이터 단어마다 패리티 비트를 전송하지 않습니다.
even
데이터 단어에 패리티 비트를 더한 값이 짝수 패리티가 됩니다.
odd
데이터 단어에 패리티 비트를 더한 값이 홀수 패리티가 됩니다.
4.4.1.2 FlowControlType 열거형
WebIDLenum FlowControlType {
  "none",
  "hardware"
};
none
흐름 제어가 활성화되지 않습니다.
hardware
RTS와 CTS 신호를 이용한 하드웨어 흐름 제어가 활성화됩니다.

4.5 connected 속성

connected getter의 단계는 다음과 같습니다:

  1. this.[[connected]]를 반환합니다.

4.6 readable 속성

readable getter 단계는 다음과 같습니다:

  1. this.[[readable]]null이 아니면 this.[[readable]]를 반환한다.
  2. this.[[state]]"opened"가 아니면, null을 반환한다.
  3. this.[[readFatal]]true이면 null을 반환한다.
  4. streamnew ReadableStream으로 둔다.
  5. pullAlgorithm을 다음 단계로 둔다:
    1. desiredSize하이 워터마크까지 채워넣을 바이트 수로 둔다 this.[[readable]]에 대하여.
    2. this.[[readable]]현재 BYOB 요청 뷰가 null이 아니면, desiredSizethis.[[readable]]현재 BYOB 요청 뷰바이트 길이로 설정한다.
    3. 다음 단계를 병렬로 실행한다:
      1. OS에 포트에서 desiredSize 바이트까지 읽기 요청을 수행하여 결과를 바이트 시퀀스 bytes에 저장한다.
        Note
        this.[[state]]"forgotten" 이 되면 포트가 연결 해제됨과 동일하게 처리해야 합니다.
      2. 글로벌 태스크 큐관련 글로벌 객체에 생성한 뒤, thisserial port task source를 이용해 다음 단계를 수행한다:
        1. 오류가 없다면,
          1. this.[[readable]]현재 BYOB 요청 뷰가 null이 아니라면, bytesthis.[[readable]]현재 BYOB 요청 뷰에 기록하고, viewthis.[[readable]]현재 BYOB 요청 뷰로 둔다.
          2. 그 외에는, viewbytes로부터 Uint8Arraythis관련 렐름에서 생성해 대입한다.
          3. view를 enqueue하여 this.[[readable]]에 추가한다.
        2. 버퍼 오버런 발생 시, error 호출, this.[[readable]]에 "BufferOverrunError" DOMException을 전달하고 읽기 스트림 닫기 처리 단계를 실행한다.
        3. 브레이크 조건 발생 시, error 호출, this.[[readable]]에 "BreakError" DOMException을 전달하고 읽기 스트림 닫기 처리 단계를 실행한다.
        4. 프레이밍 오류 발생 시, error 호출, this.[[readable]]에 "FramingError" DOMException을 전달하고 읽기 스트림 닫기 처리 단계를 실행한다.
        5. 패리티 오류 발생 시, error 호출, this.[[readable]]에 "ParityError" DOMException을 전달하고 읽기 스트림 닫기 처리 단계를 실행한다.
        6. 운영체제 오류 발생 시, error 호출, this.[[readable]]에 "UnknownError" DOMException을 전달하고, 읽기 스트림 닫기 처리 단계를 실행한다.
        7. 포트가 연결 해제됨이면, 다음을 실행한다:
          1. this.[[readFatal]]true로 설정
          2. error 호출, this.[[readable]]에 "NetworkError" DOMException을 전달한다.
          3. 읽기 스트림 닫기 처리 단계를 실행한다.
    4. undefined로 즉시 resolve된 promise를 반환한다.
    Note

    이 알고리즘에서 반환되는 Promise는 곧바로 resolve되어 스트림 취소를 차단하지 않습니다. [STREAMS]에서는 이 알고리즘이 청크가 enqueue될 때까지 다시 호출되지 않음을 명시합니다.

  6. cancelAlgorithm을 다음 단계로 둔다:
    1. promise새 promise로 둔다.
    2. 다음 단계를 병렬로 수행한다.
      1. 운영체제에게 해당 포트의 모든 소프트웨어, 하드웨어 수신 버퍼 내용을 폐기하도록 요청한다.
      2. 글로벌 태스크 큐관련 글로벌 객체에 생성한 뒤, thisserial port task source로 다음을 실행한다:
        1. 읽기 스트림 닫기 처리 단계를 실행한다.
        2. promise를 undefined로 resolve한다.
    3. promise를 반환한다.
  7. 바이트 읽기 지원으로 stream 설정pullAlgorithmpullAlgorithm로, cancelAlgorithmcancelAlgorithm로, highWaterMarkthis.[[bufferSize]]로 설정하여 stream을 구성한다.
  8. this.[[readable]]stream으로 둔다.
  9. stream을 반환한다.
읽기 스트림 닫기 처리를 handle closing the readable stream이라 하며, 다음 단계를 수행한다:
  1. this.[[readable]]null로 설정한다.
  2. this.[[writable]]null이고 this.[[pendingClosePromise]]null이 아니면, resolve this.[[pendingClosePromise]]undefined로 한다.

4.7 writable 속성

writable getter 단계는 다음과 같습니다:

  1. this.[[writable]]null이 아니면 this.[[writable]]를 반환한다.
  2. this.[[state]]"opened"가 아니면 null을 반환한다.
  3. this.[[writeFatal]]true이면 null을 반환한다.
  4. stream새로운 WritableStream으로 둔다.
  5. signalstreamsignal로 둔다.
  6. writeAlgorithm을 다음 단계로 둔다. chunk가 주어졌을 때:
    1. promise새 promise로 둔다.
    2. 단언: signalaborted가 아니다.
    3. chunkBufferSource 타입의 IDL 값으로 변환될 수 없다면, promiseTypeError와 함께 reject하고 promise를 반환한다. 그렇지 않으면, 변환 결과를 source에 저장한다.
    4. 버퍼 소스 복사본 가져오기source를 복사해 bytes에 저장한다.
    5. 병렬로 다음 단계를 실행한다:
      1. 운영체제에 bytes를 포트에 기록하라고 요청한다. 또는, 이후 병합을 위해 청크를 저장한다.
        참고
        운영체제는 bytes가 실제 전송된 후가 아니라 전송 대기열에 들어간 시점에 이 작업이 반환될 수도 있습니다.
        참고
        this.[[state]]"forgotten"이 되면 포트가 연결 해제됨으로 처리되어야 합니다.
      2. 글로벌 태스크 큐관련 글로벌 객체 에 추가하고, thisserial port task source로 다음 단계들을 실행한다:
        1. 청크가 성공적으로 기록되었거나, 이후 병합을 위해 저장된 경우 promise resolve promiseundefined를 전달한다.
          참고
          [STREAMS]에서는 writeAlgorithm이 이전에 반환된 Promise가 resolve될 때만 다시 호출된다고 명시합니다. 효율성을 위해 구현은 여러 청크를 내부 큐에 쌓아 운영체제에 단일 요청으로 보내기 위해 Promise를 미리 resolve해도 됩니다.
        2. 운영체제 에러 발생 시, promise reject promise에 "UnknownError" DOMException을 전달한다.
        3. 포트가 연결 해제됨이면 다음을 실행한다:
          1. this.[[writeFatal]]true로 설정한다.
          2. promise reject promise에 "NetworkError" DOMException을 전달한다.
          3. 쓰기 스트림 닫기 처리 단계를 실행한다.
        4. signalabortedpromise reject promisesignalabort reason을 전달한다.
    6. promise를 반환한다.
  7. abortAlgorithm을 다음과 같이 둔다:
    1. promise새 promise로 둔다.
    2. 다음 단계를 병렬로 실행한다.
      1. 운영체제가 포트의 모든 소프트웨어 및 하드웨어 전송 버퍼를 폐기하도록 한다.
      2. 글로벌 태스크 큐관련 글로벌 객체에 추가하고, thisserial port task source로 다음 단계를 실행한다:
        1. 쓰기 스트림 닫기 처리 단계를 실행한다.
        2. promise resolvepromiseundefined를 전달한다.
    3. promise를 반환한다.
  8. closeAlgorithm을 다음과 같이 둔다:
    1. promise새 promise로 둔다.
    2. 다음 단계를 병렬로 실행한다.
      1. 운영체제에 포트의 모든 소프트웨어 및 하드웨어 전송 버퍼를 비우도록 명령한다.
      2. 글로벌 태스크 큐관련 글로벌 객체에 추가하고, thisserial port task source로 다음 단계를 실행한다:
        1. 쓰기 스트림 닫기 처리 단계를 실행한다.
        2. signalaborted라면 promise reject promisesignalabort reason을 전달한다.
        3. 그 외에는 promise resolvepromiseundefined를 전달한다.
    3. promise를 반환한다.
  9. stream 설정 stream writeAlgorithmwriteAlgorithm으로, abortAlgorithmabortAlgorithm으로, closeAlgorithmcloseAlgorithm으로, highWaterMarkthis.[[bufferSize]]로, sizeAlgorithm을 바이트 단위 사이즈 계산 알고리즘으로 설정한다.
  10. 다음 abort 단계signal에 추가한다:
    1. 운영체제에 포트 쓰기 요청이 얼마나 데이터가 기록되었든 최대한 빨리 반환되게 한다.
  11. this.[[writable]]stream으로 설정한다.
  12. stream을 반환한다.
쓰기 스트림 닫기 처리를 handle closing the writable stream라고 하며, 다음 단계를 수행한다:
  1. this.[[writable]]null로 설정한다.
  2. this.[[readable]]null이고 this.[[pendingClosePromise]]null이 아니면, resolve this.[[pendingClosePromise]]undefined로 한다.

4.8 setSignals() 메서드

setSignals() 메서드의 단계는 다음과 같습니다:

  1. promise새로운 promise로 둡니다.
  2. this.[[state]]"opened"가 아니라면, promise를 "InvalidStateError" DOMException으로 reject하고 promise를 반환합니다.
  3. signals에 지정된 멤버가 하나도 존재하지 않으면, promiseTypeError로 reject하고 promise를 반환합니다.
  4. 다음 단계들을 병렬로 수행합니다:
    참고
    원래는 signals에 명시된 변경사항이 원자적으로 적용되는 것이 이상적이지만, 이는 POSIX나 Windows API에서는 지원되지 않으며, 사용자 에이전트는 이러한 단계를 구현할 때 해당 API를 사용하게 됩니다. 따라서 아래에 나오는 순서가 실제 애플리케이션에서 의존되는 순서일 가능성이 높습니다.
    1. signals["dataTerminalReady"] 가 존재하면, 운영체제에 시리얼 포트의 "data terminal ready" 또는 "DTR" 신호를 assert(true일 때)하거나 deassert(false일 때)하도록 요청합니다.
    2. signals["requestToSend"]가 존재하면, 운영체제에 시리얼 포트의 "request to send" 또는 "RTS" 신호를 assert(true일 때)하거나 deassert(false일 때)하도록 요청합니다.
    3. signals["break"]가 존재하면, 운영체제에 시리얼 포트의 "break" 신호를 assert(true일 때)하거나 deassert(false일 때)하도록 요청합니다.
      참고
      "break" 신호는 일반적으로 송신선을 "mark" 전압에 고정하여 구현되며, assert되어 있는 동안 데이터 전송이 중단됩니다.
    4. 운영체제가 어떤 이유로든 이러한 신호의 상태 변경에 실패하면, 글로벌 태스크 큐관련 글로벌 객체에 생성하고, thisserial port task sourcepromise를 "NetworkError" DOMException으로 reject합니다.
    5. 글로벌 태스크 큐관련 글로벌 객체에 생성하고, thisserial port task sourceresolve promiseundefined를 전달합니다.
  5. promise를 반환합니다.

4.8.1 SerialOutputSignals 딕셔너리

WebIDLdictionary SerialOutputSignals {
  boolean dataTerminalReady;
  boolean requestToSend;
  boolean break;
};
dataTerminalReady
데이터 터미널 레디 (DTR)
requestToSend
리퀘스트 투 센드 (RTS)
break
브레이크

4.9 getSignals() 메서드

getSignals() 메서드의 단계는 다음과 같습니다:
  1. promise새로운 promise로 둡니다.
  2. this.[[state]]"opened"가 아니라면, promise를 "InvalidStateError" DOMException으로 reject하고 promise를 반환합니다.
  3. 다음 단계들을 병렬로 수행합니다:
    1. 연결된 장치가 assert할 수 있는 제어 신호들의 상태를 운영체제에 쿼리합니다.
    2. 운영체제가 어떠한 이유로든 이러한 신호의 상태를 결정하지 못하면, 글로벌 태스크 큐관련 글로벌 객체에 생성하고, thisserial port task sourcepromise를 "NetworkError" DOMException으로 reject하며 이 단계를 중단합니다.
    3. "data carrier detect" 또는 "DCD" 신호가 장치에서 assert되었으면 dataCarrierDetecttrue, 아니면 false로 둡니다.
    4. "clear to send" 또는 "CTS" 신호가 assert되었으면 clearToSendtrue, 아니면 false로 둡니다.
    5. "ring indicator" 또는 "RI" 신호가 assert되었으면 ringIndicatortrue, 아니면 false로 둡니다.
    6. "data set ready" 또는 "DSR" 신호가 assert되었으면 dataSetReadytrue, 아니면 false로 둡니다.
    7. signalsordered map «[ "dataCarrierDetect" → dataCarrierDetect, "clearToSend" → clearToSend, "ringIndicator" → ringIndicator, "dataSetReady" → dataSetReady ]»로 둡니다.
    8. 글로벌 태스크 큐관련 글로벌 객체에 생성하고, thisserial port task sourceresolve promisesignals를 전달합니다.
  4. promise를 반환합니다.

4.9.1 SerialInputSignals 딕셔너리

WebIDLdictionary SerialInputSignals {
  required boolean dataCarrierDetect;
  required boolean clearToSend;
  required boolean ringIndicator;
  required boolean dataSetReady;
};
dataCarrierDetect 멤버
데이터 캐리어 디텍트 (DCD)
clearToSend 멤버
클리어 투 센드 (CTS)
ringIndicator 멤버
링 인디케이터 (RI)
dataSetReady 멤버
데이터 셋 레디 (DSR)

4.10 close() 메서드

close() 메서드의 단계는 다음과 같습니다:

  1. promise새 promise로 둔다.
  2. this.[[state]]"opened"가 아니면, promise를 "InvalidStateError" DOMException으로 reject하고 promise를 반환한다.
  3. cancelPromisecancelthis.[[readable]]에 호출한 결과값 또는 undefined로 resolve된 promise로 둔다. this.[[readable]]null일 경우.
  4. abortPromiseabortthis.[[writable]]에 호출한 결과값 또는 undefined로 resolve된 promise로 둔다. this.[[writable]]null일 경우.
  5. pendingClosePromise새 promise로 둔다.
  6. this.[[readable]]this.[[writable]] 모두 null이면, pendingClosePromise를 undefined로 resolve한다.
  7. this.[[pendingClosePromise]]pendingClosePromise로 설정한다.
  8. combinedPromise여러 promise를 모두 기다리는 promise로서 «cancelPromise, abortPromise, pendingClosePromise»로 둔다.
  9. this.[[state]]"closing"으로 둔다.
  10. combinedPromise 반응으로 다음을 실행한다.
  11. promise를 반환한다.

4.11 forget() 메서드

forget() 메서드의 단계는 다음과 같습니다:

  1. 사용자 에이전트가 이 동작을 수행할 수 없다면(예: 관리자가 허용한 권한일 때), undefined로 resolve된 promise를 반환한다.
  2. 다음 단계를 병렬로 실행한다:
    1. this.[[state]]"forgetting"으로 설정한다.
    2. 기존에 requestPort() 호출 결과로 사이트에 접근 허가된 시리얼 포트 목록에서 this를 제거한다.
    3. this.[[state]]"forgotten"으로 설정한다.
    4. 글로벌 태스크 큐 생성관련 글로벌 객체에 하고 thisserial port task sourcepromise resolvepromiseundefined로 한다.
  3. promise를 반환한다.

5. 차단 목록

이 명세는 웹사이트가 접근할 수 있는 포트 집합을 제한하기 위해 https://github.com/WICG/serial 리포지토리의 차단 목록 파일에 의존합니다.

Bluetooth 서비스 클래스 ID 차단 목록을 분석한 결과는 URL url에서 커스텀 서비스 ID를 나타내는 리스트UUID값입니다.

Serial Port Profile 서비스 클래스 ID는 값이 "00001101-0000-1000-8000-00805f9b34fb"인 BluetoothServiceUUID 입니다.

{{BluetoothServiceUUID} serviceUuid}가 만약 다음 단계를 거쳐 true를 반환하면 차단된 Bluetooth 서비스 클래스 UUID입니다:

  1. uuidBluetoothUUID.getService()serviceUuid로 호출한 결과로 둡니다.
  2. blocklistBluetooth 서비스 클래스 ID 차단 목록을 분석https://github.com/WICG/serial/blob/main/blocklist.txt의 결과로 둡니다.
  3. 만약 blocklist포함하고 있다면 uuid를, true를 반환합니다.
  4. 만약 uuid같으며, Serial Port Profile 서비스 클래스 ID와, false를 반환합니다.
  5. 만약 uuid다음 문자열로 끝나면 "-0000-1000-8000-00805f9b34fb", true를 반환합니다.
  6. 그 밖의 경우 false를 반환합니다.

6. 통합

6.1 권한 정책

이 명세는 serial 속성이 Navigator 객체에 노출할 메서드의 사용 여부를 제어하는 기능을 정의합니다.

이 기능의 이름은 "serial"입니다.

이 기능에 대한 기본 허용 사용 목록(allowlist)'self'입니다.

7. 보안 고려사항

이 절은 비규범적(참고용)입니다.

이 API는 [WEB-BLUETOOTH] 및 [WEBUSB]과 비슷한 보안 위험을 내포하며 이들로부터 얻은 교훈이 본 명세에도 적용됩니다. 주요 위협은 다음과 같습니다: 위와 같은 모든 공격에 대한 주요 대응책은 requestPort() 패턴이며, 이는 사용자의 상호작용이 필요하고 한 번에 하나의 장치만 접근 권한을 부여하도록 합니다. 이로 인해 사이트가 연결된 모든 장치를 열거하여 취약한 장치가 있는지 확인하는 드라이브-바이 공격이 방지됩니다. 사이트는 장치 접근 요청 시 사용자에게 명확하게 알릴 필요가 있습니다. 구현에서는 또한 사이트가 장치와 통신 중임을 시각적으로 표시하거나, 언제든 권한을 취소할 수 있는 제어 기능을 제공할 수 있습니다.

이 명세는 네트워크 공격자에 의해 악의적 코드가 삽입되는 것을 방지하기 위해 보안 컨텍스트에서 사이트가 제공되어야 함을 요구합니다. 이를 통해 권한 결정 시 사용자에게 표시되는 사이트의 신원이 정확함을 보장합니다. 또한, 본 명세는 교차 출처 iframe에서 API 사용을 허용하기 전에 반드시 [PERMISSIONS-POLICY]를 통한 명시적 설정을 요구합니다. [CSP3]와 함께 사용할 때 이러한 메커니즘은 악의적 코드 삽입 공격으로부터 보호합니다.

남은 문제는 피싱 공격을 통한 장치 악용입니다. 공격자가 사용자를 속여 악성 사이트에 장치 접근 권한을 부여하도록 만들 수 있습니다. 이 공격은 장치의 본래 기능을 악용하거나, 악성 펌웨어를 설치해 나중에 호스트 컴퓨터를 공격하는 데 사용될 수 있습니다. 호스트 소프트웨어는 연결된 장치의 입력을 적절히 검증하지 않아 취약해질 수 있습니다. 보안 연구에 따르면 연결된 장치를 신뢰하지 않는 쪽으로 소프트웨어 벤더의 인식을 유도하고 있습니다.

이러한 유형의 공격을 완전히 막을 수 있는 메커니즘은 없습니다. 페이지에서 장치로 보낼 데이터는 불투명한 바이트 시퀀스이기 때문입니다. 특정 데이터 유형 전송을 차단하려는 시도를 하더라도 장치 제조사가 우회 방법을 마련할 가능성이 높습니다.

사용자 에이전트는 장치 접근을 추가로 통제할 수 있는 메커니즘을 구현할 수 있습니다:

[WEB-BLUETOOTH]와 [WEBUSB] 구현에서도 이러한 대응책이 실험적으로 적용되었으나, 근본적인 한계가 있습니다. 첫째, 특정 장치가 취약한지 정의하는 것이 어렵습니다. 예를 들어, 이 API로 사이트는 마이크로컨트롤러 개발 보드에 펌웨어를 업로드할 수 있습니다. 이는 교육 및 취미 시장에서 흔히 사용되는 핵심 사례입니다. 하지만 이 보드들은 펌웨어 서명 검증을 하지 않아 손쉽게 악성 장치로 변질시킬 수 있습니다. 분명히 취약하지만 차단되어서는 안 되는 사례입니다.

또한, USB 및 Bluetooth의 경우 메타데이터로 해당 장치의 제조사/모델을 식별할 수 있는 out-of-band 기제가 정의되어 있기 때문에, 이러한 취약 장치 목록 관리가 꽤 효과적으로 작동합니다. 하지만 일반 USB/Bluetooth-시리얼 어댑터나 DB-25, DE-9, RJ-45 커넥터 등 "진짜" 시리얼 포트는 연결된 장치의 신원을 알 수 있는 메타데이터가 없기 때문에 접근 차단이 불가능합니다.

8. 개인정보 보호 고려사항

이 절은 비규범적(참고용)입니다.

시리얼 포트와 시리얼 장치에는 두 종류의 민감한 정보가 존재합니다. 포트가 USB 또는 Bluetooth 장치인 경우, 벤더 및 제품 ID(제조사와 모델 명시)는 물론, 일련번호나 MAC 주소가 해당됩니다. 또한 시리얼 장치 자체에 시리얼 포트를 통해 명령어로 얻을 수 있는 자체 식별자가 있을 수 있습니다. 장치에는 이 외에도 식별 가능 여부와 상관없이 추가 개인 정보가 저장될 수 있습니다.

장치 권한을 관리하기 위해, 구현에서는 USB 벤더 ID, 제품 ID, 시리얼 번호 등 장치 식별자를 사용자 환경설정 파일에 저장하여 사용자가 사이트에 접근을 허용한 장치의 안정적인 식별자로 삼을 수 있습니다. 이러한 정보는 사이트에 직접 노출되지 않으며, 권한이 철회되거나 사이트 데이터가 삭제될 때 함께 제거됩니다.

접근 권한이 부여된 후 페이지가 장치로 보낼 수 있는 명령을 통해, 페이지가 장치에 저장된 여러 민감한 정보에 접근할 수 있게 될 수 있습니다. 7. 보안 고려사항에서 언급한 바와 같이 이러한 정보 접근을 원천적으로 차단하는 것은 비현실적이며 바람직하지도 않습니다.

구현에서는 사용자가 어떤 장치에 대해 어떤 사이트에 접근을 허용하는지 완벽한 통제를 제공해야 하며, 사용자 상호작용 없이 권한이 부여되지 않아야 합니다. 이것이 requestPort() 메서드의 설계 목적입니다. 이 방식은 사이트가 연결된 모든 장치를 은밀히 열거/수집하는 것을 막습니다. 이는 파일 선택기 UI와 유사합니다. 사이트는 파일 시스템 구조를 전혀 모르고, 오직 사용자가 직접 선택한 파일/디렉터리만 알 수 있습니다. 구현에서는 사이트가 이런 권한을 사용 중일 때 탭이나 주소창에 아이콘 등 시각적 표시로 알릴 수 있습니다.

"프라이빗" 또는 "시크릿" 브라우징 모드를 지원하는 구현은 반드시 일반 프로필의 권한이 당해 세션에 이월되지 않아야 하며, 세션 내에서 부여된 권한도 세션 종료 시 보존되지 않도록 해야 합니다. 사용자가 이런 세션에서 장치 접근을 허용할 때 경고할 수도 있습니다. 이는 사용자가 수동으로 식별 정보를 입력하는 경우와 마찬가지로, 위에서 언급한 식별자 및 장치와의 통신에서 얻을 수 있는 기타 고유 특성을 통해 세션 간에 사용자를 식별할 수 있기 때문입니다.

사용자는 이 API로 장치 접근 권한을 부여할 때 웹 보안 모델의 기존 경계가 어떻게 깨질 수 있는지 잘 모를 수 있습니다. 보안 UI와 문서는 사이트가 장치에 대한 접근 권한을 부여 받을 경우 해당 장치와 그 안의 데이터 전체에 대한 완전한 제어 권한이 허용됨을 명확히 안내해야 합니다.

9. 적합성

비규범적(참고용)으로 표시된 절 외에도, 본 명세의 모든 작성 가이드라인, 다이어그램, 예시, 참고는 규범적이지 않습니다. 그 밖의 모든 내용은 규범적입니다.

A. 감사의 글

이 문서 개발에 기여한 분들은 다음과 같습니다.

B. 참고 문헌

B.1 규범적 참고문헌

[dom]
DOM Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://dom.spec.whatwg.org/
[html]
HTML Standard. Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
[PERMISSIONS-POLICY]
Permissions Policy. Ian Clelland. W3C. 18 July 2025. W3C Working Draft. URL: https://www.w3.org/TR/permissions-policy-1/
[STREAMS]
Streams Standard. Adam Rice; Domenic Denicola; Mattias Buelens; 吉野剛史 (Takeshi Yoshino). WHATWG. Living Standard. URL: https://streams.spec.whatwg.org/
[WEB-BLUETOOTH]
Web Bluetooth. Jeffrey Yasskin. W3C Web Bluetooth Community Group. Draft Community Group Report. URL: https://webbluetoothcg.github.io/web-bluetooth/
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/

B.2 비규범적 참고문헌

[CSP3]
Content Security Policy Level 3. Mike West; Antonio Sartori. W3C. 11 July 2025. W3C Working Draft. URL: https://www.w3.org/TR/CSP3/
[WEBUSB]
WebUSB API. W3C. Draft Community Group Report. URL: https://wicg.github.io/webusb/