Skip to content

Commit ff7611f

Browse files
committed
[README]
1 parent 003c688 commit ff7611f

File tree

4 files changed

+475
-99
lines changed

4 files changed

+475
-99
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ build/
3030
.npmrc
3131
.yarnrc.local.yml
3232
*.tsbuildinfo
33+
packages/main/README.md

GUIDE_CREDENTIALS.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# How to Get Google Service Account Credentials
2+
3+
To use this library, you need a Google Service Account, which allows your application to authenticate and access your Google Sheet on your behalf. Follow these steps to get the necessary credentials.
4+
5+
## Step 1: Create a Google Cloud Project
6+
7+
1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
8+
2. If you don't have a project, create one by clicking the project dropdown at the top of the page and selecting **"New Project"**.
9+
3. Give your project a name and click **"Create"**.
10+
11+
## Step 2: Enable the Google Sheets API
12+
13+
1. In your project's dashboard, navigate to the **"APIs & Services"** > **"Library"** section.
14+
2. Search for **"Google Sheets API"**.
15+
3. Click on it and then click the **"Enable"** button.
16+
17+
## Step 3: Create a Service Account
18+
19+
1. In the **"APIs & Services"** section, go to **"Credentials"**.
20+
2. Click **"Create Credentials"** and select **"Service account"**.
21+
3. Fill in the service account details:
22+
* **Service account name**: Give it a descriptive name (e.g., "Spreadsheet ORM Bot").
23+
* **Service account ID**: This will be generated automatically.
24+
* **Description**: Optional, but helpful.
25+
4. Click **"Create and Continue"**.
26+
5. **Grant access (Optional)**: You can skip granting this service account a role for now by clicking **"Continue"**.
27+
6. **Grant users access (Optional)**: You can also skip this step. Click **"Done"**.
28+
29+
## Step 4: Generate a JSON Key
30+
31+
1. You should now see your newly created service account in the credentials list.
32+
2. Click on the service account's email address to manage it.
33+
3. Go to the **"KEYS"** tab.
34+
4. Click **"Add Key"** and select **"Create new key"**.
35+
5. Choose **JSON** as the key type and click **"Create"**.
36+
6. A JSON file will be automatically downloaded to your computer. **This is your credential file. Keep it safe and do not commit it to public repositories!**
37+
38+
The downloaded JSON file will contain your `client_email` and `private_key`.
39+
40+
## Step 5: Share Your Google Sheet
41+
42+
1. Open the Google Sheet you want to use as a database.
43+
2. Click the **"Share"** button in the top-right corner.
44+
3. In the downloaded JSON file, find the `client_email` value (it looks like `...-..@...iam.gserviceaccount.com`).
45+
4. Paste this email address into the "Add people and groups" field.
46+
5. Give it **"Editor"** permissions.
47+
6. Click **"Send"** (or "Share").
48+
49+
You are now ready to use the credentials in your application!

README.ko.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# Spreadsheet ORM (한국어)
2+
3+
[![NPM Version](https://img.shields.io/npm/v/spreadsheet-orm.svg)](https://www.npmjs.com/package/spreadsheet-orm)
4+
[![NPM Downloads](https://img.shields.io/npm/dm/spreadsheet-orm.svg)](https://www.npmjs.com/package/spreadsheet-orm)
5+
[![Build Status](https://github.com/codingbotPark/spreadsheet-orm/actions/workflows/node.js.yml/badge.svg)](https://github.com/codingbotPark/spreadsheet-orm/actions/workflows/node.js.yml)
6+
[![License](https://img.shields.io/npm/l/spreadsheet-orm.svg)](https://github.com/codingbotPark/spreadsheet-orm/blob/main/LICENSE)
7+
8+
**Spreadsheet ORM**은 Google 스프레드시트를 데이터베이스처럼 사용할 수 있도록 설계된 강력하고 현대적인 타입-세이프(type-safe) ORM(객체-관계 매핑) 라이브러리입니다. 단순한 행/열 조작을 넘어, 스키마 관리, 마이그레이션, 유창한(fluent) 쿼리 빌더와 같은 데이터베이스 수준의 기능을 활용해 보세요.
9+
10+
## 주요 특징
11+
12+
- **타입-세이프 스키마 정의**: TypeScript로 테이블 구조를 정의하고, 완벽한 타입 안정성과 자동완성 기능을 누리세요.
13+
- **강력한 쿼리 빌더**: `SELECT`, `INSERT`, `UPDATE`, `DELETE` 작업을 위한 유창하고 연쇄적인(chainable) API를 제공합니다.
14+
- **스키마 동기화**: 데이터베이스 마이그레이션처럼, 코드에 정의된 스키마와 실제 스프레드시트 구조를 항상 동기화된 상태로 유지합니다.
15+
- **자동 타입 추론**: 정의된 스키마로부터 TypeScript 타입을 자동으로 추론하여, 개발 전 과정에서 완벽한 타입 안정성을 보장합니다.
16+
- **모던 API**: TypeScript와 ES 모듈로 구축되어 깔끔하고 직관적인 개발 경험을 제공합니다.
17+
18+
## 설치하기
19+
20+
```bash
21+
# Yarn 사용 시
22+
yarn add spreadsheet-orm
23+
24+
# NPM 사용 시
25+
npm install spreadsheet-orm
26+
```
27+
28+
## 빠른 시작
29+
30+
### 1. 인증 정보 (Credentials)
31+
32+
먼저 Google 서비스 계정 인증 정보가 필요합니다. 인증 정보를 발급받는 자세한 단계별 안내는 [**인증 정보 발급 가이드**](./GUIDE_CREDENTIALS.md)를 참고해 주세요.
33+
34+
### 2. 핵심 개념
35+
36+
전체 작업 흐름은 다음 세 단계로 이루어집니다:
37+
1. **스키마 정의**: `defineTable` 함수를 사용하여 테이블(시트)의 구조를 정의합니다.
38+
2. **클라이언트 초기화**: 인증 정보와 스키마를 사용하여 클라이언트 인스턴스를 생성합니다.
39+
3. **동기화 및 쿼리**: `schemaManager`로 스키마를 동기화하고, `queryBuilder`로 데이터를 조작합니다.
40+
41+
### 3. 전체 예제
42+
43+
다음은 시작을 위한 전체 예제 코드입니다.
44+
45+
```typescript
46+
import {
47+
createSpreadsheetClient,
48+
defineTable,
49+
fieldBuilder,
50+
type InferTableType
51+
} from "spreadsheet-orm";
52+
53+
// 인증 정보는 환경 변수 등 안전한 방법으로 불러오는 것을 권장합니다.
54+
import credentials from "./your-google-credentials.json";
55+
56+
// --- 1단계: 스키마 정의하기 ---
57+
58+
const Users = defineTable("Users", {
59+
id: fieldBuilder.string().default("UUID()").build(), // 기본값 설정
60+
name: fieldBuilder.string().build(),
61+
email: fieldBuilder.string().build(),
62+
age: fieldBuilder.number().optional().build(), // optional: 비워둘 수 있는 필드
63+
createdAt: fieldBuilder.date().default(new Date()).build(),
64+
});
65+
66+
const Posts = defineTable("Posts", (field) => ({
67+
id: field.string().build(),
68+
title: field.string().build(),
69+
content: field.string().build(),
70+
// Users 테이블의 id 필드를 참조하는 "외래 키" 관계 생성
71+
authorId: field.reference(Users, "id").build(),
72+
}));
73+
74+
75+
// --- 2단계: 스키마로부터 타입 자동 추론하기 ---
76+
77+
type User = InferTableType<typeof Users.fields>;
78+
type Post = InferTableType<typeof Posts.fields>;
79+
80+
// 이제 완벽한 타입 안정성을 가집니다!
81+
// const newUser: User = { id: "1", name: "Jane Doe", email: "jane@example.com" };
82+
83+
84+
// --- 3단계: 클라이언트 초기화하기 ---
85+
86+
const client = createSpreadsheetClient({
87+
// 인증 정보
88+
email: credentials.client_email,
89+
privateKey: credentials.private_key,
90+
spreadsheetID: "YOUR_SPREADSHEET_ID_HERE",
91+
92+
// 스키마 목록
93+
schemas: [Users, Posts],
94+
95+
// (선택) 코드에는 있지만 실제 시트가 없을 때의 처리 전략
96+
onMissingSchema: "create", // 'create'(생성), 'ignore'(무시), 'error'(오류) 중 선택
97+
});
98+
99+
100+
// --- 4단계: 스키마 동기화 및 쿼리 실행하기 ---
101+
102+
async function main() {
103+
// 스키마를 실제 스프레드시트와 동기화합니다 (데이터베이스 마이그레이션과 유사).
104+
// 'smart' 모드는 데이터 손실 없이 없는 시트를 생성하고 컬럼 순서를 바로잡습니다.
105+
console.log("스키마 동기화를 시작합니다...");
106+
await client.schemaManager.sync({ mode: "smart" });
107+
console.log("동기화 완료!");
108+
109+
// 쿼리 빌더를 사용하여 CRUD 작업 수행
110+
console.log("새로운 사용자를 추가합니다...");
111+
await client.query()
112+
.insert(["1", "John Doe", "john@example.com", 30]).into("Users")
113+
.and(["2", "Jane Smith", "jane@example.com"]).into("Users") // and()로 insert 연결
114+
.execute();
115+
116+
console.log("사용자 목록을 조회합니다...");
117+
const allUsers = await client.query().select().from("Users").execute();
118+
console.log("모든 사용자:", allUsers);
119+
120+
console.log("25세 이상인 사용자를 조회합니다...");
121+
const filteredUsers = await client.query()
122+
.select(["name", "email"])
123+
.from("Users")
124+
.where((row) => {
125+
const ageIndex = Users.orderedColumns.indexOf("age");
126+
// row[0]은 행 번호 인덱스이므로, 데이터 컬럼은 row[1]부터 시작합니다.
127+
return Number(row[ageIndex + 1]) > 25;
128+
})
129+
.execute();
130+
console.log("필터링된 사용자:", filteredUsers);
131+
}
132+
133+
main().catch(console.error);
134+
```
135+
136+
## API 레퍼런스
137+
138+
### 스키마 정의 (`defineTable`)
139+
140+
`defineTable`을 사용하여 시트의 구조를 정의합니다. 두 번째 인자로는 각 값이 `.build()`로 끝나는 `fieldBuilder` 체인인 객체를 전달합니다.
141+
142+
- `defineTable(sheetName, fields, [columnOrder])`
143+
144+
`fieldBuilder`는 각 데이터 타입에 맞는 메소드를 제공합니다:
145+
- `string()`
146+
- `number()`
147+
- `boolean()`
148+
- `date()`
149+
- `reference(schema, fieldName)`: 다른 테이블 필드와의 연결(참조)을 생성합니다.
150+
151+
각 필드 빌더는 `.build()`를 호출하기 전에 다음과 같은 수정자를 연결할 수 있습니다:
152+
- `.optional()`: 필드를 선택적으로(비워둘 수 있도록) 만듭니다.
153+
- `.default(value)`: 스키마 동기화 시 새로운 항목에 대한 기본값을 제공합니다.
154+
155+
### 스키마 관리 (`client.schemaManager`)
156+
157+
`schemaManager`는 코드에 정의된 내용과 실제 스프레드시트 구조가 일치하도록 보장합니다.
158+
159+
- `sync({ mode })`: 스키마를 동기화합니다.
160+
- `mode: 'strict'`: 불일치하는 부분이 있으면 오류를 발생시킵니다.
161+
- `mode: 'smart'`: (권장) 데이터 손실 없이, 없는 시트를 생성하고 기존 시트의 컬럼 순서를 바로잡습니다.
162+
- `mode: 'force'`: 스키마와 일치하지 않는 기존 시트를 덮어쓰므로 데이터가 손실될 수 있습니다.
163+
- `mode: 'clean'`: 모든 데이터를 지우고 스키마 헤더만 새로 씁니다.
164+
165+
### 쿼리 빌더 (`client.query()`)
166+
167+
쿼리 빌더는 데이터 조작을 위한 유창한(fluent) API를 제공합니다.
168+
169+
- **SELECT**:
170+
```typescript
171+
// 모든 컬럼 선택
172+
await client.query().select().from("Users").execute();
173+
174+
// 특정 컬럼 선택 및 필터 적용
175+
await client.query()
176+
.select(["name", "email"])
177+
.from("Users")
178+
.where(row => Number(row[3]) > 30) // age(3번째 컬럼이라 가정)로 필터링
179+
.execute();
180+
```
181+
182+
- **INSERT**:
183+
```typescript
184+
const newRow = ["3", "Peter Jones", "peter@example.com", 42];
185+
await client.query().insert(newRow).into("Users").execute();
186+
```
187+
188+
- **UPDATE**:
189+
```typescript
190+
const updatedData = ["Peter Jones Jr.", "peter.jr@example.com", 43];
191+
await client.query()
192+
.update(updatedData)
193+
.from("Users")
194+
.where(row => row[1] === "3") // id가 "3"인 행 대상
195+
.execute();
196+
```
197+
198+
- **DELETE**:
199+
```typescript
200+
await client.query()
201+
.delete()
202+
.from("Users")
203+
.where(row => row[2] === "peter.jr@example.com") // 이메일이 일치하는 행 대상
204+
.execute();
205+
```
206+
207+
- **쿼리 연결 (`and`)**:
208+
여러 작업을 하나의 배치(batch) 요청으로 연결하여 성능을 향상시킬 수 있습니다.
209+
```typescript
210+
await client.query()
211+
.insert(["4", "Alice", "alice@example.com"]).into("Users")
212+
.and()
213+
.insert(["p1", "My First Post", "...", "4"]).into("Posts")
214+
.execute();
215+
```
216+
217+
## 기여하기
218+
219+
언제나 기여를 환영합니다! 편하게 Pull Request를 보내주시거나 이슈를 등록해주세요.
220+
221+
## 라이선스
222+
223+
이 프로젝트는 MIT 라이선스를 따릅니다.

0 commit comments

Comments
 (0)