이상한모임 주최(주관? 주도?)로 <TDD(Test Driven Development) 를 통한 iOS 앱 개발>이라는 스터디를 시작하였다. 오늘이 그 첫 모임. 간단히(가 될지는 모르겠지만(괄호를 너무 남발하는것 같아(알면 그만하던가))) 그 내용에 대해 복기 하고 이해한 것에 대해 정리해보고자 한다.

본문의 내용은 다른분들께서 더 잘 정리해 주신 것도 있고, 스터디 내용은 @happy__engineer 님께서 최종 정리해주실 예정이니 아래의 노트는… 그냥 스킵해도 된다. 사실 얘기하고 듣느라 메모 안한 내용이 80%쯤 되는 것 같다.


BeBEmdpCQAA3rfu.jpg-large
초상권따위…

1장. 소프트웨어 테스트와 단위 테스트

테스트의 목적: What for?

  • 1차적 목적
    • 소프트웨어 테스트를 하는 목적은 소프트웨어가 효과적이라는 것을 보여주는 것(잘 돌아간다!)이지, 버그를 잡기 위함이 아니다.
    • 테스트 구현 시점이 실제 코딩 시점보다 빨라야 한다. 구현하기 전에 테스트 코드를 작성하라.
  • 2차적 목적
    • 모듈별로 테스트해서 통과했음에도 불과하도 리얼환경에서는 모듈간의 간섭 등으로 버그가 발생한다.
    • 테스트를 많이 하면 할 수록 좋지만, 테스트 인력 및 엔지니어의 시간과 노력이 들어가므로 합의점이 필요하다.
    • 테스트를 통해 (비용의 감소를 통해) 이윤을 극대화 해야한다.
  • 기타
    • TDD에 대해 필요성과 이해가 충분히 선행되어야 한다. 그렇지 않으면 효율에 대한 기대감 대비 실망감이 커져 테스트주도 개발에 어려움을 겪게 된다.

테스트의 대상: Who?

  • 폭포수 모델 (개발방법론 중 하나)
    • 초기에 정확하지않은 정보로 요구사항을 정의하고 일정을 픽스
    • 나중에 발생되는 이슈에 대해 정상적으로 대응할 수 없다. 빼고가거나 일정을 늦추거나.
    • 방식 : 요구사항 분석 – (분석이 완벽한 것으로 착각) – (부실한) 설계 – 병렬구현 – 테스트 – 배포
    • 문제점
      • 프로젝트 개발 초기엔 요구사항은 완벽하지 않다. 완벽한 설계란 어렵다.
      • 단계별로 분리되어 있어서 한 단계의 끝은 다음 단계의 시작점이므로,
      • 이전 단계와 다음 단계에 걸쳐 생기는 이슈에 대응할만한 시간적 여유를 두는 것이 어렵다.
      • 이전 단계가 모두 정상적으로 설계/동작한다는 보장이 어렵다.
  • 폭포수 모델에서 최종 테스트 단계에서 코드 수정을 가하는 것은 리스크가 크다.
  • 그러므로 개발자가 중간중간에 테스트를 꼭 해야한다.

테스트의 시기: When?

유닛 테스트는 구현 전이나 구현 후에 항상 해야한다.

테스트의 실제 사례

iOS의 경우에는 반드시 UI가 필요한 구조여서 UI Automation Instrument를 이용해 테스트 할 수 있다.
자세한 내용은 3장부터…

단위 테스트가 적합한 곳

  • 유닛테스트
    • 유닛테스트 목적
      • 프로그램의 각 부분을 고립(격리)시켜 각 부분이 정확하게 동작하는지 확인하여 상호 종속성(Dependency)이 없게 개발하면 각 유닛테스트를 완료하는 것은 그 자체로 완성되어 가는 것이다.
      • 퇴행버그(잘 되던 것이 안되기 시작)를 막을 수 있고, 쉽게 확인도 가능하다. 이후 업데이트 등으로 추가적인 서비스가 붙었을 때 발생하는 이슈에 대해 대응하기 쉽다.
    • 유닛테스트를 하지 않으면?
      • 고립되어 있지 않으면 다음 단계의 테스트를 진행할 때 이전 데이터(메모리, 결과값 등..)가 남겨져 있는 경우가 발생하여 영향을 받을 수 있다.
      • 이전 단계의 유닛은 패스하였으나, 현재 유닛에서 이슈가 발생하는 경우에 이전 결과값이나 데이터의 영향인지, 현재 유닛에서 발생한 이슈인지 정확히 파악하는 것이 어렵다.
    • 기타
      • 심도있는 테스트를 위해 개발자 이외의 테스터가 참여해야 한다.

단위 테스트가 iOS 개발자에게 의미하는 점

  1. 비교적 적은 비용으로 많은 비용을 아낄 수 있다.
  2. 개발의 시간과 디버깅을 하는 시간을 확보한다.

덧. 스터디 관련 글 더보기


아니 제목은 거창하게 달아놓고 본문 내용을 날로먹은건가!!! 싶어 창을 닫았다면 스크롤을 끝까지 안내려보신…게 아니라 나가신 분들이니 이 글도 못읽을 수 있겠구나…OTL 어쨋든 당신의 프로젝트에 TDD가 필요한 이유라고 제목을 달았으니, 이제부터는 철저하게 기획자, 운영자, 그리고 경영자를 위해 쓰는 글이 될 것이다. 아니 그래야만 한다 -_- …

오늘은 개념에 대한 이야기였으니, 그래서 TDD가 뭔데? 라고 묻는다면 딱 한마디로 보험 이라고 정의하고 싶다. 뭔 말인지 모르겠다면 다음의 드립(…)을 보자.

대한민국 사망 원인 1위 암. 하지만 최근 의료기술의 발달로 암 생존률이 높아지고 있습니다.
( 그것도 있는사람들 얘기죠. 치료비에 생활비까지.. )
게다가 암환자 열명 중 여덟은 실직을 하고, 네 명 중 한 명은 돈때문에 치료를 포기합니다.
그래서 암보험이 필요한거죠. 물가가 높아져도 나이가 많아져도 보험료는 오르지 않습니다. 게다가 각종암에 희귀암도 보장합니다.

케이블방송 좀 보신 분들이면 익숙한 광고멘트일 듯? 모 암보험의 광고카피다.

우리는 걸릴지 안걸릴지 모르는 몇가지 질병을 위해 매달 3~5만원씩을 보험사에 지불한다. 평생 안걸리면 그냥 날리는 돈임에도 불구하고, 암 같은 병에 걸리면 엄청나게 많은 돈이 들어가기 때문에 몫돈지출을 막기위해서 평소에 투자를 해두는 것이다. 하지만 보험이라는 것은 약관이 존재하여 모든 질병에 대해서 보장되는 것도 아니고, 어떤 질병에 대해서만, 그것도 지급보장액 만큼만 보험료를 청구할 수 있다.

앱 평점하락 원인 1위 크래시. 하지만 최근 레퍼런스의 발달로 크래시 발생률이 낮아지고 있습니다.
( 그것도 개발팀이 짱짱한 회사의 얘기죠. 빠듯한 개발일정에 수많은 버그까지.. )
게다가 개발자 열명 중 여덟은 야근을 하고, 네 명 중 한 명은 일정때문에 테스트를 포기합니다.
그래서 TDD가 필요한거죠.
요구사항이 많아져도, 기능이 많아져도 테스트비용은 오르지 않습니다. 게다가 UI 테스트, 자동테스트도 보장합니다.

그래서 TDD는 보험이라고 한것이다. 사실 테스트 주도 개발이라고 하지만, 그 수많은 테스트 케이스를 전부 작성할 수는 없다. TDD를 적용한 프로젝트라고 해서 크리티컬한 버그가 생기지 말라는 법도 없다. 하지만 TDD 방식으로 코드를 작성하고 유닛테스트를 통해 코드를 안정화 시킬 수 있다. 그리고 추후에 발생하는 이슈에 대해서도 유연하게 대처가 가능하다.

아래의 표를 보면 그 ‘몫 돈’에 대해 이해가 더 쉽다.(과연…?)

스크린샷 2014-01-16 오전 2.40.09

요구사항에 문제가 있었고, 그걸 요구사항 분석할때 발견하면 비용은 1이 들어간다. 왜냐면 쓱- 고치면 되니까. 마찬가지로 요구사항은 정상이지만, 설계단계에서 문제가 발생했고 설계 중에 고쳐내면 역시 비용은 1이 들어간다.

하지만 요구사항에서 문제가 있었으나 배포 이후에 그것을 발견하면 비용은 10~100이 들어간다. 요구사항쯤이야 업데이트를 통해 반영할 수도 있고, 추가해줄 수도 있는 내용이다. 물론 다 갈아엎어야 되는 반드시 필요한 요구사항이 누락되었을 수도 있다. 여튼 개발하기 귀찮거나 뭐하면 기획자와 딜해서 빼면 그만이다. 여기서 중요한건 설계 단계에서 문제가 발생하였으나, 배포 이후에 발견했을 때 비용은 전자보다 높은 25~100이 들어간다는 것이다. 설계단계에서 잘못된 건 그것을 바로잡기 위해서는 전체 구조가 뒤집어질 수 있기 때문이다.

비용에 대해 이해가 쉽지 않다면 ‘리스크’라는 말로 바꿔봐도 될 것 같다.
SQLite로 DB를 설계했는데 잘못생각해서 나중에 CoreData로 바꿔야 한다면… 마이그레이션 잘못했다가 데이터 전부 날리고 CS 폭주하며 별점은 (0점이 없어서 주는)1점과 욕으로 리뷰가 도배되는 순간 앱의 인생은 끝났다고 보면 된다. 이렇게 극적인 상황을 만들어 놓고 보면 사실 그 비용은 100으로 보기에도 너무 적다는 생각이 든다.

또 한국 기획자(난 아니라고 우기..고 싶다) 특성상 기능 붙이는 걸 참 좋아한다. 그래서 시작은 미약하였으나 그 끝은 창대한 경우가 굉장히 많다. 만약 유닛테스트와 모듈화가 잘 이뤄지지 않았다면 하나의 기능이 영향을 미치는 모든 다른 기능까지 모두 테스트를 해야한다. iOS의 경우 OS버전별로 API 차이가 심한 경우가 많은데, 만약 테스트 코드가 없다면 모든 버전에 대해서 n번 이상의 테스트가 진행되어야 하고, 이는 서비스가 지속될수록 그 비용이 증가할 수밖에 없다. 테스터들에게 “똑같은 것만 도대체 몇번째야 더러워서 테스트 못해먹겠으니 제발 최소지원버전 좀 높여주세요” 라는 컴플레인을 듣기쉽다.

초기 버전에 대한 개발비용/테스트 비용만 생각하지말고 이전버전 혹은 업데이트 시에 들어가는 개발비용/유지보수비용/테스트 비용까지 생각한다면, 수작업 테스트를 피해 반복되는 테스트를 상당부분 자동화 할 수 있는 TDD는 어쩌면 필수인 것이다. (보험 가입상담은 @#$!@#$로 전화주세요~)

물론 특약(테스트 케이스)을 추가하거나, 갱신기간에 따라 보험료(유지보수비용)가 증가하는 것은 보험이나 TDD나 매한가지다. 코드가 늘어날수록, 테스트 코드도 함께 추가해줘야 한다. 그러나 테스트 케이스가 추가되면 커버할 수 있는 이슈들도 많아지므로 유지보수/안정화 과정을 통해 코드 커버리지도 높아지게 된다.

적용하기

주절주절. 말이 많았던 것 같다. 여튼 위에서 하고싶었던 말은 당신의 프로젝트 TDD가 필요한 이유였고, (이상적이긴 하지만) 적용은 아래와 같이하는 것이 좋겠다.

  1. 기획자에게 TDD 적용하기
    • 기획시 UI/UX 이외에 구조적인 단위(unit)를 고려하며 기획
    • 요구사항과 함께 필요한 테스트케이스와 결과값(화면 포함)에 대해서 기술
    • 작성한 테스트 케이스는 운영팀과 개발팀에게 공유
    • 유닛테스트에 참여하여 테스트케이스 보완
  2. 운영자에게 TDD 적용하기
    • 테스트 케이스와 테스트 케이스 이외의 테스트방법을 정리 및 업데이트
    • 유닛테스트에 참여하여 테스트케이스 보완
    • 앱 출시 후 발생한 이슈를 앱버전, OS버전별로 기록
    • 버전 업데이트시 해당 내용을 테스트 코드로 반영할 수 있도록 기획팀과 개발팀에게 공유
  3. 경영자(PM포함)에게 TDD 적용하기
    • 프로젝트 기간에 개발과 풀테스트와는 별개로 테스트코드 작성과 유닛테스트를 진행할 수 있는 기간을 고려하여 산정
    • 유닛테스트 및 모듈테스트 시 기획자 및 운영자가 테스터로 참여할 수 있도록 개발 프로세스 변경 및 적용