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

영상 내보내기를 실시간 녹화에서 한 장씩 그려 내는 방식으로 바꾸고, 얇던 피아노 소리를 다시 만들며 나온 것들.

실시간에서 푸는 것

시간을 내주고 품질을 산다

내보내기가 화면을 실시간으로 녹화하는 방식이었다. 재생 속도대로 흘러가며 찍으니 무거운 설정에서는 컴퓨터가 못 따라가 장면이 군데군데 빠진다. 앞선 TIL 1에서 초기 버전엔 충분하다고 보고 “결과물 품질이 중요해지면 돌아온다”며 미뤄 둔 바로 그 자리다.

한 장씩 그려서 인코더에 넣는 방식(오프라인 렌더)으로 바꿨다. 시계를 보고 찍는 게 아니라 “몇 번째 프레임”으로 시간을 만드니, 기기가 느리면 오래 걸릴 뿐 빠지는 장면이 없다.

여기서 사용자에게 제안하는 거래가 바뀐다. 전에는 “빠르지만 기기에 따라 결과가 다름”이었고 지금은 “오래 걸리지만 결과는 항상 같음”이다. 결과물을 남에게 보여 줄 물건으로 만드는 도구라면 뒤쪽이 맞다. 실시간이 꼭 필요한 건 미리 보는 동안뿐이다.

사용자는 오래 걸리는 작업 중에 딴짓을 한다

영상 하나 뽑는 데 몇 분이 걸리면 사용자는 그 앞에 앉아 기다리지 않는다. 다른 창을 보러 간다.

그런데 브라우저는 사용자가 안 보는 탭의 작업을 일부러 늦춘다(백그라운드 스로틀링). 배터리를 아끼려는 것인데, 우리 입장에서는 사용자가 다른 창을 보는 동안 작업이 기어가는 셈이다. 소리 쪽도 같은 이유로 끊겼다.

오래 걸리는 작업을 만들 때는 “사용자가 이걸 보고 있지 않을 것”을 전제로 삼아야 한다. 그 전제 위에서 멈추지 않게 만드는 방법을 찾고, 끝나면 알려 줄 방법도 같이 생각해야 한다.

결과물이 크면 메모리에 쌓지 말고 흘려보낸다

긴 영상은 완성 파일이 수 GB가 된다. 다 만들어 놓고 마지막에 저장하는 방식이면 그 전부가 메모리에 있어야 하고, 어느 길이부터는 그냥 터진다.

그래서 만들면서 바로 디스크로 흘려보내게 했다(스트리밍 쓰기). 완성될 때까지 메모리에 있는 건 지금 만드는 조각뿐이다.

결과물 크기가 입력에 따라 커지는 기능은 어느 지점에서 한계가 오는지를 초기에 확인해야 한다. 짧은 것으로만 테스트하면 정상으로 보이고, 사용자는 대개 긴 것을 넣는다.


흉내와 진짜

흉내 내는 것보다 진짜를 가져오는 게 빠를 때가 있다

피아노 소리가 얇아서 제대로 만들어 보기로 했다. 실제 현이 울리는 원리를 따라 배음(기본음 위에 겹치는 정수배 진동)을 쌓고, 소리 크기가 시간에 따라 변하는 곡선(엔벨로프)을 때리는 순간과 여운 두 단으로 나누고, 세게 칠수록 밝게 들리도록 고음을 깎는 필터를 세기에 연동했다. 확실히 나아졌다.

그런데도 전자 피아노 느낌이 남았다. 결국 실제 피아노를 녹음한 소리로 통째로 갈아탔다. 몇 개 음만 녹음해 두고 그 사이는 재생 속도를 바꿔 음정을 맞추면(피치시프트) 88건반이 다 커버된다. 이렇게 여러 음을 나눠 녹음해 쓰는 방식을 멀티샘플링이라고 한다.

들인 시간이 아깝지만, 만들어 보고 나서야 “이 방향으로는 한계가 있다”를 알았다. 다시 한다면 순서를 바꿀 것 같다. 흉내로 어디까지 되는지 짧게 확인하고, 목표 수준이 그보다 높으면 처음부터 진짜를 가져오는 쪽으로 간다.

판단 기준은 “만들 수 있나”가 아니라 “우리가 원하는 수준이 흉내로 도달 가능한 범위 안인가”다. 소리·질감처럼 사람이 진짜를 아주 잘 아는 영역은 대개 범위 밖이다.


제한과 알림

제한은 우회 경로까지 본다

무료 버전에는 결과물에 워터마크를 남긴다. 처음엔 뽑은 영상에만 넣었는데, 그러면 화면을 다른 녹화 프로그램으로 찍으면 표시 없는 영상이 나온다.

그래서 미리보기 화면에도 같이 그렸다. 화면을 찍어도 표시가 따라간다.

제한을 걸 때는 “이걸 피하려면 어떻게 하겠나”를 한 번 생각해 봐야 한다. 완전히 막을 수는 없지만, 가장 쉬운 우회로 하나를 막는 것만으로 대부분이 걸러진다. 반대로 그 하나를 안 막으면 제한 자체가 의미를 잃는다.

사용자가 잃을 수 있는 것은 미리 알린다

이 앱은 작업을 서버에 저장하지 않는다. 새로고침하면 하던 작업이 사라진다.

이걸 안내에 명시했다. 구조를 바꿔 자동 저장을 넣는 게 더 좋겠지만, 그전까지는 최소한 알려야 한다. 알리지 않으면 사용자는 저장되는 줄 알고 작업하다 잃는다.

제품에 “이렇게 하면 잃는다”는 지점이 있으면 그건 알림 대상이다. 고칠 수 없어서 안 알리는 것과 고칠 때까지 알리는 것은 다르다.


자기완결과 규모

프로젝트 파일은 그것만으로 열려야 한다

작업 파일에 반주 음원을 통째로 넣었다. 경로만 기록하면 파일 크기는 작지만, 다른 컴퓨터에서 열면 음원을 못 찾는다. 웹에서는 애초에 경로를 기억할 수도 없다.

프로젝트 파일은 사용자가 남에게 보내거나 다른 기기로 옮기는 물건이다. 열었을 때 그것만으로 완전하지 않으면 사용자는 “파일이 깨졌다”고 받아들인다. 용량이 커지는 건 그다음 문제다.

규모가 작으면 정석이 아니라 가벼운 쪽이 맞다

다국어를 넣을 때 보통은 문구를 딕셔너리에 모아 두고 키(이름표)로 불러 쓴다. 규모가 커지면 그게 맞다.

이 앱은 문구가 많지 않아서, 문구가 필요한 자리마다 한국어와 영어를 나란히 적는 방식으로 갔다. 번역이 코드 옆에 있으니 문맥이 안 흩어지고, 새 문구를 추가할 때 파일을 오갈 일이 없다.

정석은 대체로 큰 규모를 전제로 만들어진다. 우리 규모가 그 전제 안에 있는지 먼저 보고, 아니면 가벼운 쪽을 골라도 된다. 나중에 커지면 그때 옮기는 비용이, 지금 무겁게 시작하는 비용보다 싼 경우가 많다.

기기(GPU)마다 지원하는 코덱 프로파일(영상 압축 규격의 화질 등급)이 달라서, 가장 좋은 것부터 시도하고 안 되면 한 단계씩 낮추는 폴백을 뒀다. 사용자 기기를 우리가 고를 수 없는 이상, “제일 좋은 것”을 고정으로 요구하면 누군가는 아예 못 쓴다.


요약

  • 실시간에서 풀면 시간을 내주고 결과 품질을 산다 — 남에게 보여 줄 결과물이면 그 거래가 맞다
  • 오래 걸리는 작업은 사용자가 안 보고 있을 것을 전제로 만든다
  • 결과물 크기가 입력에 따라 커지면 한계 지점을 초기에 확인한다
  • 흉내로 도달 가능한 범위를 먼저 재 본다 — 사람이 진짜를 잘 아는 영역은 대개 범위 밖이다
  • 제한을 걸 땐 가장 쉬운 우회로 하나는 같이 막는다. 안 막으면 제한이 의미를 잃는다
  • 잃을 수 있는 지점은 고치기 전까지 알린다
  • 프로젝트 파일은 그것만으로 열려야 한다. 용량은 그다음 문제다
  • 정석은 큰 규모를 전제로 한다 — 우리 규모가 그 전제 안인지 먼저 본다

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