오늘은 안드로이드 스튜디오에서 레파지토리 기능을 구현 중 context 가 필요하여 관련 내용을 공부했습니다.
공부한 내용은 아래 목차와 같이 포스팅할 예정입니다.
- MVVM with Repository
- context
- Repository 에서 context 사용하기
이번 포스팅에서는 1. MVVM with Repository 에 관해 알아보도록 합니다.
처음 이 디자인 패턴을 접했을 때는 개념이 너무 안와닿았아서 최대한 쉽게 설명해보려 합니다.
먼저 MVVM 이 무엇의 약자인 지, 왜 필요한지에 대해 생각해보아야겠죠.
그림순으로 나열한다면 View - ViewModel - Model 이 되겠네요.
간단하게 설명해보자면 한 곳에서 구현되던 기능을 View, ViewModel, Model 로 각각 기능을 나누어 구현하겠다. 라는 것 입니다.
저희가 더 나은 프로그램을 만들기 위해 노력하는 게 어떤게 있을까요?
다음 사람이 내 코드를 보기 쉽게 만들고, 재사용 및 유지보수가 용이하고, 확장성있게 만들려는 노력을 합니다.
이를 위해서는 코드를 보다 깨끗하고 정확하며 읽기 쉽고, 유지 관리 가능하게 해야겠죠 !
그러기 위해 코드 각각을 분리 (의존성 분리 ) 하는 것 입니다.
예를 들어 하드디스크에 파일을 보관하고 가족들이 필요할 때 마다 확인한다고 생각해봅시다.
파일 저장 형태 - 하드디스크 내부
우리집 강아지.jpg, 내 사진.jpg, 생일파티영상.mp4, 내가 좋아하는 노래.mp3, 언니 사진.jpg, 내 이력서.hwp, 엄마 사진.jpg, 가족여행사진.jpg, 언니 이력서.hwp, 아빠 사진.jpg
이러한 여러 파일을 위와 같이 저장해두면 어떻게 될까요?
모든 가족들이 파일 이름 하나하나를 비교하며 자신이 현재 필요한 문서를 찾기 위해 전체를 확인해볼 것 입니다.
운이 좋으면 바로 찾고 운이 안좋으면 파일 목록 하나하나를 확인해보며 나올 때 까지 찾아야겠지요,
하지만 우리는 어떻게 하면 더 효율적일 지 이미 알고 있습니다.
폴더로 구분하는거죠!
파일 저장 형태
'나' 폴더 - 내 사진.jpg, 내가 좋아하는 노래.mp3, 내 이력서.hwp
'언니' 폴더 - 언니 사진.jpg, 언니 이력서.hwp
'엄마' 폴더 - 엄마 사진.jpg
'아빠' 폴더 - 아빠 사진.jpg
'강아지' 폴더 - 우리집 강아지.jpg
'가족' 폴더 - 생일파티영상.mp4, 가족여행사진.jpg
그럼 누가 사용하든 자신의 폴더에 들어가 원하는 파일을 찾을 수 있습니다.
여기서 더 효율적이게 만들려면 폴더 내의 파일명으로도 분리가 가능하겠죠!
파일 저장 형태
'나' 폴더
- '사진' : 내 사진.jpg
- '음악' : 내가 좋아하는 노래.mp3
- '한글' : 내 이력서.hwp
이런 식으로 말이죠!
우리는 그럼 이를 코드에 적용해서 한 곳에 모여있던 코드들을 위와 같이 분리하여 보기 쉽게 만들어 줄 수 있습니다.
일반적인 상황을 가정해보겠습니다.
- 유저가 아이디와 비밀번호를 입력
- 유저가 화면 내의 로그인 버튼을 클릭
- 서버에서 해당 유저의 아이디와 비밀번호가 일치하는 지 확인
- 결과값 전송 ( Success or Fail 로 가정 )
- 서버에서 받아온 success 신호로 로그인 성공 시 실행되는 코드 실행
위와 같은 상황일 때 LoginActivity 를 생성했다고 가정하고 자바로 간단히 작성해보자면
// string 에서 .equals 를 사용해야하지만 가독성을 위해 == 로 표기했습니다.
// 서버에 id, pw 확인 후 응답값을 returnValue 로 가정
loginbtn.setOnClickListner{ // 1. 로그인 버튼 클릭
// 2. 서버와 통신
서버 통신 코드 생략 ..
// 3. 결과값
returnValue = jsonResponse.getBoolean("success");
// 4. 결과값으로 구분하여 구현
if(returnlValue){
Intent intent = new Intent(MainAcitivty.this, LoginActivity.class);
startActivity(intent);
}else {
Toast.makeText(getApplicationContext(),"로그인에 실패하였습니다.",Toast.LENGTH_SHORT).show();
return;
}
} ;
살짝 복잡한 느낌이 있지요?
여기서 메소드로 코드를 분리시켜 보겠습니다.
loginbtn.setOnClickListner{ //1. 로그인 버튼 클릭
// 2. 서버와 통신한 값을 boolean 으로 받아온 후 해당 코드 실행
if ( userLogin(id, password) ) {
StartMainActivity();
} else { // 오류 발생
}
} ;
// 2-메소드. 통신 후 결과값
public static boolean userLogin(String id, String pw){
// 서버와 통신
코드 생략 ...
// 서버에 id, pw 확인 후 응답값을 returnValue 로 가정
returnValue = jsonResponse.getBoolean("success");
return returnValue ;
}
}
public static void startMainActivity(){
Intent intent = new Intent(MainAcitivty.this, LoginActivity.class);
startActivity(intent);
}
자, 각각이 어떤 역할을 하는 지 이해하기 훨씬 간단해졌죠 ?!
예를 들어 자동 로그인 기능을 추가하고자 할 때 메인액티비티 intent 코드를 또 작성할 필요없이 startMainActivity 메소드를 사용하면 됩니다!
어느정도 정리는 되었지만 그래도 LoginAcitivty에 모든 동작 코드가 구현되어 있습니다.
아무리 메소드로 나누어 코드를 처리하려 해도 코드가 계속 쌓인다면 읽기 힘들어 질 것입니다.
물론 동작에 있어 문제는 없지만 체계적인 구조가 전혀 없어 추후에 유지 보수도 어렵겠지요.
그렇기 때문에 디자인 패턴을 적용하여 역할을 분리시키는 것이 필요하고,
저희는 오늘 그 중 하나인 MVVM에 대해 공부하는 것 입니다.
그래서 MVVM 는 독립적으로 구현을 하는데요, 다시 위 상황으로 설명해보겠습니다.
- 유저가 아이디와 비밀번호를 입력
- 유저가 화면 내의 로그인 버튼(view)을 클릭
1-1. view : 이벤트 발생, viewmodel 에 데이터 요청
1-2. viewModel : Model에게 데이터 요청- 서버에서 해당 유저의 아이디와 비밀번호가 일치하는 지 확인
2-1. Model : View Model에서 요청한 값 반환 ( success or fail )- 서버에서 받아온 success 신호로 로그인 성공 시 실행되는 코드 실행
3-1. ViewModel : Model로 부터 받은 값을 라이브 데이터에 저장
3-2. View : LiveData 감지, 저장된 값을 받아옴
이렇게 구현됩니다! 갑자기 너무 어려워졌나요 .. ? 정상입니다 !
더 쉽게 각각의 역할을 한 번 이해해봅시다.
View
Acitivity / Fragment 로 나타낼 수 있습니다.
사용자와 상호작용 (클릭, 입력 등)을 뷰모델에 전달합니다.
ViewModel의 데이터를 관찰하여 UI를 갱신합니다.
ViewModel
View가 요청한 데이터를 Model로 요청합니다.
Model로 부터 요청한 데이터를 받습니다.
Model
ViewModel이 요청한 데이터를 반환합니다.
데이터에 접근하는 ( 내부 DB , 백엔드 API 호출 등) 역할
처음엔 너무 이해가 안갔는데 틀을 딱 잡아준 도표가 있어 저도 적용해보았습니다.
(그려주신 분 너무 감사합니다)
이로써 뷰모델은 자신의 로직에만 집중할 수 있게 되는 것이지요!
관리할 코드와 파일이 많아지지만, 모듈화가 명확해지고 모듈의 교체도 유연해진다는 장점이 있지요!
장점
- View 가 ViewModel 의 Data 를 관찰하고 있으므로 UI 업데이트가 간편
- ViewModel이 데이터를 홀드하고 있으므로 Memory Leak 발생 가능성 배제
(View 가 직접 Model 에 접근하지 않아 Activity / Fragment 라이프 사이클에 의존하지 않기 때문)- 기능별 모듈화가 잘 되어 유지 보수에 용이 (e.g. ViewModel 재사용 및 DB 교체 등의 작업이 편리함)
여기까지 오셨다면 MVVM을 거의 삼켜먹었다고 할 수 있습니다.
어렵게 느껴지신다면 바로바로 정상입니다.
기존의 LoginActivity 에서 MVVM 을 적용한다면
View : LoginAcitivity
ViewModel : LoginViewModel
Model : LoginModel
이렇게 두 개의 파일을 더 생성하여 처리를 해야하는거죠!
MVVM 패턴을 적용한 로그인 코드는 다음에 기회가 된다면 다뤄보도록 하겠습니다.
그런데 MVVM을 검색해서 찾다보니 Repository 라는 기능도 알게 되었습니다.
MVVM 디자인 패턴 사용 시, Repository 라는 인터페이스가 존재합니다.
이전에 설명했던 그림의 ViewModel 과 Model 사이에 Repository 라는 개념이 생겼는데요,
위 그림은 뷰모델이 데이터를 요청하면 "레파지토리"가 가져와서 전달해주네요!
Repository는
- ViewModel 과 데이터를 주고받기 위해, 데이터 API 를 포함하는 클래스입니다.
- Repository 의 존재 덕분에 ViewModel 이 데이터를 관리할 필요가 없게 됩니다
- ViewModel과 Model 사이에 위치하여, ViewModel이 데이터를 요청하면 Repository가 데이터를 가져오거나 저장하는 역할을 합니다.
- ViewModel은 직접 데이터베이스에 접근하는 것을 막아 로직을 추상화
- Repository는 데이터 소스 (예를 들어, 데이터베이스, API, 캐시 등)에 대한 접근을 캡슐화하여, ViewModel이 데이터를 처리할 때 추상적인 수준에서 데이터에 접근 가능
~> Repository는 ViewModel과 Model 사이에서 데이터를 중계하는 역할
데이터 액세스 로직을 캡슐화하여 ViewModel의 복잡성을 줄이는 데 도움이 됩니다.
https://medium.com/draftkings-engineering/android-mvvm-and-repositories-in-the-real-world-e54c3582f832
https://velog.io/@dddooo9/Android-MVVM-패턴을-사용하는-이유와-방법
https://velog.io/@haero_kim/Android-깔쌈하게-MVVM-패턴과-AAC-알아보기
이자리를 빌려 저의 MVVM탐험기에 도움을 주어 감사합니다