강남언니의 iOS 개발 챕터에서는 이 후에 적극적으로 TDD를 도입해 보려고 합니다. 이 글은 우리가 왜 그러한 결정을 내리게 되었는지 그리고 TDD가 어떤 의미를 가지고 있는 지, 그 배경을 설명하는 글로 TDD x iOS 시리즈의 첫 번째 글입니다.

개인적인 견해 일 뿐 iOS챕터의 생각과는 방향이 다를 수 있다는 점을 미리 밝혀 둡니다…ㅎㅎ

[TDD x iOS]

강남언니 팀에서 iOS앱 개발을 하고 있는 Go입니다.

모든 개발이 그렇지만, iOS앱 개발은 복잡합니다. 강남언니처럼 가파르게 성장하는 서비스라면, 더더욱 복잡합니다. 요구 사항은 빠르게 변화하고, 동시에 점점 복잡해 집니다. 개발자는 빠른 개발과 안정적인 배포 라는 두 마리의 토끼를 동시에 잡아야 합니다.

개발자에게는 이 험난한 여정을 헤쳐나갈 수 있는 무기, '좋은 코드'가 필요합니다.

우리는 좋은 코드가 필요하다

좋은 코드가 무엇인지는 또 다시 여러 가지 정의를 내릴 수 있겠지만, 강남언니 iOS챕터에서는 읽기 쉬운 코드, 그리고 고치기 쉬운 코드를 좋은 코드라고 생각하고 있습니다.

코드가 복잡해 지면 복잡해 질수록, 서비스가 커지면 커질 수록, 좋은 코드를 유지해 나가는 건 생각처럼 쉬운 일이 아닙니다.

그것은 마치 달리는 기차의 바퀴를 갈아 끼우는 것과 비슷합니다.

그렇기 때문에 많은 경우, 좋은 아키텍처(architecture)를 적용해서 이 문제를 풀고자 시도합니다. 역할을 잘 나눈 컴포넌트(component)의 정의대로 복잡한 코드를 정리하고, 읽기도 쉽고 고치기도 쉬운 코드를 만들어 나가려는 것 입니다. 한 번 정리하는 것이 어려울 수는 있겠지만, 일단 자리를 잡고 나면, 일정한 패턴으로 쓰여진 코드를 읽는 것도 점점 쉬워질 것이고, 컴포넌트별로 잘 역할을 나눠 놓았으니 코드를 수정할 지점을 찾기도 쉬워질 것입니다.

사실은 지금 강남언니의 iOS앱도 MVVM 아키텍처를 채택하고, 앱 전반을 MVVM에서 정의한 컴포넌트들로 정리해 나가고 있습니다.

아키텍처가 해결할 수 없다

그렇다면, 좋은 아키텍처를 찾아서 앱 전반에 걸쳐 다 적용하고 나면, 읽기 쉽고, 고치기 쉬운, '좋은 코드'가 되는 걸까요? 빠른 개발과 안정적인 배포 라는 두 마리의 토끼를 모두 잡을 수 있게 되는 걸까요? 섣부른 결론을 내고 싶지는 않지만, 그렇지 않을 가능성도 꽤 있는 것 같습니다.

요구 사항은 계속해서 변화하고, 기술은 또 계속해서 발전합니다. 아무리 좋은 아키텍처를 적용했다고 하더라도, 시간이 지나면서 생기는 변화를 계속해서 충분히 반영하지 못한다면, 결국은 어느 지점부터는 레거시(legacy)가 됩니다. 읽기 어려울 뿐더러, 고치기는 더 어려운 코드가 됩니다.

'좋은 코드'를 '지속적으로' 유지하는데 필요한 건, 좋은 아키텍처를 적용하는 것이 아니라, 지속적인 리팩토링(refactoring)일 지도 모른다고 생각했습니다.

SwiftUI의 등장으로 MVVM+RxSwift의 운명은 어떻게 될 것인가...

지속적인 리팩토링를 위하여

만약, 지금 개발해야 하는 것이 UI가 없는 프레임웍(framework)이라면 어떤 아키텍처로 시작 하시겠습니까? MVVM이나 MVP는 UI를 염두에 둔 아키텍처라 적합하지 않을 것 같습니다. 디자인 패턴 책을 펼치고 어떤 패턴으로 코드를 짤 것인지 미리 설계를 하는 것도 좋은 방법일 수 있습니다. 사실, 더 좋은 방법은 조금씩 구현해 보고 완성 할 때까지 계속해서 리팩토링하는 방법입니다. 미리 모든 걸 알 수 있다면 좋겠지만, 보통은 미리 예측하는 것 보다 하면서 배워 나가는 것이 더 간편하고, 더 많이 알게 됩니다.

UI를 가진 앱을 만들 때도 마찬가지라고 생각합니다. 어떤 아키텍처가 정답 인지 알 수 없다면, 제일 좋은 방법은 요구 사항을 조금씩 구현해 보고, 지속적으로 리팩토링을 하면서 코드를 가꿔 나가는 것입니다.

사실, MVVM이나 MVP와 같은 iOS 아키텍처들도 지속적으로 코드를 정리해 나간 결과물 입니다. 해 보기도 전에 어떻게 해야할 지를 알고 있었던 것들이 아닙니다. Apple의 MVC대로 코드를 작성하면서 겪은 수많은 시행 착오를 연구하고 고민한 끝에 나온 결과물 입니다.

"The best Architectures, requirements, and designs emerge from self-organizing teams"

좋은 코드를 만드는 방법은 더 좋은 아키텍처를 고르고 거기에 맞춰 코드를 써 나가는 것이 아닙니다. 좋은 코드는 조금씩 코드를 추가하면서 지속적으로 리팩토링을 하는 과정에서 점차 창발(emerge)합니다.

(Agile Manifesto에 적힌 12가지 원칙 중에서 인용했습니다.)

애증의 agile...

그래서 우리는 TDD로 간다

하지만 계속해서 리팩토링을 하게 되면 자연히 코드의 안정성을 떨어지게 됩니다. 좋은 아키텍처로 역할을 잘 분리놨다면, 조금 상황이 나을 수도 있겠지만, 아키텍처 자체가 바뀐다면 그마저도 불안정합니다. 지속적으로 리팩토링을 해 나가려면, 동시에 안정을 보장할 수 있는 방법도 같이 고민해야 합니다.

계속해서 리팩토링을 하면서도 안정성을 보장할 수 있는 방법은 무엇 일까요?

리팩토링을 잘하고 있는지, 잘못하고 있는지는 어떻게 빠르게 알 수 있을까요?

여러가지 방법이 있을 수는 있겠지만, 현존하는 가장 좋은 방법은 이 글을 쓰게 된 목적인 TDD(Test Driven Development)입니다. TDD는 지속적인 리팩토링을 가능하게 해주고, 우리가 매순간 '좋은 코드'를 유지할 수 있도록 하는 훌륭한 도구입니다.

출처: (https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html)

  • 테스트를 작성하면서, 코드의 동작을 미리 생각해보고 정의할 수 있습니다.
  • 테스트를 통과할 수 있을 만큼의 코드를 작성하는 것으로 불필요한 코드를 미리 만드는 것을 방지할 수 있습니다.
  • 리팩토링을 통해서 중복을 제거하고, 테스트를 통해 리팩토링이 잘 되었는지 즉각적인 피드백(feecback)을 얻을 수 있습니다.
  • 그리고 더 많은 좋은 점은, 그림의 출처에 있습니다.

사실, 지금까지 답정너 였습니다…

요약

지금까지의 내용을 요약해 보면, 다음과 같습니다.

  • 달리는 기차에 바퀴를 갈아 끼우려면, 필요한 것은 좋은 코드.
  • 좋은 코드를 얻는 방법은 지속적인 리팩토링.
  • 지속적인 리팩토링을 가능하게 하는 현존하는 가장 좋은 도구는 TDD.

다음 편에서는 "그렇다면, TDD 어디서부터 시작할 것인가?"라는 주제를 가지고, 좀 더 실제 예와 코드로 이야기를 이어 보겠습니다. (여러분 2편에서 봬요~)