본문 바로가기
잡(job)기술

Appwrite Android Quick Start 정리

by 무니이구나 2025. 5. 15.


Android 프로젝트를 Appwrite와 연동하기 위해 진행한 Quick Start 작업을 진행했지만 시행착오들을 겪었다. 첫 시도라 중간중간 기억해둬야 하겠다 싶은 개념이나 작업들이 있어, 이를 기록해두기 위해서 이 글을 작성한다.

---

### 1. Android 프로젝트 생성

1. Android Studio에서 **New Project** 클릭
2. **Empty Activity** 템플릿 선택 후 **Next**
3. 앱 이름(App name)과 패키지 이름(Package name) 입력. 패키지 이름이 Appwrite 프로젝트의 Android 플랫폼과 연동이 되기 때문에 중요하다.

   * 예: `cohttp://m.example.myapp`
4. **Finish** 클릭하여 프로젝트 생성

---

### 2. Appwrite 프로젝트 및 Android 플랫폼 등록

1. 브라우저에서 Appwrite Console([https://cloud.appwrite.io](https://cloud.appwrite.io)) 접속 후 로그인

2. **Create Project** 또는 기존 프로젝트 선택

3. 프로젝트 대시보드에서 **Settings > Platforms** 이동

4. **Add Platform → Android** 클릭

5. App 이름과 **패키지 이름** 입력

   * 패키지 이름은 Android Studio에서 설정한 `cohttp://m.example.myapp`과 동일하게 입력

6. **Save** 클릭

---

### 3. Appwrite SDK 추가

1. `app/build.gradle.kts` (또는 `app/build.gradle`) 파일 열기
2. `dependencies` 블록에 다음 한 줄 추가:

   ```kotlin
   implementation("io.appwrite:sdk-for-android:7.0.1")
   ```
3. **Sync Now** 클릭하여 Gradle Sync 실행

---

### 4. Appwrite 클라이언트 싱글톤 구현 및 구성 요소 설명

이 단계에서는 Appwrite SDK를 앱 전역에서 편리하게 사용하기 위해 **싱글톤 패턴**을 적용한다. `Appwrite.kt` 객체 하나에 주요 구성 요소를 모아 두어, 어디서든 `Appwrite.init(context)` 한 번으로 초기화 후 바로 호출할 수 있다.

#### Appwrite SDK 주요 구성 요소

* **Client**: Appwrite 서버와 통신하는 HTTP 클라이언트이다.

  * `setEndpoint()`, `setProject()` 메서드를 이용해 요청할 기본 URL과 프로젝트 ID를 설정한다.
* **ID**: 고유 식별자(ID)를 생성하는 유틸리티 클래스이다.

  * `ID.unique()` 등을 통해 사용자(User), 문서(Document) 등 리소스에 사용할 ID를 자동 생성한다.
* **Account**: 사용자 인증 및 세션 관리를 담당하는 서비스 클래스이다.

  * 회원가입, 로그인, 로그아웃, 사용자 정보 조회 등의 메서드를 제공한다.

#### Region 및 Project ID 확인 방법

* **Region**: Appwrite 인스턴스가 호스팅된 위치를 의미한다. 프로젝트를 생성할 때 선택할 수 있도록 메뉴가 나타나는데, 아직은 선택권이 없는 것 같다. Frankfurt만 사용할 수 있었다.

  * Appwrite 콘솔에 로그인 후, 프로젝트 대시보드로 진입하면 프로젝트 이름의 우측 편에 Frankfurt 라는 Region을 확인할 수 있고, 클립보드에 복사도 가능하도록 되어 있다.

* **Project ID**: Appwrite 콘솔에서 프로젝트를 식별하는 고유 ID이다.

  1. Region을 확인할 때와 마찬가지로 대시 보드에서 프로젝트 이름의 우측에 16진수 형태의 수로 이뤄진 값이 보인다. Region과 마찬가지로 클립보드에 복사도 가능하다.

#### 싱글톤 구현 및 주요 메서드

1. **파일 생성**: `Appwrite.kt`를 프로젝트 루트 패키지(예: `cohttp://m.example.myapp`)에 추가. 공식 문서의 quick start 예제에 간단한 설명을 추가해뒀다.

2. **코드 예시**:

   ```kotlin
   package cohttp://m.example.myapp

   import android.content.Context
   import io.appwrite.Client
   import io.appwrite.ID
   import io.appwrite.services.Account

   object Appwrite {
       // Appwrite 서버와 통신할 클라이언트 객체
       lateinit var client: Client
       // 인증, 세션 관리 등을 처리하는 Account 서비스 객체
       lateinit var account: Account

       /**
        * 앱 시작 시 호출하여 Client와 Account를 초기화한다.
        * - context: 애플리케이션 Context
        * - setEndpoint: Appwrite 엔드포인트 URL (지역 및 버전 포함)
        * - setProject: 프로젝트 ID 설정
        */
       fun init(context: Context) {
           client = Client(context)
               .setEndpoint("https://<REGION>.cloud.appwrite.io/v1")
               .setProject("[PROJECT_ID]")
           account = Account(client)
       }

       /**
        * 이메일/비밀번호로 사용자 회원가입을 수행한다.
        * - userId: 고유 ID 생성 (ID 유틸 사용)
        * - email: 사용자 이메일
        * - password: 사용자 패스워드
        * 반환: 생성된 User 객체
        */
       suspend fun onRegister(email: String, password: String) =
           account.create(
               userId = ID.unique(),
               email = email,
               password = password
           )

       /**
        * 이메일/비밀번호로 로그인 시 세션을 생성한다.
        * - email: 사용자 이메일
        * - password: 사용자 패스워드
        * 반환: 생성된 Session 객체
        */
       suspend fun onLogin(email: String, password: String) =
           account.createEmailPasswordSession(email, password)

       /**
        * 현재 활성화된 세션을 삭제하여 로그아웃 처리한다.
        */
       suspend fun onLogout() {
           account.deleteSession("current")
       }
   }
   ```

3. **용도 요약**:

   * `init`: 앱 시작 시 Appwrite 서버와의 연결을 설정
   * `onRegister`: 신규 사용자 등록 (회원가입)
   * `onLogin`: 기존 사용자 로그인 및 세션 생성
   * `onLogout`: 로그인 세션 종료 및 로그아웃 처리

이제 이 싱글톤 객체를 통해 어느 액티비티나 컴포넌트에서든 손쉽게 Appwrite 기능을 호출할 수 있다.

### 5. 간단한 로그인/회원가입 화면 구현

1. `MainActivity.kt`에 Compose UI 추가
2. 아래 예제에서 **package** 선언은 각자가 만든 것으로 변경:

   ```kotlin
   package cohttp://m.example.myapp

   import android.os.Bundle
   import androidx.activity.ComponentActivity
   import androidx.activity.compose.setContent
   import androidx.compose.foundation.layout.*
   import androidx.compose.foundation.text.KeyboardOptions
   import androidx.compose.material3.*
   import androidx.compose.runtime.*
   import androidx.compose.ui.Modifier
   import androidx.compose.ui.text.input.PasswordVisualTransformation
   import androidx.compose.ui.text.input.KeyboardType
   import androidx.compose.ui.unit.dp
   import kotlinx.coroutines.launch

   class MainActivity : ComponentActivity() {
       @OptIn(ExperimentalMaterial3Api::class)
       override fun onCreate(savedInstanceState: Bundle?) {
           super.onCreate(savedInstanceState)
           Appwrite.init(applicationContext)

           setContent {
               var email by remember { mutableStateOf("") }
               var password by remember { mutableStateOf("") }
               var user by remember { mutableStateOf<String?>(null) }
               val scope = rememberCoroutineScope()

               Surface(
                   modifier = Modifier.fillMaxSize(),
                   color = MaterialTheme.colorScheme.background
               ) {
                   Column(
                       modifier = Modifier
                           .fillMaxSize()
                           .padding(16.dp),
                       verticalArrangement = Arrangement.Center
                   ) {
                       if (user != null) {
                           Text(text = "Logged in as $user")
                           Button(onClick = {
                               scope.launch { Appwrite.onLogout(); user = null }
                           }, modifier = Modifier.padding(top = 16.dp)) {
                               Text("Logout")
                           }
                       } else {
                           TextField(
                               value = email,
                               onValueChange = { email = it },
                               label = { Text("Email") },
                               keyboardOptions = KeyboardOptions(
                                   keyboardType = KeyboardType.Email
                               ),
                               modifier = Modifier.fillMaxWidth()
                           )
                           TextField(
                               value = password,
                               onValueChange = { password = it },
                               label = { Text("Password") },
                               visualTransformation = PasswordVisualTransformation(),
                               modifier = Modifier
                                   .fillMaxWidth()
                                   .padding(top = 8.dp)
                           )
                           Row(
                               modifier = Modifier
                                   .fillMaxWidth()
                                   .padding(top = 16.dp),
                               horizontalArrangement = Arrangement.SpaceEvenly
                           ) {
                               Button(onClick = {
                                   scope.launch {
                                       Appwrite.onLogin(email, password)
                                       user = email
                                   }
                               }) { Text("Login") }
                               Button(onClick = {
                                   scope.launch { Appwrite.onRegister(email, password) }
                               }) { Text("Register") }
                           }
                       }
                   }
               }
           }
       }
   }
   ```

---

### 6. 실행 및 확인

1. 에뮬레이터 또는 실제 기기에서 앱 실행
2. **Register** → **Login** → **Logout** 순으로 기능 동작 확인