---
# System prepended metadata

title: "\U0001F3AC 박스오피스"
tags: [project]

---

![](https://hackmd.io/_uploads/r1MNQyh9n.png)
![](https://hackmd.io/_uploads/BJmDtsin3.png)


## Ground Rules

### 규칙
- TIL, 일일 회고 작성 시간(매일 22시부터 1시간 작성 진행)
- 페어프로그래밍 시간 제한 최대 30분
- 공식문서 읽기
- 컨디션 안 좋을 때 말하기!!!

### 스크럼
- 오전 10시 디스코드에서 진행
- 금일 진행 사항 공유하기(오늘의 할일)

### 프로젝트 규칙
- 네이밍 준수하기(가이드 라인)
- 커밋 메시지 규칙 진행
- 코드에 대한 기록 그때그때 하기
- 캡쳐 많이 하기 찰칵찰칵📸


## 일일 스크럼
### 🙌 07/24 월
- 오늘의 컨디션
    - hoon🐝: 졸려요🤣
    - MINT😈: 약간 졸린? 여름엔 망고를 먹어야죠!
- 특이사항
    - hoon🐝: 밈, 솜과 데이트 ㅎㅎ(with. 호섭이, 보름이)
    - MINT😈: 미라클 모닝 장렬하게 실패! 온 세상이 행복하답니다 이제 도망쳐야짛ㅎㅎㅎ
- 오늘 할 일 
    - [x] 레드망고가 제철이에요~
    - [x] 활동학습 예습
    - [ ] STEP 1 구현
    - [ ] STEP 1 PR

### 🙌 07/25 화
- 오늘의 컨디션
    - hoon🐝: 나쁘지 않아요😊
    - MINT😈: 흠
- 특이사항
    - hoon🐝: 여행 계획하기 ㅋㅋㅋㅋ
    - MINT😈: 훈 여수 납치 계획
- 오늘 할 일 
    - [x] STEP 1 구현
    - [x] STEP 1 PR
    - [x] 공식문서 스터디
    - [ ] URL 공식문서 읽기
    - [ ] 여수 계획

### 🙌 07/26 수
- 오늘의 컨디션
    - hoon🐝: 몸이 무거워요오....😢
    - MINT😈: 케챱이 없어요오....😢
- 특이사항
    - hoon🐝: 스트레칭이 필요해요 ㅋㅋㅋㅋ, 1시 반부터 노가리 데이
    - MINT😈: 2시부터 1시간 반 정도 토요 스터디 보충
- 오늘 할 일 
    - [ ] 네트워크 필요한 개체 공부
    - [x] 클로저 공식문서
    - [x] 활동학습 예습
    - [ ] URL

### 🙌 07/27 목
- 오늘의 컨디션
    - hoon🐝: 더워요🥵
    - MINT😈: 낮잠 예정 후후
- 특이사항
    - hoon🐝: 더워서 아무것도 하기가 싫어요 ㅋㅋㅋㅋㅋㅋㅋ 운동도 빼먹을 예정... <-더위 먹는것보다는 포동포동을 선택!
    - MINT😈: 오뉴가 제 위에서 자고 이써요 ㅋㅅㅋ <- 오뉴 귀요미
- 오늘 할 일 
    - [x] URL Session
    - [x] 하비께 디엠
    - [ ] Github 예시 코드 찾기

### 🙌 07/28 금 (늦잠 꾸러기들~ 언제 일어나시는지 감시 중😏 어라?)
- 오늘의 컨디션
    - hoon🐝: 피곤치 MAX!!🥱     <--아침 일찍 왔다가 낭패본 그 
    - MINT😈: 꽤나 좋아요~ 늦잠 최고 <--좀처럼 일어나지 않는 그녀
- 특이사항
    - hoon🐝: 로웬 멘토링 4시 <--멘토링 하시는 동안 자고 올 예정
    - MINT😈: 로데 멘토링 4시 <--멘토링 하시는 동안 자고 올 예정
- 오늘 할 일 
    - [x] README 작성
    - [x] 멘토링 4시
    - [ ] TCP 강의 영상 <--이거 언제보시나 기다리는 중(밤 10시에 작성!!히히)
    - [ ] osi 7계층
    - [x] 하비 답변 공부
    - [ ] GitHub 예시 코드 찾기
    - [ ] 망고쥬스 마시기 <--- ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 스파이망고 ㅋㅋㅋ

### 🙌 07/31 월 (왜 오늘 스크럼 안하시죵?? 저 기대하고 있는뎅!!)
- 오늘의 컨디션
    - hoon🐝: 주말에 푹 쉬어서 상태가 좋습니다.😆 <- 꿀피부 그
    - MINT😈: 엄청 많이 자서 기뻐요~ 😆 <- 푹자서 기쁜 그녀
- 특이사항
    - hoon🐝: 오늘 열심히 책을 볼거에요.🔥🔥🔥
    - MINT😈: 저도 책에 집중을 해보겠어요!🌊🌊🌊
- 오늘 할 일 
    - [x] HTTP 영상 보기
    - [x] 책 읽기

### 🙌 08/01 화 (오늘은 무엇을 하실 예정이신가요??)
- 오늘의 컨디션
    - hoon🐝: 푹 잤습니다.👍 <--오 푹 주무셨군요??
    - MINT😈: 많이 먹었습니닿ㅎㅎㅎ <--맛있게 드셨어요??뭘 드셨을까용?🤩
- 특이사항
    - hoon🐝: 오후에 치과! <--오호 오후에 치과가시는군요?
    - MINT😈: 민트 점심 먹고 서울 올라가는-! <--응??오늘 서울 올라오세요??
- 오늘 할 일 
    - [x] HTTP 강의 끝까지
    - [x] 책 읽기

### 🙌 08/02 수: (오늘 두 분은 쉬시나요?? 저 실시간 옵저빙하는것 같네요 😏) <-- ㅋㅋㅋㅋㅋ책만 읽은 하루~
- 오늘의 컨디션
    - hoon🐝: 해피 해피😊
    - MINT😈: 해피 민트!!
- 특이사항
    - hoon🐝: 책 완독!!! 내용은 몰라요...🤣🤣🤣 <-- 우와 결국 완독하셨군요!!!
    - MINT😈: 쏘오오오오오 해피 민트!!!!!!
- 오늘 할 일 
    - [x] 책 읽기
    - [x] 5,6기 초청 취업 설명회 듣기

### 🙌 08/03 목 
- 오늘의 컨디션
    - hoon🐝: 몸이 무거워요. ㅋㅋㅋㅋㅋㅋㅋ <--훈 씻으셨나요?
    - MINT😈: 꼬질 민트 <-- 킁킁(어디서 꼬수분내가..근데 꼬질민트 귀욤)
- 특이사항
    - hoon🐝: 이따가 낮잠을... ㅎㅎ <-- 낮잠 필수!
    - MINT😈: 저는 잠시 작은엄마 직장! <--오옷?견학가신건가요?
- 오늘 할 일 
    - [x] 활동 학습 예습
    - [x] osi 7계층 정리
    - [x] 오후 8시 멘토링 훈민트 둘 다
    - [ ] TCP/IP 정리

### 🙌 08/04 금 (오늘은 무엇을 하실 예정이신가용? 불금인데~🥳)
- 오늘의 컨디션
    - hoon🐝: 괜찮아요.🙂 <-- 어디가 어떻게 괜찮은거지요? 상세하게 알려주세요
    - MINT😈: 민트 사망 <--민트 어디갔어요?!! 민트를 찾아주세요!
- 특이사항
    - hoon🐝: 토요스터디 준비😂 <--화이팅입니다.
    - MINT😈: 민트 패스 <--민트는 스크럼 패스인가요?
- 오늘 할 일 
    - [x] 하비와의 만남 <-- 만나셨나요?(두근두근🤩) 실패!
    - [ ] 

### 🙌 08/07 월
- 오늘의 컨디션
    - hoon🐝: 주말이 행복했습니다😊
    - MINT😈: 잠이 부족해서 보충해써요오
- 특이사항
    - hoon🐝: 활동 학습 예습~!!
    - MINT😈: 히히 기절 한 번 했습니다~
- 오늘 할 일 
    - [x] 활동 학습 예습
    - [x] TLS/SSL 정리
    - [ ] HTTP 버전별 정리
    - [ ] Cache/cookie 정리

### 🙌 08/08 화 (오늘 스텝2 진행하시나요?!! 두분 화이팅입니다!)
- 오늘의 컨디션
    - hoon🐝: 낫배드🙂
    - MINT😈: 히히 열심히 걸은 민트😈
- 특이사항
    - hoon🐝: 여권 찾기😢
    - MINT😈: 훈민트 뽀에버😈
- 오늘 할 일 
    - [ ] Cache 정리
    - [x] HTTPS 정리

### 🙌 08/09 수 (오늘 엄청 바쁘신 두 분~)
- 오늘의 컨디션
    - hoon🐝:  아주아주아주 힘듭니다😩 훈 오늘은 낮잠 주무시죠 ㅋㅋㅋㅋ😈 <-- 여권 신청하고 오셨나요?
    - MINT😈:  기절트.. 여독...<-- 민트의 행방을 찾습니다~
- 특이사항
    - hoon🐝: 여권을 만들기 <-- 외식 예정아니십니까?..아니다 배달인가?
    - MINT😈: 저녁은...외식.. <-- 두분 같이 드세요? (같이 먹고 싶다요)
- 오늘 할 일 
    - [X] URL cache 코드 짜기
    - [ ] cache 발표자료 만들기
    - [ ] TCP 및 HTTP 버전 별 정리

### 🙌 08/10 목 (훈, 민트 모두 건강 잘 챙기세요ㅠ_ㅠ)
- 오늘의 컨디션
    - hoon🐝: 어제 푹쉬어서 좋습니다.😊 <--훈 괜찮으신가요? 네 (다행이네요)
    - MINT😈: 집인데 집에 가고 싶은 기분 <--민트도 더위 안 먹게 조심하세요~, 저 그 기분 너무 잘알아요..
- 특이사항
    - hoon🐝: 밀린 공부를 호다닥~
    - MINT😈: 낮잠 잘래욥😴 <-낮잠 꿀잠
- 오늘 할 일 
    - [x] cache 발표자료 완성하기, 대본 작성하기
    - [x] TCP 및 HTTP 버전 별 정리
    - [x] 활동 학습 예습

### 🙌 08/11 금 (아싸!! 불금입니다!!)
- 오늘의 컨디션
    - hoon🐝: 이상해요😕 <-이가 아프신건가요?
    - MINT😈: 허리 아포요🥲 <-어랏??허리가..허리가 왜?..
- 특이사항
    - hoon🐝: 병원을 다녀오겠습니다🏥 <--어랏? 허리는 민트가 아프신대 병원은 왜 훈이 가시죠?😲
    - MINT😈: 스트레칭과 산책 필수~😈 <--맞아요 건강지켜야해요
- 오늘 할 일 
    - [ ] HTTP 버전 별 정리
    - [x] 노션에 발표 정리

### 🙌 08/14 월
- 오늘의 컨디션
    - hoon🐝: 점점 회복중입니다.😊
    - MINT😈: 해피민트~!
- 특이사항
    - hoon🐝: 활동 학습 예습
    - MINT😈: 즐거운 활동학습! 내일 아부지 생신🎂
- 오늘 할 일 
    - [x] STEP 2 코드 샘플 찾기
    - [x] 공식 문서
    - [X] Core Data 활동학습 복습

### 🙌 08/15 화
- 오늘의 컨디션
    - hoon🐝: 잘 잤습니다.😆
    - MINT😈: 허리 아파서 의자를 정적인 걸로 바꿨어요ㅜㅜ 스트레칭과 자세교정이 시급합니다ㅜ
- 특이사항
    - hoon🐝: 오후에 친구들과 여행 계획을 세웁니다.🛫
    - MINT😈: 꿈에 훈이 나왔어요!😈
- 오늘 할 일 
    - [x] STEP 2 코드 샘플 탐색
    - [ ] STEP 2 코드 작성
    - [ ] STEP 2 PR
    - [ ] 공식 문서

### 🙌 08/16 수
- 오늘의 컨디션
    - hoon🐝: 푹 잤어요 ㅋㅋㅋㅋㅋㅋ🙂
    - MINT😈: 굿~ 엄청 잤어요~
- 특이사항
    - hoon🐝: 오후에 잠시 외출하고 오겠습니다.☺️
    - MINT😈: 샤인머스캣이 맛있어요!🍇
- 오늘 할 일 
    - [x] STEP 2 코드 작성
    - [x] STEP 2 PR
    - [ ] 공식 문서
    - [X] 활동 학습 예습

### 🙌 08/17 목
- 오늘의 컨디션
    - hoon🐝: 조금 피곤합니다.
    - MINT😈: 현재는 졸립니다ㅜ
- 특이사항
    - hoon🐝: 활동학습 + 특강 예습
    - MINT😈: 오늘 하루가 사라졌어요~
- 오늘 할 일 
    - [x] 활동학습 예습
    - [x] 특강

### 🙌 08/18 금
- 오늘의 컨디션
    - hoon🐝: 조금 피곤해요. 그래도 마지막 날!!!🥰
    - MINT😈: 캬캬 
- 특이사항 
    - hoon🐝: 아침에 병원을 다녀왔어요 ㅎㅎ. 저녁에는 저도 인천으로~✈️
    - MINT😈: 저녁 약속 후후😆
    - Mango: 어디가요! 나도 데려가요!!
- 오늘 할 일 
    - [ ] 리뷰 수정
    - [ ] 공식 문서
    - [ ] README 작성
    - [ ] 망고 데리고 놀기





# 팀 회고
## 우리팀이 잘한 점
- 

## 우리팀 개선할 점
- 

## 서로에게 좋았던 점 피드백

    

## 서로에게 하고싶은 말

    













# PR: STEP 1

## 고민되었던 점

### `Unit Test`에서 `json` 파일 사용
- STEP 1에서는 `API` 서버와 실제 통신을 하지 않고 테스트하기 위해 `box_office_sample.json` 파일을 제공합니다. 제공된 `box_office_sample.json` 파일은 `Unit Test`의 목적으로만 사용될 거라는 예상 때문에 [`BoxOfficeTests`에 `json` 파일을 추가](https://stackoverflow.com/questions/46726498/add-test-assets-for-xctest)하였습니다. 새로운 `Asset Catalog`를 생성하고 `json` 파일을 추가 후 다음과 같이 사용하려고 하였습니다.

    ```swift
    guard let json = NSDataAsset(name: "box_office_sample") else {
        return
    }
    ```

    위와 같이 사용하면 파일을 불러오지 못하여 `json` 상수에 `nil`이 할당되는 문제가 있었습니다. 확인해 보니 사용하는 `Bundle`이 현재 `BoxOfficeTests` 번들이 아닌 점이 문제였습니다. 문제를 해결하기 위해 사용하는 번들을 지정해 주었습니다.
    
    ```swift
    let bundle = Bundle(for: JsonDecodeTests.self)
    guard let json = NSDataAsset(name: "box_office_sample", bundle: bundle) else {
        return
    }
    ```
    
    `Bundle(for:)` 메서드를 사용하여 현재 지정된 클래스가 연결된 `NSBundle` 객체를 반환받고 이를 다시 넣어줌으로써 `box_office_sample`의 파일 위치를 특정하여 사용할 수 있었습니다.

## 해결이 되지 않은 점

### `Equatable protocol`을 외부 파일에서 `extension`으로 구현
- `Unit Test`를 진행하던 도중 `BoxOfficeItemInformation`을 구조체끼리 비교해 주고 싶었습니다. 특별한 타입을 구현한 게 아니라서 단순히 `Equatable`을 채택하면 테스트를 진행할 수 있었습니다. 그런데 저희는 해당 `Equatable protocol`이 단순히 `Unit Test`에서 확인해 보기 위한 것일 뿐이기에 `test file`로 빼주고 싶었습니다. 때문에 `extension`으로 옮겨서 `Equatable`을 채택해 주었는데 다음과 같은 에러가 발생하였습니다.

    ![](https://hackmd.io/_uploads/r14OUJaqh.png)
    
    찾아보니 `Equatable`안에 구현되어 있는 `==` 메서드는 접근 권한이 `public`이 아니라서 다시 `public`으로 만들어주어야 하는 것이었습니다. 프로퍼티가 굉장히 많은데 이를 하나하나 작성해서 비교하는 것은 비효율적일 것 같아 내부의 프로퍼티들을 전부 다 반복으로 돌릴 수 있는 방법을 찾아보다가 결국 `BoxOfficeItemInformation`에서 가장 정체성을 나타내는 유일한 값인 `movieCode`를 비교하는 것으로 `extension`을 작성하였습니다.

    ```swift
    extension BoxOfficeItemInformation: Equatable {
        public static func == (lhs: BoxOfficeItemInformation, rhs: BoxOfficeItemInformation) -> Bool {
            return lhs.movieCode == rhs.movieCode
        }
    }
    ```
    
    

# PR: STEP2

## 고민했던 점

### `URLSession shared` 사용
- URLSession 클래스가 task 생성을 위해 기본으로 제공하는 싱글톤 객체입니다. `shared`를 사용하는 경우 다음과 같은 제한이 있었습니다.

    - 데이터가 서버에서 도착할 때 점진적으로 데이터를 얻을 수 없다.
    - 기본 연결 동작을 사용자 지정할 수 없다.
    - 인증 기능에 제한이 있다.
    - 앱이 실행 중이지 않은 상태에서 다운로드나 업로드를 실행할 수 없다.

    위와 같은 제한 사항이 존재하기 때문에 `shared`를 사용하여는 경우 일반적으로 `cache`, `cookie storage` 또는 `credential storage`를 커스텀 하여 사용하는 것을 피해야 합니다.
    
    현재 프로젝트를 진행하며 위와 같은 제한 사항과 관련한 내용이 없기 때문에 `shared`를 사용하였습니다.
    
    ```swift
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if error != nil {
            completion(.failure(.dataTaskFail))
            return
        }

        guard let httpResponse = response as? HTTPURLResponse else {
            completion(.failure(.responseCasting))
            return
        }

        guard (200...299).contains(httpResponse.statusCode) else {
            completion(.failure(.invalidStatus))
            return
        }

        guard let data = data else {
            completion(.failure(.noData))
            return
        }

        completion(.success(data))
    }
    task.resume()
    ```
    
    추가적으로 찾은 자료에서는 `shared`를 사용하는 경우 configuration을 사용하여 설정하는 부분에 대한 오버헤드가 없기 때문에 더 빠른 속도를 보인다는 내용이었습니다.
    
    - 참고 자료 : [URLSession: shared vs default](https://stackoverflow.com/questions/57971904/shared-urlsession-vs-urlsession-with-default-configuration)

## 해결하지 못한 점

### `handler` 중복 합치기
- `fetchData` 메서드에 `escaping closure`를 사용해 `completion handler`를 구현하였습니다. `Result` 타입을 사용해 실패하면 `error`를 반환하고 성공하면 `data`를 반환하는데 그때 해당 `json data`를 `decode`해 사용할 수 있는 모델을 만들고 싶었습니다. 이때 `Decodable`을 채택한 2개의 서로 다른 모델 타입을 제외하고는 `completion handler`의 내용이 같기에 하나의 프로퍼티로 선언해 전달해주고 싶었습니다.

    ```swift
    private let boxOfficeDataCompletion: (Result<Data, NetworkError>) -> Void = { result in
        switch result {
        case .success(let data):
            guard let decodedData = BoxOfficeData.decode(data: data) else {
                return
            }
        case .failure(let error):
            print(error.localizedDescription)
        }
    }
    ```
    
    위의 코드에서 guard let 안의 BoxOfficeData만 MovieInformation과 옵션을 줘서 번갈아가며 사용할 수 있게 해주고 싶었습니다.
    
- 위와 같은 이유로 다음과 같이 2가지 방법을 생각해 보았지만 두 방법 모두 성공하진 못했습니다.
    
    1. closure로 타입 전달

        ```swift
        private let boxOfficeDataCompletion: (Decodable, (Result<Data, NetworkError>)) -> Void = { type, result in
            switch result {
            case .success(let data):
                guard let decodedData = type.decode(data: data) else {
                    return
                }
            case .failure(let error):
                print(error.localizedDescription)
            }
        }
        ```

        클로저로 타입을 전달해주는 방법입니다. 다만 이 경우 fetchData 함수에서도 성공한 경우 completion(.success((`type`, data)))로 fetchData에서부터 type을 전달해주어야 합니다. 그러나 이때 type을 전달해 줄 수가 없었습니다.
    
    2. fetchData 함수를 부르는 함수에 타입 전달

        ```swift
        func prepareData<T: Decodable>(request: URLRequest, type: T) {
            let handler: (Result<Data, NetworkError>) -> Void = { result in
                switch result {
                case .success(let data):
                    guard let decodedData = type.decode(data: data) else {
                        return
                    }
                    print(decodedData)
                case .failure(let error):
                    print(error.localizedDescription)
                }
            }
            fetchData(request: request, completion: handler)
        }
        ```
        
        그러나 이렇게 한 후 사용이 가능한지 실험해보는 과정에서 다음과 같은 에러가 발생하며 Decodable 타입을 사용할 수가 없었습니다. 
        ![](https://hackmd.io/_uploads/SyDJvLq32.png)
    
- 결국 합치지는 못하고 각각 다른 completion handler를 만들어 enum을 통해 전해줄 수 있게 구현하였습니다.
    
    ```swift
    enum completion {
        case boxOfficeData
        case movieInformation
        
        var handler: (Result<Data, NetworkError>) -> Void {
            switch self {
            case .boxOfficeData:
                return NetworkManager().boxOfficeDataCompletion
            case .movieInformation:
                return NetworkManager().movieInformationCompletion
            }
        }
    }
    ```

## 조언을 얻고 싶은 점
### `URLSession shared` 사용 예시
- 어떤 예시에서 `shared`를 사용하는 것이 좋은지를 찾지 못하였습니다. 기본적으로 `URLSession.init(configuration: .default)`를 바탕으로 `shared`가 만들어졌다고 하는데 어떤 경우에 `shared`를 사용할 수 있을까요? `default`와 `shared`를 사용하는 기준점이 위의 제한 사항 외에도 다른 이유가 있을까요?