PokeClip의 클립은 한 번 내보내고 끝나는 파일이 아니어야 했습니다. 같은 하이라이트를 세로 쇼츠로 바꾸거나 배경음악만 끄려면, 편집자가 처음 선택한 구간과 오디오가 남아 있어야 합니다. 완성 영상만 저장하면 이미 합쳐진 음악과 자막을 다시 분리하는 문제부터 풀어야 합니다.
저는 원본을 보존하고 편집 선택을 JSON 레시피로 저장하는 구조를 택했습니다. 사용자가 바꾼 것은 레시피에, 서버가 만든 것은 렌더 결과물에 남기고 두 수명을 나눴습니다. 제가 맡은 미디어·인프라 계층과 팀원이 구현하는 웹·Spring 코어·렌더 워커가 주고받을 계약도 이 구분에서 정했습니다. 설계 초안과 조사에는 AI를 활용했습니다.
되돌릴 수 있어야 하는 것은 파일보다 선택이었다
편집 결과만 저장하는 방식은 재생과 배포가 단순합니다. 그러나 다시 편집하려면 사용자의 선택을 복원할 별도 정보가 필요합니다. 특히 PokeClip은 편집용 원본에 6개 오디오 트랙을 보존하고, 되감기용 출력은 영상 1개·오디오 1개로 구분합니다. 최종 혼합본만 보관하면 원하는 소리만 다시 고를 수 있다는 제품의 전제가 사라집니다.
그래서 원본은 유지하고, 사용자가 바꾼 선택은 레시피 JSON에 남기도록 설계했습니다. 레시피가 답해야 할 질문을 다음처럼 나눴습니다.
| 레시피의 정보 | 재렌더링할 때 필요한 이유 |
|---|---|
| 선택 시간 구간 | 원본의 어느 부분을 가져올지 결정 |
| 크롭·출력 비율 | 가로 영상과 세로 쇼츠의 화면 구성 재현 |
| 트랙·트랙별 음량 | 원본의 소리 중 무엇을 얼마나 사용할지 결정 |
| 제목·자막 스타일 | 영상 위 표현을 다시 구성 |
예를 들어 음악의 음량을 0으로 바꾸면 원본 파일을 덮어쓰는 대신 레시피의 선택을 바꿉니다. 서버는 같은 원본을 새 레시피로 읽어 다른 결과물을 만듭니다. 원본과 결과물 사이에 편집 의도가 남으므로, 파일을 다시 만들 필요가 생겨도 사용자가 처음부터 선택을 반복할 이유가 줄어듭니다.
미리보기와 최종 파일이 같은 일을 할 필요는 없었다
에디터에서 음량이나 화면 위치를 조금 바꿀 때마다 서버가 완성 파일을 만들면, 인터랙션이 렌더 작업의 완료에 묶입니다. 화면에서는 클라이언트의 video와 화면 합성으로 선택을 확인하고, 저장 시점의 최종 렌더를 서버의 비동기 작업으로 보내도록 책임을 나눴습니다.
렌더·업로드에는 작업 상태와 진행률, 재시도가 필요합니다. 긴 작업을 화면 요청의 수명과 분리하고, 재시도에서도 어떤 원본과 편집 선택을 사용했는지 다시 확인할 수 있어야 합니다. 다만 레시피가 존재한다는 사실만으로 모든 재시도가 동일한 바이트를 만든다고 보장할 수는 없습니다. 여기서 확정한 것은 편집 입력의 명시화이며, 인코더 환경까지 포함한 결과의 완전한 재현성 검증은 별도의 문제입니다.
자동 업로드 템플릿도 같은 레시피 모델을 사용하도록 했습니다. 구간이 정해지기 전에는 구간을 비워 두고, 실제 클립이 선택되면 나머지 편집 설정과 함께 사용합니다. 수동 편집과 자동 생성이 전혀 다른 입력 형식을 요구하지 않게 하는 선택입니다. 자막도 영상에 직접 입히는 번인과 별도 자막인 CC 출력 모드를 구분해, 편집자의 선택을 렌더 계층이 받을 계약에 포함했습니다.
빠른 재포장 경로를 렌더까지 확장하지 않았다
되감기용 재포장은 압축된 미디어를 다시 담는 작업입니다. 반면 크롭, 상하 분할, 자막 번인은 영상의 픽셀을 바꿉니다. FFmpeg의 streamcopy 설명도 디코딩·필터링·인코딩 없이 압축 패킷을 전달하는 경로를 구분합니다. 픽셀을 바꾸는 필터 처리는 별도의 필터링 경로가 필요합니다.
저는 필터가 없는 키프레임 컷에만 smart cut을 적용하는 예외도 검토했습니다. 이 예외를 채택하면 어떤 레시피는 재포장하고 어떤 레시피는 재인코딩하므로, 입력 조건에 따라 두 실행 경로를 유지·검증해야 합니다. 당시에는 그 복잡성을 늘리기보다 최종 렌더의 기본 흐름을 재인코딩으로 통일했습니다. 되감기는 압축 데이터를 그대로 전달하면 되지만, 편집 결과는 사용자가 고른 화면과 소리를 만들어야 한다는 차이가 기준이었습니다.
이렇게 정리하자 미디어 계층이 넘길 원본과 시간 구간, 편집 화면이 남길 레시피, 렌더 계층이 만들 결과물을 구분할 수 있었습니다. 자막 번인처럼 픽셀을 바꾸는 선택도 레시피의 입력으로 전달합니다. 렌더 워커가 화면 상태를 추측하지 않고 명시된 입력을 읽도록 합의한 것입니다.
원본을 남기는 대가도 남는다
다시 편집할 수 있으려면 원본을 계속 보관하고 읽을 수 있어야 합니다. 재인코딩을 기본 경로로 정한 만큼 렌더 자원도 필요합니다. 이 비용을 받아들이는 대신, 음악 음량을 바꿀 때 원본을 덮어쓰거나 이전 편집 선택을 복원하는 일을 피하도록 설계했습니다.
여기까지 확보한 결과는 입력 계약입니다. 에디터·렌더·업로드 전체의 운영 배포와 렌더 시간·비용 절감은 별도 검증이 남아 있습니다. 다음 구현을 평가할 기준도 이 계약에서 나옵니다. 원본은 그대로 두고 레시피의 선택만 바꿨을 때, 원하는 새 결과물을 만들 수 있어야 합니다.
이 글은 PokeClip·MediaMTX 기술 기록 모음의 일부입니다. 포트폴리오 전체본 5쪽에서 연결된 사례도 함께 볼 수 있습니다.