tosslab agile & coding

49
안안안안안 안안 안안안안 안안안안 2016. 04. 14 안안안

Upload: seongug-jung

Post on 21-Jan-2017

305 views

Category:

Internet


2 download

TRANSCRIPT

Page 1: Tosslab Agile & Coding

안드로이드 개발시작에서 배포까지

2016. 04. 14정승욱

Page 2: Tosslab Agile & Coding

발표자 소개• 정승욱 (Steve)

• 토스랩 | 안드로이드 개발 리더• Google Developer Expert | Android

• medium : @jsuch2362github : @ZeroBrainfacebook : @steve.SU.Jslack : gdgkr@nobrain_steve

Page 3: Tosslab Agile & Coding

발표 목차• 모바일에서 애자일 프로세스• 안드로이드 MVP 아키텍처• UnitTest On Android

• CI 연동

Page 4: Tosslab Agile & Coding

토스랩의 애자일

Page 5: Tosslab Agile & Coding

토스랩의 애자일• 2 주간의 스프린트• 기획 , 디자인 , 개발 , QA 모두 수행• 2 주마다 정기 배포• 플래닝 포커 , 코드리뷰 , 짝프로그래밍 ,테스트코드 작성 , CI 모니터 , 인하우스 배포 수행

Page 6: Tosslab Agile & Coding

2 주 스프린트 들여다보기 - 1• 첫날 오전 (1/10)

• 이슈 그루밍 (Issue Grooming) 회의• - 2 주간 작업할 이슈를 파악• - 이슈에 대한 상세 논의 시간 : 우선순위 / 추가 / 수정 부분 파악• - 이슈를 최종 확정 짓는 회의

Page 7: Tosslab Agile & Coding

2 주 스프린트 들여다보기 - 2• 첫날 오후 (1/10)

• Planning Poker

• - 이슈의 난이도 , 양에 따라 상대적인 점수 평가• - 새로운 이슈에 대한 선평가 , 이전 스프린트에 대한 후평가• - 합의가 될 때까지 계속 논의• - 이슈에 대한 상세한 구현 논의

Page 8: Tosslab Agile & Coding

2 주 스프린트 들여다보기 - 3• 개발 (2/10 ~ 3/10)

• - 이슈의 처리• - 이전 스프린트의 QA 이슈 대응• - 팀 전체 이슈를 개개인이 선점 후 처리

Page 9: Tosslab Agile & Coding

2 주 스프린트 들여다보기 - 4• 코드리뷰 -Day (4/10 ~ 5/10)

• - 업무의 최우선 순위는 코드리뷰• - 코드리뷰 후 원래의 이슈를 대응• - 긍정적인 피드백이 나올 때 까지 논의와 수정을 반복

Page 10: Tosslab Agile & Coding

2 주 스프린트 들여다보기 - 5• 다시 개발 (6/10 ~ 8/10)

• - 이슈 처리가 최우선 업무• - QA 가 끝난 버전의 내부 배포 ( 이전 스프린트 결과물 )

Page 11: Tosslab Agile & Coding

2 주 스프린트 들여다보기 - 6• 코드리뷰 (9/10 ~ 10/10)

• - 스프린트의 개발 검토• - 리뷰되어야만 소스 통합• - 리뷰를 완료하기 위해 최대한 집중

Page 12: Tosslab Agile & Coding

2 주 스프린트 들여다보기 - 7• 데모데이 (10/10)

• 각 팀의 결과물을 공유하는 자리• - 새로운 기능에 대한 공유 : First Moment

• - 회사 전 인원이 제품에 집중하는 시간• - 비지니스 팀은 Sales, CS 등 정보 공유• - 제품과 회사의 비전을 공유하는 시간

Page 13: Tosslab Agile & Coding

2 주 스프린트 들여다보기 - 8• 회고 (10/10)

• - 모바일 프로덕트 전체 회고 : 기획 , 디자인 , 안드로이드 , iOS

• - 개발팀 전체 회고 : 백엔드 , 웹 , 모바일• - 이슈 진행에 문제 , 프로세스상 문제 ,

일정이나 커뮤니케이션의 문제 등 전반적인 검토- 논의된 내용은 행동지침까지 나오도록 함- 기록 하여 C 레벨이 경영에 참고

Page 14: Tosslab Agile & Coding

안드로이드와 MVP

출처 : http://www.ballplaya.com/

Page 15: Tosslab Agile & Coding

MVP 패턴• 프론트에 기존의 MVC 를 응용시 문제 : 복잡한 UI 인터랙션 Flow 를 구현하기 어려움• Microsoft 에서 제안한 MVVM 이 시초• 이를 응용해서 마틴 파울러가 MVP 제안

Page 16: Tosslab Agile & Coding

MVC vs MVP

출처 : https://tomyrhymond.wordpress.com/2011/09/16/mvc-mvp-and-mvvm/

Page 17: Tosslab Agile & Coding

MVC? MVP!• MVC 의 문제• View 와 Controller 의 경계 모호• -> 왜 ? 프론트는 View 가 외부 인터랙션 시발점• -> View 가 Controller 의 기능 일부 수행• -> 역할이 모호 , 클래스가 비대해짐• -> 테스트 코드 작성에 악영향

Page 18: Tosslab Agile & Coding

MVP 모습

출처 : http://www.tinmegali.com/en/model-view-presenter-android-part-1/

Page 19: Tosslab Agile & Coding

MVP 의 역할 - Model• Model

• - 비지니스 로직 수행• - 데이터 모델• - DB, API 통신

Page 20: Tosslab Agile & Coding

MVP 의 역할 - View• View

• - UI 요소에 접근• - UI 갱신과 정보 취득• - 외부 인터랙션 시작• - Presenter 를 요소로 가짐

Page 21: Tosslab Agile & Coding

MVP 의 역할 - Presenter• Presenter

• - MVC 의 Controller 유사• - 외부의 인터랙션 처리 중 논리적 연산이 필요한 경우 흐름 제어• - Model 과 View 를 가짐

Page 22: Tosslab Agile & Coding

코드로 보는 MVP - 1

Page 23: Tosslab Agile & Coding

코드로 보는 MVP - 2

Page 24: Tosslab Agile & Coding

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(); }

}

Page 25: Tosslab Agile & Coding

public class HomeActivity extends AppCompatActivityimplements HomePresenter.View {

@OnTextChanged(R.id.et_home_search) void onChangedSearchText(CharSequence text) { homePresenter.inputSearchText(text.toString()); }

@Override public void refresh() { imageAdapterDataView.refresh(); }

}

Page 26: Tosslab Agile & Coding

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(); } }}

Page 27: Tosslab Agile & Coding

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(); } }}

Page 28: Tosslab Agile & Coding

Code Reviewgithub : https://github.com/ZeroBrain/GDG-ATSL-ON-MVP

Page 29: Tosslab Agile & Coding

안드로이드와 Test

http://www.nari7.co.kr/shop/shopdetail.html?branduid=288&special=1

Page 30: Tosslab Agile & Coding

Android Test History• 1. 초기 Android 테스트 - JUnit3

- 에뮬레이터 필요• 2. Robolectric Test Framework

- JUnit 4 - 에뮬레이터 없이 가능 - 굉장히 복잡한 설정이 요구 - 3rd Party 라 Android Full Feature 지원이 느림

Page 31: Tosslab Agile & Coding

현재 안드로이드 테스트• Android Test Support Library

• - JUnit4 지원• - 에뮬레이터 없는 테스트 지원• - 안드로이드 특화 Rule 과 Assertion 지원• - UI Automater : 다중 앱 테스트 지원

Page 32: Tosslab Agile & Coding

@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)))); }}

Page 33: Tosslab Agile & Coding

@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)))); }}

Page 34: Tosslab Agile & Coding

@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)))); }}

Page 35: Tosslab Agile & Coding

@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)))); }}

Page 36: Tosslab Agile & Coding

@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)))); }}

Page 37: Tosslab Agile & Coding

Live Coding

github : https://github.com/ZeroBrain/GDG-ATSL-ON-MVP

Page 38: Tosslab Agile & Coding

안드로이드와 CI, 그리고 모니터

http://changtle.tistory.com/654

Page 39: Tosslab Agile & Coding

토스랩의 도입 이유• 품질에 대한 지속적인 모니터링 - 자동 빌드 리포팅 - 자동 테스트 리포팅 - 정적 분석 리포팅• 배포 자동화 - 웹 / 백엔드의 배포 자동화

Page 40: Tosslab Agile & Coding

안드로이드의 CI• Gradle 빌드에 대해 사전 이해가 요구 - Gradle Build CLI 이용• 정적분석 - PMD, CheckStyle 등 가능• 테스트 빌드 - 빌드 / 테스트 결과 리포팅

Page 41: Tosslab Agile & Coding

FeatureBranch

PushPull Request Webhook

CI 와 Github

Page 42: Tosslab Agile & Coding

토스랩의 CI• 대상 Branch

• - develop : 개발 완료된 코드들이 통합되는 브랜치 : QA 가 이루어지는 브랜치

• - Branches of Pull-Request : 코드 리뷰 중인 코드

Page 43: Tosslab Agile & Coding

리포트 - 잔디 메세지

CI 리포트

Page 44: Tosslab Agile & Coding

리포트 - 잔디 메세지

GitHub 리포트

Page 45: Tosslab Agile & Coding

Wrap Up

Page 46: Tosslab Agile & Coding

무엇을 ?• 애자일 - 예측가능하고 효율적인 업무를 위해 도입• MVP - 일관성 있는 아키텍쳐를 위해 도입• Test - Regression 이슈 대응을 위해 도입• CI - 지속적인 모니터를 위해 도입

Page 47: Tosslab Agile & Coding

왜 ?• 애자일 - Story 점수 : 불필요한 커뮤니케이션 감소 - 120 점이 넘어가면 업무 협의 , 긴급 이슈는 우선순위 조정 - 코드리뷰 : 예측 불가능한 코드 제거 - QA 와 배포의 일정에 대한 탄력적이며 신뢰할 수 있음 - 회고 : 발전적인 업무 프로세스를 위해 필수• MVP

- 코드리뷰 시간 단축 - 테스트에 용이 - OOP 설계의 5 대 원칙에도 적합• Test - 신뢰할 수 있는 코드 생성• CI - 자동화에 따른 업무 효율 증가

Page 48: Tosslab Agile & Coding

어떻게 ?• Test

• - 모든 시작은 Test 에 용이한 코드 개발• - 아키텍쳐 관점에서 Test => MVP

• - 클래스 각각에 대해 Test => OOP - SOLID

• - 신뢰하고 지속적으로 모니터 할 수 있는 시스템 => Jenkins

• Agile 은 거들뿐 ...

Page 49: Tosslab Agile & Coding

Q & A