Skip to content

Instantly share code, notes, and snippets.

@novelview9
Last active April 11, 2026 02:08
Show Gist options
  • Select an option

  • Save novelview9/512596a4ae687dc241d06aa203cbf36c to your computer and use it in GitHub Desktop.

Select an option

Save novelview9/512596a4ae687dc241d06aa203cbf36c to your computer and use it in GitHub Desktop.
Ghost Library: 구름 한글 입력기 (gureum/gureum) 아키텍처 증류 — AI 코딩 에이전트용 재구현 스펙

Ghost Library: 루룸 키보드 (Luroom Keyboard)

레퍼런스: gureum/gureum — macOS 한글 입력기 (BSD + LGPL) 핵심 의존: libhangul (C, 한글 조합 상태 머신), Apple InputMethodKit

macOS 전용 한글 입력기. 3가지 핵심 기능에 집중한다:

  1. 모아치기 — 초성/중성/종성 동시 타건, 순서 무관
  2. Vi 모드 — ESC/Ctrl+[ → 자동 영문 전환
  3. 내장 QWERTY — 입력기 내부 한영전환으로 씹힘 제로

아키텍처

참조: OSXCore/InputController.swift, OSXCore/InputReceiver.swift, OSXCore/InputMethodServer.swift

macOS TSM → IMKServer (싱글턴)
  → InputController (IMKInputController)
  → InputReceiver (전처리, 커밋/업데이트 흐름)
  → Composer Chain
      ├─ HangulComposer (libhangul 래핑, 모아치기 포함)
      └─ RomanComposer (내장 QWERTY, 패스스루)

3계층 분리: IMK 인터페이스 → 처리 로직 → 조합 로직


핵심 패턴: Composer Chain

참조: OSXCore/Composer.swift

protocol Composer {
    var delegate: Composer! { get set }
    var composedString: String { get }     // 조합 중 (밑줄)
    var commitString: String { get }        // 확정 대기
    func dequeueCommitString() -> String    // 소비 후 비움
    func cancelComposition()
    func input(text:key:modifiers:client:) -> InputResult
}
  • 기본 구현이 모든 호출을 delegate로 위임
  • 이중 버퍼: composedString(밑줄) / commitString(확정) + dequeue 큐 패턴
  • GureumComposerdelegate를 HangulComposer ↔ RomanComposer로 동적 교체 (Strategy)

기능 1: 모아치기 (동시 타건)

참조: OSXCore/HangulComposer.swift, GureumTests/GureumTests.swift (모아치기 테스트 ~130줄)

동작

초성/중성/종성을 동시에 눌러도 올바른 글자로 조합된다. 타건 순서에 무관.

"ㄱ"+"ㅏ" 동시 → "가"  (순서 무관)
"ㄲ" 동시 타건 → "까"
"ㄱ"+"ㅏ"+"ㄴ" 동시 → "간"

구현

libhangul의 hangulAutoReorder 옵션으로 활성화. HGInputContext가 입력 순서를 내부적으로 재정렬하여 조합.

// Configuration에서 설정
var hangulAutoReorder: Bool  // true → 모아치기 활성화

HangulComposer가 KVO로 이 설정을 감시하여 실시간 반영.

테스트 케이스

gureum 원본에서 검증하는 조합: , , , , , , — 각각 역순 입력과 정순 입력의 결과가 동일한지 + 역순 삭제까지 검증.


기능 2: Vi 모드

참조: OSXCore/GureumComposer.swiftfilterCommand(), OSXCore/Configuration.swift

동작

한글 입력 중 ESC 또는 Ctrl+[ 입력 시:

  1. 현재 조합 중인 한글을 확정(commit)
  2. 자동으로 영문(QWERTY) 모드로 전환
  3. Vim/Neovim에서 Normal 모드 진입 시 별도 한영전환 불필요

구현

// GureumComposer.filterCommand() 내부
if config.romanModeByEscapeKey {
    if key == .escape || (key == .ansiLeftBracket && modifiers.contains(.control)) {
        return .changeLayout(.roman)
    }
}

전환 흐름:

filterCommand() → .changeLayout(.roman)
  → cancelAndCommit() (조합 확정)
  → delegate = romanComposer
  → selectMode() → macOS에 알림

설정

var romanModeByEscapeKey: Bool  // true → Vi 모드 활성화 (기본: true로 설정)

기능 3: 내장 QWERTY (한영전환 씹힘 해결)

참조: OSXCore/RomanComposer.swift, OSXCore/GureumComposer.swift

동작

macOS의 입력기 전환 시스템을 거치지 않고, 입력기 내부에서 한글↔영문을 전환한다. 전환 지연/씹힘이 없음.

구현

GureumComposer가 두 Composer를 소유하고 delegate를 교체:

class GureumComposer: Composer {
    let hangulComposer = HangulComposer()    // 두벌식
    let romanComposer = RomanComposer()      // 내장 QWERTY

    // delegate를 교체하여 모드 전환
    func changeLayout(_ layout: ChangeLayout) {
        cancelAndCommit()  // 현재 조합 확정
        if layout == .hangul {
            delegate = hangulComposer
        } else {
            delegate = romanComposer
        }
        // macOS에 모드 변경 알림
        selectMode(inputMode)
    }
}

RomanComposer는 composedString이 항상 빈 문자열 (조합 상태 없음, 즉시 커밋).


한영전환: CapsLock

참조: OSXCore/InputMethodServer.swiftIOKitty, OSXCore/InputController.swift

동작

입력 동작
CapsLock 짧게 (<0.5초) 한영 전환
CapsLock 길게 (≥0.5초) 원래 CapsLock (대문자 잠금)

구현

macOS가 CapsLock을 입력기 전에 자체 처리하므로, IOHIDManager로 하드웨어 레벨 선점:

class IOKitty {
    // IOHIDManager로 CapsLock 키 이벤트 감지
    // 0.5초 타이머로 짧게/길게 구분
    var capsLockTriggered: Bool

    func handleCapsLock(pressed: Bool) {
        if pressed {
            startTimer(0.5)
        } else {
            if elapsed < 0.5 {
                capsLockTriggered = true  // → 한영전환
                restoreLEDState()         // CapsLock LED 복원
            } else {
                // 원래 CapsLock 동작 유지
            }
        }
    }
}

접근성 권한(IOHIDRequestAccess) 필요.


한글 조합 엔진

참조: OSXCore/HangulComposer.swift, libhangul

조합 흐름

"r"(ㄱ) → process → preedit=[ㄱ]              → 밑줄 "ㄱ"
"k"(ㅏ) → process → preedit=[가]              → 밑줄 "가"
"s"(ㄴ) → process → commit=[가] preedit=[ㄴ]  → "가" 확정, 밑줄 "ㄴ"

libhangul HGInputContext가 초성/중성/종성 상태 머신 관리. HangulComposer는 얇은 래퍼.

유니코드

  • libhangul 출력: 첫가끝(U+1100대) → representableString()으로 호환 자모(U+3131대) 변환
  • 초성/중성 필러(U+115F, U+1160) 제거

자판

두벌식(han2)만 지원. setKeyboard(identifier: "2")로 고정.


설정

참조: OSXCore/Configuration.swift

UserDefaults 싱글턴. 루룸 키보드에 필요한 설정만:

설정 기본값 용도
romanModeByEscapeKey true Vi 모드
hangulAutoReorder true 모아치기
enableCapslockToToggleInputMode true CapsLock 한영전환

핵심 구현 노트

  1. Composer Chain — 한글/영문 전환 = delegate 교체. 확장 시 새 Composer 추가만으로 가능. → Composer.swift
  2. 이중 버퍼 + dequeue — IMK 필수 패턴. 조합 중(밑줄)과 확정을 분리. → InputReceiver.swift
  3. IOHIDManager — CapsLock 짧게/길게 구분은 NSEvent 불가, 하드웨어 선점 필수. → InputMethodServer.swift
  4. libhangul — 한글 조합 복잡성을 캡슐화. 직접 구현하지 않는다. → libhangul

참조 소스

파일 원본 경로 역할
Composer.swift OSXCore/ Composer 프로토콜
GureumComposer.swift OSXCore/ 라우터, Vi 모드, 한영전환
HangulComposer.swift OSXCore/ libhangul 래핑, 모아치기
RomanComposer.swift OSXCore/ 내장 QWERTY
InputController.swift OSXCore/ IMK 이벤트 핸들러
InputReceiver.swift OSXCore/ 입력 처리 오케스트레이터
InputMethodServer.swift OSXCore/ IMKServer + IOKitty(CapsLock)
Configuration.swift OSXCore/ 설정 (3개만)
KeyCode.swift OSXCore/ 하드웨어 키 코드
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment