[내일배움캠프] 260114 TIL - 열거형

Bambu·2026년 1월 14일

내배캠 TIL

목록 보기
19/52

1. 모닝 스터디 - 알고리즘

1) 프로그래머스 - 치킨 쿠폰
2) 프로그래머스 - 리스트 자르기
3) 프로그래머스 - 배열 조각하기

2. 프로그래밍 심화 주차 - 야구 게임 만들기

1) 열거형 활용하기

코드 내 등장하는 여러 게임 메세지들을 하나의 열거형으로 분리하고 있다.
대부분은 단순히 문자열을 원시값으로 정의하면 됐었는데, 힌트를 주는 부분에서 난관에 부딪혔다.

        // 힌트에 따른 분기 처리
        if hint.strike == 3 {
            isCorrect = true
            print("🎉 정답입니다!\n")
        } else if hint.strike == 0 && hint.ball == 0 {
            print("❌ Nothing\n")
        } else {
            print("🎯 \(hint.strike) 스트라이크 ⚾️ \(hint.ball) 볼 입니다!\n")
        }

기존의 힌트는 (strike: Int, ball: Int) 타입의 hint 변수에 접근하여 문자열을 출력하고 있었다.

하지만 게임 메세지는 문자열을 원시값으로 채택한 열거형으로 구현중이었다.

enum SystemMessage: String {
    case welcome = """
            환영합니다! 원하시는 번호를 입력해주세요. (: 1)
            1. 게임 시작하기  2. 게임 기록 보기  3. 종료하기
            """
    
    case gameStart = "\n❮ 게임을 시작합니다. ❯"
    
    ...
    
}

하지만 이 경우, strikeball에 따른 메세지를 출력하지 못한다.

케이스 별로 다른 연관값을 주어 해결하려 했지만, 그 경우 외부에서 열거형 객체를 생성해주어야한다는 단점이 있었다.

enum SystemMessage {
	case welcome (String)
    case hint (strike: Int, ball: Int)
}

// 위 처럼 정의할 경우 따로 객체를 생성해야함
let message = SystemMessage.welcome("환영합니다!")

이 방법은 번거롭다고 생각했다.

그래서 별도의 Hint 열거형을 생성하여 해결해보려 했다.

enum Hint {
	case hint(strike: Int, ball: Int)
    
    func toString() -> String {
    	switch self {
        case .hint(let s, let b):
        	return "\(s) 스트라이크 \(b) 볼 입니다!\n"
        }
    }
}

이렇게 하면 가능은 한데...

        // 힌트에 따른 분기 처리
        if hint.strike == 3 {
            isCorrect = true
            print("🎉 정답입니다!\n")
        } else if hint.strike == 0 && hint.ball == 0 {
            print("❌ Nothing\n")
        } else {
            let m = Hint.hint(strike: hint.strike, ball: hint.ball)
            print(m.toSring())
        }

이 경우에는 그럼 Hint.hint.toString 내에서 분기 처리해서 케이스별 메세지를 내보내는 게 낫지 않나? 라는 고민이 생겼다.

그런데! 그렇게 하면 '정답을 비교한다'라는 기능을 checkAnswer 함수가 아니라 메세지 표시 열거형에서 하지 않나?라는 생각을...

→ 지금 생각해보니 메세지만 처리하는 거니 괜찮지 않을까 싶다.

그리고 메세지를 생성하기 위한 코드가 한 줄이 더 들어간다는 게... 조금 마음에 걸려 방법이 없을지 튜터님께 여쭤보았다.

그리고 알려주신 세 가지 방법!

가. 확장과 프로토콜 활용하기

SystemMessage 열거형을 확장하여 CustomStringConvertible 프로토콜을 채택하는 방법

enum SystemMessage {
	case welcome
    case hint(strike: Int, ball: Int)
}

extension SystemMessage: CustomStringConvertible {
	var description: String {
    switch self {
    case .welcome: 
    	return "환영합니다!"
    case .hint(let s, let b):
    	return s == 3 ? "🎉 정답입니다! 🎉"
            : s == 0 && b == 0 ? "❌ Nothing\n" :
            "🎯 \(s) 스트라이크 ⚾️ \(b) 볼 입니다!\n"
}

CustomStringConvertible 프로토콜은 description 변수를 정의하여 케이스 별로 문자열 값을 사용가능하도록 한다.

여기서 정의해둔 문자열 description 값은 String Interpolation으로 열거형을 사용할 때 description에 접근할 필요 없이 자동으로 사용된다고 한다!

print("\(SystemMessage.welcome)") // "환영합니다!" 출력

나. 타입 변수 활용하기

enum 혹은 struct 타입에 타입 변수와 타입 메서드를 활용하는 방법

enum SystemMessage {
	static var welcome = "환영합니다!"
    static var hint = ""
    
    static func getHint(for strike: Int, _ ball: Int) {
            hint = strike == 3 ? "🎉 정답입니다! 🎉"
        : strike == 0 && ball == 0 ? "❌ Nothing\n" :
        "🎯 \(strike) 스트라이크 ⚾️ \(ball) 볼 입니다!\n"
	}
}

→ 열거형을 선언하고 확장해줄 필요 없이 한방에 선언 가능!

💡 보통 열거형으로 사용하는 이유?
위 예시와 같은 용도로 사용할 경우, 따로 systemMessage라는 객체를 만들 필요가 없음. 그러니 인스턴스가 만들어질 수 있는 구조체보다 열거형을 사용!
-> 혹시나 인스턴스가 만들어진다거나 하는 걸 방지하는 목적이 아닐지?

3. 수준별 분반

1) ARC

unowned와 weak의 차이

weak를 사용하면 HeapObject 내부에 side...Table이 생겨서 메모리를 더 사용
→ side 무슨 테이블은 64byte, unowned를 처리하는 테이블은 8byte라서 용량에 차이가 있음!

하지만! 둘 중 뭘 써야할지 모르겠다면 weak를 쓸 것!
unowned는 참조할 녀석이 없을 때 nil값을 반환해주지 않고 런타임 에러를 일으킴
⇒ 메모리 조금 더 잡아먹는게 앱의 안전성을 낮추는 것보다 낫다!!

2) Memory Leak

XCode는 모든 메모리 누수를 잡아주지는 못함
→ Swift의 ARC는 자동으로 메모리를 관리하는 무언가가 아니라, RC를 증가시키고 감소시키는 함수를 코드별로 넣어주는 것뿐임.
⇒ 그래서 순환 참조 같은 경우는 컴파일러가 알아내기 힘듦

debugSwift를 통해 메모리 누수가 일어나는 지점을 알아내고, XCode의 Memory Graph Debugger를 통해 원인을 알아내고 수정
(실습 진행)

profile
안녕하세요, iOS 개발을 공부하고 있는 Bambu입니다. (프로필: Swifticons)

0개의 댓글