[번역] DroidKaigi 2016 ~ 5년간 「Hatena Bookmark」앱을 계속 개발하는 기술

[번역] DroidKaigi 2016 ~ 5년간 「Hatena Bookmark」앱을 계속 개발하는 기술

Aug 28, 2016. | By: pluulove

본 포스팅은 5 年続く 「はてなブックマーク」 アプリを継続開発する技術 을 기본으로 번역하여 작성했습니다

제 일본어 실력으로 인하여 오역이나 오타가 발생할 수 있습니다.

실제 슬라이드의 일본어 부분을 번역했다는 점 양해바랍니다.


1p

5년간 「Hatena Bookmark」 앱을 계속 개발하는 기술

NOBUOKA Yuya

주식회사 Hatena (Hatena Co., Ltd.)

2p

자기소개

  • Hatena id:nobuoka
  • Twitter @nobuoka
  • 소프트웨어 엔지니어
    • 모바일 앱 (Android 앱 / UWP 앱)
    • 웹 서비스 (Java / Scala / Perl / TypeScript)
  • 경력
    • 2012-2014년 : 주로「B!」서버 사이드
    • 2014년 :「소년 점프 루키」
    • 20215년 :「B!」 Android 앱 개발

3p

배경

4p

「Hatena Bookmark」 앱 개발

  • 「Hatena Bookmark」
    • 자사의 web 서비스
    • 소셜 북마크
    • 서비스 자체는 10년 이상 계속되고 있다
  • 이번 달 2/4에 Android 앱「Hatena Bookmark」가 5주년!

5p

「Hatena Bookmark」에서 web 페이지를 북마크

6p

다른 사람이 북마크한 web 페이지 리스트를 관람할 수 있다

7p

현재 B! Android 앱 개발팀

  • 기획 (매니저도) 2명
  • 디자이너 1명
  • 엔지니어 2(+1)명

8p ~ 9p

B! Android 앱의 흐름

  • 2011-02-04 최초 릴리즈
    • API level 4+ 대응
    • 최신 Android는 2.3이던 시대
  • 계속해서 개발 (엔지니어가 겸임하여 1~2명 정도)
  • 2015년부터 엔지니어 2(+1)명 체재
    • 큰 기능추가나 변경할 수 있도록
  • 5년간 이어와서, 근 1년간 특별히 개발이 한창

10p

오래 개발하고 있는 앱에서 만나는 문제

  • 코드 변경이 의도치 않게 영향을 일으켜 기대하는 동작을 하지 않게 된다
  • 기능추가, 변경 시에 기존 설계에는 대응되지 않는다
  • Android 플랫폼의 변경에 따라가게 된다
  • 등등

11p

이 개발이 향하는 곳

  • 계속 앱 개발하기 위해서는?
    • 팀에서 정하고 방침을 소개
  • 주제로는
    • 자동 테스트, CI 서버, 빌드 시스템
    • 팀 체재, 개발 플로우
    • 앱 설계, 라이브러리화

12p

제 1 부

테스트 · CI · 자동화

13p

소프트웨어 테스트 이야기

14p

소프트웨어 테스트

  • 테스트를 작성하고 있습니까?
    • Instrumented tests
    • Local unit tests (Gradle plugin 1.1 부터)
  • 테스트는 우리 개발을 도와준다
    • 버그의 조기 발견 (신규개발 시에도 코드 변경 시에도)
    • 테스트하기 쉽게 → 보다 좋은 설계
  • Gradle로 테스트가 작성하기 쉽게 되었다
    • → Gradle 도입 후부터 테스트를 적도록

Getting Started with Testing, Android Developers

15p

16p

B! 앱 개발과 테스트

  • 코드 품질을 높이는 하나의 방법으로 테스트를 이용

  • 표준 테스트 도구를 베이스로
  • 테스트용 빌드 타입 (“ttest”)를 준비
    • Build config / ProGuard / 테스트용 코드

17p ~ 18p

목적을 의식하여 테스트를 작성

  • 여러 가지 상황 · 동작을 테스트하는 것은 어렵다
  • 무엇을 테스트할것 인가 불명확한 채 테스트를 작성해도 효과는 엹다
  • 목적에 따라서 방법도 바뀐다
  • 각 API level에서 문제없이 동작하는가?
  • 수동으로 동작 확인으로는 발견하기 어려운 항목의 검사
    • 스크린 뷰의 기록이라든지
  • 이후에 변경 시에 놓치기 쉬운 부분
  • 복잡한 처리가 기대하는 대로 움직이는가?
  • 외부와의 상호작용
  • 등등

19p

테스트를 자동으로 실행한다

  • 수동으로는 좀처럼 테스트를 실행 하지 않는다
    • 테스트 실행에는 시간이 걸리고 귀찮다
    • 테스트 실패를 알게 되는 것이 늦다
  • → 자동으로 실행되도록
  • Jenkins
  • Android SDK 에뮬레이터를 이용
  • SDK 셋업 : sdk-manager-plugin

20p ~ 21p

테스트 결과 피드백

  • 자동으로 움직여도 알지 못하면 의미 없다
  • 눈에 보이는 곳에 피드백한다
    • Slack 채널
    • GHE의 pull request
  • Jenkins → Slack
    • Jenkins Slack plugin
  • Jenkins → GitHub
    • curl 명령어로 GitHub의 API를 부른다
  • 플러그인 등이 없어도 Web API 등이 제공된다면 호출 가능 하다

22p

  • Jenkins의 Pipeline plugin 이용
    • 이전 이름 Workflow plugin
    • Job 처리를 Groovy의 DSL로 기술 → 유연하게 적기 쉽게 · 관리하기 쉽게
    • Android 에뮬레이터 기동 · 종료를 Gradle Task로 → Job을 유연하게 · Jenkins에 적게 의존하도록
  • 스크린샷으로 표시 테스트

23p

테스트를 적고 바꾸기 쉽게 하고, 품질을 높이자

  • 결함검출이나 품질을 높이기 위해서 하나의 방법
    • 코드 리뷰 등도 조합해서
  • 필요하면 테스트용 build type을 준비
  • 무엇을 모적으로 테스트를 적는가 의식한다
  • 움직이지 않으면 썩으므로
    • 우선은 자동화
    • 눈에 보이는 곳에 피드백

24p

릴리즈 흐름과 자동회

25p

릴리즈의 흐름

  • 주초에 릴리즈 내용 상담 · 결정
    • 기본적으로 릴리즈 가능한 것부터 낸다
  • 기능이 갖추어진다 → 릴리즈용 브런치를 자른다
    • 릴리즈용 브런츠에 버전을 갱신
    • 자동 테스트, 패키지 작성, 팀내 배포 · 동작 확인
  • Play Store에 업로드 · 공개

26p ~ 27p

매번하는 순서

  • 릴리즈용 패키지 빌드
  • 팀내에 배포 (Beta by Crashlytics)
  • GitHub Enterprise에 “릴리즈” 작성
  • Google Play Store에 업로드 · 공개

28p

릴리즈 플로우가 수동이면?

  • 귀찮다
    • APK 패키지를 만들고 Play Store에 업로드하는 정도라면 괜찮아도
  • 실수하기 쉽다
    • 순서 빠짐 (clean, GHE의 릴리즈 작성 등)
    • 업로드해야할 패키지가 엇갈리다

29p

자동화하자!

30p

Android 앱을 Jenkins로 빌드해서 GitHub에 “릴리즈”를 만들자

31p

Gradle Task 작성 & Jenkins상에서 실행

  • Gradle Task
    • 릴리즈용 APK 패키지 작성
    • GHE의 “릴리즈”를 작성 & 업로드
    • 장래적으로는 Google Play Store에도
  • Jenkins 상에서 실행
    • 장래적으로 Pipeline plugin을 예정
      • 릴리즈 플로우의 자동화가 쉬울듯하다

32p

릴리즈 플로우 자동화를 하자

  • 귀찮다고 느낀다면 / 실수할 것 같다고 느낀다면
  • 우선 Gradle Task를 적자
    • 의외로 간단하게 여러 가지가 된다
    • 충분히 시간을 들이기에는 어려우므로 조금씩
  • CI 서버상에서 실행
    • 수중에 빌드 환경이 준비안된 상황에서도 릴리즈 가능하다

33p

테스트나 CI 관련 이야기

34p

제 2 부

개발 플로우

35p

개발 흐름

  • 기획 · Task 관리 (Trello)
  • 디자인 · 설꼐 · 코딩
    • 커뮤니케이션은 구두 · 채팅 도구 (Slack)
      • 도쿄 · 쿄토의 원격, 비동기 커뮤니케이션
  • 코드 리뷰 (GHE)
  • 자동 테스트 (Jenkins)
  • 팀내에의 패키지 배포 (Beta by Crashlytics)

36p

B! 앱개발과 Git 브런치

  • Git flow

37p

B! 앱 개발과 코드 리뷰

  • 사내 문화로 코드리뷰는 정착
  • 계속해서 개발하기 위해서라도 중요
    • 다른 사람이 봐도 의도를 알 수 있는가?
    • 장래, 변경이 어렵게 되어있지 않은가?
  • 리뷰하기 쉽게 의뢰측도 주의를 기울이자
  • 적절한 규모 (수백 줄 정도)
  • 변경 목적을 명확하게 (복수 변경을 엮지 않는다)

38p

Dev 브런치에 머지되는 단계에 리뷰 완료한 상태가 되도록

39p

규모가 크다면 분리한다

40p

Refactoring

  • 오랫동한 계속 개발하고 있고, 해나가기 위해서는 Refactoring이 필요
    • 기능추가 · 변경시에 설계를 변경할 필요
    • 레거시 코드 개선
  • Refactoring시의 문제
    • 기능추가 · 변경전에 필요한 Refactoring을 가리기 어렵다
    • 다른 변경과 충돌(Conflict)한다

41p

B! 앱개발과 Refactoring

  • Refactoring과 기능 추가는 나눈다
    • 섞으면 리뷰하기 어렵다
  • 기능개발 시에 필요한 항목을 Refactoring으로 분리한다
    • 불필요한 Refactoring은 하지 않는다
    • 이것은 안된다, 라는 곳은 Refactoring한다
  • 작은 단위로 Refactoring
    • 리뷰하기 쉽다
    • 빠르게 dev 브런지에 포함되어 충돌을 피한다

42p

개발 중에 Refactoring이 필요하게 되었다면 브런치를 새롭게 나누어 먼저 리뷰

43p

Preview 버전과 대규모 새로운 기능개발

  • 대규모 새로운 기능개발
    • dev 브런치에서 Refactoring하면 feature 브런치와 충돌(Conflict)하기 쉽다
  • 계속해서 dev 브런치에 머지?
    • 개발 중의 상태에 릴리즈되면 곤란하다!!!
  • Preview 버전(product flavor)에서만 기능을 유효하게
    • 이 자체가 버그의 원인이 될 수 있으므로 주의
    • 규모가 큰 경우에는 유효
  • 개발 중의 상태에 동작 확인하기에도 쉽다

44p

Preview 버전 전용 화면

45p

유지 보수하기 쉬운 코드를 만드는 체제

  • 코드 리뷰하자
    • 의뢰측은 리뷰하기 쉽게 하도록 주의를 기울인다
  • 필요한 Refactoring 계속해서 한다
    • Refactoring과 기능개발은 나눈다
  • 개발용 Preview 버전 등을 만들면 좋을지도
    • Product flavor

46p

제 3 부

설계 · 구현

47p

계속해서 개발하는 데 필요한 것

  • 오래된 API level 대응
    • Android Support Library
    • 스스로 구현
  • 장래에 걸쳐 유지 보수하기 쉬운 설계 · 구현
    • Class의 역할을 명확하게, Class간의 결합을 적게
    • 처리는 공통화한다
    • Activity, Fragment를 비대화시키지 않는다
    • 복잡한 처리는 라이브러리를 만드는 등으로 숨긴다
    • 의도가 알기 쉽게 (주석이나 Annotation 등)

48p

Android Support Library 상호기능

  • B! 앱은 현재 API level 10+ 대응
  • v7 appcompat library등이 매우 중요하다
  • 여러 사람이 사용하고 있는 기능
    • Fragment, AppCompatActivity, 등
  • 그다지 쓰이지 않는 편리 기능
    • Drawable tinting 등

49p

오래된 API level을 지원하는 구현

  • Support Library의 구현을 참고
  • 예를 들면 ProgressBar의 색 변경
    • “android:progressTint”같은 것
    • ProgressBar의 서브 클래스를 만들어 대응
  • 예를 들면 그림자 구현: “Build.VERSION.SDK_INT”로 분기
    • API level 21 이후는 elevation
    • 그 미만에는 Drawable
  • 오래된 API level에서는 포기하고 아무것도 하지 않는 경우도

50p

Annotation으로 지원

  • Annotation Support Library을 쓰자!
  • @Nullable / @NonNull
  • 각종 리소스 (@StringRes 등)
  • @MainThread / @WorkerThread
    • 정리안된 오래된 코드에서 편리

51p

Activity · Fragment를 비대화시키지 않는다

52p

Activity나 Fragment는 비대화되기 쉽다

  • 여러 Callback 메소드로 여러가지를 처리
  • View 조작을 직접 하게된다
    • setContentView 메소드나 findViewById 메소드 등의 존재
    • View 조작을 위한 Class 준비가 불편
  • 어디에 적으면 좋을지 망설이는 것
  • 특정 Activity/Fragment에만 필요한 처리

53p

게다가 동일한 처리를 분산

  • Activity/Fragment내에 작성된 처리가 다른 곳에서도 필요하게 된다면?
  • 분리하기 어려우면 그대로 복사/붙여넣기 등
  • 동일한 처리가 복수 Activity/Fragment에 존재!!!!
  • 유지 보수하기 어렵다

54p ~ 55p

구체적인 예

  • Web 페이지의 리스트 항목의 Context Menu
    • 「나중에 읽기」
    • → 비로그인 상태의 경우, 로그인 화면으로 이동시 로그인해서 돌아온 경우는 「나중에 읽기」의 HTTP Request를 송신
  • Activity/Fragment의 은밀한 부분
    • Context Menu 선택시의 처리 (onContextMenuItemSelected)
    • onActivityResult 메소드

56p

Activity나 Fragment에서 처리를 분리

  • Activity/Fragment
    • 유저 입력
    • View/UI 처리
    • Lifecycle에 따른 상태관리
    • Callback 메소드
    • 그 외 어떤 처리

→ 어떤 처리를 하는 Class

57p

Activity나 Fragment에서 처리를 분리

  • Activity/Fragment
    • Callback 메소드
  • 그 외 어떤 처리
  • 유저 입력
  • View/UI 처리
  • Lifecycle에 따른 상태관리

(밖으로 분리한 부분의 설계 수정도 중요)

58p

Callback 메소드에서의 메소드 호출

  • onCreate, onStart, onStop 등
  • 메소드 호출을 요구되는 곳이 많다
    • 광고 등
  • 문제
    • 명시적으로 호출할 필요가 있는 것이 불편
    • 호출을 잊기 쉽다

59p

CallbacksAppComponents

  • 자작 라이브러리
    • Activity나 Fragment의 기본 Class를 제공
    • 각 Callback 메소드의 호출을 받는 인터페이스를 구현한 객체를 등록할 수 있다
    • 등록해두면 기본 Class에서 마음대로 부른다
  • 각 Callback 메소드에서 여러 처리를 할 필요가 있으므로 Activity/Fragment에서 처리를 분리하기 어려운 문제를 해결

60p

기본 Class가 Callback 메소드를 호출

61p

https://github.com/nobuoka/CallbacksAppComponents

62p

복잡한 처리를 라이브러리화 한다

63p ~ 64p

복잡한 사양에는 복잡한 처리를 적을 필요

  • 점점 복잡하게 되는 페이지 일람의 리스트 처리
    • 리스트의 Section을 분류한다
    • 리스트 항목을 동적으로 추가한다
    • 리스트 마지막에 무엇을 표시 (광고나 다음 페이지 읽기 버튼이나)
    • 기사 일람의 리스트 도중(10번째나)에 「추천 유저」 등을 표시 → 심하다!!!!

65p

복잡한 처리를 라이브러리화한다

  • 복잡한 기능은 2개로 나누어 생각한다
    • 기능에 특화된 부분 (다루는 Class · 데이터 내용 등)
    • 범용적으로 복잡한 부분 → 라이브러리화
  • 복잡한 처리는 라이브러리에 밀어 넣는다
  • 어플리케이션 코드에서는 단순하게 라이브러리를 사용할 뿐

66p ~ 67p

ComponentsRecyclerAdapter

  • 앞선 복잡한 리스트를 실현하기위해 만든 라이브러리
  • 복수의 item view type의 사용을 용이하게
  • 표시를 위한 데이터 구조를 컴포넌트의 Mock 구조로
    • 컴포넌트에서 Section 분리를 하거나
    • 기본 리스트의 도중에 다른 컴포넌트를 담은 컴포넌트를

68p

https://github.com/nobuoka/ComponentsRecyclerAdapter

69p

유지 보수하기 쉬운 코드를 적자

  • Annotation과 lint를 활용
  • 문서 주석
  • 설계를 생각
    • Activity/Fragment를 비대화시키지 않는다
    • 복잡한 처리는 라이브러리화한다
    • 등등

70p ~ 71p

정리

  • 계속해서 개발을 하기 위해서는?
  • 코드 변경을 하기 쉽게 · 코드 품질을 높인다
    • 소프트웨어 테스트 (자동화 · 피드백 중요)
    • Refactoring (기능개발과는 분리)
    • 코드 리뷰
    • Annotation의 설계 · 구현
  • 더욱이, 자동화
    • Gradle Task를 적는 것부터 시작하자

comments powered by Disqus

Currnte Pages Tags

Android DroidKaigi

About

Pluu, Android Developer Blog Site

이전 블로그 링크 :네이버 블로그

Using Theme : SOLID SOLID Github

Social Links