이 프로젝트는 MVVM 패턴 실습에 초점을 맞춘 간단한 TodoList 앱입니다.
Room 데이터베이스를 활용하여 로컬에 데이터를 저장하며, View ↔ ViewModel ↔ Model 간의 역할을 분리하는 것을 실습하였습니다.
- 언어 (Languages): Java, XML
- 개발 환경: Android Studio
- 아키텍처 (Architecture): MVVM (Model-View-ViewModel)
- 데이터베이스 (Database): Room
앱은 Room DB → Repository → ViewModel → View 흐름을 기반으로 동작합니다.
DB 접근을 위한 Dao
인터페이스를 정의합니다.
LiveData를 반환하여 데이터 변경 시 자동으로 UI에 반영되도록 합니다.
@Dao public interface TodoDao { @Query("SELECT * FROM TODOENTITY") LiveData<List<TodoEntity>> getAllData(); @Insert(onConflict = OnConflictStrategy.REPLACE) void setInsertTodo(TodoEntity todo); @Query("DELETE FROM TodoEntity") void deleteAllTodo(); @Query("DELETE FROM TodoEntity WHERE id = :id") void deleteDataWhereId(int id); }
2. Repository
Repository는 DB 접근 로직을 캡슐화하여 ViewModel이 데이터 소스를 직접 알지 않아도 되도록 합니다. 비동기 처리를 위해 Executor
를 사용합니다.
public class TodoRepository { private TodoDao dao; private LiveData<List<TodoEntity>> allData; private Executor executor = Executors.newSingleThreadExecutor(); public TodoRepository(Application application) { TodoDB db = TodoDB.getDatabase(application); dao = db.dao(); allData = dao.getAllData(); } public LiveData<List<TodoEntity>> getAllData() { return allData; } public void InsertData(TodoEntity todoEntity) { executor.execute(() -> dao.setInsertTodo(todoEntity)); } public void deleteAllData() { executor.execute(() -> dao.deleteAllTodo()); } public void deleteDataWhereId(int id) { executor.execute(() -> dao.deleteDataWhereId(id)); } }
3. ViewModel
ViewModel은 Repository를 통해 데이터를 가져오고, LiveData
로 관리하여 View에 전달합니다. UI 관련 로직과 데이터 보존 역할을 담당합니다.
public class Todo_ViewModel extends AndroidViewModel { private TodoRepository repository; private LiveData<List<TodoEntity>> liveData; public Todo_ViewModel(@NonNull Application application) { super(application); repository = new TodoRepository(application); liveData = repository.getAllData(); } public LiveData<List<TodoEntity>> getAllData() { return liveData; } public void insertData(TodoEntity data) { repository.InsertData(data); } public void deleteAllData() { repository.deleteAllData(); } public void deleteDataWhereId(int id) { repository.deleteDataWhereId(id); } }
4. View (MainActivity & Adapter)
View는 사용자 입력을 처리하고, ViewModel의 LiveData
를 관찰(Observer) 하여 자동으로 UI를 업데이트합니다.
viewModel.getAllData().observe(this, todoData -> { adapter.setData(todoData); adapter.notifyDataSetChanged(); });
➡️ LiveData 값이 변경될 때마다 RecyclerView UI가 자동 갱신됩니다.
binding.btnAdd.setOnClickListener(it -> { String todoList = binding.etTodo.getText().toString().trim(); if (todoList.isEmpty()) { Toast.makeText(this, "할일을 입력해주세요", Toast.LENGTH_SHORT).show(); } else { TodoEntity data = new TodoEntity(); data.setTodo(todoList); viewModel.insertData(data); } });
public interface OndeleteClickListener { void deleteClick(int id); }
@Override public void onBindViewHolder(@NonNull Todo_Adpater.Viewholder holder, int position) { holder.todo.setText(data.get(position).getTodo()); holder.delete.setOnClickListener(it -> { listener.deleteClick(data.get(position).getId()); }); }
➡️ 삭제 버튼 클릭 → Adapter 콜백 실행 → ViewModel의 deleteDataWhereId()
호출 → Repository → Room DB 삭제 → LiveData 변경 → UI 자동 반영
- 사용자 입력 (추가/삭제) →
MainActivity
→ViewModel
호출 - ViewModel →
Repository
통해 DB 요청 위임 - Repository →
Room DB
접근 (비동기 처리) - DB 변경 →
LiveData
업데이트 →ViewModel
→View
자동 반영
- Todo 추가
- Todo 삭제
- Room DB를 통한 데이터 영구 저장
- LiveData & Observer를 통한 실시간 UI 업데이트
사용자 입력 ↓ View (MainActivity / Adapter) ↓ ViewModel (Todo_ViewModel) ↓ Repository (TodoRepository) ↓ Room DB (TodoDao, TodoEntity) ↓ LiveData 업데이트 ↓ View 자동 반영 (Observer)