프로젝트로 돌아가기

pub.dev 공개 · 제품 적용 완료

Unicode Typing Grader

실제 앱에서 필요해진 채점 로직을 Unicode 대응 Pure Dart OSS로 독립시켰습니다.

개인 앱에서 필요했던 타이핑 채점기를 독립적인 Pure Dart 패키지로 분리해 pub.dev에 공개했습니다. NFC 정규화, grapheme 단위 편집 거리, 버전 정책과 conformance fixture로 같은 입력이 같은 결과를 내는 경계를 정의하고 실제 앱에도 적용했습니다.

기간
2026.08.24 - 현재
담당 범위
패키지 설계·구현·테스트·공개·제품 재적용
개인 OSS
저장소
공개 리포지토리

제품 안의 채점 규칙을 공개 계약으로 만들었습니다

개인 앱의 채점 기능을 만들면서 NFC 정규화, grapheme 단위 비교, 공백·종결부호 정책, active time 기반 CPM은 특정 화면에 속할 책임이 아니라고 판단했습니다.

계산 부분을 Pure Dart로 분리해 한·영·일 문서, 브라우저 예제, conformance fixture와 함께 pub.dev에 공개했습니다. 이후 실제 앱에도 적용해 패키지 밖에 남겨야 할 책임을 확인했습니다.

제품에서 OSS를 사용하는 경계

제품 계약을 유지하면서 채점 계산을 공개 엔진으로 일원화

  1. 01

    연습 화면

    조합이 끝난 입력 전달

  2. 02

    제품 정책

    기존 저장·표시 형식

  3. 03

    얇은 어댑터

    버전과 필드명 매핑

  4. 04

    공개 OSS 엔진

    NFC·grapheme·편집 경로

  5. 05

    제품 결과

    고정소수점 지표 저장

눈에 보이는 한 글자를 문자열 길이와 같다고 보지 않았습니다

한글 완성형·분해형, 일본어 결합 문자, ZWJ로 연결된 이모지는 같은 모양이어도 내부 표현과 code point 수가 다릅니다. String.length나 단순 일치 비교만 사용하면 정답을 오답으로 보거나 이모지 하나를 여러 글자로 셀 수 있었습니다.

엔진은 Flutter widget과 IME lifecycle에 의존하지 않고 같은 문자열·정책·시간에 같은 결과를 내야 했습니다. 짧은 학습 문장을 대상으로 하므로 편집 경로를 설명할 수 있는 구현을 우선하고, O(n×m) 시간·메모리 경계를 README에 명시했습니다.

NFC 정규화 후 extended grapheme cluster로 나누고 Levenshtein distance를 계산했습니다. 동점인 편집 경로에는 고정 우선순위를 두고, 공백과 종결부호는 버전이 붙은 정책으로 처리하며 정확도와 gross CPM은 1000배 정수로 반환합니다.

  • 한글 완성형·분해형, 일본어 kana, 결합 문자, ZWJ 이모지를 테스트.
  • 정책 JSON을 엄격하게 파싱하고 알 수 없는 버전·값·필드를 거절.
  • 공통 입력과 기대 결과를 conformance/v1 fixture로 공개.

포맷·정적 분석·테스트 39개·배포 사전 검증을 통과했고, 공개 저장소 CI에서 Dart 3.8과 stable을 확인합니다.

Unicode 대응은 정규화 함수를 추가하는 데서 끝나지 않고, 무엇을 한 글자로 세며 동점인 오류 경로를 어떻게 설명할지까지 계약으로 고정해야 했습니다.

떼어낸 엔진을 원래 제품에 다시 넣었습니다

패키지 공개 후에도 적용 앱이 내부 복사본을 계속 사용하면 수정 지점이 둘로 나뉘고 Unicode 규칙이나 지표 반올림이 달라질 수 있습니다. 실제 소비자가 없다면 재사용 가능하다는 설명도 설계상의 주장에 머뭅니다.

공개 패키지 모델로 적용 앱의 기존 모델을 바로 교체하면 저장 데이터 호환성을 깨뜨릴 수 있었습니다. OSS 경계를 독립적으로 유지하면서 적용 앱 고유 표현도 보존해야 했습니다.

적용 앱은 pub.dev의 0.1.2를 직접 사용하고 기존 모델과 패키지 모델을 바꾸는 작은 어댑터만 남겼습니다. 공통 계산은 패키지에 모으고 앱 고유의 표시·저장 형태는 변환 지점에서 분리했습니다.

  • 중복 비교·지표 계산 제거했습니다.
  • local fork 없이 pub.dev 0.1.2 사용.
  • 기존 모델 호환 처리를 작은 어댑터로 한정.

실제 개인 앱에 공개 패키지를 적용하고 기존 저장 데이터를 유지한 채 중복 비교·지표 계산을 제거했습니다.

공통 엔진과 제품 계약을 같은 것으로 취급하지 않고 변환 지점을 작게 남겨, 재사용과 기존 호환성을 함께 지킬 수 있었습니다.

적용한 앱에서 채점 결과를 확인한 한국어 UI

unicode_typing_grader 0.1.2를 pub.dev와 GitHub에 공개했습니다. 테스트 39개와 CI를 갖췄고 실제 앱에서도 공개 패키지를 사용합니다.

재사용할 수 있어 보이는 코드를 다른 저장소로 옮기는 것만으로는 OSS가 완성되지 않는다고 보게 됐습니다. 공개 API, 버전 계약, edge case 테스트, 라이선스, 실제 소비자 재적용까지 닫아야 제품 밖에서도 변경을 설명할 수 있는 기술 자산이 됩니다.

2026년 8월 처음 공개한 0.1.x 패키지로, 외부 사용자 수·장기 호환성·대표 입력 길이 기준 benchmark는 아직 확인하지 않았습니다.

  • 실사용 문장 길이 기준 시간·메모리 benchmark 추가
  • 외부 소비자 피드백을 모아 1.0.0에서 고정할 API 경계 결정
  • 패키지 업데이트 시 Dart·Go fixture 검증을 필수 check로 구성