사용자 관리
Connect Base 사용자 관리 심화 가이드입니다.
역할 및 권한
AppMember.role 은 free-form 문자열 컬럼입니다(기본값: 빈 문자열). 아래 4개는 예시 컨벤션일 뿐 시스템이 자동으로 인식하는 값은 아닙니다 — RLS 규칙 / 함수 / 워크플로우에서 auth.role 로 직접 비교해 권한을 구현합니다.
| 역할 (예시) | 설명 | 권한 (예시) |
|---|---|---|
| owner | 앱 소유자 | 모든 권한 |
| admin | 관리자 | 대부분 권한 (앱 삭제 제외) |
| member | 일반 멤버 | 기본 CRUD |
| guest | 게스트 | 읽기만 |
📌 콘솔의
owner/admin은 별개의 콘솔 사용자(User) 권한이며, 앱 멤버(AppMember) 와 다른 개념입니다.
커스텀 역할
콘솔에서 커스텀 역할 생성:
{
"name": "moderator",
"permissions": [
"users:read",
"users:update",
"content:read",
"content:update",
"content:delete"
]
}권한 체크
SDK에서 확인
const me = await cb.auth.getMe()
// 권한은 custom_data 에 저장합니다
const permissions = (me.custom_data?.permissions ?? []) as string[]
if (permissions.includes('users:delete')) {
// 삭제 버튼 표시
}API에서 확인
서버리스 함수에서 권한 확인:
export async function handler(event, context) {
const user = context.user
if (!user.permissions.includes('admin:access')) {
return {
statusCode: 403,
body: JSON.stringify({ error: 'Forbidden' })
}
}
// 관리자 기능 실행
}세션 관리
토큰 구조
Access Token (1시간)
├── userId
├── appId
├── role
├── permissions
└── exp
Refresh Token (7일)
├── userId
├── appId
└── exp토큰 갱신 / 세션 만료 처리
📌 토큰 콜백은 SDK 초기화 시 옵션으로 등록합니다.
cb.auth.onXxx(...)같은 메서드는 존재하지 않습니다.
import ConnectBase from 'connectbase-client'
const cb = new ConnectBase({
publicKey: 'cb_pk_...',
// access_token / refresh_token 이 갱신될 때마다 호출
onTokenRefresh: ({ accessToken, refreshToken }) => {
localStorage.setItem('cb_access_token', accessToken)
localStorage.setItem('cb_refresh_token', refreshToken)
},
// 401 등 인증 실패 시 호출
onAuthError: (error) => {
console.error('인증 실패:', error)
},
// refresh_token 까지 만료되어 재로그인이 필요할 때 호출
onTokenExpired: () => {
localStorage.removeItem('cb_access_token')
localStorage.removeItem('cb_refresh_token')
window.location.href = '/login'
}
})소셜 로그인
지원 프로바이더
SDK OAuthProvider 타입 기준 6종:
- Naver
- Kakao
- Apple
- GitHub
- Discord
소셜 로그인 시 AppMember.email 컬럼에 이메일이 자동 저장됩니다(Apple 의 경우 relay 주소). 콘솔 → 인증 설정에서 사용할 프로바이더만 ON 으로 토글하세요.
설정
콘솔 > 설정 > 인증에서 각 프로바이더 설정:
- Client ID 입력
- Client Secret 입력
- Redirect URL 설정
사용
// 리다이렉트 방식 로그인 (권장) — 모든 환경에서 안정적
await cb.oauth.signIn('google', 'https://myapp.com/callback')
// 콜백 페이지에서 결과 처리 (3.23.0+: Promise 반환)
const result = await cb.oauth.getCallbackResult()
// 팝업 방식 로그인 (COOP 정책 제한 주의)
const result = await cb.oauth.signInWithPopup('google', 'https://myapp.com/callback')모범 사례
1. 최소 권한 원칙
필요한 최소한의 권한만 부여:
// ❌ 나쁜 예: 모든 권한
{ role: 'admin' }
// ✅ 좋은 예: 필요한 권한만
{
role: 'custom',
permissions: ['products:read', 'products:update']
}2. 정기적 세션 검증
// 주기적으로 세션 유효성 확인
setInterval(async () => {
try {
await cb.auth.getMe()
} catch {
// 세션 만료, 재로그인 요청
showLoginModal()
}
}, 5 * 60 * 1000) // 5분마다3. 감사 로그
중요 작업은 로깅:
async function deleteUser(userId: string) {
await cb.database.createData('tbl_audit_logs', {
data: {
action: 'user_deleted',
targetId: userId,
performedBy: currentUser.id,
timestamp: new Date().toISOString()
}
})
await cb.database.deleteData('tbl_users', userId)
}앱간 / 플랫폼 이슈 큐 (2026-05-10)
사용자 / SDK / MCP 에서 발생한 이슈를 비동기 큐로 발행할 수 있습니다. 워크플로우 / 웹훅과 달리 응답을 기다리지 않고, 운영자가 처리 후 답신을 보내는 비동기 양방향 채널 입니다.
Cross-App Issue Queue
oauth:issue:report scope 를 가진 Consumer 가 Provider 앱 운영자에게 이슈를 발행하는 큐입니다. pull-based 로 운영자가 큐에서 가져가며, AI 가 자동 응답 불가하면 needs_human 으로 escalation 합니다.
// Consumer 앱이 Provider 운영자에게 이슈 발행
await cb.support.reportToProvider({
providerAppId: 'provider-app-id',
accessToken: providerToken, // OAuth 발급 토큰
subject: '주문이 결제됐는데 처리 안 됨',
body: 'order_id=order_xxx 가 30분째 pending 상태입니다.',
metadata: { order_id: 'order_xxx' }
})새로운 앱간 비동기 통신은 이 큐를 재사용하는 것을 권장합니다.
Platform Issue Queue
ConnectBase 자체 버그 / 문의를 위한 별도 큐(platform_issues 테이블) 입니다. End-user / SDK / MCP 에서 발행하면 IsSystemAdmin 권한 사용자가 처리합니다.
// 플랫폼 자체에 문제 보고
await cb.support.reportToPlatform({
subject: 'SDK 가 404 를 반환',
body: 'cb.database.queryData 호출이 갑자기 404 가 됩니다.',
category: 'sdk_bug'
})두 큐는 entity 가 분리되어 있어, Provider 앱이 자기 앱의 사용자 이슈만 보고 / ConnectBase 플랫폼 이슈는 별도 채널로 운영됩니다.