대용량 사이트 내보내기를 위한 LUXCM의 작동방식

대용량 사이트를 위한 분할 내보내기 — 업로드 용량 제한과 파트 나누기의 원리

나는 분할 내보내기를 부가 기능이 아니라 핵심 기능으로 다룬다.

사이트가 조금만 커져도 이 기능 없이는 가져오기 화면까지 파일을 올리는 것 자체가 불가능해지는 경우를 실제로 봤기 때문이다.

이 글에서는 왜 이런 일이 생기는지, 그리고 내가 이걸 어떤 방식으로 풀었는지를 코드에 있는 실제 검증 로직과 함께 설명한다.

업로드 한도는 변덕이 아니라 공유 자원을 지키기 위한 장치다

나는 처음에 “요즘 호스팅이면 큰 파일도 그냥 올라가지 않나”라고 생각하는 사람들을 자주 만났다.

Bluehost의 셰어드 호스팅 업로드 한도 안내는 이 생각이 왜 틀렸는지 정확히 설명한다. 실제 한도는 “호스팅사와 요금제에 따라 다르지만, 보통 몇 MB에서 128MB 사이”라는 것이다.

그리고 이 한도가 존재하는 이유는 임의로 정해진 게 아니다 — 여러 사이트가 하나의 서버 자원을 나눠 쓰는 셰어드 호스팅 환경에서, “제한 없는 업로드 하나가 그 자원 풀을 충분히 붙잡아서 같은 서버의 다른 모든 사이트를 느려지게 하거나 죽게 만들 수 있다”는 것이다.

CPU, 메모리, 디스크 I/O를 보호하기 위해 upload_max_filesize, post_max_size, memory_limit을 보수적으로 잡아두는 건 워드프레스의 제약이 아니라 “공유 환경 자체에 내장된 자원 보호 장치”라는 게 이 글의 결론이다.

나는 이 설명을 읽으면서, 사용자에게 “호스팅사에 요청해서 한도를 올려달라”고 안내하는 게 근본적인 해법이 될 수 없다고 다시 확인했다.

기본값 19MB, 조정 범위 1~500MB로 정한 이유

내보내기 화면에서 조각 하나의 최대 크기는 기본 19MB이고, “고급” 옵션을 켜면 1MB에서 500MB 사이에서 자유롭게 조정할 수 있다.

나는 이 기본값을 임의로 정하지 않았다. Bluehost의 글이 짚은 “보통 몇 MB에서 128MB 사이”라는 실제 범위를 생각하면, 19MB는 대부분의 호스팅 환경에서 별도의 설정 변경 없이도 안전하게 통과할 수 있는 값이다.
반대로 서버 업로드 한도를 이미 높여둔 사용자나, 오히려 한도가 아주 낮은 극단적인 환경을 쓰는 사용자를 위해 1MB부터 500MB까지 폭넓게 조정할 수 있게 열어뒀다.

이건 “모두에게 맞는 하나의 정답”이 없다는 걸 인정하고, 사용자가 자기 호스팅 환경에 맞춰 직접 맞출 수 있게 한 것이다.

파일명 앞에 순번을 붙인 이유

나는 조각 파일의 이름을 NEWS-LUX-20260825-074327.wpmpx.part1of2처럼 끝에 순번을 붙이는 방식에서, part1of2-NEWS-LUX-20260825-074327.wpmpx처럼 앞에 붙이는 방식으로 바꿨다.

이건 사소해 보이지만 실제 사용성 문제에서 나온 결정이다. 여러 조각을 폴더 안에서 나열했을 때, 유일하게 다른 부분인 순번이 파일명 맨 끝의 이미 길고 서로 거의 똑같은 문자열 뒤에 묻혀 있으면, 파일 탐색기나 업로드 창에서 한눈에 구분하기 어렵다.

순번을 맨 앞으로 옮기면 여러 조각을 나열했을 때 첫 글자만 봐도 순서를 바로 알 수 있다. 다만 나는 예전 버전이 만든 “끝에 붙는” 형식의 파일도 계속 인식하도록 두 가지 패턴을 모두 파싱하게 만들었다 — 이미 그 형식으로 만들어둔 조각 파일을 가진 사용자가 있을 수 있기 때문이다.

재조립은 “일단 합치고 보는” 방식이 아니다

가져오기 화면에서 조각들을 한�웘번에 선택해서 올리면, 나는 이걸 바로 합치지 않는다. 먼저 세 가지를 순서대로 검증한다.

첫째, 조각 파일이 아닌 파일이 섞여 있는지 확인한다. 파일명이 partN of M- 패턴에 맞지 않으면 “조각 파일이 아닌 파일이 함께 섞여 있습니다”라는 오류를 즉시 낸다.

둘째, 서로 다른 내보내기 작업에서 나온 조각이 섞였는지 확인한다. 모든 조각은 같은 기본 파일명과 같은 전체 조각 수를 가져야 하는데, 이게 다르면 “서로 다른 내보내기 묶음의 조각이 섞여 있습니다”라고 알린다. 이건 사용자가 실수로 어제 내보낸 파일과 오늘 다시 내보낸 파일의 조각을 뒤섞어 올리는 상황을 막기 위한 것이다.

셋째, 같은 순번의 조각이 중복으로 올라왔는지 확인한다. 예를 들어 part2of5가 두 번 올라오고 part3of5가 아예 없는 경우, “같은 순번의 조각이 중복 업로드됐습니다”라고 알린다.

이 세 검증을 모두 통과해도, 마지막으로 실제로 모인 조각의 개수가 전체 개수와 일치하는지 확인한다. 부족하면 “조각이 부족합니다(올린 개수: N, 필요한 개수: M). 내보내기 화면에서 만들어진 조각 파일을 전부 함께 올려주세요”라고 정확한 숫자와 함께 알려준다.

나는 이 네 가지 검증 중 어느 하나라도 실패하면 작업을 조용히 이어가지 않는다 — 부분적으로 잘못된 파일을 가지고 가져오기를 시도하는 것보다, 처음부터 정확히 무엇이 문제인지 알려주고 멈추는 게 맞다고 판단했기 때문이다.

진짜 “재개 가능한 업로드”는 아니라는 것도 정직하게 말해야 한다

나는 이 분할 방식을 Filestack의 대용량 업로드 처리 가이드가 설명하는 “청크 업로드”와 비교해서 정확히 구분하고 싶다.

이 가이드는 파일을 하나의 요청으로 통째로 올릴 때 생기는 문제들을 짚는다 — “서버의 실행 시간 제한이, 느리거나 불안정한 네트워크 환경에서 업로드 완료를 방해한다”는 타임아웃 문제, 연결이 끊기면 “처음부터 다시 시작해야 하는” 재시도 비용 문제, 그리고 “대용량 파일을 메모리 기반으로 처리하는 셰어드 서버가 가용 자원을 모두 써버려 결국 서버가 죽는” 문제까지. 이 가이드가 제시하는 해법인 tus나 Resumable.js 같은 진짜 청크 업로드 프로토콜은, 하나의 파일을 바이트 단위로 쪼개서 전송하다가 끊기면 끊긴 지점부터 정확히 다시 이어서 보낼 수 있다.

내가 만든 분할은 이것과는 다르다.

.wpmpx의 각 조각은 그 자체로 독립적으로 완결된 하나의 파일이고, 사용자는 이 조각들을 하나씩 완전한 HTTP 업로드 요청으로 올린다.

조각 하나의 크기가 작으니 서버의 실행 시간 제한에 걸릴 위험이 줄어들고, 조각 하나가 업로드 중 실패하더라도 전체 수백 MB를 처음부터 다시 올릴 필요 없이 그 조각 하나만 다시 올리면 된다.

대신 정확히 무엇을 하는지 — 파일 자체를 여러 개의 완결된 조각으로 나누고, 그 조각들을 엄격하게 검증한 뒤, 메모리 부담 없이 순서대로 다시 이어붙이는 것 — 을 그대로 말하는 쪽을 택했다.

결론을 정리하면

분할 내보내기는 호스팅마다 제각각인, 그러나 나름의 이유가 있는 업로드 한도라는 현실 위에서 설계됐다.

기본값과 조정 범위는 실제 호스팅 환경의 분포를 고려해서 정했고, 파일명의 순번 위치는 실제 사용성 문제에서 나온 결정이었다.

그리고 가장 중요한 부분은 재조립 단계다 — 섞인 파일, 서로 다른 묶음, 중복된 순번, 부족한 개수를 각각 따로 검증해서 정확히 무엇이 잘못됐는지 알려주고, 검증을 통과한 조각들은 메모리 부담 없이 스트리밍 방식으로 이어붙인다.

나는 이게 진짜 의미의 재개형 업로드는 아니라는 것도 숨기지 않는다. 다만 이 정도의 설계만으로도, 대용량 사이트를 옮기려다 업로드 한도 앞에서 막막해지는 상황은 확실히 없앨 수 있다고 생각한다.

참고 자료