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

투명 배경 영상 내보내기를 4K까지 밀어 보다 포기한 이야기, 유료 경계를 다시 그은 이야기, 그리고 모래 파티클을 세 번 실패하고 얻은 것.

해상도라는 벽

4배 무거운 게 아니라 아예 안 되는 지점이 있다

투명 배경 영상은 투명도를 살려야 해서 화면을 압축 없이 다뤄야 한다. 4K는 한 장이 33MB쯤 되고, 초당 수십 장이 흘러간다.

1080p에서 잘 돌던 방식이 4K에서 세 군데서 연달아 막혔다. 전체 메모리는 남아 있는데도 33MB짜리 연속 공간을 못 잡아 터지고(주소 공간 파편화), 작업을 워커(백그라운드 스레드)에 나눠 맡기니 스레드 사이로 화면을 넘기는 과정이 불안정해지고, 한 번에 몇 장까지 물릴지 정해 둔 값이 4K에서는 네 배로 불어나 초과했다. 하나씩 고쳐도 다음 벽이 나왔다.

여기서 배운 건 기술이 아니라 판단이다. 크기를 키우는 일은 대체로 “그만큼 느려진다”가 아니라 “어느 지점부터 아예 안 된다”에 가깝다. 그리고 벽이 연달아 나올 때는 하나씩 넘는 대신 이 길이 맞는지를 다시 봐야 한다.

결국 이 앱은 1080p까지로 확정하고, 4K는 나중에 다른 방식으로 만드는 별도 제품으로 미뤘다. 아쉬운 결정이지만 세 번째 벽에서 멈춘 게 맞았다고 본다. 넘었어도 네 번째가 있었을 것이다.

한도는 개수가 아니라 실제 자원으로 잡는다

동시에 처리할 화면 수를 “몇 장”으로 정해 뒀던 게 위 문제 중 하나였다. 그 숫자는 1080p 기준으로 정한 것이라 4K에서는 같은 장 수가 네 배 무게였다.

한도를 정할 때 세는 단위가 실제로 소비하는 자원과 비례하지 않으면, 조건이 바뀌는 순간 한도가 거짓말이 된다. 장 수가 아니라 용량으로 한도를 잡으니 해상도가 바뀌어도 알아서 적게 물린다.

같은 함정이 “동시 접속 몇 명”, “한 번에 몇 건” 같은 데도 있다. 세기 쉬운 단위와 실제로 부담을 만드는 단위가 다르면 언젠가 어긋난다.


사용자에게 무엇을 요구할 것인가

설치를 요구하는 순간 사람이 떨어진다

앞선 TIL 18에서 윈도우는 용량이 작고 품질 좋은 Ut Video로 가기로 했었다. 문제는 그 파일을 편집 프로그램에서 열려면 사용자가 코덱(영상을 풀어 읽는 프로그램)을 따로 설치해야 한다는 점이었다.

우리 입장에서는 “설치 링크 안내하면 되지”인데, 사용자 입장에서는 영상 하나 쓰려고 처음 보는 프로그램을 깔라는 요구다. 게다가 편집을 맡기거나 다른 사람에게 파일을 넘기면 그 사람도 깔아야 한다.

그래서 편집 프로그램들이 설치 없이 바로 읽는 ProRes 4444로 바꿨다. 파일은 커졌지만 “받아서 그냥 얹으면 된다”가 됐다.

산출물 포맷을 고를 때 기준은 우리 쪽 효율이 아니라 받는 사람의 손이 몇 번 가느냐다. 특히 남에게 넘기는 파일이면 그 손이 두 배로 든다.

함께 정한 것 — ProRes는 원래 애플 코덱이라, ffmpeg가 만든 우리 쪽 구현은 정식 인코더가 아니다. 실무 호환에는 문제가 없지만 제품 화면과 홍보 문구에서는 그 이름을 앞세우지 않고 “호환” 정도로만 적기로 했다. 이름을 빌려 쓰는 자리에서는 한 발 물러서 적는 게 싸게 먹힌다.

유료 경계를 하나로 묶으면 나중에 못 쪼갠다

앞선 TIL 17에서 잠금 판단을 한 곳에 모으면서, 워터마크 제거·4K·투명 배경을 「유료인가」 하나로 묶어 잠갔다. 그런데 위에서 4K를 이 앱에서 포기하기로 하면서 정책이 바뀌었다. 투명 배경은 유료로 열고 4K는 아무에게도 안 여는 것이다.

묶어 둔 판정으로는 4K만 따로 뺄 수가 없었다. 기능마다 따로 판정하게 나누고 나서야 정책을 자유롭게 바꿀 수 있었다.

유료 경계는 처음 정한 대로 끝까지 가는 일이 거의 없다. 가격을 바꾸거나, 등급을 늘리거나, 기능 하나를 무료로 풀어 유입을 늘려 보거나 한다. 그럴 때 코드가 발목을 잡지 않으려면 기능별로 잠금을 따로 두는 게 맞다. 잠금 하나가 여러 기능을 대표하고 있으면 그 조합을 바꾸는 순간 다시 만들어야 한다.


무리를 자연스럽게 만들기

뭉침은 공유하고 움직임은 개별로

노트를 치면 모래알 같은 알갱이들이 한 무리로 떠오르되 각자 자유롭게 흩날리는 모션을 만들려 했다. 세 번 실패했다.

처음엔 화면 전체에 흐름장(위치마다 방향과 세기가 정해진, 보이지 않는 흐름의 지도)을 깔고 알갱이를 태웠다. 한 무리는 좁은 자리에 모여 있어서 다들 같은 흐름을 타고, 결국 다 같이 도는 도넛이 됐다. 다음엔 무리를 뭉치려고 중심으로 당기는 힘(스프링)을 줬다. 점으로 당기면 그 점 둘레에 껍질이 생겨서 또 고리가 됐고, 화음을 치면 여러 무리가 같은 박자로 부풀었다 오므라들었다. 세 번째로 좌우로 못 벗어나게 벽을 세웠더니, 벽에 닿은 알갱이들이 거기 멈춰 서서 양쪽에 줄이 생겼다.

세 번 다 원인이 같았고, 앞선 TIL 13에서 나선을 가둘 때 본 것과도 같았다. 좁은 무리에 공유 규칙을 걸면 다 같이 움직여서 대형이 보인다. 자연스러움은 각자 다르게 움직이는 데서 온다.

답은 공유를 버리는 것이었다. 알갱이마다 자기만의 리듬을 주니 고리도 집단 박자도 한 번에 사라졌다. 좌우로 가두는 것도 벽 대신 “항상 그 폭 안에서 살랑거리는 움직임”으로 바꿨다. 벗어날 수가 없으니 벽에 닿을 일도 없고, 그래서 줄도 안 생긴다.

여기서 세 가지를 가져간다. 점으로 당기면 껍질이 생기고, 벽을 세우면 거기 쌓이고, 같은 규칙을 공유하면 대형이 보인다. 무리로 보이게 하고 싶은 것과 개별로 보이게 하고 싶은 것을 먼저 나누고, 무리 쪽만 공유해야 한다. 여기서 공유한 건 “어느 건반에서 태어나 위로 간다”까지였고 나머지는 전부 개별이었다.


요약

  • 크기를 키우는 일은 “그만큼 느려진다”가 아니라 “어느 지점부터 안 된다”에 가깝다
  • 벽이 연달아 나오면 하나씩 넘지 말고 이 길이 맞는지 다시 본다
  • 한도는 세기 쉬운 단위가 아니라 실제로 부담을 만드는 단위로 잡는다
  • 산출물 포맷은 우리 효율이 아니라 받는 사람의 손이 몇 번 가느냐로 고른다
  • 유료 잠금은 기능별로 나눠 둬야 나중에 정책을 바꿀 수 있다
  • 무리는 뭉침만 공유하고 움직임은 개별로 — 공유 규칙은 대형을 만들고 벽은 줄을 만든다

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 insertOnConflictUpdate vs DoUpdate(target) 27 세션 필터 28 아코디언(Accordiaon) UI를 펼친상태로 만들기 29 input의 step 30 Word Cloud 31 Google Sheets를 데이터 버스로(with AppSheet) 32 Django 모델 텍스트 필드 자동 수집 패턴 33 localStorage로 섹션 토글 상태 유지 34 순차 ID 생성(`select_for_update()` + `max()` 조합) 35 역참조 검색과 distinct() 36 xlsx 다운로드와 로딩 오버레이 충돌 37 Android 파일 공유 MIME 타입 38 AssetManifest — Flutter 빌드 타임 asset 목록 런타임 조회 39 UTF-8 BOM과 PowerShell 파일 쓰기 40 소리꽃 KeyBloom TIL 1 41 소리꽃 KeyBloom TIL 2 42 소리꽃 KeyBloom TIL 3 43 메트로놈 Simple Metronome TIL 1 44 소리꽃 KeyBloom TIL 4 45 메트로놈 Simple Metronome TIL 2 46 소리꽃 KeyBloom TIL 5 47 메트로놈 Simple Metronome TIL 3 48 정적 블로그 SEO 정비와 Pagefind 검색 도입 49 메트로놈 Simple Metronome TIL 4 50 안드로이드 AudioTrack 연속 재생, 실측 피커 정렬, 카메라 토치 플래시 51 메트로놈 Simple Metronome TIL 5 52 모래게임 Sandrop TIL 1 53 메트로놈 Simple Metronome TIL 6 54 모래게임 Sandrop TIL 2 55 모래게임 Sandrop TIL 3 56 메트로놈 Simple Metronome TIL 7 57 모래게임 Sandrop TIL 4 58 모래게임 Sandrop TIL 5 59 모래게임 Sandrop TIL 6 60 소리꽃 KeyBloom TIL 6 61 모래게임 Sandrop TIL 7 62 모래게임 Sandrop TIL 8 63 소리꽃 KeyBloom TIL 7 64 모래게임 Sandrop TIL 9 65 소리꽃 KeyBloom TIL 8 66 모래게임 Sandrop TIL 10 67 소리꽃 KeyBloom TIL 9 68 소리꽃 KeyBloom TIL 10 69 소리꽃 KeyBloom TIL 11 70 소리꽃 KeyBloom TIL 12 71 소리꽃 KeyBloom TIL 13 72 온실 GreenHouse TIL 1 73 소리꽃 KeyBloom TIL 14 74 온실 GreenHouse TIL 2 75 소리꽃 KeyBloom TIL 15 76 소리꽃 KeyBloom TIL 16 77 소리꽃 KeyBloom TIL 17 78 소리꽃 KeyBloom TIL 18 79 소리꽃 KeyBloom TIL 19 80 소리꽃 KeyBloom TIL 20 81 로그스톤 상표 셀프 출원 TIL 1 82 소리꽃 KeyBloom TIL 21 읽는 중 83 소리꽃 KeyBloom TIL 22 84 소리꽃 KeyBloom TIL 23 85 소리꽃 KeyBloom TIL 24 86 MiniMacro TIL 1 87 Astro가 무엇인지, 왜 옮기는지 88 MiniMacro TIL 2 89 메트로놈 Simple Metronome TIL 8 90 메트로놈 Simple Metronome TIL 9 91 블로그 Astro 이관 TIL 92 오픈데이 Openday TIL 1 93 오픈데이 Openday TIL 2 94 Unreal Engine MCP TIL 1 95 콘티온 Conti On TIL 11 96 오픈데이 Openday TIL 3 97 오픈데이 Openday TIL 4 98 오픈데이 Openday TIL 5 99 ScorePlayer TIL 1 100 BuildingHub TIL 1 101 ScorePlayer TIL 2 102 BuildingHub TIL 2 103 ScorePlayer TIL 3 104 Unreal Engine MCP TIL 2 105 궁 미로 PalaceMaze TIL 1 106 메트로놈 Simple Metronome TIL 10 107 메트로놈 Simple Metronome TIL 11 108 오픈데이 Openday TIL 6 109 BuildingHub TIL 3 110 Obsidian vault에서 코드만 빼기 — directory junction 111 ScorePlayer TIL 4 112 오픈데이 Openday TIL 7 113 URL은 바뀔 수 있는 것에 묶지 않는다 114 도구는 기능이 아니라 내 일에 맞는지로 고른다 115 돌이키기 힘든 변경은 작게 먼저 확인한다 116 궁 미로 PalaceMaze TIL 3 117 궁 미로 PalaceMaze TIL 2 118 소리꽃 KeyBloom TIL 29 119 오픈데이 Openday TIL 8 120 BuildingHub TIL 4 121 ScorePlayer TIL 5 122 BuildingHub TIL 5 123 MiniMacro TIL 3 124 소리꽃 KeyBloom TIL 30 125 궁 미로 PalaceMaze TIL 4