뒤로
심은광
심은광 ·

GoogleSheets를 데이터 베이스로 활용하기 (feat. nextjs)

최근에 랜딩 페이지 제작 의뢰를 받게 되었는데,

데이터를 어디에 저장하는 게 좋을까 고민하다가 GoogleSheets API를 알게 되었고 바로 적용해 봤습니다.

현재 바로 활용할 수 있는 데이터베이스 서버가 없거나,

일회성 데이터인데 테이블까지 만들어서 사용하기가 번거롭게 느껴지시는 분이 계시다면 활용해 보시는 것을 적극 추천드립니다.

적용 결과부터 보여 드리고, 간단한 사용법을 설명해 보겠습니다.

[적용결과]

register.gif

1. Google Sheets API 등록하기

API 등록과 관련해서 잘 정리된 블로그 글이 있어서 아래 포스팅 참고해주시면 좋을 것 같아요 !!

2. NEXTJS API 활용하기

아래 코드는 nextjs api를 활용해서,

googleSheetAPI 권한을 인증하고, Sheet에 등록정보를 입력해 주는 간단한 api 코드입니다.

이해가 조금 어려울 수 있는 부분에는 주석을 남겨놨습니다.

import { NextApiRequest, NextApiResponse } from "next";
import { GoogleSpreadsheet } from "google-spreadsheet";
import { JWT } from "google-auth-library";

async function loadGoogleDoc() {
  try {
//  formattedKey: vercel에서 환경변수에 저장한 값은 \n이 \\n으로 인코딩되어 있어서 다시 디코딩해줘야함
    const formattedKey = process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, "\n");
//  Google Sheets API를 등록하는 과정에서 발급받은 키, 이메일을 입력해주세요.
    const serviceAccountAuth = new JWT({
      key: formattedKey,
      email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
      scopes: ["https://www.googleapis.com/auth/spreadsheets"],
    });
// Google Doc의 SheetId를 입력해주세요.
    const doc = new GoogleSpreadsheet(
      process.env.GOOGLE_SHEET_ID || "",
      serviceAccountAuth
    );
    await doc.loadInfo();
    return doc;
  } catch (error) {
    console.log(error);
  }
}

export default async function googleSheet(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method === "POST") {
    try {
      const doc = await loadGoogleDoc();
      if (!doc)
        return res
          .status(200)
          .json({ ok: false, error: "사전등록에 실패했습니다." });
// "유저등록정보" 라는 이름의 sheet가 존재하는지 확인하고, 없다면 만들어줍니다.
      let sheet = doc.sheetsByTitle["유저등록정보"];
      if (!sheet) {
        sheet = await doc.addSheet({
          headerValues: ["email", "createdAt"],
          title: "유저등록정보",
        });
      }
// sheet에서 모든row 정보를 가져옵니다.
      const rows = await sheet.getRows();
// 이미 등록된 이메일인지 검증합니다.
      const isRegistered = rows.some(
        (row) => row.get("email") === req.body.email
      );
      if (isRegistered) {
        return res
          .status(200)
          .json({ ok: false, error: "이미 등록된 이메일입니다." });
      }
      const now = new Date();
      const utc = now.getTime() + now.getTimezoneOffset() * 60 * 1000;
      const koreaTimeDiff = 9 * 60 * 60 * 1000;
// sheet에 새로운 정보를 등록해줍니다.
      await sheet.addRow({
        email: req.body.email,
        createdAt: new Date(utc + koreaTimeDiff).toLocaleString(),
      });
      return res.status(200).json({ ok: true });
    } catch (error) {
      console.log(error);
      return res.status(500).json({ error: "Internal server error" });
    }
  }
}

위에서 작성한 api를 프론트엔드에서 활용하는 예시코드입니다.

const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    try {
      e.preventDefault();
      if (isLoading) return;
      if (email === "") return;
      setIsLoading(true);
      const { data } = await axios.post("/api/googleSheet", {
        email: email,
      });
      if (data.ok) return message.success("사전등록이 완료되었습니다.");
      message.error(data.error);
    } catch (e) {
      console.log(e);
      message.error("사전등록에 실패했습니다.");
    } finally {
      setIsLoading(false);
    }
  };

마치며..

데이터 저장을 위한 서버를 따로 띄우는게 굉장히 번거롭게 느껴져서 저는 google sheets api를 활용해봤습니다 !!

혹시 이것보다 더 간편하고 좋은 방법을 알고계시는 분이 있으시면 저도 알려주세요 ㅎㅎ

읽어주셔서 감사합니다 !!

0

댓글

로그인 후 댓글을 남길 수 있습니다.

William Jung
William Jung

은광님은 대체 24시간을 얼마나 알차게 사용하시는거에요

심은광
심은광

최근에 부쩍 이것저것 하는게 많아졌네요 ㅎㅎ.. 물 들어올때 열심히 해야죠!!

최현세
최현세

최고 이십니다 은광님 :D !!

심은광
심은광

오 현세님 디스콰이엇에서 뵙게 될 줄이야.. 감사합니다!!

김학범
김학범

구글시트가 임시데이터베이스 개념으로 사용하기 좋은것 같습니다. 저희도 고객분들이 데이터베이스에 대한 비슷한 피드백을 주시기도 해서 구글시트나 Supabase, 셀렉트 자체 디비 등 여러 대안들을 설명드리고 있었는데 비슷한 경험을 하신것 같아서 반가운 글이었습니다. 은광님 경험, 글로 써주셔서 감사합니다. 잘 읽었습니다!

심은광
심은광

구글시트를 사용해보면서 이게 괜찮은 방법인가 의구심이 들기도 했었는데, 꽤나 많이 사용되고 있는 방식인 것 같아서, 안심이되네요ㅎㅎ Supabase는 처음 들어봤는데 한번 알아봐야겠어요. 감사합니다 !!

FameLee
FameLee

에어테이블이나 nocodb도 괜찮더라구여!

심은광
심은광

오.. 두개다 처음 들어봤는데 한번 알아봐야겠어요 ㅎㅎ 꿀팁 정말 감사합니다.

김진영
김진영

저는 405 Method Not Allowed가 뜨는데 이유가 뭘까요? ㅜㅠ