내일배움캠프 iOS 3일차

임클·2025년 3월 5일

내일배움캠프

목록 보기
3/44
post-thumbnail

일정


데일리미션

1. 스레드(Thread, 쓰레드)

스레드는 프로그램 내에서 실행되는 가장 작은 실행 단위입니다. iOS에서는 하나의 애플리케이션이 기본적으로 메인 스레드(Main Thread)에서 실행되며, UI 업데이트와 사용자 이벤트 처리는 메인 스레드에서 이루어져야 합니다. 추가적인 작업을 위해 백그라운드 스레드를 생성할 수 있습니다.

2. 멀티스레드(Multi Thread, 멀티쓰레드)

멀티스레딩은 하나의 프로세스에서 여러 개의 스레드를 실행하는 방식입니다. 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의 주요 큐 종류

  • Main Queue: UI 업데이트와 관련된 작업은 반드시 메인 큐에서 실행해야 합니다. (DispatchQueue.main)
  • Global Queue: 백그라운드 작업을 수행할 수 있는 큐이며, 여러 우선순위(QoS)를 가질 수 있습니다. (DispatchQueue.global(qos: .background))
  • Custom Queue: 개발자가 직접 생성하여 사용할 수 있는 큐입니다. (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 개발에서의 활용 예시

  1. 네트워크 요청을 백그라운드에서 처리하고 UI 업데이트
        DispatchQueue.global(qos: .userInitiated).async {
            let data = fetchDataFromAPI()
        
            DispatchQueue.main.async {
                updateUI(with: data)
            }
        }
        
  1. 여러 개의 비동기 작업을 실행하되, 순서를 제어하고 싶은 경우
        let queue = OperationQueue()
        
        let operation1 = BlockOperation {
            print("첫 번째 네트워크 요청 실행")
        }
        
        let operation2 = BlockOperation {
            print("두 번째 데이터 처리 실행")
        }
        
        operation2.addDependency(operation1) // 첫 번째 작업이 끝난 후 두 번째 작업 실행
        
        queue.addOperations([operation1, operation2], waitUntilFinished: false)
        

정리

멀티스레딩은 iOS 앱에서 성능 최적화 및 UI 멈춤 방지 를 위해 필수적인 기술입니다.

  • GCD(Grand Central Dispatch) 는 단순한 비동기 작업을 빠르게 처리할 때 적합합니다.
  • OperationQueue 는 작업의 의존성 관리 가 필요한 경우 사용하면 좋습니다.
  • 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: 사용자가 신경 쓰지 않는 작업이므로 낮은 우선순위

정리

  • QoS를 설정하면 시스템이 적절한 우선순위를 자동으로 할당 하여 실행
  • UI 관련 작업(.userInteractive)은 가장 높은 우선순위 로 실행
  • 백그라운드 작업(.background)은 리소스가 여유로울 때 실행
  • 우선순위를 통해 앱의 성능을 최적화 할 수 있음 이러한 GCD의 QoS 기능을 활용하면, 중요한 작업을 빠르게 처리하고 덜 중요한 작업은 효율적으로 실행할 수 있습니다.

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의존성 및 우선순위가 필요한 복잡한 작업 에 적합합니다.

따라서 상황에 따라 적절한 방식을 선택하는 것이 중요합니다.

3. 뷰(View)

뷰는 화면에 표시되는 UI 요소를 의미합니다. UIView 클래스를 기반으로 하며, 버튼(UIButton), 레이블(UILabel), 이미지뷰(UIImageView) 등의 다양한 서브클래스를 포함합니다. 뷰는 계층 구조를 가지며, 부모 뷰(슈퍼뷰)와 자식 뷰(서브뷰) 관계를 형성합니다.

4. 뷰 컨트롤러(View Controller)

뷰 컨트롤러(UIViewController)는 하나의 화면을 관리하는 객체입니다. 사용자의 인터랙션을 처리하고, 뷰를 업데이트하며, 다른 뷰 컨트롤러로 전환하는 역할을 합니다. 생명주기 메서드(viewDidLoad, viewWillAppear, viewDidAppear 등)를 통해 적절한 시점에 작업을 수행할 수 있습니다.

5. 스토리보드(Storyboard)

스토리보드는 iOS 앱의 UI를 설계하는 방식 중 하나로, Xcode의 인터페이스 빌더에서 제공됩니다. 뷰 컨트롤러와 그 사이의 전환(segue)을 시각적으로 연결하여 화면 전환 흐름을 쉽게 설계할 수 있습니다. 하지만 규모가 큰 프로젝트에서는 관리가 어려울 수 있어 코드 기반 UI 설계도 많이 활용됩니다.

6. 오토 레이아웃(Auto Layout)

오토 레이아웃은 다양한 화면 크기에서 UI 요소들의 위치와 크기를 자동으로 조정하는 레이아웃 시스템입니다. 제약조건(Constraints)을 설정하여 뷰 간의 관계를 정의하고, 디바이스 크기에 따라 적절한 UI를 구성할 수 있습니다. NSLayoutConstraint, Visual Format Language, UIStackView 등을 활용할 수 있습니다.

Auto Layout 상세 분석

Auto Layout은 iOS에서 다양한 화면 크기와 기기에 대응할 수 있도록 UI를 동적으로 배치하는 제약 기반 레이아웃 시스템입니다. 화면 크기가 변해도 일관된 UI 구성을 유지할 수 있도록 돕습니다.


1. Auto Layout의 기본 개념

1) Constraints (제약)

Auto Layout은 뷰들 간의 위치, 크기, 정렬을 결정하는 규칙(Constraints)을 설정하여 동적으로 레이아웃을 조정합니다.

예시:

  • 위치: "이 버튼을 화면의 중앙에 배치"
  • 크기: "이 뷰의 너비를 100pt로 고정"
  • 정렬: "이 라벨을 버튼의 오른쪽에 10pt 간격을 두고 배치"

💡 필수적인 제약 조건(Constraints)

  1. X 좌표 또는 가로 위치 제약 (leading, trailing, centerX 등)
  2. Y 좌표 또는 세로 위치 제약 (top, bottom, centerY 등)
  3. 너비(width) 또는 가로 크기
  4. 높이(height) 또는 세로 크기

위 4가지 요소가 모두 충족되지 않으면 레이아웃이 모호(Ambiguous)해져서 앱 실행 시 경고 또는 오류가 발생할 수 있습니다.


Intrinsic Content Size

어떤 UI 요소들은 기본 크기(콘텐츠 크기)를 가지며, 이를 Intrinsic Content Size라고 합니다.

예시:

  • UILabel: 텍스트 내용에 따라 크기가 결정됨
  • UIButton: 버튼의 타이틀과 내부 패딩에 따라 크기가 정해짐

이러한 뷰는 별도의 width 또는 height 제약을 명시하지 않아도 내부 콘텐츠 크기에 맞춰 자동으로 크기가 결정됩니다.

Intrinsic Content Size를 무시하고 크기를 조절해야 할 경우?

Content Hugging PriorityContent Compression Resistance Priority 설정 필요

  • Content Hugging Priority: 뷰가 너무 커지는 것을 방지 (값이 높을수록 콘텐츠 크기에 맞추려 함)
  • Content Compression Resistance Priority: 뷰가 너무 작아지는 것을 방지 (값이 높을수록 크기를 유지하려 함)

2. Auto Layout 적용 방법

Auto Layout을 적용하는 방법은 다음과 같습니다.

1) Interface Builder (스토리보드)에서 Constraints 설정

Xcode의 스토리보드에서 Auto Layout을 시각적으로 추가할 수 있습니다.

  • Align: 뷰를 가운데 정렬 또는 특정 위치에 정렬
  • Pin: 뷰와 다른 뷰 간의 간격 또는 크기 설정
  • Add Constraints: 특정 위치 및 크기 제약 조건 추가

🔹 장점: 직관적인 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이 예상대로 동작하지 않을 때

  • Autoresizing Mask가 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로 변환됨
  • 따라서 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을 최적화하는 팁

  1. 가능하면 UIStackView를 적극 활용 → 불필요한 제약을 줄여 가독성을 높일 수 있음
  2. Constraint Priority를 활용하여 동적으로 크기 조절
  3. Intrinsic Content Size를 이해하고 활용하여 버튼, 레이블 등의 크기를 자연스럽게 설정
  4. Debugging 도구 사용
    • Xcode의 "View Debugger" 기능으로 Auto Layout 문제를 시각적으로 확인
    • po view.constraints 명령어로 현재 적용된 제약 조건 확인

5. 결론

  • Auto Layout은 모든 화면 크기에 대응할 수 있도록 UI를 동적으로 배치하는 필수 기술
  • *Constraints (제약 조건)**을 사용하여 뷰의 위치와 크기를 설정
  • Intrinsic Content Size 및 Priority 개념을 활용하여 최적의 레이아웃 구성
  • 코드와 스토리보드를 적절히 활용하여 유지보수하기 쉬운 레이아웃 설계가 중요

Auto Layout을 잘 활용하면 다양한 화면 크기에서 안정적인 UI를 제공할 수 있으며, 유지보수도 용이해집니다.

7. UI 키트(UIKit)

UIKit은 iOS의 기본 UI 프레임워크로, 화면을 구성하는 뷰, 컨트롤, 제스처, 애니메이션, 이벤트 처리 등을 제공합니다. UIView, UIButton, UITableView, UICollectionView, UIAlertController 등 다양한 UI 요소를 포함하며, UIViewController 기반의 화면 관리를 지원합니다.

8. 델리게이트(Delegate)

델리게이트 패턴은 객체 간의 간접적인 통신을 가능하게 하는 디자인 패턴입니다. iOS에서 UITableViewDelegate, UITextFieldDelegate 같은 프로토콜을 사용하여 특정 동작이 발생했을 때 이를 다른 객체가 처리할 수 있도록 합니다. 일반적으로 protocol을 정의하고, 이를 채택한 객체에서 동작을 구현하는 방식으로 사용됩니다.

9. 네비게이션 컨트롤러(Navigation Controller)

네비게이션 컨트롤러(UINavigationController)는 계층적인 화면 이동을 관리하는 컨트롤러입니다. 스택(Stack) 구조를 사용하여 pushViewController(_:animated:)로 새로운 화면을 추가하고, popViewController(animated:)로 이전 화면으로 돌아갈 수 있습니다. 일반적으로 백 버튼이 자동으로 제공되며, 네비게이션 바를 통해 화면 제목이나 버튼을 설정할 수 있습니다.


실습 앱개발

(필수사항) 내일배움캠프를 시작하며, 다짐을 남기는 앱을 만들어 봅시다.

“내일배움캠프를 시작하며”라는 제목의 앱을 만들어 봅시다.

5개월 뒤에 iOS 6기를 수료하고, 어엿한 주니어 개발자가 될 나를 상상하며 각오의 한 마디를 기억하고 추억하는 사이트를 만들어 보는 것이에요 🙂

  • 배경 이미지 변경 → 5개월간 함께 할 온라인 학습공간, Zep안에서 원하는 이미지를 캡쳐하여 변경해보세요.
  • 앨범 이미지/제목/내용을 본인이 원하는 형태로 변경해보세요.
    ex) 오늘 내가 공부할 내용 이미지, 오늘의 다짐 한 마디, 오늘의 학습 목표 등
  • 다짐을 저장하는 버튼을 만들고, 카드 형태로 저장되어 볼 수 있게 만들어보세요.

예상 설계
배경 → 이미지 변경

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)
    }
}

0개의 댓글