PokeClip의 클립 생성은 선택한 시간대에 겹치는 원본 파일 몇 개를 받는 작업입니다. 이미 약 4초짜리 독립 객체와 시간 인덱스가 있는데, 큰 파일 일부를 읽는 저장 기법이나 별도 라이브 저장 서버를 추가할 이유가 있을까요. 기능 이름보다 현재 작업 단위에서 남는 이득을 확인해야 했습니다.
저는 KV·FUSE·라이브 저장 계층 도입을 보류하고, 기존 객체와 제한된 병렬 다운로드를 유지했습니다. 후속 S3 GET 대조에서는 1MiB 파일 8개의 완료 시간이 246.2→130.5ms로 줄었습니다. 반면 4MiB 파일 8개는 411→359ms에 그쳤습니다. 동시성을 늘리는 효과조차 입력 크기에 따라 달라졌습니다.
조사와 실험은 AI와 협업했습니다. 제 역할은 초기 조사의 잘못된 전제를 재검토하게 하고, 새 계층을 늘리기 전에 파일 가용 시점·조회 범위·배치 완료 시간을 나눠 판단한 것입니다. 아래 PUT과 GET은 각각 개발 환경의 별도 시험입니다.
기술의 해법보다 입력 단위를 먼저 비교했습니다
큰 파일의 부분 읽기 방식은 바이트 위치를 찾는 인덱스가 필요합니다. 우리는 이미 시간별 파일을 갖고 있으므로, 예를 들어 12초 구간에 겹치는 몇 개 객체를 찾으면 됩니다. 새 저장 계층을 넣으면 인덱스와 실패 복구도 하나 더 운영해야 합니다.
| 비교 대상 | 우리 요구에 대입한 질문 |
|---|---|
| S3 키 분산 | 이미 방송·시간으로 나뉜 키에 추가 분산이 필요한가 |
| 별도 라이브 저장 서버 | 원본 가용성·복구 책임을 더 맡을 이득이 있는가 |
| 큰 파일 부분 읽기·FUSE | 작은 독립 객체를 가진 현재 구조에도 맞는가 |
| 제한된 병렬 GET | 현 인덱스와 객체를 유지하며 기다림을 줄일 수 있는가 |
처음 조사에서는 “S3 쓰기는 비핵심”이라고 정리했지만, 쓰기가 끝나야 편집할 원본을 사용할 수 있다는 사실을 빠뜨렸습니다. DVR은 사용자마다 다른 객체를 탐색하고 렌더·AI 작업은 직접 GET한다는 차이도 충분히 반영하지 못했습니다. 저는 이 전제부터 다시 검토하도록 요구했습니다.
그 결과 질문도 바뀌었습니다. PUT이 느린지뿐 아니라 원본이 언제 가용해지는지, 필요한 파일을 정확히 골랐는지, 선택한 배치를 얼마나 기다리는지를 따로 봐야 했습니다. 조회 계약과 다운로드 동시성, pending age를 다시 검토했고, 이때 드러난 첫 조각 누락은 구간 겹침 조건의 정정으로 이어졌습니다.
PUT 수십 ms가 무엇을 설명하는지 제한했습니다
2026년 8월 30–31일의 개발 EC2 t3.small 원시 기록을 재집계했습니다. 두 크기의 객체를 각각 30회 순차 PUT했고 회차별 다른 키를 사용했습니다. 단일 curl 호출에서 연결을 재사용했으며, 첫 요청과 이후 29건을 나눴습니다.
| 객체 크기 | 첫 요청 | 이후 29회 p50 | 이후 29회 p95 |
|---|---|---|---|
| 1,206,272B | 71.62ms | 40.52ms | 49.02ms |
| 849,920B | 61.43ms | 36.07ms | 39.82ms |
총 60회 모두 HTTP 200이었습니다. 비교한 값은 curl time_total이며 p95는 29표본의 nearest-rank 값입니다. 중간 응답 시점 차이의 원인이 확인되지 않아 TTFB를 비교 지표로 쓰지 않았습니다. 개별 p95를 더해서 종단 p95라고 부르지도 않았습니다. PUT 결과와 해석은 이 입력·순차 조건에서 저장 구간이 수십 ms였다는 정도를 말합니다. 파일 마감, 훅, 재포장, DB 확정은 별도로 봐야 합니다.
객체 하나보다 배치 전체가 끝나는 시간이 필요했습니다
2026년 10월 6일에는 실제 S3 GET으로 8개 객체를 모두 수신하고 SHA256까지 확인하는 시간을 비교했습니다. 서울 리전의 Python 3.13 ARM64 Lambda, 512MB, 연결 풀 16, 최대 시도 1, 256KiB 읽기 조건입니다. 1·2·4·8 동시성을 조건별 7회씩 무작위 순서로 실행했습니다.
| 파일 8개 배치의 중앙값 | 순차 | 2병렬 | 4병렬 | 8병렬 |
|---|---|---|---|---|
| 1MiB × 8 | 246.2ms | 154.4ms | 136.1ms | 130.5ms |
| 4MiB × 8 | 411ms | 372ms | 391ms | 359ms |
순차에서 8병렬로 바꾸면 1MiB 배치는 약 47%, 4MiB 배치는 약 13% 짧아졌습니다. 특히 4MiB에서는 2병렬 372ms가 4병렬 391ms보다 짧았습니다. 이 결과로 8병렬을 모든 작업의 기본값으로 정할 수는 없었습니다. 입력이 커지면 요청을 더 많이 겹치는 것만으로 기다림이 같은 비율로 줄지 않았기 때문입니다. AWS의 S3 성능 지침에 나온 동시 연결도 실제 워커의 파일 크기와 자원 조건에서 상한을 정해야 합니다.
8병렬 한 배치로 예열했고, 예열을 포함한 Lambda 456객체의 크기·해시가 일치했습니다. 같은 코드의 로컬 Mac 1MiB 배치는 동시성 1·2·4·8에서 884.9 / 437.9 / 312.6 / 257.0ms였습니다. Mac은 예열 포함 232객체를 확인했고 SDK 재시도는 0이었습니다. 코드가 같아도 네트워크·CPU·Python·SDK 조건이 달라 Lambda와 합쳐 개선 배수를 만들지 않았습니다.
저장 계층 추가보다 먼저 남은 검증
PUT은 짧은 순차 버스트이고 GET은 임시 Python 다운로드 실험입니다. 실제 렌더 워커에 적용할 때는 파일 크기 분포·메모리·동시 작업 수를 다시 확인해야 합니다. 재사용 프로세스의 누적 RSS 최고값으로 동시성별 메모리 차이를 계산하지도 않았습니다. 같은 프로세스의 과거 최고값이 다음 조건에 남기 때문입니다.
배치 실험은 현재 객체 단위를 유지하면서 기다림을 줄일 여지가 있음을 보여 줬습니다. 그래서 새 인덱스와 복구 경로를 운영할 계층부터 추가하기보다, 구간 조회를 바로잡고 실제 워커의 다운로드 상한을 확인하는 순서를 택했습니다. 이 단계가 해결하지 못하는 병목이 확인되면 저장 계층 도입을 다시 비교할 수 있습니다. 도입을 보류한 이유와 다시 검토할 조건을 함께 남긴 결정입니다.
이 글은 PokeClip의 개발·검증 기록을 2026년 10월 15일 기준으로 정리한 CASE 19입니다. 전체 기술 기록에서 연결되는 결정을 함께 읽을 수 있습니다.