1. 소개
KeyboardEvent에서
code
속성은 눌린 키의 물리적 위치를 나타내는 값을 인코딩합니다. 이 값은 현재 로케일(예: "en-US"), 레이아웃(예: "dvorak"), 변환 키 상태(예: "Shift + Control")를
무시하므로, 키보드를 범용 버튼 집합으로 쓰고자 하는 앱(게임 등)에 이상적입니다. code
속성의 개념은 각 물리 키에 대해 플랫폼에 구애받지 않는 스캔코드를
제공하는 것에 있습니다.
반면 key
속성은 로케일, 레이아웃, 변환 키를 고려하여 키를 눌렀을 때 생성되는 값을 담습니다. 거의 모든 유니코드 문자가 유효한 `key` 속성이 될 수 있고, 여러 특수 지정값도 있습니다(KeyboardEvent
key 속성 값 참고),
수천 가지 key
값이 존재할 수 있습니다.
대부분의 사용자는 자신의 로케일과 레이아웃에 맞는 실제 키보드를 사용하므로, key
값이 키캡에 인쇄된 문자와 동일할 것이라 간주할 수 있습니다. 사용자가 다른 레이아웃을 선택했다면 키캡과 입력 결과가 다름을 인지하고 있을 것입니다.
이 명세의 API는 이 기본적인 code
→ key
매핑을 쉽게 획득하는 방법을 제공합니다.
2. 키보드 맵 API
Keyboard Map API는 Keyboard
인터페이스를 확장하여 현재 키보드 레이아웃과 관련된 속성과 메서드를 추가합니다.
2.1. Navigator 인터페이스
keyboard
속성은 Navigator
객체에 있으며 Keyboard-Lock에서 정의되어 있습니다.
2.2. KeyboardLayoutMap 인터페이스
현재 한 가지 엔진에서만 지원됨.
Opera55+Edge79+
Edge (레거시)없음IE없음
Android용 Firefox없음iOS Safari없음Android용 Chrome69+Android WebView69+삼성 인터넷10.0+Opera Mobile48+
[Exposed =Window ]interface {KeyboardLayoutMap readonly maplike <DOMString ,DOMString >; };
KeyboardLayoutMap
은 code
값을 key
값에 대응시키는 읽기 전용 맵 모음입니다.
2.3. Keyboard 인터페이스
현재 한 가지 엔진에서만 지원됨.
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()
현재 한 가지 엔진에서만 지원됨.
Opera56+Edge79+
Edge (레거시)없음IE없음
Android용 Firefox없음iOS Safari없음Android용 Chrome69+Android WebView69+삼성 인터넷10.0+Opera Mobile48+
getLayoutMap()이 호출되면, 사용자 에이전트는 다음 단계를 실행해야 합니다:
-
p라는 새
Promise를 생성. -
this의 관련 전역 객체의 연결된 Document가 "keyboard-map" 정책 제어 기능을 사용할 수 없으면
-
p를 "
SecurityError"DOMException으로 거부합니다. -
p를 반환합니다
-
-
다음 단계를 병렬로 실행:
-
비어 있는
KeyboardLayoutMapmap을 만든다. -
Writing System Keys 표의 "KeyboardEvent code" 열의 각 code 값에 대해
-
layout을 우선순위가 가장 높은 ASCII-입력 가능 키보드 레이아웃 또는 해당 레이아웃이 없으면 사용 가능한 것 중 우선순위가 가장 높은 키보드 레이아웃으로 한다.
-
code가 layout에서 유효한 키가 아니면 다음으로.
-
key를 변환 키 없음 상태에서 layout이 code로 생성할
key값으로 정한다. -
key가 데드 키라면
-
key를 데드 키의 독립적 문자 표에 정해진 데드 키의 단독 문자로 설정한다.
-
-
<code, key> 쌍으로 map entry e 생성
-
e를 map에 추가한다.
-
-
map으로 p를 resolve한다.
-
-
p를 반환한다.
사용자 에이전트는 키보드 레이아웃이 바뀔 때마다 캐시를 업데이트(또는 무효화)한다면 map을 캐싱해 반환할 수 있습니다.
2.4. 데드 키 및 조합 문자
키보드 맵 API의 목적이 사용자의 키보드에 대한 사람이 읽을 수 있는 설명을 제공하는 것이므로, 데드 키 또는 조합 문자는 직접 사용할 수 있도록 독립적인 형태로 변환되어야 합니다.
다음 표는 일반적인 데드 키 및 조합 문자를 독립적인 문자로 매핑하는 방법을 정의합니다.
| 데드 키 이름 | 유니코드 조합 | 독립형 문자 | 유니코드 독립형 |
|---|---|---|---|
| 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 입력 가능 키보드 레이아웃은 다음과 같습니다:
-
수정 키 없이 "A"~"Z"의 모든 기본 ASCII 문자를 입력할 수 있어야 합니다.
-
모든 공통 문자 입력 키에 대해 유효하고 출력 가능한 문자를 생성해야 합니다.
공통 문자 입력 키는 모든 키보드 레이아웃에 공통적으로 있는 키이며, 이는 UIEvents-Code 명세의 [UIEvents-Code]에서 파란색으로 표시된 그림 13에 나타납니다.
3. 키보드 이벤트
3.1. layoutchange 이벤트
사용자 에이전트가 현재 키보드 레이아웃이 변경된 것을 감지하면, 반드시 layoutchange 이벤트를 발생해야 합니다. 레이아웃 변경 타이밍은 플랫폼에 따라 다르지만, 일반적으로 사용자의 직접적인 동작(예: 사용자가 새 레이아웃 선택)이나 선호 레이아웃을 가진 앱으로 전환 등 간접적으로도 자주 발생합니다.
다음 항목에 유의하세요:
-
사용 가능한 레이아웃 집합을 수정해도 현재 레이아웃이 바뀌지 않는 한 layoutchange 이벤트가 발생하지 않아야 합니다.
-
물리적 키보드를 추가/제거해도, 시스템이 물리적 키보드 추가/제거에 따라 레이아웃을 바꿔주는 기능이 없는 한 이벤트를 발생하지 않아야 합니다. 즉, 이벤트를 트리거하는 것은 물리 키보드의 추가/제거가 아니라 레이아웃의 변경입니다.
-
현재 레이아웃이 바뀌면, 가장 우선순위가 높은 ASCII 입력 가능 레이아웃이 예전과 동일하더라도 반드시 이벤트를 발생시켜야 합니다.
사용자 에이전트가 포그라운드가 아닐 때 키보드 레이아웃이 변경됐다면, 포커스를 다시 얻을 때 반드시 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 입력 가능 키보드 레이아웃을 반환함으로써, 이런 정보의 지문 추적 가치는 낮아집니다. (서로 같은 값을 반환하는 사용자가 많아짐)
아래와 같은 상황에서는 레이아웃 정보가 사용자 식별에 활용될 수 있습니다:
-
비표준 ASCII 레이아웃(예: 드보락, 코울맥 등)을 사용하는 사용자
-
지역 기본값과 다른 ASCII 레이아웃을 사용하는 사용자(예: 미국 거주자가 UK/프랑스 레이아웃 사용)
이 API가 없어도, 유사한 지문 추출이 가능하긴 하지만 사용자가 직접 타이핑하고 그 결과 KeyboardEvent를
분석해야 하므로 더 어렵습니다.
7.1. 프라이버시 완화책
사용자 보호의 1차 수단으로, 본 명세는 API를 보안 컨텍스트에서만, 그리고 현재 활성 최상위 브라우징 컨텍스트에서만 또는 정책 제어 기능을 통해 접근 가능하도록 요구합니다.
프라이버시 영향을 걱정하는 사용자 에이전트는 다음과 같은 추가 완화책도 고려할 수 있습니다:
-
사이트가 이 API를 사용할 때마다 권한 요청 프롬프트 띄우기
-
항상 "표준" 맵 반환하기 (단, 표준 맵은 지역별로 달라야 하며, 예를 들어 프라이버시 모드에서 US-QWERTY만 반환할 경우 영국 사용자의 식별도가 더 높아질 수 있음)
7.2. 프라이버시 모드
사용자 에이전트가 "시크릿" 또는 "프라이버시 모드"를 제공하더라도, 이 API는 평소와 동일하게 동작해야 합니다. 그 이유는 사용자의 프라이버시를 완전히 보장할 수 있는 중립 값이 존재하지 않기 때문입니다.
시크릿/프라이버시 모드에서 반환할 값을 사용자가 직접 선택할 수 있게 할 수도 있지만, 이 값이 (해외로 이동할 때 등) 적절히 갱신되지 않으면 오히려 보안 착각을 줄 수 있으니 주의해야 합니다.
8. 감사의 글
이 제안서의 논의에 참여해주신 분들께 감사드립니다:
Hadley Beeman (W3C TAG), Joe Downing (Google), Masayuki Nakano (Mozilla), Julien Wajsberg (Mozilla)
9. 용어 해설
- 스캔코드
-
키보드 하드웨어가 각 키에 고유하게 부여하는 값입니다. 자세한 정보는 https://en.wikipedia.org/wiki/Scancode 참고.