0625 백엔드 실무 심화 (5/N): 대용량 엑셀 다운로드와 데이터 내보내기 설계
✅ 1. 데이터 내보내기란 무엇인가?
- 데이터 내보내기(Export)는 DB에 저장된 데이터를 엑셀, CSV, JSON 같은 파일 형태로 변환해서 관리자나 운영자가 사용할 수 있게 제공하는 기능입니다.
- 실무에서는 관리자 페이지에서 상담 신청 목록, 주문 목록, 사전예약 신청자, 광고 유입 데이터, 정산 데이터 등을 엑셀로 다운로드하는 기능이 자주 필요합니다.
관리자 페이지
↓
검색 조건 설정
↓
엑셀 다운로드 클릭
↓
백엔드에서 DB 조회
↓
엑셀 파일 생성
↓
관리자가 파일 다운로드
➕ 1-1. 엑셀 다운로드가 중요한 이유
- 운영팀이 상담, 주문, 정산, 통계 업무에 활용합니다.
- 광고 성과 분석에 필요합니다.
- 외부 업체나 통신사에 전달할 데이터가 필요할 수 있습니다.
- 관리자 페이지에서 보기 어려운 많은 데이터를 한번에 정리할 수 있습니다.
- 회사 내부 보고 자료로 활용할 수 있습니다.
✅ 2. 단순 엑셀 다운로드와 대용량 엑셀 다운로드
- 데이터가 적을 때는 요청 즉시 엑셀을 생성해서 바로 내려줘도 됩니다.
- 하지만 데이터가 많아지면 이 방식은 서버 부하, timeout, 메모리 부족 문제를 만들 수 있습니다.
➕ 2-1. 단순 다운로드
관리자 요청
↓
DB에서 500건 조회
↓
엑셀 파일 생성
↓
바로 다운로드 응답
- 데이터가 적은 경우에는 구현이 간단합니다.
- 소규모 목록, 짧은 기간 데이터, 테스트용 기능에 적합합니다.
➕ 2-2. 대용량 다운로드
관리자 요청
↓
DB에서 10만 건 조회
↓
엑셀 파일 생성
↓
응답 지연
↓
서버 메모리 증가
↓
timeout 가능
- 데이터가 많을수록 단순 다운로드 방식은 위험합니다.
- 백엔드가 파일 생성에 묶이면 다른 API 응답에도 영향을 줄 수 있습니다.
✅ 3. 엑셀 다운로드에서 자주 발생하는 문제
➕ 3-1. 서버 timeout
- 엑셀 생성이 오래 걸리면 브라우저나 Nginx, API 서버에서 timeout이 발생할 수 있습니다.
관리자가 최근 1년치 상담 신청 엑셀 다운로드
↓
DB 조회와 파일 생성에 60초 이상 소요
↓
Nginx timeout 또는 브라우저 timeout
↓
다운로드 실패
➕ 3-2. 메모리 사용량 증가
- 전체 데이터를 한 번에 메모리에 올린 뒤 엑셀을 만들면 서버 메모리를 많이 사용합니다.
- 데이터가 많으면 Node.js 프로세스가 느려지거나 죽을 수 있습니다.
100,000건 데이터 조회
↓
배열에 전부 저장
↓
엑셀 워크북 생성
↓
메모리 급증
➕ 3-3. DB 부하 증가
- 대량 조회는 DB에 큰 부담을 줍니다.
- 인덱스 없이 날짜 범위, 상태값, 검색어 조건을 걸면 DB가 느려질 수 있습니다.
엑셀 다운로드 요청
↓
조건 없는 전체 조회
↓
DB CPU 상승
↓
일반 목록 API까지 느려짐
➕ 3-4. 개인정보 노출 위험
- 엑셀 파일에는 이름, 전화번호, 주소, 상담 메모 같은 민감정보가 들어갈 수 있습니다.
- 다운로드 권한, 파일 보관 위치, 만료 시간, 로그 기록을 신중하게 설계해야 합니다.
엑셀 파일을 public S3에 업로드
↓
URL만 알면 누구나 접근 가능
↓
개인정보 유출 사고 가능
✅ 4. 엑셀 다운로드 API 설계 기준
- 엑셀 다운로드 기능은 단순히 “파일 하나 내려주기”가 아닙니다.
- 검색 조건, 권한, 데이터 범위, 파일 생성 방식, 개인정보 처리, 이력 관리까지 함께 설계해야 합니다.
➕ 4-1. 기본 요청 예시
GET /api/admin/consults/export?status=PENDING&source=NAVER&startDate=2026-06-01&endDate=2026-06-25
➕ 4-2. 고려해야 할 요청 조건
| 항목 | 설명 |
|---|
keyword | 이름, 전화번호, 상품명 검색 |
status | 신청 상태 |
source | 유입경로 |
startDate | 조회 시작일 |
endDate | 조회 종료일 |
productId | 상품별 조회 |
managerId | 담당자별 조회 |
format | xlsx, csv |
fields | 내보낼 컬럼 선택 |
- 목록 조회 API와 엑셀 다운로드 API는 같은 검색 조건을 공유하는 것이 좋습니다.
- 그래야 화면에서 보는 데이터와 엑셀로 내려받는 데이터가 일치합니다.
✅ 5. 권한 체크
- 엑셀 다운로드는 일반 목록 조회보다 더 강한 권한 체크가 필요합니다.
- 개인정보가 포함될 가능성이 높기 때문입니다.
➕ 5-1. 권한 기준 예시
SUPER_ADMIN:
전체 다운로드 가능
MANAGER:
본인 담당 데이터만 다운로드 가능
VIEWER:
목록 조회만 가능, 다운로드 불가
➕ 5-2. 백엔드 권한 체크가 중요한 이유
- 프론트엔드에서 버튼을 숨기는 것만으로는 보안이 아닙니다.
- 사용자가 직접 API를 호출할 수 있기 때문에 백엔드에서 반드시 권한을 확인해야 합니다.
프론트엔드:
다운로드 버튼 숨김
하지만:
API URL을 알면 직접 요청 가능
해결:
백엔드 Guard/권한 검사 필수
✅ 6. 즉시 다운로드 방식
- 데이터가 많지 않다면 요청 즉시 엑셀을 생성해서 응답으로 내려줄 수 있습니다.
- 초기 서비스에서는 이 방식으로 시작할 수 있습니다.
➕ 6-1. 흐름
1. 관리자 권한 확인
2. 검색 조건 검증
3. DB 조회
4. 엑셀 파일 생성
5. 응답 헤더 설정
6. 파일 다운로드
➕ 6-2. 응답 헤더 예시
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename="consults_20260625.xlsx"
Content-Disposition을 설정하면 브라우저가 파일 다운로드로 처리합니다.
- 파일명에는 날짜나 조건을 넣으면 관리자가 구분하기 쉽습니다.
✅ 7. NestJS 즉시 다운로드 예시
@Get('consults/export')
async exportConsults(
@Query() query: SearchConsultDto,
@Res() res: Response,
) {
const buffer = await this.consultExportService.exportToExcel(query);
const filename = `consults_${dayjs().format('YYYYMMDD_HHmmss')}.xlsx`;
res.setHeader(
'Content-Type',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
);
res.setHeader(
'Content-Disposition',
`attachment; filename="${filename}"`,
);
return res.send(buffer);
}
- 작은 데이터에는 이 방식이 단순합니다.
- 하지만 데이터가 커질수록
buffer를 메모리에 들고 있는 것이 부담이 될 수 있습니다.
✅ 8. 대용량 다운로드는 Queue로 분리하기
- 데이터가 많다면 즉시 다운로드보다 비동기 생성 방식이 안전합니다.
- 관리자가 다운로드를 요청하면 ExportJob을 만들고, Worker가 파일을 생성한 뒤 S3에 업로드합니다.
➕ 8-1. 비동기 Export 흐름
관리자
↓ 엑셀 생성 요청
API 서버
↓ ExportJob 생성
Queue
↓ Worker가 작업 처리
DB 조회
↓ 엑셀 파일 생성
S3 업로드
↓ ExportJob 상태 완료
관리자
↓ 완료된 파일 다운로드
➕ 8-2. 장점
- API timeout을 피할 수 있습니다.
- 대용량 작업을 Worker로 분리할 수 있습니다.
- 생성 상태를 관리자에게 보여줄 수 있습니다.
- 실패한 작업을 재시도할 수 있습니다.
- 생성 이력을 남길 수 있습니다.
✅ 9. ExportJob 테이블 설계
- 대용량 엑셀 다운로드를 비동기로 처리하려면 작업 상태를 저장하는 테이블이 필요합니다.
➕ 9-1. Prisma 모델 예시
model ExportJob {
id Int @id @default(autoincrement())
type String
status String
requestedById Int
query Json
fileKey String?
fileName String?
errorMessage String?
rowCount Int?
createdAt DateTime @default(now())
startedAt DateTime?
completedAt DateTime?
expiresAt DateTime?
}
➕ 9-2. 주요 필드 설명
| 필드 | 설명 |
|---|
type | 어떤 데이터 내보내기인지 |
status | PENDING, PROCESSING, COMPLETED, FAILED |
requestedById | 요청한 관리자 ID |
query | 검색 조건 |
fileKey | S3에 저장된 파일 key |
fileName | 다운로드 파일명 |
errorMessage | 실패 원인 |
rowCount | 내보낸 데이터 수 |
expiresAt | 다운로드 만료일 |
query에는 당시 사용한 검색 조건을 저장합니다.
- 나중에 어떤 조건으로 파일을 만들었는지 추적할 수 있습니다.
✅ 10. Export 상태값 설계
PENDING:
작업 생성됨, 아직 처리 전
PROCESSING:
Worker가 처리 중
COMPLETED:
파일 생성 및 업로드 완료
FAILED:
생성 실패
EXPIRED:
다운로드 가능 기간 만료
- 상태값을 명확히 관리하면 관리자 페이지에서 작업 진행 상태를 보여주기 쉽습니다.
➕ 10-1. 관리자 화면 예시
| 요청일 | 종류 | 상태 | 건수 | 요청자 | 다운로드 |
|---|
| 2026-06-25 10:30 | 상담 신청 | 처리 중 | - | admin | - |
| 2026-06-25 10:10 | 주문 목록 | 완료 | 1,240 | admin | 다운로드 |
| 2026-06-24 18:20 | 사전예약 | 실패 | - | admin | 재시도 |
✅ 11. CSV와 XLSX 선택 기준
- 데이터를 내보낼 때 반드시 엑셀 xlsx만 써야 하는 것은 아닙니다.
- 데이터량이 많으면 CSV가 더 가볍고 빠를 수 있습니다.
➕ 11-1. XLSX
| 장점 | 단점 |
|---|
| 서식 적용 가능 | 파일 생성이 무거움 |
| 여러 시트 가능 | 메모리 사용량 증가 |
| 운영자가 보기 편함 | 대용량에 불리할 수 있음 |
➕ 11-2. CSV
| 장점 | 단점 |
|---|
| 가볍고 빠름 | 서식 없음 |
| 대용량에 유리 | 한글 인코딩 주의 |
| 처리 구조 단순 | 여러 시트 불가 |
➕ 11-3. 실무 기준
운영자가 보기 편한 소량 데이터:
XLSX
10만 건 이상 대량 데이터:
CSV 검토
서식/시트가 필요한 보고서:
XLSX
외부 시스템 연동:
CSV
✅ 12. 대량 조회 최적화
- 엑셀 다운로드는 많은 row를 조회하기 때문에 DB 조회 최적화가 중요합니다.
- 일반 목록 API보다 더 많은 데이터를 가져오므로, 잘못 만들면 DB에 큰 부하를 줍니다.
➕ 12-1. 피해야 할 방식
const consults = await this.prisma.consult.findMany({
where,
include: {
histories: true,
user: true,
product: true,
},
});
- 모든 관계 데이터를 한 번에 include하면 데이터가 커질 수 있습니다.
- 필요 없는 필드까지 가져오면 메모리와 네트워크 비용이 증가합니다.
➕ 12-2. 필요한 필드만 select
const consults = await this.prisma.consult.findMany({
where,
select: {
id: true,
name: true,
phone: true,
productName: true,
status: true,
source: true,
createdAt: true,
},
orderBy: {
createdAt: 'desc',
},
});
- 엑셀에 필요한 컬럼만 조회해야 합니다.
- 개인정보 컬럼은 권한에 따라 제외하거나 마스킹할 수 있습니다.
✅ 13. Chunk 단위 처리
- 대량 데이터를 한 번에 조회하지 않고 일정 개수씩 나눠서 처리하는 방식입니다.
- 메모리 사용량을 줄이는 데 도움이 됩니다.
➕ 13-1. Chunk 처리 흐름
1. 1,000건 조회
2. 엑셀/CSV에 기록
3. 다음 1,000건 조회
4. 반복
5. 파일 생성 완료
➕ 13-2. Cursor 기반 Chunk 예시
let cursor: number | undefined = undefined;
const limit = 1000;
while (true) {
const rows = await this.prisma.consult.findMany({
where,
take: limit,
...(cursor && {
cursor: {
id: cursor,
},
skip: 1,
}),
orderBy: {
id: 'asc',
},
select: {
id: true,
name: true,
phone: true,
status: true,
createdAt: true,
},
});
if (rows.length === 0) {
break;
}
cursor = rows[rows.length - 1].id;
}
- 대량 Export에서는 Offset보다 Cursor 방식이 안정적인 경우가 많습니다.
- 단, 정렬 기준이 명확해야 합니다.
✅ 14. Streaming 방식
- 대용량 파일은 메모리에 한 번에 만들기보다 스트림으로 생성하는 것이 좋습니다.
- CSV는 Streaming 방식으로 처리하기 비교적 쉽습니다.
➕ 14-1. Streaming이 필요한 이유
Buffer로 전체 파일 생성:
데이터가 클수록 메모리 사용량 증가
Streaming:
데이터를 조금씩 쓰면서 파일 생성
메모리 부담 감소
➕ 14-2. 실무 기준
- 수천 건 정도는 일반 Buffer 생성도 가능할 수 있습니다.
- 수만~수십만 건 이상이면 Streaming 또는 비동기 Export를 검토하는 것이 좋습니다.
- xlsx는 라이브러리 선택에 따라 streaming 지원 여부가 다릅니다.
- CSV는 대용량에 더 유리합니다.
✅ 15. 개인정보 마스킹
- 엑셀 다운로드에는 개인정보가 포함될 수 있습니다.
- 권한에 따라 마스킹하거나 일부 컬럼을 제외해야 합니다.
➕ 15-1. 마스킹 예시
이름:
홍길동 → 홍*동
전화번호:
01012345678 → 010****5678
이메일:
test@example.com → te**@example.com
➕ 15-2. 권한별 다운로드 예시
| 권한 | 이름 | 전화번호 | 상담 메모 |
|---|
| SUPER_ADMIN | 전체 표시 | 전체 표시 | 전체 표시 |
| MANAGER | 전체 표시 | 일부 마스킹 | 담당 건만 표시 |
| VIEWER | 마스킹 | 마스킹 | 다운로드 불가 |
- 개인정보가 있는 파일은 다운로드 이력을 반드시 남기는 것이 좋습니다.
- 누가 언제 어떤 조건으로 다운로드했는지 추적할 수 있어야 합니다.
✅ 16. 다운로드 이력 로그
- 엑셀 다운로드는 보안상 이력을 남겨야 합니다.
- 특히 개인정보가 포함된 경우에는 추적 가능성이 중요합니다.
➕ 16-1. ExportLog 예시
model ExportLog {
id Int @id @default(autoincrement())
adminId Int
type String
query Json
rowCount Int?
fileKey String?
ipAddress String?
userAgent String?
createdAt DateTime @default(now())
}
➕ 16-2. 남겨야 할 정보
- 요청 관리자 ID
- 다운로드 종류
- 검색 조건
- 다운로드 건수
- 요청 IP
- User-Agent
- 파일 key
- 요청 시간
- 성공/실패 여부
문제 상황:
개인정보가 포함된 엑셀 파일 외부 유출 의심
↓
확인할 것:
누가 언제 어떤 조건으로 다운로드했는지
✅ 17. S3 업로드와 Presigned URL
- 비동기 Export 방식에서는 생성된 파일을 S3에 저장하고, 다운로드할 때 Presigned URL을 발급하는 구조가 좋습니다.
- 파일을 public으로 열면 안 됩니다.
➕ 17-1. 흐름
Worker가 엑셀 파일 생성
↓
S3 private 경로에 업로드
↓
ExportJob에 fileKey 저장
↓
관리자가 다운로드 클릭
↓
백엔드 권한 확인
↓
Presigned URL 발급
↓
일정 시간 동안만 다운로드 가능
➕ 17-2. S3 key 예시
private/exports/consults/2026/06/25/export_73591.xlsx
private/exports/orders/2026/06/25/export_73591.csv
private/exports 경로는 public으로 열면 안 됩니다.
- Lifecycle 정책으로 오래된 파일을 자동 삭제하는 것이 좋습니다.
✅ 18. Export 파일 보관 기간
- 엑셀 파일은 영구 보관할 필요가 없는 경우가 많습니다.
- 개인정보가 들어간 파일은 오래 보관할수록 리스크가 커집니다.
➕ 18-1. 보관 기준 예시
일반 상담 목록 Export:
7일 후 삭제
정산용 Export:
30일 보관
법적/회계 자료:
회사 정책에 따라 별도 보관
임시 다운로드 파일:
1일 후 삭제
➕ 18-2. Lifecycle 정책
private/exports/
- 7일 후 삭제
private/reports/monthly/
- 90일 후 Glacier 이동
- 1년 후 삭제
- 파일 보관 기간은 보안, 비용, 업무 필요성을 함께 고려해야 합니다.
- 민감한 파일일수록 짧게 보관하는 것이 안전합니다.
✅ 19. 실무 체크리스트
➕ 19-1. 엑셀 다운로드 설계 체크리스트
- 목록 조회와 같은 검색 조건을 사용하는가?
- 다운로드 권한을 백엔드에서 확인하는가?
- 데이터가 많을 때 Queue 방식으로 분리할 수 있는가?
- 필요한 컬럼만 조회하는가?
- 개인정보 마스킹 기준이 있는가?
- 다운로드 이력을 남기는가?
- 실패 시 관리자에게 상태를 보여주는가?
- 파일 보관 기간과 삭제 정책이 있는가?
➕ 19-2. 성능 체크리스트
- 전체 데이터를 한 번에 메모리에 올리지 않는가?
- 필요한 필드만 select하는가?
- 검색 조건 컬럼에 인덱스가 있는가?
- 날짜 범위 조건이 적용되는가?
- 대량 데이터는 chunk 단위로 처리하는가?
- CSV와 XLSX 중 적절한 형식을 선택했는가?
- Export 작업이 일반 API 응답에 영향을 주지 않는가?
➕ 19-3. 보안 체크리스트
- Export 파일이 public S3에 올라가지 않는가?
- Presigned URL 만료 시간이 적절한가?
- 개인정보 컬럼 접근 권한이 구분되어 있는가?
- 누가 다운로드했는지 기록되는가?
- 검색 조건과 rowCount가 로그에 남는가?
- 오래된 Export 파일이 자동 삭제되는가?
- 엑셀 파일명에 개인정보가 들어가지 않는가?
✅ 20. AI를 활용해 엑셀 다운로드를 설계할 때 질문법
- 엑셀 다운로드 기능은 데이터량, 권한, 개인정보, 성능, 비동기 처리 여부를 함께 알려줘야 정확한 설계를 받을 수 있습니다.
➕ 20-1. 좋은 질문 예시
NestJS + Prisma + PostgreSQL 관리자 페이지에서 상담 신청 엑셀 다운로드 기능을 만들고 싶어.
상황:
1. 상담 신청 데이터는 1년 기준 10만 건 이상 예상
2. 관리자는 검색 조건으로 status, source, startDate, endDate, keyword를 사용함
3. 목록 조회와 같은 조건으로 엑셀을 내려받아야 함
4. 이름, 전화번호, 상담 메모 같은 개인정보가 포함될 수 있음
5. SUPER_ADMIN은 전체 다운로드 가능
6. MANAGER는 본인 담당 데이터만 다운로드 가능
7. 파일이 커질 수 있어서 Queue + S3 + Presigned URL 방식을 고려 중
8. 오래된 파일은 7일 후 삭제하고 싶음
요청:
- API 구조
- ExportJob 테이블 설계
- Worker 처리 흐름
- Prisma 조회 최적화
- 개인정보 마스킹 기준
- S3 key 설계
- 다운로드 이력 로그
- 실패/재시도 처리
를 실무 기준으로 설명해줘.
➕ 20-2. AI 답변 검증 기준
- 즉시 다운로드와 비동기 Export 방식을 구분하는가?
- 권한 체크를 프론트가 아니라 백엔드에서 하라고 하는가?
- 대량 데이터 조회 시 필요한 필드만 select하라고 하는가?
- chunk 또는 streaming 처리를 고려하는가?
- 개인정보 마스킹과 다운로드 이력을 설명하는가?
- S3 public 업로드가 아니라 private + Presigned URL을 권장하는가?
- 파일 보관 기간과 Lifecycle 정책을 고려하는가?
- Export 실패 상태와 재시도 흐름을 설계하는가?
📌 요약
- 데이터 내보내기는 DB 데이터를 엑셀, CSV 같은 파일로 변환해 운영자가 활용할 수 있게 제공하는 기능입니다.
- 데이터가 적을 때는 요청 즉시 파일을 생성해도 되지만, 데이터가 많아지면 timeout, 메모리 증가, DB 부하 문제가 발생할 수 있습니다.
- 대용량 Export는 Queue, Worker, S3, Presigned URL 구조로 분리하는 것이 안정적입니다.
- 목록 조회와 엑셀 다운로드는 같은 검색 조건을 공유해야 화면 데이터와 다운로드 데이터가 일치합니다.
- Export에는 개인정보가 포함될 수 있으므로 백엔드 권한 체크, 마스킹, 다운로드 이력 로그가 중요합니다.
- 대량 조회 시에는 필요한 필드만
select하고, Chunk 또는 Streaming 방식으로 메모리 사용량을 줄여야 합니다.
- 생성된 파일은 S3 private 경로에 저장하고, 다운로드 시 Presigned URL을 발급하는 방식이 안전합니다.
- 오래된 Export 파일은 Lifecycle 정책으로 자동 삭제해 보안 위험과 비용을 줄이는 것이 좋습니다.