이 글은 Today I Learn 시리즈의 97번째 기록입니다. (총 213개)

콘티온을 손보며 걸렸던 것들을 모았다. 앞쪽은 접속 로그를 다듬다 이어진 일이고, 뒤쪽은 설정과 화면에서 따로 나온 일이다.


며칠 뒤 로그를 열었더니 읽히지 않았다

며칠 지나 접속 로그(누가 언제 무엇을 했는지 쌓이는 기록) 화면을 보니, 동작 이름 옆에 10:1, 3 같은 숫자만 적힌 줄이 있었다. 앞은 “10번 사용자에게 곡 관리 권한을 줬다”, 뒤는 “3번 콘티의 이름을 바꿨다”는 뜻이었는데 화면만 봐서는 알 수가 없었다.

로그를 남기는 순간에는 맥락이 다 있다. 어느 화면에서 무슨 버튼을 눌렀는지 아니까 id(항목마다 붙는 고유 번호)만 있어도 충분해 보인다. 하지만 로그를 읽는 순간은 정반대로 맥락이 없다. “10”이 누구인지 알려면 사용자 목록을 따로 뒤져야 하고, 그 사람이 이미 지워졌다면 영영 알 수 없다.

그래서 기준을 바꿨다. 이 줄 하나만 떼어 놓고 봐도 무슨 일이 있었는지 알 수 있는가. 권한 기록에는 이름과 id를 같이 남기고(id는 동명이인 때문), 이름 변경에는 “이전 이름 → 새 이름”을 남긴다. 이름을 적어 두면 대상이 나중에 지워져도 기록은 읽힌다.


다른 사람 기록이 목록에 안 보였다

로그 목록을 보다가 다른 팀원들 기록이 안 뜨는 걸 알아챘다. 거르는 조건이 잘못 걸린 줄 알았는데, 사람으로 거르는 조건 자체가 없었다. 그래서 사람별로 몇 건씩 쌓였는지 세어 봤다.

대부분이 내 기록이었다. 화면은 최근 200건을 보여주는데 그 대다수가 나였고, 다른 사람 기록은 그 사이에 한두 건씩 드문드문 섞여 있었다. 없어진 게 아니라 묻혀 있었던 것이다.

“안 보인다”는 증상은 원인이 세 갈래다.

  • 없거나 — 기록이 애초에 안 쌓였다
  • 걸러졌거나 — 조회 조건이 빼 버렸다
  • 묻혔거나 — 있긴 한데 비율에 밀려 눈에 안 띈다

코드를 읽으면 둘째까지는 확인된다. 셋째는 실제 데이터를 세어 봐야 나온다.

그래서 가져오는 양을 늘리고, 사람별로 골라 볼 수 있는 선택 칸을 붙였다. 선택 칸의 이름 목록은 이미 불러온 로그에서 뽑는다. 로그에 한 번도 안 나오는 사람은 골라 봐야 빈 화면이라, 그런 이름은 처음부터 안 보이는 편이 낫다.


곁들여 — 화면마다 필요한 이력은 좁았다

같은 날 자막 편집 화면과 템플릿 화면에도 각각 수정 이력을 붙였다. 전체 로그 화면이 이미 있는데 따로 만든 이유가 있다.

전체 로그는 모든 일을 시간순으로 쌓는 감사(監査)용이다. 자막을 고치는 사람이 알고 싶은 건 “이 곡을 누가 마지막으로 손댔나” 하나뿐이고, 그걸 전체 로그에서 찾으려면 곡을 특정하고 동작을 걸러야 한다. 같은 기록을 읽더라도 질문이 좁으면 좁은 창을 그 자리에 따로 두는 게 낫다.


마지막 로그인이 전부 Invalid Date로 떴다

팀원 목록의 “마지막 로그인” 칸이 전부 Invalid Date(날짜로 읽을 수 없다는 브라우저 표시)로 떴다. 값은 DB(데이터베이스)에 멀쩡히 들어 있었다.

열어 보니 다른 시각 칸은 전부 DB가 직접 만든 형식(2026-07-19 10:08:34)인데, 이 칸 하나만 앱 코드가 만든 ISO 형식(국제 표준 시각 표기 — 날짜와 시각 사이에 T, 끝에 세계 표준시 UTC를 뜻하는 Z가 붙는 꼴)으로 들어가 있었다. 화면은 DB 형식 하나만 있다고 가정하고 변환을 걸고 있었고, 이미 다른 형식인 값에 같은 변환이 덧붙으면서 깨졌다. 오류 메시지도 없이 조용히 망가진 것이다.

두 형식 모두 그 자체로는 멀쩡하다. 문제는 같은 종류의 값을 만드는 곳이 둘이었다는 것이다. 만드는 곳이 여럿이면 형식은 언젠가 갈린다. DB에 넣는 시각은 전부 DB가 만들게 하면 갈릴 여지가 없다.

같은 패턴을 훑어보니 곡 검수 대기 시각도 똑같이 앱이 만들고 있었다. 지금 검수를 기다리는 곡이 없어서 드러나지 않았을 뿐이다. 하나를 고쳤으면 같은 모양의 자리를 다 찾아봐야 한다. 지금 안 보이는 곳에도 같은 문제가 있다.


메인 글꼴을 바꾸면 따라와야 했다

자막 PPT의 글자 크기와 글꼴을 자막 담당자가 직접 정할 수 있게 하기로 했다. 요소마다(가사·제목·번호·안내·구간) 글꼴을 따로 고를 수 있게 하되, 따로 안 고르면 메인 글꼴을 쓴다.

이걸 저장하는 방법은 두 가지다.

  • 설정할 때 메인 글꼴 이름을 각 요소에 복사해 넣는다
  • 각 요소는 비워 두고, 쓸 때 메인 글꼴을 가져온다

앞쪽이 단순해 보이지만, 메인 글꼴을 다른 글꼴로 바꾸는 순간을 떠올리면 답이 갈린다. 복사해 둔 값은 그대로 남아서 다섯 군데를 하나씩 다시 고쳐야 한다. 비워 둔 쪽은 전부 따라 바뀐다.

값을 복사하면 그 순간의 사진이 되고, 비워 두면 원본을 가리키는 살아 있는 참조가 된다. 어느 쪽이 맞는지는 “원본이 바뀌면 따라가야 하나”로 고른다.

이러고 나니 원래 “글꼴”이던 설정의 뜻도 바뀌었다. 전에는 가사 글꼴이었는데 가사도 자기 설정을 따로 갖게 되면서 모두가 기대는 기본값이 됐다. 그래서 화면 이름도 “메인 글꼴”로 바꿨다.


제목을 키웠더니 구간 글자도 커졌다

구간 슬라이드(콘티 안에서 곡 묶음의 이름을 크게 띄우는 구분 슬라이드)의 글자 크기는 “제목의 2배”로 계산되고 있었다. 처음 만들 때는 합리적이었다. 정할 값이 하나 줄고, 제목을 키우면 구간도 비례해 커지니 일관돼 보였다.

그런데 제목 크기를 설정으로 빼자 문제가 드러났다. 담당자가 제목을 40에서 50으로 올리면 구간이 80에서 100으로 같이 커진다. 설정한 사람은 그걸 의도하지 않았다.

“제목의 2배”는 현장의 규칙이 아니라 처음에 값을 하나 덜 정하려고 둔 편의였다. 어떤 값을 다른 값에서 계산해 쓰고 있다면 물어볼 게 있다. 이게 원래 그래야 하는 규칙인가, 아니면 값을 두 번 쓰기 싫어서인가. 뒤쪽이면 각자 따로 두는 게 낫다.

구간 크기를 따로 떼면서 기본값은 80, 지금까지 계산돼 나오던 값과 같게 뒀다. 설정을 새로 늘릴 때 기본값을 기존 결과와 맞추면 아무도 손대기 전까지 결과가 똑같다. 기능을 늘리면서 기존 결과가 조용히 바뀌면 쓰는 사람이 놀란다.


곡 하나 복사와 콘티 복사의 모양이 달랐다

곡 하나의 가사를 복사하는 기능이 이미 있었고, 콘티 전체를 복사하는 기능을 새로 붙였다. 콘티 복사는 이런 모양이다.

콘티 제목

곡 번호 곡 제목
(가사)…

곡 번호 곡 제목
(가사)…

둘을 번갈아 붙여 넣어 보니 곡 하나를 복사할 때만 제목 줄이 없었다. 두 화면이 같은 곡을 각자 조립하고 있었기 때문이다. 번호 표기도 목록에는 대괄호가 붙고 복사에는 안 붙는 식으로 따로 만들어지고 있었다.

한쪽만 고치면 또 갈린다. 그래서 곡 한 덩어리를 만드는 자리를 하나로 모으고, 콘티 복사는 그걸 불러 이어 붙이게 했다. 큰 단위가 작은 단위를 불러 쓰면 곡 덩어리의 모양은 두 곳에서 어긋날 수가 없다.

기능이 아니라 형식을 공유해야 할 때가 있다. “같은 규칙을 따라야 하는 두 곳”이 보이면 그 규칙을 한 자리에 두는 게 맞다.


메뉴 버튼 높이가 옆 칸과 안 맞았다

화면을 보니 콘티 선택 칸 옆의 메뉴 버튼(⋯)이 선택 칸보다 낮았다. 둘을 감싼 줄에는 flex(한 줄로 늘어선 요소들의 크기와 정렬을 부모가 한꺼번에 맞추는 CSS 배치 방식)로 “자식들을 같은 높이로” 설정이 걸려 있었는데도 그랬다.

원인은 사이에 낀 상자였다. 버튼은 드롭다운 메뉴를 띄우려고 한 번 더 감싸져 있었고, 같은 높이로 늘어난 건 그 감싼 상자까지였다. 정렬 설정은 부모와 바로 아래 자식 한 단계에서만 작동한다. 사이에 요소가 하나 끼면 거기서 끊긴다. 스타일만 보고 화면 구조를 안 보면 놓치는 자리다.

높이를 맞추고 나서도 점 세 개가 정확히 가운데로 오지 않았다. 문자는 점이 글자 상자의 한가운데가 아니라 수학 기호 높이에 찍히도록 그려진 문자라, 남는 오차는 글리프(글꼴 안에 들어 있는 글자의 실제 그림) 자체의 것이다. CSS로 맞출 수 있는 건 글자가 든 상자까지다.

곁들여, 버튼 두 개를 양끝에 두는 정렬(space-between)에서 잠긴 콘티일 때 왼쪽 “그룹 추가” 버튼이 사라지면 오른쪽 “콘티 복사” 버튼이 왼쪽으로 끌려왔다. 한쪽이 사라질 수 있는 자리라면 빈 칸이라도 자리를 남겨 둬야 반대쪽이 제자리를 지킨다.


요약

  • 로그는 남기는 시점이 아니라 읽는 시점 기준으로 쓴다. 그 줄만 떼어 봐도 알 수 있어야 하고, id만 남기면 대상이 지워졌을 때 영영 못 읽는다
  • “안 보인다”는 없거나·걸러졌거나·묻혔거나 셋 중 하나다. 셋째는 코드가 아니라 데이터를 세어 봐야 나온다
  • 같은 기록이라도 질문이 좁으면 그 화면에 좁은 이력 창을 따로 두는 게 낫다
  • 같은 종류의 값을 만드는 곳이 여럿이면 형식이 갈린다. DB에 넣는 시각은 DB가 만들게 하고, 하나를 고치면 같은 패턴을 다 훑는다
  • 값을 복사하면 그 순간의 사진, 비워 두면 살아 있는 참조다. “원본이 바뀌면 따라가야 하나”로 고른다
  • 다른 값에서 계산해 박아 둔 값은 그 값을 설정으로 뺄 때 빚이 된다. 원래 그래야 하는 규칙이 아니면 따로 둔다
  • 설정을 새로 늘릴 땐 기본값을 기존 결과와 같게 둬서 결과가 조용히 바뀌지 않게 한다
  • 같은 형식을 두 곳에서 조립하면 갈린다. 만드는 자리를 하나로 모으고 큰 단위가 작은 단위를 불러 쓰게 한다
  • flex 정렬은 바로 아래 자식까지만 먹는다. 남는 오차는 글리프 자체일 수 있고, 한쪽이 사라질 수 있는 양끝 정렬은 빈 자리를 남겨 둔다

Series: Today I Learn

1 C++ 자료형(Data Type) 2 MD5 vs pHash 3 C++에서 함수의 선언과 정의 4 Tkinter padx, pady 5 메모리와 포인터 변수 6 Call by Value, Call by Reference, Call by Pointer 비교 7 const 8 Gemfile — Jekyll 프로젝트의 의존성 파일 9 kramdown-parser-gfm — Jekyll의 GFM 파서 10 파서(Parser) 11 AHU vs OHU 12 I might try it vs I'll try it 뉘앙스 차이 13 SESSION_EXPIRE_AT_BROWSER_CLOSE=True 14 configuration key 15 Git stash vs discard 16 subprocess.Popen으로 Windows 탐색기에 명령어를 전달 17 Post 잔디 분석하기 18 Google Sheets Sync 최적화 19 DSL (Domain Specific Language)과 GPL (General Purpose Language) 20 마크다운 표 그리는 방법 21 쿼리 파라미터(Query Parameter). 기존 QR코드 재활용 22 Django 보안 취약점 점검 및 수정 23 OOP Object-Oriented Programming 객체 지향 프로그래밍 24 Fernet 대칭 암호화 25 Jekyll 코드블록 안의 Liquid 태그 26 습관만들기 Pawbit TIL 1 27 insertOnConflictUpdate vs DoUpdate(target) 28 세션 필터 29 아코디언(Accordiaon) UI를 펼친상태로 만들기 30 input의 step 31 습관만들기 Pawbit TIL 2 32 습관만들기 Pawbit TIL 3 33 Word Cloud 34 Google Sheets를 데이터 버스로(with AppSheet) 35 Django 모델 텍스트 필드 자동 수집 패턴 36 localStorage로 섹션 토글 상태 유지 37 순차 ID 생성(`select_for_update()` + `max()` 조합) 38 역참조 검색과 distinct() 39 xlsx 다운로드와 로딩 오버레이 충돌 40 Android 파일 공유 MIME 타입 41 AssetManifest — Flutter 빌드 타임 asset 목록 런타임 조회 42 크레센도 Phaser + TypeScript TIL 1 43 크레센도 Phaser + TypeScript TIL 2 44 크레센도 Phaser + TypeScript TIL 3 45 크레센도 Phaser + TypeScript TIL 4 46 크레센도 Phaser + TypeScript TIL 5 47 여둘까 Office Layout TIL 1 48 습관만들기 Pawbit TIL 4 49 여둘까 Office Layout TIL 2 50 여둘까 Office Layout TIL 3 51 크레센도 Phaser + TypeScript TIL 6 52 UTF-8 BOM과 PowerShell 파일 쓰기 53 소리꽃 KeyBloom TIL 1 54 습관만들기 Pawbit TIL 5 55 여둘까 Office Layout TIL 4 56 소리꽃 KeyBloom TIL 2 57 소리꽃 KeyBloom TIL 3 58 메트로놈 Simple Metronome TIL 1 59 소리꽃 KeyBloom TIL 4 60 메트로놈 Simple Metronome TIL 2 61 소리꽃 KeyBloom TIL 5 62 습관만들기 Pawbit TIL 6 63 크레센도 Phaser + TypeScript TIL 7 64 메트로놈 Simple Metronome TIL 3 65 소설 타임라인 관리 도구 Lumior TIL 1 66 습관만들기 Pawbit TIL 7 67 정적 블로그 SEO 정비와 Pagefind 검색 도입 68 크레센도 Phaser + TypeScript TIL 8 69 메트로놈 Simple Metronome TIL 4 70 습관만들기 Pawbit TIL 8 71 안드로이드 AudioTrack 연속 재생, 실측 피커 정렬, 카메라 토치 플래시 72 메트로놈 Simple Metronome TIL 5 73 모래게임 Sandrop TIL 1 74 습관만들기 Pawbit TIL 9 75 메트로놈 Simple Metronome TIL 6 76 모래게임 Sandrop TIL 2 77 모래게임 Sandrop TIL 3 78 습관만들기 Pawbit TIL 10 79 습관만들기 Pawbit TIL 11 80 메트로놈 Simple Metronome TIL 7 81 모래게임 Sandrop TIL 5 82 모래게임 Sandrop TIL 4 83 습관만들기 Pawbit TIL 12 84 모래게임 Sandrop TIL 6 85 소리꽃 KeyBloom TIL 6 86 모래게임 Sandrop TIL 7 87 모래게임 Sandrop TIL 8 88 소리꽃 KeyBloom TIL 7 89 모래게임 Sandrop TIL 9 90 소리꽃 KeyBloom TIL 8 91 모래게임 Sandrop TIL 10 92 소리꽃 KeyBloom TIL 9 93 콘티온 ContiOn TIL 1 94 소리꽃 KeyBloom TIL 10 95 콘티온 ContiOn TIL 2 96 습관만들기 Pawbit TIL 13 97 콘티온 ContiOn TIL 3 읽는 중 98 모래게임 Sandrop TIL 11 99 소리꽃 KeyBloom TIL 11 100 여둘까 Office Layout TIL 5 101 모래게임 Sandrop TIL 12 102 모래게임 Sandrop TIL 13 103 콘티온 ContiOn TIL 4 104 모래게임 Sandrop TIL 14 105 소리꽃 KeyBloom TIL 12 106 습관만들기 Pawbit TIL 14 107 모래게임 Sandrop TIL 15 108 모래게임 Sandrop TIL 16 109 소리꽃 KeyBloom TIL 13 110 온실 GreenHouse TIL 1 111 콘티온 ContiOn TIL 5 112 모래게임 Sandrop TIL 17 113 소리꽃 KeyBloom TIL 14 114 온실 GreenHouse TIL 2 115 콘티온 ContiOn TIL 6 116 모래게임 Sandrop TIL 18 117 콘티온 ContiOn TIL 7 118 모래게임 Sandrop TIL 19 119 소리꽃 KeyBloom TIL 15 120 모래게임 Sandrop TIL 20 121 모래게임 Sandrop TIL 21 122 소리꽃 KeyBloom TIL 16 123 소리꽃 KeyBloom TIL 17 124 소리꽃 KeyBloom TIL 18 125 여둘까 Office Layout TIL 6 126 소리꽃 KeyBloom TIL 19 127 소리꽃 KeyBloom TIL 20 128 콘티온 ContiOn TIL 8 129 콘티온 ContiOn TIL 9 130 로그스톤 상표 셀프 출원 TIL 1 131 소리꽃 KeyBloom TIL 21 132 로그스톤 샵 LogStone Shop TIL 1 133 소리꽃 KeyBloom TIL 22 134 로그스톤 샵 LogStone Shop TIL 2 135 소리꽃 KeyBloom TIL 23 136 모래게임 Sandrop TIL 22 137 모래게임 Sandrop TIL 23 138 소리꽃 KeyBloom TIL 24 139 로그스톤 샵 LogStone Shop TIL 3 140 모래게임 Sandrop TIL 24 141 소리꽃 KeyBloom TIL 25 142 모래게임 Sandrop TIL 25 143 MiniMacro TIL 1 144 모래게임 Sandrop TIL 26 145 콘티온 ContiOn TIL 10 146 Astro가 무엇인지, 왜 옮기는지 147 모래게임 Sandrop TIL 27 148 소리꽃 KeyBloom TIL 26 149 MiniMacro TIL 2 150 로그스톤 샵 LogStone Shop TIL 4 151 메트로놈 Simple Metronome TIL 8 152 모래게임 Sandrop TIL 28 153 여둘까 Office Layout TIL 7 154 메트로놈 Simple Metronome TIL 9 155 블로그 Astro 이관 TIL 156 여둘까 Office Layout TIL 8 157 로그스톤 샵 LogStone Shop TIL 5 158 모래게임 Sandrop TIL 29 159 오픈데이 Openday TIL 1 160 로그스톤 샵 LogStone Shop TIL 6 161 모래게임 Sandrop TIL 30 162 오픈데이 Openday TIL 2 163 모래게임 Sandrop TIL 31 164 Unreal Engine MCP TIL 1 165 모래게임 Sandrop TIL 32 166 소리꽃 KeyBloom TIL 27 167 콘티온 ContiOn TIL 11 168 오픈데이 Openday TIL 3 169 로그스톤 샵 LogStone Shop TIL 7 170 모래게임 Sandrop TIL 33 171 소리꽃 KeyBloom TIL 28 172 오픈데이 Openday TIL 4 173 여둘까 Office Layout TIL 9 174 오픈데이 Openday TIL 5 175 ScorePlayer TIL 1 176 모래게임 Sandrop TIL 34 177 BuildingHub TIL 1 178 ScorePlayer TIL 2 179 여둘까 Office Layout TIL 10 180 BuildingHub TIL 2 181 ScorePlayer TIL 3 182 Unreal Engine MCP TIL 2 183 궁 미로 PalaceMaze TIL 1 184 메트로놈 Simple Metronome TIL 10 185 메트로놈 Simple Metronome TIL 11 186 오픈데이 Openday TIL 6 187 BuildingHub TIL 3 188 Obsidian vault에서 코드만 빼기 — directory junction 189 ScorePlayer TIL 4 190 여둘까 Office Layout TIL 11 191 오픈데이 Openday TIL 7 192 URL은 바뀔 수 있는 것에 묶지 않는다 193 도구는 기능이 아니라 내 일에 맞는지로 고른다 194 돌이키기 힘든 변경은 작게 먼저 확인한다 195 궁 미로 PalaceMaze TIL 2 196 궁 미로 PalaceMaze TIL 3 197 소리꽃 KeyBloom TIL 29 198 오픈데이 Openday TIL 8 199 BuildingHub TIL 4 200 ScorePlayer TIL 5 201 로그스톤 샵 LogStone Shop TIL 8 202 여둘까 Office Layout TIL 12 203 오픈데이 Openday TIL 9 204 BuildingHub TIL 5 205 MiniMacro TIL 3 206 소리꽃 KeyBloom TIL 30 207 오픈데이 Openday TIL 10 208 여둘까 Office Layout TIL 13 209 소리꽃 KeyBloom TIL 31 210 궁 미로 PalaceMaze TIL 4 211 로그스톤 샵 LogStone Shop TIL 9 212 로그스톤 샵 LogStone Shop TIL 10 213 소리꽃 KeyBloom TIL 32