로그 줄

로그 줄

학습 연습 문제

소개

요일이나 나침반 방향, 컨트롤러의 버튼처럼 정해진 몇 가지 상태 중 하나만 가질 수 있는 데이터를 모델링할 때는 Int나 String 같은 기본 타입을 쓰면 문제가 많아요. 정수나 문자열을 쓰면 코드에서 999 같은 잘못된 상태 코드나 "leftt" 같은 오타가 아닌지 끊임없이 확인해야 하고, 사람이 봐도 그 숫자가 무엇을 나타내는지 쉽게 알 수 없어요.

Swift는 이 문제를 열거형으로 해결해요(enum이라고 줄여 부르기도 해요). 열거형은 서로 관련된 이름 있는 값들의 공통 타입을 정의해요. 컴파일러는 가능한 모든 케이스를 미리 알고 있기 때문에, 컴파일 시점에 코드가 정확하고 빠짐없이 작성됐는지 검사할 수 있어요. 열거형은 메서드와 이니셜라이저, 연산 프로퍼티도 가질 수 있어요.

열거형 정의하기

enum 키워드 뒤에 타입 이름을 쓰고, 중괄호 안에 케이스를 나열하면 열거형을 정의할 수 있어요. Swift에서는 관례적으로 타입 이름에는 UpperCamelCase를, 각 케이스에는 lowerCamelCase를 사용해요.

enum NESButton {
    case up
    case down
    case left
    case right
    case a
    case b
    case select
    case start
}

여러 케이스를 쉼표로 구분해서 한 줄에 정의할 수도 있어요.

enum NESButton {
    case up, down, left, right, a, b, select, start
}

열거형 케이스를 가리킬 때는 열거형 타입 이름, 점(.), 케이스 이름 순서로 써요. 타입을 이미 알고 있거나 컴파일러가 추론할 수 있으면 타입 이름을 생략하고 더 짧은 점 문법을 쓸 수 있어요.

var lastPressed = NESButton.up

// Swift infers the type, so you can omit the type name:
lastPressed = .down

let konamiCode: [NESButton] = [.up, .up, .down, .down, .left, .right, .left, .right, .b, .a]

메서드와 이니셜라이저

열거형은 클래스나 구조체처럼 메서드와 사용자 정의 이니셜라이저를 정의할 수 있어요. 열거형 메서드 안에서 self는 현재 케이스 값을 가리켜요.

enum Coin {
    case heads
    case tails

    init(_ value: Int) {
        if value.isMultiple(of: 2) {
            self = .heads
        } else {
            self = .tails
        }
    }

    func flip() -> Coin {
        switch self {
        case .heads: return .tails
        case .tails: return .heads
        }
    }
}

let tails = Coin(13)     // .tails
let heads = Coin(0)      // .heads
let flipped = heads.flip() // .tails

원시값

열거형 케이스에는 원시값이라고 부르는 기본값을 미리 지정할 수 있어요. 한 열거형 안의 모든 원시값은 같은 타입(Int, String, Character 등)이어야 해요.

enum NESButton: Character {
    case up = "⬆️"
    case down = "⬇️"
    case left = "⬅️"
    case right = "➡️"
    case a = "🅰️"
    case b = "🅱️"
    case select = "✅"
    case start = "🚦"
}

print(NESButton.left.rawValue) // Prints "⬅️"

init(rawValue:)를 사용하면 원시값으로 열거형을 초기화할 수 있어요. 유효하지 않은 원시값이 들어올 수도 있기 때문에, 이 이니셜라이저는 옵셔널을 반환해요.

let upButton = NESButton(rawValue: "⬆️") // Optional(NESButton.up)
let invalid = NESButton(rawValue: "🙄")  // nil

Swift는 String과 Int 열거형의 원시값을 자동으로 지정할 수 있어요.

  • String 열거형에서는 각 케이스의 원시값이 기본적으로 케이스 이름이 돼요.
  • Int 열거형에서는 별도로 지정하지 않으면 원시값이 0부터 시작해서 1씩 증가해요.
enum Planet: Int {
    case mercury = 1, venus, earth, mars
}

print(Planet.earth.rawValue) // Prints 3

지침

이 연습 문제에서는 로그 라인을 처리해요.

각 로그 라인은 다음과 같은 형식의 문자열이에요: "[<LVL>]: <MESSAGE>".

로그 레벨은 다음과 같아요:

  • TRC (추적)
  • DBG (디버그)
  • INF (정보)
  • WRN (경고)
  • ERR (오류)
  • FTL (치명적)

필요한 기능을 모두 갖추려면 세 가지 작업을 각각 완료해야 해요.

1. LogLevel 열거형 만들기

위의 로그 레벨에 해당하는 여섯 개의 케이스와, 로그 레벨이 없거나 표준이 아닌 경우를 위한 unknown 케이스를 가진 LogLevel 열거형을 정의해요.

  • trace
  • debug
  • info
  • warning
  • error
  • fatal
  • unknown

2. 로그 레벨 파싱하기

다음으로, 로그 라인의 로그 레벨을 파싱하는 LogLevel 이니셜라이저를 구현해요:

LogLevel("[INF]: File deleted")
// Returns LogLevel.info
LogLevel("Something went wrong!")
// Returns LogLevel.unknown

3. 로그 라인을 짧은 형식으로 변환하기

로그 라인의 로그 레벨은 꽤 길어요. 로그 라인을 저장하는 데 필요한 디스크 공간을 줄이기 위해 짧은 형식을 만들었어요: "<ENCODED_LEVEL>:<MESSAGE>".

인코딩된 로그 레벨은 로그 레벨을 숫자로 단순하게 매핑한 것이에요:

  • trace - 0
  • debug - 1
  • info - 4
  • warning - 5
  • error - 6
  • fatal - 7
  • unknown - 42

짧아진 로그 라인 형식을 String으로 출력하는 shortFormat(message: String) -> String 메서드를 구현해요:

let overflow = LogLevel.error
overflow.shortFormat(message: "Stack overflow")
// Returns "6:Stack overflow"
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Swift Exercism

로그 줄 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Swift 트랙을 개념 35개연습 문제 116개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.