패스잇

모바일 개발

iOS / Swift 면접 질문

Optional과 안전한 언래핑, Struct와 Class(값 타입·참조 타입), var와 let, ARC와 강한 참조 순환, GCD와 async/await·Actor, Instruments로 메모리 누수 진단, UITableView·UICollectionView 스크롤 성능. iOS 면접은 Swift 문법을 넘어 메모리와 동시성을 어떻게 다루는지, 성능 문제를 어떤 도구로 좁혀 가는지를 묻습니다. 단골 질문을 모범답안과 함께 담았습니다.

총 21문제 · 기초 7 · 중급 7 · 심화 7 · 모범답안 포함

iOS / Swift 면접 질문 · 기초

Q1 기초

Swift에서 `var`와 `let`의 차이점을 설명하고, 각각 어떤 경우에 사용하는 것이 적절한지 예시와 함께 설명해주세요.

힌트 · `var`는 변경 가능한 값에, `let`은 변경 불가능한 값에 사용하며, `let`은 컴파일 타임 최적화에 유리합니다.

네, Swift에서 var와 let은 변수를 선언할 때 사용하는 키워드인데, 가장 큰 차이점은 가변성입니다.

전체 모범답안 펼치기

네, Swift에서 var와 let은 변수를 선언할 때 사용하는 키워드인데, 가장 큰 차이점은 가변성입니다.

var로 선언된 변수는 값을 변경할 수 있습니다. 예를 들어, var count = 0 이라고 선언했다면, count = 1처럼 값을 바꿀 수 있죠. 반면에 let으로 선언된 변수는 상수라서, 한 번 값이 할당되면 변경할 수 없습니다. let name = "Alice" 라고 선언했다면, name의 값을 다른 값으로 바꿀 수 없습니다.

let은 불변성을 보장하기 때문에, 코드의 안정성을 높이고 예상치 못한 값 변경으로 인한 오류를 줄일 수 있습니다. 또한 컴파일러가 최적화를 더 잘 수행할 수 있도록 돕습니다.

어떤 경우에 사용해야 할지 예를 들어보자면, 반복문에서 인덱스를 나타내는 변수는 var로 선언해서 값을 계속 변경해야 하고, 원주율처럼 변하지 않는 값은 let으로 선언하는 것이 좋습니다. 가능하면 let을 사용하는 것이 더 안전하고 효율적인 코드를 작성하는 데 도움이 됩니다.

#가변성#불변성#타입 추론#메모리 안정성#상수

이 질문 단독 페이지 →

Q2 기초

Swift의 Optional이 무엇이며, 왜 필요한지 설명해주세요. 또한, Optional 값을 안전하게 언래핑하는 기본적인 방법 두 가지를 설명해주세요.

힌트 · `Optional`은 값이 있을 수도 있고 없을 수도 있음을 나타내는 타입입니다. `if let`과 `guard let`을 사용하여 안전하게 값을 추출할 수 있습니다.

Swift의 Optional은 변수나 상수가 값을 가질 수도 있고, nil (값이 없음)을 가질 수도 있다는 것을 나타내는 타입입니다. Optional이 필요한 이유는 프로그램의 안정성을 높이기 위해서입니다. 값이…

전체 모범답안 펼치기

Swift의 Optional은 변수나 상수가 값을 가질 수도 있고, nil (값이 없음)을 가질 수도 있다는 것을 나타내는 타입입니다. Optional이 필요한 이유는 프로그램의 안정성을 높이기 위해서입니다. 값이 없을 수 있는 상황을 명시적으로 처리하도록 강제함으로써, 예기치 않은 런타임 에러 (특히 nil 포인터 에러)를 방지할 수 있습니다.

Optional 값을 안전하게 언래핑하는 기본적인 방법 두 가지는 다음과 같습니다.

  1. if let 바인딩: Optional 값이 있는지 확인하고, 있다면 그 값을 새로운 상수에 할당하여 블록 내에서 사용합니다. 값이 nil이면 블록은 실행되지 않습니다.

    if let unwrappedValue = optionalValue {
        print("값이 있습니다: \(unwrappedValue)")
    } else {
        print("값이 없습니다.")
    }
    
  2. guard let 바인딩: 함수나 메서드 내에서 Optional 값이 있는지 확인하고, 없다면 else 블록에서 함수를 종료합니다. 값이 있다면 언래핑된 값을 함수 내에서 계속 사용할 수 있습니다.

    func processValue(optionalValue: Int?) {
        guard let unwrappedValue = optionalValue else {
            print("값이 없습니다. 종료합니다.")
            return
        }
        print("값이 있습니다: \(unwrappedValue)")
    }
    
#옵셔널#nil#안전성#강제 언래핑#옵셔널 바인딩

이 질문 단독 페이지 →

Q3 기초

Swift에서 `Struct`와 `Class`의 주요 차이점은 무엇인가요? 값 타입(Value Type)과 참조 타입(Reference Type)의 관점에서 설명해주세요.

힌트 · `Struct`는 값 타입으로 복사 시 새로운 인스턴스가 생성되고, `Class`는 참조 타입으로 참조를 공유합니다. 상속은 `Class`만 가능합니다.

네, Swift에서 Struct와 Class의 가장 큰 차이점은 값 타입이냐 참조 타입이냐에 있습니다.

전체 모범답안 펼치기

네, Swift에서 Struct와 Class의 가장 큰 차이점은 값 타입이냐 참조 타입이냐에 있습니다.

Struct는 값 타입이기 때문에 변수에 할당하거나 함수에 전달할 때마다 실제 값이 복사됩니다. 즉, 복사본을 변경해도 원본에는 영향을 주지 않습니다. 값 타입 데이터는 주로 스택 메모리에 저장됩니다.

반면 Class는 참조 타입입니다. 변수에 할당하거나 함수에 전달할 때 실제 값이 복사되는 것이 아니라 값의 주소, 즉 참조가 복사됩니다. 따라서 여러 변수가 하나의 인스턴스를 참조할 수 있고, 참조를 통해 인스턴스를 변경하면 모든 참조에서 변경된 내용이 반영됩니다. 참조 타입 데이터는 힙 메모리에 저장됩니다.

또 다른 중요한 차이점은 Class는 상속을 지원하지만, Struct는 상속을 지원하지 않는다는 점입니다.

#값 타입#참조 타입#스택 메모리#힙 메모리#상속

이 질문 단독 페이지 →

Q4 기초

iOS 앱 개발에서 `Delegate` 패턴은 어떤 목적으로 사용되며, 어떻게 구현하는지 기본적인 원리를 설명해주세요.

힌트 · `Delegate` 패턴은 객체 간의 약한 결합을 통해 특정 이벤트를 다른 객체에게 위임하여 처리하도록 하는 디자인 패턴입니다. 프로토콜을 활용합니다.

Delegate 패턴은 iOS 앱 개발에서 객체 간의 통신을 위해 사용되는 디자인 패턴입니다. 핵심은 한 객체가 특정 이벤트나 작업을 다른 객체에게 "위임"하는 것입니다.

전체 모범답안 펼치기

Delegate 패턴은 iOS 앱 개발에서 객체 간의 통신을 위해 사용되는 디자인 패턴입니다. 핵심은 한 객체가 특정 이벤트나 작업을 다른 객체에게 "위임"하는 것입니다.

구현 원리는 다음과 같습니다. 먼저, 프로토콜을 정의합니다. 이 프로토콜은 위임받는 객체(delegate)가 구현해야 할 메서드들을 선언합니다. 위임하는 객체는 이 프로토콜 타입의 프로퍼티를 가지고, 필요할 때 delegate 프로퍼티에 할당된 객체의 메서드를 호출합니다.

이렇게 하면 위임하는 객체는 위임받는 객체의 구체적인 타입에 의존하지 않고, 프로토콜에 정의된 메서드만 호출하면 되므로 느슨한 결합을 유지할 수 있습니다. 예를 들어, UITableView는 UITableViewDelegate 프로토콜을 통해 테이블 뷰의 동작을 컨트롤러에게 위임합니다.

#객체 간 통신#느슨한 결합#프로토콜#메서드 호출#위임

이 질문 단독 페이지 →

Q5 기초

Swift의 `Protocol`이 무엇이며, 어떤 상황에서 유용하게 사용될 수 있는지 구체적인 예를 들어 설명해주세요.

힌트 · `Protocol`은 특정 기능이나 속성을 정의하는 청사진입니다. 여러 타입이 공통된 기능을 구현하도록 강제하거나, 타입 간의 의존성을 줄이는 데 사용됩니다.

Swift의 Protocol은 특정 역할을 수행하기 위한 메서드, 프로퍼티 등의 요구사항을 정의하는 일종의 '청사진'입니다. 클래스, 구조체, 열거형 등 어떤 타입이든 이 프로토콜을 채택(adopt)해서 해당 요구사…

전체 모범답안 펼치기

Swift의 Protocol은 특정 역할을 수행하기 위한 메서드, 프로퍼티 등의 요구사항을 정의하는 일종의 '청사진'입니다. 클래스, 구조체, 열거형 등 어떤 타입이든 이 프로토콜을 채택(adopt)해서 해당 요구사항을 실제로 구현할 수 있습니다.

Protocol이 유용한 상황은 크게 세 가지로 볼 수 있습니다. 첫째, 추상화를 통해 코드의 유연성을 높일 수 있습니다. 예를 들어, Drawable이라는 프로토콜을 정의하고, Rectangle, Circle 클래스가 이를 채택하면, Drawable 타입의 배열에 이 객체들을 함께 담아 관리할 수 있습니다.

둘째, 다형성을 구현할 수 있습니다. 여러 타입이 동일한 프로토콜을 채택함으로써, 동일한 방식으로 동작하도록 만들 수 있습니다.

셋째, Delegate 패턴 구현에 활용됩니다. 특정 이벤트 발생 시 다른 객체에게 알림을 보내는 데 사용되어, 객체 간의 결합도를 낮추고 유연성을 확보할 수 있습니다. 예를 들어, UITableViewDelegate 프로토콜을 통해 테이블 뷰의 이벤트 처리를 위임하는 것이 대표적인 예시입니다.

#추상화#다형성#유연성#Delegate#Mixins

이 질문 단독 페이지 →

Q6 기초

Swift의 `Extension`이 무엇이며, 기존 클래스, 구조체, 열거형 또는 프로토콜에 어떤 기능을 추가할 수 있는지 설명해주세요.

힌트 · `Extension`은 기존 타입에 새로운 기능을 추가할 수 있게 해주지만, 저장 프로퍼티는 추가할 수 없습니다. 메서드, 계산 프로퍼티, 이니셜라이저 등을 추가할 수 있습니다.

Swift의 Extension은 기존에 존재하는 클래스, 구조체, 열거형, 또는 프로토콜에 새로운 기능을 추가할 수 있는 기능입니다. 쉽게 말해, 이미 정의된 타입에 코드를 수정하지 않고도 기능을 확장할 수 있게 해…

전체 모범답안 펼치기

Swift의 Extension은 기존에 존재하는 클래스, 구조체, 열거형, 또는 프로토콜에 새로운 기능을 추가할 수 있는 기능입니다. 쉽게 말해, 이미 정의된 타입에 코드를 수정하지 않고도 기능을 확장할 수 있게 해줍니다.

Extension을 통해 추가할 수 있는 기능으로는 메서드, 계산 프로퍼티, 새로운 이니셜라이저, 서브스크립트, 그리고 특정 프로토콜을 준수하도록 하는 기능 등이 있습니다.

다만, Extension으로는 저장 프로퍼티를 추가할 수는 없습니다. 기존 타입의 메모리 구조를 변경하는 것은 불가능하기 때문입니다. 예를 들어, String 타입에 문자열의 길이를 반환하는 계산 프로퍼티를 추가하거나, 특정 포맷으로 날짜를 출력하는 메서드를 추가하는 데 사용할 수 있습니다.

#기존 타입 확장#기능 추가#연산 프로퍼티#메서드 추가#이니셜라이저

이 질문 단독 페이지 →

Q7 기초

Swift의 `Enum` (열거형)은 무엇이며, 원시 값(Raw Value)과 연관 값(Associated Value)의 차이점을 설명하고 각각의 사용 예를 들어주세요.

힌트 · `Enum`은 관련된 값들을 그룹화하는 타입입니다. `Raw Value`는 각 케이스에 고정된 값을 할당하고, `Associated Value`는 각 케이스와 함께 추가적인 데이터를 저장할 수 있습니다.

Swift의 Enum은 관련된 값들을 그룹화하여 타입 안전성을 높이는 데 사용되는 타입입니다. 예를 들어, 요일을 표현하는 Enum을 만들 수 있습니다.

전체 모범답안 펼치기

Swift의 Enum은 관련된 값들을 그룹화하여 타입 안전성을 높이는 데 사용되는 타입입니다. 예를 들어, 요일을 표현하는 Enum을 만들 수 있습니다.

원시 값(Raw Value)은 Enum 케이스에 미리 정의된 고정된 값입니다. 예를 들어, enum Weekday: String { case monday = "Mon", tuesday = "Tue" } 처럼 각 요일에 해당하는 문자열을 할당할 수 있습니다. Weekday.monday.rawValue는 "Mon"을 반환합니다.

연관 값(Associated Value)은 Enum 케이스와 함께 추가적인 정보를 저장할 수 있게 해줍니다. 예를 들어, enum Result { case success(String), failure(Error) } 처럼 성공 시 문자열 메시지, 실패 시 에러 객체를 함께 저장할 수 있습니다. 연관 값은 패턴 매칭을 통해 접근할 수 있습니다. switch result { case .success(let message): print(message) ... } 와 같이 사용할 수 있습니다. 원시 값은 모든 케이스에 동일한 타입의 값을 할당해야 하지만, 연관 값은 각 케이스마다 다른 타입의 값을 가질 수 있다는 차이점이 있습니다.

#타입#원시 값#연관 값#패턴 매칭#타입 안전성

이 질문 단독 페이지 →

읽기만으론 부족합니다. 직접 말해보세요

패스잇 앱에서 iOS / Swift 질문에 직접 답하면 AI가 1:1로 답변을 코칭합니다.

iOS / Swift 면접 질문 · 중급

Q8 중급

iOS 앱 개발 시 ARC(Automatic Reference Counting)가 어떻게 동작하는지 설명하고, 강한 참조 순환(Retain Cycle)이 발생하는 대표적인 상황과 이를 해결하는 방법에 대해 구체적인 코드 예시 없이 개념적으로 설명해주세요.

힌트 · ARC는 참조 카운트를 관리하며, 클로저나 델리게이트 패턴에서 `weak` 또는 `unowned` 키워드를 사용하여 순환 참조를 방지할 수 있습니다.

ARC는 iOS 앱 개발에서 메모리 관리를 자동으로 해주는 기술입니다. 객체가 더 이상 필요하지 않다고 판단되면 자동으로 메모리에서 해제해줍니다. ARC는 객체의 참조 횟수를 추적하여 동작하는데, 객체를 참조하는 변…

전체 모범답안 펼치기

ARC는 iOS 앱 개발에서 메모리 관리를 자동으로 해주는 기술입니다. 객체가 더 이상 필요하지 않다고 판단되면 자동으로 메모리에서 해제해줍니다. ARC는 객체의 참조 횟수를 추적하여 동작하는데, 객체를 참조하는 변수나 상수가 늘어날 때마다 참조 횟수가 증가하고, 참조가 끊어질 때마다 감소합니다. 참조 횟수가 0이 되면 시스템은 해당 객체를 메모리에서 해제합니다.

강한 참조 순환은 두 객체가 서로를 강하게 참조하여, 어느 객체도 메모리에서 해제될 수 없는 상황을 말합니다. 예를 들어, A 객체가 B 객체를 강하게 참조하고, B 객체도 A 객체를 강하게 참조하면 순환 참조가 발생합니다.

이러한 순환 참조는 주로 클로저나 델리게이트 패턴에서 발생하기 쉽습니다. 해결 방법으로는 weak 또는 unowned 키워드를 사용하여 참조 중 하나를 약한 참조 또는 미소유 참조로 만드는 것입니다. weak 참조는 객체가 해제될 때 자동으로 nil이 되지만, unowned 참조는 객체가 항상 존재한다고 가정하므로, 객체가 해제된 후 접근하면 런타임 에러가 발생할 수 있습니다. 따라서 상황에 맞게 적절한 키워드를 선택해야 합니다.

#ARC#참조 횟수#강한 참조 순환#약한 참조#미소유 참조

이 질문 단독 페이지 →

Q9 중급

Swift에서 구조체(Struct)와 클래스(Class)의 주요 차이점을 메모리 관리 및 데이터 전달 관점에서 설명하고, 각각 어떤 상황에서 사용하는 것이 적절한지 구체적인 사용 사례와 함께 비교하여 설명해주세요.

힌트 · 구조체는 값 타입으로 스택에 저장되고 복사되며, 클래스는 참조 타입으로 힙에 저장되고 참조됩니다. 불변성, 상속, 성능 등을 고려하여 선택합니다.

네, Swift에서 구조체와 클래스의 주요 차이점을 설명드리겠습니다.

전체 모범답안 펼치기

네, Swift에서 구조체와 클래스의 주요 차이점을 설명드리겠습니다.

가장 큰 차이는 메모리 관리 방식입니다. 구조체는 값 타입으로, 변수에 할당하거나 함수에 전달할 때 값이 복사됩니다. 따라서 스택 메모리에 저장되며, 불변성을 유지하기 쉽습니다. 반면 클래스는 참조 타입으로, 변수에 할당하거나 전달할 때 메모리 주소가 복사됩니다. 힙 메모리에 저장되며, 여러 곳에서 동일한 인스턴스를 참조할 수 있습니다.

데이터 전달 관점에서 보면, 구조체는 값을 복사하므로 원본 데이터가 변경되지 않습니다. 클래스는 참조를 전달하므로, 한 곳에서 변경하면 다른 곳에서도 변경된 값이 반영됩니다.

사용 사례를 비교하자면, 구조체는 좌표(x, y), 색상(red, green, blue)처럼 간단하고 불변성이 중요한 데이터에 적합합니다. 클래스는 UIViewController, 데이터 관리 객체처럼 상태를 가지고 상속이 필요한 경우에 적합합니다. 예를 들어, struct Point { var x: Int; var y: Int } 와 같이 간단한 데이터 모델을 만들 때는 구조체를 사용하는 것이 좋습니다.

#값 타입#참조 타입#스택 메모리#힙 메모리#상속

이 질문 단독 페이지 →

Q10 중급

iOS 앱에서 비동기 작업을 처리하기 위해 GCD(Grand Central Dispatch)를 사용하는 경우, `DispatchQueue.main.async`와 `DispatchQueue.global().async`의 차이점을 설명하고, 각각 어떤 용도로 사용되는지 예시를 들어 설명해주세요.

힌트 · `main` 큐는 UI 관련 작업을 처리하는 직렬 큐이며, `global` 큐는 백그라운드에서 CPU-intensive 작업을 처리하는 동시 큐입니다.

GCD를 사용하여 비동기 작업을 처리할 때 DispatchQueue.main.async와 DispatchQueue.global().async는 중요한 차이점을 가집니다.

전체 모범답안 펼치기

GCD를 사용하여 비동기 작업을 처리할 때 DispatchQueue.main.async와 DispatchQueue.global().async는 중요한 차이점을 가집니다.

DispatchQueue.main.async는 메인 큐에서 작업을 실행합니다. 메인 큐는 UI 업데이트와 관련된 모든 작업을 처리하는 직렬 큐입니다. 즉, UI를 변경하거나 UI 관련 데이터를 업데이트할 때는 반드시 메인 큐를 사용해야 합니다. 예를 들어, 네트워크 요청 후 응답을 받아 UI에 표시할 때 DispatchQueue.main.async를 사용합니다.

DispatchQueue.global().async는 백그라운드에서 작업을 실행합니다. 글로벌 큐는 시스템에서 관리하는 동시 큐이며, 다양한 우선순위를 가집니다. CPU 사용량이 많은 작업이나 시간이 오래 걸리는 작업을 메인 큐에서 분리하여 앱의 응답성을 유지하는 데 사용됩니다. 예를 들어, 이미지 처리나 복잡한 계산 작업을 백그라운드에서 처리할 때 DispatchQueue.global().async를 사용합니다.

요약하자면, UI 관련 작업은 main.async를, 그 외의 작업은 global().async를 사용하여 앱의 성능과 사용자 경험을 최적화할 수 있습니다.

#Main Queue#Global Queue#UI 업데이트#우선순위#병렬 처리

이 질문 단독 페이지 →

Q11 중급

Swift에서 델리게이트(Delegate) 패턴은 어떤 목적으로 사용되며, 이를 구현하기 위한 핵심적인 요소(프로토콜, weak 키워드 등)들을 설명하고, 델리게이트 패턴이 NotificationCenter나 KVO에 비해 가지는 장점은 무엇인지 설명해주세요.

힌트 · 델리게이트는 객체 간의 일대일 통신을 위해 프로토콜을 사용하며, 특정 이벤트에 대한 책임을 위임하여 결합도를 낮춥니다. 명확한 메서드 정의와 타입 안정성을 제공합니다.

델리게이트 패턴은 Swift에서 객체 간의 일대일 통신을 위해 사용됩니다. 특정 객체가 해야 할 일을 다른 객체에게 위임하여 코드의 결합도를 낮추고 유연성을 높이는 것이 목적입니다.

전체 모범답안 펼치기

델리게이트 패턴은 Swift에서 객체 간의 일대일 통신을 위해 사용됩니다. 특정 객체가 해야 할 일을 다른 객체에게 위임하여 코드의 결합도를 낮추고 유연성을 높이는 것이 목적입니다.

구현 핵심 요소로는 먼저 프로토콜이 있습니다. 델리게이트 프로토콜을 정의하여 위임할 메서드를 명시하고, 위임을 받는 객체는 이 프로토콜을 채택하여 해당 메서드를 구현합니다. 또한, 순환 참조를 방지하기 위해 델리게이트는 weak 키워드를 사용하여 약한 참조로 선언합니다.

델리게이트 패턴은 NotificationCenter나 KVO에 비해 타입 안정성이 높고, 1:1 통신에 특화되어 있다는 장점이 있습니다. NotificationCenter는 1:N 통신에 적합하지만 타입 정보가 약하고, KVO는 속성 감시에 유용하지만 델리게이트처럼 명확한 메서드 정의를 제공하지 않습니다. 델리게이트는 특정 이벤트에 대한 책임을 명확하게 위임하고, 컴파일 시점에 오류를 검출할 수 있어 더욱 안전한 코드를 작성할 수 있도록 돕습니다.

#Delegate 패턴#프로토콜#weak 참조#타입 안정성#1:1 통신

이 질문 단독 페이지 →

Q12 중급

Swift의 에러 처리(Error Handling) 방식에 대해 설명하고, `do-catch`, `try?`, `try!`의 각각의 용도와 차이점을 구체적인 상황과 연결하여 설명해주세요.

힌트 · `do-catch`는 에러를 직접 처리하고, `try?`는 에러 발생 시 nil을 반환하며, `try!`는 에러가 발생하지 않을 것이라고 확신할 때 사용합니다.

Swift의 에러 처리 방식은 Error 프로토콜을 준수하는 타입을 통해 에러를 표현하고, docatch, try?, try! 구문을 사용하여 에러를 처리합니다.

전체 모범답안 펼치기

Swift의 에러 처리 방식은 Error 프로토콜을 준수하는 타입을 통해 에러를 표현하고, do-catch, try?, try! 구문을 사용하여 에러를 처리합니다.

do-catch는 에러가 발생할 수 있는 코드를 do 블록 안에 넣고, catch 블록에서 특정 에러를 잡아 처리합니다. 예를 들어, 파일 읽기 작업 중 에러가 발생하면 에러 메시지를 사용자에게 보여주는 방식으로 처리할 수 있습니다.

try?는 에러 발생 가능성이 있지만, 에러 처리 로직이 중요하지 않을 때 사용합니다. 에러가 발생하면 nil을 반환하고, 성공하면 옵셔널 값을 반환합니다. 예를 들어, 이미지 다운로드 시 실패해도 앱이 멈추지 않고 기본 이미지를 보여주는 경우에 사용할 수 있습니다. 옵셔널 바인딩을 통해 안전하게 값을 사용할 수 있습니다.

try!는 에러가 절대 발생하지 않을 것이라고 확신할 때 사용합니다. 만약 에러가 발생하면 런타임 에러가 발생하여 앱이 크래시될 수 있으므로, 사용에 주의해야 합니다. 예를 들어, 앱 번들에 포함된 리소스 파일 접근 시 파일이 반드시 존재한다고 확신할 때 사용할 수 있습니다. 하지만 일반적으로는 try!보다는 try?나 do-catch를 사용하여 에러를 안전하게 처리하는 것이 좋습니다.

#Error 프로토콜#do-catch#try?#try!#옵셔널 바인딩

이 질문 단독 페이지 →

Q13 중급

Swift 클로저(Closure)의 특징에 대해 설명하고, 클로저에서 강한 참조 순환(Retain Cycle)이 발생할 수 있는 상황과 이를 방지하기 위한 캡처 리스트(Capture List)의 `weak` 및 `unowned` 사용법에 대해 설명해주세요.

힌트 · 클로저는 주변 환경의 변수를 캡처할 수 있으며, 이로 인해 강한 참조 순환이 발생할 수 있습니다. `[weak self]`나 `[unowned self]`를 사용하여 참조 카운트 문제를 해결합니다.

Swift 클로저는 익명 함수라고 생각하시면 됩니다. 특징으로는 일급 객체이기 때문에 변수에 할당하거나 함수의 인자로 전달할 수 있습니다. 또한, 주변 환경의 변수를 캡처할 수 있다는 장점이 있습니다.

전체 모범답안 펼치기

Swift 클로저는 익명 함수라고 생각하시면 됩니다. 특징으로는 일급 객체이기 때문에 변수에 할당하거나 함수의 인자로 전달할 수 있습니다. 또한, 주변 환경의 변수를 캡처할 수 있다는 장점이 있습니다.

하지만 이 캡처 때문에 강한 참조 순환이 발생할 수 있습니다. 예를 들어, 클래스 인스턴스가 클로저를 소유하고, 클로저가 그 인스턴스를 다시 참조할 때 서로 강하게 참조하여 메모리에서 해제되지 못하는 상황이 발생할 수 있습니다.

이를 해결하기 위해 캡처 리스트를 사용합니다. [weak self]는 self를 약한 참조로 캡처하여, self가 해제되면 클로저 내의 self도 nil이 됩니다. [unowned self]는 self가 항상 존재한다고 가정하고 약한 참조보다 더 빠르게 접근하지만, self가 해제된 후 접근하면 런타임 에러가 발생할 수 있습니다. 따라서 self가 항상 존재한다고 확신할 수 있을 때만 unowned를 사용해야 합니다.

#일급 객체#참조 타입#강한 참조 순환#캡처 리스트#weak/unowned

이 질문 단독 페이지 →

Q14 중급

Swift에서 옵셔널 값에 접근할 때 옵셔널 체이닝(Optional Chaining)과 강제 언래핑(Forced Unwrapping)의 차이점을 설명하고, 각각 어떤 상황에서 사용하는 것이 안전하고 적절한지 예시를 들어 설명해주세요.

힌트 · 옵셔널 체이닝은 `nil`일 경우 `nil`을 반환하여 안전하며, 강제 언래핑은 `nil`일 경우 런타임 에러를 발생시킵니다. 안전한 코드를 위해 옵셔널 체이닝을 선호합니다.

Swift에서 옵셔널 값에 접근하는 방법으로 옵셔널 체이닝과 강제 언래핑이 있습니다. 둘의 가장 큰 차이점은 안전성입니다.

전체 모범답안 펼치기

Swift에서 옵셔널 값에 접근하는 방법으로 옵셔널 체이닝과 강제 언래핑이 있습니다. 둘의 가장 큰 차이점은 안전성입니다.

옵셔널 체이닝은 옵셔널 값이 nil일 경우, 뒤따르는 프로퍼티나 메서드를 실행하지 않고 그냥 nil을 반환합니다. 따라서 런타임 에러를 발생시키지 않아 안전합니다. 예를 들어, person?.address?.street처럼 사용하면 person이나 address가 nil일 경우에도 에러 없이 nil을 반환합니다.

반면, 강제 언래핑은 옵셔널 값이 nil이 아니라고 확신할 때 사용합니다. !를 사용하여 값을 강제로 추출하는데, 만약 값이 nil이라면 런타임 에러가 발생합니다. 예를 들어, myOptional!처럼 사용합니다.

일반적으로 옵셔널 체이닝을 사용하는 것이 더 안전합니다. 강제 언래핑은 정말로 값이 nil일 가능성이 없고, nil일 경우 프로그램이 예상치 못한 동작을 하는 것을 방지하기 위해 사용하는 것이 좋습니다. 즉, 디버깅 과정에서 특정 변수가 항상 값을 가져야 하는 경우에 일시적으로 사용하거나, 앱의 로직상 절대 nil이 될 수 없는 경우에만 신중하게 사용해야 합니다.

#옵셔널 체이닝#강제 언래핑#안전성#nil 반환#런타임 에러

이 질문 단독 페이지 →

iOS / Swift 면접 질문 · 심화

Q15 심화

Swift의 async/await 및 Actor를 활용하여 고도로 동시성 높은 네트워크 요청 시스템을 설계할 때, 스레드 안전성을 보장하고 리소스 사용을 최적화하며, 특히 상호 의존적인 요청 처리 및 취소 메커니즘을 어떻게 구현할 것인지 구체적으로 설명하시오.

힌트 · TaskGroup을 통한 병렬 처리 및 의존성 관리, Task.cancel()을 이용한 취소 전파, Actor의 isolation 및 reentrancy 특성을 고려합니다.

네, 고도로 동시성 높은 네트워크 요청 시스템을 설계할 때 스레드 안전성, 리소스 최적화, 상호 의존적인 요청 처리 및 취소 메커니즘을 구현하는 방법에 대해 설명드리겠습니다.

전체 모범답안 펼치기

네, 고도로 동시성 높은 네트워크 요청 시스템을 설계할 때 스레드 안전성, 리소스 최적화, 상호 의존적인 요청 처리 및 취소 메커니즘을 구현하는 방법에 대해 설명드리겠습니다.

async/await와 Actor를 활용하면 스레드 안전성을 확보하면서 비동기 코드를 간결하게 작성할 수 있습니다. Actor는 상태를 캡슐화하고 동시 접근을 직렬화하여 데이터 경쟁을 방지합니다.

상호 의존적인 요청 처리는 TaskGroup을 사용하여 관리합니다. TaskGroup 내에서 여러 개의 비동기 작업을 병렬로 실행하고, 각 작업 간의 의존성을 설정하여 순서를 제어할 수 있습니다. 예를 들어, 첫 번째 요청이 완료된 후에만 두 번째 요청을 시작하도록 할 수 있습니다.

취소 메커니즘은 Task.cancel()을 통해 구현합니다. TaskGroup 내의 작업 중 하나가 취소되면, TaskGroup 전체를 취소하여 관련된 모든 작업이 중단되도록 합니다. 이를 통해 리소스 낭비를 막고, 사용자 경험을 개선할 수 있습니다.

리소스 최적화를 위해 불필요한 작업 생성을 줄이고, 재사용 가능한 객체를 활용합니다. 또한, 네트워크 요청 시 타임아웃을 설정하여 응답이 없는 요청이 무한정 대기하는 것을 방지합니다.

#async/await#Actor#DispatchGroup#TaskGroup#Continuation

이 질문 단독 페이지 →

Q16 심화

대규모 iOS 애플리케이션에서 복잡한 뷰 계층과 데이터 모델 간의 메모리 누수를 진단하고 해결하기 위한 심층적인 전략을 설명하고, Instruments 도구 중 어떤 것을 주로 활용할 것인지 구체적인 시나리오와 함께 제시하시오.

힌트 · Allocations 및 Leaks 도구를 사용하여 참조 그래프를 분석하고, 클로저의 캡처 리스트와 델리게이트 패턴에서의 약한 참조 사용을 검토합니다.

네, 안녕하세요. 대규모 iOS 앱에서 메모리 누수를 진단하고 해결하는 것은 성능과 안정성에 매우 중요합니다. 복잡한 뷰 계층과 데이터 모델 간의 누수는 특히 까다롭죠.

전체 모범답안 펼치기

네, 안녕하세요. 대규모 iOS 앱에서 메모리 누수를 진단하고 해결하는 것은 성능과 안정성에 매우 중요합니다. 복잡한 뷰 계층과 데이터 모델 간의 누수는 특히 까다롭죠.

우선, Instruments의 Allocations 도구를 사용하여 메모리 사용량을 추적합니다. 특정 객체가 예상보다 오래 살아있는지 확인하고, Retain Count를 분석하여 어떤 객체가 강한 참조를 유지하는지 파악합니다. Leaks 도구는 누수되는 객체를 직접적으로 보여주기 때문에 초기 진단에 유용합니다.

예를 들어, 사용자 프로필 뷰 컨트롤러가 데이터를 가져오는 동안 메모리 누수가 발생한다고 가정해 보겠습니다. Leaks 도구에서 해당 뷰 컨트롤러가 누수되는 것을 확인했다면, Allocations 도구로 이동하여 해당 객체의 참조 그래프를 조사합니다. 클로저 내에서 self를 캡처할 때 [weak self]를 사용하지 않았거나, 델리게이트 패턴에서 weak 참조를 사용하지 않아 Retain Cycle이 발생하는 경우가 흔합니다.

// 예시: Retain Cycle 발생 가능성이 있는 코드
myObject.completionHandler = { [self] result in
    self.updateUI(with: result) // self에 강한 참조 발생
}

이러한 부분을 집중적으로 검토하고, 필요하다면 weak 또는 unowned를 사용하여 참조를 끊어 메모리 누수를 해결합니다.

#Instruments#Leaks#Retain Cycle#ARC (Automatic Reference Counting)#Weak Reference

이 질문 단독 페이지 →

Q17 심화

복잡한 커스텀 UICollectionView 또는 UITableView에서 셀 스크롤 성능 저하가 발생했을 때, 이를 진단하고 최적화하기 위한 고급 기법들을 설명하시오. 특히 오프스크린 렌더링, 레이아웃 계산, 이미지 로딩 관점에서 접근하시오.

힌트 · draw(_:) 메서드의 최적화, 비동기 이미지 로딩 및 캐싱, 셀의 레이아웃 계산을 미리 수행하는 Pre-fetching, CALayer의 rasterization 속성을 고려합니다.

네, UICollectionView나 UITableView에서 셀 스크롤 성능 저하가 발생했을 때, 저는 다음과 같은 방법들을 사용해서 진단하고 최적화합니다.

전체 모범답안 펼치기

네, UICollectionView나 UITableView에서 셀 스크롤 성능 저하가 발생했을 때, 저는 다음과 같은 방법들을 사용해서 진단하고 최적화합니다.

먼저 Instruments를 사용해서 병목 지점을 찾습니다. CPU 사용량, 메모리 할당, Core Animation FPS 등을 집중적으로 봅니다.

오프스크린 렌더링은 성능에 큰 영향을 줄 수 있습니다. 불필요한 그림자나 마스크는 피하고, 필요한 경우 shouldRasterize를 사용해서 캐싱합니다. 하지만 남용하면 오히려 성능이 저하될 수 있으므로 주의해야 합니다.

레이아웃 계산은 가능한 한 미리 수행합니다. prefetchingDataSource를 사용해서 화면에 나타나기 전에 셀의 레이아웃을 계산해두면 스크롤 성능을 향상시킬 수 있습니다.

이미지 로딩은 비동기적으로 처리하고, 메모리 및 디스크 캐싱을 활용합니다. SDWebImage나 Kingfisher 같은 라이브러리를 사용하면 편리합니다.

마지막으로, 셀의 reuseIdentifier를 제대로 사용하고 있는지 확인합니다. 재사용 메커니즘을 효율적으로 활용하면 불필요한 객체 생성을 줄일 수 있습니다. draw(_:) 메서드를 직접 구현해야 한다면 최대한 효율적으로 작성해야 합니다.

#오프스크린 렌더링#레이아웃 캐싱#비동기 이미지 로딩#reuseIdentifier#contentOffset

이 질문 단독 페이지 →

Q18 심화

현재 유지보수 중인 대규모 레거시 MVVM 기반 iOS 앱의 특정 모듈을 Clean Architecture 또는 Composable Architecture로 점진적으로 전환해야 하는 상황입니다. 기존 아키텍처와의 충돌을 최소화하면서 안전하게 마이그레이션하는 전략과 고려사항은 무엇입니까?

힌트 · 새로운 아키텍처를 적용할 경계를 명확히 하고, 어댑터 패턴을 사용하여 기존 코드와 통합하며, 점진적인 리팩토링과 피처 플래그를 활용하여 위험을 줄입니다.

이런 상황에서는 점진적인 접근이 중요합니다. 먼저, 새로운 아키텍처를 적용할 명확한 경계를 설정하는 것이 핵심입니다. 예를 들어, 특정 기능이나 화면 단위로 시작할 수 있습니다.

전체 모범답안 펼치기

이런 상황에서는 점진적인 접근이 중요합니다. 먼저, 새로운 아키텍처를 적용할 명확한 경계를 설정하는 것이 핵심입니다. 예를 들어, 특정 기능이나 화면 단위로 시작할 수 있습니다.

기존 MVVM 코드와 새로운 아키텍처 간의 충돌을 최소화하기 위해 어댑터 패턴, 특히 Facade 패턴이나 Anti-Corruption Layer를 활용하여 두 아키텍처 간의 인터페이스를 정의할 것입니다. 이를 통해 새로운 모듈은 기존 시스템의 세부 사항을 알 필요 없이 일관된 방식으로 상호작용할 수 있습니다.

마이그레이션 과정의 위험을 줄이기 위해 Feature Toggle을 적극적으로 활용하여 새로운 아키텍처로 구현된 기능을 점진적으로 활성화하고, 필요하다면 즉시 이전 버전으로 롤백할 수 있도록 합니다. 또한, Side-by-Side 방식으로 기존 및 신규 구현을 병행하며 테스트 자동화를 강화하여 안정성을 확보할 것입니다.

핵심은 작은 단위로 시작하고, 명확한 경계를 설정하며, 기존 시스템과의 호환성을 유지하는 것입니다.

#Facade 패턴#Anti-Corruption Layer#Feature Toggle#Side-by-Side#테스트 자동화

이 질문 단독 페이지 →

Q19 심화

SwiftUI에서 ViewBuilder를 활용하여 고도로 재사용 가능한 커스텀 컨테이너 뷰를 설계하고, 이 컨테이너 뷰 내부의 자식 뷰들이 불필요하게 업데이트되는 것을 방지하기 위한 성능 최적화 기법을 설명하시오. Preference Key와 GeometryReader의 활용 방안을 포함하여 설명하시오.

힌트 · EquatableView를 사용하여 불필요한 뷰 업데이트를 방지하고, PreferenceKey로 자식 뷰의 정보를 상위 뷰로 전달하며, GeometryReader는 최소한으로 사용하고 레이아웃 우선순위를 조절합니다.

네, SwiftUI에서 재사용 가능한 컨테이너 뷰를 설계하고 성능을 최적화하는 방법에 대해 설명드리겠습니다.

전체 모범답안 펼치기

네, SwiftUI에서 재사용 가능한 컨테이너 뷰를 설계하고 성능을 최적화하는 방법에 대해 설명드리겠습니다.

ViewBuilder를 사용하면 다양한 뷰를 유연하게 컨테이너에 담을 수 있습니다. 이때, 자식 뷰의 불필요한 업데이트를 막기 위해 EquatableView를 활용합니다. EquatableView는 뷰의 내용이 변경되었을 때만 업데이트를 트리거하여 성능을 향상시킵니다.

자식 뷰의 정보를 컨테이너 뷰로 전달할 때는 PreferenceKey를 사용합니다. 예를 들어, 자식 뷰의 크기나 위치 정보를 상위 뷰에서 활용해야 할 때 유용합니다.

GeometryReader는 레이아웃 계산에 강력하지만, 과도하게 사용하면 성능 저하를 일으킬 수 있습니다. 따라서 필요한 경우에만 사용하고, 레이아웃 우선순위를 조절하여 불필요한 레이아웃 계산을 줄이는 것이 좋습니다. 또한, Identifiable 프로토콜을 활용하여 리스트나 ForEach 구문에서 뷰를 효율적으로 업데이트할 수 있습니다.

#ViewBuilder#PreferenceKey#GeometryReader#EquatableView#Identifiable

이 질문 단독 페이지 →

Q20 심화

Combine 프레임워크를 사용하여 실시간 데이터 스트림(예: 웹소켓)을 처리할 때, 백프레셔(Backpressure) 문제를 어떻게 해결할 것이며, 스트림 중간에 발생하는 다양한 종류의 에러를 효과적으로 처리하고 복구하는 전략에 대해 설명하시오.

힌트 · Subscriber의 demand 요청을 통해 백프레셔를 제어하고, catch, replaceError, retry 등의 연산자를 활용하여 에러를 복구하거나 적절히 처리합니다.

Combine 프레임워크에서 실시간 데이터 스트림을 처리할 때 백프레셔와 에러 처리는 중요한 부분입니다.

전체 모범답안 펼치기

Combine 프레임워크에서 실시간 데이터 스트림을 처리할 때 백프레셔와 에러 처리는 중요한 부분입니다.

백프레셔는 Subscriber가 Publisher에게 demand를 요청하는 방식으로 해결합니다. 즉, Subscriber가 처리할 수 있는 만큼만 데이터를 요청하여 Publisher가 과도하게 데이터를 보내는 것을 방지합니다. receive(subscription:)에서 request(.max(n))을 통해 조절할 수 있습니다.

에러 처리는 catch, replaceError, retry와 같은 연산자를 활용합니다. catch는 에러를 잡아서 대체 Publisher를 제공하고, replaceError는 에러를 특정 값으로 대체합니다. retry는 에러 발생 시 스트림을 재시도하여 일시적인 문제 해결에 유용합니다. 예를 들어, 웹소켓 연결이 끊어졌을 때 retry를 사용하여 재연결을 시도할 수 있습니다. 또한, 에러 로깅 및 사용자에게 적절한 피드백을 제공하는 것도 중요합니다.

#Publisher#Subscriber#Backpressure#Error Handling#Retry

이 질문 단독 페이지 →

Q21 심화

대규모 iOS 프로젝트에서 모듈화(Modularization)를 적용하여 빌드 시간을 단축하고 팀 협업 효율성을 높이고자 할 때, 모듈 간의 의존성 관리 전략과 모듈 분리 기준, 그리고 Xcode 빌드 시스템 관점에서 고려해야 할 최적화 방안은 무엇입니까?

힌트 · 단방향 의존성, 순환 의존성 방지, SPM 또는 CocoaPods를 통한 의존성 관리, 정적/동적 프레임워크 선택, precompiled binaries 활용을 고려합니다.

네, 대규모 iOS 프로젝트 모듈화는 빌드 시간 단축과 협업 효율성 향상에 매우 중요합니다.

전체 모범답안 펼치기

네, 대규모 iOS 프로젝트 모듈화는 빌드 시간 단축과 협업 효율성 향상에 매우 중요합니다.

먼저, 모듈 간 의존성 관리는 단방향 의존성을 지향하고 순환 의존성을 방지하는 것이 핵심입니다. 이를 위해 아키텍처 설계 단계에서 모듈 간의 역할을 명확히 정의하고, 의존성 역전 원칙(DIP)을 적용할 수 있습니다. SPM이나 CocoaPods를 사용하여 의존성을 관리하고, 인터페이스 기반으로 모듈 간 통신을 구현합니다.

모듈 분리 기준은 기능별, 레이어별, 또는 팀별로 나눌 수 있습니다. 응집도는 높이고 결합도는 낮추는 방향으로 설계해야 합니다.

Xcode 빌드 시스템 관점에서는 정적 라이브러리보다 동적 프레임워크를 사용하는 것이 빌드 시간을 단축하는 데 유리할 수 있습니다. 다만, 앱 실행 시 로딩 시간이 증가할 수 있으므로 적절한 균형을 찾아야 합니다. Precompiled binaries를 활용하여 자주 변경되지 않는 모듈의 빌드 시간을 줄일 수도 있습니다. Tuist나 Bazel과 같은 빌드 도구를 사용하여 빌드 과정을 자동화하고 최적화하는 것도 좋은 방법입니다.

#SPM (Swift Package Manager)#Tuist#Bazel#Framework vs Static Library#Modular Architecture

이 질문 단독 페이지 →

함께 보면 좋은 모바일 개발 면접 질문

모바일 개발 면접 질문 전체 보기 → 전체 카테고리

보유한 iOS / Swift 질문은 이게 전부가 아닙니다

패스잇 앱에는 직무별 면접 질문 수천 개와 모범답안이 담겨 있습니다. AI 모의면접으로 직접 답하고, 약점을 분석받아 보세요.