Firebase 대체제이고 완전 로 유명한 백엔드 오픈소스 Appwrite 에 대해 알아보자. 웹앱, 모바일앱 개발시 많이 사용된다.
1. Appwrite 란?
Appwrite는 최신 웹 또는 모바일 애플리케이션을 구축하는 데 필요한 모든 핵심 API를 제공하는 서비스형 오픈 소스 백엔드 솔루션입니다. 다양한 Appwrite 서비스에는 대부분의 널리 사용되는 언어를 지원하는 인증, 데이터베이스, 저장소 및 기능을 관리하기 위한 API가 있습니다.
- Appwrite 1.0 출시 (2022년9월14일)
- Appwrite Console 2.0 출시 (2022년11월16일)
1) 특징
- 간단하고 유연함, 개인정보보호 및 보안
- 100% 오픈소스
- 도커 컨테이너로 배포 : 셀프 호스팅
- 클라우드 버전은 아직 준비중
2) 서비스
- Account 사용자 인증 및 계정 관리, 30여개의 OAuth 공급자 지원
- Users 프로젝트 사용자 관리
- Teams 팀과 사용자를 그룹화, 역활 관리
- Databases 데이터 관리 (MariaDB, MySQL)
- Storage 파일 CRUD
- Functions 격리된 환경에서 다양한 플랫폼의 함수를 실행
- Realtime 실시간 이벤트 발행과 구독
- Locale 로케일 데이터 관리
- Avatars 사용자, 국가, 아이콘, QR코드 등을 관리
3) 아키텍처
2. SvelteKit 예제
먼저 Web SDK 를 설치한다.
1
npm install appwrite
1) Appwrite Client
예제에서는 API Key 에 대한 언급이 없다. (없으면 권한 없다고 오류 나던데)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { Client as Appwrite, Databases, Account } from 'appwrite';
// .env 에서 환경변수를 가져오고
const server = {
endpoint: import.meta.env.VITE_APP_ENDPOINT.toString(),
project: import.meta.env.VITE_APP_PROJECT.toString(),
collection: import.meta.env.VITE_APP_COLLECTION_ID.toString(),
database: import.meta.env.VITE_APP_DATABASE_ID.toString(),
};
// 클라이언트를 생성하고, Account 와 Databases 클라이언트를 생성
const client = new Appwrite();
const account = new Account(client);
const database = new Databases(client);
// 클라이언트에 Appwrite 접속을 위한 환경변수를 설정
client.setEndpoint(server.endpoint).setProject(server.project);
// 기본 sdk 로 노출하고
const sdk = { account, database };
export { sdk, server };
2) +page.svelte
Appwrite 의 Svelte 예제는 SvelteKit 을 사용하지 않았다. (router 만 사용)
- onMount 로 페이지 로딩시 state 설정
- Appwrite Account 로 Session을 get 하고
- state 에 설정한다. (서버에 접속하여 세션을 생성한 후 저장)
- 세션이 정상적으로 생성되었으면 todos 페이지 이동
/todos
페이지 이동시 state.account 를 체크한다- conditionsFailed 인 경우, 루트(
/
) 경로로 이동
- conditionsFailed 인 경우, 루트(
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<script lang="ts">
import { onMount } from "svelte";
import { state } from "./store";
import { sdk } from "./appwrite";
const routes = {
"/": Landing,
"/login": Login,
"/signup": SignUp,
"/todos": wrap({
component: Todo,
conditions: [() => !!$state.account],
}),
"*": Landing,
};
onMount(async () => {
try {
const account = await sdk.account.get();
state.init(account);
} catch (error) {
state.init(null);
} finally {
if ($state.account) {
push("/todos");
}
}
});
</script>
<Alert />
<Router {routes} on:conditionsFailed={() => push("/")} />
3) 데이터베이스 서비스
Todos
- listDocuments 는 select 테이블과 같다.
- addTodo 는 insert 문인데, user 권한과 ID 가 필요하다.
- 레코드는 ID 와 json 데이터로 구성:
{ content, is_complete }
- account.$id 로 user Role 을 생성하고
- user Role 에 생성하는 레코드(Document)에 대한 권한을 부여한다.
- 레코드는 ID 와 json 데이터로 구성:
- updateDocument 시에도 User 권한을 명시해야 함
- deleteDocument 는 User 권한 없이도 실행이 되는건가?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
const createTodos = () => {
const { subscribe, update, set } = writable<Todo[]>([]);
return {
subscribe,
fetch: async () => {
const response: any = await sdk.database.listDocuments(server.database, server.collection);
console.log(response);
return set(response.documents);
},
addTodo: async (content: string) => {
const user = Role.user(get(state).account.$id);
const todo = await sdk.database.createDocument<Todo>(
server.database,
server.collection,
ID.unique(),
{
content,
isComplete: false,
},
[
Permission.read(user),
Permission.update(user),
Permission.delete(user),
]
);
return update((n) => [todo, ...n]);
},
removeTodo: async (todo: Todo) => {
await sdk.database.deleteDocument(server.database, server.collection, todo.$id);
return update((n) => n.filter((t) => t.$id !== todo.$id));
},
updateTodo: async (todo: Partial<Todo>) => {
const user = Role.user(get(state).account.$id);
await sdk.database.updateDocument(
server.database,
server.collection,
todo.$id,
todo,
[
Permission.read(user),
Permission.update(user),
Permission.delete(user),
]
);
return update((n) => {
const index = n.findIndex((t) => t.$id === todo.$id);
n[index] = {
...n[index],
...(<Todo>todo),
};
return n;
});
},
};
};
8. 참고자료
1) TailwindCSS 템플릿
2) Appwrite : Blog
3) SvelteKit Templates
- SwyxKit - SvelteKit Blog Starter
- Getting Started with Appwrite in NextJS by Building An App
- How to secure pages with HTTP Basic Auth using SvelteKit
- Simplify Authentication in SvelteKit
9. Review
- 아, 설명서와 예제가 너무 부실하다.
- 참고할 코드 조각들이 없어서 뭘 해보려고 해도 답답하다.
- 소스코드를 뒤져야 이해가 될 듯. 삽질을 많이 해야함
- User, Team 에 대해 Role 을 부여하여 Access Permission 을 제어하는데
- Account 가 User 와 무슨 관계인지 이해가 안되어 답답하다.
- Account 는 서비스 사용자이고, User 는 리소스 권한 대상을 가리키나?
- 데이터에 대한 보안을 철저히 한 것은 좋은데, 오버헤드가 커 보인다.
소회
- 원스톱으로 Docker 설치가 되는 것을 보고 ‘와~ 멋지다’ 라고 탄성했다.
- Console 이 바로 실행되는 것을 보고 ‘정말 좋다’ 라고 감탄했다.
- 그러나 …
- 내가 굳이 불편한 Appwrite 를 잡고 있을 필요가 있을까?
- Supabase 처럼 prisma 와 연계할 수 없다. (API로만 사용 가능)
- Appwrite 는 클라우드 서비스가 아직 없다.
- 월 5달러의 돈이 들더라도 신경쓸 필요 없는 Supabase 클라우드를 쓰자.
- 사용자가 많고 예제가 많은 것이 좋은 도구인데, Svelte 는 아직도 부족하다.
- 그냥 Flutter 로 가는게 맞지 않을까? Web App 도 배포할 수 있다는데.
- 계속 빙빙 돌다보니 시간만 축내고 성과는 내지 못하는 상태가 되었다.
끝! 읽어주셔서 감사합니다.