첫 앱 출시기 2 — Figma 대신 HTML 목업으로 시작했고, 나중에 놓아줬어요
코드보다 목업이 먼저였는데, 그게 Figma가 아니라 HTML 한 장이었어요. 목업의 역할은 처음부터 빠른 시작이었습니다. 그 역할이 끝나는 자리를 어떻게 알아봤고, 어떻게 놓아줬는지까지 적어요.
지난 글에서는 무엇을 만들었고 무엇을 안 만들었는지를 이야기했어요. 시세 연동도, 자산 관리도, 서버도 없이 질문 두 개에만 답하는 앱으로 범위를 좁힌 이야기였죠.
이번 글은 어떤 순서로 만들었나입니다. 먼저 결과부터 적으면, 코드보다 목업이 먼저였고, 그 목업이 Figma가 아니라 HTML 한 장이었어요. 왜 그랬는지, 그리고 혼자 만들 때 생기는 가장 큰 문제를 어떻게 풀었는지 적어볼게요.
첫 커밋에 앱과 목업이 같이 들어갔어요
Initial commit: add stock calculator app and design mockup
저장소 구조가 이렇게 생겼습니다.
stock-calculator/
├── design/ ← 화면 설계 + 디자인 토큰 (mockup.html)
└── app/ ← 안드로이드 소스
design/mockup.html 한 장에 모든 화면이 들어 있어요.
계산기, 이력, 설정, 시트, 확인창, 키패드 서랍까지요. 열면 브라우저에서 바로 돕니다.
왜 Figma가 아니었을까요
이유가 셋 있었어요.
하나 — 토큰이 곧 코드예요
목업의 :root에 CSS 변수로 색·간격·모서리·움직임을 정의해두면,
그게 그대로 앱의 토큰 표가 됩니다. 디자인 툴에서 코드로 옮기는 “번역” 단계가 아예 없어요.
:root {
--s1: 8px; --s1h: 12px; --s2: 16px;
--radius-card: 18px;
--ease: cubic-bezier(.25,1,.5,1);
--exit: cubic-bezier(.4,0,1,1);
}
둘 — 움직임을 진짜로 볼 수 있어요
시트가 올라오는 속도, 키패드가 닫히는 곡선. 이건 정지 화면으로는 판단이 안 됩니다. 브라우저에서 실제로 열고 닫아봐야 “이거 왜 이렇게 굼뜨지?”가 보여요.
셋 — 반응형을 바로 잴 수 있어요
폭을 못 박은 iframe 안에 목업을 띄우고 scrollWidth와 clientWidth를 비교하면
가로 스크롤이 나는지 바로 압니다. 디자인 툴에서는 좁은 화면 프레임을 따로 그려야 하죠.
여기서 한 번 크게 헤맸어요. 헤드리스 브라우저로 스크린샷만 찍어서 판단하려고 했거든요.
--window-size=390을 줘도 스크린샷만 390px로 잘리고 레이아웃은 489px로 그려집니다. 오른쪽이 잘린 걸 보고 “가로 스크롤 버그다”라며 없는 버그를 고칠 뻔했어요. 스크린샷은 예쁜지를 보는 도구이지, 넘치는지를 재는 도구가 아니었습니다.
혼자 만들 때의 진짜 문제 — 합의할 상대가 없어요
회사에서라면 디자이너와 개발자가 목업을 놓고 다툽니다. 그리고 그 다툼이 결정을 남기죠.
혼자면 그게 없어요. 그래서 결정과 그 이유를 글로 남기는 자리를 따로 만들었습니다.
- 저장소 루트의 원칙 문서 — 브랜드·디자인 시스템·엔지니어링 원칙
CHANGELOG.md— 판올림마다 “사용자에게”와 “안에서”를 나눠서- 기록용 금고 — 판단의 이유, 배운 것
특히 CHANGELOG에 되돌림 항목을 따로 둔 게 크게 도움이 됐어요.
- 되돌림: 통화 기호를 «원» 접미사로 시작했다가 ₩ 접두사로
— 일곱 언어가 생긴 순간 «원»은 한국어를 읽는 사람에게만 통하는 그림이 된다
- 되돌림: 이름 입력 시트의 자동 포커스
— 얼굴 피커와 함께 서면서 자판이 시트를 가린 채 튀어서
되돌린 결정을 지워버리면 다음 사람(대개 미래의 나)이 같은 자리를 두 번 팝니다. 실제로 “연보라를 걷어내자”를 두 번 시도했다가, 기록을 보고 왜 되돌렸는지 알고 멈춘 적이 있어요.
사이클 — 목업에서 합의하고, 앱으로 옮기고, 다시 목업
이 프로젝트의 리듬은 이랬습니다.
- 목업에서 먼저 바꿉니다. 색이든 간격이든 배치든요.
- 브라우저에서 열어보고 판단합니다. 아니다 싶으면 여기서 끝. 코드는 안 건드려요.
- 좋으면 앱으로 옮깁니다.
- 앱에서 새로 알게 된 것은 목업에도 반영합니다.
4번이 중요했어요. 목업과 앱이 갈라지기 시작하면 목업은 그냥 낡은 그림이 되고, 그때부터는 아무도 안 보게 되거든요. 실제로 커밋 메시지에 이런 게 여러 개 있습니다.
Design: 목업 키패드 서랍·이력 머리줄 2단·길게 누름 시트 영수증 구조를 앱과 한 벌로 정렬
미리 말씀드리면, 나중에는 이 4번을 놓았어요. 목업이 낡아서가 아니라 제 몫을 다 해서였는데, 그 이야기는 이 글 끝에서 하겠습니다.
빨리 만들고 계속 고친다가 아니라, 빨리 만들고 계속 되돌린다
2주 중 마지막 사흘은 거의 전부 디자인 정리였어요. 커밋 제목만 봐도 보입니다.
Design: 연보라 걷어내기 — sunken을 primarySoft와 갈라놓고…
Design: sunken을 #F5F4F8로 한 칸 더 밝게
Design: 홈 세그먼트 트랙을 흰 면(surface)으로
Design: 바탕 위에 서는 것들을 흰 면으로
같은 자리를 네 번 만졌습니다. 처음엔 이게 낭비처럼 느껴졌는데, 지나고 보니 한 번에 맞출 수 있는 종류가 아니었어요.
연보라를 걷으니 파인 면이 회색으로 떴고, 그걸 밝히니 세그먼트 트랙이 애매해졌습니다. 색은 혼자 서지 않고 옆의 색과 함께 서더라고요.
기능도 마찬가지였어요. 키패드의 “바깥 누르면 닫기”는 세 번 만에 맞췄습니다. 투명 층을 위에 덮었다가, 아래에 깔았다가, 결국 아무도 안 가져간 탭을 듣는 방식으로요.
처음부터 열한 언어를 노리지 않았어요
8월 18일에 영어가 들어갔고, 8월 22일에 아홉 개가 더 들어갔습니다. 한국어만으로 먼저 완성한 다음 언어를 늘렸어요.
이 순서가 맞았습니다. 한국어 하나로 화면이 정리되기 전에 열한 언어를 얹었으면 문구를 고칠 때마다 열한 번씩 고쳐야 했을 거예요.
대신 처음부터 지킨 규칙이 하나 있어요. 코드에 문자열 리터럴을 심지 않는다. 서식까지 포함해서요.
// 이렇게 쓰는 순간 그 화면은 한국어 전용이 됩니다
Text("${amount}원")
날짜 서식도, %1$s주 같은 단위도, 심지어 통화 이름 옆의 괄호까지
전부 문자열 리소스에 뒀습니다.
일본어와 중국어는 전각 괄호()를 쓰고 그 앞에 공백을 두지 않아요.
그런데 코드에서 " (" + symbol + ")"로 이어 붙이면 그 화면은 반각 괄호 언어 전용이 됩니다.
이 규칙 하나 덕분에 나중에 언어 아홉 개를 얹는 게 “번역만 하면 되는 일”이 됐어요.
그리고 나중에 목업을 놓아줬어요
이 글을 여기서 끝내면 “HTML 목업 좋아요”로 읽힐 텐데, 그건 절반만 맞아요.
목업 동기화는 2026년 8월 22일에 멈췄습니다.
Design: 목업 시트 동기화 마무리 — 확인창 부제·선택지 두 줄, 종목 삭제/이력 삭제 문구를 앱과 한 벌로
이 커밋이 마지막이에요. 그 뒤로 앱은 계속 갔는데 목업은 그 자리에 있습니다. 그리고 그건 사고가 아니라 놓아준 것이었어요.
목업의 역할은 빠른 시작이에요
돌아보니 제가 목업에게 시킨 일은 처음부터 하나였습니다.
빨리 시작하게 해줄 것.
앱이 아직 없던 2주 전에는 그게 정말 컸어요. Compose를 다시 빌드하며 색을 고르는 것보다 브라우저에서 새로고침하는 게 훨씬 빨랐고, 아니다 싶으면 코드를 한 줄도 안 건드리고 되돌릴 수 있었으니까요.
무엇보다 그때 목업의 :root에 적은 값들이 그대로
Aurora Ledger라는 디자인 시스템이 됐습니다.
그러니까 목업이 한 일은 화면을 그리는 것이 아니라 값에 이름을 붙이는 것이었어요. 그리고 그 일에는 끝이 있습니다.
시작이 끝나면 역할도 끝나요
빠른 시작이 역할이라면, 앱이 서고 나면 그 역할은 자연히 작아집니다. 이제는 앱을 켜는 게 제일 빠르니까요.
그런데 문제는 역할이 작아지는 만큼 비용은 커진다는 것이었어요. 앱이 앱다워질수록 목업이 흉내 낼 수 없는 것들이 늘어났거든요.
- 열한 개 언어에서 글자 길이가 달라지며 버튼이 밀리는 것
- 자판이 올라오며 시트를 가리는 것
- 실제 기기의 스크롤 관성과 길게 누름의 타이밍
- 시스템 폰트 크기 설정을 키웠을 때 무너지는 자리
이걸 HTML로 재현하려면 목업이 앱을 흉내 내는 일을 해야 합니다. 빨리 시작하라고 만든 물건에게 앱 흉내를 시키는 거죠. 그 순간 목업은 앞서가는 자리가 아니라 따라오는 자리가 됩니다.
뒤처진 쪽은 늘 정해져 있었어요
8월 22일에 이런 걸 발견했습니다. 안내 시트에서 “그대로입니다”가 “그대로입니 / 다”로 잘려 있었어요.
한글은 낱말 안에서 끊지 않는다는 게 우리 법이고,
목업 CSS에는 keep-all이 들어 있었습니다. 그런데 앱에는 옮겨 온 적이 없었어요.
여기서 중요한 건 어느 쪽이 틀렸느냐가 아니라 어느 쪽을 사용자가 여느냐였습니다. 목업이 맞고 앱이 틀렸는데, 사용자가 여는 건 앱이에요.
이런 어긋남을 몇 번 겪고 나니 알겠더라고요. 목업이 앞서던 동안에는 목업이 진실이었는데, 앱이 앞서기 시작한 순간부터는 목업이 맞는 것조차 도움이 안 됩니다. 고쳐야 하는 건 늘 앱 쪽이니까요.
두 벌을 맞추는 일이 그때부터 순수한 비용이 됐어요. 디자인 시스템 세우기 1에서 색 표가 여섯 벌이 됐던 것과 같은 병이고, 이번엔 그 두 벌이 CSS와 Kotlin이었을 뿐입니다.
놓아주는 것과 버리는 것은 달라요
그래서 목업을 지우지 않았습니다. 지금도 저장소에 그대로 있어요. 다만 앱과 맞추는 일을 그만뒀습니다.
지웠으면 “저건 실패한 시도”가 되는데, 그건 사실이 아니거든요. 목업은 2주 동안 제 몫을 다 했고, 그 결과가 지금 Kotlin 토큰으로 살아 있습니다.
목업은 낡은 게 아니라 제 일을 마쳤다. 다만 그걸 알아채는 데 며칠이 걸렸고, 그동안 두 벌을 맞추느라 시간을 좀 썼다.
되돌린 결정을 지우지 않는 이유가 이거예요. “HTML 목업으로 시작하라”만 남겨두면 다음 사람은 그걸 끝까지 들고 갑니다. 언제 놓아야 하는지가 같이 적혀 있어야 완결된 조언이에요.
그래서 제가 얻은 규칙은 이렇게 생겼습니다.
시작을 위한 도구는 시작이 끝나면 놓아준다. 그 도구가 나빠져서가 아니라, 붙잡고 있으면 그때부터 값을 치르기 때문에.
이번 작업에서 정한 기준
여기까지가 만드는 이야기예요.
다음 두 편이 이 시리즈를 쓴 진짜 이유입니다. 코드가 아니라 절차를 통과하는 이야기거든요. 먼저 광고부터 붙여볼게요.