키보드 맵

커뮤니티 그룹 초안 보고서,

이번 버전:
https://wicg.github.io/keyboard-map/
이슈 추적:
GitHub
편집자:
(Google)
설명 문서:
키보드 맵 설명 문서

개요

이 명세는 웹사이트가 주어진 code 값을 사용하여 사용자에게 해당 키를 식별할 수 있도록 보여줄 수 있는 올바른 key 값으로 변환할 수 있게 하는 API를 정의합니다. code 에서 key 로의 변환은 사용자가 현재 선택한 키보드 레이아웃을 기반으로 합니다. 이 API는 키보드를 버튼 집합처럼 다루고 사용자에게 그 버튼을 설명해주어야 하는 웹 애플리케이션에서의 사용을 목적으로 합니다.

문서 상태

이 명세는 Web Platform Incubator Community Group에서 발행한 것입니다. W3C 표준이 아니며, W3C 표준 트랙에도 있지 않습니다. W3C 커뮤니티 기여자 라이선스 협약 (CLA)에 따라 제한적 옵트아웃 및 기타 조건이 적용됩니다. W3C 커뮤니티 및 비즈니스 그룹에 대해 자세히 알아보세요.

이 문서는 에디터 초안으로, 첫 공개 작업 초안(First Public Working Draft)으로 제안된 상태입니다.

1. 소개

KeyboardEvent에서 code 속성은 눌린 키의 물리적 위치를 나타내는 값을 인코딩합니다. 이 값은 현재 로케일(예: "en-US"), 레이아웃(예: "dvorak"), 변환 키 상태(예: "Shift + Control")를 무시하므로, 키보드를 범용 버튼 집합으로 쓰고자 하는 앱(게임 등)에 이상적입니다. code 속성의 개념은 각 물리 키에 대해 플랫폼에 구애받지 않는 스캔코드를 제공하는 것에 있습니다.

반면 key 속성은 로케일, 레이아웃, 변환 키를 고려하여 키를 눌렀을 때 생성되는 값을 담습니다. 거의 모든 유니코드 문자가 유효한 `key` 속성이 될 수 있고, 여러 특수 지정값도 있습니다(KeyboardEvent key 속성 값 참고), 수천 가지 key 값이 존재할 수 있습니다.

대부분의 사용자는 자신의 로케일과 레이아웃에 맞는 실제 키보드를 사용하므로, key 값이 키캡에 인쇄된 문자와 동일할 것이라 간주할 수 있습니다. 사용자가 다른 레이아웃을 선택했다면 키캡과 입력 결과가 다름을 인지하고 있을 것입니다.

이 명세의 API는 이 기본적인 codekey 매핑을 쉽게 획득하는 방법을 제공합니다.

2. 키보드 맵 API

Keyboard Map API는 Keyboard 인터페이스를 확장하여 현재 키보드 레이아웃과 관련된 속성과 메서드를 추가합니다.

keyboard 속성은 Navigator 객체에 있으며 Keyboard-Lock에서 정의되어 있습니다.

2.2. KeyboardLayoutMap 인터페이스

KeyboardLayoutMap

현재 한 가지 엔진에서만 지원됨.

Firefox없음Safari없음Chrome69+
Opera55+Edge79+
Edge (레거시)없음IE없음
Android용 Firefox없음iOS Safari없음Android용 Chrome69+Android WebView69+삼성 인터넷10.0+Opera Mobile48+
[Exposed=Window]
interface KeyboardLayoutMap {
  readonly maplike<DOMString, DOMString>;
};

KeyboardLayoutMapcode 값을 key 값에 대응시키는 읽기 전용 맵 모음입니다.

2.3. Keyboard 인터페이스

Keyboard

현재 한 가지 엔진에서만 지원됨.

Firefox없음Safari없음Chrome68+
Opera55+Edge79+
Edge (레거시)없음IE없음
Android용 Firefox없음iOS Safari없음Android용 Chrome68+Android WebView68+삼성 인터넷10.0+Opera Mobile48+
partial interface Keyboard {
  Promise<KeyboardLayoutMap> getLayoutMap();

  attribute EventHandler onlayoutchange;
};

참고: 기본 Keyboard 인터페이스는 [Keyboard-Lock]에서 정의되어 있습니다.

2.3.1. getLayoutMap()

Keyboard/getLayoutMap

현재 한 가지 엔진에서만 지원됨.

Firefox없음Safari없음Chrome69+
Opera56+Edge79+
Edge (레거시)없음IE없음
Android용 Firefox없음iOS Safari없음Android용 Chrome69+Android WebView69+삼성 인터넷10.0+Opera Mobile48+

getLayoutMap()이 호출되면, 사용자 에이전트는 다음 단계를 실행해야 합니다:

  1. p라는 새 Promise를 생성.

  2. this관련 전역 객체연결된 Document"keyboard-map" 정책 제어 기능을 사용할 수 없으면

    1. p를 "SecurityError" DOMException으로 거부합니다.

    2. p를 반환합니다

  3. 다음 단계를 병렬로 실행:

    1. 비어 있는 KeyboardLayoutMap map을 만든다.

    2. Writing System Keys 표의 "KeyboardEvent code" 열의 각 code 값에 대해

      1. layout을 우선순위가 가장 높은 ASCII-입력 가능 키보드 레이아웃 또는 해당 레이아웃이 없으면 사용 가능한 것 중 우선순위가 가장 높은 키보드 레이아웃으로 한다.

      2. codelayout에서 유효한 키가 아니면 다음으로.

      3. key를 변환 키 없음 상태에서 layoutcode로 생성할 key 값으로 정한다.

      4. key데드 키라면

        1. key데드 키의 독립적 문자 표에 정해진 데드 키의 단독 문자로 설정한다.

      5. <code, key> 쌍으로 map entry e 생성

      6. emap에 추가한다.

    3. map으로 p를 resolve한다.

  4. p를 반환한다.

사용자 에이전트는 키보드 레이아웃이 바뀔 때마다 캐시를 업데이트(또는 무효화)한다면 map을 캐싱해 반환할 수 있습니다.

게임에서 어떤 키를 눌러야 하는지 안내하려면:
navigator.keyboard.getLayoutMap().then(function(map) {
  var keyUp = map.get("KeyW");
  showUserDialog("Press " + keyUp + " to move up.");
});
"US International" 키보드에서 작은따옴표 (') 키는 뒤따르는 문자에 악센트를 더하는 데드 키입니다. 이때 키보드 맵 엔트리는 Quote (code 값)에서 "'"(U+0027) 문자로 매핑됩니다.

2.4. 데드 키 및 조합 문자

키보드 맵 API의 목적이 사용자의 키보드에 대한 사람이 읽을 수 있는 설명을 제공하는 것이므로, 데드 키 또는 조합 문자는 직접 사용할 수 있도록 독립적인 형태로 변환되어야 합니다.

다음 표는 일반적인 데드 키 및 조합 문자를 독립적인 문자로 매핑하는 방법을 정의합니다.

표 1: 데드 키의 독립형 동등 문자
데드 키
이름
유니코드
조합
독립형
문자
유니코드
독립형
Grave U+0300 "`" U+0060
Acute U+0301 "'" U+0027
Circumflex U+0302 "^" U+005e
Tilde U+0303 "~" U+007e
Diaeresis U+0308 "¨" U+00a8

2.5. ASCII 입력 가능 키보드 레이아웃

ASCII 입력 가능 키보드 레이아웃은 다음과 같습니다:

공통 문자 입력 키는 모든 키보드 레이아웃에 공통적으로 있는 키이며, 이는 UIEvents-Code 명세[UIEvents-Code]에서 파란색으로 표시된 그림 13에 나타납니다.

3. 키보드 이벤트

3.1. layoutchange 이벤트

사용자 에이전트가 현재 키보드 레이아웃이 변경된 것을 감지하면, 반드시 layoutchange 이벤트를 발생해야 합니다. 레이아웃 변경 타이밍은 플랫폼에 따라 다르지만, 일반적으로 사용자의 직접적인 동작(예: 사용자가 새 레이아웃 선택)이나 선호 레이아웃을 가진 앱으로 전환 등 간접적으로도 자주 발생합니다.

다음 항목에 유의하세요:

사용자 에이전트가 포그라운드가 아닐 때 키보드 레이아웃이 변경됐다면, 포커스를 다시 얻을 때 반드시 layoutchange 이벤트를 발생해야 합니다.

이 이벤트 처리 예시:
navigator.keyboard.addEventListener("layoutchange", function() {
  // 사용자 키보드 맵 설정 갱신
  updateGameControlSettingsPage();
});

3.1.1. layoutchange 이벤트 발생

"layoutchange" 이벤트를 발생하려면, 사용자 에이전트의 keyboard 속성이 할당된 Navigator 객체에 layoutchange라는 이벤트를 발생시켜야 합니다.

4. 통합

4.1. 권한 정책

이 명세는 getLayoutMap() 메서드가 Keyboard 인터페이스에 노출될지 제어하는 기능을 정의합니다.

이 기능의 이름은 "keyboard-map"입니다.

이 기능의 기본 allowlist는 "self"입니다.

5. 모바일 기기 관련 사항

이 API는 키보드 중심의 API이며, 모바일 기기는 물리 키보드가 일반적으로 없으므로 보통 이 API가 제공되거나 지원되지 않습니다.

그러나 모바일 기기에서 물리 키보드 연결을 지원한다면, 플랫폼에 맞는 문자 입력 키의 일부 집합을 반환하는 방식으로 이 API를 구현할 수 있습니다.

사용자가 물리 키보드 레이아웃을 직접 설정해야 하는(기본값이 없는) 플랫폼에서는, 항목이 없는 레이아웃 맵을 반환하는 방식으로 이 API를 지원할 수 있습니다.

6. 보안 관련 사항

이 API는 정적인 데이터만 반환하며 시스템 상태를 변경하지 않으므로 별도의 보안 이슈가 없습니다.

7. 개인정보 보호 관련 사항

이 API를 포함해, 현재 기기 상태 정보를 제공하는 모든 API는 사용자의 "fingerprint"를 조합해 식별 가능성을 높일 위험이 있습니다.

실제 활성 레이아웃 대신 가장 우선순위가 높은 ASCII 입력 가능 키보드 레이아웃을 반환함으로써, 이런 정보의 지문 추적 가치는 낮아집니다. (서로 같은 값을 반환하는 사용자가 많아짐)

아래와 같은 상황에서는 레이아웃 정보가 사용자 식별에 활용될 수 있습니다:

이 API가 없어도, 유사한 지문 추출이 가능하긴 하지만 사용자가 직접 타이핑하고 그 결과 KeyboardEvent를 분석해야 하므로 더 어렵습니다.

7.1. 프라이버시 완화책

사용자 보호의 1차 수단으로, 본 명세는 API를 보안 컨텍스트에서만, 그리고 현재 활성 최상위 브라우징 컨텍스트에서만 또는 정책 제어 기능을 통해 접근 가능하도록 요구합니다.

프라이버시 영향을 걱정하는 사용자 에이전트는 다음과 같은 추가 완화책도 고려할 수 있습니다:

7.2. 프라이버시 모드

사용자 에이전트가 "시크릿" 또는 "프라이버시 모드"를 제공하더라도, 이 API는 평소와 동일하게 동작해야 합니다. 그 이유는 사용자의 프라이버시를 완전히 보장할 수 있는 중립 값이 존재하지 않기 때문입니다.

시크릿/프라이버시 모드에서 반환할 값을 사용자가 직접 선택할 수 있게 할 수도 있지만, 이 값이 (해외로 이동할 때 등) 적절히 갱신되지 않으면 오히려 보안 착각을 줄 수 있으니 주의해야 합니다.

8. 감사의 글

이 제안서의 논의에 참여해주신 분들께 감사드립니다:

Hadley Beeman (W3C TAG), Joe Downing (Google), Masayuki Nakano (Mozilla), Julien Wajsberg (Mozilla)

9. 용어 해설

스캔코드

키보드 하드웨어가 각 키에 고유하게 부여하는 값입니다. 자세한 정보는 https://en.wikipedia.org/wiki/Scancode 참고.

적합성

문서 관례

적합성 요구사항은 설명적 단언과 RFC 2119 용어의 조합으로 표현됩니다. 이 문서의 규범적 부분에 등장하는 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL”이라는 핵심 단어들은 RFC 2119의 설명에 따라 해석되어야 합니다. 다만 가독성을 위해, 이 용어들은 본 명세에서 모두 대문자로 표기되지 않습니다.

이 명세의 모든 본문은 비규범, 예제, 노트로 명시된 일부 섹션을 제외하고는 모두 규범적입니다. [RFC2119]

이 명세의 예제는 “for example”로 시작하거나 class="example"로 본문과 구분하며, 아래와 같이 나타납니다:

이것은 참고용 예시입니다.

참고 노트는 “Note”로 시작하며, class="note"로 본문과 구분하며, 아래와 같이 나타납니다:

참고: 이것은 참고용 노트입니다.

적합한 알고리즘

알고리즘 내에서 명령형 표현(예: "선행 공백 문자를 제거", "false를 반환하고 이 단계를 중단")으로 기술된 요구사항은 알고리즘 소개에 사용된 핵심 단어("must", "should", "may" 등)의 의미에 따라 해석되어야 합니다.

알고리즘 또는 구체적 단계로 기술된 적합성 요구는 최종 결과가 동등하다면 어떤 방식으로든 구현할 수 있습니다. 특히 본 명세에 정의된 알고리즘은 이해하기 쉽게 작성된 것이며 성능을 우선한 것은 아닙니다. 구현자는 자체 최적화를 장려합니다.

색인

이 명세에서 정의된 용어

참조로 정의된 용어

참고문헌

규범적 참고문헌

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[Keyboard-Lock]
Keyboard Lock. cg-draft. URL: https://wicg.github.io/keyboard-lock/
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy. 16 July 2020. WD. URL: https://www.w3.org/TR/permissions-policy-1/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[UIEVENTS]
Gary Kacmarcik; Travis Leithead. UI Events. 1 June 2022. WD. URL: https://www.w3.org/TR/uievents/
[UIEvents-Code]
Gary Kacmarcik; Travis Leithead. UI Events KeyboardEvent code Values. 1 June 2017. CR. URL: https://www.w3.org/TR/uievents-code/
[UIEVENTS-KEY]
Gary Kacmarcik; Travis Leithead. UI Events KeyboardEvent key Values. 1 June 2017. CR. URL: https://www.w3.org/TR/uievents-key/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL 색인

[Exposed=Window]
interface KeyboardLayoutMap {
  readonly maplike<DOMString, DOMString>;
};

partial interface Keyboard {
  Promise<KeyboardLayoutMap> getLayoutMap();

  attribute EventHandler onlayoutchange;
};