안녕하세요! 이번에는 코틀린을 사용한 안드로이드 앱 개발 강좌 시리즈의 일환으로, 태스크 관리 애플리케이션을 만들어 보겠습니다. 해당 애플리케이션은 사용자에게 할 일 목록을 관리할 수 있는 기능을 제공하며, 기본적인 CRUD(Create, Read, Update, Delete) 작업을 학습하는 데 도움이 될 것입니다.
1. 프로젝트 설정
먼저 새로운 안드로이드 프로젝트를 설정해야 합니다. Android Studio를 열고 다음 단계를 따릅니다:
- New Project를 클릭합니다.
 - Empty Activity을 선택하고 Next를 클릭합니다.
 - Project Name에 ‘TaskManager’를 입력하고 Language를 Kotlin으로 설정합니다.
 - 마지막으로 Finish를 클릭합니다.
 
2. 데이터 모델 정의
태스크를 나타내는 데이터 모델인 Task 클래스를 정의합니다. 이 클래스는 태스크의 제목, 설명 및 완료 여부를 속성으로 가집니다:
data class Task(
    var id: Long = 0,
    var title: String,
    var description: String,
    var isCompleted: Boolean = false
)
3. 사용자 인터페이스 설계
태스크 관리 애플리케이션의 기본 사용자 인터페이스는 다음과 같은 요소로 구성됩니다:
- 태스크 목록을 보여주는 RecyclerView
 - 새 태스크를 추가하는 버튼
 - 태스크 편집 및 삭제 옵션
 
먼저 activity_main.xml 파일을 수정하여 사용자 인터페이스를 구성합니다:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <Button
        android:id="@+id/btn_add_task"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="새 태스크 추가"/>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
</LinearLayout>
4. RecyclerView 설정
RecyclerView를 사용하여 태스크 목록을 표시합니다. 다음으로 TaskAdapter 클래스를 만들어서 태스크 항목들을 리스트형태로 보여주도록 설정합니다:
class TaskAdapter(private val tasks: List) : RecyclerView.Adapter() {
    inner class TaskViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val taskTitle: TextView = itemView.findViewById(R.id.task_title)
        val taskDescription: TextView = itemView.findViewById(R.id.task_description)
        val taskStatus: CheckBox = itemView.findViewById(R.id.task_status)
    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TaskViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.task_item, parent, false)
        return TaskViewHolder(view)
    }
    override fun onBindViewHolder(holder: TaskViewHolder, position: Int) {
        val task = tasks[position]
        holder.taskTitle.text = task.title
        holder.taskDescription.text = task.description
        holder.taskStatus.isChecked = task.isCompleted
        holder.taskStatus.setOnCheckedChangeListener { _, isChecked ->
            task.isCompleted = isChecked
        }
    }
    override fun getItemCount(): Int {
        return tasks.size
    }
}  
4.1. task_item.xml 레이아웃 만들기
RecyclerView의 각 항목을 위해 task_item.xml 파일을 아래와 같이 생성합니다:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <TextView
        android:id="@+id/task_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"/>
    <TextView
        android:id="@+id/task_description"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="14sp"/>
    <CheckBox
        android:id="@+id/task_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>
5. Task 추가 및 삭제 기능 구현
새로운 태스크를 추가하고 기존 태스크를 삭제하는 기능을 구현합니다. MainActivity에서 다음과 같이 코드를 추가합니다:
class MainActivity : AppCompatActivity() {
    private val taskList = mutableListOf()
    private lateinit var taskAdapter: TaskAdapter
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        taskAdapter = TaskAdapter(taskList)
        findViewById(R.id.recycler_view).apply {
            layoutManager = LinearLayoutManager(this@MainActivity)
            adapter = taskAdapter
        }
        findViewById  
5.1. 태스크 추가 다이얼로그 레이아웃 만들기
dialog_add_task.xml 파일을 생성하고 다음과 같이 내용을 작성합니다:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <EditText
        android:id="@+id/task_title_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="태스크 제목"/>
    <EditText
        android:id="@+id/task_description_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="태스크 설명"/>
</LinearLayout>
6. 데이터 저장 관리
앱을 재시작한 후에도 태스크 목록을 유지하기 위해 Room 데이터베이스를 활용하겠습니다. Room을 사용하면 SQLite 데이터베이스에 객체를 쉽게 저장하고 관리할 수 있습니다. 이 과정을 위한 세팅을 진행하겠습니다:
- Gradle 파일에 Room 종속성을 추가합니다:
 
dependencies {
    implementation "androidx.room:room-runtime:2.4.1"
    kapt "androidx.room:room-compiler:2.4.1"
}
6.1. Room 엔티티 정의
새로운 TaskEntity 클래스를 사용하여 Room 데이터베이스의 엔티티를 정의합니다:
@Entity(tableName = "tasks")
data class TaskEntity(
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    val title: String,
    val description: String,
    val isCompleted: Boolean = false
)
6.2. DAO 인터페이스 정의
Room 데이터베이스와 상호작용하기 위해 DAO(Data Access Object) 인터페이스를 정의합니다:
@Dao
interface TaskDao {
    @Insert
    suspend fun insert(task: TaskEntity)
    @Query("SELECT * FROM tasks")
    suspend fun getAllTasks(): List
    @Delete
    suspend fun delete(task: TaskEntity)
} 
6.3. 데이터베이스 클래스 정의
Room 데이터베이스를 상속받은 데이터베이스 클래스를 정의합니다:
@Database(entities = [TaskEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun taskDao(): TaskDao
}
6.4. 데이터베이스 초기화
메인 액티비티에서 데이터베이스를 초기화하고 태스크를 로드하는 로직을 추가합니다:
private lateinit var db: AppDatabase
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "task-database").build()
    // 태스크 로드
    loadTasks()
}
private fun loadTasks() {
    CoroutineScope(Dispatchers.IO).launch {
        val tasks = db.taskDao().getAllTasks()
        taskList.clear()
        taskList.addAll(tasks.map { Task(it.id, it.title, it.description, it.isCompleted) })
        runOnUiThread { taskAdapter.notifyDataSetChanged() }
    }
}
7. 태스크 삭제 기능 구현
사용자가 태스크를 삭제할 수 있도록 기능을 구현합니다. 아래 코드를 추가합니다:
override fun onBindViewHolder(holder: TaskViewHolder, position: Int) {
    // 기존 코드 유지
    holder.itemView.setOnLongClickListener {
        deleteTask(task)
        true
    }
}
private fun deleteTask(task: Task) {
    CoroutineScope(Dispatchers.IO).launch {
        db.taskDao().delete(TaskEntity(task.id, task.title, task.description, task.isCompleted))
        runOnUiThread {
            taskList.remove(task)
            taskAdapter.notifyDataSetChanged()
        }
    }
}
8. 마무리 및 추가 개선 사항
이로써 당신의 태스크 관리 애플리케이션의 기본 구조와 기능이 완성되었습니다! 이 애플리케이션은 사용자가 태스크 목록을 추가하고, 편집하고, 삭제할 수 있는 기능을 제공합니다. 앞으로의 계획을 포함하여 몇 가지 추가 개선 사항을 고려해볼 수 있습니다:
- 태스크 완료 시점의 날짜를 표시하는 기능 추가
 - 태스크 우선순위를 설정할 수 있는 기능 추가
 - 태스크 검색 및 필터 기능 추가
 - UI/UX 향상을 위한 디자인 개선
 
코틀린과 안드로이드 스튜디오를 활용하여 당신만의 태스크 관리 애플리케이션을 만들어 보는 것은 훌륭한 학습 경험이 될 것입니다. 이 강좌가 도움이 되었기를 바랍니다. 질문이나 추가적인 내용이 필요하다면 댓글로 남겨주세요!