기능가격문서블로그
mote 받기
← 블로그
개발·2026년 7월 2일·읽는 시간 5분

글자를 직접 그리지 않고 플랫폼에 맡긴 이유

한글은 멀쩡한데 한자만 어딘가 낯설게 보인 적이 있나요. 글자를 직접 그리지 않고 플랫폼 텍스트 엔진에 맡기기로 하면, 우리 일은 "무엇을 그릴지"가 아니라 "어떤 글꼴을 달라고 말할지"가 돼요.

m
mote
같은 문자열을 Noto Sans CJK의 KR 서브셋과 JP 서브셋으로 그린 비교. 한글은 같지만 뼈 골·곧을 직 같은 한자의 자형이 달라요.
같은 문자열을 Noto Sans CJK의 KR 서브셋과 JP 서브셋으로 그린 비교. 한글은 같지만 뼈 골·곧을 직 같은 한자의 자형이 달라요.

텍스트가 화면에 나타나기까지는 많은 일이 필요해요. 글자를 모양으로 바꾸고, 줄을 나누고, 픽셀로 그려야 하죠. 한글 조합부터 한자, 이모지, 아랍 문자, 글꼴 폴백까지 처리해야 해요.

우리는 이 과정을 새로 만들지 않았어요. 리눅스의 Pango와 HarfBuzz, 윈도우의 DirectWrite, macOS의 TextKit 2에 맡겼어요.

운영체제가 오랫동안 다듬어 온 영역이기 때문이에요. 우리가 직접 만들면 CJK 문자나 접근성처럼 까다로운 부분에서 오히려 품질이 나빠질 가능성이 컸어요. 제품의 차별점도 렌더러 자체가 아니라 타이포그래피와 플랫폼다운 사용감에 있다고 봤고요.

대신, 언제 만들지는 우리가 정했어요

텍스트 레이아웃은 만드는 데 시간과 메모리가 들어요. 큰 문서에는 줄이 수만 개나 있어서 전부 만들 수 없었어요.

그래서 화면에 보이는 줄과 그 주변만 만들어요. 화면에서 멀어진 줄은 바로 버리고요. 1MB짜리 문서를 오래 스크롤해도 살아 있는 레이아웃은 마흔 개에서 예순 개 남짓이에요.

아직 만들지 않은 줄은 높이를 미리 추정해요. 스크롤 막대가 문서 전체 길이를 알아야 하니까요.

물론 추정이 틀릴 때도 있어요. 새로 계산한 높이가 예상과 다르면 화면이 밀릴 수 있죠. 이때 차이만큼 스크롤 위치를 같은 프레임에서 보정해요. 사용자의 눈에는 아무 일도 일어나지 않아야 해요.

한글을 조합하는 줄은 꾸밈 없이 그려요. 입력 중에 굵게나 숨김 같은 속성이 끼어들면 입력기가 아는 글자 수와 화면의 글자 수가 달라질 수 있기 때문이에요.

처음 만난 문제는 한자의 얼굴이었어요

플랫폼에 맡기면 플랫폼의 기본값도 따라와요.

우분투의 기본 UI 글꼴에는 한글이 없어요. 그래서 시스템이 대신 고른 글꼴은 Noto Sans CJK JP였어요. 한글은 잘 보였지만 일부 한자가 일본식 자형으로 나타났어요. 같은 유니코드라도 한국과 일본에서 모양이 다른 글자가 있기 때문이에요.

해결은 간단했어요. UI 글꼴 목록에서 Noto Sans CJK KR을 앞에 뒀어요.

하지만 더 중요한 건 따로 있었어요. 우리가 기준을 정하지 않으면 플랫폼은 사용자의 언어가 아니라 자신의 기본값으로 답한다는 점이에요.

글꼴의 순서로 언어별 모양을 만들어요

글꼴 스택은 앞에서부터 글자를 찾아요. 첫 번째 글꼴에 없는 글자만 다음 글꼴로 넘어가죠.

이 성질을 활용하면 영문은 세리프로, 한글은 산세리프로 보여줄 수 있어요. 설정 한 줄로 언어마다 어울리는 글꼴을 조합하는 거예요.

읽기 글꼴을 비우면 본문 글꼴을 그대로 써요. 따로 지정하면 읽기 모드에서만 바뀌고요. 소스 모드에는 코드 글꼴을 사용해요.

글자 크기는 운영체제의 접근성 설정을 따라요. 시스템에서 글자를 키운 사용자가 앱마다 다시 설정하지 않아도 돼요.

설정 카드의 모양 탭 — 글꼴 칸에는 쉼표로 이어진 스택이 그대로 들어 있고, 읽기 글꼴은 비어 있어 "본문과 동일"이라고 흐리게 적혀 있어요.
설정 카드의 모양 탭 — 글꼴 칸에는 쉼표로 이어진 스택이 그대로 들어 있고, 읽기 글꼴은 비어 있어 "본문과 동일"이라고 흐리게 적혀 있어요.

화면 낭독기도 같은 기반을 활용해요. 플랫폼의 텍스트 계층에 접근성 기능이 이미 연결돼 있어서, 우리는 요청받은 줄의 내용을 정확히 전달하면 돼요.

번역이 빠지면 빌드가 실패해요

UI 문장은 영어로 작성하고, 그 문장을 번역표의 열쇠로 써요. 번역이 없으면 화면에는 영어가 그대로 나타나요.

문제는 오류가 나지 않는다는 점이에요. 번역이 빠져도 조용히 영어가 섞여 나오죠.

그래서 검사 스크립트를 만들었어요. 코드에서 실제로 쓰는 문장을 모아 12개 언어의 번역표와 비교해요. 한 언어에만 문장을 추가하거나 번역을 빠뜨리면 빌드가 실패해요.

번역표마다 열쇠가 304개, 코드에서 실제로 쓰이는 문장이 282개예요. 지원 언어는 English, 한국어, 日本語, 简体中文, 繁體中文, Deutsch, Français, Español, Português (Brasil), Русский, Italiano, Polski예요.

설정의 일반 탭 — 맨 위 행이 언어 드롭다운이고, 한국어가 골라져 있어 아래 항목 이름들도 전부 한국어예요.
설정의 일반 탭 — 맨 위 행이 언어 드롭다운이고, 한국어가 골라져 있어 아래 항목 이름들도 전부 한국어예요.

언어를 바꾸면 같은 문서로 새 창을 열고 기존 창을 닫아요. 이미 떠 있는 위젯 수백 개를 하나씩 번역하면 빠뜨릴 가능성이 커요. 거의 쓰지 않는 복잡한 경로를 유지하는 것보다, 언어를 바꾸는 순간 창을 한 번 다시 여는 편이 더 안전하다고 판단했어요.

플랫폼에 맡기면 결과도 플랫폼을 닮아요

같은 문서라도 운영체제마다 픽셀이 완전히 같지는 않아요. 글자를 다듬는 방식과 기본 글꼴이 다르기 때문이에요.

우리는 억지로 같게 만들지 않아요. 대신 글자 크기의 비율, 줄 높이, 여백처럼 문서의 기준을 같게 유지해요.

설치된 글꼴에 따라 폴백 결과도 달라질 수 있어요. 그래서 특정 결과를 약속하는 대신 글꼴을 요청하는 순서를 투명하게 보여줘요. 사용자가 직접 바꿀 수도 있고요.

직접 그리지 않는다고 책임까지 사라지는 건 아니에요. 무엇을 맡기고, 어떤 순서로 요청할지 더 정확하게 정해야 해요.

← 이전 글Rust 코어 하나에 플랫폼별 셸을 얹은 이유다음 글 →문서가 길어져도 타이핑이 느려지지 않는 이유
RSS로 새 글 받기
글을 위한 기본
한국어
© 2026 mote
멘토그래드 (MentorGrad) · 대표 윤상빈 · 사업자등록번호 475-15-02958 · 통신판매업신고 2026-서울도봉-0075 · 서울특별시 도봉구 마들로13길 61, 씨드큐브 창동, 4층 C동 411H호 · 010-2012-3741 · support@motemd.com