ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • NextStep Android Architecture with TDD 1기 - 2 MVP Architecture with Test
    안드로이드 2021. 8. 18. 00:19
    반응형

    이 글은 NextStep의 안드로이드 아키텍처 with TDD 교육 과정을 듣고 쓴 후기 시리즈이다.

    Effective Kotlin 1기 과정을 수강하고 1년 뒤, 이 과정을 수강할 수 있는 좋은 기회가 있어 참여하게 되었다.

    이 과정을 수강하면서 배운 내용을 정리하면서 후기를 남기면, 안드로이드 테스트에 대해 관심 있는 다른 사람들에게도 꽤 쓸만한 글이 될거라 생각되어서 글을 쓰기 시작했다.

     

    1편 링크 - NextStep Android Architecture with TDD 1기 - 1 MVC, Domain, Multi Module Project

    2편 링크 - NextStep Android Architecture with TDD 1기 - 2 MVP Architecture with Test [지금 보고있는 곳]

    3편 링크 - NextStep Android Architecture with TDD 1기 - 3 MVVM Architecture with Test

     

    피드백 강의

    지난 이팩티브 코틀린 미션 피드백 중 "메서드가 아무 것도 리턴하지 않으면 사이드 이펙트가 발생한다" 
    이 피드백이 항상 코드를 짤 때마다 내 귀에서 맴돌았는데, 이번 미션에서도 마찬가지어서 곰곰이 생각하면서 설계를 해보았다.

    입력 후 수식의 결과를 리턴해주면 되지 않을까. 어차피 문자열을 나타내 주어야 하니 String으로 리턴하자라는 생각으로 객체를 설계 / 작성했다.
    생각보다 설계가 컴팩트하게 잘 나온 것 같아서 기분 좋게 이번 강의를 들었다.

    강사님은 리턴할 때 자기 자신을 (Expression)을 리턴하는 형태, 즉 함수형 프로그래밍 패러다임으로 로직을 구성하셨다.

    그러고 보니 지난 이팩티브 코틀린 블랙잭 미션에서 함수형 프로그래밍에 대해 맛을 보여주신 리뷰어 분이었다는 것을 새삼 다시 깨달았다.
    또, 그 때 피드백은 반영했지만 1년이 지난 지금 해당 피드백을 계속해서 "기억"은 했지만 내 코드 스타일에는 녹아있지 않았다는 것을 알게되었다.

    아, 불변하는 자기 자신을 리턴하는 방법을 왜 잊고있었을까?
    강사님의 설계 자체는 내가 생각한 커다란 틀과 비슷했었다.
    나도 객체지향이란 것을 조금은 스스로 다룰 수 있구나 라는 생각에 뿌듯했고,
    큰 맥락을 비슷하게 생각 했다는 점에서 신기했다.

     


     

    이번 미션은 기존의 프로젝트를 MVP 아키텍처로 리팩터링하고, 새로운 기능을 추가해보는 형태였다.

    1주차에서 자신이 구현한 문자열 계산기를 옮겨서 리팩터링을 진행해도 좋다 하셨으나, 다른 사람이 짜놓은 프로젝트를 리팩터링 하면서 테스트 케이스를 늘리고, 새로운 기능을 추가해보는 경험이 더 가치있다고 생각해서 과감하게 내 이전 코드는 버리고 새로 시작했다.

     

     

    1단계 미션

    1단계 미션은 기존 app 모듈에 모든 로직이 들어가 있는데, Domain 로직을 따로 멀티모듈로 분리하는 미션이었다.
    기존 로직에 도메인 로직을 추출해서 멀티모듈로 옮겨야지....! 라는 말이 머쓲할 정도로 도메인 로직이 완벽하게 분리되어있었다 😅
    그래서 그냥 순수 코틀린 라이브러리 멀티모듈 프로젝트 하나 파서 로직을 옮기기만 하면 되었다.

    1단계 Pull Request Link

     

     

    2단계 미션

    MVP 아키텍처를 2년만에 다시 하게 되다니 감회가 새로웠지만, 이제는 강의 내용을 조금 놓쳐도 알아서 짤 수 있었다.
    참.... 신기했다. 처음 MVP 아키텍처를 공부 할 때, 아무리 봐도 이해가 되지 않았던 코드들이었는데.
    내가 과연 개발자란 직업을 가질 수 있을까 하며 좌절했던 그 코드들이었는데.
    이제는 쓱 보면, 무슨 내용인지, 어디에 무엇을 채워 넣어야하는지 한 번에 보였다. 그만큼 노력도 했지만.. 그래도 너무나 신기했다. 😊 

    아무튼 그때는 테스트 코드는 생각조차 할 수 없었기 때문에 Presenter의 테스트 코드 작성은 완전 처음이었다.
    1년 전 모키토 라이브러리를 이용해서 모킹 테스트를 해본적은 있었지만, 이번에 사용하는 라이브러리는 mockk 이고, 너무 오랜만이어서 테스트 작성에 시간이 많이 걸렸다.
    기능이 쉽다보니 테스트도 대부분의 로직이 비슷비슷했다.
    그래도 TDD로 작성해보는 경험을 얻기 위해서 한 땀 한 땀 실패 테스트 작성 후 리팩터링 과정을 거쳤다.

    테스트 이름을 이전 미션에서는
    "최근 입력이 숫자가 아닐 때 숫자를 누르면 화면에 해당 숫자가 추가 된다" 이런식으로 지었다.
    모든 숫자를 입력해도 그렇다. 라는 의도를 넣기 위해서 범용적인 단어를 사용했는데, 한방에 이해가 되는 말이 아닌 것 같았다.

    그래서 이번에는 
    "수식에 3이 입력 되어 있을때 5를 입력하면 35가 나타나야 한다" 이런식으로 지었다.
    완전하게 구체적으로 무슨말인지 알아 들을 수 있다고 생각하는데, 숲이 아니라 나무를 보는 느낌. 너무 특정 케이스만 말하는 게 아닐까? 하는 생각이 들기도 했다.

    확실히 둘 다 장단점이 명확해서 프로젝트 성격에 알맞게 선택하는 게 중요하지 않을까 하는 생각으로 마무리 지었다.

     

    Presenter 테스트 코드를 모두 작성한 다음에는, 이전 미션의 UI 테스트를 모두 복사해서 가져온 다음 MainActivity를 리팩터링했다.

     

    코드가 싹 줄어드는 이 쾌감... 역시 로직 분리는 채고야....

     

    2단계 Pull Request Link

     

     

    3단계 미션

    마지막으로는 계산기의 계산 기록이 모두 남고, 볼 수 있는 기능을 추가하는 것이었다. 추가해야할 코드들은 아래와 같았다.

    1. 계산 기록을 저장할 도메인 객체
    2. 리사이클러뷰 테스트

    계산 기록을 저장할 CalculatorMemory 객체를 설계하면서, 가장 기본적인 것에서 실수를 했다 😥

     

    바로 이 녀석인데, 저장 기록은 계속 추가되어야 하니까 mutableList를 써야하지~ 라고 단순하게 생각하고 말아서 중요한 것을 잊고말았다!
    늘 외부에서 내부의 상태를 변형시키지 않아야 된다는 생각을 하는데, 이땐 깜빡해버리고 놓쳐버렸다.

    무튼, 리뷰어님의 설명대로, MutableList를 그대로 생성자 주입 해주면, 외부에서 해당 List의 참조를 갖고 리스트에 변형을 일으키면 (add, remove 등을 호출) CalculatorMemory에선 아무 일도 하지 않았는데 변경 사항이 생기는 매우 괴랄한 객체가 되어버린다.

     

    해당 문제점은 위 코드처럼 해결했다.

    initialHistories 네이밍은 그냥 histories로 해도 되지만, 생성자에 넣는 값은 초기값이라는 것을 명시하고 싶어서 이렇게 지어보았다.

     

    RecyclerView Test

    다음으로 가장 애먹었던 것은 리사이클러뷰 테스트이다.

    처음에 든 생각은... 않이 RecyclerView는 도대체 어떻게 테스트해야되지 ?!?!?!

     

    그래서 바로 Google 선생님에게 물어보았지만, 생각보다 RecyclerView에 대한 공식적인 지원이 별로 없다는 생각이 들었다. 보는 글 마다 방법이 모두 상이했고, 모두 납득이 가는 방법이기는 했다.

    검색한 방법 중 그나마 제일 짧고 읽기 쉬워보이는 방법으로 리사이클러뷰에 대한 테스트를 진행하고 받은 피드백은 정말 충격적이었다.

     

    아..! 왜 이생각을 못했을까.

    ArrayList나 LinkedList의 구현체를 그대로 변수에 받는 게 아닌, List, MutableList 처럼 interface를 받아서 쓰듯이. View 또한 그러한 패러다임을 가질 수 있다는 것을 왜 생각하지 못했을까?

    UI 테스트를 Unit 테스트처럼 빡빡하게 할 필요가 없다고 느끼게 되었다. View의 내부 구현은 정말 무궁무진하게 구현할 수 있기 때문에 구체적인 구현체를 테스트 하는 것은 별로이다!

    다시 생각해보면, Google에서 공식적으로 RecyclerView에 대한 테스트 유틸을 충분히 만들 수 있었을 텐데, 만들지 않은 이유가 바로 이러한 이유 때문은 아닐까 하고 생각이 든다.


    마무리

    생각보다 presenter Test가 난해하다는 느낌을 많이 받았다. 이래서 MVP 아키텍처가 자연스럽게 잘 쓰이지 않게 된 것일까? View에 대한 의존성이 반드시 생기고, 모킹을 반 강제적으로 해야한다는 점, Unit 테스트가 아니라 UI 테스트를 하는 것만 같은 느낌. 모두 MVP 아키텍처의 단점이지 않을까 생각한다.

    이번 미션은 익숙하지 않은 mock 테스트와 RecyclerView Test의 경험이 주가 되었다.
    사실 UI 테스트는 하지 않아도 좋다고 하셨으나, 공부하고 싶기도 했고 리뷰 받고 싶은 부분도 있어서 시간을 더 투자해서 공부해보았다. 덕분에 도움도 되고 인사이트 확장도 되는 좋은 경험이었다.

    이제 다음은 3주차 MVVM으로 들어가게 된다.

     


     

     

    이 글이 도움이 되셨나요?

    말리빈에게 커피를 한 잔 쥐어 주세요! ☕ 커피를 인풋으로 더 좋은 글을 아웃풋으로 쓰려 노력하겠습니다 😊

     

    Buy Me A Coffee

     

    반응형

    댓글

Designed by Tistory.