내보내기 타임아웃 방지: 비동기 작업, 진행 표시, 스트리밍
대용량 CSV 및 PDF 리포트의 내보내기 타임아웃을 방지하기 위한 비동기 작업, 진행 표시, 페이징 및 스트리밍 다운로드 방법.

간단한 말로: 왜 내보내기가 타임아웃되나
내보내기가 타임아웃되는 건 서버가 정해진 마감 시간 안에 작업을 끝내지 못하기 때문입니다. 그 마감 시간은 브라우저, 리버스 프록시, 앱 서버, 또는 데이터베이스 연결 중 어디에선가 정해질 수 있습니다. 사용자 입장에서는 때때로 성공하고 때때로 실패해서 랜덤처럼 느껴지기도 합니다.
화면상으로는 보통 다음과 같습니다:
- 끝나지 않는 스피너
- 다운로드가 시작되었다가 "network error"로 중단됨
- 긴 대기 후 에러 페이지
- 파일이 다운로드되지만 비어 있거나 손상됨
대용량 내보내기는 시스템의 여러 부분을 동시에 건드리기 때문에 부담이 큽니다. 데이터베이스는 많은 행을 찾고 조합해야 하고, 앱 서버는 그것을 CSV로 포맷하거나 PDF로 렌더링해야 합니다. 브라우저는 큰 응답을 연결 끊김 없이 받아야 합니다.
큰 데이터셋이 명백한 원인이지만, "작은" 내보내기도 무거워질 수 있습니다. 비싼 조인, 많은 계산 필드, 행별 조회, 인덱스가 부족한 필터가 일반적인 리포트를 타임아웃 위기로 바꿉니다. PDF는 레이아웃, 폰트, 이미지, 페이지 나눔과 관련된 추가 조회가 자주 있어 특히 위험합니다.
재시도는 상황을 악화시킵니다. 사용자가 새로 고침하거나 Export를 다시 클릭하면 동일한 작업이 두 번 시작될 수 있습니다. 데이터베이스는 중복 쿼리를, 앱 서버는 중복 파일 생성을 처리해야 하며 시스템이 이미 힘들 때 부하가 폭증합니다.
타임아웃을 막으려면 내보내기를 일반 페이지 로드처럼 다루지 말고 백그라운드 작업으로 생각하세요. AppMaster 같은 노코드 빌더에서도 패턴이 도구보다 중요합니다. 긴 작업은 "버튼 클릭→응답 대기" 흐름과 달라야 합니다.
앱에 맞는 내보내기 패턴 선택
대부분의 실패는 앱이 모든 상황에서 같은 패턴을 쓰기 때문에 발생합니다. 데이터 크기와 처리 시간이 크게 달라지는 경우에도 말이죠.
간단한 동기식 내보내기(사용자 클릭 → 서버 생성 → 다운로드 시작)는 내보내기가 작고 예측 가능한 경우에 괜찮습니다. 수백 행 정도, 기본 컬럼, 무거운 포맷이 없고 동시 사용자가 많지 않다면 몇 초 안에 끝나는 단순한 방식이 더 낫습니다.
시간이 오래 걸리거나 예측 불가능한 경우에는 비동기(async) 내보내기 작업을 사용하세요. 대용량 데이터, 복잡한 계산, PDF 렌더링 작업, 또는 한 느린 내보내기가 다른 요청을 막을 수 있는 공유 서버 환경에 적합합니다.
비동기 작업이 더 적합한 경우:
- 내보내기가 정기적으로 10~15초 이상 걸릴 때
- 사용자가 넓은 날짜 범위나 "전체 기간"을 요청할 때
- 차트, 이미지, 많은 페이지를 포함한 PDF를 생성할 때
- 여러 팀이 피크 시간에 내보내기를 수행할 때
- 실패 시 안전한 재시도가 필요할 때
스트리밍 다운로드는 내보내기를 순서대로 만들 수 있고 초기에 바이트 전송을 시작할 수 있을 때 도움이 됩니다. 전체 파일을 메모리에 먼저 만들 필요가 없어 빠르게 느껴지고 메모리 부담을 줄입니다. 긴 CSV에는 아주 적합하지만, 첫 줄을 쓰기 전에 모든 계산이 끝나야 한다면 별로 도움이 되지 않습니다.
접근 방식을 결합할 수 있습니다: 비동기 작업으로 내보내기를 생성(또는 스냅샷을 준비)하고, 준비되면 스트리밍으로 다운로드를 제공하는 식입니다. AppMaster에서는 "Export Requested" 레코드를 만들고 백엔드 비즈니스 프로세스에서 파일을 생성한 다음 사용자가 브라우저 요청을 열어두지 않고도 결과를 다운로드하도록 하는 실용적인 방법이 있습니다.
단계별: 비동기 내보내기 작업 구축
가장 큰 변화는 단순합니다: 사용자가 클릭한 동일한 요청 안에서 파일을 생성하지 마세요.
비동기 내보내기 작업은 작업을 두 부분으로 나눕니다: 작업을 생성하는 빠른 요청과 백그라운드에서 파일을 만드는 무거운 작업.
실용적인 5단계 흐름
- 내보내기 요청을 캡처(요청자, 필터, 선택한 컬럼, 출력 형식).
- 상태(queued, running, done, failed), 타임스탬프, 에러 필드를 가진 작업 레코드를 만듭니다.
- 큐, 스케줄러 워커, 전용 워커 프로세스를 사용해 백그라운드에서 무거운 일을 실행합니다.
- 결과를 스토리지(오브젝트 스토리지나 파일 스토어)에 쓰고, 작업 레코드에 다운로드 참조를 저장합니다.
- 앱 내 알림, 이메일 또는 팀에서 이미 사용하는 메시지 채널로 사용자에게 준비 완료를 알립니다.
작업 레코드를 진실의 원천으로 유지하세요. 사용자가 새로 고침하거나 기기를 바꾸거나 탭을 닫아도 동일한 작업 상태와 동일한 다운로드 버튼을 보여줄 수 있습니다.
예: 지원 매니저가 지난 분기 모든 티켓을 내보낸다고 합시다. 스피너가 돌아가는 탭을 기다리는 대신, 관리자 화면에 작업 항목이 queued에서 done으로 이동하고 다운로드가 나타납니다. AppMaster에서는 Data Designer에 작업 테이블을 모델링하고 Business Process Editor에서 백그라운드 로직을 구성하며 상태 필드로 UI 상태를 제어할 수 있습니다.
사용자가 신뢰하는 진행 표시
좋은 진행 표시기는 불안을 줄이고 사용자가 Export를 여러 번 클릭하는 일을 막습니다. 또한 앱이 실제 진행을 보여주면 사용자가 기다릴 의향이 생겨 간접적으로 타임아웃 발생을 줄여줍니다.
사람들이 이해하기 쉬운 방식으로 진행을 표시하세요. 퍼센트만 보여주면 오해를 낳을 수 있으니 구체적 항목과 함께 보여줍니다:
- 현재 단계(데이터 준비 중, 행 가져오는 중, 파일 생성 중, 업로드 중, 준비 완료)
- 처리된 행 수/전체 행 수(또는 처리된 페이지 수)
- 시작 시각과 마지막 업데이트 시각
- 남은 시간(합리적으로 안정적일 때만)
거짓 정밀도를 피하세요. 총 작업량을 모르면 73% 같은 숫자를 보여주지 마세요. 먼저 마일스톤을 보여주고 분모를 알게 되면 퍼센트로 전환하세요. 간단한 패턴으로는 설정 단계에 0%~10%, 행 처리 기반에 10%~90%, 파일 마무리에 90%~100%를 할당하는 방법이 있습니다. 페이지 수가 가변적인 PDF의 경우에는 "렌더링된 레코드"나 "완료된 섹션" 같은 더 작은 단위를 추적하세요.
너무 자주 업데이트하면 데이터베이스나 큐를 과도하게 두드리게 되므로, 살아있는 느낌은 주되 부담을 주지 않을 빈도로 업데이트하세요. 보통은 13초마다 또는 N행(예: 5001,000행)마다 진행을 기록하는 방식이 적당합니다. 또한 퍼센트가 움직이지 않을 때에도 UI가 "작업 중"이라고 보여줄 수 있도록 가벼운 하트비트 타임스탬프를 기록하세요.
작업이 예상보다 오래 걸릴 때 사용자가 제어할 수 있게 하세요. 실행 중인 내보내기를 취소하게 하고, 첫 번째 작업을 잃지 않고 새 작업을 시작하게 하며, 상태(Queued, Running, Failed, Ready)와 짧은 에러 메시지를 포함한 내보내기 기록을 볼 수 있게 하세요.
AppMaster에서는 일반적으로 ExportJob(status, processed_count, total_count, step, updated_at) 같은 레코드를 두고 UI가 이를 폴링하여 비동기 작업이 백그라운드에서 파일을 생성하는 동안 정직한 진행을 보여줍니다.
작업을 제한하는 페이징과 필터링
대부분의 타임아웃은 내보내기가 한 번에 모든 것을 처리하려 들기 때문에 발생합니다: 너무 많은 행, 열, 조인. 가장 빠른 해결책은 작업을 제한해서 사용자가 더 작고 명확한 데이터 조각을 내보내게 하는 것입니다.
사용자의 목표에서 시작하세요. 누군가 "지난달 실패한 송장"이 필요하면 기본값을 "전체 기간의 모든 송장"으로 두지 마세요. 필터는 귀찮은 일이 아니라 자연스러운 기능처럼 느껴지게 하세요. 간단한 날짜 범위와 상태 필터만으로도 데이터셋을 90% 이상 줄이는 경우가 많습니다.
좋은 내보내기 요청 폼은 보통 날짜 범위(기본값: 최근 7일 또는 30일 같은 합리적 값), 한두 개의 핵심 상태, 선택적 검색 또는 고객/팀 선택, 가능하면 카운트 미리보기(예상치라도)를 포함합니다.
서버측에서는 페이징으로 데이터를 청크 단위로 읽으세요. 이렇게 하면 메모리가 안정적으로 유지되고 진행 상황의 자연스러운 체크포인트를 얻습니다. 페이징할 때는 안정적인 정렬(예: created_at, 그다음 id)을 항상 사용하세요. 그렇지 않으면 새 행이 앞페이지로 들어가 누락되거나 중복될 수 있습니다.
긴 내보내기 동안 데이터가 변경되므로 "일관성"이 무엇인지 결정하세요. 간단한 방법은 작업 시작 시 스냅샷 시간을 기록하고 그 타임스탬프까지의 행만 내보내는 것입니다. 엄격한 일관성이 필요하면 데이터베이스가 지원하는 일관된 읽기나 트랜잭션을 사용하세요.
AppMaster 같은 노코드 도구에서는 이것을 비즈니스 프로세스로 깔끔하게 매핑할 수 있습니다: 필터를 검증하고 스냅샷 시간을 설정한 뒤 페이지 단위로 루프를 돌며 더 이상 가져올 것이 없을 때까지 처리합니다.
서버를 과부하시키지 않는 스트리밍 다운로드
스트리밍은 파일을 생성하면서 동시에 사용자에게 전송을 시작하는 것입니다. 서버가 전체 CSV나 PDF를 메모리에 먼저 만들 필요가 없습니다. 큰 파일에서 타임아웃을 막는 가장 신뢰할 수 있는 방법 중 하나입니다.
스트리밍이 느린 쿼리를 빠르게 만들어주지는 않습니다. DB 작업이 첫 바이트 전까지 5분 걸리면 요청은 여전히 타임아웃될 수 있습니다. 보통 해결책은 스트리밍과 페이징을 결합해 청크를 가져오고 쓰는 방식입니다.
메모리를 낮게 유지하려면 생성하면서 바로 쓰세요. 하나의 청크(예: 1,000 CSV 행 또는 한 PDF 페이지)를 생성하고 응답에 쓰고 플러시한 뒤 다음을 진행합니다. "나중에 정렬하려고 행을 큰 배열에 모아두기"를 피하세요. 안정적 순서가 필요하면 데이터베이스에서 정렬하세요.
헤더, 파일명, 콘텐츠 타입
브라우저와 모바일 앱이 다운로드를 올바르게 처리하도록 명확한 헤더를 사용하세요. 올바른 콘텐츠 타입(text/csv 또는 application/pdf 등)과 안전한 파일명을 설정하세요. 파일명은 특수문자를 피하고 짧게 유지하며, 동일 리포트를 반복해서 내보낼 수 있으니 타임스탬프를 포함하세요.
재개와 부분 다운로드
초기에 재개(resume)를 지원할지 결정하세요. 기본 스트리밍은 생성된 PDF에 대해 바이트 범위 재개를 지원하지 않는 경우가 많습니다. 재개를 지원하려면 Range 요청을 처리하고 동일한 작업에 대해 일관된 출력을 생성해야 합니다.
출시 전 체크리스트:
- 본문 쓰기 전에 헤더를 전송하고, 청크 단위로 작성 후 플러시
- 청크 크기를 일정하게 유지해 부하 시에도 메모리 평탄 유지
- 결정론적 정렬을 사용해 출력물이 신뢰 가능하도록 함
- 재개 지원 여부와 연결 끊김 시 동작을 문서화
- 서버 측 제한(최대 행, 최대 시간)을 두고 한도 초과 시 친절한 오류 반환
AppMaster에서 내보내기를 빌드하면 생성 로직은 백엔드 흐름에 두고 브라우저가 아닌 서버 측에서 스트리밍하세요.
대용량 CSV 내보내기: 실용적 전술
큰 CSV를 하나의 블롭으로 다루지 마세요. 루프 형태로 만드세요: 데이터 한 조각을 읽고, 행을 쓰고, 반복하세요. 이렇게 하면 메모리가 일정하게 유지되고 재시도가 더 안전해집니다.
CSV는 행 단위로 쓰세요. 비동기 작업에서 생성하더라도 "모든 행을 모아서 문자열화" 방식은 피하세요. 쓰기용 라이터를 열어두고 각 행이 준비되는 즉시 추가하세요. 스택이 지원하면 DB 커서나 페이징을 사용해 수백만 레코드를 한꺼번에 로드하지 마세요.
CSV 정확성은 속도만큼 중요합니다. 파일은 열어보면 괜찮아 보이다가 Excel에서 열었을 때 열이 밀릴 수 있습니다.
깨지지 않는 CSV를 위한 규칙
- 쉼표, 인용부호, 개행은 항상 이스케이프(필드를 전체 따옴표로 감싸고 내부 따옴표는 두 개로)하세요
- UTF-8로 출력하고 비영어 이름까지 엔드투엔드로 테스트하세요
- 안정적인 헤더 행과 고정된 열 순서를 유지하세요
- 날짜와 소수점 형식을 표준화하세요(하나의 포맷을 선택하고 고수)
- 값이 =, +, -, @로 시작하면 수식이 되지 않도록 피하세요
성능 저하는 보통 쓰기가 아니라 데이터 접근에서 발생합니다. 각 행 루프 안에서 고객을 로드하는 N+1 조회를 조심하세요. 관련 데이터를 한 번에 가져오거나 미리 로드한 뒤 행을 쓰세요.
내보내기가 정말 거대해지면 일부러 파일을 분할하세요. 예: 5년치 주문을 월별 파일 60개로 나누면 한 달의 처리 지연이 전체를 막지 않습니다.
AppMaster를 사용하면 Data Designer에 데이터셋을 모델링하고 비즈니스 프로세스를 백그라운드에서 실행해 페이징하며 각 페이지를 처리하는 방식으로 구현할 수 있습니다.
대용량 PDF 내보내기: 예측 가능하게 유지하기
PDF 생성은 CSV보다 보통 느립니다. 레이아웃, 폰트, 이미지, 페이지 처리 때문에 CPU를 많이 씁니다. PDF는 빠른 응답이 아니라 백그라운드 작업과 명확한 한계로 다루세요.
템플릿 선택이 2분 걸리는 내보내기를 20분으로 만들 수 있습니다. 단순한 레이아웃이 유리합니다: 컬럼 수를 줄이고 중첩 테이블을 피하며 예측 가능한 페이지 분할을 사용하세요. 이미지는 특히 느리게 만듭니다—해상도가 높거나 원격에서 렌더링 중 가져오면 속도가 급격히 떨어집니다.
속도와 신뢰성을 높이는 템플릿 결정:
- 폰트는 1~2개로 제한하고 폰트 폴백 체인은 피함
- 헤더와 푸터는 단순하게(페이지마다 동적 차트는 피함)
- 큰 래스터 이미지보다 벡터 아이콘 선호
- 텍스트를 여러 번 재측정하는 "auto fit" 레이아웃은 피함
- 복잡한 투명도와 그림자 사용 자제
대량 내보내기는 배치 렌더링을 하세요. 작은 섹션이나 페이지 범위 단위로 생성하여 임시 파일에 쓰고, 마지막에 합치는 방식은 메모리를 안정적으로 유지하고 작업자가 중간에 죽어도 재시도가 안전합니다. 또한 비동기 작업과 의미 있는 진행 단계(예: "데이터 준비", "페이지 1-50 렌더링", "파일 마무리")와 잘 어울립니다.
또한 PDF가 정말로 사용자가 원하는 것인지 재검토하세요. 대부분 행·열 분석이 목적이면 CSV를 함께 제공하는 것이 더 낫습니다. 요약용 작은 PDF와 전체 데이터 CSV를 병행 제공하는 전략도 좋습니다.
AppMaster에서는 PDF 생성을 백그라운드 작업으로 실행하고 진행을 보고한 뒤 작업 완료 시 완성 파일을 다운로드하게 하는 흐름이 자연스럽게 맞습니다.
타임아웃을 유발하는 흔한 실수들
내보내기 실패는 보통 불가사의하지 않습니다. 몇 가지 선택은 200행에서는 괜찮다가 200,000행에서는 무너집니다.
가장 흔한 실수들:
- 내보내기 전체를 하나의 웹 요청 안에서 실행
- 시간 기반으로 진행을 표시(작업량이 아닌 시간으로 진행을 재는 것)
- 모든 행을 메모리에 한꺼번에 읽고 쓰기
- 장시간 DB 트랜잭션을 유지하거나 락을 무시
- 무제한 내보내기 허용 및 정리 정책 부재
구체적 예: 지원팀 리드가 지난 2년치 모든 티켓을 내보내려 하고 아무 반응이 없어 두 번 클릭했습니다. 이제 동일한 작업이 두 번 시작되어 데이터베이스와 앱 서버가 경쟁하고 결국 둘 다 타임아웃됩니다.
AppMaster 같은 노코드 도구로 구성해도 같은 규칙이 적용됩니다: 내보내기를 요청 경로밖으로 빼고, 진행을 행 단위로 추적하고, 출력은 생성하면서 쓰며, 동시에 실행할 수 있는 내보내기 수에 단순한 제한을 두세요.
출시 전 빠른 점검
내보내기 기능을 프로덕션에 배포하기 전에 타이머 마인드셋으로 빠르게 점검하세요. 긴 작업은 요청 경로 밖에서 실행되고, 사용자는 정직한 진행을 보고, 서버는 모든 것을 한 번에 처리하려 들지 않습니다.
사전 점검 체크리스트:
- 대용량 내보내기는 백그라운드 작업으로 실행(작은 것은 일관되게 빠르면 동기식 허용)
- 사용자는 queued, running, done, failed 같은 명확한 상태와 타임스탬프를 봄
- 데이터는 안정적 정렬로 청크 단위로 읽음(예: 생성 시간 + ID)
- 완성된 파일은 사용자가 탭을 닫아도 나중에 다운로드 가능
- 오래된 파일과 작업 기록에 대한 제한 및 정리 정책(나이 기반 삭제, 사용자별 최대 작업 수, 저장소 한도)
최악의 시나리오를 직접 시도해보는 것이 좋은 체크입니다: 허용하는 최대 날짜 범위를 다른 사용자가 동시에 레코드를 추가하는 상황에서 내보내기 해보고 중복, 누락, 또는 진행 정체가 있는지 확인하세요. 문제가 있다면 정렬이나 청크 전략을 개선해야 합니다.
AppMaster에서 이 점검 항목들은 Business Process Editor의 백그라운드 프로세스, 데이터베이스의 내보내기 작업 레코드, 그리고 UI가 읽어 폴링하는 상태 필드로 깔끔히 매핑됩니다.
실패를 안전하게 만드세요. 실패한 작업은 에러 메시지를 보관하고 재시도 가능해야 하며, 불완전한 파일이 "완료됨"처럼 보이지 않도록 하세요.
예시: 대량 데이터를 내보내도 앱을 멈추지 않게 하기
운영 매니저는 매달 두 가지 내보내기가 필요합니다: 분석용으로 지난 2년치 주문 CSV와 회계용으로 월별 송장 PDF 묶음. 이들 작업을 일반 웹 요청에서 처리하면 결국 시간 제한에 걸립니다.
먼저 작업을 제한하세요. 내보내기 화면은 날짜 범위(기본: 최근 30일), 선택적 필터(상태, 지역, 영업 담당자), 명확한 컬럼 선택을 묻습니다. 이 한 가지 변경만으로도 2년치 수백만 행 문제가 관리 가능한 문제로 바뀌는 경우가 많습니다.
사용자가 Export를 클릭하면 앱은 Export Job 레코드(type, filters, requested_by, status, progress, error_text)를 만들고 큐에 넣습니다. AppMaster에서는 Data Designer 모델과 백그라운드에서 실행되는 Business Process가 흔한 구현입니다.
작업이 실행되는 동안 UI는 사용자가 신뢰할 수 있는 상태를 보여줍니다: queued, processing(예: 3/20 청크), generating file, ready(다운로드 버튼), failed(짧은 에러와 재시도 옵션).
청크 처리(Chunking)가 핵심입니다. CSV 작업은 예를 들어 한 번에 50,000행씩 페이지를 읽고 각 페이지를 출력에 쓰며 청크마다 진행을 업데이트합니다. PDF 작업은 월별로 묶어 한 달치씩 처리하면 한 달이 느려도 전체가 막히지 않습니다.
문제가 발생하면(잘못된 필터, 권한 누락, 저장 오류) 작업은 Failed로 표시되고 사용자가 조치할 수 있는 짧은 메시지를 보여줍니다: "March invoices를 생성하지 못했습니다. 재시도하거나 Job ID 8F21과 함께 지원팀에 문의하세요." 재시도는 동일한 필터를 재사용해 사용자가 처음부터 다시 설정할 필요가 없게 합니다.
다음 단계: 내보내기를 일상 기능으로 만들기
장기적으로 타임아웃을 막는 가장 빠른 방법은 내보내기를 일회성 버튼이 아니라 일관된 패턴의 표준 기능으로 만드는 것입니다.
기본 접근법을 하나 정하고 모든 곳에서 사용하세요: 비동기 작업이 백그라운드에서 파일을 생성하고 준비되면 사용자가 다운로드합니다. 이 하나의 결정으로 대부분의 "테스트에서는 됐는데 운영에서는 안 됨" 문제를 제거할 수 있습니다. 사용자의 요청이 전체 파일을 기다릴 필요가 없어지기 때문입니다.
이미 생성한 파일을 사람들이 쉽게 찾게 하세요. 사용자별, 작업공간별 또는 계정별 내보내기 기록 페이지는 반복 내보내기를 줄이고 지원팀이 "내 파일 어디 있나요?"에 답하기 쉽게 하며 상태, 에러, 만료를 보여줄 자연스러운 장소가 됩니다.
AppMaster로 이 패턴을 구성하면 플랫폼이 실제 소스 코드를 생성하고 백엔드 로직, 데이터베이스 모델링, 웹/모바일 UI를 한 곳에서 지원하므로 신뢰할 수 있는 비동기 내보내기 작업을 빠르게 구축하는 데 유리합니다. (참고: appmaster.io)
그다음 실제로 무엇이 문제인지 측정하세요. 느린 DB 쿼리, CSV 생성 시간, PDF 렌더링 시간을 추적하면 어떤 리포트나 필터 조합이 문제인지를 빠르게 알 수 있습니다. 완벽한 관찰성은 필요 없고, 내보내기별 지속 시간과 행 수를 로깅하는 것만으로도 충분히 유용한 인사이트를 얻을 수 있습니다.
내보내기도 다른 제품 기능처럼 다루세요: 일관되고 측정 가능하며 지원하기 쉬운 기능으로 만드세요.
자주 묻는 질문
내보내기가 타임아웃되는 것은 요청 경로 어딘가에 설정된 마감 시간 전에 작업이 끝나지 않았기 때문입니다. 이 제한은 브라우저, 리버스 프록시, 앱 서버 또는 데이터베이스 연결에서 올 수 있어, 근본 원인은 같아도 결과는 때때로 무작위처럼 보입니다.
간단한 동기식(클릭 → 서버 생성 → 다운로드)은 데이터가 작고 예측 가능하며 몇 초 내에 항상 끝나는 경우에만 괜찮습니다. 내보내기가 보통 10~15초 이상 걸리거나, 광범위한 기간, 무거운 계산, PDF 생성이 포함된다면 브라우저 요청을 열어두지 않도록 비동기 작업(async job)을 사용하세요.
먼저 작업(job) 레코드를 만들고, 무거운 처리는 백그라운드에서 수행한 뒤 완성된 파일을 사용자가 다운로드하게 합니다. AppMaster에서는 Data Designer에 ExportJob 모델을 만들고, 백엔드 Business Process가 status, 진행 필드와 저장된 파일 참조를 업데이트하는 방식이 흔히 사용됩니다.
시간이 아니라 실제 작업량을 기록하세요. 실무적인 방법은 step, processed_count, total_count(알 수 있을 때), updated_at 같은 필드를 저장하고 UI가 이를 폴링하여 사용자가 멈췄다고 느끼지 않게 하는 것입니다.
내보내기 요청을 멱등하게(idempotent) 만들고 작업 레코드를 진실의 원천으로 삼으세요. 사용자가 다시 클릭하면 동일한 필터의 이미 실행 중인 작업을 보여주거나 중복 생성을 차단하면 같은 비싼 작업이 두 번 시작되지 않습니다.
메모리를 일정하게 유지하려면 청크 단위로 읽고 쓰세요. 안정적인 정렬(예: created_at 이후 id)을 사용한 페이징은 긴 내보내기 동안 행을 놓치거나 중복하는 일을 막아 줍니다.
작업이 시작될 때 스냅샷 시간을 기록하고 그 타임스탬프까지의 행만 내보내면 실행 중 데이터 변경으로 출력물이 움직이는 것을 방지할 수 있습니다. 더 엄격한 일관성이 필요하면 DB가 지원하는 일관된 읽기(consisent read)나 트랜잭션 전략을 고려하세요. 하지만 대부분의 경우 명확한 스냅샷 규칙으로 충분합니다.
스트리밍은 결과를 순서대로 만들어 곧바로 바이트를 전송할 수 있을 때 효과적입니다(특히 큰 CSV에서). 하지만 첫 바이트 전까지 수 분이 소요되는 느린 쿼리를 해결해주지는 못하고, 아무 것도 쓰지 않고 오래 기다리면 여전히 타임아웃이 발생할 수 있으니 페이징과 함께 사용하세요.
행을 생성하면서 바로 쓰고 엄격한 CSV 이스케이프 규칙을 지키세요. 인코딩은 보통 UTF-8로 하고 헤더, 열 순서를 고정하며, 루프 안에서 발생하는 N+1 조회를 피하기 위해 관련 데이터를 미리 가져오세요. 이런 점들이 느리거나 깨진 CSV의 주요 원인입니다.
PDF는 레이아웃, 폰트, 이미지, 페이지 나눔 때문에 CPU 부하가 크므로 백그라운드 작업으로 처리하고 명확한 한계를 두세요. 템플릿을 단순화하고 큰 원격 이미지를 렌더링 중에 불러오지 않도록 하며, 진행 상황을 의미 있는 단계로 보고하면 신뢰도가 올라갑니다.


