스파르타코딩 클럽/본 캠프

10. 스파르타 코딩클럽 [본캠프 - 온보딩 10일차]

UDDT 2025. 3. 14. 18:03

10일차 - 강의 주차

강의 1. Swift 문법 종합반 

 옵셔널(Optional) 

      1. Optional

          - swift는 기본적으로 nil(값 없음)을 허용하지 않음.

          - 값이 없을 수 있는 상황을 정의할 때 Optional Type으로 지정하며, nil을 저장할 수 있음

          - 기존 Type에 ?를 붙여 Optional Type으로 지정

             * 기존 Type: Int, String, Float ...., Array, Dictionary, Set ...., struct, class, enum, 심지어는 클로저까지

          - Optional Type의 값에 접근하면 Optional로 래핑된 값이 반환됨

struct Person {
    var name: String
    var age: Int
    var car: String // 모든 사람이 자동차를 갖고 있나요!?
        
    func introduce() {
        print("안녕하세요. 제 이름은 \(name)이고, 나이는 \(age)살 입니다.")
    }
}

 

 

      2. Optional으로 선언하는 법

// 기본 타입 옵셔널로 선언하기
var intValue: Int? 
var stringValue: String?

// Collection Type 옵셔널로 선언하기
var array: [String]? 
var dictionary: [String: String]?

// struct, class, enum 을 타입처럼 사용하므로 Optional로 선언 가능
struct Person {
	let name: String
}

var optionalPerson: Person?

// 클로저 옵셔널로 선언하기
var closure: (() -> Void)?
// 값을 할당할 때는 기존의 타입과 동일하게 사용
var intValue: Int? = 1
print(intValue) // Optional(1)

var stringValue: String?
stringValue = "안녕하세요"
print(stringValue) // Optional("안녕하세요") 

var nilValue: String? = nil
print(nilValue) // nil : 값이 없음을 의미하는 nil이 출력됩니다.

struct Person {
	let name: String
}

var optionalPerson: Person? = Person(name: "Brody")


var closure: (() -> Void)? = {
	print("Fire")
}
//출력 결과 없음

 

      3. Optional은 기본 타입과 연산이 불가능함

          - Optional(5) + 5 와 같은 연산이 불가능함

          - Optional(5) + Optional(5)와 같은 연산도 불가능함

var intValue: Int? = 5

print(intValue)
// Optional(5)

intValue + 5 
// Error : Value of optional type 'Int?' must be unwrapped to a value of type 'Int'


   * 왜 Optional(5) + Optional(5)는 같은 Type인데도 연산이 안될까?

     Optional Type의 특성 때문. Int?라는 Type 자체가 Optional(5)일수도, nil일수도 있기 때문에 직접 연산할 수가 없음

 

 옵셔널 체이닝(Optional Chaining)

      1. Optional Chaining

          - 옵셔널 타입을 포함하는 복잡한 데이터 구조에서 옵셔널 값이 nil인지 간결하게 체크할 수 있는 방법

          - 중첩된 프로퍼티나 메서드 호출을 한줄로 처리 가능(중간에 nil이 있으면 자동으로 nil을 반환)

          - 옵셔널 값에 접근할 때 프로퍼티나 메서드 이름 뒤에 ?를 붙여서 체이닝 가능

struct Person {
    var name: String = "Default Name"
    var animal: Animal? // 반려동물이 있을수도 있고, 없을 수도 있음
}

struct Animal {
    let name: String
    var age: Int? // 언제 태어난지 모른다면 나이를 정할 수 없어서 Optional 타입으로 설정
}

let person: Person? = Person(name: "Uddt", animal: nil)
print(person?.animal?.name) // person은 nil이 아니므로 다음으로 넘어감
                            // animal?을 확인해보니 nil이여서 nil을 반환
                            // 출력 값 : nil                      

let person2 = Person(name: "Uddt", animal: Animal(name: "Dog", age: 5))
print(person2.animal?.name)  // person2는 옵셔널이 아니기 때문에 ?를 붙이지 않음
                             // animal?에 값이 있기 때문에 넘어감
                             // name에도 값이 있어서 Animal의 "Dog"를 옵셔널 래핑하여 반환
                             // 출력 값 : Optional("Dog")
                             
let stringValue: String? = "안녕하세요"

print(stringValue?.count) // Optional(5)
					      // 옵셔널 체이닝으로 연결되어 있기 때문에 Optional로 래핑된 값이 출력
					      // 옵셔널 값이기 때문에 언래핑하여 사용

 

 옵셔널 언래핑(Optional Unwrapping)

      1. Optional Unwrapping

          - 옵셔널 : 값이 없을 수도 있는 경우를 안전하게 처리하기 위한 것

          - 옵셔널은 기본적으로 값이 래핑되어 있어서 바로 사용하지 못함

          - 이때, 옵셔널을 제거하고 값으로 변환하는 과정을 'Optional Unwrapping'이라 함

 

       2. 옵셔널 바인딩(Optional Binding)

          - 조건문 if로 안전하게 언래핑하는 방식

// 값이 있을 때 if let 옵셔널 바인딩 코드
var intValue: Int? = 10

if let intValue = intValue { 
    print(intValue) // 출력 값 : 10
                    // Optional 언래핑되어 실제 값이 출력
                    // 옵셔널 언래핑된값은 해당 블록에서만 사용 가능
}

print(intValue) // Optional(10)
// 값이 없을 때 if let 옵셔널 바인딩을 사용하여 else에서 작업 진행

var optionalValue: String? // 초기 값이 nil인 상태
if let optionalValue = optionalValue {
    print(optionalValue) // optionalValue가 nil이 아니면 해당 코드블록이 실행되어 값이 출력됩니다.
} else {
    print("optionalValue 값은 nil 입니다.") // 출력 값 : optionalValue 값은 nil 입니다.
}

 

          - 조건문 guard로 안전하게 언래핑하는 방식

             * 옵셔널 바인딩 결과가 nil일 경우, 해당 코드 블록을 빠져나가게 하여 이후 코드가 실행되지 않도록 함

                (보통 클로저나 메서드 내부에서 주로 사용)

// 값이 없는 경우
func guardLetFunction() {
    var optionalValue: String? // 아무런 값을 주지않아서 nil인 상태
    guard let optionalValue = optionalValue else {
        print("guard 실행") // optionalValue가 nil이면 해당 코드블록이 실행됩니다.
        return // 함수를 종료하여 아래의 코드로 내려가지 못하게 막습니다.
    }
    
    print(optionalValue) // 위의 guard let에서 함수가 종료되어 실행되지 못함
}

guardLetFunction()

/* 출력 값
	"guard 실행"
*/

 

func guardLetFunction() {
    var optionalValue: String? = "Hello"
    guard let optionalValue = optionalValue else {
        print("guard 실행") 
        return // 함수를 종료하여 아래의 코드로 내려가지 못하게 막습니다.
    }
    
    print(optionalValue) // Optional이 언래핑된 값 "Hello"가 출력됩니다.
}

guardLetFunction() 

/* 출력 값
	"Hello"
*/

 

       3. 기본 값 제공(Nil - Coalescing Operator)

          - 조건문 if로 안전하게 언래핑하는 방식

             * Optional 값에 ??를 붙여 사용 (옵셔널 값이 nil이면 ?? 뒤에있는 기본 값을 사용)

let name: String? = nil
print(name ?? "Default Name") // 값이 nil이여서 ?? 뒤에 있는 "Default Name"이 출력됨

let greeting = "안녕하세요 \(name ?? "A")님!"
print(greeting) // "안녕하세요 A님!" 이 호출됩니다.
                // name이 nil이여서 기본 값 "A"를 사용

 

       4. 옵셔널 강제 언래핑(Force Unwrapping)

          - 옵셔널 값에 !를 붙여 사용

          - nil이 아니라면 언래핑 되지만, nil일 경우 크래시 발생

             * nil이 아님을 확신할 때만 사용하되, 최대한 사용하지 않는 것이 좋음

var name: String? = "Uddt"

print(name!)  // "Uddt" 출력

name = nil

print(name!) // 런타임 오류 발생

 

       5. 옵셔널 묵시적 언래핑(Implicitly Unwrapped Optional)

          - 옵셔널 타입을 선언할 때 !를 사용하여 선언(원래는 ?를 사용하여 선언)

          - 값을 사용할 때 자동으로 언래핑, 별도의 언래핑 작업 없이 직접 사용 가능

          - 강제 언래핑과 동일하게 값이 있다고 확신이 들때만 사용해야 함

          - 옵셔널 바인딩 가능

var name: String! = "Uddt" // 타입을 String!으로 설정하여 묵시적 옵셔널 언래핑을 사용

print(name) // Optional("Uddt") : 값 자체는 옵셔널이지만,
print(name.count) // 5 : 값에서 가져온 값은 옵셔널값이 아닌 일반 값 출력

if let name = name {
    print(name) // "Uddt" 출력
}
var intValue: Int! = 5

print(intValue)
//Optional(5)

let sumIntValue = intValue + 5  //원래의 Optional이라면 오류가 나겠지만 묵시적 언래핑이라 연산 가능
print(sumIntValue)
//10

 중첩 타입(Nested Type)

      1. 중첩된 타입(Nested Type)

          - 타입 중첩을 통해 구조적으로 복잡한 클래스나 구조체 등을 더 조직적으로 관리할 수 있음

          - class, strcut, enum 등에서 사용 가능

          - 중첩된 타입을 사용하면 코드의 가독성이 높아지고, 타입 간의 연관성이 명확해짐

          - 타입의 블럭 안에서 다른 타입을 정의하고 사용하는 방식으로 구현

struct Car {
    struct Company { // Car 안에 중첩된 Company 구조체
        var name: String
        var phoneNumber: String
        
        func contact() {
            print("\(name) 회사의 A/S 센터 번호는 \(phoneNumber)입니다.")
        }
    }
    
    enum Model {
        case sedan
        case hatchback
        case suv
    }
    
    var model: Model
    var company: Company
    var name: String
    var color: String
}


let myCar = Car(model: .sedan, 
                company: Car.Company(name: "스파르타!", phoneNumber: "000-000-000"),
//                company: .init(name: "스파르타!", phoneNumber: "000-000-000"), // .init을 해서 만들어도 됨
                name: "붕붕이",
                color: "Black")


myCar.company.contact() // myCar의 company 프로퍼티의 contact 함수를 호출
// 결과값 : 스파르타! 회사의 A/S 센터 번호는 000-000-000입니다.
print(myCar.model) // myCal의 model 프로퍼티를 출력

 

 접근제어자(Access Modifier)

      1. 접근제어자

          - 가지고 있는 데이터를 숨기고 싶을 때(접근의 제한을 걸고 싶을 때) 사용

// 접근제어자가 없을 때
struct Person {
    var name: String
    var age: Int
    var havingMoney: Int
    
    func introduce() {
        print("안녕하세요. 제 이름은 \(name)이고, 나이는 \(age)살 입니다.")
    }
}

var me = Person(name: "Uddt", age: 26, havingMoney: 10000)
print(me.havingMoney) // Person 외부에서 havingMoney에 아무렇게나 접근 가능

// 접근제어자가 있다면?
struct Person {
    var name: String
    var age: Int
    private var havingMoney: Int
    
    func introduce() {
        print("안녕하세요. 제 이름은 \(name)이고, 나이는 \(age)살 입니다.")
    }
}

var me = Person(name: "Uddt", age: 26, havingMoney: 10000)
print(me.havingMoney) // 컴파일 에러

 

          1-1. open

                 - 모든 외부 모듈에서 접근할 수 있는 접근 제어자

                    * 가장 개방적인 접근 수준(유일하게 class에서만 사용 가능)

 

          1-2. public

                 - 모든 외부 모듈에서 접근할 수 있는 접근 제어자

                    * 가장 개방적인 접근 수준

 

          1-3. internal

                 - 기본값. 접근 제어자를 따로 설정하지 않았다면 internal

                    * 동일한 모듈 내에서 접근 가능


          1-4. fileprivate

                 - 동일한 파일 내부에서만 접근할 수 있는 접근 제어자


          1-5. private

                 - 가장 제한적인 접근 제어자

                 - 해당 요소를 선언한 스코프(예를 들면, class, struct, enum의 코드블럭 안)에서만 사용 가능

                 - struct에서 private 프로퍼티가 있으면 memberwise init을 통해 자동으로 초기화할 수 없음(수동으로 초기화 필요)

          * 모듈 : 코드 배포의 단일 단위(swift에서 import를 사용하면 다른 모듈에서 데이터를 가져올 수 있음)

struct Person {
    var name: String
    public var age: Int
    private var havingMoney: Int
    
    
    init(name: String, age: Int, havingMoney: Int) {
        self.name = name
        self.age = age
        self.havingMoney = havingMoney
    }
    
    private func printMoney() {
        print("나는 \(havingMoney) 원 있다!")
    }

    func test() {
        printMoney()
    }
}


let person = Person(name: "Brody", age: 20, havingMoney: 3000)

person.test()  // test 함수는 internal(default) 이여서 호출이 가능합니다.
						  // test 함수 안에서는 private printMoney 함수에 접근이 가능합니다.
						  // 출력 값 : 나는 3000 원 있다!0
person.age // public 이기 때문에 접근 가능합니다.

// 빌드 오류 발생
person.havingMoney // person의 havingMoney는 private 프로퍼티 이기 때문에 접근할 수 없습니다.
									 // 'havingMoney' is inaccessible due to 'private' protection level

person.printMoney() // person의 printMoney함수는 private 메소드여서 접근할 수 없습니다.
										// 'printMoney' is inaccessible due to 'private' protection level

 

 프로토콜(protocol)

      1. protocol

          - 프로토콜 자체는 기능을 구현하지 않으며, 오직 설계만 제공

          - class, struct, enum에서 프로토콜을 제어할 수 있으며, 프로토콜에서 정의한 메서드와 속성을 모두 구현해야 함

          - 프로토콜을 채택할 때는 타입이름 : 프로토콜 이름 으로 채택하며, 여러 개의 프로토콜을 채택할 수 있음

          - 프로토콜에서 정의되는 속성은 모두 var로 선언되어야 함

          - 프로토콜에서 정의하는 프로퍼티는 읽기 전용인 { get } 또는 읽기-쓰기 가능한 { get set } 으로 설정할 수 있음

          - { get } 으로만 설정해도 프로퍼티의 값을 변경할 수는 있으나, 명시적으로 작성하면 코드의 의도를 쉽게 파악할 수 있음

          - 프로토콜에서 정의하는 메서드는 이름, 파라미터, 리턴 타입만 선언. 구현부는 미작성

          - Swift에서 프로토콜은 다른 언어에서 말하는 인터페이스 개념과 유사함

// 프로토콜 정의하는 법

protocol 프로토콜이름 {
	// 프로퍼티 정의
	// 메소드 정의
}

protocol FullyNamed {
	var fullName: String { get } 
	
	func sayMyFullName() -> String // 구현부 미작성
}
// 1개의 프로토콜 채택

protocol FullyNamed {
	var fullName: String { get } 
	
	func sayMyFullName() -> String // 구현부 미작성
}

class Person: FullyNamed { // FullyName 프로토콜을 채택합니다.
    var fullName: String  // FullyName 프로토콜에 있는 fullName 프로퍼티를 구현해야 합니다.
    
    func sayMyFullName() -> String { // 프로토콜에 있는 메소드를 구현해야 합니다.
        return fullName
    }
    
    init(fullName: String) {
        self.fullName = fullName
    }
}

var person = Person(fullName: "Brody")

print(person.fullName) // "Brody" 출력
print(person.sayMyFullName()) // "Brody" 출력
// 여러개의 프로토콜 채택

protocol FullyNamed {
    var fullName: String { get }
    
    func sayMyFullName() -> String
}

protocol ShortNamed {
    var shortName: String { get }
}

class Person: FullyNamed, ShortNamed { // 프로토콜 여러개를 채택하는 클래스입니다.
    var fullName: String
    
    func sayMyFullName() -> String {
        return fullName
    }
    
    var shortName: String {
        return "ShortName"
    }
    
    init(fullName: String) {
        self.fullName = fullName
    }
}


var person = Person(fullName: "Brody")

print(person.fullName) // "Brody" 출력
print(person.sayMyFullName()) // "Brody" 출력
print(person.shortName) // "ShortName" 출력

 

      2. 클래스 전용 protocol

          - 클래스 전용 프로토콜은 strcut, enum에서는 사용 불가

          - 프로토콜 정의 시, AnyObject를 채택하면 클래스 전용 프로토콜로 만들 수 있음 

protocol OnlyClassProtocol: AnyObject {

}

 

익스텐션(extension)

      1. Extension

          - extension 키워드를 사용하여 기존 타입을 확장

          - 하나 이상의 프로토콜을 extension하여 사용할 수 있음

          - 하나의 타입에 여러번 extension 가능

          - extension이 가능한 것 : 연산 프로퍼티(계산 속성), 메서드, 새로운 초기화(Init), 중첩된 타입(Nested Type)

// 기본 사용 방법

struct Person {
	 let name: String
}

// extension 키워드 작성 후 확장시키고 싶은 타입 이름을 명시합니다.
extension Person {

}

// 특정 타입의 프로토콜을 확장시키고 싶을 때
extension Person: Equatable {

}
// 확장할 수 있는 것들

struct Person {
    let lastName: String
    let firstName: String
    let age: Int
}

protocol FullyNamed {
    var fullName: String { get }
    
    func sayMyFullName() -> String
}

// extension에서 연산 프로퍼티를 구현할 수 있습니다.
extension Person {
    var nameAge: String {
        return "\(firstName)(\(age)세)"
    }
}

// extension에서 메소드를 구현할 수 있습니다.
extension Person {
    func sayHello() {
        print("\(firstName)님 안녕하세요?")
    }
}

// extension에서 protocol을 채택하여 구현할 수 있습니다.
extension Person: FullyNamed {
    var fullName: String {
        return "\(lastName)\(firstName)"
    }
    
    func sayMyFullName() -> String {
        return "제 이름은 \(fullName)입니다."
    }
}


let person = Person(lastName: "홍", firstName: "길동", age: 20)

print(person.nameAge) // extension에서 구현한 연산속성 사용 가능
person.sayHello()     // extension에서 구현한 메서드를 호출 가능
print(person.fullName) // extension에서 구현한 프로토콜을 사용 가능
print(person.sayMyFullName()) // extension에서 구현한 프로토콜 사용 가능

/* 
길동(20세)
길동님 안녕하세요?
홍길동
제 이름은 홍길동입니다.
*/

 

 Swift 기본 Recap

// enum 질문
// OX 퀴즈
// Q. enum은 한국말로 구조체라고 부른다!?
// A. No. 열거형
// Q. enum으로 정의하면 데이터 타입으로 사용할 수 없다?
// A. X. 사용 가능
// Q. Swift의 enum은 값을 그룹화하여 안전하게 사용할 수 있는 데이터 타입이다!?
// A. O
// Q. enum은 원시 값을 가질 수 있으며, 문자열이나 정수등으로 원시 값을 설정할 수 있다?
// A. O, rawValue를 가질 수 있음.
// Q. enum에 연관 값을 정의할 때는 case 뒤에 소괄호로 타입을 지정하여 연관값을 저장할 수 있다!?
// A. 연관값을 정의할 때는 case 뒤에 ()를 지정하여 연관값 저장 가능.


// enum 질문
// Q. enum에 대해서 설명해주세요.
// A. enum은 열거형으로, case를 통해 값을 관리하고 raw Value로 접근이 가능하며, 연관 값을 사용할 수 있는 Type
// Q. enum에서 그룹화 된 값을 정리하기 위해서 사용하는 키워드는 무엇인가요?
// A. case
// Q. enum에서 rawValue에 대해서 설명해주세요.
// A. rawValue란 원시값으로, 원시값을 지정해서 값을 저장하거나 사용할 수 있음.
// Q. enum에서 rawValue를 사용하기 위해서는 어떻게 enum을 선언해야 되나요?
// A. enum 이름 뒤에 Type을 지정해주면 됩니다. 사용할 때에는 .rawValue를 하면 됩니다.


// enum 질문
// 아래의 한국말을 코드로 작성해주세요.
// 계절을 나타내는 열거형 Season을 선언하세요.
// 봄, 여름, 가을, 겨울을 가질수 있습니다.

enum Season {
    case spring
    case summer
    case autumn
    case winter
}


// enum 질문
// 아래의 한국말을 코드로 작성해주세요.
// 택배 상태를 나타내는 열거형 DeliveryStatus를 선언하세요.
// 주문 완료, 배송 중(송장 번호 포함), 배송 완료(날짜 포함)를 정의하세요.
// 이름은 자유롭게 만드시면 됩니다.
// 연관값을 사용해서 만들어주세요.

enum DeliveryStatus {
    case ordered
    case delivering(trackingNumber: Int)
    case delivered(date: String)
}


// enum 질문
// 아래의 코드를 보고 질문에 답변해주세요.
enum Month: Int {
    case january = 1
    case february
    case march
}

let currentMonth = Month(rawValue: 2)
let testmonth: Month = .january

// Q. currentMonth를 출력하면 어떤값이 나올까요?
// A. Optional(february)
// Q. testmonth의 이름은 컨밴션이 맞고 있나요?
// A. 맞지 않습니다. testMonth 형식으로 사용하면 더 좋을거 같습니다.
// testmonth.rawValue
// Q. testmonth의 rawValue을 출력하면 어떤값이 나올까요?
// A. 1


// enum 질문
// Q. enum내에서 함수를 정의할 수 있나요?
// A. 네


// enum 질문
// Q. 아래의 한국말을 코드로 작성해주세요.
// 1. 교통 신호를 나타내는 열거형 TrafficLight를 선언하세요.
// 2. 각 신호등 상태인 red, yellow, green을 정의하세요.
// 3. enum 내부에 state 이름의 함수를 선언하고 문자열을 반환해주세요.
// state 함수는 red 일 때 "멈추세요", yellow일 때 "속도를 줄이세요", green일 때 "통과하세요"를 반환해주세요.
enum TrafficLight {
    case red
    case yellow
    case green

    func state() -> String {
        switch self {
        case .red:
            return "멈추세요"
        case .yellow:
            return "속도를 줄이세요"
        case .green:
            return "통과하세요"
        }
    }
}

let trafficLight: TrafficLight = .red
let trafficLight2 = TrafficLight.red
trafficLight.state()


// enum 질문
// 아래의 코드를 보고 질문에 답변해주세요.
enum NetworkState {
    case connected
    case disconnected(errorMessage: String)
    case connecting
}

let state = NetworkState.disconnected(errorMessage: "No Internet")
switch state {
case .connected:
    print("연결되었습니다.")
case .disconnected(let errorMessage):
    print("연결 실패: \(errorMessage)")
case .connecting:
    print("연결 중...")
}

// Q. 해당 코드를 실행하면 어떤 값이 출력되나요?
// A. 연결 실패: No Internet
// Q. case disconnected는 연관값을 저장하나요?
// A. O
// Q. case connecting은 연관값을 저장하고 있나요?
// A. X



// Struct 질문
// OX 퀴즈
// Q. 구조체는 프로퍼티(변수,상수) 와 메소드(함수)로 구성되어 있다?
// A. O
// Q. 구조체는 상속이 가능하다?
// A. X
// Q. mutating 키워드는 Struct에서 사용하는 키워드이다!?
// A. O
// Q. Struct는 reference type이다?
// A. X
// Q. 구조체 내부의 프로퍼티는 let으로 선언하고, 구조체의 인스턴스를 var로 선언했을 때 내부 프로퍼티의 값을 변경할 수 있을까요?
// A. X
// Q. 구조체 내부에 있는 저장되어 있는 변수를 메소드라고 부른다?
// A. X

// Struct 질문
// Q. mutating 키워드에 대해서 설명해주세요.
// A. mutating은 프로퍼티를 변경하는 메서드를 선언할 때 사용하는 키워드
// Q. struct에서 제공하는 memberwise init이란 무엇인가요?
// A. 컴파일러가 자동으로 init을 생성해서 별도의 호출없이 초기화해주는 문법
// Q. struct, class 안에 있는 함수를 부르는 이름은 ???
// A. 메서드

// Struct 질문
// 아래의 한국말을 코드로 작성해주세요.
// 1. 사람(Person)을 나타내는 구조체를 정의하세요.
// 2. name과 age 프로퍼티를 정의해주세요.
// 3. 자기소개 메소드를 추가하세요.

struct Person {
    var name: String
    var age: Int

    func introduceMyself() {
        print("안녕하세요. 저는 \(name)이고, 나이는 \(age)입니다.")
    }
}


// Struct 질문
// 아래의 코드를 보고 질문에 답변해주세요.
struct Car {
    var brand: String
    var year: Int
}

let myCar = Car(brand: "CarBrand", year: 2024)
print(myCar.brand)

// Q. 위 코드의 출력값은 무엇일까요?
// A. CarBrand
// Q. myCar.year = 2025 를 할당 한 뒤에 myCar.year를 출력하면 어떻게 될까요?
// A. Error가 발생함. myCar는 let이므로 값을 변경할 수 없음.


// Struct 질문
// 아래의 코드를 보고 질문에 답변해주세요.
struct Rectangle {
    var width: Int
    var height: Int

    func area() -> Int {
        return width * height
    }
}

let rect = Rectangle(width: 10, height: 20)
// Q. rect 상수에 area함수를 사용하여 area 변수에 할당하는 코드를 작성해주세요.
// A. var area = rect.area()


// Struct 질문
// 아래의 한국말을 코드로 작성해주세요.
// 1. 구조체 Book을 정의하세요.
// 2. title, author, price 프로퍼티를 정의하세요.
// 3. 책 정보를 출력하는 메소드를 추가하세요.
struct Book {
    let title: String
    let author: String
    var price: Int

    func getBookInfo() {
        print("이 책은 \(title)이며, 저자는 \(author)이고, 가격은 \(price)입니다.")
    }
}


// Struct 질문
// 아래의 코드를 읽고 오류를 수정해보세요.
struct Point {
    var x: Int
    var y: Int

    mutating func moveTo(x: Int, y: Int) {
        self.x = x
        self.y = y
    }

    func multiple(x: Int, y: Int) -> Int {
        return x * y
    }
}

var point = Point(x: 7, y: 5)
point.moveTo(x: 15, y: 20)
point.x = 20
// Q. 위의 코드를 수정해주세요.
// Q. 위의 코드에서 multiple 이름을 가진 x와 y의 곱한값을 반환하는 함수를 개발해주세요.


// Struct 질문
// 아래 코드를 보고 질문에 답변해주세요.
struct Product {
    var name: String
    var price: Int = 0
}

let item = Product(name: "Laptop")
print(item.price)
// Q. 어떤값이 출력되게 될까요?
// A. 0
// Q. [O/X] price에 값을 주어서 초기화할 수 있나요?
// A. O



// Class 질문
// OX 퀴즈
// Q. 클래스는 프로퍼티(변수,상수) 와 메소드(함수)로 구성되어 있다?
// A. O
// Q. 클래스는 상속이 가능하다?
// A. O
// Q. mutating 키워드는 class에서 사용하는 키워드이다!?
// A. X struct에서 사용되는 키워드
// Q. 클래스는 reference type이다?
// A. O
// Q. 클래스는 멤버와이즈 init을 제공해준다?
// A. X
// Q. 클래스의 인스턴스를 let으로 선언하면 프로퍼티를 변경할 수 없습니다.
// A. X
// Q. 클래스 내부에 있는 저장되어 있는 함수를 메소드라고 부른다?
// A. O


// Class 질문
// 아래의 한국말을 코드로 작성해주세요.
// 1. Car라는 클래스를 정의하세요.
// 2. brand와 year라는 프로퍼티를 가지고, 초기화 메소드로 brand와 year를 설정하세요.
class Car1 {
    let brand : String
    let year: Int

    init(brand: String, year: Int) {
        self.brand = brand
        self.year = year
    }
}

// Class 질문
// 아래의 코드에서 init 초기화를 제거해보세요!
// 그리고 질문에 답변해주세요.
class Cat {
    var name: String = "name"
    var age: Int = 6
}

// Q. [O/X] Cat 인스턴스를 Var 변수에 넣은 뒤 age값을 변경하면 오류가 발생할까요!?
// A. X
// Q. [O/X] Cat 인스턴스를 let 상수에 넣은 뒤 age값을 변경하면 오류가 발생할까요!?
// A. X
// Q. let 인스턴스에 프로퍼티를 변경할 수 있다면 이유는 뭘까요!?
// A. 인스턴스는 값 Type이 아닌 참조 타입으로, 프로퍼티를 변경할 수 있습니다.


// Class 질문
// 아래의 코드를 보고 질문에 답변해주세요.
class Animal {
    var type: String
    var legs: Int

    init(type: String, legs: Int) {
        self.type = type
        self.legs = legs
    }

    func describe() -> String {
        return "This animal is a \(type) and has \(legs) legs."
    }
}

let dog = Animal(type: "Dog", legs: 4)
print(dog.describe())
// Q. 출력값은 무엇일까요!?
// A. This animal is a Dog and has 4 legs.

// Class 질문
// 아래 질문에 답변해주세요.
// Q. class, enum, struct 중에 reference type은 어떤것들이 있나요?
// A. class
// Q. 클래스에서 deinit 메소드는 언제 호출되나요?
// A. 인스턴스가 메모리에서 해제될 때 호출됨.
// Q. 클래스에서 self 키워드의 의미에 대해서 설명해주세요.
// A. 자기 자신의 인스턴스를 사용하겠다는 의미
// Q. 클래스에서 convenience init과 init의 차이점은 무엇인가요?
// A. convenience init은 self.init을 호출해서 init을 실행해주어야 함. init을 쉽게 도와주는 역할

// Class 질문
// 아래 코드를 보고 질문에 답변해주세요.
class Laptop {
    var brand: String
    var memorySize: Int

    init(brand: String, memorySize: Int) {
        self.brand = brand
        self.memorySize = memorySize
    }

    func upgradeMemory(by amount: Int) {
        self.memorySize += amount
    }
}

let myLaptop = Laptop(brand: "Apple", memorySize: 8)
myLaptop.upgradeMemory(by: 8)
print(myLaptop.memorySize)
// Q. 이 코드의 출력값은 무엇일까요?
// A. 16
// Q. 해당 코드에서 class를 struct으로 변경되면 잘 작동할까요?
// A. 아니요

// Class 질문
// 아래의 한국말을 코드로 변환해주세요.
// 1. Student라는 클래스를 정의하세요.
// 2. name과 grade 프로퍼티를 가지고, 초기화 메소드로 name과 grade를 설정하세요.
// 3. 학생을 소개하는 메소드를 추가하세요.
class Student {
    var name: String
    var grade: Int

    init(name: String, grade: Int) {
        self.name = name
        self.grade = grade
    }

    func introduceStudent() {
        print("이 학생의 이름은 \(name)이고, \(grade)학년입니다.")
    }
}


// 인스턴스 질문
// 아래의 질문에 답변해주세요.
// Q. class, struct, enum과 같은 설계도에서 실제 메모리에 생성되는 것을 무엇이라고 하나요?
// A. 인스턴스
// Q. 클래스나 구조체의 인스턴스를 만들 때 왜 init 키워드를 사용하는가요?
// A. 인스턴스를 만들기 전에 init 키워드를 사용하여 값을 초기화하기 위해서

// 인스턴스 질문
// OX 퀴즈
// Q. 붕어빵 틀과 붕어빵의 관계처럼, class는 설계도이고, 그 설계도로 만들어진 실제 객체를 인스턴스라고 합니다.
// A. O
// Q. init 메소드를 사용하면 모든 변수와 상수(프로퍼티)를 초기화해야 합니다.
//    그러나 변수에 기본값을 지정했거나, Optional로 선언한 경우 초기화를 생략할 수 있습니다.
// A. O


// 프로퍼티 질문
// 아래의 질문에 답변해주세요.
// Q. 프로퍼티(Property)란 무엇인가요?
// A. enum, class나 struct 안에 let이나 var로 선언된 요소들을 속성(property)라 합니다.
// Q. 저장 프로퍼티에 대해서 설명해주세요.
// A. Stored Property는 저장 속성으로, var name: String과 같은 형태로 선언된 프로퍼티입니다.

// 프로퍼티 질문
// 아래의 퀴즈에 O/X로 답변해주세요.
// Q. enum에서 저장 프로퍼티를 선언할 수 있다!?
// A. X (저장할 수 없음)
// Q. 연산프로퍼티에는 직접 값을 저장할 수 없다!?
// A. X (직접 저장하지는 않지만 연산 기능을 수행하고 반환하는 역할)
// Q. 연산 프로퍼티에는 let 만 사용가능하고 var는 사용할 수 없다!?
// A. X - 무조건 var만 사용해야 함.
// Q. 프로퍼티 옵저버는 연산 프로퍼티에서도 사용할 수 있습니다.
// A. 사용 할 수 없음. 저장 Property에서만 사용할 수 있음

// 프로퍼티 질문
// 아래의 코드를 보고 질문에 답변해주세요.
struct Person3 {
    var name: String
    var age: Int = 0
}
// Q. 위 코드에서 name과 age는 어떤 종류의 프로퍼티인가요?
// A. name은 저장 속성, age도 저장 속성

// 프로퍼티 질문
// 아래의 코드를 보고 질문에 답변해주세요.
struct Person2 {
    var name: String
    var age: Int = 20
    var isAdult: Bool {
        return age >= 20
    }
}
let person = Person2(name: "Test")
// Q. 위의 코드에서 isAdult는 어떤 프로퍼티 인가요?
// A. isAdult는 연산 속성(계산 속성)
// Q. person.isAdult을 출력하면 어떤값이 출력될까요?
// A. true


// 프로퍼티 질문
// 아래의 한국말을 코드로 작성해주세요.
// Car라는 구조체를 정의하고 brand와 year라는 저장 프로퍼티를 추가하세요.
// isNewCar라는 연산 프로퍼티를 추가하여, year가 2021 이상이면 true를 반환하세요.
struct stCar {
    let brand : String
    var year: Int
    var isNewCar: Bool {
        return year >= 2021
    }
}


// 프로퍼티 질문
// 아래의 코드를 보고 답변에 질문해주세요.
struct Person1 {
    var name: String
    var age: Int
    static var species = "Human"
}
// Q. species 는 어떤 프로퍼티 인가요?
// A. Type property
// Q. species에 접근해서 출력하는 코드를 아래 작성해주세요.
// A. Person1.species


// 프로퍼티 질문
// 아래의 코드를 보고 질문에 답해주세요
struct Person4 {
    var age: Int {
        willSet {
            print("Age will be set to \(newValue)")
        }
        didSet {
            print("Age was \(oldValue)")
        }
    }
}

// Q. willSet와 didSet을 무엇이라고 부르나요?
// A. 프로퍼티 옵저버
// Q. age는 연산프로퍼티, 저장프로퍼티중 무엇인가요?
// A. 저장 프로퍼티
// Q. willSet, didSet 중 어떤게 먼저 호출되나요?
// A. willSet이 먼저 호출



// 메소드 질문
// 아래의 질문에 답변해주세요.
// Q. 인스턴스 메소드에 대해서 설명해주세요.
// A. 인스턴스 메서드란 클래스나 struct에서 선언된 함수를 말합니다.


// 메소드 질문
// OX 퀴즈
// Q. class 내부에서 사용하는 함수를 메소드라고 부른다?
// A. O
// Q. struct에서 인스턴스 메소드를 사용하려면 mutating 키워드를 사용해야만 프로퍼티 값을 변경할 수 있다.
// A. O
// Q. 클래스의 인스턴스 메소드는 프로퍼티를 직접 변경할 수 있다.
// A. O
// Q. class, struct, enum 모두에서 타입 메소드를 사용할 수 있으며, static 키워드를 사용해 정의할 수 있다.
// A. X


// 메소드 질문
// 아래의 한국말로 코드로 작성해주세요.
// Car라는 구조체를 정의하고, speed라는 Int 타입의 프로퍼티를 추가하세요.
// accelerate라는 인스턴스 메소드를 추가하여, speed에 10을 더해 속도를 증가시키세요.
struct strCar {
    var speed: Int
    mutating func acclerate() {
        self.speed += 10
    }
}

// 메소드 질문
// 아래의 코드를 보고 질문에 답변해주세요.
class Calculator {
    var defaultValue = 0
    static func add(a: Int, b: Int) -> Int {
        return a + b
    }
}
// Q. 위의 코드에서 오류가 발생하는 이유에 대해서 설명해주세요.
// A. defaultValue는 인스턴스가 만들어졌을 때에 사용이 가능합니다.
// Q. Calculator 클래스의 add 메소드는 어떻게 호출할 수 있나요?
// A. Calculator.add(a: 3, b: 7) 등으로 호출 가능



// 옵셔널 질문
// 아래의 질문에 답변해주세요.
// Q. 옵셔널이란 무엇인가요?
// A. 옵셔널이란 nil을 저장할 수 있게 해주는 Type입니다.


// 옵셔널 질문
// OX 퀴즈
// Q. Swift는 기본적으로 nil을 가질 수 있다!?
// A. X
// Q. 옵셔널 타입의 변수는 nil을 가질 수 있다!?
// A. O
// Q. 옵셔널 타입은 기본 타입과 달리 값이 없을 수 있는 상태를 처리할 수 있다.
// A. O

// 옵셔널 질문
// 아래의 코드를 보고 질문에 답변해주세요.
var optionalString: String? = "Hello, world!"
print(optionalString)
// Q. 위의 코드를 실행하면 어떤값이 출력될까요?
// A. optional("Hello, world")



// 옵셔널 질문
// 아래의 한국말을 코드로 작성해주세요.
// 1. Int 타입의 optionalInt라는 변수를 선언하고 옵셔널로 설정하세요.
// 2. 해당 변수에 nil 값을 할당하세요.
var optionalInt: Int? = nil

// 옵셔널 질문
// 아래의 코드를 보고 질문에 답변해주세요.
struct firstCar {
    var model: String?
    var year: Int?
}

var myFirstCar = firstCar(model: nil, year: 2020)
print(myFirstCar.model)
// Q. 위 코드를 실행하면 어떤 값이 출력되나요?
// A. nil
// Q. [O/X] myFirstCar 변수는 옵셔널 타입인가요?
// A. X

// 옵셔널 질문
// 아래의 코드를 보고 질문에 답변해주세요.
let optionalInt: Int? = 5
print(optionalInt + 3)
// Q. 위의 코드에서 오류가 나는 이유가 무엇인가요?
// A. optionalInt와 Int는 Type이 달라서 연산이 불가능함.


// 옵셔널 질문
// 아래의 질문에 답변해주세요.
// Q. 옵셔널 체이닝(Optional Chaining)이란 무엇인가요?
// A. 옵셔널을 안전하게 사용할 수 있으며 ?를 사용하여 연결하는 기능

// 옵셔널 질문
// 아래의 코드를 보고 질문에 답변해주세요.
struct Owner {
    var pet: Pet?
}

struct Pet {
    var name: String?
}

var owner = Owner(pet: Pet(name: "Buddy"))
print(owner.pet?.name)
// Q. 위의 코드의 실행하면 어떤 값이 출력되나요?
// A. optional("Buddy")
owner = Owner(pet: nil)
print(owner.pet?.name)
// Q. 위의 코드를 실행하면 어떤 값이 출력될까요!?
// A. nil


// 옵셔널 질문
// OX 퀴즈
// Q. 옵셔널 체이닝을 사용할 때 중간 값이 nil이면 전체 연산이 중단되고 nil이 반환된다.
// A. O


// 옵셔널 질문
// 코드를 보고 질문에 답변해주세요
let stringValue: String? = "Hello"
print(stringValue?.count)
// Q. 위 코드를 실행하면 어떤값이 출력될까요?
// A. Optional(5)

// 옵셔널 질문
// 아래의 질문에 답변해주세요.
// Q. 옵셔널 바인딩이란 무엇인가요?
// A. 옵셔널 바인딩이란 옵셔널 Type의 값을 언래핑할 때 안전하게 처리하도록 도와주는 문법 if문과 guard문을 활용해 언래핑 할 수 있음.
// Q. 강제 언래핑(Force Unwrapping)란 무엇인가요?
// A. 강제 언래핑이란 !를 통해 optional 값을 강제로 언래핑하는 것으로, 값이 들어 있는 것이 확실할 때 처리함.
// Q. 옵셔널 언래핑하는 방법에 대해서 아는만큼 설명해주세요.
// A. 옵셔널 체이닝(?. 로 추출), 옵셔널 바인딩(if, guard 조건문으로 추출), 강제 언래핑(값에서 !로 추출), 묵시적 언래핑(선언할 때 !로 선언), 3항 연산자로 추출

// 옵셔널 질문
// OX 퀴즈
// Q. 옵셔널 바인딩을 사용할 때는 반드시 if let이나 guard let을 사용해야 한다.
// A. O
// Q. guard let을 사용했을 때 옵셔널 언래핑에 성공하면 함수를 종료해야 한다!?
// A. X, 언래핑에 실패하면 종료
// Q. 옵셔널 값을 강제 언래핑하면 값이 nil일 경우 런타임 오류가 발생한다.
// A. O

// 옵셔널 질문
// 아래의 코드를 보고 질문에 답변해주세요.
var name: String? = "Brody"
if let unwrappedName = name {
    print(unwrappedName)
}
// Q. 위 코드를 실행하면 출력 결과는 무엇인가요?
// A. Brody


// 옵셔널 질문
// 아래의 코드를 보고 질문에 답변해주세요.
var optionalValue: Int? = nil
print(optionalValue ?? 10)
// Q. 위 코드를 실행하면 출력 결과는 무엇인가요?
// 10

// 옵셔널 질문
// Q. 아래의 한국말을 코드로 작성해주세요.
// 1. String? 타입의 변수를 선언하고, nil일 때 기본값으로 "Unknown"을 출력하는 코드를 작성하세요.
// var name: String?
// print(name ?? "Unknown")

// 옵셔널 질문
// 아래의 코드를 보고 질문에 답변해주세요.
var age: Int? = 25
print(age!)
// Q. 위 코드를 실행하면 출력 결과는 무엇인가요?
// A. 25

// 옵셔널 질문
// 아래의 코드를 보고 질문에 답변해주세요.
var optionalName: String? = "Brody"
print(optionalName!)
optionalName = nil
print(optionalName!)
// Q. 위 코드를 실행하면 어떤 오류가 발생하며, 그 이유는 무엇인가요?
// nil일때 강제 언래핑을 하면 런타임 에러가 발생함.



// 프로토콜 질문
// 아래의 질문에 답변해주세요.
// Q. 프로토콜에 대해서 설명해주세요.
// A. 프로토콜은 청사진. 사용해야 할 프로퍼티나 메서드를 정의해놓은 설계도
// Q. 프로토콜에서 프로퍼티를 정의하는 방법에 대해서 설명해주세요.
// A. var name: String 으로 선언만. { get } , { get set }을 정해주면 됨.
// Q. 클래스 전용 프로토콜을 정의하려면 어떻게 해야 할까요?
// A. AnyObject를 채택하면 됨.


// 프로토콜 질문
// OX 퀴즈
// Q. 프로토콜은 class, struct 에서만 사용할 수 있다?
// A. enum에서도 사용 가능
// Q. 프로토콜에서 정의한 프로퍼티는 항상 var 만 사용할 수 있다?
// A. O
// Q. 프로토콜을 채택한 class는 프로토콜에서 정의한 모든 프로퍼티, 메소드를 구현해야 한다?
// A. O
// Q. 프로토콜은 여러개를 채택할 수 있다!?
// A. O
// Q. 프로토콜에서 정의된 메소드는 기본적으로 구현되어 있기 때문에, 프로토콜을 채택한 타입은 이를 재정의할 필요가 없다.
// A. X


// 프로토콜 질문
// 아래의 한국말을 코드로 작성해주세요.
// Car라는 프로토콜을 선언하고, drive라는 메소드를 정의해주세요.
// 이 메소드는 파라미터를 받지 않고, String을 반환해야 합니다.
// Car 프로토콜을 채택하는 MyCar class 를 정의해보세요.
protocol MyCar {
    func drive() -> String
}

class MyCarClass: MyCar {
    func drive() -> String {
        return "운전을 시작합니다."
    }
}


// 프로토콜 질문
// 아래의 코드를 보고 질문에 답변해주세요.
protocol Playable {
    func play() -> String
}

class Game: Playable {
    func play() -> String {
        return "Playing game"
    }
}

let game = Game()
print(game.play())
// Q. 위 코드를 실행하면 출력 결과는 무엇인가요?
// A. Playing game


// 프로토콜 질문
// 아래의 한국말을 코드로 작성해주세요.
// Q. Walkable이라는 프로토콜을 정의하고, walk라는 메소드를 구현하세요. 이 메소드는 String을 반환합니다.
// 해당 프로토콜에서 feetCount라는 발갯수를 저장할 수 있는 프로퍼티도 정의해주세요.
// 그리고 Dog라는 구조체가 이 프로토콜을 채택하고 "Dog is walking"이라는 문자열을 반환하도록 코드를 작성하세요.
protocol Walkable {
    func walk() -> String
    var feetCount: Int { get set }
}

struct Dog: Walkable {
    func walk() -> String {
        return "Dog is walking"
    }

    var feetCount: Int
}
let dog4 = Dog(feetCount: 4)
print(dog4.walk())


// extension 질문
// 아래의 질문에 답변해주세요.
// Q. extension에 대해서 설명해주세요.
// A. extension은 확장으로, 클래스나 struct의, enum 기능을 수평적으로 늘려 사용할 수 있는 문법이다.
// Q. extension은 어떤것을 확장할 수 있나요?
// A. 연산 프로퍼티, 메서드, class, enum, strcut


// extension 질문
// OX 퀴즈
// Q. extension을 사용하면 기존의 구조체, 클래스의 원본 코드를 수정할 필요 없이 기능을 추가할 수 있다.
// A. O
// Q. extension에서는 저장 프로퍼티를 정의할 수 있다.
// A. X, 연산 프로퍼티 사용
// Q. extension에서 프로토콜을 채택하면 해당 프로토콜의 요구사항을 반드시 구현해야 한다.
// A. O
// Q. extension을 여러 번 선언하여 하나의 타입을 여러 단계로 확장할 수 있나요?
// A. O
// Q. extension을 사용하면 기본 데이터 타입(Int, String 등)도 확장할 수 있다.
// A. O

 

강의 2. iOS 앱 개발 입문

 스토리보드 UI

    1. IBOutlet : Interface Builder와 연결된 객체

        - UI 객체를 스토리보드에서 Ctrl을 누른채로 Drag & Drop 하여 IBOutlet 생성(마우스 우클릭으로도 Drag & Drop 가능)

@IBOutlet weak var imageView: UIImageView!

        - weak는 약한 참조를 의미

        - 스토리보드와 UI 객체의 연결고리 역할

        - IB는 Interface Builder의 약어

 

    2. IBAction : Interface Builder와 연결된 Action

        - UI 객체를 스토리보드에서 Ctrl을 누른채로 Drag & Drop 하여 IBAction 생성(마우스 우클릭으로도 Drag & Drop 가능)

        - 스토리보드와 UI 액션(이벤트)의 연결고리 역할

 

 스토리보드에서 이미지와 버튼 추가하기

    1. UI ImageView 생성 후 제약 추가

 

    2. Asset에 이미지 파일을 넣고, Image View 선택 후 Inspector에서 image 선택

 

    3. ImageView를 Outlet으로 연결

 

    4. Button 생성 후 Outlet으로 연결

 

    5. Button을 Action으로도 연결

 

    6. Button을 클릭하면 print로 확인할 수 있게 코드 추가

 

 버튼에 동작 추가하기

    1. ImageView의 이미지 변경

 

    2. 버튼이 눌리면 이미지가 변경되게 코드로 추가

 

    3. 버튼을 누르면 다음과 같이 이미지가 바뀌게 됨

 

 스토리보드에서 로그인 화면 구현하기

    1. View의 Background 컬러 변경

 

    2. Label 추가 후 Login 입력, 텍스트 설정 변경

 

    3. Label의 제약 추가

 

    4. ID를 입력할 Text Field 추가, 제약 설정

 

    5. Password를 입력할 TextField 추가, 제약 설정

 

    6. Button 추가, 제약 설정

 

    7. Button inspector에서 cornerRadius 적용

 

    8. 결과 확인

 

    9. Button에 Action 추가

 

    10. ID Field는 Outlet으로 연결

 

    11. Password Field도 Outlet으로 연결

 

    12. ID, PW 확인을 위한 코드 작성

 

    13. 결과 확인

결과