[Swift] OpenAPI 활용 영화 순위 앱 만들기(2)

승민·2025년 5월 14일

Swift

목록 보기
9/10
post-thumbnail
  • OpenAPI 를 활용해서 영화진흥위원회에서 제공하는 영화 순위를 활용할 예정이에요.
  • 다음 내용은 이전 블로그 내용에 의존하고 있어요.
  • 이전 내용 실습을 완료한 후 다음 내용을 진행할 수 있어요.

JSON 파싱 후 앱에 나타내기

무비 데이터를 파싱하기 위해 JSON 내용을 기반으로 구조체 정의

구조체를 통해 파싱한 데이터를 추출하여 출력

데이터를 앱에 보여주기 위해 옵셔널 MovieData 형으로 정의

JSON의 내용을 정의한 변수에 넣기

indexPath.row를 통해 해당 인덱스를 추출하여 영화 순위 출력

실행속도 개선을 위해서 메인 쓰레드에서 처리되도록 비동기 처리

실행결과

파싱된 날짜를 실행기준으로 변경하기

어제 날짜를 구하는 함수 정의

movieURL을 var로 변경하고 뒷부분 날짜 지우기

movieURL과 함수 결과를 합치기

UI 변경하여 여러 정보 나타내기

영화 제목이 나오는 부분 크기 축소하기

제약 조건 삭제하기

빈 곳에 Label 2개 추가하기

Label 2개 모두 선택 후 Stack View에 넣기

합쳐진 Stack View와 또다른 Label 선택 후 Stack View에 넣기

우측 하단에 Constraints로 값을 5씩 줘서 제약조건 추가

실행결과

라벨명 변경 및 폰트를 Bold로 변경

우측 2개 Label도 다음과 같이 변경

최상위 Stack View 선택 후 Alignment를 Fill로 변경

2개의 Stack View 모두 Distribution을 Fill Equally로 변경

실행 결과

실행 결과 - 가로 화면

관객수 출력 연동하기

Assistant를 통해 2개의 Label 연동 - audiAccumulate

Assistant를 통해 2개의 Label 연동 - audiCount

Assistant 종료 후 ViewController 수정

실행 결과

영화 순위 출력과 천(3자리)단위 출력 형식 변경

tableView(_: cellForRowAt:) 함수 내용을 다음과 같이 변경

실행결과

헤더 정보 출력과 잘린 문자열 처리

헤더 정보를 출력하기 위해 코드 수정

실행결과

잘린 문자열 처리를 위한 Lines를 2로 변경 후 AutoShrink를 다음과 같이 설정

실행 결과

전체 코드

import UIKit

let movie=["야당", "A MINECRAFT MOVIE 마인크래프트 무비","썬더볼츠*", "거룩한 밤: 데몬 헌터스", "파과"]

struct MovieData : Codable {
    let boxOfficeResult : BoxOfficeResult
}
struct BoxOfficeResult : Codable {
    let dailyBoxOfficeList : [DailyBoxOfficeList]
}
struct DailyBoxOfficeList : Codable {
    let movieNm : String
    let audiCnt : String
    let audiAcc : String
    let rank : String
}

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    @IBOutlet weak var table: UITableView!
    var movieData : MovieData?
    var movieURL = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=<API Key>&targetDt="
    override func viewDidLoad() {
        super.viewDidLoad()
        table.delegate = self
        table.dataSource = self
        movieURL = movieURL + makeYesterdayString()
        getData()
    }
    func makeYesterdayString() -> String {
        // 1. 오늘 날짜 구하기
        let today = Date()
        // 2. Calendar를 사용해서 "어제" 날짜로 만듦
        let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: today)!
        // 3. DateFormatter로 yyyyMMdd 형식 지정
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyyMMdd"  // 반드시 MM과 dd 소문자!
        // 4. 어제 날짜를 문자열로 변환
        return formatter.string(from: yesterday)
        
        /*
         func makeYesterdayString() -> String {
            let y = Calendar.current.date(byAdding:.day, value : -1, to : Date())!
            let dateF = DateFormatter()
            dateF.dateFormat = "yyyyMMdd"
            let day = dateF.string(from: y)
            return day
         }
         */
    }
    func getData() {
        // 함수 안에서는 guard-let 으로 Optional unWrapping하는 것이 가독성이 좋음
        guard let url = URL(string: movieURL) else { return }
        let session = URLSession(configuration: .default)
        // session.dataTask는 선언할 함수의 내용이 길기 때문에 후행 클로저로 처리
        let task = session.dataTask(with: url) { data, response, error in
            if error != nil {
                print(error!)
                return
            }
            guard let JSONdata = data else { return }
            // print(JSONdata)
            // let dataString = String(data: JSONdata, encoding: .utf8)
            // print(dataString!)
            let decoder = JSONDecoder()
            do {
                let decodedData = try decoder.decode(MovieData.self, from: JSONdata)
                self.movieData = decodedData
                // print(decodedData.boxOfficeResult.dailyBoxOfficeList[0].movieNm)
                // print(decodedData.boxOfficeResult.dailyBoxOfficeList[0].audiAcc)
                DispatchQueue.main.async {
                    self.table.reloadData()
                }
            } catch {
                print(error)
            }
        }
        task.resume()
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
        // 그냥 사용하는 경우에는 guard-let을 안쓰고 옵셔널 체이닝으로 해도 되지만,
        // 해당 문자열을 변경하는 경우 구문을 따로 빼서 처리하는 것이 안전함
        guard let mRank = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].rank else
        { return UITableViewCell() }
        guard let mName = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].movieNm else
        { return UITableViewCell() }
        cell.movieName.text = "[\(mRank)위] \(mName)"
        // 문자열을 수정하기 위해 옵셔널 언래핑 후 문자열 수정
        if let aCnt = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiCnt {
            let numF = NumberFormatter()
            numF.numberStyle = .decimal
            let aCount = Int(aCnt)!
            let result = numF.string(for: aCount)!+"명"
            cell.audiCount.text = "어제: \(result)"
        }
        if let aAcc = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiAcc {
            let numF = NumberFormatter()
            numF.numberStyle = .decimal
            let aAcc1 = Int(aAcc)!
            let result = numF.string(for: aAcc1)!+"명"
            cell.audiAccumulate.text = "누적: \(result)"
        }
        return cell
    }
//    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//        let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
//        cell.movieName.text = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].movieNm
//        cell.audiCount.text = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiAcc
//        cell.audiAccumulate.text = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiCnt
//        // print(indexPath.description)
//        return cell
//    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        //print(indexPath.description) // 개발자용 print 구문
    }
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "🍿박스오피스(영화진흥위원회제공:" + makeYesterdayString() + ")🍿"
    }
    func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
        return "made by gsmin"
    }
}

정리

  • 받아온 영화 순위를 앱에 보여주는 기능을 구현했어요.
  • 추가적인 정보를 나타내고 긴 내용에 대해 처리하는 부분까지 구현했어요.

출처 : Smile Han - iOS 프로그래밍 기초

0개의 댓글