

import UIKit
let movie = ["야당", "마인크래프트", "썬더볼츠", "진격의 거인", "야당2"]
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!
let movieURL = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=자신의 키&targetDt=20250513"
// url 주소 변수 추가
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self // self 두 줄 없으면 동작 안 함
table.dataSource = self
getData()
}
func getData() { // 옵셔널 형이라 풀어줘야 된다
guard let url = URL(string: movieURL) else { return } // guard let : 거짓일 경우를 먼저 작성
let session = URLSession(configuration: .default) // .default 를 가장 많이 사용
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
print(error!)
return
} // if 문을 이용하여 에러처리
guard let JSONdata = data else { return }
let dataString = String(data: JSONdata, encoding: .utf8)
// utf8 방식으로 인코딩된 데이터를 String형으로 변경하여 자료의 크기가 아닌 자료 자체를 출력
// print(dataString!) // 전체 영화 순위 데이터
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(MovieData.self, from: JSONdata)
// MovieData는 자료형이기 때문에 값도 써줘야 된다 // + 예외처리도 해줘야 한다.
print(decodedData.boxOfficeResult.dailyBoxOfficeList[0].movieNm)
print(decodedData.boxOfficeResult.dailyBoxOfficeList[0].audiAcc)
} catch {
print(error)
}
}
task.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5 // 5칸짜리 테이블 뷰
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell // 다운캐스팅
cell.movieName.text = movie[indexPath.row]
// print(indexPath.description) // 추가 (현재 보이는 셀들의 인덱스를 개발자만 보이도록 출력)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.description) // 특정 셀을 클릭(선택) 했을 때 해당하는 row 값을 개발자만 보이도록 출력
}
func numberOfSections(in tableView: UITableView) -> Int {
return 5 // 섹션의 수를 2칸으로 나눔 (총 10칸)
}
}

https://developer.apple.com/documentation/foundation/jsondecoder



movieData 선언


클로저 안에서 프로퍼티 접근 시 self. 을 사용해야 한다


UITableView.reloadData() must be used from main thread only
-> UITableView.reloadData()를 메인 스레드(Main Thread)에서 호출하지 않아서 발생한 에러
UiView 에서 동작하는 소스는 background에서 동작하면 안 되고 메인 쓰레드에서 동작해야 된다

DispatchQueue.main.async { } 를 사용하여 메인 스레드에서 작업을 비동기적으로 실행
어제 날짜에 대한 데이터를 가져오는 함수 만들기


movieURL 의 값에서 targetDt 값을 비워둬야 한다
import UIKit
let movie = ["야당", "마인크래프트", "썬더볼츠", "진격의 거인", "야당2"]
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=자신의 키값&targetDt="
// url 주소 변수 추가
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self // self 두 줄 없으면 동작 안 함
table.dataSource = self
movieURL = movieURL + makeYesterdayString() // 어제 날짜를 불러오기 위한 함수 만들고 호출하기
getData()
}
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 url = URL(string: movieURL) else { return } // guard let : 거짓일 경우를 먼저 작성
let session = URLSession(configuration: .default) // .default 를 가장 많이 사용
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
print(error!)
return
} // if 문을 이용하여 에러처리
guard let JSONdata = data else { return }
let dataString = String(data: JSONdata, encoding: .utf8)
// utf8 방식으로 인코딩된 데이터를 String형으로 변경하여 자료의 크기가 아닌 자료 자체를 출력
// print(dataString!) // 전체 영화 순위 데이터
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(MovieData.self, from: JSONdata)
// MovieData는 자료형이기 때문에 값도 써줘야 된다 // + 예외처리도 해줘야 한다.
self.movieData = decodedData // 중요!
print(decodedData.boxOfficeResult.dailyBoxOfficeList[0].movieNm) // 영화 이름
print(decodedData.boxOfficeResult.dailyBoxOfficeList[0].audiAcc) // 누적 관객수
DispatchQueue.main.async {
self.table.reloadData()
// (중요!) UiView 에서 동작하는 소스는 background에서 동작하면 안 되고 메인 쓰레드에서 동작해야 된다
}
} catch {
print(error)
}
}
task.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10 // 5칸짜리 테이블 뷰
}
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
// print(indexPath.description) // 추가 (현재 보이는 셀들의 인덱스를 개발자만 보이도록 출력)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.description) // 특정 셀을 클릭(선택) 했을 때 해당하는 row 값을 개발자만 보이도록 출력
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1 // 섹션의 수 설정
}
}


Constraint 삭제 시 출력되는 화면


실행화면
기본 옵션


실행화면
스택뷰 안에 스택뷰가 있음



실행화면




cell.audiAccumulate.text = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiAcc
cell.audiCount.text = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiCnt

기존 코드 주석 후 새로운 코드 추가
// 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
// // print(indexPath.description) // 추가 (현재 보이는 셀들의 인덱스를 개발자만 보이도록 출력)
// cell.audiAccumulate.text = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiAcc
// cell.audiCount.text = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiCnt
// return cell
// }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
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, titleForHeaderInSection section: Int) -> String? {
return "박스오피스(영화진흥위원회 제공: " + makeYesterdayString() + ")"
} // 헤더 부분
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return "made by CDH"
} // 푸터 부분



Lines 의 수를 0으로 할 경우 글씨의 크기가 고정된다 (2줄 이상이 되어도)
Lines 의 수를 2로 설정