Android MVP 패턴 실무 적용: 노트패드 앱 리팩터링

1. MVP 아키텍처 개요

MVP(Model-View-Presenter)는 애플리케이션을 세 가지 주요 구성 요소로 분리하는 아키텍처 패턴입니다.

  • Model: 데이터 처리 계층. 데이터 저장 및 검색을 담당
  • View: UI 표시 계층. 사용자 상호작용을 처리
  • Presenter: 비즈니스 로직 계층. View와 Model을 연결

1.1 주요 장점

  1. 역할 분리
    • View: UI 표시에만 집중
    • Model: 데이터 조작에만 집중
    • Presenter: 비즈니스 로직 처리
  2. 결합도 감소
    • 인터페이스 기반 통신
    • 각 계층 독립 개발 가능
    • 단위 테스트 용이
  3. 코드 유지보수성 향상
    • 로직 구조화
    • 모듈 간 영향 최소화
    • 재사용 가능성 증대

1.2 아키텍처 구조도

View <--> Presenter <--> Model
↑             ↑           ↑
Interface  Interface   Interface

2. 노트패드 앱 분석 및 리팩터링

기존 코드: https://github.com/yape29/android_notePad/tree/master

리팩터링 코드: https://github.com/yape29/android_notePad/tree/20241209-MVP설계모델재구성응용

2.1 기존 구조 분석

2.1.1 NotesList.java 분석

View 부분:

  • 노트 목록 레이아웃(notelist_main.xml)
  • ListView로 노트 표시
  • FloatingActionButton으로 노트 추가
  • 검색 입력창 및 버튼
  • 분류 표시 버튼
  • 즐겨찾기 기능 버튼

Presenter 로직(추출 필요):

  • 노트 목록 데이터 로드
  • 검색 처리
  • 분류 표시
  • 즐겨찾기 관리
  • 노트 삭제
  • 새 노트 생성

2.2 컨트랙트 인터페이스 정의

public interface NotesContract {
    interface NotesView {
        void showNotes(Cursor cursor);
        void showError(String message);
        void openNoteEditor(Uri uri);
        void finishView();
        void refreshView();
        void updateListView(Cursor cursor);
        void showDeleteButton(boolean show);
    }

    interface NotesPresenter {
        void loadNotes();
        void loadNotesInClassify(String classifyName);
        void loadClassifies();
        void deleteNote(Uri noteUri);
        void createNote();
        void onDestroy();
    }

    interface NoteEditorView {
        void showNote(Cursor cursor);
        void showError(String message);
        void finishView();
        void updateTitle(String title);
        void updateText(String text);
        void showClassifyDialog(Cursor classifyCursor, String currentClassify);
    }

    interface NoteEditorPresenter {
        void loadNote(Uri uri);
        void saveNote(String text, String title);
        void deleteNote();
        void loadClassifies();
        void updateNoteClassify(String classify);
        void createNewClassify(String classifyName);
        void onDestroy();
    }
}

2.3 Presenter 구현

NotesPresenter 구현

public class NotesPresenter implements NotesContract.NotesPresenter {
    private NotesContract.NotesView viewRef;
    private Context contextInstance;

    public NotesPresenter(NotesContract.NotesView view, Context context) {
        this.viewRef = view;
        this.contextInstance = context;
    }

    @Override
    public void loadNotes() {
        try {
            String[] projection = {
                NotePad.Notes._ID,
                NotePad.Notes.COLUMN_NAME_TITLE,
                NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE,
                NotePad.Notes.COLUMN_NAME_STAR
            };
            
            Cursor dataCursor = contextInstance.getContentResolver().query(
                NotePad.Notes.CONTENT_URI,
                projection,
                null,
                null,
                NotePad.Notes.DEFAULT_SORT_ORDER
            );
            
            viewRef.updateListView(dataCursor);
        } catch (Exception e) {
            viewRef.showError("데이터 로드 실패");
        }
    }

    // 나머지 메서드 구현...
}

2.4 Activity 수정

NotesList.java 수정

public class NotesList extends ListActivity implements NotesContract.NotesView {
    private NotesContract.NotesPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        presenter = new NotesPresenter(this, this);
        
        setContentView(R.layout.notelist_main);

        FloatingActionButton addBtn = findViewById(R.id.add_note);
        addBtn.setOnClickListener(v -> presenter.createNote());

        presenter.loadNotes();
    }

    @Override
    public void showNotes(Cursor cursor) {
        // 노트 목록 표시 구현...
    }

    // 다른 인터페이스 메서드 구현...
}

3. 리팩터링 효과 분석

3.1 코드 구조 비교

리팩터링 전:

- NotesList.java (UI + 로직)
  - View: 리스트 표시, 버튼 배치
  - Presenter: 데이터 로드, 이벤트 처리
- NoteEditor.java (UI + 로직)
  - View: 편집 인터페이스, 버튼 배치
  - Presenter: 노트 처리, 분류 관리
- NotePadProvider.java (데이터 처리)

리팩터링 후:

- contract/
  - NotesContract.java (인터페이스 정의)
- presenter/
  - NotesPresenter.java (목록 로직)
  - NoteEditorPresenter.java (편집 로직)
- view/
  - NotesList.java (UI 전용)
  - NoteEditor.java (UI 전용)
- provider/
  - NotePadProvider.java (데이터 처리)

3.2 장점

  1. 역할 명확화
    • Activity는 UI 작업만 담당
    • Presenter는 비즈니스 로직 처리
    • Provider는 데이터 저장 집중
  2. 코드 분리
    • 인터페이스 기반 통신
    • 모듈 간 독립성
    • 테스트 용이성
  3. 유지보수성 향상
    • 구조 명확
    • 변경 시 영향 최소화
    • 재사용 가능성 증가

4. 결론

MVP 패턴을 활용한 노트패드 앱 리팩터링을 통해:

  • 기존 코드의 View와 Presenter 역할 분석
  • 명확한 계약 인터페이스 정의
  • 코드 분리 및 역할 명확화
  • 유지보수성과 테스트 가능성 향상

MVP 패턴은 코드량 증가를 동반하지만, 구조적 이점이 크며 실제 프로젝트에서는 프로젝트 규모와 요구사항에 따라 적절한 아키텍처 선택이 중요합니다.

태그: Android Architecture Presenter Pattern interface design Code Refactoring Android Development

7월 18일 08:00에 게시됨