TIL - 20260625

juni·2026년 6월 25일

TIL

목록 보기
387/468

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담당자별 조회
formatxlsx, 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어떤 데이터 내보내기인지
statusPENDING, PROCESSING, COMPLETED, FAILED
requestedById요청한 관리자 ID
query검색 조건
fileKeyS3에 저장된 파일 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,240admin다운로드
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;
  }

  // 파일에 rows 기록

  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. 엑셀 다운로드 설계 체크리스트

  1. 목록 조회와 같은 검색 조건을 사용하는가?
  2. 다운로드 권한을 백엔드에서 확인하는가?
  3. 데이터가 많을 때 Queue 방식으로 분리할 수 있는가?
  4. 필요한 컬럼만 조회하는가?
  5. 개인정보 마스킹 기준이 있는가?
  6. 다운로드 이력을 남기는가?
  7. 실패 시 관리자에게 상태를 보여주는가?
  8. 파일 보관 기간과 삭제 정책이 있는가?

➕ 19-2. 성능 체크리스트

  1. 전체 데이터를 한 번에 메모리에 올리지 않는가?
  2. 필요한 필드만 select하는가?
  3. 검색 조건 컬럼에 인덱스가 있는가?
  4. 날짜 범위 조건이 적용되는가?
  5. 대량 데이터는 chunk 단위로 처리하는가?
  6. CSV와 XLSX 중 적절한 형식을 선택했는가?
  7. Export 작업이 일반 API 응답에 영향을 주지 않는가?

➕ 19-3. 보안 체크리스트

  1. Export 파일이 public S3에 올라가지 않는가?
  2. Presigned URL 만료 시간이 적절한가?
  3. 개인정보 컬럼 접근 권한이 구분되어 있는가?
  4. 누가 다운로드했는지 기록되는가?
  5. 검색 조건과 rowCount가 로그에 남는가?
  6. 오래된 Export 파일이 자동 삭제되는가?
  7. 엑셀 파일명에 개인정보가 들어가지 않는가?

✅ 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 답변 검증 기준

  1. 즉시 다운로드와 비동기 Export 방식을 구분하는가?
  2. 권한 체크를 프론트가 아니라 백엔드에서 하라고 하는가?
  3. 대량 데이터 조회 시 필요한 필드만 select하라고 하는가?
  4. chunk 또는 streaming 처리를 고려하는가?
  5. 개인정보 마스킹과 다운로드 이력을 설명하는가?
  6. S3 public 업로드가 아니라 private + Presigned URL을 권장하는가?
  7. 파일 보관 기간과 Lifecycle 정책을 고려하는가?
  8. Export 실패 상태와 재시도 흐름을 설계하는가?

📌 요약

  • 데이터 내보내기는 DB 데이터를 엑셀, CSV 같은 파일로 변환해 운영자가 활용할 수 있게 제공하는 기능입니다.
  • 데이터가 적을 때는 요청 즉시 파일을 생성해도 되지만, 데이터가 많아지면 timeout, 메모리 증가, DB 부하 문제가 발생할 수 있습니다.
  • 대용량 Export는 Queue, Worker, S3, Presigned URL 구조로 분리하는 것이 안정적입니다.
  • 목록 조회와 엑셀 다운로드는 같은 검색 조건을 공유해야 화면 데이터와 다운로드 데이터가 일치합니다.
  • Export에는 개인정보가 포함될 수 있으므로 백엔드 권한 체크, 마스킹, 다운로드 이력 로그가 중요합니다.
  • 대량 조회 시에는 필요한 필드만 select하고, Chunk 또는 Streaming 방식으로 메모리 사용량을 줄여야 합니다.
  • 생성된 파일은 S3 private 경로에 저장하고, 다운로드 시 Presigned URL을 발급하는 방식이 안전합니다.
  • 오래된 Export 파일은 Lifecycle 정책으로 자동 삭제해 보안 위험과 비용을 줄이는 것이 좋습니다.

0개의 댓글