스레드는 프로그램 내에서 실행되는 가장 작은 실행 단위입니다. iOS에서는 하나의 애플리케이션이 기본적으로 메인 스레드(Main Thread)에서 실행되며, UI 업데이트와 사용자 이벤트 처리는 메인 스레드에서 이루어져야 합니다. 추가적인 작업을 위해 백그라운드 스레드를 생성할 수 있습니다.
멀티스레딩은 하나의 프로세스에서 여러 개의 스레드를 실행하는 방식입니다. iOS에서는 GCD(Grand Central Dispatch)와 OperationQueue를 사용하여 멀티스레딩을 쉽게 구현할 수 있습니다. 이를 활용하면 네트워크 요청, 데이터 처리 등의 무거운 작업을 백그라운드에서 수행하여 UI가 멈추지 않도록 할 수 있습니다.
멀티스레딩(Multi-threading)은 하나의 프로세스에서 여러 개의 스레드를 실행하여 동시에 여러 작업을 수행하는 기술입니다. iOS에서는 멀티스레딩을 효과적으로 활용하기 위해 GCD(Grand Central Dispatch) 와 OperationQueue 를 제공합니다.
멀티스레딩의 필요성
iOS 앱에서 네트워크 요청, 대용량 데이터 처리, 파일 입출력 등의 무거운 작업을 메인 스레드에서 실행하면 UI가 멈추거나 응답이 느려지는 문제가 발생할 수 있습니다. 이를 방지하기 위해 멀티스레딩을 사용하여 이러한 작업을 백그라운드 스레드 에서 실행하고, UI 업데이트는 다시 메인 스레드에서 처리하는 방식으로 성능을 최적화할 수 있습니다.
iOS에서 멀티스레딩을 구현하는 방법
1. GCD(Grand Central Dispatch)
GCD는 경량화된 멀티스레딩 기술 로, 단순한 비동기 작업을 수행할 때 사용됩니다. 작업을 큐에 추가하면, 시스템이 자동으로 스레드를 관리하여 실행합니다.
GCD의 주요 큐 종류
DispatchQueue.main)DispatchQueue.global(qos: .background))DispatchQueue(label: "com.example.myqueue"))GCD 사용 예시
DispatchQueue.global(qos: .background).async {
// 백그라운드에서 실행할 작업
let data = heavyComputation()
DispatchQueue.main.async {
// UI 업데이트는 반드시 메인 큐에서 실행
updateUI(with: data)
}
}
2. OperationQueue
OperationQueue는 GCD보다 더 정교한 작업 제어 가 필요한 경우 사용할 수 있는 방식입니다. 작업의 우선순위 설정, 의존성 관리, 취소 기능 등을 지원합니다.
OperationQueue 사용 예시
let queue = OperationQueue()
let operation1 = BlockOperation {
print("첫 번째 작업 실행")
}
let operation2 = BlockOperation {
print("두 번째 작업 실행")
}
// operation2가 operation1이 끝난 후 실행되도록 설정
operation2.addDependency(operation1)
queue.addOperations([operation1, operation2], waitUntilFinished: false)
위 코드에서 operation2.addDependency(operation1)을 통해, operation1이 완료된 후 operation2가 실행 되도록 설정할 수 있습니다.
GCD vs. OperationQueue 비교
| 비교 항목 | GCD (Grand Central Dispatch) | OperationQueue |
|---|---|---|
| 사용 방식 | 간단한 비동기 처리 | 작업 간 의존성 설정 가능 |
| 작업 취소 | 직접 취소 불가능 | cancel()을 통해 작업 취소 가능 |
| 우선순위 | QoS(Quality of Service) 설정 가능 | queuePriority로 세밀한 우선순위 조정 가능 |
| 적합한 경우 | 빠른 비동기 작업 (네트워크 요청, 파일 다운로드 등) | 복잡한 작업 흐름 제어가 필요한 경우 (의존성 관리 등) |
실제 iOS 개발에서의 활용 예시
DispatchQueue.global(qos: .userInitiated).async {
let data = fetchDataFromAPI()
DispatchQueue.main.async {
updateUI(with: data)
}
}
let queue = OperationQueue()
let operation1 = BlockOperation {
print("첫 번째 네트워크 요청 실행")
}
let operation2 = BlockOperation {
print("두 번째 데이터 처리 실행")
}
operation2.addDependency(operation1) // 첫 번째 작업이 끝난 후 두 번째 작업 실행
queue.addOperations([operation1, operation2], waitUntilFinished: false)
정리
멀티스레딩은 iOS 앱에서 성능 최적화 및 UI 멈춤 방지 를 위해 필수적인 기술입니다.
이러한 멀티스레딩 기법을 적절히 활용하면, 원활한 사용자 경험과 최적화된 앱 성능을 제공할 수 있습니다.
GCD에서 우선순위 설정방법
GCD에서 우선순위를 설정하려면 Quality of Service(QoS) 를 사용해야 합니다. DispatchQueue.global(qos: .background) 와 같은 방식으로 설정할 수 있으며, iOS에서는 다음과 같은 QoS 옵션을 제공합니다.
QoS(Quality of Service) 우선순위 종류
| QoS 옵션 | 설명 |
|---|---|
.userInteractive | 즉각적인 UI 업데이트가 필요한 작업 (예: 애니메이션, 터치 응답) |
.userInitiated | 사용자가 빠른 결과를 기대하는 작업 (예: 데이터 로드) |
.default | 기본 우선순위 (명시적으로 지정하지 않을 경우) |
.utility | 몇 초에서 몇 분 정도 걸리는 작업 (예: 다운로드, 백업) |
.background | 사용자가 즉시 볼 필요 없는 작업 (예: 데이터 정리, 동기화) |
QoS를 활용한 GCD 우선순위 예제
아래 예제에서는 여러 개의 비동기 작업을 실행하고, 각각 다른 QoS를 적용합니다.
import Foundation
// 가장 낮은 우선순위 (백그라운드 작업)
DispatchQueue.global(qos: .background).async {
print("🟢 Background Task 실행")
}
// 일반적인 작업 (유틸리티)
DispatchQueue.global(qos: .utility).async {
print("🔵 Utility Task 실행")
}
// 사용자 요청에 대한 빠른 응답 (사용자 주도)
DispatchQueue.global(qos: .userInitiated).async {
print("🟡 User Initiated Task 실행")
}
// UI 업데이트에 사용 (가장 높은 우선순위)
DispatchQueue.global(qos: .userInteractive).async {
print("🔴 User Interactive Task 실행")
}
QoS 적용 결과 예상
iOS의 스케줄러는 QoS에 따라 우선순위를 조정하지만, 항상 순서대로 실행되는 것은 보장되지 않습니다.
대체로 높은 우선순위(.userInteractive)의 작업이 먼저 실행되며, 낮은 우선순위(.background)는 리소스가 여유로울 때 실행됩니다.
QoS를 활용한 네트워크 요청 예제
예를 들어, 앱에서 이미지를 다운로드 하는 작업은 .userInitiated 로 설정하고, 로그 파일을 정리하는 작업 은 .background 로 설정할 수 있습니다.
func downloadImage() {
DispatchQueue.global(qos: .userInitiated).async {
let imageData = fetchImageData()
DispatchQueue.main.async {
updateUIImage(with: imageData)
}
}
}
func cleanUpLogs() {
DispatchQueue.global(qos: .background).async {
deleteOldLogs()
}
}
userInitiated: 사용자가 기다리는 작업이므로 높은 우선순위background: 사용자가 신경 쓰지 않는 작업이므로 낮은 우선순위정리
.userInteractive)은 가장 높은 우선순위 로 실행.background)은 리소스가 여유로울 때 실행OperationQueue 활용 예시
OperationQueue는 GCD보다 더 정교한 멀티스레딩 기능을 제공합니다. 작업 간 의존성 관리, 우선순위 설정, 취소 기능 등을 지원하며, 여러 개의 작업을 쉽게 관리할 수 있습니다.
1. 기본적인 OperationQueue 사용
아래 예제는 OperationQueue를 사용하여 여러 개의 작업을 비동기적으로 실행 하는 방법을 보여줍니다.
import Foundation
let queue = OperationQueue()
queue.addOperation {
print("첫 번째 작업 실행")
}
queue.addOperation {
print("두 번째 작업 실행")
}
queue.addOperation {
print("세 번째 작업 실행")
}
💡 OperationQueue에 추가된 작업들은 자동으로 비동기 실행 됩니다.
2. BlockOperation을 활용한 여러 작업 실행
BlockOperation을 사용하면 여러 개의 작업을 그룹화하여 실행 할 수 있습니다.
let blockOperation = BlockOperation()
blockOperation.addExecutionBlock {
print("작업 1 실행")
}
blockOperation.addExecutionBlock {
print("작업 2 실행")
}
blockOperation.addExecutionBlock {
print("작업 3 실행")
}
let queue = OperationQueue()
queue.addOperation(blockOperation)
💡 하나의 BlockOperation 안에서 여러 개의 블록을 추가하면, 여러 작업이 동시에 실행 될 수 있습니다.
3. 작업 간 의존성 설정
어떤 작업이 다른 작업이 끝난 후 실행되도록 강제 하고 싶을 때 addDependency() 를 사용할 수 있습니다.
let queue = OperationQueue()
let operation1 = BlockOperation {
print("첫 번째 작업 실행")
}
let operation2 = BlockOperation {
print("두 번째 작업 실행")
}
// operation2가 operation1이 끝난 후 실행되도록 설정
operation2.addDependency(operation1)
queue.addOperations([operation1, operation2], waitUntilFinished: false)
💡 operation2.addDependency(operation1) → 첫 번째 작업이 완료된 후 두 번째 작업이 실행 됩니다.
4. 우선순위 설정
각 작업에 대해 실행 우선순위를 설정할 수 있습니다.
let queue = OperationQueue()
let highPriorityOperation = BlockOperation {
print("🟥 높은 우선순위 작업 실행")
}
let lowPriorityOperation = BlockOperation {
print("🟦 낮은 우선순위 작업 실행")
}
highPriorityOperation.queuePriority = .high
lowPriorityOperation.queuePriority = .low
queue.addOperations([highPriorityOperation, lowPriorityOperation], waitUntilFinished: false)
💡 우선순위가 높다고 해서 무조건 먼저 실행되는 것은 아니며, 시스템 상태에 따라 조정될 수 있습니다.
(즉, .high 우선순위가 있지만 CPU 부하가 높다면 .low 작업이 먼저 실행될 수도 있음)
| 우선순위 옵션 | 설명 |
|---|---|
.veryHigh | 가장 높은 우선순위 |
.high | 높은 우선순위 |
.normal | 기본값 |
.low | 낮은 우선순위 |
.veryLow | 가장 낮은 우선순위 |
5. 작업 취소
cancel() 메서드를 사용하면 실행 중이거나 대기 중인 작업을 취소할 수 있습니다.
let queue = OperationQueue()
let operation = BlockOperation {
print("작업 실행 중...")
sleep(2) // 작업이 오래 걸린다고 가정
print("작업 완료")
}
queue.addOperation(operation)
// 1초 후 작업 취소
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
operation.cancel()
print("작업이 취소됨")
}
💡 작업이 시작되기 전에 cancel()을 호출하면 실행되지 않으며, 이미 실행 중이라면 취소할 수 없습니다.
cancel()이 호출되었는지 확인하려면 다음과 같이 isCancelled 속성을 사용할 수 있습니다.
let operation = BlockOperation {
if !OperationQueue.current?.operations.first?.isCancelled ?? false {
print("작업 실행")
} else {
print("작업이 취소됨")
}
}
6. 최대 동시 실행 작업 개수 설정
기본적으로 OperationQueue는 가능한 많은 작업을 병렬로 실행합니다.
하지만 동시에 실행할 작업 개수를 제한 할 수도 있습니다.
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2 // 최대 동시 실행 개수 설정
queue.addOperation {
print("작업 1 실행")
sleep(2)
}
queue.addOperation {
print("작업 2 실행")
sleep(2)
}
queue.addOperation {
print("작업 3 실행")
sleep(2)
}
💡 최대 2개의 작업만 동시에 실행되며, 나머지는 순차적으로 실행됩니다.
7. 비동기 네트워크 요청을 OperationQueue로 관리
예를 들어, 여러 개의 이미지를 다운로드하는 작업을 OperationQueue로 관리할 수 있습니다.
import UIKit
class ImageDownloadOperation: Operation {
let url: URL
init(url: URL) {
self.url = url
}
override func main() {
if isCancelled { return } // 취소된 경우 실행하지 않음
guard let imageData = try? Data(contentsOf: url) else { return }
if isCancelled { return }
DispatchQueue.main.async {
print("이미지 다운로드 완료: \(url)")
// UI 업데이트 코드 (예: UIImageView에 적용)
}
}
}
let queue = OperationQueue()
let imageUrls = [
URL(string: "https://example.com/image1.jpg")!,
URL(string: "https://example.com/image2.jpg")!,
URL(string: "https://example.com/image3.jpg")!
]
// 여러 개의 이미지 다운로드 작업을 추가
for url in imageUrls {
let operation = ImageDownloadOperation(url: url)
queue.addOperation(operation)
}
💡 OperationQueue를 사용하면 여러 개의 네트워크 요청을 효과적으로 관리 하면서도, 작업이 취소될 경우 즉시 중단할 수 있습니다.
정리
OperationQueue를 사용하면 다음과 같은 장점이 있습니다.
✅ GCD보다 더 세밀한 제어 가능 (의존성, 우선순위, 취소 기능 지원)
✅ 비동기 작업을 쉽게 관리 가능 (대량의 작업 처리에 유리)
✅ 네트워크 요청, 데이터 처리 등의 복잡한 멀티스레딩 작업에 적합
| 기능 | GCD (DispatchQueue) | OperationQueue |
|---|---|---|
| 사용 방식 | 간단한 비동기 처리 | 세밀한 작업 관리 |
| 작업 취소 | 직접 취소 불가능 | cancel()로 취소 가능 |
| 의존성 설정 | 불가능 | addDependency() 사용 가능 |
| 우선순위 설정 | QoS(Quality of Service) | queuePriority 사용 가능 |
| 최대 실행 개수 조정 | 불가능 | maxConcurrentOperationCount 설정 가능 |
GCD는 단순한 비동기 작업 에 적합하고, OperationQueue는 의존성 및 우선순위가 필요한 복잡한 작업 에 적합합니다.
따라서 상황에 따라 적절한 방식을 선택하는 것이 중요합니다.
뷰는 화면에 표시되는 UI 요소를 의미합니다. UIView 클래스를 기반으로 하며, 버튼(UIButton), 레이블(UILabel), 이미지뷰(UIImageView) 등의 다양한 서브클래스를 포함합니다. 뷰는 계층 구조를 가지며, 부모 뷰(슈퍼뷰)와 자식 뷰(서브뷰) 관계를 형성합니다.
뷰 컨트롤러(UIViewController)는 하나의 화면을 관리하는 객체입니다. 사용자의 인터랙션을 처리하고, 뷰를 업데이트하며, 다른 뷰 컨트롤러로 전환하는 역할을 합니다. 생명주기 메서드(viewDidLoad, viewWillAppear, viewDidAppear 등)를 통해 적절한 시점에 작업을 수행할 수 있습니다.
스토리보드는 iOS 앱의 UI를 설계하는 방식 중 하나로, Xcode의 인터페이스 빌더에서 제공됩니다. 뷰 컨트롤러와 그 사이의 전환(segue)을 시각적으로 연결하여 화면 전환 흐름을 쉽게 설계할 수 있습니다. 하지만 규모가 큰 프로젝트에서는 관리가 어려울 수 있어 코드 기반 UI 설계도 많이 활용됩니다.
오토 레이아웃은 다양한 화면 크기에서 UI 요소들의 위치와 크기를 자동으로 조정하는 레이아웃 시스템입니다. 제약조건(Constraints)을 설정하여 뷰 간의 관계를 정의하고, 디바이스 크기에 따라 적절한 UI를 구성할 수 있습니다. NSLayoutConstraint, Visual Format Language, UIStackView 등을 활용할 수 있습니다.
Auto Layout 상세 분석
Auto Layout은 iOS에서 다양한 화면 크기와 기기에 대응할 수 있도록 UI를 동적으로 배치하는 제약 기반 레이아웃 시스템입니다. 화면 크기가 변해도 일관된 UI 구성을 유지할 수 있도록 돕습니다.
1. Auto Layout의 기본 개념
1) Constraints (제약)
Auto Layout은 뷰들 간의 위치, 크기, 정렬을 결정하는 규칙(Constraints)을 설정하여 동적으로 레이아웃을 조정합니다.
예시:
💡 필수적인 제약 조건(Constraints)
위 4가지 요소가 모두 충족되지 않으면 레이아웃이 모호(Ambiguous)해져서 앱 실행 시 경고 또는 오류가 발생할 수 있습니다.
Intrinsic Content Size
어떤 UI 요소들은 기본 크기(콘텐츠 크기)를 가지며, 이를 Intrinsic Content Size라고 합니다.
예시:
UILabel: 텍스트 내용에 따라 크기가 결정됨UIButton: 버튼의 타이틀과 내부 패딩에 따라 크기가 정해짐이러한 뷰는 별도의 width 또는 height 제약을 명시하지 않아도 내부 콘텐츠 크기에 맞춰 자동으로 크기가 결정됩니다.
Intrinsic Content Size를 무시하고 크기를 조절해야 할 경우?
→ Content Hugging Priority와 Content Compression Resistance Priority 설정 필요
2. Auto Layout 적용 방법
Auto Layout을 적용하는 방법은 다음과 같습니다.
1) Interface Builder (스토리보드)에서 Constraints 설정
Xcode의 스토리보드에서 Auto Layout을 시각적으로 추가할 수 있습니다.
🔹 장점: 직관적인 UI 편집 가능
🔹 단점: 복잡한 레이아웃을 코드로 수정하기 어려울 수 있음
2) 코드로 Auto Layout 설정 (NSLayoutConstraint, Anchors)
스토리보드 없이 코드에서 직접 Auto Layout을 설정할 수도 있습니다.
1️⃣ NSLayoutConstraint 사용 (전통적인 방식)
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false // Auto Layout 활성화
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
button.widthAnchor.constraint(equalToConstant: 100),
button.heightAnchor.constraint(equalToConstant: 50)
])
🔹 장점: 코드로 유연하게 레이아웃 설정 가능
🔹 단점: 제약을 일일이 작성해야 해서 코드가 길어질 수 있음
2️⃣ NSLayoutAnchor 사용 (간결한 방식)
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
🔹 장점: NSLayoutConstraint보다 간결하고 직관적
🔹 단점: 복잡한 레이아웃에는 여전히 코드가 길어질 수 있음
3️⃣ UIStackView 사용 (반응형 레이아웃)
UIStackView를 활용하면 내부 뷰들을 자동으로 정렬하고, 크기를 균등하게 배치할 수 있습니다.
let stackView = UIStackView(arrangedSubviews: [label, button])
stackView.axis = .vertical
stackView.spacing = 10
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
🔹 장점: 내부 요소 정렬이 자동으로 이루어지므로 Auto Layout 설정이 간편
🔹 단점: 커스텀 배치가 필요하면 추가적인 제약 조건 필요
3. Auto Layout에서 발생할 수 있는 문제 및 해결 방법
1) Constraint Conflict (제약 조건 충돌)
같은 요소에 서로 다른 제약 조건이 적용되면 충돌이 발생할 수 있습니다.
✅ 해결 방법: 충돌하는 제약 조건을 확인하고 하나를 제거하거나 priority 값을 조정
2) Ambiguous Layout (모호한 레이아웃)
Auto Layout이 충분한 정보를 얻지 못해 뷰의 위치를 결정할 수 없는 경우 발생합니다.
✅ 해결 방법: 최소한 4가지 필수 제약(X, Y, Width, Height)이 모두 설정되었는지 확인
3) Auto Layout이 예상대로 동작하지 않을 때
translatesAutoresizingMaskIntoConstraints = false 설정Autoresizing Mask와 Auto Layout 충돌 문제
UIKit에서 Auto Layout이 도입되기 전에 사용되던 Autoresizing Mask는 뷰의 크기와 위치를 부모 뷰의 크기에 맞춰 자동으로 조정하는 방식입니다.
Auto Layout을 사용할 때 translatesAutoresizingMaskIntoConstraints = false를 설정하지 않으면 기본적으로 Autoresizing Mask가 Auto Layout의 Constraints로 변환되는데, 이 과정에서 충돌이 발생할 수 있습니다.
1. Autoresizing Mask란?
Autoresizing Mask는 Auto Layout 이전에 사용되던 방식으로, 부모 뷰 크기가 변할 때 자식 뷰의 크기나 위치를 자동으로 조정하는 시스템입니다.
이를 위해 특정 플래그(비트 연산자)를 사용하여 뷰가 부모 뷰 크기에 따라 어떻게 조정될지를 정의했습니다.
UIKit에서는 다음과 같은 Autoresizing Mask 옵션이 있습니다:
.flexibleWidth → 부모 뷰의 너비 변경 시 따라감.flexibleHeight → 부모 뷰의 높이 변경 시 따라감.flexibleTopMargin → 부모 뷰의 크기가 변해도 위쪽 여백 유지.flexibleBottomMargin → 부모 뷰의 크기가 변해도 아래쪽 여백 유지.flexibleLeftMargin → 부모 뷰 크기가 변해도 왼쪽 여백 유지.flexibleRightMargin → 부모 뷰 크기가 변해도 오른쪽 여백 유지 예제: Autoresizing Mask 사용
let subview = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
subview.autoresizingMask = [.flexibleWidth, .flexibleHeight]
위 코드는 부모 뷰의 크기가 변경될 때 subview의 너비와 높이도 함께 변하도록 설정하는 것입니다.
2. Autoresizing Mask와 Auto Layout 충돌
UIKit에서 Auto Layout을 적용할 때는 Autoresizing Mask가 필요 없지만, 기본적으로 활성화된 상태입니다.
즉, Auto Layout이 활성화된 상태에서 frame을 직접 설정하거나 autoresizingMask를 사용할 경우, Auto Layout과 Autoresizing Mask가 서로 충돌할 수 있습니다.
🚨 충돌하는 상황 예시
let button = UIButton()
button.frame = CGRect(x: 50, y: 50, width: 200, height: 50) // 직접 위치 및 크기 설정
button.translatesAutoresizingMaskIntoConstraints = false // ❌ 설정하지 않음
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
button.widthAnchor.constraint(equalToConstant: 100),
button.heightAnchor.constraint(equalToConstant: 50)
])
⚠️ 문제점
frame을 직접 설정했지만, Auto Layout을 사용하고 있음translatesAutoresizingMaskIntoConstraints = false를 설정하지 않았기 때문에 Autoresizing Mask가 Auto Layout의 Constraints로 변환됨✅ 해결 방법
button.translatesAutoresizingMaskIntoConstraints = false // Autoresizing Mask 비활성화
이렇게 설정하면 Autoresizing Mask가 Auto Layout으로 변환되지 않아서 충돌을 방지할 수 있습니다.
3. Autoresizing Mask가 필요한 경우
대부분의 경우 Auto Layout을 사용할 때는 translatesAutoresizingMaskIntoConstraints = false를 설정해야 하지만, Auto Layout을 사용하지 않는 경우에는 Autoresizing Mask를 활용할 수도 있습니다.
예를 들어, Autoresizing Mask는 코드에서 Auto Layout을 사용하지 않고 크기를 자동 조정하고 싶을 때 유용합니다.
let subview = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
subview.autoresizingMask = [.flexibleWidth, .flexibleHeight] // 부모 뷰 크기에 맞춰 자동 조정
view.addSubview(subview)
위 코드는 부모 뷰 크기가 변경될 때 subview의 크기도 자동으로 조정되도록 설정한 것입니다.
4. 결론
✅ Auto Layout을 사용할 때는 translatesAutoresizingMaskIntoConstraints = false를 반드시 설정해야 충돌을 방지할 수 있음
✅ Autoresizing Mask는 Auto Layout 이전에 사용된 방식으로, 부모 뷰 크기 변경 시 자식 뷰의 크기를 자동으로 조정하는 기능
✅ Auto Layout과 Autoresizing Mask를 동시에 사용하면 충돌이 발생할 수 있음
✅ 스토리보드에서 Auto Layout을 설정하면 translatesAutoresizingMaskIntoConstraints = false가 자동으로 적용됨 (코드에서는 직접 설정해야 함)
이제 Auto Layout을 사용할 때 translatesAutoresizingMaskIntoConstraints = false를 설정해야 하는 이유가 이해되셨을 것입니다! 🚀
4. Auto Layout을 최적화하는 팁
po view.constraints 명령어로 현재 적용된 제약 조건 확인5. 결론
Auto Layout을 잘 활용하면 다양한 화면 크기에서 안정적인 UI를 제공할 수 있으며, 유지보수도 용이해집니다.
UIKit은 iOS의 기본 UI 프레임워크로, 화면을 구성하는 뷰, 컨트롤, 제스처, 애니메이션, 이벤트 처리 등을 제공합니다. UIView, UIButton, UITableView, UICollectionView, UIAlertController 등 다양한 UI 요소를 포함하며, UIViewController 기반의 화면 관리를 지원합니다.
델리게이트 패턴은 객체 간의 간접적인 통신을 가능하게 하는 디자인 패턴입니다. iOS에서 UITableViewDelegate, UITextFieldDelegate 같은 프로토콜을 사용하여 특정 동작이 발생했을 때 이를 다른 객체가 처리할 수 있도록 합니다. 일반적으로 protocol을 정의하고, 이를 채택한 객체에서 동작을 구현하는 방식으로 사용됩니다.
네비게이션 컨트롤러(UINavigationController)는 계층적인 화면 이동을 관리하는 컨트롤러입니다. 스택(Stack) 구조를 사용하여 pushViewController(_:animated:)로 새로운 화면을 추가하고, popViewController(animated:)로 이전 화면으로 돌아갈 수 있습니다. 일반적으로 백 버튼이 자동으로 제공되며, 네비게이션 바를 통해 화면 제목이나 버튼을 설정할 수 있습니다.
(필수사항) 내일배움캠프를 시작하며, 다짐을 남기는 앱을 만들어 봅시다.
“내일배움캠프를 시작하며”라는 제목의 앱을 만들어 봅시다.
5개월 뒤에 iOS 6기를 수료하고, 어엿한 주니어 개발자가 될 나를 상상하며 각오의 한 마디를 기억하고 추억하는 사이트를 만들어 보는 것이에요 🙂
카드 형태로 저장되어 볼 수 있게 만들어보세요.예상 설계
배경 → 이미지 변경
db는 coredata 사용해보기
앨범 이미지 … photosUI?
새로운 데이터를 저장(추가)할때 이미지는 앨범이미지로? 아니면 local에 저장한 이미지들 중 선택하게?

카드형태 디자인
import Foundation
import UIKit
extension UIViewController {
func screen() -> UIScreen? {
guard let window = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return view.window?.windowScene?.screen }
return window.screen
}
func cardSize() -> CGSize {
guard let window = screen()?.bounds else {
return CGSize()
}
let width = window.width
let cWidth = width / 2 - CGFloat(Sizes.padding.value) * 1.5
let cHeight = cWidth * 1.5
return CGSize(width: cWidth, height: cHeight)
}
func cardImageSize() -> CGSize {
let (cWidth, cHeight) = (cardSize().width, cardSize().height)
let cardImageHeight = cHeight * 2/3
return CGSize(width: cWidth, height: cardImageHeight)
}
}