HJ
다른 글 보러 가기

Turso를 알아보자

10/20/2024

Turso는 SQLite 기반의 서버리스 플랫폼으로, 기존 데이터베이스 시스템과는 다소 다른 독특한 특징을 가지고 있습니다.

그중 가장 눈에 띄는 특징은 사용자별로 데이터베이스를 생성할 수 있다는 점인데요, 새로운 사용자가 회원가입을 할 때마다 서버는 그 사용자만을 위한 전용 데이터베이스를 만들어 다른 사용자의 데이터와 격리시킬 수 있습니다. 이런 점은 SaaS 제품을 개발할 때 특히 유용할 것 같습니다.

그렇다면 이 특징을 실제로 코드에 어떻게 적용할 수 있을지 궁금하실 건데요, 간단한 예제를 통해 그 방법을 살펴보겠습니다.

Turso 감잡기

libsql://{databaseName}-{orgName}.turso.io

이 주소는 Turso로 생성한 사용자별 데이터베이스의 주소 형식입니다. 여기서 orgName은 여러분의 Organization 이름이고, databaseName은 여러분이 만들고자 하는 데이터베이스 이름입니다. 제 경우, 사용자별로 데이터베이스를 생성하고 싶어서 userId 값을 데이터베이스 이름으로 사용합니다.

따라서 사용자별로 서로 다른 데이터베이스에 쿼리를 요청하려면, 서버의 컨트롤러나 라우터 단에서 세션이나 토큰을 통해 userId를 확인하고, 이를 바탕으로 해당 사용자의 데이터베이스에 쿼리를 보내는 방식으로 코드를 구성하면 됩니다.

그 전에 이 주소로 요청을 보내기 위한 userId를 기반으로 한 데이터베이스를 어떻게 만들 수 있을까요?

import { createClient } from "@tursodatabase/api"

function createUserDatabase(userId) {
	const turso = createClient({
	  org: process.env.TURSO_ORG_NAME,
	  token: process.env.TURSO_AUTH_TOKEN,
	})
	
	const database = await turso.databases.create(userId, { schema: process.env.TURSO_SCHEMA })
}

이런 방식으로 Turso SDK를 사용하여 코드 내에서 사용자별 데이터베이스를 생성할 수 있습니다. 이 코드를 회원가입 로직에 추가하면 돼요.

그렇지만 데이터베이스 이름에 userId를 사용할 때 주의할 점이 있어요. 이름에는 소문자 알파벳, 숫자, 그리고 대시만 사용할 수 있다는 제약이 있어서 저는 nanoid라는 라이브러리를 사용해 이 조건에 맞는 userId를 생성하고 있답니다.

import { customAlphabet } from "nanoid"

async function signUp() {
	const userId = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz-", 8)()
	// ...
	await createUserDatabase(userId)
}

그럼 회원가입을 완료한 사용자가 서버에 요청을 보냈을 때의 전반적인 처리 과정을 어떻게 이루어질까요?

router.get("/todos", async (req) => {
	// 세션으로부터 userId값을 획득
	const userId = await validateSessionCookie(req)
	const todos = await todoService().getTodos(userId)
	return todos
})
async function todoService() {
	return {
		getTodos: (userId) => {
			const libsql = createLibsqlClient({
				url: `${userId}-${process.env.TURSO_ORG_NAME}.turso.io`,
				token: process.env.TURSO_AUTH_TOKEN
			})
			const db = createDB(libsql)
			return db.todo.findMany()
		}
	}
}

기존 데이터베이스 방식과 달리, 이 경우에는 사용자별 데이터베이스를 사용하기 때문에 findMany 실행 시 userId로 필터링할 필요가 없어요. 단순히 모든 투두를 불러오면 해당 사용자의 데이터만 조회됩니다.

⚠️ db 인스턴스를 의존성으로 주입할 때는 스코프를 설정하여 다른 사용자의 요청과 겹치지 않도록 주의해야 합니다.

이어서

Turso에 관심이 생기셨다면 공식 문서를 확인해보세요. 부모 스키마, 마이그레이션, 그룹, 복제 등 이 글에서 다루지 못한 주제들과 부족한 부분들은 시간 날 때마다 이 글에 조금씩 추가해 나가도록 하겠습니다.

📎 참고로 이 방식을 적용한 제 프로젝트는 Writeflow입니다. Writeflow는 Turso를 활용하여 사용자별 데이터를 완전히 격리된 환경에서 관리하고 있어요.

다른 글 보러 가기

Powered by Writeflow