명당까치 키코의 화면은 남색 밤하늘 아래 3D 지형이 떠 있는 그림으로 시작합니다. 이 색들은 민화 한 점에서 왔습니다. 그리고 2026년 10월 1일부터는 웹과 iOS·안드로이드 앱에서 “밤의 민화”, “낮”, “노을” 세 가지 화면을 고를 수 있습니다.
낮 테마 하나를 만드는 데 생각보다 오래 걸렸습니다. 같은 안료로 낮을 세 번 만들었고 세 번 다 어긋났습니다. 이 글은 왜 어긋났는지, 그리고 결국 무엇을 포기하고 무엇을 지켰는지에 대한 기록입니다.
밤의 민화: 여덟 안료
7월 28일, 키코의 시각 방향으로 “밤의 민화”를 정했습니다. 호랑이와 까치를 그린 호작도 한 점에서 여덟 가지 안료를 뽑았고, 이 여덟 개가 제품 전체의 색을 맡습니다.
| 안료 | 색 | 맡은 일 |
|---|---|---|
| 쪽빛 | #26339B |
하늘, 그림의 바탕 |
| 감청 | #171F55 |
지형의 기본색 |
| 호박 | #DFA13C |
높은 건물, 길 강조 |
| 진사 | #BE4A2A |
터를 표시하는 도장, 주요 버튼 |
| 미색 | #F1E6CD |
낮은 건물, 길, 글자, 밝은 카드 |
| 송록 | #2C6E52 |
물길 |
| 금니 | #E5B93F |
점수 숫자, 달, 기의 흐름 |
| 먹 | #22201E |
미색 카드 위의 글씨 |
쓰는 방식에도 규율을 두었습니다. 쪽빛 하늘은 3D 장면의 배경이자 페이지의 바탕이라, 지도와 페이지가 한 장으로 읽힙니다. 깊이는 안개나 조명 효과가 아니라 감청 위의 기복으로 표현합니다. 물은 송록 띠, 길은 미색 띠로 그리고, 건물은 높이 45m를 향해 미색에서 호박으로 올라갑니다. 높이를 모르는 건물은 4m짜리 납작한 미색 판으로 둡니다. 그래서 건축물대장으로 높이를 채운 건물은 눈으로도 구분됩니다. 빛나는 것은 없습니다.
색은 코드 한 곳에서만 정의합니다. 하나의 팔레트 파일이 3D 장면, 리포트 화면, 공유 카드 이미지에 모두 색을 대고, CSS는 같은 값을 변수로 손수 맞춰 둡니다. CSS가 TypeScript 파일을 불러올 수 없기 때문입니다.
접근성 문제도 하나 있었습니다. 진사를 미색 위 작은 글씨에 쓰면 명암비가 4.03:1이라 작은 글씨 기준(4.5:1)에 못 미칩니다. 그래서 작은 글씨와 버튼에는 진사를 조금 깊게 한 보조색(#A03A20, 5.43:1)을 쓰고, 원래 진사는 큰 장식에만 씁니다.
안료가 아니라 역할로 부르기
낮 테마를 만들려고 보니 구조부터 문제였습니다. 처음 설계 문서에는 “낮 변주는 같은 여덟 안료 위에 두 번째 역할 표를 두는 것”이라고 적어 두었는데, 정작 화면 코드는 역할이 아니라 안료 이름을 직접 쓰고 있었습니다. 세어 보니 안료를 직접 부르는 곳이 331곳, 역할을 부르는 곳이 22곳이었습니다. 안료를 직접 쓰면 테마를 바꿀 때 그 자리만 밤에 남습니다.
그래서 역할 층을 만들었습니다. “페이지 바탕 위의 글씨”, “강조”, “바탕 위로 떠오른 면”, “바탕과 반대로 칠한 조각과 그 위의 글씨”, “미색 카드 위의 글씨”, “그림 위에 찍는 표시” 같은 이름입니다. 화면의 모든 색은 이 이름만 쓰고, 테마는 이름에 어떤 안료를 배정할지만 바꿉니다.
이름을 나누는 일이 생각보다 중요했습니다. 예를 들어 토스트 알림 같은 “바탕과 반대로 칠한 조각 위의 글씨”와 “미색 카드 위의 글씨”는 밤에는 둘 다 먹이라 한 이름으로 묶어 두었습니다. 그런데 낮 표를 적용하자 토스트는 감청 조각으로 뒤집혔고, 그 위에 먹 글씨가 앉아 읽히지 않았습니다. 카드는 어느 테마에서나 종이라 먹이 맞고, 토스트 글씨는 조각을 따라 미색이 되어야 합니다. 밤에 같은 값이라도 하는 일이 다르면 이름을 갈라야 했습니다.
낮 1차: 같은 여덟 안료, 다른 배정
9월 9일의 첫 낮 테마는 안료의 hex 값을 하나도 바꾸지 않고 배정만 바꾸는 방식이었습니다. 생각은 이랬습니다. 밤은 감청 바탕에 미색으로 그린 그림이고, 낮은 미색 종이에 먹과 쪽빛으로 그린 그림이다. 뒤집기가 아니라 매체를 바꾸는 것이다.
벤치마크로 삼은 서울 3D 지도 사이트의 낮 화면과 나란히 놓아 보니 두 군데를 거꾸로 짜 두었습니다.
- 길을 먹으로 칠했더니 화면에서 가장 어두운 것이 길이 되어, 지도가 “지형 위의 도로망”이 아니라 “도로망 뒤의 지형”으로 읽혔습니다. 잘 만든 지도의 낮 길은 지형보다 밝습니다. 미색으로 되돌렸습니다.
- 높은 건물을 쪽빛 원색으로 칠했더니 종이 위에서 혼자 소리를 질렀습니다. 그림을 끌고 가야 하는 것은 땅입니다. 건물은 미색에서 살짝 푸른 미색으로 가는 좁은 범위로 줄였습니다.
밝은 바탕은 옅은 기복을 삼켜 버리기 때문에, 지형 기복이 섞이는 비율도 밤 0.32, 낮 0.62로 갈랐습니다.
이때 지킨 원칙이 하나 있습니다. 밤은 한 픽셀도 바뀌면 안 된다. 실제로 같은 리포트를 찍어 비교했고, 글 쪽 절반의 차이는 0이었습니다. 그 과정에서 한 가지 시도를 되돌렸습니다. 강조 색을 역할 변수로 바꿔 보았더니 똑같이 #E5B93F로 풀리는데도 밤 화면의 글 쪽에서 51,142픽셀이 달라졌습니다. 원인을 설명하지 못한 채 밤을 건드릴 수는 없어서 되돌리고, 낮에만 적용되는 CSS 두 줄로 같은 목적을 이뤘습니다.
낮 2·3차: “사막이나 황야 같다”
결과물을 본 평은 짧았습니다. “사막이나 황야 같다.” 정확한 지적이었고, 원인은 셋이었습니다. 그중 둘은 취향이 아니라 연결되지 않은 배선이었습니다.
- 바탕이 말 그대로 모래였습니다. 깊은 미색(
#E6D7B4)은 따뜻한 모래색이고, 거기에 송록을 섞으니 올리브가 됐습니다. 모래 평지에 올리브 덤불이면 정의상 황야입니다. 바탕을 송록 쪽으로 식혔습니다. - 물이 한 번도 파랬던 적이 없었습니다. 팔레트에 물 색을 넣어 두고 정작 물을 그리는 함수는 송록을 직접 박아 쓰고 있었습니다. 올리브 바탕 위의 올리브 물이니 사라질 수밖에 없었습니다.
- 먼 산이 낮에도 밤색으로 가라앉았습니다. 멀리 있는 지형을 페이지 바탕색으로 섞어 거리감을 내는데, 그 페이지 색이 밤의 남색으로 고정돼 있었습니다. 낮에는 흙탕물처럼 어두웠습니다.
건물 색도 한 번 더 고쳤습니다. 건물을 쪽빛 계열로 올렸더니 “건물이 물길보다 더 파랗다”는 말이 나왔습니다. 한 그림에서 파랑이 두 가지를 가리키면 읽는 사람이 매번 어느 쪽이 물인지 다시 판단해야 합니다. 그래서 뜻을 셋으로 갈랐습니다. 먹은 지은 것, 쪽빛은 물, 송록은 땅.
결정: 밤은 민화로, 낮은 지도로
그래도 낮은 어딘가 어긋나 있었습니다. 세 번째 판을 앞에 두고 원인을 다시 따져 보니 취향이 아니라 팔레트에 중간 톤이 없다는 것이었습니다. 호작도의 여덟 안료는 밤을 위해 뽑힌 색입니다. 감청, 쪽빛, 호박, 진사는 어두운 바탕에서 빛나라고 있는 색이고, 밝은 바탕에 올리면 미색(모래)과 송록(올리브) 사이에 아무것도 없습니다. 낮의 땅에 필요한 채도 낮은 중간 초록(#91AD7B 같은)이 여덟 안에 없었습니다.
9월 15일, 제품 결정을 내렸습니다. 밤이 민화의 정체성을 지고, 낮은 지도로 읽힌다. 낮과 노을은 벤치마크한 서울 3D 지도 사이트의 배색을 실측해서 그대로 씁니다.
- 낮: 하늘과 바탕
#E6EDE7, 지형#D6DFC2에서#91AD7B, 건물#D2D7C9에서#9DBDC0, 길#EDE9D9, 물#3D94B0 - 노을: 하늘과 바탕
#EBD7C6, 길#EBCDA4, 물#739BA9. 지형과 건물은 낮과 같습니다. 저녁은 빛이 바뀌는 것이지 땅이 다시 칠해지는 것이 아니기 때문입니다.
여덟 안료 규율의 의도적인 예외입니다. 그래서 팔레트 파일과 CSS 양쪽 주석에 경위를 남겼습니다. 파일이 스스로와 모순되지 않도록요. 그래도 두 가지는 여전히 키코의 것으로 남겼습니다. 터를 표시하는 도장은 진사입니다. 지도 위의 서명이지 풍경이 아니니까요. 강조 색은 깊은 진사입니다. 금니는 밝은 바탕에서 읽히지 않습니다.
작은 것들: 토글 이름, 조사, 깜빡임
테마 토글은 밤 → 낮 → 노을 → 밤 순서로 돕니다. 버튼에는 지금 상태가 아니라 다음에 갈 곳을 적습니다.
여기서 한국어 조사 문제가 나왔습니다. “…로 바꾸기”를 만들 때 ${이름}으로 같은 템플릿을 쓰면 “노을으로”, “밤의 민화으로” 같은 비문이 나옵니다. 로와 으로는 앞말의 받침에 달려 있으니까요. 실제로 한 번 그렇게 내보냈다가 고쳤습니다. 이름이 셋뿐이라 “밤의 민화로”, “낮으로”, “노을로”를 표에 바른 형태로 적어 두었습니다.
낮 테마를 고른 사람이 페이지를 열 때마다 밤이 번쩍이면 안 됩니다. 그래서 테마의 진실은 문서 최상단 요소의 속성 하나에 두고, 화면이 처음 그려지기 전에 작은 스크립트가 저장된 값을 그 속성에 먼저 써 둡니다. 브라우저 저장소에 못 남겨도(사파리 사생활 보호 모드 등) 조용히 진행합니다. 테마가 기억되지 않는 것은 불편이지 고장이 아닙니다.
예외도 하나 생겼습니다. 9월 29일, 낮 테마에서 랜딩 페이지의 갤러리가 읽히지 않는다는 것을 발견했습니다. 먹 글씨가 먹빛 바탕에 앉아 있었습니다. 랜딩은 자체 배색을 가진 별도의 그림이라, 그 안에서는 역할 값을 고정했습니다.
웹과 앱이 같은 표를 쓰기까지
10월 1일, 같은 세 테마를 iOS(1.0.8)와 안드로이드(1.0.5) 앱에 넣었습니다. 원칙은 “웹과 같은 역할 표”였습니다.
- iOS의 Swift 파일과 안드로이드의 Kotlin 파일이 웹의 테마 순서, 이름, 조사가 붙은 이름을 그대로 옮겨 적고, CSS의 역할 표를 세 벌 그대로 미러링합니다. 저장 키 이름도 웹과 같습니다.
- 여덟 안료는 그대로 두고, 앱 화면 코드가 안료를 직접 부르던 곳을 모두 역할 이름으로 바꿨습니다. 웹에서 331곳 대 22곳을 정리하며 배운 것을 앱에서는 처음부터 적용한 셈입니다.
- 상태 표시줄 아이콘, 탭 바, 앱 안에서 여는 웹 페이지의 툴바 색까지 테마를 따릅니다.
- 앱이 웹 리포트를 열 때 주소에 현재 테마를 붙여 보냅니다. 웹의 첫 페인트 스크립트는 주소의 유효한 테마 값을 저장된 값보다 우선하고 그 값을 저장합니다. 그래서 앱에서 낮을 고른 사람은 웹 리포트도 낮으로 봅니다. 이 동작은 시험으로 잠가 두었습니다.
- 홈 화면 위젯은 밤 그대로 둡니다.
아이콘은 그보다 이틀 앞선 9월 29일에 정리했습니다. 서비스 아이콘은 이제 키코 한 마리만, 낮 테마의 바탕색 위에 둡니다.
배운 것
- 색은 이름이 아니라 역할로 부르세요. 안료 이름을 직접 쓴 자리는 테마가 바뀔 때 혼자 남습니다.
- 밤에 같은 값이라도 하는 일이 다르면 이름을 가르세요.
- “분위기가 이상하다”는 평은 대개 구조적인 원인이 있습니다. 사막처럼 보인 이유는 따뜻한 바탕, 연결되지 않은 물 색, 고정된 거리감 색이었습니다.
- 팔레트에도 맡을 수 있는 일과 없는 일이 있습니다. 밤을 위해 뽑은 여덟 색으로 낮의 중간 톤을 만들 수는 없었습니다. 정체성을 지킬 곳(밤, 도장, 강조)과 빌려 올 곳(낮의 땅)을 나누는 편이 정직했습니다.
- 한 번 정한 표를 모든 플랫폼이 그대로 옮겨 적게 하세요. 웹과 두 앱이 같은 이름과 같은 순서를 쓰니, 앱에서 웹으로 넘어가도 화면이 이어집니다.
세 가지 화면은 kiko.jocoding.io의 3D 지도에서 직접 바꿔 볼 수 있습니다. 키코는 전통 풍수를 현대 지형 데이터로 다시 읽어 보는 문화·오락 콘텐츠입니다.