Siena aguayo droidcon boston header

reset --hard 금지! 대규모 리팩토링 전략을 소개합니다.

Indiegogo의 모바일 소프트웨어 엔지니어인 Siena Aguayo가 리팩토링 철학과 법칙을 소개합니다.


소개

저는 샌프란시스코의 크라우드 펀딩 플랫폼인 Indiegogo의 소프트웨어 엔지니어로 일하는 Siena Aguayo입니다. iOS와 안드로이드 앱을 모두 개발했으며 현재 풀스택 웹 개발자입니다.

왜 리팩토링을 하는가?

이런 상황을 생각해 볼까요? 리팩토링을 시작했는데 뭔가 잘못됐음을 깨닫고 이를 고치려고 하다가 다른 것을 또 찾아냈습니다. 이런 과정이 반복되면 뭘 하고 있었는지 잊어버리기 쉽죠.

제가 알려드릴 전략은 다른 사람들의 생각에 기반을 둔 것입니다. 특히 Ruby 커뮤니티의 거장 Sandi Metz와, 유명한 “리팩토링” 책의 저자 Martin Fowler의 생각에 영향을 받았습니다.

사람은 완벽하지 않으므로 처음 시작부터 완벽한 코드를 작성할 수 없으며, 앞으로 생겨날 요구를 모두 예측하는 것도 불가능합니다. 그러므로 리팩토링이 필요한 것이죠.

리팩토링 4단계

  • 발견
  • 변경
  • 테스트
  • 반복

이런 개발 뉴스를 더 만나보세요

발견

첫 단계는 “발견”입니다. 변경할 코드를 식별해야 하죠. 가끔은 코드에 리팩토링이 필요하다는 사실이 눈에 확 띌 수도 있습니다. 하지만 어디서부터 시작해야 할지 모르는 경우도 있겠죠.

이 경우에는 코드의 나쁜 냄새를 찾아내는 직감을 믿으세요. “코드 구린내(Code Smell)”는 Martin Fowler의 리팩토링 책에서 유래한 단어입니다.

코드 구린내 분류

핀란드의 Mica Mantiles 교수가 개발한 계층적 분류를 소개합니다.

1) 블로터: 코드를 크게 만드는 것으로 지나치게 많은 책임 역시 보유합니다.

예: 장황한 메서드, 방대한 클래스, 강박적 기본 타입 사용, 과다한 매개변수, 데이터 뭉치

  • 강박적 기본 타입 사용은 문자열이나 정수가 특별한 의미를 갖기 시작했을 때를 뜻합니다.

  • 과다한 매개변수와 데이터 뭉치: 데이터 뭉치는 항상 코드 베이스를 따라다니는 두 개 이상의 데이터로 분리되지 않습니다. 이런 것이 관찰되면 별도의 클래스로 묶는 것이 좋습니다.

2) 객체 지향 어뷰저: 객체 지향 프로그래밍을 잘못 적용한 애플리케이션입니다.

  • 스위치 문: 레이어의 특정 부분에서만 자신의 데이터를 스위치 하는 경우에는 괜찮습니다.

  • 임시 필드: 한 번만 사용해야 합니다.

  • 방치된 상속물: 인터페이스 일부만을 구현할 때를 뜻합니다.

  • 인터페이스가 다른 대용 클래스

3) 변화 방해 요소

이런 요소들이 변경 사항을 만들기 어렵게 합니다.

예: 기능의 산재와 확산적 변경

기능의 산재는 목표한 것을 달성하기 위해 많은 다른 부분에서 작은 변화를 만들어야 하는 때를 뜻합니다. 확산적 변경은 반대 상황으로 매우 큰 파일로 가서 특정 하위 집합을 작업하는 경우입니다. 이는 다른 책임을 가진 다른 것들이 독립돼야 한다는 신호입니다.

예: 평형 상속 계층: 다른 클래스에서 반복되는 개념이 존재하는 경우

4) 불필요한 요소

예: 직무유기 클래스(Lazy Class), 데이터 클래스, 중복 코드, 죽은 코드, 막연한 범용 코드, 불필요한 주석

  • 직무유기 클래스(Lazy Class): 유틸리티에 더는 존재할 필요가 없는 클래스.

  • 중복 코드

  • 죽은 코드: 더는 사용하지 않는 코드

  • 막연한 범용 코드: 일반적 용도로 생성한 메서드가 특정 용도로 굳어진 경우

  • 불필요한 주석: 주석 자체가 나쁜 것은 아니지만 코드 구린내를 가릴 수 있습니다. 주석에 설명해야 할 것이 있다면 다른 코드에 해결해야 할 문제점이 있다는 지표가 될 수 있죠.

5) 커플러(Coupler)

커플러란 다른 것들의 동작을 지나치게 많이 알고 있는 요소들 뜻합니다.

예: 잘못된 소속/지나친 관여, 메세지 체인, 과잉 중개 메서드

  • 잘못된 소속: 클래스가 다른 클래스에 대해 너무 많은 정보를 갖고 있어서 해당 클래스의 소속처럼 느껴지는 경우

  • 메세지 체인: 한 가지에 대해 너무 많은 메서드를 호출할 때

  • 과잉 중개 메서드: 두 대상 사이에 들어가야만 하는 것

변경

변경하려면 한 번에 가능한 작은 범위로 변경해야 합니다. 일반적으로 변경할 때보다 작아도 좋습니다. 이렇게 하면 좀 더 원활하게 리팩토링할 수 있습니다.

테스트

새 코드에 대한 테스트도 작성해야겠죠? 하지만 기존 코드에 테스트가 없는 경우 실제 테스트를 적용할 수 있는 시점에 테스트 적용 범위를 추가하는 것이 좋습니다.

반복

반복은 리팩토링이 일정 경지에 이르고 안전하다고 확신한 다음 하는 것이 좋습니다. 반복으로 인해 모든 것이 실패하는 경우를 겪지 않기 위해서입니다.

전략 적용

Indiegogo 안드로이드 앱에 이 리팩토링 전략을 적용해 보겠습니다.

Indiegogo에서는 캠페인에 돈을 투자해서 특전을 얻을 수 있습니다. 예를 들어 20달러를 캠페인에 투자하고 티셔츠를 받을 수도 있죠.

특전을 다루는 뷰를 리팩토링하겠습니다. 특전은 캠페인의 상태에 따라 다르게 표시됩니다.

  • 매진: 일부 제한된 수가 남아 있지만 특전은 매진된 상태
  • 종료: 캠페인이 끝나서 특전을 더는 구매할 수 없는 상태

모델-뷰-뷰모델(MVVM, Model-View-ViewModel)

이런 상황을 담당하는 코드를 리모델링하기 위해 MVVM 패턴을 사용하겠습니다. 즉, 리팩토링할 코드는 MVVM의 일부가 되겠죠.

  • 모델: 모델은 버튼에 표시될 텍스트와 같이 뷰에 보일 데이터를 포함합니다.

  • 뷰: 안드로이드 프레임워크 뷰에서 상속된 것으로, 여기서는 버튼입니다.

  • 뷰모델: 데이터를 모델에서 받아서 뷰에 할당하는 클래스입니다.

시연

상단의 플레이 버튼을 누르면 14분 8초부터 위 움직이는 이미지에 대한 설명을 포함한 리팩토링 시연을 볼 수 있습니다.

요약

시연 내용을 요약하자면 제네릭 헬퍼 클래스 내부의 이전 코드의 메인 조건문의 복잡성을 줄였습니다. MVVM 스타일의 아키텍처로 전환하면 조건문이 훨씬 관리하기 쉬워집니다.

Q&A

**_Q: 좋은 유닛 테스트를 확보하고 테스트가 일관적으로 실행되면서 제대로 필요한 것을 점검하는지 확인하는 방법을 추천해줄 수 있나요? **

Siena: 앞서 말한 리팩토링 전략으로 가능합니다. 충분히 요소들을 독립시켜서 안드로이드 프레임워크 등에 대한 의존성을 제거했다면 유닛 테스트를 작성해도 좋은 시점입니다.

다음: Realm의 Java 모델 알아보기

General link arrow white

컨텐츠에 대하여

2017년 4월에 진행한 Droidcon Boston 행사의 강연입니다. 영상 녹화와 제작, 정리 글은 Realm에서 제공하며, 주최 측의 허가 하에 이곳에서 공유합니다.

Siena Aguayo

Siena is a mobile software engineer at Indiegogo in San Francisco, where she has developed apps on iOS, Android, and Rails/Angular. A Los Angeles native and Pokémon master, Siena began her career as a software engineer by attending Hackbright Academy, a programming bootcamp for women in San Francisco. In addition to building Android apps, Siena enjoys playing video games, knitting, jamming on the guitar, and studying Japanese. You can find her on Twitter @sienatime.

4 design patterns for a RESTless mobile integration »

close