Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

검색키워드를 입력하면 서버 데이터를 전달받아 목록 화면에 나타냅니다. #1

Merged
merged 7 commits into from
Aug 2, 2022

Conversation

just1103
Copy link
Owner

@just1103 just1103 commented Aug 2, 2022

📚 배경

  • 사용자가 검색한 도서 데이터를 서버에서 받아와 검색 목록을 나타냅니다.
  • 검색키워드를 입력할 때마다 목록이 즉시 업데이트되도록 구현하여 사용자가 원하는 검색결과를 신속히 파악할 수 있도록 했습니다.
  • 도서의 평점 (averageRating)을 목록 화면에 5개 별 이미지로 나타내어 사용자가 인기 있는 도서를 쉽게 찾을 수 있도록 구현했습니다.
  • 서버 데이터에 도서의 imageURL이 없는 경우가 빈번하여 임의의 SF Symbol 이미지를 나타내어 에러 화면으로 인식되는 것을 방지했습니다.

📚 작업 내용

  • URLSession을 통해 네트워크 통신을 구현했습니다. (MockURLSession을 통한 테스트 실행)
  • SearchController를 통해 사용자가 검색어 (책 제목, 저자, 출간연도 등)를 입력합니다.
  • 검색어가 입력될 때마다 해당 키워드를 포함하는 데이터를 서버에서 받아옵니다.
  • 총 검색결과 수는 상단의 Label에 나타내고, 검색된 도서는 CollectionView에 나타냅니다.
  • 화면을 아래로 Scroll하여 목록 하단에 도달하면 서버에서 다음 페이지의 데이터를 받아오도록 Pagination을 구현했습니다.
  • 데이터를 받아올 때 ActivityIndicator를 통해 로딩 애니메이션을 보여줍니다.
  • 이미지 Cache를 구현했습니다.

1. 네트워크 구현 및 API 추상화

RxSwift를 활용하여 비동기 작업을 처리했습니다. 서버에서 받아온 데이터는 Observable 타입으로 반환하고, ViewModel에서 ViewController에 전달 (Binding)하여 화면에 나타내도록 구현했습니다. 이때 데이터를 화면에 나타내는 최말단 시점에만 Subscribe하여 Stream이 끊기지 않는 구조를 유지했습니다.

또한 API를 열거형으로 관리하는 경우, API를 추가할 때마다 새로운 case를 생성하여 열거형이 비대해지고, 열거형 관련 switch문을 매번 수정해야 하는 번거로움이 있었습니다. 따라서 API마다 독립적인 구조체 타입으로 관리되도록 변경하고, URL 프로퍼티 외에도 HttpMethod 프로퍼티를 추가한 APIProtocol 타입을 채택하도록 개선했습니다. 이로써 코드유지 보수가 용이하며, 협업 시 각자 담당한 API 구조체 타입만 관리하면 되기 때문에 충돌을 방지할 수 있습니다.

2. MVVM-C 적용

Coordinator를 통해 의존성 주입을 관리하고, 화면전환 역할을 전담하도록 했습니다. 이를 위해 navigationController생성자 주입으로 하위 ChildCoordinator에 전달하고, 화면전환 시 해당 navigationController가 다음 화면을 push 하도록 했습니다.

3. DiffableDataSource 및 Snapshot 활용

사용자가 검색 키워드를 입력할 때마다 목록을 업데이트하도록 구현하기 위해 DiffableDataSource를 활용했습니다. reloadData()를 호출할 필요가 없으므로 CollectionView Cell이 업데이트될 때마다 애니메이션이 적용되어 UX 측면에서 유리하다고 판단했습니다.

또한 RxSwift를 통해 ViewModel과 ViewController를 Binding 시켜 역할을 분리했습니다. 예를 들어 사용자가 목록화면에서 스크롤을 최하단으로 내리면, ViewModel은 서버를 통해 데이터를 업데이트하고, ViewController는 Snapshot을 apply하여 화면을 다시 그리도록 했습니다.

4. Compositional Layout 활용

CompositionalLayout을 활용하여 Item/Group/Section 요소를 반응성 있게 배열했습니다. 또한 높이는 estimatedHeight, 너비는 fractionalWidth를 활용하여 Cell의 크기가 Device에 따라 유동적으로 조절됩니다. 특히 estimatedHeight를 사용하여 Cell의 높이를 고정하지 않고, Cell의 내부 구성에 따라 자동으로 산정하도록 했습니다.

5. SearchController를 통한 검색창 구현

UISearchController를 활용하여 NavigationBar 내부에 검색창이 위치하도록 했습니다. SearchBar 대신 UISearchController을 사용한 이유는 목록을 Scroll할 때 자동으로 검색창을 숨기고, 검색창에 텍스트를 입력할 때 자동으로 Navigation Title을 숨기는 등 사용자에게 보다 직관적인 UX를 구현할 수 있기 때문입니다.

6. 이미지 Cache 구현

UIImageView 익스텐션으로 메모리 Cache가 있으면 해당 이미지를 가져오고, 없으면 imageURL을 통해 받아오도록 했습니다. 이때 ImageCacheManager를 추가하여 Notification을 통해 메모리가 부족하면 Cache를 삭제하도록 했습니다.

📚 테스트 방법

  • JSONParserTests에서 Mock 데이터 (MockSearchResult, MockBookItem)을 활용하여 Parsing이 정상작동하는지 테스트했습니다.
  • MockNetworkProviderTests에서 네트워크 관련 기능이 정상작동하는지 테스트했습니다. MockURLSession을 통해 실제 서버의 영향을 받지 않는 독립적인 테스트가 가능합니다.
  • NetworkProviderTests에서 실제 서버 데이터가 정상적으로 전달되는지 테스트했습니다.

📚 리뷰 노트

  • 주석으로 표시한 내용은 시간적 여유가 있을 경우 추가 구현할 예정입니다.
  • CompositionalLayout에서 estimatedHeight를 사용할 때, iOS 15.0 이상 15.3 미만 버전에서 crash가 발생하는 것을 발견했습니다. 이에 대응하여 사용자의 기기 버전이 iOS 15.0~15.3인 경우 Alert를 띄워 사용자에게 업데이트를 권하도록 구현했습니다.
  • 서버에 동일한 요청을 했음에도 전달되는 데이터 (총 검색결과 수)가 달라지는 등 서버가 불안정한 부분이 있습니다.

📚 스크린샷

- CollectionView DiffableDataSource 및 Snapshot 추가
- estimatedHeight 적용하여 BookItemCell을 나타냄
- SearchController를 통해 검색바 구현
- Binding 구현
- SearchBar의 취소버튼으로 검색어를 삭제하면 서버 에러가 발생하므로
  해당 조건에서 서버 요청을 하지 않도록 처리함
- imageURL이 없는 Item이 발생하면 별도 이미지를 보여주도록 처리
- BookItemDTO 타입을 BookItem로 전환하여 필요한 데이터만 관리하도록 처리
- 기존 searchCount 레이블은 유지함 (API 특성 반영)
- ViewModel에서 다음 pageNumber로 Item을 fetch함
- 기존 snapshot에 새로운 Item을 append하여 적용
- ContainerStackView의 margin을 없애고, Label에 constraints를 적용함
- 목록의 Cell을 탭하면 Item 정보를 받아오는 기능 구현
- ActivityIndicator를 나타낼 조건을 ViewModel이 알고 있으므로
- ViewController를 delegate로 설정함
- 메모리 관리를 위해 weak 변수로 선언
@just1103 just1103 merged commit 1a4d88f into master Aug 2, 2022
@just1103 just1103 added the feature 새로운 기능을 구현했습니다. label Aug 2, 2022
@just1103 just1103 changed the title 네트워크 및 목록 화면을 구현했습니다. 사용자가 검색키워드를 입력하면 서버 데이터를 전달받아 목록 화면에 나타냅니다. Aug 2, 2022
@just1103 just1103 changed the title 사용자가 검색키워드를 입력하면 서버 데이터를 전달받아 목록 화면에 나타냅니다. 검색키워드를 입력하면 서버 데이터를 전달받아 목록 화면에 나타냅니다. Aug 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature 새로운 기능을 구현했습니다.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant