🔥 Swift ArgumentParser 소개

1025자
14분

ArgumentParser는 Swift 개발자들이 명령줄 인터페이스 도구를 쉽고 안전하게 만들도록 도와줍니다. 간단한 Swift 타입을 선언하는 것만으로도 강력한 명령줄 도구를 만들 수 있죠. 먼저 명령줄에서 수집해야 할 정보를 정의하는 타입을 선언하는 것부터 시작합니다. 그리고 각 저장 프로퍼티에 ArgumentParser의 프로퍼티 래퍼 중 하나를 사용하여 표시하고, ParsableCommand 프로토콜을 채택한 후, run() 메서드에서 명령의 로직을 구현하면 강력한 명령줄 도구를 만들 수 있습니다. 만약 asyncrun 메서드를 사용하고 싶다면 AsyncParsableCommand 프로토콜을 대신 채택하세요.

import ArgumentParser
 
@main
struct Repeat: ParsableCommand {
    @Argument(help: "The phrase to repeat.")
    var phrase: String
 
    @Option(help: "The number of times to repeat 'phrase'.")
    var count: Int? = nil
 
    mutating func run() throws {
        let repeatCount = count ?? 2
        for _ in 0..<repeatCount {
            print(phrase)
        }
    }
}
 
swift

사용자가 위에서 선언한 명령을 실행하면, ArgumentParser 라이브러리가 명령줄 인자를 파싱하고, 명령 타입의 인스턴스를 생성한 다음, run() 메서드를 호출하거나 유용한 메시지와 함께 종료합니다.

lecture image

이렇게 ArgumentParser를 사용하면 복잡한 명령줄 인터페이스를 가진 도구도 쉽고 직관적으로 개발할 수 있습니다.

명령과 하위 명령 정의하기

복잡한 명령줄 도구를 개발할 때는 하위 명령을 사용하여 기능을 체계적으로 분류하는 게 좋습니다. ArgumentParser를 사용하면 이런 하위 명령 구조를 쉽게 만들 수 있습니다. 최상위 명령 타입에 ParsableCommand 프로토콜을 채택하고, 하위 명령 타입에도 같은 프로토콜을 채택하면 ArgumentParser가 자동으로 하위 명령 구조를 인식하고 적절한 명령을 실행할 수 있습니다.

뿐만 아니라 각 명령에 대한 도움말 메시지도 쉽게 정의할 수 있습니다. 명령 타입에 configuration 속성을 추가하고 CommandConfiguration 인스턴스를 할당하면 도움말 메시지를 구성할 수 있습니다. 이 인스턴스를 통해 명령의 추상, 상세 설명, 사용법 문자열 등을 지정할 수 있죠. 도움말 메시지를 호출하는 플래그도 원하는 대로 변경할 수 있습니다.

이렇게 ArgumentParser를 활용하면 복잡한 명령줄 도구도 하위 명령 구조로 깔끔하게 나눌 수 있고, 각 명령에 대해 상세한 도움말을 제공할 수 있습니다. 이제 실제로 하위 명령을 가진 도구를 만들어 보면서 ArgumentParser의 진가를 확인해 봅시다!

Argument, Option, Flag 선언하기

명령줄 도구의 인터페이스, 즉 사용자로부터 입력받을 데이터를 선언하는 건 무척 중요합니다. ArgumentParser는 이를 위해 @Argument, @Option, @Flag 등의 property wrapper를 제공합니다. @Argument는 위치에 기반한 인자를, @Option은 이름을 가진 옵션을, 그리고 @Flag는 On/Off 스위치 같은 역할을 하는 플래그를 나타냅니다.

각 property wrapper는 다양한 초기화 옵션을 제공하므로, 입력값의 필수 여부, 기본값, 유효한 값의 범위 등을 유연하게 지정할 수 있습니다. 뿐만 아니라 @OptionGroup property wrapper를 사용하면 연관된 여러 옵션을 하나의 그룹으로 묶을 수도 있습니다. 이런 식으로 좀 더 구조화된 명령줄 인터페이스를 설계할 수 있죠.

물론 이렇게 설계한 인터페이스를 실제로 파싱할 수 있어야 하는데, 이를 위해 ParsableArguments 프로토콜을 사용합니다. 이 프로토콜을 채택한 타입은 명령줄 인자에서 해당 타입의 인스턴스를 생성할 수 있습니다.

이렇게 다양한 property wrapper와 프로토콜을 조합하면 명령줄 도구에 필요한 거의 모든 종류의 입력을 처리할 수 있습니다. 자, 이제 우리도 실전에서 이들을 활용해 보면서 강력하면서도 사용하기 쉬운 명령줄 인터페이스를 만들어 봅시다!

맞춤형 도움말 제공하기

사용자를 배려하는 좋은 명령줄 도구라면 각 인자와 옵션, 플래그에 대한 상세한 도움말을 제공해야 합니다. ArgumentParser를 사용하면 이런 도움말을 손쉽게 작성할 수 있습니다. @Argument, @Option, @Flag property wrapper의 help 매개변수에 도움말 메시지를 지정하면 ArgumentParser가 이를 토대로 자동으로 도움말을 생성합니다.

이때 ArgumentHelp 타입을 사용하면 좀 더 확장된 형태의 도움말을 제공할 수 있습니다. 이 타입을 통해 도움말 메시지뿐만 아니라 사용 가능한 값의 예시나 토론, 가시성 수준 등을 추가로 설정할 수 있죠. 특히 ArgumentVisibility 열거형을 사용하면 기본 도움말에는 표시하지 않고 --verbose 옵션으로 상세 도움말을 요청했을 때만 표시되는 항목들을 설정할 수 있습니다.

이런 식으로 맞춤형 도움말을 제공하면 사용자가 명령줄 도구를 좀 더 쉽게 이해하고 활용할 수 있습니다. 뿐만 아니라 NameSpecification 타입을 사용하면 property의 이름과 실제 인자 레이블을 다르게 지정할 수도 있습니다. 이를 활용하면 코드의 가독성은 유지하면서도 사용자에게는 좀 더 직관적인 인터페이스를 제공할 수 있겠죠.

맞춤형 타입 활용하기

명령줄 인자로 받은 값을 항상 기본 타입으로만 다루는 건 아닙니다. 때로는 우리만의 맞춤형 타입을 사용하면 코드를 더 명확하고 안전하게 작성할 수 있죠. ArgumentParser는 이런 맞춤형 타입도 손쉽게 다룰 수 있도록 ExpressibleByArgument 프로토콜을 제공합니다.

이 프로토콜을 채택한 타입은 명령줄 인자에서 직접 인스턴스를 생성할 수 있습니다. 예를 들어 날짜를 나타내는 Date 타입에 이 프로토콜을 채택하면, 사용자가 문자열로 입력한 날짜를 파싱해서 Date 인스턴스로 받을 수 있습니다. 이렇게 하면 우리 코드에선 항상 Date 타입을 다루게 되므로 훨씬 안전하고 명확해집니다.

이와 유사하게 EnumerableFlag 프로토콜을 사용하면 연관 값을 가진 열거형을 플래그로 사용할 수 있습니다. 이 경우 사용자는 해당 플래그를 여러 번 지정할 수 있고, 우리 코드에선 열거형 케이스의 배열을 받을 수 있습니다. 옵션 집합을 나타내는데 아주 유용하죠.

이렇듯 맞춤형 타입을 적극 활용하면 명령줄 도구의 인터페이스를 한층 더 세련되게 다듬을 수 있습니다. 뿐만 아니라 강력한 타입 시스템의 도움을 받아 좀 더 안전하고 직관적인 코드를 작성할 수 있습니다. 자, 이제 우리도 우리만의 멋진 타입을 선언하고 ArgumentParser와 함께 사용해 봅시다!

유효성 검사와 오류 처리

사용자로부터 입력받은 값이 항상 우리가 원하는 형식을 충족하진 않습니다. 따라서 좋은 명령줄 도구라면 입력값의 유효성을 검사하고, 잘못된 입력에 대해선 알기 쉬운 오류 메시지를 표시해야 합니다. ArgumentParser는 이를 위한 다양한 기능을 제공합니다.

먼저 @Argument@Optionvalidation 매개변수를 사용하면 입력값이 만족해야 할 조건을 지정할 수 있습니다. 예를 들어 validation: { $0 >= 0 }라고 쓰면 해당 인자가 0 이상의 값이어야 함을 나타냅니다. 여기서 클로저를 사용하면 좀 더 복잡한 조건도 거뜬히 표현할 수 있습니다.

만약 유효성 검사에 실패하면 ValidationError를 던집니다. 우리는 이 오류를 catch해서 적절한 오류 메시지를 표시할 수 있습니다. ValidationError 타입은 message 속성을 통해 표준 오류 메시지를 제공하지만, 물론 우리가 직접 오류 메시지를 지정할 수도 있습니다.

이 외에도 CleanExitExitCode 타입을 사용하면 오류 상황에서 프로그램을 깔끔하게 종료할 수 있습니다. 특히 ExitCode 타입은 프로그램의 종료 코드를 나타내므로, 다른 명령줄 도구와의 연계에 유용하게 사용할 수 있습니다.

이렇듯 ArgumentParser가 제공하는 다양한 오류 처리 메커니즘을 잘 활용하면, 사용자에게 명쾌한 피드백을 제공하는 강건한 명령줄 도구를 개발할 수 있습니다.

셸 자동 완성 스크립트

익숙한 셸의 자동 완성 기능을 우리 명령줄 도구에서도 사용할 수 있다면 얼마나 좋을까요? 긴 옵션 이름을 일일이 타이핑하지 않아도 되고, 가능한 인자 값들을 쉽게 확인할 수 있을 테니까요. 놀랍게도 ArgumentParser를 사용하면 이런 자동 완성 스크립트를 무척 쉽게 만들 수 있습니다.

CompletionCommand 타입을 사용하면 Bash, Zsh, Fish 등 다양한 셸에 대한 자동 완성 스크립트를 자동으로 생성할 수 있습니다. 사용자는 이렇게 생성된 스크립트를 자신의 셸 설정에 추가하기만 하면 우리 명령줄 도구에서도 자동 완성 기능을 사용할 수 있습니다. 또한, 셸의 종류와 설치 방법을 안내하는 메시지도 쉽게 표시할 수 있습니다.

뿐만 아니라 CompletionKind 타입을 활용하면 자동 완성 동작을 좀 더 세밀하게 제어할 수 있습니다. 해당 인자나 옵션에 사용할 수 있는 값의 목록을 제공하거나, 커스텀 자동 완성 스크립트를 지정하는 것도 가능합니다. 이를 통해 우리 도구만의 독특한 자동 완성 경험을 제공할 수 있습니다.

자동 완성은 사용자의 생산성과 만족도를 크게 향상시키는 강력한 기능입니다. ArgumentParser와 함께라면 이런 멋진 기능을 아주 손쉽게 우리 명령줄 도구에 추가할 수 있습니다.