ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TIL 11일차
    iOS 앱 개발 부트캠프/TIL 2024. 9. 26. 17:50

    오늘은 머리를 아프게 했던 성적관리 시스템은 일단 남겨두고 빠르게 할 수 있을듯한 그 다음 과제들을 먼저 하기로 했다.

    성적관리 시스템의 다음 문제는 다음과 같은 문제였는데 매우 간단해 보였고, 오늘 문제는 굳이 스토리보드를 이용할 필요 없이 플레이그라운드로 할 수 있어 보였다.

    처음부터 플레이그라운드를 쓰지 않았던 건 플레이그라운드가 뭔지 몰라서 그런 것도 있고, 입력을 받아야 한다는 문장 때문에 그렇기도 했고 앱 개발이 하고 싶어서 참여한 캠프이니 어려워도 앱 개발 비스무리한 걸 하고 싶었기 때문이었다..

    하지만 배운 거 없이 낯선 언어의 문법 학습과 앱 UI 구성까지 온전히 다 독학으로 하려니 너무 어렵고 이해 안되는 것 투성이라 흥미가 떨어지려 하니 쉬운 것부터 하나씩 하는 게 나을 것 같다.

    var width = 10.0
    var height = 5.0
    
    var area: Double {
        if width <= 0 || height <= 0 {
            return 0
        }else{
            return width * height
        }
    }
    
    var circumference: Double {
        if width <= 0 || height <= 0 {
            return 0
        } else {
            return 2 * (width + height)
        }
    }
    
    print("넓이: \(area), \n둘레: \(circumference)")

    연산 프로퍼티도 그냥 넘어 왔으면 그게 뭐야 싶었을테지만 데이터 타입 연습할 때 해봐서 익숙한 형식의 프로퍼티이다.

    넓이를 구하는 area와 둘레를 구하는 circumference를 선언하고 if문을 사용해 width와 height가 0 이하일때는 0을 반환하고 아니면 넓이와 둘레를 각각 반환하도록 간단하게 구성하였고 print를 이용해 출력하여 무난히 완성하였다.


    다음은 간단한 은행 계좌 관리 시스템이었다.

    var balance : Int = 0 {
        willSet {
            print("잔액이 \(balance)에서 \(newValue)원으로 변경 됩니다.")
        }
        didSet {
            if balance < 0 {
                print("🚨경고: 잔액은 음수가 될 수 없습니다. 잔액을 0원으로 설정합니다.\n")
                balance = 0
            } else {
                print("잔액이 \(balance)원으로 변경 되었습니다.\n")
            }
        }
    }
    print("현재 잔액은 \(balance)원입니다.\n")
    balance = 1000
    balance = -500
    balance = 20000
    balance = 0

    willSet과 didSet을 이용하라기에 그게 무엇인가 찾아보니 swift에서 제공하는 기능으로, 프로퍼티의 값이 변경될 때 자동으로 호출되는 프로퍼티 옵저버라고 한다.

    willSet은 값이 변경되기 직전에 호출되어 새로 저장될 값을 확인 할 수 있고 newValue라는 매개변수로 제공되며, didSet은 값이 변경된 직후에 호출되어 기존 값을 확인할 수 있고 oldValue라는 매개변수로 제동된다고 한다.

    이걸 활용해 쓴 코드인데, balance는 초기에 0이었다가 다른 값으로 변하면, 변하기 직전에 willSet에서 기존 잔액인 balance와 변하게 될 잔액인 newValue를 출력해 확인 할 수 있고 balance값이 변한 직후 didSet에서 변한 잔액인 balance와 변하기 직전의 잔액인 oldValue를 출력해 확인 할 수 있다.

    이를 활용하면 프로퍼티의 값을 설정할 때 검증 로직을 추가하여 잘못된 값이 설정되지 않도록 할 수 있다고 한다. didSet의 oldValue를 통해 특정 범위의 값이 아니면 oldValue로 값을 복구하는 식인 것이다. 또, UI에서 모델의 데이터가 변경될 때 UI를 자동으로 업데이트 하는데도 쓸 수 있기도 하고, 프로퍼티의 값이 변경될 때 특정 알림을 전송하거나 이벤트를 발생시키는 트리거로도 활용 가능하다고 한다.


    다음은 간단한 운동 관리 시스템이다.

    /운동 관리 시스템
    //필요한 변수 : 시간, 몸무게 2개, 체온, 체력, 횟수
    var time: Int = 0
    var weight1: Double = 0.0
    var weight2: Double = 0.0
    var temperature: Double = 0.0
    var health: Int = 0
    var count: Int = 0
    
    
    //운동 시간 함수
    time = 45
    
    func workout(min: Int) {
        if min >= 60 {
            print("\(min)분이나 운동했어요. 운동을 많이 했습니다!")
        } else {
            min >= 30 ? print("\(min)분이면 적당히 운동했어요.") : print("\(min)분이면 운동이 부족해요")
        }
    }
    workout(min: time)
    time = 20
    workout(min: time)
    time = 70
    workout(min: time)
    print()
    
    //몸무게 비교 함수
    weight1 = 60.0
    weight2 = 50.0
    
    func compare(weightA: Double, weightB: Double) {
        if weightA > weightB {
            print("2번이 1번보다 가벼워요.")
        } else if weightA < weightB{
            print("1번이 2번보다 가벼워요.")
        } else {
            print("몸무게가 같아요.")
        }
    }
    compare(weightA: weight1, weightB: weight2)
    weight1 = 60.0
    weight2 = 60.0
    compare(weightA: weight1, weightB: weight2)
    print()
    
    //체온, 체력에 따른 운동 가능 여부 함수
    temperature = 37.0
    health = 80
    
    func status(bodyTemperature: Double, stamina: Int) {
        if bodyTemperature <= 36.5 && stamina >= 70 {
            print("아직 더 운동할 수 있어요.")
        } else {
            print("충분히 운동했어요.")
        }
    }
    status(bodyTemperature: temperature, stamina: health)
    temperature = 36.5
    health = 60
    status(bodyTemperature: temperature, stamina: health)
    temperature = 36.2
    health = 95
    status(bodyTemperature: temperature, stamina: health)
    print()
    
    //운동 횟수 기록 범위연산자
    count = 5
    func countWorkout(excerciseCount: Int) {
        if (1...10).contains(excerciseCount) {
            print("\(excerciseCount)번 운동 했어요.")
        }else {
            print("운동 횟수는 1~10 사이의 숫자를 입력해주세요")
        }
    }
    countWorkout(excerciseCount: count)
    count = 11
    countWorkout(excerciseCount: count)

    우선 문제에 따라 필요한 변수들인 운동 시간, 2명의 몸무게, 체온, 체력, 운동 횟수에 대한 저장 프로퍼티를 선언하고, 메서드를 작성해 저장 프로퍼티의 값을 일일이 바꿔가며 테스트 하였다.

    처음엔 운동시간 메서드를 작성해 60분 이상이면 운동을 많이 했다는 글을 출력하고, 아니라면 더 해보라는 글을 출력해야 했지만 5번 문제에 삼항연산자를 활용해 30분 이상이면 적당히, 30분 미만이면 부족하다는 출력을 하라고해서 통합시켰다.

    따라서 if문의 첫 분기를 time>=60으로 하고 else문에서 삼항연산자를 활용해 30분 이상이면서 60분 미만이면 적당히 운동했다는 글을, 30분 미만이라면 부족하다는 글을 출력하도록 하였다.

    다음은 몸무게 비교 메서드인데 두 사람의 몸무게를 받아서 비교연산자를 활용해 단순 비교한 뒤 출력하는 문제이니 어렵지 않았다.

    다음으로 체온, 체력에 따른 운동 가능 여부에 대해 출력하는 메서드를 작성했다. 체온을 의미하는 bodyTemperature와 체력을 의미하는 stamina의 값을 매개변수로 입력 받아 받아 체온이 36.5도 이하, 체력이 70 이상일 때 더 운동할 수 있다는 글을 출력하고 아니면 충분히 운동했다는 글을 출력해야 하므로 체온과 체력 둘 중 하나라도 부족한 게 있는지 확인하기 위해 && 연산자를 사용하였다.

    마지막으로 범위 연산자를 활용한 운동 횟수 기록 메서드이다. 운동횟수 excerciseCount를 매개변수로 입력받아 1~10사이의 수라면 해당 횟수만큼 운동했다는 글을 출력하고 그 외의 숫자를 입력하면 1~10사이의 값을 입력하라는 문구를 출력하도록 하였다.


    이후 며칠전부터 계속 하던 네비게이션 컨트롤러에 대해 공부하기로 했다. 구글링을 하여 찾다 어떤 예시를 보게 되었는데 코드에 lazy 라든가 super, self라는 키워드를 자주 보게 되어서 궁금하여 해당 키워드에 대해 또 찾아보게 되었다.

    먼저 lazy는 프로퍼티를 선언할 때 붙이는 키워드인데, lazy로 선언된 프로퍼티는 다른 프로퍼티랑 다르게 메모리와 성능을 절약하기 위해 초기화되지 않고 대기하다가 처음 사용될 때 메모리에 할당되어 초기화된다고 한다.

    이렇게 했을 때 장점은 메모리를 효율적으로 관리할 수 있게 된다고 한다. 뷰를 불러올 때 모든 요소를 한번에 메모리에 올리면 과부하가 걸리고 앱이 종료될 수도 있는데, 기다렸다가 사용될 때 초기화하면 효율적으로 메모리 자원을 관리할 수 있게 되고 앱이 요청을 수행하는 동작 시간도 줄어들게 될 것이다.

    주의할 것은 lazy 프로퍼티를 선언 할 땐 항상 var로 선언해야 한다고 한다. let으로 선언된 상수 프로퍼티는 초기화가 끝나기 전까지 반드시 값을 가져야 하는데 lazy 프로퍼티는 초기화가 완료된 시점까지 초기값을 알 수 없기 때문에 상수 프로퍼티는 lazy로 선언될 수 없다고 한다.


    super 키워드는 부모 클래스의 메서드나 프로퍼티에 접근할 때 사용하는 키워드라고 한다. iOS 앱 프로젝트를 처음 만들어 뷰컨트롤러를 보면 UIViewController 안에 override func viewDidLoad()가 있고 그안에 super.viewDidLoad()가 있는 걸 볼 수 있었다. 이 super.viewDidLoad()가 부모 클래스인 UIViewController의 viewDidLoad 메서드를 호출하는 것이었다.

    여기서 또 궁금해진것이 부모 클래스는 또 무엇인가 하는 것이었다. 부모 클래스는 슈퍼 클래스 라고도 부르는데 상속 관계에서 기본 기능을 제공하는 클래스라고 한다. 자식 클래스(서브 클래스)는 부모 클래스로부터 기능을 상속 받아 그대로 사용할 수도 있고 필요한 부분을 변경할 수도 있다고 한다.

    이 상속이라는 개념은 프로그래밍에서 재사용성을 높이고 중복을 줄이기 위해 사용된다고 한다. 상속 받은 클래스의 메서드나 프로퍼티를 그대로 사용할 수도 있고, 오버라이딩을 통해 새로운 동작을 정의하기도 한다고 한다. 다음은 내가 이해한 예시이다.

    class Animal {
        var name: String
        
        init(name: String) {
            self.name = name
        }
        
        func makeSound() {
            print("Animal sound")
        }
    }
    
    class Dog: Animal {
        override func makeSound() {
            print("Bark")
        }
    }

    부모 클래스인 Animal이 선언 되었고 저장 프로퍼티인 name과 메서드인 makeSound가 선언 되었다. Dog 클래스는 선언 될 때 콜론 뒷부분을 Animal로 지정하여 Animal 클래스를 상속받았고 override를 통해 makeSound 메서드의 내용을 "animal sound"에서 "Bark"로 변경하였다.

    이 예제를 쓰고 이해하면서 또 다시 든 생각이, Dog 클래스가 Animal 클래스를 상속받을 때 쓴 Dog: Animal 의 형태가 연산 프로퍼티나 메서드에서 타입을 지정하는 것이랑 비슷해보여서 뭐가 다른가 알아보았다. 클래스에서 상속을 선언하는 것과 비슷해 보이지만 메서드에서 하는 것은 매개변수나 반환하는 값의 타입을 지정하는 것이라고 한다. 예를 들어,

    func sum (a : Int, b : Int) -> Int {
    	return a+b
    }

    라는 메서드가 있을 때  -> Int  부분은 이 메서드가 반환하는 return의 값 a+b가 Int 타입임을 나타내는 것이다. 연산 프로퍼티도 마찬가지로 연산 프로퍼티의 결과값, 즉 반환하는 타입이 해당 타입임을 나타내는 것이라고 한다. 참고로 Int나 Double, Float, bool 등과 같은 기본 타입들은 구조체로써, 값 타입이라고 한다. 상속은 클래스간에 이루어지는 것이므로 구조체인 기본 타입들은 당연히 상속이 불가능하다.


    사전 캠프에 참여한게 정말 다행이라고 생각한다. 첫 단추를 잘못끼워 해매던 첫번째 퀘스트를 건너뛰고 나머지 퀘스트들을 오늘 오전 중에 전부 끝내고서 의기양양 해져서는 네비게이션 컨트롤러를 공부한다고 정보를 찾아 읽다가 한 줄 건너 한 줄마다 어려운 키워드들이 등장했다.
    알긴 아는데 잘 모르겠거나 아예 모르겠는 새로운 키워드들이 계속 등장해대니 매번 새로운 정보를 찾느라 한 페이지 똑바로 읽어 나간 것이 없었다. 여태껏 작성한 간단한 코드들조차 그냥 되는대로 작성해 실행해보고 안되는 부분이나 에러난 부분만 수정해서 땜질하듯 코딩했구나 싶었다. 코드를 작성하다 등장하는 키워드들을 하나 놓치지 않고 이해하고 넘어가고 싶다는 생각이 많이 들었고, 이걸 본 캠프가 아닌 사전 캠프인 지금 하고 있어서 정말 다행이라고 생각하고 있다.

    'iOS 앱 개발 부트캠프 > TIL' 카테고리의 다른 글

    TIL 13일차  (2) 2024.09.30
    TIL 12일차  (2) 2024.09.27
    TIL 10일차  (1) 2024.09.25
    TIL 9일차 - 성적 관리 시스템2  (0) 2024.09.24
    TIL 8일차 - 성적 관리 시스템 제작하기  (0) 2024.09.23
Designed by Tistory.