Skip to content

Android Architecture 기반 구조 리팩터링#116

Open
ExpeditionMoon wants to merge 16 commits into
devfrom
architecture/android
Open

Android Architecture 기반 구조 리팩터링#116
ExpeditionMoon wants to merge 16 commits into
devfrom
architecture/android

Conversation

@ExpeditionMoon

Copy link
Copy Markdown
Owner

작업 목적

Android Architecture 기준으로 프로젝트 구조를 리팩터링했습니다.

이번 작업은 Robert C. Martin의 Clean Architecture 적용 브랜치와 비교하기 위한 아키텍처 학습 및 구조 비교 목적의 작업입니다.

작업 브랜치

  • architecture/android

주요 변경 사항

1. UiState와 Effect 책임 분리

화면에 지속적으로 표시되는 상태와 일회성 이벤트를 분리했습니다.

  • UiState: loading, input, selected item, list data 등 화면 상태
  • Effect: snackbar, toast, 화면 이동, 카메라 이동, 저장 완료 등 일회성 이벤트

이를 통해 recomposition 상황에서 일회성 이벤트가 반복 처리되는 문제를 줄이고, ViewModel의 책임을 명확히 했습니다.

2. ViewModel coroutine 처리 정리

consult 작성 흐름을 중심으로 ViewModel coroutine 처리 방식을 정리했습니다.

  • 상담 생성/수정 흐름을 viewModelScopesuspend 함수 기반으로 정리
  • ViewModel 내부에서 별도 CoroutineScope를 직접 생성하지 않도록 개선
  • 상담 생성 후 알림 전송 흐름을 상담 생성 성공 처리 안에서 순차적으로 수행
  • 저장/조회/알림 전송 흐름을 읽기 쉽게 정리

3. ViewModel 의존성 기준 정리

Android Architecture 기준으로 단순 조회/검색 작업은 Repository를 직접 사용하도록 정리했습니다.

  • 단순 Repository 호출만 감싸는 UseCase 제거
  • 복잡한 비즈니스 규칙, 검증, 여러 Repository 조합이 필요한 UseCase는 유지
  • Android Architecture와 Clean Architecture의 차이를 비교할 수 있도록 의존성 기준 정리

4. data/domain 모듈 책임 정리

data 모듈에서는 Repository, DataSource, Mapper 책임을 점검했습니다.

  • Repository 결과 처리 기준 정리
  • Dispatcher 사용 기준 정리
  • DataResourceResult 중심의 결과 반환 기준 통일
  • DTO 구조 변경은 이번 범위에서 제외

domain 모듈에서는 단순 UseCase를 정리하고 Repository 반환 타입 기준을 점검했습니다.

5. component-ui 공통 UI 책임 정리

component-ui가 domain 모델에 직접 의존하지 않도록 정리했습니다.

  • PharmacyUiModel
  • PharmacistUiModel

을 추가하여 공통 UI는 화면 표시용 모델만 알도록 변경했습니다.

또한 PharmacySelector의 검색어 상태를 hoisting하여 공통 UI가 상태를 직접 소유하지 않도록 수정했습니다.

6. navigation callback 기준 정리

feature screen에서 NavController를 직접 사용하지 않도록 정리했습니다.

  • Screen은 navigation callback만 받도록 변경
  • 실제 navigate, popBackStack, navigateUp 처리는 NavGraphBuilder 또는 app shell에서 담당
  • ViewModel은 navigation을 직접 실행하지 않고 Effect로 이동 의도만 전달

7. app 모듈 최종 정리

사용되지 않는 TopBar route helper를 제거했습니다.

  • TitleBarCustom.kt 삭제

최종 검증

아래 명령어로 전체 debug 빌드가 성공하는 것을 확인했습니다.

./gradlew clean assembleDebug

결과:

BUILD SUCCESSFUL

비교 예정

이 PR은 Android Architecture 적용 결과입니다.

이후 별도 브랜치에서 Robert C. Martin의 Clean Architecture 기준으로 같은 프로젝트를 리팩터링하고, 두 구조의 차이를 비교할 예정입니다.

  • Android Architecture 브랜치: architecture/android
  • Clean Architecture 브랜치: architecture/clean

관련 이슈

Refs #115

consult 패키지의 화면 상태와 일회성 이벤트 처리 방식을 분리했습니다.

UiState는 화면에 유지되는 상태만 담당하고, 스낵바/화면 이동/카메라 이동/상담 생성 완료 같은 일회성 이벤트는 Effect가 담당하도록 정리했습니다.

Refs #115
consult ViewModel의 coroutine 실행 흐름을 viewModelScope 기준으로 정리했습니다.
반복 호출될 수 있는 목록/상세/약사 조회 작업은 기존 Job을 취소한 뒤 새 작업을 시작하도록 변경했습니다.
검색 Flow는 collectLatest의 취소가 실제 검색 작업까지 전달되도록 suspend 함수 기반으로 정리했습니다.

Refs #115
ConsultUseCases 묶음 의존성을 제거하고 ViewModel이 필요한 UseCase와 Repository를 직접 주입받도록 정리했습니다.
복잡한 비즈니스 흐름은 UseCase로 유지하고, 단순 데이터 접근은 Repository를 직접 사용하도록 의존성 기준을 명확히 했습니다.

Refs #115
로그인, 회원가입, 마이페이지의 일회성 이벤트를 Effect로 분리했습니다.

UiState는 화면에 유지되는 상태만 담당하고, 스낵바/화면 이동/카메라 이동 같은 일회성 이벤트는 Effect에서 처리하도록 정리했습니다.
회원가입 약국 검색 화면은 ViewModel 직접 참조 대신 state, effect, callback을 받도록 변경했습니다.

Refs #115
복약 화면의 일회성 이벤트를 MedicationEffect로 분리했습니다.
UiState는 화면에 유지되는 상태만 담당하고, 스낵바 메시지와 복약 등록 완료 후 화면 이동은 Effect에서 처리하도록 정리했습니다.
복약 히스토리 화면의 중복 초기 로딩 호출을 제거했습니다.

Refs #115
consult와 profile ViewModel에서 단순 조회/검색 UseCase 의존성을 Repository 직접 주입으로 변경했습니다.
약국 검색, 복약 조회, 복약 기록 조회는 각 Repository를 직접 사용하도록 정리했습니다.
불필요해진 단순 UseCase 제거는 domain 모듈 정리 단계에서 별도로 진행할 예정입니다.

Refs #115
검색 화면의 일회성 메시지를 SearchEffect로 분리했습니다.
UiState는 검색 결과와 로딩 상태만 담당하고, Toast 메시지는 Effect에서 처리하도록 정리했습니다.

Refs #115
DDI 화면의 userMessage를 errorMessage로 변경해 화면에 유지되는 오류 상태임을 명확히 했습니다.
사용하지 않는 메시지 초기화 함수를 제거하고, DDI 화면 상태 수집을 collectAsStateWithLifecycle로 변경했습니다.

Refs #115
처방전 OCR 화면의 로딩 상태를 PrescriptionUiState로 정리했습니다.
화면 이동 이벤트는 PrescriptionEffect로 이름을 변경하고 effect 흐름으로 수집하도록 정리했습니다.

Refs #115
home 화면의 닉네임 상태를 HomeUiState로 통합했습니다.
search 화면의 검색어 상태를 SearchUiState에 포함해 화면에서 uiState만 수집하도록 정리했습니다.

Refs #115
data 모듈 Repository의 DataResourceResult 변환과 Dispatcher 적용 방식을 공통화했습니다.
suspend 작업과 Flow 작업의 Loading/Success/Failure 처리 기준을 RepositoryResult로 정리했습니다.
AI, 위치, 약 검색, 이미지 업로드 등 IO 작업에 dispatcher 적용 기준을 명확히 했습니다.
누락되어 있던 UserLifeStyle Repository/DataSource Hilt 바인딩을 추가했습니다.

Refs #115
사용되지 않는 단순 조회/정렬 UseCase를 제거했습니다.
DrugSearchRepository의 반환 타입을 Result에서 DataResourceResult로 변경해 Repository 결과 처리 기준을 통일했습니다.
search ViewModel은 변경된 Repository 반환 타입에 맞춰 Loading/Success/Failure를 처리하도록 정리했습니다.

Refs #115
PharmacySelector가 내부에서 검색어 상태를 직접 보관하지 않도록 변경했습니다.
검색어 값과 변경 콜백은 호출자가 전달하도록 정리하고, 공통 UI에서 SharedFlow를 직접 수집하던 cameraMoveEvent API를 제거했습니다.
사용하지 않는 PharmacyMap API를 제거하고 consult, profile 호출부를 변경된 PharmacySelector API에 맞게 수정했습니다.

Refs #115
component-ui 공통 컴포넌트가 domain 모델을 직접 참조하지 않도록 PharmacyUiModel과 PharmacistUiModel을 추가했습니다.
PharmacySelector, PharmacyMap, PharmacyListItem, PharmacistListItem은 UI 전용 모델을 사용하도록 변경했습니다.
consult와 profile 호출부에서는 domain 모델을 UI 모델로 변환해 전달하도록 정리하고, component-ui의 domain 모듈 의존성을 제거했습니다.

Refs #115
feature Screen에서 NavController 직접 의존을 제거하고 navigation graph에서 callback으로 화면 이동을 연결하도록 정리했습니다.
consult, home, profile, prescription, search 화면의 navigate/popBackStack 처리를 상위 NavGraphBuilder로 이동했습니다.
Consult 목록 refresh 처리는 savedStateHandle 접근을 navigation graph로 옮기고 Screen에는 shouldRefresh와 onRefreshHandled callback만 전달하도록 변경했습니다.

Refs #115
사용되지 않는 TitleBarCustom을 삭제했습니다.
TopBar route 처리는 각 화면과 navigation callback 구조로 정리되어 있어 별도 NavController 기반 helper가 필요하지 않도록 정리했습니다.

Refs #115
@ExpeditionMoon ExpeditionMoon added the type: architecture 구조 설계 관련 label Jun 1, 2026
Repository owner deleted a comment from chatgpt-codex-connector Bot Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: architecture 구조 설계 관련

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant