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

외부 도구에 파일을 만들어 넘기는 일을 한 달 전에 한 번 「불가」로 적어 뒀는데, 이번에 실물을 뜯어보고 그 판단을 뒤집었다. 뒤집히기까지 거친 장면들을 남긴다.


「못 만든다」고 적어 둔 것을 다시 꺼냈다

앞선 TIL 11에서 외부 자막기 파일을 직접 만들 수 있는지 판단했고, 결론은 못 만든다였다. 근거는 둘이었다. 그 파일이 독자 규격의 바이너리 포맷(사람이 읽는 글자가 아니라 프로그램용 이진 데이터로 저장된 형식)이라는 것, 그리고 안에 텍스트로는 채울 수 없는 3차원 장면 데이터가 들어 있다는 것이었다. 그래서 그 도구가 스스로 열어 둔 입력 통로를 쓰기로 했고, 콘티온은 텍스트 파일을 내보내는 데서 멈췄다.

한 달을 그렇게 쓰다가 이번에 실물 폴더를 통째로 받았다. 행사 네 번 분량에 파일이 천 개 넘게 들어 있었다. 「이 안에서 순서 목록을 뽑아낼 수 있는지, 그리고 콘티온에서 무엇을 내려받으면 이 안의 글자를 갈아 끼울 수 있는지」가 이번 질문이었다.


매뉴얼에 있는 기능이 현장에선 안 쓰이고 있었다

파일을 보기 전에 먼저 나온 안은 그 도구가 제공하는 데이터 링크(외부 텍스트 파일을 줄 번호로 끌어다 쓰는 기능 — 앞선 TIL 11에서 찾아 둔 그 통로)를 쓰는 것이었다. 설정해 두면 콘티온은 텍스트만 내보내면 된다.

그 자리에서 접었다. 자막 담당자가 그런 설정까지 알 리 없다고 봤기 때문이다. 확인해 보니 실제로 그 기능은 천 개 넘는 파일 어디에도 쓰이고 있지 않았다. 매뉴얼에 있다고 현장에서 쓰이는 건 아니었다.

기능이 있느냐와 그 기능이 쓰이느냐는 다른 문제다. 도구를 바꾸는 방법을 낼 때는 그걸 배워야 하는 사람이 누구인지부터 봐야 한다. 배울 사람이 안 배우면 그 방법은 없는 것과 같다. 쓰는 사람이 새로 익혀야 할 것이 있다면, 그건 해법의 부록이 아니라 해법의 가격이다.


파일 하나 뽑아서 바로 넣고 싶었다

다음으로 나온 안은 콘티온이 파일을 곡별로 쪼개 주는 것이었다. 담당자는 텍스트를 한 번에 넣으면 줄마다 자막 한 장이 생기는 방식으로 일하고 있었는데, 제목과 가사는 디자인이 달라서 제목판과 가사판을 따로 만든 뒤 최종본에 번갈아 끼워 넣고 있었다. 그러니 곡별로 나눠 주면 순서대로 넣기만 해도 된다는 것이었다.

그건 내가 원한 게 아니었다. 넣는 횟수가 두 번에서 곡 수만큼으로 늘 뿐, 사람이 하나씩 자리를 맞추는 일은 그대로 남는다. 모양만 바꾼 같은 노동이다. 내가 원한 건 파일 하나를 뽑아 그대로 넣으면 끝나는 것이었다.

불편의 정체는 겪는 사람이 말해야 한다. 만든 쪽에서 본 병목은 「끼워 넣는 횟수」였고, 실제 불편은 「손으로 자리를 맞춘다」였다. 이 둘은 해법이 완전히 다르다. 횟수를 줄이는 쪽으로는 아무리 다듬어도 답이 안 나온다. 그러니 불편하다고 말할 때는 무엇이 불편한지를 내가 정확히 말해 주는 편이 빠르다.


다시 뜯어 보니 글자가 그냥 줄줄이 있었다

그래서 접어 뒀던 쪽을 다시 봤다. 파일을 만들어 주는 것 말이다.

열어 보니 안에 글자가 길이 접두 문자열(글자 앞에 「몇 글자인지」를 먼저 적어 두는 저장 방식)로 줄줄이 놓여 있었다. 자막 문구도, 이미지 파일 이름도, 화면에 붙은 이름표도 전부 그 꼴이었다. 한 달 전에 못 만든다고 판단한 근거는 파일 종류였는데, 정작 우리가 건드려야 할 글자는 그냥 평범하게 들어 있었다.

여기서 질문이 갈렸다는 것도 알았다. 「이 형식의 파일을 처음부터 만들 수 있나」와 「이미 있는 파일을 복제해 한 군데만 바꿀 수 있나」는 전혀 다른 질문이다. 앞엣것은 그 형식을 전부 이해해야 하지만, 뒤엣것은 바꿀 한 군데만 알면 된다. 한 달 전에는 이 둘을 「파일을 만든다」로 뭉뚱그려서 어려운 쪽 답으로 결론을 내버렸다.

복제하는 쪽으로 보면 남은 근거 하나도 같이 풀린다. 3차원 장면 데이터는 우리가 만들 필요가 없다. 복제한 원본에 그대로 남아 있고 우리는 글자만 갈아 끼우니, 「텍스트로는 그걸 채울 수 없다」는 근거 자체가 이 방식에선 무의미해진다.

겉으로 붙은 형식의 이름이 아니라 안에 무엇이 어떻게 놓였는지가 답을 정한다. 그리고 그건 열어 봐야만 안다. 뒤집는 김에 반대 근거로 보이던 것도 하나 확인해 지웠다 — 「안 되는 이유를 찾았다」 싶을 때가 오히려 한 번 더 확인할 자리였다.


「대표」를 「대표자」로 바꾸면 뒤가 다 밀린다

마지막으로 본 건 글자 수가 달라져도 되느냐였다. 한 글자가 늘면 파일이 그만큼 길어지고, 그 뒤에 있는 모든 것의 자리가 밀린다.

이때 갈리는 기준이 있었다. 파일 안에 오프셋 필드나 길이 필드(무엇이 몇 번째 자리에 있다, 전체 크기가 얼마다를 따로 적어 둔 값)가 있느냐다. 그런 값이 있으면 한 글자만 늘어도 그 값들이 전부 거짓이 되어 파일이 깨진다. 없으면 앞에서부터 차례로 읽어 내려가는 구조라, 길이가 바뀌어도 그 글자의 길이 숫자만 고치면 된다.

찾아보니 그런 값이 하나도 없었다. 이게 이번 판단의 결정적 근거였다.

이건 이 도구에만 해당하는 얘기가 아니다. 남이 만든 파일을 손대도 되는지 볼 때, 눈으로 훑어서 「복잡해 보인다」로 정할 게 아니라 이 질문 하나를 먼저 던지면 된다. 안에 오프셋·길이 필드가 있는가. 있으면 손대는 순간 전부 맞춰야 하고, 없으면 한 군데만 고쳐도 된다.

다만 여기까지는 파일을 읽어서 낸 결론이라, 실제로 그 도구가 열어 주는지는 별개다. 그래서 원본은 그대로 두고 복사본에 글자 수가 다른 문구 세 개를 심어 시험 파일을 만들었다. 같은 글자 수로 바꾸는 건 당연히 되니 시험할 값어치가 없고, 늘어나고 줄어드는 경우라야 방금 세운 판단이 맞는지가 드러난다.


요약

  • 도구를 바꾸는 해법을 낼 때는 그걸 배워야 하는 사람이 누구인지부터 본다. 쓰는 사람이 새로 익혀야 하는 것은 해법의 부록이 아니라 가격이다.
  • 매뉴얼에 있는 기능이 현장에서 쓰이고 있는 건 아니다. 실물을 뒤져 보면 안 쓰는 기능이 그대로 드러난다.
  • 불편의 정체는 겪는 사람이 말해야 한다. 「횟수가 많다」와 「손으로 자리를 맞춘다」는 다른 문제고, 해법도 다르다.
  • 「이 형식을 처음부터 만들 수 있나」와 「있는 것을 복제해 한 군데만 바꿀 수 있나」는 다른 질문이다. 뭉뚱그리면 어려운 쪽 답으로 결론이 난다. 복제하는 쪽에서는 못 만드는 부분이 원본에 그대로 남으므로 그 근거가 사라진다.
  • 형식의 이름이 아니라 안에 무엇이 어떻게 놓였는지가 답을 정한다. 그건 열어 봐야 안다.
  • 남의 파일을 손대도 되는지는 안에 오프셋·길이 필드가 있느냐로 가른다. 있으면 전부 맞춰야 하고, 없으면 한 군데만 고치면 된다.
  • 시험은 가장 의심스러운 조건으로 짠다. 당연히 되는 경우를 시험하면 아무것도 알아내지 못한다.

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 | 룸톤 Roomtone TIL 1 99 | 모래게임 Sandrop TIL 11 100 | 소리꽃 KeyBloom TIL 11 101 | 여둘까 Office Layout TIL 5 102 | 모래게임 Sandrop TIL 12 103 | 모래게임 Sandrop TIL 13 104 | 콘티온 ContiOn TIL 4 105 | 모래게임 Sandrop TIL 14 106 | 소리꽃 KeyBloom TIL 12 107 | 습관만들기 Pawbit TIL 14 108 | 모래게임 Sandrop TIL 15 109 | 모래게임 Sandrop TIL 16 110 | 소리꽃 KeyBloom TIL 13 111 | 온실 GreenHouse TIL 1 112 | 콘티온 ContiOn TIL 5 113 | 모래게임 Sandrop TIL 17 114 | 소리꽃 KeyBloom TIL 14 115 | 온실 GreenHouse TIL 2 116 | 콘티온 ContiOn TIL 6 117 | 모래게임 Sandrop TIL 18 118 | 콘티온 ContiOn TIL 7 119 | 모래게임 Sandrop TIL 19 120 | 소리꽃 KeyBloom TIL 15 121 | 룸톤 Roomtone TIL 2 122 | 모래게임 Sandrop TIL 20 123 | 모래게임 Sandrop TIL 21 124 | 소리꽃 KeyBloom TIL 16 125 | 소리꽃 KeyBloom TIL 17 126 | 소리꽃 KeyBloom TIL 18 127 | 여둘까 Office Layout TIL 6 128 | 소리꽃 KeyBloom TIL 19 129 | 소리꽃 KeyBloom TIL 20 130 | 콘티온 ContiOn TIL 8 131 | 콘티온 ContiOn TIL 9 132 | 로그스톤 상표 셀프 출원 TIL 1 133 | 소리꽃 KeyBloom TIL 21 134 | 로그스톤 샵 LogStone Shop TIL 1 135 | 소리꽃 KeyBloom TIL 22 136 | 로그스톤 샵 LogStone Shop TIL 2 137 | 룸톤 Roomtone TIL 3 138 | 소리꽃 KeyBloom TIL 23 139 | 모래게임 Sandrop TIL 22 140 | 모래게임 Sandrop TIL 23 141 | 소리꽃 KeyBloom TIL 24 142 | 로그스톤 샵 LogStone Shop TIL 3 143 | 모래게임 Sandrop TIL 24 144 | 소리꽃 KeyBloom TIL 25 145 | 모래게임 Sandrop TIL 25 146 | MiniMacro TIL 1 147 | 룸톤 Roomtone TIL 4 148 | 모래게임 Sandrop TIL 26 149 | 콘티온 ContiOn TIL 10 150 | Astro가 무엇인지, 왜 옮기는지 151 | 룸톤 Roomtone TIL 5 152 | 룸톤 Roomtone TIL 6 153 | 모래게임 Sandrop TIL 27 154 | 소리꽃 KeyBloom TIL 26 155 | MiniMacro TIL 2 156 | 로그스톤 샵 LogStone Shop TIL 4 157 | 메트로놈 Simple Metronome TIL 8 158 | 모래게임 Sandrop TIL 28 159 | 여둘까 Office Layout TIL 7 160 | 룸톤 Roomtone TIL 7 161 | 메트로놈 Simple Metronome TIL 9 162 | 블로그 Astro 이관 TIL 163 | 여둘까 Office Layout TIL 8 164 | 로그스톤 샵 LogStone Shop TIL 5 165 | 모래게임 Sandrop TIL 29 166 | 오픈데이 Openday TIL 1 167 | 로그스톤 샵 LogStone Shop TIL 6 168 | 모래게임 Sandrop TIL 30 169 | 오픈데이 Openday TIL 2 170 | 모래게임 Sandrop TIL 31 171 | Unreal Engine MCP TIL 1 172 | 모래게임 Sandrop TIL 32 173 | 소리꽃 KeyBloom TIL 27 174 | 콘티온 ContiOn TIL 11 175 | 오픈데이 Openday TIL 3 176 | 로그스톤 샵 LogStone Shop TIL 7 177 | 모래게임 Sandrop TIL 33 178 | 소리꽃 KeyBloom TIL 28 179 | 오픈데이 Openday TIL 4 180 | 여둘까 Office Layout TIL 9 181 | 오픈데이 Openday TIL 5 182 | ScorePlayer TIL 1 183 | 모래게임 Sandrop TIL 34 184 | BuildingHub TIL 1 185 | ScorePlayer TIL 2 186 | 여둘까 Office Layout TIL 10 187 | BuildingHub TIL 2 188 | ScorePlayer TIL 3 189 | Unreal Engine MCP TIL 2 190 | 궁 미로 PalaceMaze TIL 1 191 | 메트로놈 Simple Metronome TIL 11 192 | 메트로놈 Simple Metronome TIL 10 193 | 오픈데이 Openday TIL 6 194 | BuildingHub TIL 3 195 | Obsidian vault에서 코드만 빼기 — directory junction 196 | ScorePlayer TIL 4 197 | 여둘까 Office Layout TIL 11 198 | 오픈데이 Openday TIL 7 199 | URL은 바뀔 수 있는 것에 묶지 않는다 200 | 돌이키기 힘든 변경은 작게 먼저 확인한다 201 | 도구는 기능이 아니라 내 일에 맞는지로 고른다 202 | 궁 미로 PalaceMaze TIL 2 203 | 궁 미로 PalaceMaze TIL 3 204 | 소리꽃 KeyBloom TIL 29 205 | 오픈데이 Openday TIL 8 206 | 룸톤 Roomtone TIL 8 207 | BuildingHub TIL 4 208 | ScorePlayer TIL 5 209 | 로그스톤 샵 LogStone Shop TIL 8 210 | 여둘까 Office Layout TIL 12 211 | 오픈데이 Openday TIL 9 212 | BuildingHub TIL 5 213 | MiniMacro TIL 3 214 | 룸톤 Roomtone TIL 10 215 | 룸톤 Roomtone TIL 9 216 | 소리꽃 KeyBloom TIL 30 217 | 오픈데이 Openday TIL 10 218 | 여둘까 Office Layout TIL 13 219 | 룸톤 Roomtone TIL 11 220 | 소리꽃 KeyBloom TIL 31 221 | 궁 미로 PalaceMaze TIL 4 222 | 룸톤 Roomtone TIL 12 223 | 로그스톤 샵 LogStone Shop TIL 9 224 | 룸톤 Roomtone TIL 13 225 | 로그스톤 샵 LogStone Shop TIL 10 226 | 룸톤 Roomtone TIL 14 227 | 소리꽃 KeyBloom TIL 32 228 | 룸톤 Roomtone TIL 15 229 | 룸톤 Roomtone TIL 16 230 | 룸톤 Roomtone TIL 17 231 | 룸톤 Roomtone TIL 18 232 | 메트로놈 Simple Metronome TIL 12 233 | 콘티온 ContiOn TIL 12 읽는 중 234 | 온실 GreenHouse TIL 3 235 | 룸톤 Roomtone TIL 19 236 | 여러 세션을 INBOX 하나로 굴리기