코틀린 안드로이드 앱개발 강좌, 개선된 할 일 목록 앱 만들기

안녕하세요! 이번 블로그 포스트에서는 코틀린을 활용하여 안드로이드 앱을 개발하는 방법을 다룰 것입니다. 구체적으로는 개선된 할 일 목록(Todo List) 앱을 만드는 과정을 자세히 설명합니다. 이 앱에는 기본적인 할 일 목록 기능 외에도 데이터베이스와 소셜 공유 기능 등을 추가하여 더욱 유용하게 활용할 수 있도록 개선하였습니다.

강좌 개요

이 강좌는 다음의 주요 항목으로 구성됩니다:

  • 프로젝트 설정
  • UI 구성
  • Room 데이터베이스 설정 및 구현
  • 할 일 추가 및 삭제 기능 구현
  • 할 일 완료 표시 기능 구현
  • 소셜 공유 기능 추가

1. 프로젝트 설정

안드로이드 스튜디오를 통해 새로운 프로젝트를 생성하겠습니다. 프로젝트의 이름은 ‘ImprovedTodoList’로 설정합니다. 이때, 언어는 Kotlin으로 선택하고 최적의 API 레벨을 설정해주세요.

1.1 Gradle 의존성 추가

Room 데이터베이스 및 기타 필요한 라이브러리를 추가해야 합니다. ‘build.gradle (Module: app)’ 파일의 dependencies 섹션에 다음 코드를 추가합니다:

dependencies {
    implementation "androidx.room:room-runtime:2.4.2"
    kapt "androidx.room:room-compiler:2.4.2"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
    implementation "androidx.activity:activity-ktx:1.4.0"
    implementation "androidx.appcompat:appcompat:1.4.1"
    implementation "com.google.android.material:material:1.5.0"
}

2. UI 구성

먼저 기본적인 UI부터 설계하겠습니다. activity_main.xml 파일에 아래와 같은 코드로 UI를 디자인합니다:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/editTextTodo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="할 일을 입력하세요"/>

    <Button
        android:id="@+id/buttonAdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="추가"/>

    <RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

</LinearLayout>

2.1 RecyclerView 및 Adapter 설정

할 일 목록을 보여주기 위해 RecyclerView를 사용할 것입니다. 아이템의 레이아웃을 정의하는 todo_item.xml 파일을 생성하여 아래와 같은 코드를 추가합니다:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="16dp">

    <CheckBox
        android:id="@+id/checkBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <TextView
        android:id="@+id/textViewTodo"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:paddingStart="8dp"
        android:paddingEnd="8dp"/>

    <Button
        android:id="@+id/buttonDelete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="삭제"/>

</LinearLayout>

3. Room 데이터베이스 설정 및 구현

이제 Room 데이터베이스를 설정하겠습니다. Todo라는 엔티티 클래스를 생성하여 데이터 테이블의 구조를 정의합니다:

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "todo_table")
data class Todo(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    val task: String,
    var isCompleted: Boolean = false
)

3.1 TodoDao 인터페이스 생성

Todo 엔티티와 상호작용할 DAO(Data Access Object)를 생성합니다:

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update

@Dao
interface TodoDao {
    @Insert
    suspend fun insert(todo: Todo)

    @Query("SELECT * FROM todo_table ORDER BY id ASC")
    suspend fun getAllTodos(): List

    @Update
    suspend fun update(todo: Todo)
}

3.2 RoomDatabase 클래스 생성

Room 데이터베이스를 정의하는 클래스를 생성합니다:

import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import android.content.Context

@Database(entities = [Todo::class], version = 1)
abstract class TodoDatabase : RoomDatabase() {
    abstract fun todoDao(): TodoDao

    companion object {
        @Volatile
        private var INSTANCE: TodoDatabase? = null

        fun getDatabase(context: Context): TodoDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    TodoDatabase::class.java,
                    "todo_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}

4. 할 일 추가 및 삭제 기능 구현

MainActivity에서 UI와 Room 데이터베이스를 연결하게 됩니다. 할 일을 추가하는 기능을 구현합니다:

import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    private val todoViewModel: TodoViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val adapter = TodoAdapter()
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(this)

        todoViewModel.allTodos.observe(this, Observer { todos ->
            todos?.let { adapter.submitList(it) }
        })

        buttonAdd.setOnClickListener {
            val task = editTextTodo.text.toString()
            if (task.isNotEmpty()) {
                todoViewModel.insert(Todo(task = task))
                editTextTodo.text.clear()
            }
        }
    }
}

5. 할 일 완료 표시 기능 구현

할 일을 완료로 표시하는 기능을 추가하겠습니다. TodoAdapter에 체크박스 상태를 반영하는 로직을 추가합니다:

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView

class TodoAdapter : ListAdapter(TodoDiffCallback()) {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.todo_item, parent, false)
        return TodoViewHolder(view)
    }

    override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
        val todo = getItem(position)
        holder.bind(todo)
        holder.checkBox.setOnCheckedChangeListener { _, isChecked ->
            todo.isCompleted = isChecked
            // 완료 상태 업데이트
            // viewModel.update(todo) // ViewModel에서 update 메서드 호출
        }
    }

    class TodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val textView: TextView = itemView.findViewById(R.id.textViewTodo)
        val checkBox: CheckBox = itemView.findViewById(R.id.checkBox)

        fun bind(todo: Todo) {
            textView.text = todo.task
            checkBox.isChecked = todo.isCompleted
        }
    }

    class TodoDiffCallback : DiffUtil.ItemCallback() {
        override fun areItemsTheSame(oldItem: Todo, newItem: Todo): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: Todo, newItem: Todo): Boolean {
            return oldItem == newItem
        }
    }
}

6. 소셜 공유 기능 추가

마지막으로, 할 일을 소셜 미디어에 공유하는 기능을 추가하겠습니다. Intent를 사용하여 공유 기능을 구현합니다:

import android.content.Intent
import android.view.View
import androidx.recyclerview.widget.RecyclerView

class TodoAdapter : ListAdapter(TodoDiffCallback()) {
    // 이전 코드 생략...

    override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
        val todo = getItem(position)
        holder.bind(todo)

        holder.itemView.setOnClickListener {
            val shareIntent = Intent().apply {
                action = Intent.ACTION_SEND
                putExtra(Intent.EXTRA_TEXT, todo.task)
                type = "text/plain"
            }
            holder.itemView.context.startActivity(Intent.createChooser(shareIntent, "공유할 곳을 선택하세요."))
        }
    }
}

결론

이제 개선된 할 일 목록 앱이 완성되었습니다. 이 앱은 이용자가 할 일을 추가하고, 완료 여부를 표시하며, 할 일을 삭제하고, 소셜 미디어에 공유할 수 있는 기능을 제공합니다. 코틀린과 Room 데이터베이스를 활용하여 효율적인 앱을 구축할 수 있었던 이번 강좌가 여러분에게 도움이 되었기를 바랍니다. 다음 포스트에서는 추가적인 기능이나 다른 앱 개발 기술에 대해 다루어보겠습니다. 감사합니다!