Raku의 MAIN — 명령줄 인터페이스를 만드는 특수 서브

Raku의 MAIN — 명령줄 인터페이스를 만드는 특수 서브

스크립트에 명령줄 인자를 받는 인터페이스를 만들 때, Raku는 MAIN이라는 특별한 이름의 서브를 쓰면 아주 편리해져요. MAIN을 선언하면 인자 파싱과 사용법(usage) 출력까지 언어 차원에서 알아서 처리해 줍니다.

출처: Raku 공식 문서 — routine MAIN

명령줄 인터페이스에서

특수 이름 MAIN을 가진 서브는 관련 진입 패이저(BEGIN, CHECK, INIT, PRE, ENTER)가 모두 실행되고 스크립트의 메인라인이 실행된 뒤에 호출돼요. MAIN 서브가 없다고 오류가 나지는 않아요. 그런 경우엔 스크립트가 메인라인에서 인자 파싱 같은 일을 직접 해야 하죠.

MAIN 서브에서 정상적으로 빠져나오면 종료 코드가 0이 되어 성공을 뜻해요. MAIN 서브의 반환값은 무시되고요. MAIN 서브 안에서 처리되지 않은 예외가 던져지면 종료 코드가 1이 됩니다. 만약 MAIN으로의 디스패치(dispatch)가 실패하면 사용법 메시지가 STDERR에 출력되고 종료 코드는 2가 돼요.

명령줄 매개변수는 동적 변수 @*ARGS에 들어 있어서, MAIN 유닛이 호출되기 전에 메인라인에서 그 값을 바꿔 줄 수도 있어요.

또 중요한 건 MAIN 서브(멀티의 후보들)의 시그니처가 표준 멀티 디스패치 규칙에 따라 어느 후보가 실제로 호출될지를 결정한다는 점이에요. 시그니처가 곧 명령줄 인터페이스의 명세가 되는 셈이죠.

간단한 예를 볼까요.

# inside file 'hello.raku'
sub MAIN($name) {
    say "Hello $name, how are you?"
}

인자 없이 그 스크립트를 실행하면 다음과 같은 사용법 메시지가 나와요.

$ raku hello.raku
Usage:
  hello.raku <name>

$name이 필수 인자라서 인자를 안 주면 어떻게 호출해야 하는지 안내를 보여 주는 거죠. 반대로 매개변수에 기본값을 주면, 인자를 주든 안 주든 항상 정상적으로 동작해요.

# inside file 'hello.raku'
sub MAIN($name = 'bashful') {
    say "Hello $name, how are you?"
}
$ raku hello.raku
Hello bashful, how are you?
$ raku hello.raku Liz
Hello Liz, how are you?

기본값을 준 덕분에 인자 없이 실행하면 bashful이, Liz를 주면 Liz가 들어가요.

같은 일을 multi로도 만들 수 있어요.

# inside file 'hello.raku'
multi MAIN()      { say "Hello bashful, how are you?" }
multi MAIN($name) { say "Hello $name, how are you?"   }

호출되는 시그니처에 따라 다른 동작을 하니, 위 예시들과 같은 출력을 만들어 냅니다. 두 방식 중 어느 쪽을 쓸지는 전적으로 선택하기 나름이에요.

인자의 개수가 정해지지 않은 상황을 다룰 때는 슬러피 매개변수를 이용하면 돼요.

# inside file 'hello-all.raku'
sub MAIN(*@all) { for @all -> $name { say "Hello, " ~ $name } }
$ raku hello-all.raku peter paul mary
Hello, peter
Hello, paul
Hello, mary

*@all이 받은 인자들을 하나씩 꺼내 인사말을 출력하고 있죠.

Functions에서

sub MAIN을 선언하는 것만으로도 스크립트에 명령줄 인터페이스를 쉽게 지정할 수 있어요. 인자 검증, 사용법 출력, 종료 코드 처리까지 Raku가 함께 도와 주니, 직접 파싱 코드를 짤 필요가 훨씬 줄어듭니다.