Nest.js를 배워보자/5. NestJS + MySQL 실전 예제 — Excel Import

엑셀 데이터 읽기: 특정 시트 및 특정 행부터 파싱

_Blue_Sky_ 2025. 12. 2. 17:45
728x90


NestJS에서 xlsx 라이브러리를 사용하여 엑셀 파일의 데이터를 읽을 때, 원하는 특정 시트에서 특정 행부터 데이터를 추출하는 것이 실무에서 매우 중요합니다. 이는 엑셀 파일의 상단에 제목, 설명, 헤더 정보 등 불필요한 행들이 포함되어 있기 때문입니다.

1. 📖 특정 시트(Worksheet) 선택

엑셀 파일 내에는 여러 시트가 있을 수 있습니다. XLSX.read로 워크북 객체를 생성한 후, 원하는 시트의 이름을 키(Key)로 사용하여 해당 시트 객체를 가져옵니다.

// buffer: 파일 업로드로 받은 엑셀 파일의 데이터 Buffer
const workbook = XLSX.read(buffer, { type: 'buffer' });

// 💡 1. 원하는 시트 이름 지정
const sheetName = '직원_목록'; 
const worksheet = workbook.Sheets[sheetName];

if (!worksheet) {
  throw new Error(`워크시트 "${sheetName}"을(를) 찾을 수 없습니다.`);
}
728x90

2. ✂️ 특정 행부터 데이터 읽기 (Skip Rows)

xlsx 라이브러리의 XLSX.utils.sheet_to_json 함수를 사용할 때, range 옵션을 사용하여 데이터 파싱을 시작할 행을 지정할 수 있습니다.

2.1. range 옵션 이해

range 옵션은 파싱을 시작할 0-based 인덱스 또는 A1 표기법을 사용합니다.

  • 0-based 인덱스: 엑셀의 첫 번째 행(Header)은 인덱스 0입니다.
  • 시작 행 건너뛰기: 데이터가 엑셀의 3번째 행부터 시작한다면, 처음 두 행(인덱스 0, 1)을 건너뛰어야 합니다. 따라서 range: 2로 설정합니다.

2.2. 코드 적용 예시

만약 엑셀 파일의 구조가 다음과 같고, 실제 데이터는 3번째 행부터 시작한다면:

엑셀 행 (1-based) 내용 역할 인덱스 (0-based)
1 회사명 및 파일 정보 건너뛸 행 (헤더 이전) 0
2 컬럼 헤더 (Name, Email) 건너뛸 행 (헤더) 1
3 Jane Doe, jane@example.com 실제 데이터 시작 2
 
// 💡 sheet_to_json 옵션 설정
const data = XLSX.utils.sheet_to_json(worksheet, {
  // 1. 헤더 옵션: 데이터 배열로 반환 (첫 번째 행을 헤더 키로 사용하지 않음)
  header: 1, 
  
  // 2. range 옵션: 엑셀의 3번째 행(인덱스 2)부터 읽기 시작
  //    (인덱스 0, 1은 건너뜀)
  range: 2, 
});
728x90

2.3. 결과 데이터 구조

header: 1 옵션과 range: 2 옵션을 사용하면, 반환되는 data 배열의 구조는 다음과 같습니다.

  • data[0] = 엑셀의 3번째 행 데이터 (예: ['Jane Doe', 'jane@example.com', ...])
  • data[1] = 엑셀의 4번째 행 데이터

3. ⌨️ 통합된 데이터 파싱 로직

최종적으로 서비스 계층에 통합된 코드는 다음과 같습니다.

import * as XLSX from 'xlsx';

// ... (UploadService 내부)

async processExcel(buffer: Buffer) {
  const workbook = XLSX.read(buffer, { type: 'buffer' });
  const sheetName = '직원_목록';
  const worksheet = workbook.Sheets[sheetName];

  if (!worksheet) {
    throw new Error(`워크시트 "${sheetName}"을(를) 찾을 수 없습니다.`);
  }

  // 💡 엑셀의 3번째 행(인덱스 2)부터 모든 데이터를 배열로 읽어옵니다.
  const rawData = XLSX.utils.sheet_to_json(worksheet, {
    header: 1, // 배열 형태로 반환
    range: 2,  // 3번째 행부터 시작
  });

  // 이제 rawData를 DB 모델에 맞게 변환하고 저장 로직을 수행합니다.
  const usersToCreate = rawData.map((row: any[]) => ({
    name: row[0],  // 엑셀 3행, A열 (인덱스 0)
    email: row[1], // 엑셀 3행, B열 (인덱스 1)
    // ...
  }));

  // return this.prisma.user.createMany({ data: usersToCreate });
}
728x90