
위 이미지에서 한 문제 정도 출제
어떤 유형이 있는지 정도만 알면 됨

Constraint(제약)
제약은 뷰의 크기와 위치를 결정하는 규칙. 뷰의 너비, 높이, 상단, 하단, 좌측, 우측 간격 등을 제어
- Superview와 Subview
- Superview는 다른 뷰를 포함하는 뷰, Subview는 Superview 내에 위치한 뷰
- 제약은 주로 Superview와 Subview 간에 설정
- Safe Area
- 뷰의 콘텐츠가 안전하게 표시될 수 있는 영역.
- 일반적으로상태바,네비게이션바,탭바등과같은시스템UI를 고려하여설정
- Alignment과 Pin 메뉴
Resolve Auto Layout Issues
Storyboard의 하단에 위치한 메뉴로 제약을 추가하는 데 사용
Align
- Alignment 메뉴를 사용하면 뷰의 정렬을 설정
- Pin 메뉴를 사용하면 뷰간의 거리, 뷰의 너비와 높이 등을 설정
Pin
Stack View
Content Hugging 및 Content Compression Resistance Priority
- 뷰의 크기가 내용에 어떻게 맞춰져야 하는지를 결정
- Content Hugging : 내 크기를 더 크게 늘리고 싶지 않아!
- Content Compression Resistance : 내 크기를 더 줄이고 싶지 않아!
빨간색으로 강조된 부분은 시험에 무조건 나옴

constraint가 부족하거나 충돌할 경우
constraint가 부족할 때(Ambiguous Layouts) Missing Constraints(부족)
Constraints Ambiguity(모호)
constraint가 충돌할 때(Unsatisfiable Layouts)
Conflicting(충돌) Constraints
우선순위(priority) 지정
- 1000이 가장 높은 우선순위
- 충돌 시 priority 값이 작은 쪽은 오토레이아웃에서 반영되지 않음
조건 1
A.width = 0.3 * Superview.width (priority = 750) 조건 2
A.width >= 150 (priority = 1000)
우선순위
콘텐츠 허깅 vs 컴프레션 저항
직접 정한게 우선순위 더 높음
stackview에는 두 가지 종류
- 세로 방향으로 묶어주는 것이 Vertical Stack View
- 가로 방향으로 묶어주는 것이 Horizontal Stack View
스택뷰는 핵심
Axis
가로(Horizontal) 스택 뷰인지, 세로(Vertical) 스택 뷰인지?
Alignment 하위뷰의위치정렬
Distribution 하위뷰의크기배분
Spacing 하위뷰사이간격
완성본 소스
//
// ViewController.swift
// MovieCjh
//
// Created by Induk-cs on 2026/04/28.
//
import UIKit
//let name = ["살목지","란 12.3","짱구","프로젝트 헤일메리","왕과 사는 남자","내 이름은","리 크로닌의 미이라","기동전사 건담: 섬광의 하사웨이 키르케의 마녀","르누아르","마녀배달부 키키"]
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 {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//print(indexPath.description)//화면에서 클릭하면 어떤 로우의 어떤 섹션을 선택했는지 출력됨
}
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 CJH"
}
@IBOutlet weak var table: UITableView!
var movieData : MovieData?
//전날의 영화 순위로 바꾸기, 링크에서 날짜부분을 변수로 설정
var movieURL = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=b735d5397e2b621db6dda1700ae5beef&targetDt="
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
movieURL += makeYesterdayString()
getData()
// Do any additional setup after loading the view.
}
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}
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil{ return}//만일 에러가 있다면 끝내버려
guard let JSONdata = data else {return}
let dataString = String(data: JSONdata, encoding: .utf8)
print(JSONdata)
//print(response!)
//print(dataString)
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(MovieData.self, from: JSONdata)
print(decodedData.boxOfficeResult.dailyBoxOfficeList[0].movieNm)
self.movieData = decodedData
DispatchQueue.main.async{
self.table.reloadData()
}//클로저인경우 접근하려면 self써야 함, UI관련 소스는 main thread애서 처리해야 함
}catch{
print(error)
}
}
task.resume() //4단계 >>> 3689 bytes
}
}
출력 결과물

가끔 시험에 출제되는 소스
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
if let aCnt = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiCnt {
let numF = NumberFormatter()
numF.numberStyle = .decimal
let count = Int(aCnt)!
let result = numF.string(for: count)! + "명"
cell.audiCount.text = "어제 : \(result)"
}
if let aAcc = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiAcc {
let numF = NumberFormatter()
numF.numberStyle = .decimal
let acc = Int(aAcc)!
let result = numF.string(for: acc)! + "명"
cell.audiAccumulate.text = "누적 : \(result)"
}
return cell
}
//코드설명
//
// ViewController.swift
// MovieCjh
//
import UIKit
// UIKit
// iOS 화면 구성(UI) 관련 기능을 사용하는 프레임워크
// MARK: - JSON 구조체 생성
// Codable
// JSON 데이터를 Swift 구조체로 자동 변환 가능하게 하는 프로토콜
// Decodable + Encodable 합쳐진 형태
// API의 최상위 JSON 구조
struct MovieData : Codable {
// JSON의 boxOfficeResult 키와 매칭
let boxOfficeResult : BoxOfficeResult
}
// boxOfficeResult 내부 구조
struct BoxOfficeResult : Codable {
// dailyBoxOfficeList 배열 저장
let dailyBoxOfficeList : [DailyBoxOfficeList]
}
// 영화 1개의 데이터를 저장하는 구조체
struct DailyBoxOfficeList : Codable {
// let : 상수(값 변경 불가능)
let movieNm : String // 영화 제목
let audiCnt : String // 하루 관객수
let audiAcc : String // 누적 관객수
let rank : String // 영화 순위
}
// UIViewController 상속
// 화면 하나를 담당하는 클래스
// UITableViewDelegate
// 셀 선택 등 이벤트 처리
// UITableViewDataSource
// 테이블에 데이터 공급
class ViewController: UIViewController,
UITableViewDelegate,
UITableViewDataSource {
// MARK: - 테이블 행 개수
// 섹션(section)당 행(row) 개수 설정
// 현재 박스오피스 10개 출력
// section :
// 테이블의 그룹 개념
// Int 반환
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int)
-> Int {
return 10
}
// MARK: - 섹션 개수
// 테이블의 섹션 개수
func numberOfSections(in tableView: UITableView)
-> Int {
return 1
}
// MARK: - 셀 선택 이벤트
// 셀 클릭 시 실행되는 함수
func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
// indexPath.row
// 클릭된 행 번호
// indexPath.section
// 클릭된 섹션 번호
// print(indexPath.description)
}
// MARK: - 셀 구성
// 각 셀의 내용을 구성하는 함수
// 매우 중요
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
// dequeueReusableCell
// 재사용 가능한 셀 가져오기
// 메모리 절약 목적
// 화면 밖 셀을 재활용함
let cell =
tableView.dequeueReusableCell(
withIdentifier: "myCell",
for: indexPath
) as! MyTableViewCell
// MARK: guard let
// Optional 안전 해제
// movieData? :
// Optional chaining
// nil이면 실행 중단
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)"
// MARK: 일일 관객수 처리
// if let
// Optional 값 존재 시 실행
if let aCnt =
movieData?
.boxOfficeResult
.dailyBoxOfficeList[indexPath.row]
.audiCnt {
// NumberFormatter
// 숫자 형식 변경 객체
let numF = NumberFormatter()
// decimal
// 3자리 콤마 적용
numF.numberStyle = .decimal
// Int 변환
// String -> Int
let aCount = Int(aCnt)!
// string(for:)
// 숫자를 문자열로 변환
let result =
numF.string(for: aCount)!
+ "명"
cell.audiCount.text =
"어제 : \(result)"
}
// MARK: 누적 관객수 처리
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
}
// MARK: 헤더 제목
// 테이블 상단 제목
func tableView(_ tableView: UITableView,
titleForHeaderInSection section: Int)
-> String? {
return "🍿박스오피스(영화진흥위원회제공:"
+ makeYesterdayString()
+ ")🍿"
}
// MARK: 푸터 제목
// 테이블 하단 제목
func tableView(_ tableView: UITableView,
titleForFooterInSection section: Int)
-> String? {
return "made by CJH"
}
// MARK: IBOutlet
// storyboard의 tableView 연결
// IBOutlet
// Interface Builder Outlet
// 스토리보드 객체와 코드 연결
@IBOutlet weak var table: UITableView!
// Optional 변수
// 데이터 없을 수도 있음
var movieData : MovieData?
// MARK: API 주소
// 영화진흥위원회 API URL
// targetDt=
// 날짜를 뒤에 붙일 예정
var movieURL =
"https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=b735d5397e2b621db6dda1700ae5beef&targetDt="
// MARK: viewDidLoad
// 화면 로딩 후 1번 실행
override func viewDidLoad() {
super.viewDidLoad()
// delegate 연결
table.delegate = self
// datasource 연결
table.dataSource = self
// URL 뒤에 날짜 추가
movieURL += makeYesterdayString()
// API 호출
getData()
}
// MARK: 날짜 함수
// 전날 날짜 생성 함수
func makeYesterdayString() -> String {
// Calendar.current
// 현재 기기 달력 사용
// 하루(-1) 감소
let y =
Calendar.current.date(
byAdding: .day,
value: -1,
to: Date()
)!
// 날짜 형식 객체
let dateF = DateFormatter()
// yyyyMMdd 형식 지정
dateF.dateFormat = "yyyyMMdd"
// Date -> String 변환
let day =
dateF.string(from: y)
return day
}
// MARK: API 통신
func getData(){
// 문자열 -> URL 변환
guard let url =
URL(string: movieURL)
else { return }
// URLSession
// 네트워크 통신 객체
let session =
URLSession(configuration: .default)
// dataTask
// 비동기 네트워크 작업
let task =
session.dataTask(with: url) {
data, response, error in
// 에러 발생 시 종료
if error != nil {
return
}
// 데이터 없으면 종료
guard let JSONdata = data
else {
return
}
// 디버깅용 출력
print(JSONdata)
// JSONDecoder 생성
let decoder = JSONDecoder()
do {
// JSON -> 구조체 변환
let decodedData =
try decoder.decode(
MovieData.self,
from: JSONdata
)
// 첫 번째 영화 출력
print(
decodedData
.boxOfficeResult
.dailyBoxOfficeList[0]
.movieNm
)
// 데이터 저장
self.movieData = decodedData
// UI 작업은 Main Thread에서 수행
DispatchQueue.main.async {
// 테이블 새로고침
self.table.reloadData()
}
} catch {
// JSON 파싱 실패
print(error)
}
}
// task 실행
// resume 안 하면 실행 안 됨
task.resume()
}
}
//
// ViewController.swift
// MovieCjh
//
import UIKit
// MARK: - JSON 데이터 구조
// Codable
// JSON 데이터를 Swift 구조체로 자동 변환 가능
struct MovieData: Codable {
let boxOfficeResult: BoxOfficeResult
}
struct BoxOfficeResult: Codable {
let dailyBoxOfficeList: [DailyBoxOffice]
}
struct DailyBoxOffice: Codable {
let movieNm: String // 영화 제목
let audiCnt: String // 일일 관객수
let audiAcc: String // 누적 관객수
let rank: String // 순위
}
// MARK: - ViewController
class ViewController: UIViewController {
// MARK: IBOutlet
@IBOutlet weak var table: UITableView!
// MARK: 변수
// API에서 받아온 영화 데이터 저장
var movieData: MovieData?
// 영화진흥위원회 API 주소
let baseURL =
"https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=b735d5397e2b621db6dda1700ae5beef&targetDt="
// MARK: viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
getData()
}
// MARK: 테이블 설정
func setupTableView() {
// delegate 연결
table.delegate = self
// datasource 연결
table.dataSource = self
}
// MARK: 전날 날짜 생성
func makeYesterdayString() -> String {
// 현재 날짜에서 하루 감소
let yesterday =
Calendar.current.date(
byAdding: .day,
value: -1,
to: Date()
)!
// 날짜 형식 설정
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
// Date -> String 변환
return formatter.string(from: yesterday)
}
// MARK: 최종 URL 생성
func makeMovieURL() -> String {
return baseURL + makeYesterdayString()
}
// MARK: API 데이터 요청
func getData() {
// 문자열 -> URL 변환'computing' 카테고리의 다른 글
| 20260526_iOS12 (0) | 2026.05.26 |
|---|---|
| 20260525_R11 (0) | 2026.05.20 |
| 20260512_iOS10 (0) | 2026.05.12 |
| 20260511_R9 (0) | 2026.05.11 |
| 20260504_빅데이터08 (0) | 2026.05.04 |