프론트엔드 개발자들의 즐거운 상상 🎈 – 쓰면글림체 사이드 프로젝트 이야기

Mar.14.2024 김하루

Web Frontend

상상만으로도 마음 한편이 설레는 순간이 있나요? 영감이 솟구치고 미소가 절로 번지는 그런 순간이요.

제게는 사이드 프로젝트를 하는 순간이 그렇습니다. 실험적으로 이것저것 시도해 볼 수 있는 사이드 프로젝트는 상상의 즐거움을 만끽할 수 있게 해주는 취미 이상의 무언가예요. 이 상상의 즐거움은 누워있다가도 벌떡 일어나 노트북 앞에 앉게 되는 강렬한 원동력이 되지요.

이 글은 쓰면글림체라는 사이드 프로젝트에 대한 이야기예요. 어떤 사용자 경험을 만들 수 있을지 설레는 마음으로, 즐거운 상상을 담아 만들었습니다. 저와 장해민 님(@365kim, @jhaemin) 2명의 프론트엔드 개발자가 함께했어요.

쓰면글림체 이야기, 지금 시작할게요. 🎈

글의 순서
𝟎 어떤 경험을 만들게 될까
𝟏 가장 핵심이 되는 기술부터
𝟐 깜빡이는 Cursor 직접 만들기
𝟑 웹 UI에 손 그림 한 스푼
𝟒 세상에 공개하는 순간

Photographed by 장해민 님



𝟎 어떤 경험을 만들게 될까

두 달 전, 동료 개발자 장해민 님으로부터 연락을 받았어요. 해민 님은 항상 흥미로운 제안을 해주셔서 귀를 쫑긋하고 들었습니다.

하루 님,
글림체 자동으로 배치해 주는 거 같이 만들어보실래요?

‘배민 글림체’ 배치를 자동화한다니. 평소 자동화에 관심이 많았던 저는, 이 프로젝트가 재밌을 거라는 확신이 들었어요.

배민 글림체

배민 글림체(이하 글림체)는 우아한형제들에서 발표한 특별한 폰트예요. (자세히 보기)

글림체는 일반적인 폰트와 달리, 이미지 파일(.png) 형태로 제공됩니다. 이미지로 된 자음과 모음을 각자의 손길로 만드는 매력이 있는데, 위치를 하나하나 지정하는 방법이다 보니, 만드는 데 시간과 정성이 필요했어요.

쓰면, 글림체

키보드 타이핑만 해도 글림체의 배치가 자동으로 결정된다면, 글림체를 더 빠르게 저장하고 활용할 수 있겠죠. 기술이 사람 대신 글림체 배치를 대신 해주는 것. 그래서 글림체를 쓸 수 있는 또 다른 경험을 제공하는 것. 그게 이 프로젝트의 목표였어요.

프로젝트의 이름 쓰면글림체는 글자 타이핑하는 대로 글림체가 그려진다는 의미를 담고 있습니다.

글림체는 아기자기한 매력으로 우아한형제들 사내에서 이미 큰 사랑을 받고 있었습니다. 팀 신규입사자 환영인사나 다른 사내 행사에도 활용되고 있었죠. 이 프로젝트를 통해, 저처럼 글림체를 좋아하는 분들, 특히 가까운 동료분들에게도 즐거운 경험을 줄 수 있을 거라는 기대를 하며, 이 프로젝트를 시작하게 되었습니다. 🎈

사랑받는 글림체



𝟏 가장 핵심이 되는 기술부터

사이드 프로젝트의 첫 단계는 기술적 가설 검증(또는 개념 검증, Proof of Concept)으로 시작했어요. 기술적으로 ‘이게 될까?’를 탐색하는 과정입니다.

프로젝트를 함께한 해민 님의 조언으로, 이 가설 검증단계를 디자인보다 먼저 진행했어요. 덕분에 가설 검증 단계에서 얻게 된 통찰을 UI 디자인에 반영할 수 있어서 좋았습니다.

준비물 ⚒️

가설 검증에 들어가기 앞서, 소개할 두 가지 도구가 있어요. 바로 Hangul.js 라이브러리와 Canvas API 입니다.


Hangul.js
는 자음·모음을 분리하는 ‘한글 처리’ 과정을 쉽게 해주는 자바스크립트 라이브러리예요.

‘한글 처리’는 쓰면글림체의 핵심 요소 중 하나였거든요. 특히, 문자열을 글자별로 따로 분리하는 기능도 있어(group 옵션, disassemble 함수의 두 번째 인자), 글자 단위로 글림체 이미지를 관리할 때 매우 유용하게 사용했습니다.

한편, Canvas API(이하 캔버스)는 JavaScript와 HTML로 그래픽 요소를 그릴 수 있는 도구에요. 캔버스 위에 글림체 이미지를 그리고, 결과물을 하나의 이미지 파일로 손쉽게 저장할 수 있습니다.

두 개의 가설

프로젝트의 핵심 기술을 검증하기 위해 다음과 같이 두 가지 가설을 세웠어요.

첫 번째 가설을 입력하면, 이미지를 그릴 수 있다.
두 번째 가설ㅎㅏㄴ을 입력하면, 과 같은 배치로 그릴 수 있다.

글림체는 png 파일로 제공된다.

첫 번째 가설 검증

을 입력하면, 이미지를 그릴 수 있다.

먼저, 키보드 입력값에 상응하는 글림체 이미지를 표시할 수 있을지 검증해야 합니다. 예를 들어, input 요소에 초성 을 입력하면, 캔버스에 글림체 이미지가 표시되어야 해요.

첫 번째 가설 – 검증 완료

첫 번째 가설은 쉽게 검증 됐어요. 우아한형제들을 입력했을 때, 각 입력값에 해당하는 모든 이미지가 캔버스에 렌더링되었어요. 그런데 위치가 무작위로 렌더링되는 바람에, 아직 무슨 글자를 입력한 건지 알아볼 수 없을 정도네요.

이미지 사이즈 조정 안하면 벌어지는 일(ㅋㅋㅋ)

두 번째 가설 검증

ㅎㅏㄴ을 입력하면, 과 같은 배치로 그릴 수 있다.

두 번째 가설로 넘어갑니다. 이미지의 배치를 의도한 대로 자동화할 수 있을지 검증할 거예요. 예를 들어, input 요소에 ㅎㅏㄴ을 입력하면, 캔버스에 글림체 과 같은 모양으로 잘 위치가 조합된 이미지가 표시되어야 해요. 이나 ㅏㅎㄴ 과 같은 모양이면 안 되는 거죠.

그러기 위해선 각 이미지가 어디에 그려져야 하는지를 나타내는 x, y 좌표 정보를 저장하고 관리할 수 있어야 할 텐데요. 이를 위해 GlyphType을 정의합니다.

Glyph란 (윤곽선 글꼴 데이터에서) 글자 하나의 모양에 대한 기본 단위를 의미해요. 예를 들어, 은 하나의 Glyph입니다. 또 다른 예로, ㅎ, ㅏ, ㄴ 길이가 3인 Glyph의 배열로 나타낼 수 있습니다. 이제 각 이미지를 Glyph 단위로 관리할 거예요.

Glyph 배치 규칙은 다음과 같은 접근으로 시작합니다.

Glyph 배치 규칙 (1)

  • Glyph가 자음에 해당한다면, 왼쪽부터 그립니다.
  • Glyph가 모음 ㅏ, ㅑ, ㅓ, ㅕ, ㅣ에 해당한다면, 방금 그린 자음 오른쪽에 그립니다.
  • Glyph가 모음 ㅗ, ㅛ, ㅜ, ㅠ, ㅡ에 해당한다면, 방금 그린 자음 아래에 그립니다.

두 번째 가설 – 검증 중간단계

확실히 처음보단 나아졌네요.

그렇지만 우아한형제들을 입력했는데 결과물은 우아한혀저드로 보여요. 받침 위치 지정에 실패했기 때문입니다. 받침 역할을 하는 자음의 위치 지정이 누락되지 않도록, 자음·모음이 아니라 초성·중성·종성으로 구분해야 한다는 걸 깨달았어요.

그래서, 종성인 경우를 구분해서 위치 지정 규칙을 보완합니다.

Glyph 배치 규칙 (2)

  • Glyph가 종성에 해당한다면, 지금까지 그려진 높이 기준으로 아래에 그립니다.

또 다른 문제는, 중성 처럼 보인다는 거예요.

글림체에는 이미지가 따로 없어요. 그래서 2개의 이미지를 조합해서 만들어야 합니다. 그런데 둘이 너무 겹쳐 그려지는 바람에 로 보였던 것이죠.

Glyph 배치 규칙 (3)

  • Glyph가 형제가 있는 모음에 해당한다면, 두 번째 모음을 첫 번째 모음이 끝난 지점보다 조금 띄어 그립니다.

두 번째 모음 위치 보정 후

이런 과정을 거치면서, 한글 음절 하나를 구성하는데 생각보다 많은 경우의 수가 있음을 배울 수 있었어요.

두 번째 가설 – 검증 완료

이제 조금 그럴듯해 보이나요? 이 정도 선에서 가설 검증에 성공했다고 판단하고, 개발단계로 전환했어요.



더 자연스러운 글자 만들기

본격적인 개발 단계에서는, 기술 검증 단계에서 얻은 통찰을 바탕으로 프로젝트를 한 단계 더 발전시켜 보았어요.

HTML textarea 요소

우선, 검증 단계에서 사용했던 input 요소 대신, 줄 바꿈이 가능한 textarea 요소를 사용하도록 변경했어요. 그리고 이 textarea를 보이지 않도록 숨김 처리해서, 사용자가 마치 캔버스 위에서 직접 편집하는 것처럼 느낄 수 있도록 설계했습니다.

그리고 Glyph가 조합될 수 있는 더 다양한 경우의 수를 다루며 개선해 나갔어요. 예를 들어 겹받침이나 쌍자음 같은 엣지 케이스에 대한 섬세한 위치 보정을 추가해서, 각 음절이 더 자연스러워 보이도록 개선했습니다.

보정 전/후 비교

모음 구성이 복잡한 경우, 특히 처럼 모음이 초성 아래에도, 동시에 오른쪽에도 위치하는 경우도 다뤘습니다. 어떻게 보정하면 배치가 더 자연스러워 보일지 찾는 데까지 시간이 꽤 걸렸어요. 그렇지만 도전적인 상황을 해결할 때마다 렌더링 결과물이 눈에 띄게 개선되어서, 코드를 작성하는 재미가 컸답니다. 😄

행간·자간도 보기 좋게 조정하고, 수직 중앙정렬을 맞춰주는 등의 개선 작업도 진행했습니다. 지속적인 개선 작업을 거쳐, 캔버스에 글림체 이미지를 그리는 과정을 마무리했어요.

한글 처리 경우의 수와 싸운 흔적들



𝟐 깜빡이는 Cursor 직접 만들기

마지막 개발 과제가 하나 남아 있어요. 바로 깜빡이는 커서(Cursor)와 셀렉션(Selection)을 직접 만드는 거예요.

HTML textarea에 표현된 커서와 셀렉션

커서는 사용자 입력을 기다리는 동안 텍스트에서 현재 위치를 가리키는 포인터이고, 셀렉션은 사용자가 현재 선택한 범위를 하이라이트 해서 알려주는 가이드 역할을 합니다. 커서와 셀렉션 모두, 사용자의 상호작용에 따라 동적으로 변화하는 요소들이죠.

만약 HTML textarea, input과 같은 요소를 활용해서 그대로 UI를 표현한다면, 브라우저에서 기본적으로 제공하는 커서와 셀렉션을 그대로 쓸 수 있었을 거예요. 하지만 쓰면글림체의 캔버스는 마치 큰 도화지(Canvas)일 뿐이라, 커서와 셀렉션을 직접 표현해야 하는 상황이었습니다.

해민 님과 새로운 걸 직접 만들어볼 수 있는 좋은 기회라는 공감대를 바탕으로, 커서와 셀렉션을 직접 구현해 보기로 했어요. 커서와 셀렉션이 완성되면, 쓰면글림체에서 사용자가 편집하거나 조작할 때 훨씬 편하게 할 수 있을 거라 기대하면서요.

구현은 다음과 같이, 먼저 Placeholder를 표시하고, 필요한 프론트엔드 상태를 정의한 뒤, 이벤트 핸들링을 추가하는 방식으로 진행했습니다.

  • 1단계 Placeholder 표시
  • 2단계 상태 정의
  • 3단계 이벤트 핸들링

쓰면글림체 가상 커서

1단계: Placeholder 표시

첫 번째로, 커서와 셀렉션의 Placeholder를 준비해 둘 거예요. 신호만 받으면 그 위치에 정확히 표시할 수 있도록, 미리 밑그림을 그리는 작업이라고 볼 수 있습니다.

커서와 셀렉션 중, 커서부터 볼게요. 커서가 올 수 있는 위치, 즉 커서 Placeholder를 글자 사이사이에 표시해 둡니다. 커서 Placeholder는 0번부터 순서대로 각각의 index를 가져요.

이어서 셀렉션이 표시될 수 있는 위치, 셀렉션 Placeholder를 위와 같이 표시해 둡니다. 커서와 달리, 셀렉션은 글자 하나가 차지하는 공간만큼의 크기를 가져요. 셀렉션 Placeholder 역시 0번부터 순서대로 index를 가져요.

2단계: 상태 정의

그리고 구현에 필요한 프론트엔드 상태를 정의했어요. 필요한 상태는 HTML
Selection API
의 다음 4가지 주요 속성을 차용했습니다.


Selection API의 주요 속성

  • start
  • end
  • anchor
  • focus


네 가지 개념이 조금 헷갈릴 수 있어 정리하고 넘어갈게요.

우선 startend는 사용자가 선택한 텍스트 범위의 시작점과 끝점을 나타냅니다. 즉, start는 선택 범위의 맨 앞을, end맨 끝을 가리켜요.

그리고 anchorfocus 사용자가 텍스트 선택을 어디서 시작하고 끝냈는지에 대한 정보를 담아요. anchor는 선택을 시작한 지점, focus는 사용자가 마지막으로 선택을 끝낸 지점입니다.ㅤ


얼핏 보면 startanchor가 항상 같은 값일 것 같지만, 사용자가 텍스트를 선택하는 방식에 따라 다른 값을 가질 수도 있어요.

만약, 사용자가 왼쪽에서 시작해서 오른쪽으로 마우스 드래그한다면 startanchor는 예상대로 같은 값을 가집니다.

그런데, 사용자가 오른쪽에서 시작해서 왼쪽으로 마우스 드래그한다면 startanchor는 서로 다른 값을 가지게 돼요. 이때 anchor(선택을 시작한 지점)는 오히려 end(맨 끝)와 같은 값을 갖게 됩니다. 비슷해 보이지만 실은 각각 고유의 의미를 지닌 속성인 거죠.

이 속성들을 프론트엔드 상태로 관리하기만 해도, 커서와 셀렉션 UI에 사용자의 상호작용을 그대로 반영할 수 있어요. startend 값으로 가상 셀렉션 UI의 범위를, focus 값이 있으면 가상 커서 UI의 위치를 정확하게 결정할 수 있으니까요.

1단계에서 표시해둔 커서 Placeholder는, 자신의 index와 focus의 상태 값이 같아지는 순간, 깜빡이는 커서 스타일을 보여줄 거예요. (active)

비슷하게 셀렉션 Placeholder도 화면에 표시되지 않다가, 자신의 index가 startend 사이에 포함될 때, 은은한 배경색으로 선택된 상태임을 나타낼 거예요. (selected)

다만 anchor는 화면을 다시 그리는 데 필요하지는 않아서, ref 값으로 관리했습니다. anchor는 사용자가 텍스트를 왼쪽으로 선택하는지, 아니면 오른쪽으로 선택하는지 방향을 결정하는 데 참고해요. 이 방향은 start, end, focus 값을 적절하게 업데이트할 때 꼭 필요하거든요.

3단계: 이벤트 핸들링

이제 사용자의 상호작용에 따라 start, end, anchor, focus 값을 적절하게 업데이트하는 것만 남았어요. 이를 위해, 글자를 아래 그림처럼 반으로 나누었어요.

상태를 업데이트 하는 방법은 다음과 같습니다.

  • 왼쪽 절반 영역을 클릭하면, 왼쪽 index로 모든 상태를 업데이트
  • 오른쪽 절반 영역을 클릭하면, 오른쪽 index로 모든 상태를 업데이트

예를 들어, 그림의 𝐀 영역을 클릭하면, start, end, focus 모든 상태를 왼쪽 index 값인 0으로 업데이트해요.

반대로 𝐁 영역을 클릭하면, 모든 상태를 오른쪽 index 값인 1로 업데이트해요.

이렇게만 상태를 업데이트하면, 글림체 이미지를 클릭했을 때 사이사이로 커서 UI가 잘 표시되는 것을 볼 수 있어요.

클릭 동작

비슷하게, 그 영역을 지나가는 이벤트가 발생할 때도 상태 값을 업데이트해주면, 셀렉션 UI도 표시할 수 있게 됩니다. (이때 anchorRef를 활용해요.)

드래그 동작

그렇다면 캔버스에서 글림체 이미지가 그려지지 않은 공간을 클릭하면 어떻게 동작해야 할까요? 글림체 이미지가 그려지지 않은 영역의 동작을 처리하기 위해, 이 영역을 아래와 같이 왼쪽 날개, 오른쪽 날개, 그리고 바닥 영역으로 나누었어요.

왼쪽 날개 영역을 클릭하면, 해당하는 줄의 가장 index로 상태를 업데이트를 해주었습니다. 반대로, 오른쪽날개 영역을 클릭하면 해당하는 줄의 가장 마지막 index로 상태를 업데이트합니다. 그리고, 바닥 영역을 클릭하면 전체 index가장 큰 값으로 모든 상태를 업데이트했어요.


흥미진진한 가상 커서와 가상 셀렉션 구현 과제도 이렇게 클리어했습니다.




𝟑 웹 UI에 손 그림 한 스푼

이 아이디어를 어떻게 보여드릴까?를 고민했던 이야기도 간단하게 하고싶어요. 해민 님과 공유한 상상을 시각화하는 즐거운 과정이었거든요.

가장 고민이 된 것은 글림체를 입력할(키보드 타이핑 할) 영역의 표현이었습니다. 브라우저 기본 UI보다는, 좀 더 귀여운 글림체에게 어울리는 느낌을 찾고 싶었어요. 여러 아이디어가 나왔지만, 딱 꽂히는게 없었어요.

피그마 연필(Pencil) 기능

그러다 해민 님이 피그마(Figma)의 연필 기능으로 박스를 하나 그렸어요. 연필 기능은 마우스가 지나가는 경로따라 선이 그대로 남는 기능이에요.

연필로 그린 입력하는 영역

‘이거다.’ 싶었어요. 아기자기한 무드가 너무 잘 살았거든요. 그렇게 쓰면글림체의 UI 콘셉트가 손으로 그린 그림으로 결정되었습니다. 선의 형태만 살짝 보정하고, 바로 svg 파일로 추출해 웹 UI에 적용해봤어요. 웹에서도 기대한 만큼 귀엽게 표현되었고, 반응형으로 동작하게 만들 수 있는 것도 좋았습니다.

스크롤 상단 잘림 개선 전/후

한 가지, 스크롤을 위로 넘기는데, 칼로 자른 듯 끊겨서 서로 어우러지지 않는 느낌은 개선하고 싶었어요. 그래서 CSS 속성 중 clip-path를 활용해 보았어요. 입력하는 영역이 svg로 정의한 path에 따라 잘리도록 했어요. 미묘한 차이지만, 스크롤할 때 UI 표현이 더 자연스러워졌습니다.

이어서, 손으로 그린 그림 콘셉트를, 다른 UI에도 적용했어요. 이미지 저장 버튼은 구름 모양으로 만들었어요.

포커스 상태 표현 개선 전/후

이번에는 네모반듯한 포커스 스타일이 아쉬워보였어요. 포커스 스타일까지 더 조화롭게 표현되도록, stroke 색상을 제어해서 해결했습니다.

글림체 이미지를 활용해서 배경 패턴을 만들고, 은은한 애니메이션까지 적용하니 더욱 발랄한 느낌이 살아났습니다. 푸터(Footer)에 글림체 놀이터 소개까지 담아, 쓰면글림체 프로젝트의 심플한 디자인을 마무리했어요.

손으로 그린 그림 콘셉트의 쓰면글림체



𝟒 세상에 공개하는 순간

모든 도전을 마무리 하고, 세상에 선보일 준비까지 마쳤어요.

출시 준비 체크리스트
✔️ 도메인 구입(geullim.com)
✔️ 서버 환경 구성
✔️ 파비콘(favicon) 설정
✔️ SEO 관련 설정 및 검색엔진에 등록
✔️ 구글 태그 매니저(GTM) 연결

그리고 설 연휴를 앞둔 2024년 2월 3일 오후. 우아한형제들의 전사 Slack 채널을 통해 ‘쓰면글림체’ 프로젝트를 공개했습니다.


사이드 프로젝트를 진행하는 동안 누적해 온 상상이 현실이 되는 순간이었어요.🎈
감사하게도, 다양한 팀의 많은 동료분들이 직접 사용해 보고 따뜻한 호응을 보내주었어요. 먹지 않아도 배부를 만큼 큰 보람을 느낄 수 있었습니다.

(공개 여부에 대해 구성원의 동의를 받은 이미지입니다.)


𝟓 함께 만든 후기

쓰면글림체 이야기에서 장해민 님과 협업 후기를 빼놓을 수 없겠죠.

해민 님은 아이디어 뱅크입니다. 막힘없이 좋은 아이디어를 제시해 준 덕분에 사이드 프로젝트 내내 빈틈없이 즐거웠어요. 취향이 비슷하니 크게 의견이 대립할 일도 없기도 했지만, 감사하게도 항상 제 의견을 긍정적으로 검토해 주었어요. 그래서 해보고 싶은 많은 것을 시도해 볼 수도 있었어요. ‘어때요맨’, ‘좋아요맨’ 제가 붙여드린 별명이 많네요.

한편으로는 추가하고 싶은 기능이 너무 많아지기도 했어요. 지금 생각하면 조금 터무니없지만 ‘버튼을 누르면, 귀여운 하트 애니메이션이 나오게 하자.’는 아이디어도 있었어요. 그렇지만 함께 정해둔 릴리즈 날짜가 있으니, 필수 기능(MVP)에 집중하기로 했어요. 이런 측면에서 프로젝트 경험이 많은 해민 님이 적정선을 잘 잡아주었어요.

사이드 프로젝트의 달인, 장해민 님

또, 해민 님과 오프라인으로 협업할 때면 하루 님, 이거 써보셨어요?라는 말씀을 자주 했어요. 해민 님이 사용하시는 협업 도구나 개발 꿀팁을 바로 옆에서 보고 배울 수 있었습니다. 쓰면글림체의 Nginx 서버를 구성할 때는, 마치 일타강사의 강의를 듣는 듯하기도 했어요. 이런 유용한 정보를 모아, 실제 강의로 출시해도 좋겠다 싶더라고요. 당장은 다른 강의를 촬영하고 있어서 어렵겠지만요.

프로덕트를 완성하는 재미 못지않게, 함께 코드를 개선해 나가는 과정도 재밌었어요. 어느 정도 개발이 완료된 시점에, ‘페어 리팩터링’ 과정을 경험했는데요, 딱히 서로 역할을 나누지 않았는데, 자연스럽게 해민 님이 코드 전체 구조를 깔끔하게 재설계해 주었습니다. 여러 경우의 수로 분산되어 있던 로직이 말끔하게 정리되는 과정이었죠. 해민 님이 설계해 주시는 동시에 저는 옆에서 바로 저희 코드 베이스에 변경 사항을 반영했어요. 두 명의 개발자의 작업이 마치 컴퓨터가 병렬 처리를 수행하듯 빠르게 진행돼서, 신기하고 재밌는 경험이었어요. 결과적으로도 유지보수가 더 쉬운 코드로 개선되어 만족스러웠습니다.



마치며

지금까지 “프론트엔드 개발자들의 즐거운 상상🎈쓰면글림체 사이드프로젝트 이야기”였습니다. 지난 두 달간의 유쾌한 여정이 잘 전해졌을까요? 재밌게 읽으셨다면 이 글의 주인공인 쓰면글림체 geullim.com 에도 놀러 와주세요 🙂

이번 프로젝트의 마침표를 찍는 지금, 저는 또 다른 즐거운 기대를 품어봅니다. 여러분의 하루에도, 마음 한편이 설레는 즐거운 상상이 함께하길 바라요.

또 만나요!





김하루

우아한형제들에서 웹 프론트엔드 개발을 하고 있어요.