계기
애써서 만든 i18n json 파일을 좀더 효율적으로 쓰고 싶었다.
원래도 번역자(스프레드시트) <-> 개발자(json)으로 소통하고 있었는데,
번역자가 여러명이라 싱크도 맞추기 힘들고, 개발자 입장에서는 코드에 한번 시트에 한번 계속 입력해주는게 귀찮았기 때문에
한 줄로 해결할 수 있는 방법을 찾고 싶었다.
그래서 Google spreadsheet API를 활용해서 json을 -> 엑셀로, 엑셀을 -> json으로 옮기는 툴을 만들었다,
i18n-scanner 라는 라이브러리도 있었는데 그냥 json만 불러오는게 빠를 것 같아서 안 썼다.
준비물
구글 서비스 계정 생성
https://console.cloud.google.com/apis/credentials 에 가서
1. 프로젝트 생성하고 서비스 계정을 생성하고 키에 가서 JSON 형태의 비공개 키를 만들어 다운받는다.
2. API 및 서비스 > 라이브러리에 가서 Google Sheets API를 사용한다.
3. 제어하고 싶은 구글 스프레드시트 파일에서 json 파일에 있는 client_email 주소를 공유한다.
위 과정의 결과물로 JSON 파일, 스프레드시트 Document ID, sheet ID를 얻었으면 끝.
패키지 설치
React & typescript 환경에서 코드 내부에서 버튼 하나 누르면 동기화되는 그런 시스템을 생각했는데
안타깝게도 그렇게 구축하는 것과 그냥 node XXX.js 를 해버리는 것의 큰 차이가 없었다. (이유는 후술)
따라서 react 프로젝트 안에서 돌아가는 js를 만들었다. 프로젝트는 그냥 package.json을 위해 희생당했다고 보면 된다.
이런저런 많은 패키지를 테스트해보긴 했는데 결국 필요한건 하나였다.
yarn add -D googleapis
https://www.npmjs.com/package/googleapis 하나만 설치해주자.
연동 테스트
const { google } = require('googleapis');
require('dotenv').config();
// 인증 정보
const client = new google.auth.JWT(
process.env.GOOGLE_CLIENT_EMAIL,
null,
process.env.GOOGLE_PRIVATE_KEY,
['https://www.googleapis.com/auth/spreadsheets'] // Google Sheets API 권한
);
// Google Sheets API 호출 함수
async function getSheetData() {
try {
const googleSheets = google.sheets({ version: 'v4', auth: client });
// 스프레드시트에서 데이터를 읽어옴
const response = await googleSheets.spreadsheets.values.batchGet({
spreadsheetId: DOCID, // 스프레드시트 ID
ranges: sheets, // 읽고자 하는 데이터 범위
});
const sheetData = response.data.valueRanges.map((valueRange, index) => {
return {
sheet: sheets[index],
data: valueRange.values,
};
});
return sheetData;
} catch (error) {
console.error('Error accessing Google Sheets API:', error);
}
}
// 함수 실행
getSheetData().then((data)=>{
console.log(data);
});
문제의 new google.auth.JWT가, 브라우저에서는 동작을 안 하는 바람에 반나절정도 삽질했다. 보안 상의 이유로 서버에서만 동작한다고 한다. 자료도 없고, 블로그들 코드가 죄다 안되어서 고생을 좀 했다. 오늘 기준으로 잘 동작된다.
파일 경로+이름이 {path}/a.js 라면, node {path}/a.js 하면 된다. 참 쉽죠?
이 코드만 적용 성공했다면, 이제 읽어오는 것은 두려움 없이 할 수 있다.
맨 아래 실행 함수의 .then 부분에 파일을 저장하는 로직을 넣으면 된다. 이건 찾아보면 많이 나오니까 직접 해보자. 쉽다. (fs.writeFileSync 를 썼다)
process.env의 두 값은 JSON에서 CLIENT_EMAIL과 PRIVATE_KEY env에 적용한 값이다.
env를 잘 쓰려면 require('dotenv').config(); 를 꼭 import 해줘야 한다. (.env 파일을 만드는 것도 포함하여)
참고로 batchGet은 여러 시트를 불러올 때 사용하는 것으로, 그냥 get으로 사용할 수도 있다. update도 마찬가지.
테스트를 통해 값 읽어오기는 했으니 이제 시트에 업데이트도 해보자.
시트에 업데이트 하기
async function updateGoogleSheet() {
try {
const googleSheets = google.sheets({ version: 'v4', auth: client });
// 스프레드시트에 데이터를 추가
const response = await googleSheets.spreadsheets.values.batchUpdate({
spreadsheetId: DOCID, // 스프레드시트 ID
valueInputOption: 'RAW', // 데이터를 입력할 방식 RAW || USER_ENTERED
resource: {
data: [
{
majorDimension: 'ROWS', // 행 단위로 데이터 처리
range: 'test!A1', // 데이터를 추가할 위치
values: **YOUR DATA**,
},
...
],
},
});
console.log(`${response.data} cells updated.`);
} catch (error) {
console.error('Error updating Google Sheets API:', error);
}
}
// 함수 실행
updateGoogleSheet();
여기는 사실상 batchUpdate 함수가 어떻게 쓰이는지만 알면 금방 할 수 있을 것이라고 생각한다.
- valueInputOption은 입력된 데이터를 그대로 쓸지, 엑셀에 사용자가 입력할 때처럼 자동변환 해줄지 하는 옵션이다.
- majorDimension은 데이터 처리 방식인데, ROWS냐 COLUMNS냐를 고를 수 있다. 2차원 배열 읽는 순서다.
- [[1, 2, 3], [4, 5, 6]] 를
- ROWS 로 넣으면 1 2 3 / 4 5 6이 되고
- COLUMNS 로 넣으면 1 4 / 2 5 / 3 6이 된다.
- range는 {시트이름}!{범위} 식의 포맷으로 엑셀 범위 지정할때랑 똑같이 쓰면 된다.
- values에는 위에서 예시를 들었던 [[1, 2, 3], [4, 5, 6]] 처럼 2차원 배열을 넣으면 된다.
마찬가지로 여기서는 readdirSync 를 통해서 json(locale)파일을 읽어왔다. (코드에서는 생략했다.)
후기
자료가 정말정말 없었다. 왜 다른 사람들은 다 잘 되었을까?
지금 생각해보면... 말도 안되는 짓(주로 JWT)을 하고 있었기 때문이 아닐까...
내가 만드는 코드가, 어디서 돌아가고 있는지
어느 프레임워크를 쓰고 있는지, 어떤 방식으로 빌드되는지 아는 것은 중요한 것이라고 생각한다.
환경 변수에 대해서도 조금 더 알게 되었다.
누군가가 이 글을 보고 고통에서 해방되길...
참고한 블로그
https://ui.toast.com/weekly-pick/ko_20210303
https://uni-s-code.tistory.com/42
'[Frontend] > React, TS, JS' 카테고리의 다른 글
[React & Typescript] i18n 적용기 (0) | 2024.10.20 |
---|