Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Tosslab Agile & Coding

457 views

Published on

It's about Agile, Architect, Test, CI

Published in: Internet
  • Be the first to comment

Tosslab Agile & Coding

  1. 1. 안드로이드 개발 시작에서 배포까지 2016. 04. 14 정승욱
  2. 2. 발표자 소개 • 정승욱 (Steve) • 토스랩 | 안드로이드 개발 리더 • Google Developer Expert | Android • medium : @jsuch2362 github : @ZeroBrain facebook : @steve.SU.J slack : gdgkr@nobrain_steve
  3. 3. 발표 목차 • 모바일에서 애자일 프로세스 • 안드로이드 MVP 아키텍처 • UnitTest On Android • CI 연동
  4. 4. 토스랩의 애자일
  5. 5. 토스랩의 애자일 • 2주간의 스프린트 • 기획, 디자인, 개발, QA 모두 수행 • 2주마다 정기 배포 • 플래닝 포커, 코드리뷰, 짝프로그래밍, 테스트코드 작성, CI 모니터, 인하우스 배포 수행
  6. 6. 2주 스프린트 들여다보기 - 1 • 첫날 오전 (1/10) • 이슈 그루밍 (Issue Grooming) 회의 • - 2주간 작업할 이슈를 파악 • - 이슈에 대한 상세 논의 시간 : 우선순위/추가/수정 부분 파악 • - 이슈를 최종 확정 짓는 회의
  7. 7. 2주 스프린트 들여다보기 - 2 • 첫날 오후 (1/10) • Planning Poker • - 이슈의 난이도, 양에 따라 상대적인 점수 평가 • - 새로운 이슈에 대한 선평가, 이전 스프린트에 대한 후평가 • - 합의가 될 때까지 계속 논의 • - 이슈에 대한 상세한 구현 논의
  8. 8. 2주 스프린트 들여다보기 - 3 • 개발(2/10 ~ 3/10) • - 이슈의 처리 • - 이전 스프린트의 QA 이슈 대응 • - 팀 전체 이슈를 개개인이 선점 후 처리
  9. 9. 2주 스프린트 들여다보기 - 4 • 코드리뷰-Day (4/10 ~ 5/10) • - 업무의 최우선 순위는 코드리뷰 • - 코드리뷰 후 원래의 이슈를 대응 • - 긍정적인 피드백이 나올 때 까지 논의와 수정을 반 복
  10. 10. 2주 스프린트 들여다보기 - 5 • 다시 개발 (6/10 ~ 8/10) • - 이슈 처리가 최우선 업무 • - QA 가 끝난 버전의 내부 배포 (이전 스프린트 결과 물)
  11. 11. 2주 스프린트 들여다보기 - 6 • 코드리뷰 (9/10 ~ 10/10) • - 스프린트의 개발 검토 • - 리뷰되어야만 소스 통합 • - 리뷰를 완료하기 위해 최대한 집중
  12. 12. 2주 스프린트 들여다보기 - 7 • 데모데이 (10/10) • 각 팀의 결과물을 공유하는 자리 • - 새로운 기능에 대한 공유 : First Moment • - 회사 전 인원이 제품에 집중하는 시간 • - 비지니스 팀은 Sales, CS 등 정보 공유 • - 제품과 회사의 비전을 공유하는 시간
  13. 13. 2주 스프린트 들여다보기 - 8 • 회고 (10/10) • - 모바일 프로덕트 전체 회고 : 기획, 디자인, 안드로이드, iOS • - 개발팀 전체 회고 : 백엔드, 웹, 모바일 • - 이슈 진행에 문제, 프로세스상 문제, 일정이나 커뮤니케이션의 문제 등 전반적인 검토 - 논의된 내용은 행동지침까지 나오도록 함 - 기록 하여 C 레벨이 경영에 참고
  14. 14. 안드로이드와 MVP 출처 : http://www.ballplaya.com/
  15. 15. MVP 패턴 • 프론트에 기존의 MVC 를 응용시 문제 : 복잡한 UI 인터랙션 Flow 를 구현하기 어려움 • Microsoft 에서 제안한 MVVM 이 시초 • 이를 응용해서 마틴 파울러가 MVP 제안
  16. 16. MVC vs MVP 출처 : https://tomyrhymond.wordpress.com/2011/09/16/mvc-mvp-and-mvvm/
  17. 17. MVC? MVP! • MVC 의 문제 • View 와 Controller 의 경계 모호 • -> 왜? 프론트는 View 가 외부 인터랙션 시발점 • -> View 가 Controller 의 기능 일부 수행 • -> 역할이 모호, 클래스가 비대해짐 • -> 테스트 코드 작성에 악영향
  18. 18. MVP 모습 출처 : http://www.tinmegali.com/en/model-view-presenter-android-part-1/
  19. 19. MVP 의 역할 - Model • Model • - 비지니스 로직 수행 • - 데이터 모델 • - DB, API 통신
  20. 20. MVP 의 역할 - View • View • - UI 요소에 접근 • - UI 갱신과 정보 취득 • - 외부 인터랙션 시작 • - Presenter 를 요소로 가짐
  21. 21. MVP 의 역할 - Presenter • Presenter • - MVC 의 Controller 유사 • - 외부의 인터랙션 처리 중 논리적 연산이 필요한 경우 흐름 제어 • - Model 과 View 를 가짐
  22. 22. 코드로 보는 MVP - 1
  23. 23. 코드로 보는 MVP - 2
  24. 24. public class HomeActivity extends AppCompatActivity implements HomePresenter.View { @OnTextChanged(R.id.et_home_search) void onChangedSearchText(CharSequence text) { homePresenter.inputSearchText(text.toString()); } @Override public void refresh() { imageAdapterDataView.refresh(); } }
  25. 25. public class HomeActivity extends AppCompatActivity implements HomePresenter.View { @OnTextChanged(R.id.et_home_search) void onChangedSearchText(CharSequence text) { homePresenter.inputSearchText(text.toString()); } @Override public void refresh() { imageAdapterDataView.refresh(); } }
  26. 26. public class HomePresenterImpl implements HomePresenter { void loadSearchResult(String text) { searchApi.searchText(text, pageCount) .filter(channel -> channel != null && channel.getChannel() != null) .map(SearchChannel::getChannel) .filter(result -> result != null && result.getResult() > 0) .flatMap(imageResult -> Observable.from(imageResult.getItem())) .observeOn(AndroidSchedulers.mainThread()) .subscribe(imageAdapterDataModel::add, Throwable::printStackTrace, view::refresh); } @Override public void inputSearchText(String searchText) { if (!searchSubscription.isUnsubscribed()) { searchSubscription.unsubscribe(); } imageAdapterDataModel.clear(); initSubscription(); if (!TextUtils.isEmpty(searchText)) { searchSubject.onNext(searchText); } else { view.refresh(); } } }
  27. 27. public class HomePresenterImpl implements HomePresenter { void loadSearchResult(String text) { searchApi.searchText(text, pageCount) .filter(channel -> channel != null && channel.getChannel() != null) .map(SearchChannel::getChannel) .filter(result -> result != null && result.getResult() > 0) .flatMap(imageResult -> Observable.from(imageResult.getItem())) .observeOn(AndroidSchedulers.mainThread()) .subscribe(imageAdapterDataModel::add, Throwable::printStackTrace, view::refresh); } @Override public void inputSearchText(String searchText) { if (!searchSubscription.isUnsubscribed()) { searchSubscription.unsubscribe(); } imageAdapterDataModel.clear(); initSubscription(); if (!TextUtils.isEmpty(searchText)) { searchSubject.onNext(searchText); } else { view.refresh(); } } }
  28. 28. Code Review github : https://github.com/ZeroBrain/GDG-ATSL-ON-MVP
  29. 29. 안드로이드와 Test http://www.nari7.co.kr/shop/shopdetail.html?branduid=288&special=
  30. 30. Android Test History • 1. 초기 Android 테스트 - JUnit3 - 에뮬레이터 필요 • 2. Robolectric Test Framework - JUnit 4 - 에뮬레이터 없이 가능 - 굉장히 복잡한 설정이 요구 - 3rd Party 라 Android Full Feature 지원이 느림
  31. 31. 현재 안드로이드 테스트 • Android Test Support Library • - JUnit4 지원 • - 에뮬레이터 없는 테스트 지원 • - 안드로이드 특화 Rule 과 Assertion 지원 • - UI Automater : 다중 앱 테스트 지원
  32. 32. @RunWith(AndroidJUnit4.class) public class HomeActivityTest { @Rule public IntentsTestRule<HomeActivity> rule = new IntentsTestRule<>(HomeActivity.class); private HomePresenter.View view; @Before public void setUp() throws Exception { view = rule.getActivity(); } @Test public void testOnMoveLink() throws Throwable { String text = "http://www.coupang.com"; rule.runOnUiThread(() -> view.onMoveLink(text)); intending(hasAction(eq(Intent.ACTION_VIEW))); intending(hasData(eq(Uri.parse(text)))); } }
  33. 33. @RunWith(AndroidJUnit4.class) public class HomeActivityTest { @Rule public IntentsTestRule<HomeActivity> rule = new IntentsTestRule<>(HomeActivity.class); private HomePresenter.View view; @Before public void setUp() throws Exception { view = rule.getActivity(); } @Test public void testOnMoveLink() throws Throwable { String text = "http://www.coupang.com"; rule.runOnUiThread(() -> view.onMoveLink(text)); intending(hasAction(eq(Intent.ACTION_VIEW))); intending(hasData(eq(Uri.parse(text)))); } }
  34. 34. @RunWith(AndroidJUnit4.class) public class HomeActivityTest { @Rule public IntentsTestRule<HomeActivity> rule = new IntentsTestRule<>(HomeActivity.class); private HomePresenter.View view; @Before public void setUp() throws Exception { view = rule.getActivity(); } @Test public void testOnMoveLink() throws Throwable { String text = "http://www.coupang.com"; rule.runOnUiThread(() -> view.onMoveLink(text)); intending(hasAction(eq(Intent.ACTION_VIEW))); intending(hasData(eq(Uri.parse(text)))); } }
  35. 35. @RunWith(AndroidJUnit4.class) public class HomeActivityTest { @Rule public IntentsTestRule<HomeActivity> rule = new IntentsTestRule<>(HomeActivity.class); private HomePresenter.View view; @Before public void setUp() throws Exception { view = rule.getActivity(); } @Test public void testOnMoveLink() throws Throwable { String text = "http://www.coupang.com"; rule.runOnUiThread(() -> view.onMoveLink(text)); intending(hasAction(eq(Intent.ACTION_VIEW))); intending(hasData(eq(Uri.parse(text)))); } }
  36. 36. @RunWith(AndroidJUnit4.class) public class HomeActivityTest { @Rule public IntentsTestRule<HomeActivity> rule = new IntentsTestRule<>(HomeActivity.class); private HomePresenter.View view; @Before public void setUp() throws Exception { view = rule.getActivity(); } @Test public void testOnMoveLink() throws Throwable { String text = "http://www.coupang.com"; rule.runOnUiThread(() -> view.onMoveLink(text)); intending(hasAction(eq(Intent.ACTION_VIEW))); intending(hasData(eq(Uri.parse(text)))); } }
  37. 37. Live Coding github : https://github.com/ZeroBrain/GDG-ATSL-ON-MVP
  38. 38. 안드로이드와 CI, 그리고 모니 터 http://changtle.tistory.com/654
  39. 39. 토스랩의 도입 이유 • 품질에 대한 지속적인 모니터링 - 자동 빌드 리포팅 - 자동 테스트 리포팅 - 정적 분석 리포팅 • 배포 자동화 - 웹/백엔드의 배포 자동화
  40. 40. 안드로이드의 CI • Gradle 빌드에 대해 사전 이해가 요구 - Gradle Build CLI 이용 • 정적분석 - PMD, CheckStyle 등 가능 • 테스트 빌드 - 빌드/테스트 결과 리포팅
  41. 41. Feature Branch Push Pull Request Webhook CI 와 Github
  42. 42. 토스랩의 CI • 대상 Branch • - develop : 개발 완료된 코드들이 통합되는 브랜치 : QA 가 이루어지는 브랜치 • - Branches of Pull-Request : 코드 리뷰 중인 코드
  43. 43. 리포트 - 잔디 메세지 CI 리포트
  44. 44. 리포트 - 잔디 메세지 GitHub 리포트
  45. 45. Wrap Up
  46. 46. 무엇을? • 애자일 - 예측가능하고 효율적인 업무를 위해 도입 • MVP - 일관성 있는 아키텍쳐를 위해 도입 • Test - Regression 이슈 대응을 위해 도입 • CI - 지속적인 모니터를 위해 도입
  47. 47. 왜? • 애자일 - Story 점수 : 불필요한 커뮤니케이션 감소 - 120 점이 넘어가면 업무 협의, 긴급 이슈는 우선순위 조정 - 코드리뷰 : 예측 불가능한 코드 제거 - QA 와 배포의 일정에 대한 탄력적이며 신뢰할 수 있음 - 회고 : 발전적인 업무 프로세스를 위해 필수 • MVP - 코드리뷰 시간 단축 - 테스트에 용이 - OOP 설계의 5대 원칙에도 적합 • Test - 신뢰할 수 있는 코드 생성 • CI - 자동화에 따른 업무 효율 증가
  48. 48. 어떻게? • Test • - 모든 시작은 Test 에 용이한 코드 개발 • - 아키텍쳐 관점에서 Test => MVP • - 클래스 각각에 대해 Test => OOP - SOLID • - 신뢰하고 지속적으로 모니터 할 수 있는 시스템 => Jenkins • Agile 은 거들뿐...
  49. 49. Q & A

×