갤럭시 천지인을 그리워하는 아이폰 유저들을 위한 키보드
⌨️
물론이죠. 앱스토어에서 설치해보세요. GitHub 저장소도 있답니다.
애플 아이폰을 찾는 소비자들이 증가하고 있다. 눈에 띄는 부분은 아이폰을 찾는 중장년층 소비자가 늘어나고 있다는 점이다. 대부분의 사람들은 갤럭시에서 아이폰으로 못 넘어오는 이유로 통화녹음과 삼성페이를 꼽는데, 부모님이 아이폰으로 바꾸신 뒤에 내가 관찰한 바는 조금 달랐다.
아무도 손에 꼽지 않은 예상 외의 난관은 바로 키보드였다. 대한민국 소비자들은 10키 휴대전화 시절부터 문자를 입력함에 아무런 불편함이 없었다. 세종의 제자 원리를 본따 만든 천지인이라는 강력한 입력 방식 때문에, 하나의 버튼에 알파벳이 3개, 4개씩 붙어있는 영미권에 비해 쿼티 키보드의 필요성이 현저히 적었기 때문이다. 태어났을 당시부터 스마트폰이 존재하던 알파 세대가 아닌 이상 여전히 천지인 키보드를 사용하고 계시는 분들이 많다.
2010년 천지인의 특허가 개방된 이후 아이폰에도 2013년부터 천지인 키보드가 추가되었지만, 이상하게도 아이폰은 키보드의 모양이 조금 달 랐다. 가장 결정적인 차이점은 자리넘김 버튼과 띄어쓰기 버튼이 따로 존재한다는 점이다.
- 갤럭시:
ㅇ
ᆞ
ㅡ
→ 띄어쓰기 →ㅇ
ㅣ
ᆞ
ㄴ
→ 띄어쓰기 →ㄴ
ᆞ
ᆞ
ㅣ
ㅇ
- 아이폰:
ㅇ
ᆞ
ㅡ
→ 띄어쓰기 →ㅇ
ㅣ
ᆞ
ㄴ
→ 자리넘김 →ㄴ
ᆞ
ᆞ
ㅣ
ㅇ
이와 같이 2가지 다른 버튼이 따로 존재하는 것 뿐만 아니라 버튼의 각 크기도 더욱이 작아져, 오타가 지속적으로 발생하는 등 사용에 불편함을 호소하는 사람들이 많았다. 이런 이유로 갤럭시 천지인과 유사한 아이폰 키보드를 만들어보고 싶다는 결론에 이르렀다.
학습 없이 쉽게 사용할 수 있는 아이폰용 천지인 키보드를 만들어 보자!
이 프로젝트의 연구 기록도 공개되어 있다.
📜 특허권 및 법적 권리
우선 특허권과 법적 권리에 아무런 문제가 없는지를 확인했다. 조사한 결과, 조관현 특허권 보유자님께서 특허권을 정부에 기증하셔 국가 표준으로 채택된 이후, 한글 자판에 대한 특허권 사용권이 무상으로 허용되었다. 이에 아무런 문제가 없음을 확인한 후 개발에 착수했다.
🛠 기술 정하기
아이폰 자판을 만들기 위해 애플의 커스텀 키보드 만들기 문서를 정독했다. 확인 결과 일반적인 아이폰 앱을 제작하는 난이도와 비슷해보였다. 일단 ViewController 내에 버튼과 로직을 때려박아 개발하는 것은 쉬워보였으나, SwiftUI를 이용한 iOS 개발을 한 적이 없어 SwiftUI로 개발해보고 싶었다. 처음에는 새로 나온 SwiftUI Grid 기능을 쓰면 깔끔하게 버튼을 배열할 수 있을 듯했는데, 이는 사진 앱처럼 수많은 엘리먼트들을 화면에 배열하는 것에 더 최적화되어있고, 나의 경우처럼 버튼의 개수가 정해져있는 경우에는 (웹에서의 display: flex와 유사한) HStack과 VStack으로 충분하다고 판단했다.
아이폰 써드파티 키보드는 익스텐션이라는 독특한 구조를 이용해 제작한다. iOS 앱 본체가 아니면 전부 익스텐션이라고 생각하면 된다. 커스텀 키보드도 익스텐션이고, iOS 위젯도 익스텐션이고, 애플 워치 앱에도 익스텐션이 탑재된다. Ray Wenderlich의 문서를 읽으며 간단한 데모들을 제작하며 키보드 익스텐션에 대한 이해를 높였다.
가운데 이미지의 "ㅇ" 근처의 회색 배경은 iOS 기기의 NSRange와 setMarkedText라는 기능이었다. 입력 중 인 글자에 영역 처리를 해서 입력을 도와주는 기능이었는데, 중국어의 한어병음(Pinyin)처럼 문자 입력 직전에 조합용으로 사용되는 것으로 천지인용으로 적절하지 않다고 판단했다. 또하나 흥미로운 점으로 아이폰 기본 자판의 색상들은 기본으로 제공되는 어떤 systemColor와도 달랐다. 색깔을 Color Meter로 뽑아 하나하나 입력했다.
😶🌫️ 그런데 천지인은 어떻게 만들지
천지인의 입력 로직을 구현하기 위해 찬찬히 생각을 하던 중, 이게 대단히 복잡하다는 것을 알게 되었다. 예를 들어 다음의 경우를 생각해보자.
않
을 입력하기 위해안ㅅ
이 입력되어 있는 경우에ㅅㅎ
버튼을 누르면않
이 되어야 한다. 이전 글자와 재결합이 가능한지 판단해야 한다.앉
에서ㅡ
를 입력하면안즈
가 되어야 한다. 마지막 종성 하나가 추출 가능한지 확인해야 한다. 이전 글자에서 추출이 가능한지 판단해야 한다.깚
에서ㅂㅍ
키를 누르면깔ㅃ
이 되어야 한다. 즉 종성 추출 + 된소리 변경이 가능한지 판단해야 한다.갌
에서ㅅㅎ
키를 누르면갏
이 되어야 한다. 즉, 단순하게ㅅ
,ㅎ
,ㅆ
를 상호 변경하는 것 뿐만 아니라,ㄽ
등의 겹받침의 변환도 판단해야 한다.
이 외에도 수많은 경우가 많다. 조합형 한글을 나열해두고 하나하나 계산을 한다고 하더라도, 위 경우의 수를 모두 고려하는 것은 매우 어렵다. 유한상태기계로 제작할 경우 두벌식보다 훨씬 많은 약 20개 정도의 데이터 저장 스택과 수십 가지의 상태가 필요하다고 판단했다. (정확히 계산하지는 않았으니 더 단순한 구현 로직이 존재할 수도 있다. 혹시라도 이 방식대로 진짜 만들어보고 싶은 사람이 있다면 이 특허의 다이어그램을 참고하자.) 인터넷 상에 몇 가지 구현 로직들을 발견하기는 했지만, 전부 길고 복잡해 Swift로의 번역을 차치하고 코드 자체를 이해하는데도 한세월이 걸릴 듯 했다. 그러다 문득 이런 생각이 들었다.
그냥 전부 하드코딩하면 되잖아?
생각해보면 같은 버튼 조합을 입력하면 같은 글자로 찍혀야하는 것이 바로 키보드이리라. 그냥 모든 경우의 수를 생성해서 하나의 거대한 JSON 파일에 넣어버리면 어떨까! 단순하게 셈을 해보아도 한글은 약 11,000자 밖에 안 되고, 앞뒤 글자까지 같이 고려하는 케이스만 고려한다고 해도 조합은 많아야 10만 단위를 넘어가지 않을 것으로 판단했다. JSON의 크기는 2MB 안팎에서 넘어가지 않을 것이다.
과거처럼 임베디드 하드웨어에서 KB 단위로 골프를 치며 메모리 최적화를 해야하는 시대가 아니다. 한글이 인류와 함께하는 이상 분명 미래의 누군가가 천지인을 다시 제작할 일이 있을 것이고, 그를 위해서는 누군가가 온전한 천지인 지도를 만들 가치 가 충분히 존재한다.
🖨️ 활자: 세상에서 가장 단순한 천지인 구현체
그로 인해서 활자를 만들었다. 천지인의 모든 상태와 조합을 담아놓은 한글 지도 🗺️ 이다. 총 5만 여개의 입력 케이스가 존재하며, 압축된 JSON은 500KB 정도 크기이다.
고차원적 기능을 구현하기 위해서는 몇 가지 기교들이 더 필요하지만 (백스페이스 키를 눌렀을 때 글자 단위로 지워지는 것이 아니라 자소 단위로 지워진다거나, 시간에 따른 자리넘김 처리를 한다거나) 궁극적으로 핵심 입력 로직은 다음과 같이 단순하다.
const type = (이전: string, 활자: hwalja, 키: string, 수정중: boolean) => {
const 마지막한글자 = 이전.slice(-1)
const 마지막두글자 = 이전.slice(-2)
if (수정중 && 마지막두글자 in 활자[키]) return 이전.slice(0, -2) + 활자[키][마지막두글자]
if (수정중 && 마지막한글자 in 활자[키]) return 이전.slice(0, -1) + 활자[키][마지막한글자]
return 이전 + 활자[키]['']
}
단 5줄 만으로 천지인 로직 구현이 가능하니 나는 감히 이것을 전 세계에서 가장 단순한 천지인 구현체라고 칭하겠다.
전처리 방식이 궁금한 사람들이 있을텐데,
입력 가능한 11,000여 자의 한글 글자들을 종착점으로 삼아
그 글자를 생성하기 위해 마지막으로 눌러야했을 글쇠는 무엇인지,
그리고 그 글쇠를 누르기 전 상태는 무엇인지 역산했다.
예를 들어 역
이 있다면 이전 상태는 여
이고 ㄱ
을 눌러서 역
에 도달했겠군하는 식으로 역으로 계산한 것이다.
물론 이에 더해 여러 엣지 케이스들을 처리해야 했다.
4년 전 조성현이 나를 많이 도와주었다.
다음 창은 활자를 이용해 간단하게 구현해본 천지인 입력 시연이다.
활자는 모든 플랫폼에 사용할 수 있도록 공개했다.
위 데모로 직접 활자를 입력해보자!
가장 단순한 구현체이지 가 장 가벼운 구현체는 아니라는 점을 명심하자.
조합형 한글로 문자열 정규화를 하면 좀 더 경우의 수가 줄지 않나요?
활자 프로젝트에 대해 이성광 님께서 NFD로 문자열 정규화를 해서 초중종성을 떼어놓고 만들면 좀 더 경우의 수가 줄지 않을까에 대한 지적을 해주셨다.
나는 완성형 한글만 놓고 생각했는데, 말씀하신대로 조합형으로 제작 후 정규화를 거치면 경우의 수가 확실히 많이 준다.
예를 들어 안 ᄂᆞᆞㅣㅇ
같이 풀어두고 ᆞᆞㅣ
부분만 ㅕ
로 조합한 뒤, ㄴㅕㅇ
을 정규화 과정을 통해 녕
으로 변환하는 것이다.
활자 프로젝트의 경우 현 접근을 유지하기로 했다.
활자는 가장 쉽고 단순한 천지인 구현체를 지향하는 만큼
현재의 접근이 substring + replace
만으로 구현할 수 있기 때문이다.
만약 NFD와 정규화에 대한 정보를 추가해야한다면,
비록 활자 프로젝트 자체는 가벼워지겠지만,
그를 사용하는 개발자 측에서 NFD와 정규화에 대한 추가적인 학습 및 구현이 필요하다.
오토마타를 이용한 학습 곡선에 불편함을 느껴
모든 정보를 하드코딩해 가장 단순한 형태로 구현하기로 한 활자 프로젝트의 본 목적에 어긋난다.
더불어 이미 압축된 버전은 500KB 수준이기에 입력 엔진으로 사용하기에 부담이 되는 크기가 아니다.