컨트리뷰터

오픈소스 컨트리뷰션 아카데미

Rust 기반 점자 변환 라이브러리에 한국 점자 규정 제47항(분수 표기)을 구현 유니코드 정규화와 정규식 기반 문맥 인식을 통해 다양한 분수 입력을 일반화된 구조로 처리

0. 계기

오픈소스 기여에 관심은 있었지만, 혼자 이슈를 찾고 방향을 설정하는 것이 부담스러워
6주간 멘토링을 제공하는 오픈소스 컨트리뷰션 아카데미에 지원했습니다.

여러 프로젝트 중 Rust로 작성된 점자 변환 라이브러리 Braillify를 선택했습니다.
Rust는 처음이었지만, 텍스트를 구조적으로 파싱하는 문제라는 점에서
당시 수강 중이던 컴파일러 수업(정규식, 토큰 처리)의 내용을 실제로 적용해볼 수 있을 것이라 판단했습니다.

프로젝트를 살펴보던 중,
한국 점자 규정 중 제47항(분수 표기)이 아직 구현되지 않은 상태임을 확인했습니다.
명세가 비교적 명확하면서도 입력 형식이 다양해
규정 해석과 구조 설계를 함께 경험할 수 있는 과제라 판단해
해당 항목을 직접 구현하기로 결정했습니다.


1. 구현 개요

먼저 한국 점자 규정 제47항을 분석해 요구사항을 정리했습니다.

제47항 분수는 분수표 ⠌(12)을 사용하여 분모, 분수표, 분자 순으로 적고, 대분수는 정수와 분수를 붙여 적는다. [붙임] 분수를 표시하는 빗금(/)은 ⠸⠌(5612)으로 적고, 순서는 묵자를 따른다.

도출한 요구사항은 다음과 같습니다.

  • 일반 분수: 분모 → 분수표(12) → 분자
  • 문맥 내 분수: 분자 → 빗금(5612) → 분모 (숫자 뒤 슬래시)
  • 대분수: 정수와 분수를 붙여서 표기
  • LaTeX 분수($\frac{3}{4}$), 유니코드 분수() 등 다양한 입력 형식 지원

제47항의 핵심은 분수의 표기 순서가 문맥에 따라 달라진다는 점이었습니다.
이에 따라 단순 문자열 치환이 아닌,
입력 형식과 주변 문맥을 해석하는 로직이 필요했습니다.

구현 목표는 다음과 같았습니다.

  • 분수 유형(일반 / 문맥 내 / 대분수) 정확한 구분
  • 다양한 입력 형식을 하나의 처리 흐름으로 통합
  • 규정 변경이나 확장에도 대응 가능한 구조 설계
이를 위해 분수 처리를 전담하는 fraction.rs 모듈을 추가하고,
분수 유형별 인코딩 로직을 분리해 구현했습니다.

2. 구현 내용

유니코드 정규화를 활용한 분수 처리 일반화

유니코드에는 , ¾처럼 하나의 문자로 표현된 분수가 존재합니다.
이들을 개별적으로 처리하는 대신,
유니코드 표준을 활용해 입력 자체를 일반화하고자 했습니다.

NFKD 정규화를 적용하면
유니코드 분수 문자는 다음과 같이 분해됩니다.

⅔ → '2' + '⁄' + '3'

이를 기반으로 정규식을 적용해
모든 유니코드 분수를 동일한 파이프라인에서 처리할 수 있도록 구현했습니다.

let normalized: String = c.nfkd().collect();
// ⅔ → 2⁄3
let re = Regex::new(r"^(\d+)⁄(\d+)$").unwrap();

이 방식은 특정 문자 목록에 의존하지 않으며, 새로운 유니코드 분수 문자가 추가되더라도 별도의 예외 처리 없이 대응할 수 있는 구조입니다.


정규식 기반 문맥 인식 로직

2/3은 분수지만, 12/25는 날짜일 수 있습니다. 한국 점자 규정에서는 문맥 내 분수와 일반 분수의 인코딩 순서가 다르기 때문에, 이를 정확히 구분하는 것이 중요했습니다.

단순 숫자 패턴 매칭이 아닌,

  • 앞뒤 토큰과의 관계
  • 날짜로 해석될 가능성
  • 범위 표현(1/4~1/8) 여부

를 함께 고려하는 정규식 기반 문맥 판단 로직을 구현했습니다.

if !looks_like_date(word, prev_word, next_word) {
    encode_fraction_in_context(...)
}

이 과정에서 컴파일러 수업에서 배운 정규 표현식과 토큰 단위 처리 개념을 실제 서비스 코드에 적용해볼 수 있었습니다.


3. 사용한 의존성과 선택 이유

이번 구현에서는 문제를 일반화된 방식으로 처리하기 위해 아래 의존성을 추가했습니다.

unicode-normalization

유니코드 분수 문자를 표준에 따라 NFKD 정규화로 분해하기 위해 사용했습니다. 특정 문자 목록에 의존하지 않고, 모든 호환 분수 문자를 동일한 로직으로 처리할 수 있었습니다.

regex

분수, 날짜, 범위 표현 등 숫자 패턴을 문맥에 따라 구분하기 위해 사용했습니다. 규칙을 명시적으로 표현할 수 있어 점자 규정의 조건을 코드로 옮기기에 적합했습니다.

once_cell

정규식을 반복 컴파일하지 않도록 캐싱하기 위해 사용했습니다. 문맥 판단 로직을 여러 번 호출하더라도 성능 저하 없이 안정적으로 동작하도록 했습니다.


4. 성과

  • 한국 점자 규정 제47항(분수 표기) 구현
  • 유니코드 정규화 기반의 확장 가능한 분수 처리 구조 설계
  • PR #100 메인 브랜치 머지
  • Codecov 기준 테스트 커버리지 100% 달성

5. 회고

이번 기여는 규모가 큰 기능은 아니었지만, 명세를 정확히 해석하고 이를 일반화된 코드로 옮기는 경험을 할 수 있었습니다.

특히 하드코딩으로 문제를 해결하기보다, 유니코드 표준과 정규식을 활용해 입력 자체를 일반화하는 접근이 장기적으로 유지보수에 더 적합하다는 것을 배웠습니다.

또한 처음 사용하는 언어(Rust)와 낯선 도메인(점자 규정)에서도 문서를 기반으로 요구사항을 정리하고 단계적으로 구현해 나가면 충분히 의미 있는 기여를 할 수 있다는 자신감을 얻었습니다.