SDK
Query Builder
Supabase 스타일의 타입 안전한 쿼리 빌더
쿼리 빌더
SDK는 Supabase 스타일의 직관적이고 타입 안전한 쿼리 빌더를 제공합니다.
기본 사용법
const queryBuilder = client.from('products')CRUD 작업
find() - 목록 조회
여러 항목을 조회합니다.
const { data } = await client.from('products').find({
limit: 10,
page: 1,
sort: '-createdAt',
where: {
status: { equals: 'published' }
}
})
console.log(data.docs) // Product[]
console.log(data.pagination) // 페이지네이션 정보반환 타입
{
data?: {
docs: T[]
pagination: {
totalDocs: number
limit: number
page: number
totalPages: number
hasNextPage: boolean
hasPrevPage: boolean
nextPage: number | null
prevPage: number | null
}
}
error?: SDKError
}findById() - ID로 조회
특정 항목을 ID로 조회합니다.
const { data: product } = await client
.from('products')
.findById('product_id')
console.log(product) // Product | undefinedcreate() - 생성
새 항목을 생성합니다.
const { data: newProduct } = await client.from('products').create({
name: '새 상품',
price: 10000,
status: 'draft',
description: '상품 설명',
})
console.log(newProduct) // ProductTypeScript가 필수 필드와 선택 필드를 자동으로 추론합니다.
update() - 수정
기존 항목을 수정합니다.
const { data: updatedProduct } = await client
.from('products')
.update('product_id', {
name: '수정된 상품명',
price: 15000,
})
console.log(updatedProduct) // Productremove() - 삭제
항목을 삭제합니다.
const { data: deletedProduct } = await client
.from('products')
.remove('product_id')
console.log(deletedProduct) // Product (삭제된 항목)쿼리 옵션
정렬 (sort)
// 오름차순
await client.from('products').find({
sort: 'price'
})
// 내림차순
await client.from('products').find({
sort: '-createdAt'
})
// 여러 필드로 정렬
await client.from('products').find({
sort: '-createdAt,name'
})페이지네이션
const { data } = await client.from('products').find({
page: 2,
limit: 20,
})
console.log(data.pagination.hasNextPage) // boolean
console.log(data.pagination.nextPage) // number | null
console.log(data.pagination.totalPages) // number필터링 (where)
기본 비교 연산자
await client.from('products').find({
where: {
// 같음
status: { equals: 'published' },
// 같지 않음
status: { not_equals: 'draft' },
// 보다 큼
price: { greater_than: 1000 },
// 보다 큼 또는 같음
price: { greater_than_equal: 1000 },
// 보다 작음
price: { less_than: 50000 },
// 보다 작음 또는 같음
price: { less_than_equal: 50000 },
// 포함 (배열)
tags: { contains: 'featured' },
// 포함하지 않음
tags: { not_contains: 'hidden' },
// ~로 시작
name: { like: 'iPhone%' },
// 존재함
description: { exists: true },
// 존재하지 않음
thumbnail: { exists: false },
}
})IN 연산자
await client.from('products').find({
where: {
status: { in: ['published', 'featured'] },
category: { not_in: ['archived', 'deleted'] },
}
})날짜 필터
await client.from('posts').find({
where: {
createdAt: {
greater_than: '2024-01-01T00:00:00.000Z'
},
publishedAt: {
less_than: new Date().toISOString()
}
}
})복합 조건
AND 조건
await client.from('products').find({
where: {
and: [
{ status: { equals: 'published' } },
{ price: { greater_than: 1000 } },
{ stock: { greater_than: 0 } },
]
}
})OR 조건
await client.from('products').find({
where: {
or: [
{ status: { equals: 'published' } },
{ status: { equals: 'featured' } },
]
}
})복잡한 조합
await client.from('products').find({
where: {
and: [
{ status: { equals: 'published' } },
{ price: { greater_than: 1000, less_than: 50000 } },
{
or: [
{ category: { equals: 'electronics' } },
{ tags: { contains: 'featured' } },
]
}
]
}
})관계 필터링
관계된 데이터를 기준으로 필터링할 수 있습니다.
// 특정 카테고리의 상품 조회
await client.from('products').find({
where: {
category: { equals: 'category_id' }
}
})
// 관계 필드의 속성으로 필터링
await client.from('products').find({
where: {
'category.slug': { equals: 'electronics' }
}
})실전 예제
검색 기능
async function searchProducts(query: string) {
const { data } = await client.from('products').find({
where: {
or: [
{ name: { like: `%${query}%` } },
{ description: { like: `%${query}%` } },
{ tags: { contains: query } },
]
},
limit: 20,
})
return data?.docs || []
}필터링과 정렬
async function getFeaturedProducts() {
const { data } = await client.from('products').find({
where: {
and: [
{ status: { equals: 'published' } },
{ tags: { contains: 'featured' } },
{ stock: { greater_than: 0 } },
]
},
sort: '-createdAt',
limit: 10,
})
return data?.docs || []
}가격 범위 검색
async function getProductsByPriceRange(min: number, max: number) {
const { data } = await client.from('products').find({
where: {
and: [
{ price: { greater_than_equal: min } },
{ price: { less_than_equal: max } },
{ status: { equals: 'published' } },
]
},
sort: 'price',
})
return data?.docs || []
}페이지네이션 구현
async function getProductsPage(page: number = 1, limit: number = 20) {
const { data } = await client.from('products').find({
page,
limit,
where: { status: { equals: 'published' } },
sort: '-createdAt',
})
return {
products: data?.docs || [],
pagination: data?.pagination,
}
}
// 사용 예
const { products, pagination } = await getProductsPage(1, 20)
console.log(pagination.hasNextPage)
console.log(pagination.totalPages)카테고리별 필터링
async function getProductsByCategory(categorySlug: string) {
const { data } = await client.from('products').find({
where: {
and: [
{ 'category.slug': { equals: categorySlug } },
{ status: { equals: 'published' } },
]
},
sort: '-createdAt',
})
return data?.docs || []
}타입 안전성
쿼리 빌더는 완전한 타입 안전성을 제공합니다.
// ✅ OK - 올바른 필드
await client.from('products').find({
where: { name: { equals: 'iPhone' } }
})
// ❌ 타입 에러 - 존재하지 않는 필드
await client.from('products').find({
where: { invalidField: { equals: 'value' } }
})
// ✅ OK - 올바른 연산자
await client.from('products').find({
where: { price: { greater_than: 1000 } }
})
// ❌ 타입 에러 - 잘못된 연산자
await client.from('products').find({
where: { price: { invalid_operator: 1000 } }
})에러 처리
const response = await client.from('products').find()
if (response.error) {
console.error('Error:', response.error.message)
console.error('Suggestion:', response.error.suggestion)
return
}
// 성공한 경우에만 data 접근
const products = response.data.docs또는 타입 가드 사용:
import { isSuccessResponse, isErrorResponse } from '@01.software/sdk'
const response = await client.from('products').find()
if (isSuccessResponse(response)) {
// TypeScript가 response.data의 타입을 자동 추론
console.log(response.data.docs)
} else if (isErrorResponse(response)) {
// TypeScript가 response.error의 타입을 자동 추론
console.error(response.error.message)
}성능 최적화
필요한 데이터만 조회
// limit을 적절히 설정
await client.from('products').find({
limit: 10, // 한 번에 10개만 조회
})인덱싱된 필드로 필터링
// ID, slug 등 인덱싱된 필드를 우선 사용
await client.from('products').find({
where: { slug: { equals: 'iphone-15' } }
})캐싱 활용
React Query를 사용하면 자동으로 캐싱됩니다.
// React Query가 자동으로 캐싱
const { data } = client.query.useCollection('products', {
limit: 10,
})자세한 내용은 React Query 문서를 참조하세요.
다음 단계
- React Query - React 훅으로 데이터 페칭하기
- API Reference - 전체 API 레퍼런스
- 에러 핸들링 - 에러 처리 방법