iOS 캠프 [알쓸신잡: 성장하는 iOS 개발자 되기] 요약

Updated:

  • 2021.04.27 화요일 4시
  • 강연자: 전수열

1. 테스트

  • 모두가 중요하다고 생각은 하지만 모두가 하고있지는 않다
  • 무엇을 테스트할 것인가
    • End-to-End - ex: 전체 구매 과정
    • Integration - ex: 상품 상세 화면
    • Unit - ex: 상품 가격 뷰
    • 작은단위인 Unit 테스트부터 해보기를 권장

테스트 하기 쉬운 것부터

  • 가격 포맷터, 날짜 포맷터, Codable변환, String 익스텐션…
    • 입력이 같다면 출력도 같음
    • 사이드 이펙트가 없음
  • 이런 것도 굳이 테스트를 작성해야 할까?
    • 네. 익숙해지는 것이 중요하므로 많이 작성 해보는게 좋다
    • 테스트는 지식의 영역이 아니라 숙달의 영역
    • 테스트를 작성하다보면 요구사항이 명확해진다

테스트 첫 작성

func testPriceView() {
    let view = PriceView(costPrice: 10000, price: 6000)
    XCTAssertEqual(view.priceLabel.text, "[40%] 6,000원")
}

만약 원가가 정가와 같다면?

func testPriceView_whenDiscounted_displaysDiscountRateAndPrice() {
    let view = PriceView(costPrice: 10000, price: 6000)
    XCTAssertEqual(view.priceLabel.text, "[40%] 6,000원")
}

func testPriceView_whenNotDiscounted_displaysPriceOnly() {
    let view = PriceView(costPrice: 3000, price: 3000)
    XCTAssertEqual(view.priceLabel.text, "3,000원")
}

만약 할인율에 소숫점이 생긴다면?

func testPriceView_whenDiscounted_displaysRoundedDiscountRateAndPrice() {
    let view = PriceView(costPrice: 11000, price: 7000)
    XCTAssertEqual(view.priceLabel.text, "[36%] 6,000원") // 36.363636...
}

만약 정가가 0원이라면?

func testPriceView_whenPriceIsZero_displaysFree() {
    let view = PriceView(costPrice: 10000, price: 0)
    XCTAssertEqual(view.priceLabel.text, "무료")
}

이렇게 테스트를 작성해나가면 테스트 스펙이 곧 기능 명세가 된다

func testPriceView_whenNotDiscounted_displaysPriceOnly()
func testPriceView_whenDiscounted_displaysDiscountRateAndPrice()
func testPriceView_whenDiscounted_displaysRoundedDiscountRateAndPrice()
func testPriceView_whenPriceIsZero_displaysFree()

스펙을 더 명확하게 작성하고 싶다면 한글로 쓰기

func testPriceView__할인되지_않은_경우__가격만_표시합니다()
func testPriceView__할인된_경우__할인율과_가격을_표시합니다()
func testPriceView__할인된_경우__반올림된_할인율과_가격을_표시합니다()
func testPriceView__가격이_0인_경우__무료로_표시합니다()

테스트가 더 복잡해진다면?

  • 네트워크 요쳥, 시스템 프레임워크, 서드파티 라이브러리…
  • 입력이 같아도 출력이 다를 수 있다
  • 사이드이팩트가 생긴다

테스트 대역 (Test double): 연기자의 대역배우 개념

  • Dummy: 파라미터를 채우기 위해 필요한 개체
    • ex: 프로필 뷰를 테스트할 때 넘기는 User 개체
  • fake: 작동하긴 하지만 테스트만을 위해서 만들어진 구현체
    • ex: 실제 키체인에 저장하지 않고 메모리에서만 관리하는 키체인
  • Stub: 미리 지정한 결과를 반환할 수 있는 구현체
    • ex: 미리 지정한 이미지 선택 겨과를 반환하는 UIImagePickerStub
  • Spy: Stub에 더해서 함수 호출을 기록할 수 있는 구현체
    • ex: 호출된 메서드를 기록하는 MFMailComposeViewControllerSpy
  • Mock: 원하는 메서드가 의도한 대로 잘 호출되었는지를 검증할 수 있는 구현체
    • ex: 의도한 메서드가 호출되지 않으면 실패시키는 무언가

의존성 주입 (Dependency Injection)

현재 위치에따라서 테스트를 통과할 수도, 아닐수도 있음

func testGPSViewController_whenLoaded_displaysMyLatitudeAndLongitude() {
    // given
    let viewController = GPSViewController()

    // when
    viewController.loadViewIfNeeded()

    // then
    XCTAssertEqual(viewController.latitudeLabel.text, "37.5093999")
    XCTAssertEqual(viewController.longitudeLabel.text, "126.8855371")
}

의존성 주입하여 실제 현재위치가 아닌 테스트를 위한 위치값 지정

func testGPSViewController_whenLoaded_displaysMyLatitudeAndLongitude() {
    // given
    let locationManager = CLLocationManagerStub()
    locationManager.stubbedLatitude = 37.5740381
    locationManager.stubbedLongitude = 126.9745863

    let viewController = GPSViewController(locationManager: locationManager)

    // when
    viewController.loadViewIfNeeded()

    // then
    XCTAssertEqual(viewController.latitudeLabel.text, "37.5740381")
    XCTAssertEqual(viewController.longitudeLabel.text, "126.9745863")
}

TDD

  • 아래 싸이클 반복
    • 실패하는 테스트부터 작성
    • 테스트를 통과하는 최소한의 구현 작성
    • 지저분한 구현 개선
  • 리팩토링의 진짜 의미
    • 실패하는 테스트가 존재
    • 테스트를 통과하는 최소한의 구현이 존재
    • 다시 작성하는 것이 아님(다시 작성하는건 재작성)
    • 설계 변경 없이 구현만 개선하는 것! (테스트 코드는 변경되지 않음)

2. CI/CD 파이프라인

  • 테스트 작성도 중요하지만, 테스트가 계속 실행되고 검증되는 것이 중요
  • CI (Continuous Integration): 지속적 통합
    • 개발-푸시-린트-빌드-테스트
  • CD (Continuous Delivery): 지속적 배포
    • 개발-푸시-빌드-배포
    • 스테이징: 개발-푸시-빌드-배포(테스트플라이트)
    • 프로덕션: 개발-푸시-빌드-배포(앱스토어)
  • 사람이 할 일을 기계가 대신해줌으로서 사람은 더 중요한 일에 집중

3. 함께 성장하기

피드백 루프 만들기

  • 주기적으로 유효한 피드백을 동료들과 나눌 수 있는 환경
    • 행동 -> 피드백 -> 개선: 반복하며 성장
  • 짝 프로그래밍 (실시간 피드백)
    • 역할을 자주 바꾸는 것이 좋음
    • 서로의 암묵지 꺼내기 (나는 당연하다고 생각하지만 상대방은 아닐 수 있음)
    • 놓쳤다면 바로 질문하기
  • 코드 리뷰 (작업 단위 피드백)
    • 문제가 없는지 검증하는 것은 기본
    • 배경이나 의사결정 등 맥락 전달
    • PR 본문도 리뷰의 대상
    • 좋은 PR은 리뷰어가 리뷰하기 좋은 PR
    • 코딩 스타일은 웬만하면 기계가 하도록
  • PR 템플릿
    • 배경: 왜? 누가 요청?
    • 작업 내용: 작성한 코드 요약
    • 테스트 방법: 이 코드를 어떻게 테스트 할지
    • 리뷰 노트: 리뷰어에게 주는 부가적인 정보
    • 스크린샷(Before, After): 아카이빙에도 도움됨
  • 회고 (이터레이션 단위 피드백)
    • 탓하는 과정이 아니라 더 나아지기 위한 과정
    • 감정에 더 솔직해지고 감정 상태를 더 많이 공유하기
    • 왜 그런 감정을 느꼇는지에 대해서 집중적으로 회고해보는 것이 도움 됨
  • 팀 회고
    • 만족: 다음번에도 그대로 할 것들
    • 반성: 다음번에는 다르게 할 것들
    • 개선: 개선할 수 있을 것들
  • 개인 회고
    • Fact: 무엇을 했고
    • Feeling: 무엇을 느꼇고
    • Finding: 어떤 교훈이 있었다

질문 답변

  • CI/C툴로 무엇을 사용하는지?
    • GitHub Actions 사용이 가장 편했음
  • 짝 프로그래밍은 어떤 경우에 하는지? 실제 업무에도 하는지
    • 극단적으로 모든 커밋을 짝프로그래밍으로 하는 회사도 있음
    • 새로운 멤버가 온보딩 했을 때
    • 어떤 도메인을 잘아는 멤버가 있고 그걸 배우고 싶을 때
    • 더 많이 알고 있는 사람이 작성을하는 것이 지켜보는 사람이 배울 수 있는 것 같음
  • 신입을 뽑을 때 중요하게 보는 것
    • 개발 자체를 즐거워 하는 것이 느껴짐
    • 다야한 프로젝트를 도전해보고, 코드를 공개하고, 잘 정리한 모습
  • 오픈소스 컨트리뷰트를 해보고 싶은데 어떻게 시도해볼지/?
    • 문서의 오탈자를 찾아보는것 부터 시작해봐도 좋음
    • 문서의 한글 번역이 없다면 한글 번역을 해본다던가

Leave a comment