본문 바로가기
개발/Flutter

Flutter에서 Google Spreadsheet에 데이터를 추가해보자.

by leedonggeun 2023. 10. 19.

플러터

🫤 "알림이 2번 와요", "파일 다운로드가 안 돼요"
😡 "OO가 안 돼요"

 

모바일 앱을 새로 배포하고 나니, 당연하게도 기존 고객들에게서 문의가 쏟아졌다.

문제는 역시나 문의 시 이슈를 파악할 수 있는 정보(Android인지 iOS인지, 앱 버전은 무엇인지 등) 주지 않았다.
나 같은 경우, QA로 일했던 경력이 있었기 때문에 당연히 버전과 precondition 등을 제공해 줄 알았던 멍청이었던 거다.

이번엔 내가 이 문의하기 기능에 대해 어떻게 해결했는지에 대한 게시글이다.

 

왜 구글 스프레드시트로 정했을까?

나는 아래와 같은 요구사항이 필요했다.

1. 단순하게 문의를 받는 창구만 필요하다.
2. 이 단순한 구현을 위해 DB / Controller / Service 로직을 구현한다면 리소스 낭비라고 판단했다.
3. 이미 모바일앱이 아닌 웹 페이지에서는 구글 스프레드시트로 문의/건의사항을 받고 있었다.
4. 장애, 유지보수 등을 고려할 필요가 없었으면 좋겠다. (물론 구글 이슈로 발생할 수 있지만)

그럼 이제 구글 스프레드시트를 연동해보도록 하자.

GCP 프로젝트 생성

아래 링크에 접근하여 [프로젝트 선택] > [새 프로젝트]를 선택하여 프로젝트를 생성하자.
https://console.cloud.google.com

GCP 프로젝트 생성

 

Google Sheets API 검색

프로젝트가 생성됐다면, 검색창에서 [Google Sheets API]를 입력하여 검색 및 [사용]을 선택한다.

GCP 내 Google Sheets API 서비스

 

IAM 생성

API 세부정보에서 [사용자 인증 정보 만들기]를 선택하여 API 인증 정보를 생성한다.
(선택사항은 굳이 안해도 된다.)

IAM 생성 전 콘솔
IAM 생성

 

생성된 IAM 확인

좌측 메뉴의 [사용자 인증 정보] 를 선택하면 아래 [서비스 계정] 항목에 새로운 사용자가 추가된 것을 볼 수 있다.
생성된 계정을 선택한다.

생성된 IAM 정보

 

키 추가

이제 API 사용에 필요한 키 정보를 생성한다.
메뉴의 [키] > [키 추가]> [새 키 만들기]를 선택하여 JSON 형태로 생성하자.

그러면 JSON 형태의 키파일이 다운로드되는데, 이 파일은 잘 소장하고 있어야 한다. (다시 다운로드하지 못함)

IAM API Key 발급

 

스프레드시트 생성 및 공유

데이터를 편집할 스프레드시트를 생성하고, 위에서 생성된 계정을 해당 스프레드시트의 편집자 권한으로 공유하자.

생성된 서비스 계정에게 스프레드시트를 공유한다.

여기까지 진행됐으면, 이제 웹에서 할 일은 모두 끝났다.
우리는 해당 API KEY를 가지고 스프레드시트에 접근할 수 있게된 것이다.

 

플러터 의존성 추가하기

구글 문서를 보면 의존성을 추가하지 않고 직접 구현해도 됐지만, 굳이 이런 기능에 내 시간을 쓰고 싶지 않았다.
또한 어차피 api 버전이 변경되고, 지금의 버전이 deprecated되지 않는 이상 지속적으로 쓸 수 있는 기능이라고 판단했다. 

https://pub.dev/packages/gsheets

 

gsheets | Dart Package

A library for working with Google Sheets API v4. Manage your spreadsheets with gsheets in Dart.

pub.dev

 

구글 스프레드시트 id 가져오기

아까 만들어둔 구글 스프레드시트의 id를 가져오자.
(해당 스프레드시트에 들어가고, URL을 확인하면 https://docs.google.com/spreadsheets/d/{id}/edit?pli=1#gid=0 이와 같은 형태로 구성되어 있습니다.)

 

API 유틸리티 클래스 생성

구글 스프레드시트에 접근하려면 데이터들(Key, 시트 ID, 사용할 시트 이름)이 필요한데 우리는 모두 위에서 생성 및 확인을 진행했다. 준비가 됐다면, 아래와 같이 유틸리티 클래스를 생성하자.

💡 _credentials 변수는 반드시 아래의 형태 그대로 입력하자.
r과 '''는 각각 raw데이터 입력 및 멀티라인 입력에 대한 의미가 있으니...

_credentials = r'''
 {데이터}
''';
import 'package:gsheets/gsheets.dart';

class GoogleUtil {
  static const _spreadSheetId = '15wjh1vok6aPhaQwSljUJznrTsfyPDWSeQrQHp29UHiA';
  static const _sheetName = '기술_블로그_테스트';

  static const _credentials = r'''
    {
      "type": "service_account",
      "project_id": "tech-blog-test-project",
      ...
      ...
      "auth_uri": "https://accounts.google.com/o/oauth2/auth",
      "token_uri": "https://oauth2.googleapis.com/token",
      "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
      "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/dglee-tech-blog-test%40tech-blog-test-project.iam.gserviceaccount.com",
      "universe_domain": "googleapis.com"
    }
  ''';

  static final _sheet = GSheets(_credentials); // 스프레드시트
  static Worksheet? _worksheet; // 스프레드시트 중 작업 대상 시트

  static Future<void> initialize() async {
    _worksheet = await _getWorksheet(
      await _sheet.spreadsheet(_spreadSheetId),
      title: _sheetName,
    );
  }

  /// 먼저 시트를 생성하고, 이미 있을 경우는 해당 시트를 가져온다.
  static Future<Worksheet> _getWorksheet(
      Spreadsheet spreadsheet, {
      required String title,
      }) async {
    try {
      return await spreadsheet.addWorksheet(title);
    } catch (e) {
      return spreadsheet.worksheetByTitle(title)!;
    }
  }

  /// appendRow는 insert처럼 덮어씌우기가 아니라, 아래 로우에 차곡차곡 insert 된다.
  static Future<void> insert() async {
    await _worksheet!.values.appendRow(fromColumn: 1, ['test1', 'test2', 'test3']);
  }
}

 

구글 스프레드 시트에 데이터를 넣어보자

void main() async {
    ..
    ..
    
    await GoogleUtil.initialize(); // 스프레드시트 초기화
    await GoogleUtil.insert();     // 스프레드시트에 더미 데이터 추가
}

위의 로직이 수행될 경우, 기입한 기술_블로그_테스트 라는 시트를 생성하고, 해당 시트에 [test1, test2, test3] 데이터를 삽입합니다. (아래 사진은 2번 반복수행한 결과)

잘 들어간 데이터

 

이렇게 우리는 플러터에서 구글 스프레드시트에 접근해 데이터를 추가할 수 있게 되었습니다.

댓글