Google Sheets로 CSV 데이터 가져오기를 자동화하는 Cloud 함수

1. 소개

이 Codelab의 목표는 Cloud 함수를 작성하여 Cloud Storage에 대한 CSV 파일 업로드에 반응하고, 그 콘텐츠를 읽고, Sheets API를 사용하여 Google 시트를 업데이트하는 방법을 이해하는 것입니다.

e9c78061022a6760.png

이는 수동 'CSV로 가져오기'의 자동화로 볼 수 있습니다. 단계를 거칩니다. 이렇게 하면 다른 팀에서 만든 데이터도 사용할 수 있게 되는 즉시 스프레드시트에서 분석할 수 있습니다.

구현은 다음과 같습니다.

52df703605ae4bd3.png

2. 설정 및 요구사항

자습형 환경 설정

  1. Cloud Console에 로그인하고 새 프로젝트를 만들거나 기존 프로젝트를 다시 사용합니다. (Gmail 또는 G Suite 계정이 없으면 만들어야 합니다.)

dMbN6g9RawQj_VXCSYpdYncY-DbaRzr2GbnwoV7jFf1u3avxJtmGPmKpMYgiaMH-qu80a_NJ9p2IIXFppYk8x3wyymZXavjglNLJJhuXieCem56H30hwXtd8PvXGpXJO9gEUDu3cZw

ci9Oe6PgnbNuSYlMyvbXF1JdQyiHoEgnhl4PlV_MFagm2ppzhueRkqX4eLjJllZco_2zCp0V0bpTupUSKji9KkQyWqj11pqit1K1faS1V6aFxLGQdkuzGp4rsQTan7F01iePL5DtqQ

8-tA_Lheyo8SscAVKrGii2coplQp2_D1Iosb2ViABY0UUO1A8cimXUu6Wf1R9zJIRExL5OB2j946aIiFtyKTzxDcNnuznmR45vZ2HMoK3o67jxuoUJCAnqvEX6NgPGFjCVNgASc-lg

모든 Google Cloud 프로젝트에서 고유한 이름인 프로젝트 ID를 기억하세요(위의 이름은 이미 사용되었으므로 사용할 수 없습니다). 이 ID는 나중에 이 Codelab에서 PROJECT_ID라고 부릅니다.

  1. 그런 후 Google Cloud 리소스를 사용할 수 있도록 Cloud Console에서 결제를 사용 설정해야 합니다.

이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 가이드를 마친 후 비용이 결제되지 않도록 리소스 종료 방법을 알려주는 '삭제' 섹션의 안내를 따르세요. Google Cloud 새 사용자에게는 미화 $300 상당의 무료 체험판 프로그램에 참여할 수 있는 자격이 부여됩니다.

3. Google 시트를 만들고 구성하고 API를 사용 설정합니다.

먼저 새 Sheets 문서를 만들어 보겠습니다. 이 시트는 모든 사용자의 소유가 될 수 있습니다. 생성 후 식별자를 기억합니다. 이는 작성할 함수의 환경 변수로 사용됩니다.

dd77d5fc1364ad3e.png

GCP 콘솔에서 'API 및 서비스'로 이동하여 새로 만든 프로젝트에 Google Sheets API를 사용 설정합니다. 'API 라이브러리'를 선택합니다. 섹션 :

c64e2e98b8b55f16.png

'IAM 및 관리' 섹션에서 '서비스 계정'으로 이동합니다. App Engine 기본 서비스 계정의 이메일을 기록해 둡니다. your-project-id@appspot.gserviceaccount.com 형식이어야 합니다. 물론 이 작업 전용 서비스 계정을 직접 만들 수도 있습니다.

6e279d7e07d4febf.png

마지막으로, '공유' 옵션을 사용하여 이 서비스 계정에 스프레드시트에 대한 수정 권한을 버튼 :

c334062465ddf928.png

이 설정으로 이제 Cloud 함수를 작성하고 이 서비스 계정을 사용하도록 구성할 수 있습니다. 방금 만든 이 스프레드시트 문서에 쓸 수 있습니다.

4. 스토리지 버킷 만들기

Cloud 함수가 새 CSV 파일을 모니터링할 버킷을 만들어 보겠습니다.

콘솔에서 왼쪽 메뉴를 사용하여 'Storage'로 이동합니다. :

2ddcb54423979d25.png

그런 다음 csv2sheet-POSTFIX라는 새 버킷을 만듭니다 (POSTFIX를 고유한 값으로 대체). 다른 모든 설정을 기본값으로 설정합니다.

dd637080ade62e81.png

5. Cloud 함수 만들기

이제 특정 Cloud Storage 버킷에 파일이 업로드될 때 트리거되는 csv2sheet라는 Cloud 함수를 만들 수 있습니다. 코드는 Cloud 콘솔에서 바로 인라인 편집기를 사용하여 비동기 함수와 함께 Node.js 8로 작성됩니다.

6ee1a5ce63174ae8.png

트리거를 'Cloud Storage'로 설정해야 합니다. 버킷 이름을 이전 단계에서 만든 이름으로 조정하면 됩니다

또한 csv2sheet에 쓰려는 함수의 진입점을 업데이트합니다.

446e7c7c992c2d8a.png

이제 함수 본문을 다음과 같이 변경합니다.

  1. Cloud Storage 및 Sheets API 사용
  2. csv2sheet 함수를 async로 표시
  3. Cloud Storage 이벤트 메타데이터에서 fileName를 가져와서 새로 만들 새 시트의 이름을 가져옵니다.
const {google} = require("googleapis");
const {Storage} = require("@google-cloud/storage")

exports.csv2sheet = async (data, context) => {
  var fileName = data.name;
  // basic check that this is a *.csv file, etc...
  if (!fileName.endsWith(".csv")) {
    console.log("Not a .csv file, ignoring.");
    return;
  }
  // define name of new sheet  
  const sheetName = fileName.slice(0, -4);

  // TODO!
};

잠시 후에 설명하겠지만 여기서 async를 사용해야 await를 사용해야 합니다.

이 함수를 만들 때 몇 가지 중요한 옵션은 다음과 같습니다 (화면 하단의 '더보기' 링크 클릭).

  • 드롭다운을 사용하여 위에서 설명한 서비스 계정을 선택합니다.
  • 이전에 만든 시트 문서와 일치해야 하는 SPREADSHEET_ID라는 환경 변수를 정의합니다.

fd22d1873bcb8c66.png

최종 설정 단계로, 사용할 두 종속 항목으로 Cloud Storage 및 Google Sheet API가 포함된 package.json 콘텐츠는 다음과 같습니다 (콘솔의 인라인 편집기 PACKAGE.JSON 탭 사용).

{
    "name": "csv2sheet",
    "version": "0.0.42",
    "dependencies": {
        "googleapis": "^51.0.0",
        "@google-cloud/storage": "^5.0.1"
    }
}

설명된 대로 모든 항목을 구성한 후 '만들기'를 클릭합니다. ! 잠시 후 함수가 생성되고 배포됩니다.

6. 인증 및 Sheets API 설정

인라인 편집기를 사용하여 Cloud 함수에서 추가 코드를 작성하기 전에 적절한 스토리지 및 시트 범위로 Google Client API를 만들지 못하도록 차단해야 합니다 (이는 async 함수의 일부임).

콘솔의 함수 편집기에서 '수정'을 클릭합니다. csv2sheet 함수의 본문에 다음 코드를 추가합니다.

// block on auth + getting the sheets API object
const auth = await google.auth.getClient({
  scopes: [
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/devstorage.read_only"
  ]
});

여기에서 Sheets API 클라이언트를 만들 수 있습니다.

const sheetsAPI = google.sheets({version: 'v4', auth});

7. Sheets API를 사용하여 빈 시트 만들기

Sheets API 클라이언트를 사용하여 문서에 간단한 새 시트를 만들 수 있지만, 계속 진행하기 전에 관련 용어를 간단히 살펴보겠습니다.

  • 스프레드시트는 실제 문서이며 식별자로 참조됩니다 (위에서 설명했으며 문서 URL에서 볼 수 있음).
  • 시트는 문서에 있는 탭 중 하나이며 시트 이름 (탭 이름) 또는 시트 생성 시 생성된 식별자로 참조할 수 있습니다.

이를 염두에 두고 Sheets API 클라이언트를 사용하여 위치 2 (일반적으로 기본 'Sheet1' 뒤에)에 빈 시트를 만드는 함수입니다. 26개의 열, 2,000개의 행이 있고 첫 번째 행이 고정됩니다 (인라인 편집기를 사용하여 함수에 추가).

function addEmptySheet(sheetsAPI, sheetName) {
  return new Promise((resolve, reject) => {
    const emptySheetParams = {
      spreadsheetId: process.env.SPREADSHEET_ID,
      resource: {
        requests: [
          {
            addSheet: {
              properties: {
                title: sheetName,
                index: 1,
                gridProperties: {
                  rowCount: 2000,
                  columnCount: 26,
                  frozenRowCount: 1
                }
              }
            }
          }
        ]
      }
    };
    sheetsAPI.spreadsheets.batchUpdate( emptySheetParams, function(err, response) {
        if (err) {
          reject("The Sheets API returned an error: " + err);
        } else {
          const sheetId = response.data.replies[0].addSheet.properties.sheetId;
          console.log("Created empty sheet: " + sheetId);
          resolve(sheetId);
        }
      }
    );
  });
}

스프레드시트 참조를 하드 코딩하는 대신 이전에 만든 SPREADSHEET_ID 환경 변수를 사용합니다.

이 특정 시트에 대한 추가 요청을 위해 sheetId를 기억해야 합니다. 또한 시트 이름은 고유해야 하며 sheetName라는 시트가 이미 있는 경우 만들 수 없습니다.

Sheets API의 batchUpdate 함수는 문서와 상호작용하는 일반적인 방법이며 여기에 설명되어 있습니다.

8. 스토리지 CSV 파일에서 데이터 읽기

이제 데이터를 덤프할 위치가 있으므로 인라인 편집기에서 Cloud 함수를 추가로 개발하고 Cloud Storage API를 사용하여 방금 업로드된 파일에서 실제 데이터를 가져와 문자열로 저장해 보겠습니다.

function readCSVContent(sheetsAPI, file, sheetName) {
  return new Promise((resolve, reject) => {
    const storage = new Storage();
    let fileContents = new Buffer('');
    storage.bucket(file.bucket).file(file.name).createReadStream()
    .on('error', function(err) {
      reject('The Storage API returned an error: ' + err);
    })
    .on('data', function(chunk) {
      fileContents = Buffer.concat([fileContents, chunk]);
    })  
    .on('end', function() {
      let content = fileContents.toString('utf8');
      console.log("CSV content read as string : " + content );
      resolve(content);
    });
  });
}

9. 새로 만든 시트 채우기

이제 동일한 Sheet 클라이언트 API를 사용하여 만든 시트와 방금 수집한 데이터를 채울 차례입니다. 이번 기회를 통해 시트 열에 스타일을 추가 (맨 위 행의 글꼴 크기를 변경하고 굵게 표시)해 보겠습니다.

function populateAndStyle(sheetsAPI, theData, sheetId) {
  return new Promise((resolve, reject) => {
    // Using 'batchUpdate' allows for multiple 'requests' to be sent in a single batch.
    // Populate the sheet referenced by its ID with the data received (a CSV string)
    // Style: set first row font size to 11 and to Bold. Exercise left for the reader: resize columns
    const dataAndStyle = {
      spreadsheetId: process.env.SPREADSHEET_ID,
      resource: {
        requests: [
          {
            pasteData: {
              coordinate: {
                sheetId: sheetId,
                rowIndex: 0,
                columnIndex: 0
              },
              data: theData,
              delimiter: ","
            }
          },
          {
            repeatCell: {
              range: {
                sheetId: sheetId,
                startRowIndex: 0,
                endRowIndex: 1
              },
              cell: {
                userEnteredFormat: {
                  textFormat: {
                    fontSize: 11,
                    bold: true
                  }
                }
              },
              fields: "userEnteredFormat(textFormat)"
            }
          }       
        ]
      }
    };
        
    sheetsAPI.spreadsheets.batchUpdate(dataAndStyle, function(err, response) {
      if (err) {
        reject("The Sheets API returned an error: " + err);
      } else {
        console.log(sheetId + " sheet populated with " + theData.length + " rows and column style set.");
        resolve();
      }
    });    
  });
}

이 코드를 Cloud 함수에 추가하면 이제 99% 완료되었습니다.

데이터와 스타일 지정이 모두 여러 requests으로 단일 Sheets API batchUpdate 호출로 결합되는 방식을 확인합니다. 이렇게 하면 더 효율적이고 원자적인 업데이트가 가능합니다.

생성한 시트의 크기와 일치하는 편집 범위도 정의합니다. 즉, 26개 열 (시트를 만들 때 사용한 columnCount 값)을 초과하는 콘텐츠는 이 특정 코드로 실패합니다.

모든 것이 순조롭게 진행되면 이 시점에서 다음을 수행할 수 있습니다.

  1. 업데이트된 함수 저장
  2. 버킷에 CSV 파일을 드롭합니다.
  3. 스프레드시트에서 해당 데이터 팝업을 확인하세요!

10. 모두 합쳐서 흐름 테스트하기

방금 설명한 함수 호출은 원래 csv2sheet 함수에서 연속적인 차단 호출로 실행할 수 있습니다.

  const sheetId = await addEmptySheet(sheetsAPI, sheetName);
  const theData = await readCSVContent(sheetsAPI, data, sheetName);
  await populateAndStyle(sheetsAPI, theData, sheetId);

전체 함수 소스 코드가 필요한 경우 여기에서 확인할 수 있습니다 (한 세트에 모든 코드를 가져오는 것이 더 쉬울 수 있음).

모든 준비가 완료되면 CSV 파일을 오른쪽 버킷에 업로드하기만 하면 스프레드시트가 파일의 내용이 포함된 새 시트로 업데이트됩니다. 샘플 CSV 파일이 없다면 이 파일을 참고하세요.

1efae021942e64fa.png

버킷에 여러 파일을 업로드하여 어떻게 되는지 확인해 보세요.

11. 작업이 끝났습니다. 인프라를 해체할 시간

농담입니다. 해체할 인프라가 없습니다. 이 모든 것이 서버리스 방식으로 이루어졌습니다!

원하는 경우 앞서 만든 Cloud 함수와 버킷을 삭제하거나 전체 프로젝트를 삭제할 수도 있습니다.

12. 다음 단계

이 Codelab을 마치고 Cloud 함수에서 Cloud Storage 버킷에 업로드를 리슨하여 적절한 API를 사용하여 Google 시트를 업데이트하는 단계를 알아봅니다.

다음은 몇 가지 후속 단계입니다.

이 Codelab에서 문제가 발생했다면 왼쪽 하단에 있는 링크를 사용하여 문제를 신고해 주세요.

의견을 주셔서 감사합니다.