1. 첫 번째 학습 내용: 콜렉션뷰

A. 테이블뷰와 컬렉션뷰의 공통점과 차이점에 대해 이야기해봅시다.

  • 테이블뷰 - 부스트코스 설명
    • 테이블뷰는 iOS 애플리케이션에서 많이 활용하는 사용자 인터페이스입니다. 테이블뷰는 리스트 형태를 지니고 있으며 스크롤이 가능해 많은 정보를 보여 줄 수 있습니다.
  • 컬렉션뷰 - 부스트코스 설명
    • iOS 애플리케이션에서 컬럭센뷰는 그리드와 스택, 타일, 그리고 원형 배열을 포함하여 다양한 유연성을 제공하는 인터페이스입니다. 컬렉션뷰는 유연하고 변경 가능한 레이아웃을 사용하여 데이터 아이템의 정렬된 세트를 표시하는 수단입니다. 컬렉션뷰의 가장 일반적인 용도는 데이터 아이템을 그리드와 같은 형태로 표현합니다. 더불어 다양한 방법으로 컬렉션뷰의 레이아웃을 사용자 정의할 수 있습니다.

공통점

  • 여러가지를 나열한다
  • 셀을 재사용한다

차이점

  • 테이블뷰 수직 나열 / single column
  • 컬렉션뷰 수직+가로 나열, 앨범처럼 볼수 있음 / 그리드, 스택, 타일 / ordered collection of data items

컬렉션뷰 셀 vs 테이블뷰 셀

컬렉션뷰를 학습하면서 앞서 배웠던 테이블뷰와 비슷한 점이 많지 않았나요? 그렇다면 컬렉션뷰 셀과 테이블뷰 셀에는 어떠한 차이점이 있는지 알아볼까요?

  • 테이블뷰 셀의 구조는 콘텐츠 영역과 액세서리뷰 영역으로 나뉘었지만, 컬렉션뷰 셀은 배경뷰와 실제 콘텐츠를 나타내는 콘텐츠뷰로 나뉘었습니다.
  • 테이블뷰 셀은 기본으로 제공되는 특정 스타일을 적용할 수 있지만 컬렉션뷰 셀은 특정한 스타일이 따로 없습니다.
  • 테이블뷰 셀은 목록형태로만 레이아웃 되지만, 컬렉션뷰 셀은 다양한 레이아웃을 지원합니다.

A-1. DataSource와 Delegate의 역할

데이터소스

컬렉션뷰 데이터소스 객체는 컬렉션뷰와 관련하여 가장 중요한 객체이며, 필수로 제공해야 합니다. 컬렉션뷰의 콘텐츠(데이터)를 관리하고 해당 콘텐츠를 표현하는 데 필요한 뷰를 만듭니다. 데이터소스 객체를 구현하려면 UICollectionViewDataSource 프로토콜을 준수하는 객체를 만들어야 합니다. 그리고 데이터소스 객체는 최소한 collectionView(_:numberOfItemsInSection:)과 collectionView(_:cellForItemAt:) 메서드를 구현해야 하며 나머지 메서드는 선택사항입니다.

델리게이트

컬렉션뷰 델리게이트 프로토콜은 컬렉션뷰에서 셀의 선택 및 강조표시를 관리하고 해당 셀에 대한 작업을 수행할 수 있는 메서드를 정의합니다. 이 프로토콜의 메서드는 모두 선택사항입니다.

A-2. 뷰의 재사용

UICollectionViewDataSource

  • Use the [dequeueReusableCell(withReuseIdentifier:for:)](https://developer.apple.com/documentation/uikit/uicollectionview/1618063-dequeuereusablecell) to get a cell for an item in the collection view. (TableView, CollectionView)
  • Use the [dequeueReusableSupplementaryView(ofKind:withReuseIdentifier:for:)](https://developer.apple.com/documentation/uikit/uicollectionview/1618068-dequeuereusablesupplementaryview) method to get a supplementary view requested by the layout object. (Only CollectionView)

Asks your data source object to provide a supplementary view to display in the collection view.

A-3. 커스텀 뷰(레이아웃)의 구현

테이블뷰

  • 테이블 뷰는 plain과 grouped 스타일 중 한 가지의 스타일을 가질 수 있습니다.

    테이블 뷰에서는 하나의 열에 여러 행을 표시하는 형식이기 때문에, 셀의 모습을 행에 맞춰서 디자인합니다.

컬렉션뷰

  • 반면, 컬렉션뷰는 열과 행을 만들 수 있기 때문에, 꼭 행의 모습이 아니더라도 다양한 모습으로 셀을 디자인할 수 있습니다. 컬렉션 뷰 셀의 가장 큰 특징이기도 하죠.

    (http://labs.brandi.co.kr/2018/05/02/kimjh.html)

  • 또한 컬렉션 뷰는 레이아웃 객체가 있습니다. 기존에 제공하는 flow layout을 사용해도 괜찮지만, 본인이 원하는 레이아웃 모양을 custom layout을 만들어서 사용합니다. 이를 담당하는 프로토콜은UICollectionViewDelegateFlowLayout 입니다.

A-4. indexPath

  • tableView - Returns an index path representing the row and section of a given table-view cell.
  • collectionView - Gets the index path of the specified cell.
    • supplementary 메서드 차이?

A-5. Header/Footer

  • 차이점은 dequeueReusableSupplementaryView 사용

C. 궁금한 점

  • 컬렉션뷰에서 다 되는데 왜 테이블뷰랑 나누어져있을까?

    • 테이블 형태가 편하고 많이 쓰여서 만들어놓은 거 아닐까?

    • 차이점: 레이아웃

    • 컬렉션 뷰는 열과 행을 만들 수 있기 때문에 꼭 행의 모습이 아니더라도 다양한 모습으로 셀을 디자인할 수 있다.

    • 테이블뷰를 쓰다가 불편한 점이 있어서 컬렉션뷰를 만든 게 아닌가? (디자인적 요구 업그레이드)

    • 컬렉션뷰로 다 할 수 있는데 테이블뷰가 왜 있을까요?

      • 원래는 테이블뷰만 있었는데, 그 이후 컬렉션뷰를 만든 게 아닌가? 차이점을 알고 싶다.

      • 컬렉션 뷰도 세로로 하면 테이블 뷰가 아닌가? 그럼 무엇을 사용해야 하나?
        - 귀찮은 건 싫으니까 편한걸로?
        - Human Interface Guidelines/switches 에 따르면 스위치는 테이블뷰에서만 사용하라고 하던데?
        - (야곰) 기능에 따라 적절하게 선택하면 될 것 같네요~ 😱

        Disadvantages of UICollectionView

        Major disadvantages of using UICollectionView is auto sizing of your cells, we don not really need auto sizing that much so it’s not that big of a problem. It takes a lot of trial and error to get auto sizing to work correctly ( not saying I’m good at it ). UITableView wins here as all you have to do is return UITableView automatic dimensions for the height of your row in each one of your cells.

        So to sum this up, if you need something standard like most table views in iOS I will choose the UITableView, but if you need more control over your layout, go with UICollectionView.

        https://medium.com/@nitpaxy/swift-3-uicollectionview-vs-uitableview-9909bbc0ec66

2. 두 번째 학습 내용: Swift protocol을 사용해 MockURLSession 이용한 테스트

iOS Networking and Testing by 우아한 형제들 기술블로그
Unit Testing URLSession using URLProtocol. by Dhawal Dawar
[[iOS] Custom Mock Network Request](http://minsone.github.io/ios/mac/ios-mock-network-request](http://minsone.github.io/ios/mac/ios-mock-network-request)
Class URLProtocol

  • 테스트 관련 용어

    1. SUT: (System Under Test) : 주요 객체(primary object)
    2. Test Doubles: 오리지널 객체를 사용해서 테스트를 진행하기가 어려울 경우 이를 대신해서 테스트를 진행할 수 있도록 하는 객체를 의미한다.
    3. dummy
    • 더미 객체는 말 그대로 멍청한 모조품, 단순한 껍데기에 해당한다.
    • 단지 인스턴스화된 객체가 필요할 뿐 해당 객체의 기능까지는 필요하지 않은 경우에 사용한다.
    • 따라서 해당 더미 객체의 메소드가 호출됐을 때의 정상 동작은 보장되지 않는다. 보통은 타입 기본값(0, null, false 등)으로 반환값을 만들어주는 선에서 마무리한다.
    1. stub:
    • 더미 객체가 마치 실제로 동작하는 것처럼 보이게 만들어놓은 객체를 의미한다.
    • 더미 객체로 만들어진 인스턴스의 메소드를 호출하면 타입 기본값으로 리턴되거나 아무 일도 일어나지 않는 데 비해 테스트 스텁은 객체의 특정 상태를 가정해서 만들었기 때문에 특정한 값을 리턴하거나 특정 메시지를 출력한다.
    1. Mock:
    • 일반적으로는 조각하기 쉬운 재료를 이용해 추후 만들어질 제품의 외양을 흉내 낸 모조품을 의미한다.
    • 소프트웨어 개발에 있어서 Mock은 모듈의 겉모양이 실제 모듈과 비슷하게 보이도록 만든 가짜 객체다.
    • 우리가 구현을 하는 데 필요하지만 실제로 준비하기엔 여러 어려움이 따르는 대상을 필요한 부분만큼만 채워넣어서 만들어진 객체를 의미한다.
    1. fake:
    • 여러 개의 인스턴스를 대표할 수 있는 경우이거나, 좀 더 복잡한 구현이 들어가 있는 객체를 지칭하는 단어이다.
    • 복잡한 로직이나, 객체 내부에서 필요로 하는 다른 외부 객체들의 동작을 비교적 단순화하여 구현한 객체이다.
    • 결과적으로는 테스트 케이스 작성을 진행하기 위해 필요한 다른 객체들과의 의존성을 제거하기 위해 사용된다.
    1. spy:
    • 테스트에 사용되는 객체에 대해 특정 객체가 사용됐는지, 그리고 그 객체의 예상된 메소드가 정상적으로 호출됐는지 확인해야 하는 상황에서 호출 여부를 몰래 감시하여 기록했다가 요청이 들어오면 해당 기록을 전달한다.

    • 특정 메소드의 정상호출 여부 확인을 목적으로 구현되며, 더미부터 시작해서 페이크 객체에 이르기까지 테스트 더블로 구현된 객체 전 범위에 걸쳐 해당 기능을 추가할 수 있다. 테스트 스파이 객체는 다른 동작을 하면서 스파이 기능까지 하는 경우가 많다.

      출처:

      "TDD 실천법과 도구" 책 전체를 PDF 공개합니다. https://repo.yona.io/doortts/blog/issue/1

      TDD (Test Driven Development) 란? https://velog.io/@sbyeol3/TDD-Test-Driven-Development-란

3. 세 번째 학습 내용: URLSessionDataTask

Class
URLSessionDataTask

A URL session task that returns downloaded data directly to the app in memory.

class URLSessionDataTask : URLSessionTask

A URLSessionDataTask is a concrete subclass of URLSessionTask. The methods in the URLSessionDataTask class are documented in URLSessionTask.

4. 네 번째 학습 내용: Equatable Protocol

  • 테스트로 비교하려고 하는 클래스에 Equatable 채택
struct Item: Codable, Equatable {
	let id: Int
}

이렇게 하면 모든 프로퍼티를 하나하나씩 비교하는 것이 아니라
객체를 만들어서 한번에 비교할 수 있다고 함

Protocol Comparable
출처:
SJ, Steven 프로젝트 게시판

문제점/고민한점 → 해결방안

// JokesAPIProvider에 URLSession 생성자 주입시 프로토콜타입으로 받도록 합니다.
class JokesAPIProvider {
    ...
    let session: URLSessionProtocol

    init(session: URLSessionProtocol = .shared) {
        self.session = session
    }
    ...
}

분명히 우아한형제들에 나와있는데로 똑같이 따라했는데 에러남

Error message: Type 'URLSessionProtocol' has no member 'shared'

에러 메시지대로 URLSessionProtocol에는 shared가 없는데 뭘 어떻게 가져오겠다는건지... 이해가 안됐음

(shared는 URLSession에 있는건데 🤔)

결국 헤매다 답을 못 찾아서

올라프의 도움을 받아서 해결했다

  • 해결 방법

    그냥 .shared가 아니고 URLSession.shared를 넣어줘야 한다.

    class JokesAPIProvider {
        let session: URLSessionProtocol
    
        init(session: URLSessionProtocol = URLSession.shared) {
            self.session = session
        }
    }

    댓글에는 이 부분에 대한 지적은 없고 찬사만 있을 뿐이고... 🤔
    올라프 말로는 이걸 이미 아는 사람들은 눈으로 걸러서
    이상하다고 못 느꼈을 것 같다고 함...
    아무것도 모르는 초보자인 나는 그대로 따라치기만 해서 헤맸다 🤦‍

테스트할 때 객체 자체를 비교하는 방법

객체 내의 값을 하나하나 까서 비교하기보다 객체 자체를 비교할 수 있는 방법은 없을까요?

제임스 레슨 - 무엇을 테스트할 것인가?

나무상자에서 빨간 사과를 꺼내는 '행동'을 테스트해라
빨간 사과가 존재하지 않으면
행동 자체를 테스트를 못함

테스트를 할 때
네모난 빈 사각형 안에 있는
동그란 빨간색 물체를 가져오는 것을 테스트해라

프로토콜도 똑같음
직접 네트워크에 접근해서
데이터 통신이 되는 것을 테스트하는게 아니라

네트워크라는 것을
프로토콜로 껍데기만 구현하고
네트워크를 가져오는 '행위' 자체만 테스트

URLSession을
URLSessionProtocol을 채택하게 해서
직접 네트워크에 접근해서 데이터

나는 데이터를 갖고올 수 있어요!
그 동작을 테스트해서
되느냐 아니냐?

URLProtocol은 프로토콜 아니고
클래스임

올라프 레슨 - '동작'을 테스트해라

기존 테스트
getName
네임을 제대로 받는지 테스트

네트워크 모킹한다는 거는
네트워크 환경에서 어떤 걸 줬는데
어떤 동작을 하는지를 검증?

네트워크가 왔을 때
그 동작을 테스트한다 생각

클래스에 대한 로직은
구현체를 테스트

array를 관련한 struct를 만듦
array 1234 만듦

Test code - given, when, then

테스트 코드를 작성할 때 상황(given), 실행(when), 결과(then)로 나눠서 작성하는 것입니다. 이렇게 되면 테스트 코드를 세 단계로 나눠서 볼 수 있기 때문에 좀 더 가독성이 좋아져요.

// given
 // 해당 테스트를 검증하기 위해 필요한 상황을 설정

 // when
 // 로직 실행

 // then
 // 결과 검증
}

네트워크는 어떤 환경을 재현하기가 힘듦
네트워크에서 404 에러 내주는 경우
어떻게 재현할거냐?

프로토콜을 쓰면 404 에러를 내려주면 되기 때문에
그 동작을 검증해주면 됨
⇒ 프로토콜로 동작을 검증해주겠다

캐시

entity tag
response에 대한 last modified가 있으면 그걸 가져오고
아니면 cache를 가져온다? 🤔

쿠키, 세션, 캐시가 뭔가요? by 얄팍한 코딩사전

캐시 정책
캐시 크기는 얼마나 할건지?
비우는 주기는 얼마나 할건지?
비울때 알고리즘은 어떻게 할건지?

Q. 캐시랑 저장하는 것의 차이?
로컬 캐시를 구현하면 남는 장사다 !!!! by 올라프
UI는 노하우만 알면 어느정도 함

Q. 테스트 코드에서 강제 언래핑 해도 괜찮은 이유?
(프로젝트 코드에선 강제 언래핑 절대 안함)

테스트 코드를 할때
뭐가 중요하냐에 초점을 맞춘다면
encode할때 try에 ! 박은 건
넘어온 데이터를 제대로 하냐가 중요한거

어차피 하나라도 실패하면
다 테스트하는게 테스트 코드

인코드된 데이터를 넘겼는데
잘 디코드해서
내가 기대한 값이랑 같냐?
그게 중요함

profile
iOS Developer

0개의 댓글