지난 글에서는 디스코드 봇을 내 채널에 초대하고 명령어를 통해 통신하는 작업까지 했었다.
이번 글에서는 스프레드시트를 이용하여 디스코드 ID, 서버 닉네임, 패널티 카운트, 제출 일자를 관리하는 방법에 대해 작성해 보겠다.
구글 스프레드시트를 사용하기 위해서는 Google Cloud의 콘솔에서 프로젝트를 하나 생성해 주어야 한다.
좌측 상단 VIG-Discord가 있는 부분을 눌러 새 프로젝트를 만들어준다.
원하는 프로젝트 이름을 입력 후 만들기를 누른다.
프로젝트 생성 후 스프레드시트 API 사용 설정, OAuth 동의 화면 구성이 있는데, 해당 부분은 공식 문서를 참고하면 된다.
추가로 이 프로젝트의 경우 사용자 인증 정보에서 서비스 계정 하나만 생성했다. 그리고 서비스 계정의 이메일과 키 파일을 사용했다.
키 파일의 경우 이메일을 클릭하고 키 메뉴에서 키 추가 → 새 키 만들기 → JSON 선택 후 만들기를 하면 된다.
이제 데이터베이스용으로 사용할 스프레드시트를 하나 생성해 준다. 해당 스프레드시트의 url을 보면 스프레드시트의 ID를 알 수 있다. 이 또한 추후에 사용할 예정이다.
위에서 만들어둔 서비스 계정의 이메일을 공유 탭을 이용하여 추가해 준다. 권한은 편집자를 주면 된다.
이러면 코드 작성 전 할 일은 모두 완료한 것이다!
스프레드시트를 사용하기 위해서는 라이브러리를 설치해 주어야 한다.
pnpm add google-auth-library
pnpm add -D googleapis
그리고 환경 변수에 아래 내용을 추가해 준다.
# 구글 스프레드시트 ID
GOOGLE_SPREADSHEET_ID = YOUR_ID
# 서비스 계정 JSON 파일 경로
GOOGLE_SERVICE_ACCOUNT_FILE = YOUR_FILE_PATH
# 시트 이름
GOOGLE_SPREADSHEET_NAME = YOUR_SPREADSHEET_NAME
secret.json
형태로 환경 변수를 설정해 주었다.스프레드시트에 있는 모든 데이터를 조회할 때 아래처럼 사용한다.
export const getValues = async () => {
try {
const result = await service.spreadsheets.values.get({
spreadsheetId,
range: spreadsheetName,
majorDimension: "COLUMNS",
});
return result.data.values;
} catch (err) {
throw err;
}
};
getValues
를 실행했을 때 result.data.values
의 값은 아래와 같이 반환되게 된다.
[[”아이디”],[”이름”],[”패널티 카운트”],[”제출 일자”]]
만약 데이터가 아래와 같이 있다면 반환되는 값은 변하게 된다.
[[”아이디”, "1"],[”이름”, "홍길동"],[”패널티 카운트”,"0"],[”제출 일자”,"24.9.12"]]
열을 기준으로 배열이 나뉘는 이유는 majorDimension
을 COLUMNS
로 했기 때문이다.
시트의 범위를 지정하는 법은 A1 표기법을 따른다.
스프레드시트에 있는 데이터를 업데이트할 때 아래처럼 사용한다.
export const updatePenaltyCount = async (userIndex, penaltyCount) => {
try {
await service.spreadsheets.values.update({
spreadsheetId,
range: `${spreadsheetName}!C${userIndex}`,
valueInputOption: "RAW",
requestBody: {
values: [[penaltyCount + 1]],
},
});
} catch (err) {
throw err;
}
};
값을 업데이트할 때는 requestBody
안에 있는 values
에 이차원 배열로 넣어주면 된다.
만약 values가 [[penaltyCount + 1, "날짜"]]
와 같다면 C${userIndex}
와 D${userIndex}
에 값이 덮어씌워진다.
// commands/submit.js
import { SlashCommandBuilder } from "discord.js";
import { getValues, insertNewUser, updateSubmitDate } from "../utils/spreadsheet.js";
export default {
data: new SlashCommandBuilder()
.setName("submit")
.setDescription("작성한 블로그를 제출합니다.")
.addStringOption((option) => {
option.setName("url").setDescription("작성한 블로그의 URL을 입력하세요.").setRequired(true);
return option;
}),
async execute(interaction) {
const url = interaction.options.getString("url");
const nickname = interaction.member.nickname;
const userId = interaction.user.id;
const [users] = await getValues();
const existIndex = users.findIndex((id) => id === userId);
if (existIndex === -1) {
await insertNewUser(users.length + 1, userId, nickname);
} else {
await updateSubmitDate(existIndex + 1);
}
return await interaction.reply(`제출자: ${nickname}\n블로그 URL: ${url}`);
},
};
// utils/spreadsheet.js
import { config } from "dotenv";
import { GoogleAuth } from "google-auth-library";
import { google } from "googleapis";
import { formatToday } from "./date.js";
config();
const spreadsheetId = process.env.GOOGLE_SPREADSHEET_ID;
const keyFile = process.env.GOOGLE_SERVICE_ACCOUNT_FILE;
const spreadsheetName = process.env.GOOGLE_SPREADSHEET_NAME;
const auth = new GoogleAuth({
scopes: "https://www.googleapis.com/auth/spreadsheets",
keyFile,
});
const service = google.sheets({ version: "v4", auth });
export const getValues = async () => {
try {
const result = await service.spreadsheets.values.get({
spreadsheetId,
range: spreadsheetName,
majorDimension: "COLUMNS",
});
return result.data.values;
} catch (err) {
throw err;
}
};
export const insertNewUser = async (insertIndex, userId, nickname) => {
try {
await service.spreadsheets.values.update({
spreadsheetId,
range: `${spreadsheetName}!A${insertIndex}`,
valueInputOption: "RAW",
requestBody: {
values: [[userId, nickname, 0, formatToday()]],
},
});
} catch (err) {
throw err;
}
};
export const updateSubmitDate = async (existIndex) => {
try {
await service.spreadsheets.values.update({
spreadsheetId,
range: `${spreadsheetName}!D${existIndex}`,
valueInputOption: "RAW",
requestBody: {
values: [[formatToday()]],
},
});
} catch (err) {
throw err;
}
};
export const updatePenaltyCount = async (userIndex, penaltyCount) => {
try {
await service.spreadsheets.values.update({
spreadsheetId,
range: `${spreadsheetName}!C${userIndex}`,
valueInputOption: "RAW",
requestBody: {
values: [[penaltyCount + 1]],
},
});
} catch (err) {
throw err;
}
};
submit.js
에서 아래의 로직이 추가됐다.
이제 블로그 url을 제출하면 스프레드시트의 제출 일자가 업데이트되는 것을 확인할 수 있다.
아이디, 이름 등은 일부로 가렸다 ㅎ…
이번 글에서는 스프레드시트를 연동시켜서 데이터베이스로 활용하는 방법에 대해 알아보았다.
같은 채널에 있는 친구의 추천이었는데, 너무 좋은 아이디어라 현재 사용하며 매우 만족 중이다.
다음 글에서는 서버를 배포하고 해당 서버에서 봇을 실행시켜보는 방법에 대해 작성하겠다.
맨땅에 헤딩하며 작성한 글이라 부족한 점이 많을 수 있으니 피드백 주시면 아주아주 감사합니다!!