개요
Flutter
로 앱을 개발하다보면, 명령어(CLI - Command Line Interface)로 무언가를 하는 기능이 필요할 때가 있습니다. 예를 들어 pubspec.yaml
파일의 version
을 업데이트할 때, 직접 수정하는 것도 가능하지만, 다음과 같이 명령어로 업데이트를 하도록 만들 수 도 있습니다.
dart run bull:pub_version --version 2.5.7
이번 블로그 포스트에서는 Dart
를 사용하여 명령어(CLI) 툴을 개발하는 방법에 대해서 알아보도록 하겠습니다.
패키지 프로젝트 생성
Dart
를 사용해서 명령어 툴을 만들어 pub.dev
에 배포하기 위해 Dart
패키지 프로젝트를 생성할 필요가 있습니다. pub.dev
에 배포할 필요가 없다면, 기존 프로젝트에서 명령어 툴을 제작하면 되므로 이 부분은 건너뛰어도 됩니다.
그럼 다음 명령어를 실행하여 Dart
패키지를 생성합니다.
# dart create --template=package [PACKAGE_NAME]
dart create --template=package cli_example
프로젝트가 생성이 완료되면 다음과 같이 폴더와 파일들이 생성되는 것을 확인할 수 있습니다.
cli_example
├── CHANGELOG.md
├── README.md
├── analysis_options.yaml
├── example
│ └── cli_example_example.dart
├── lib
│ ├── cli_example.dart
│ └── src
│ └── cli_example_base.dart
├── pubspec.lock
├── pubspec.yaml
└── test
└── cli_example_test.dart
args 패키지 설치
args
패키지는 Dart
로 명령어(CLI)를 제작하는 것을 도와주는 패키지입니다.
args
패키지를 사용하기 위해 다음 명령어를 실행하여 args
패키지를 설치합니다.
dart pub add args
Flutter
프로젝트라면 다음 명령어를 실행하여 args
를 설치합니다.
flutter pub add args
명령어 구현
Dart
로 명령어(CLI)를 만들기 위해서는 args
패키지의 Command
클래스를 사용할 필요가 있습니다. lib/src/cli_example_base.dart
파일을 열고 다음과 같이 수정합니다.
import 'package:args/command_runner.dart';
class Echo extends Command {
@override
final name = 'echo';
@override
final description = 'Echo option';
Echo() {
argParser.addOption('message', help: 'A message to echo');
}
@override
void run() {
String? message = argResults?['message'];
// ignore: avoid_print
print('Message: $message');
}
}
Command
클래스를 상속받으면 3개의 멤버를 오버라이드(Override)해야 합니다. name
은 해당 명령어의 이름이고, description
은 명령어의 설명문입니다. 그리고 run()
함수는 해당 명령어를 구현하는 부분입니다.
Command
클래스에서는 기본적으로 argParse
와 argResults
변수를 사용할 수 있습니다. 생성자(Echo()
)에서 argParser
의 addOption
을 통해 해당 명령어의 필요한 옵션명(message
)과 설명문(help
)을 추가하였습니다.
그리고 명령어의 구현 부분인 run()
함수에서 명령어 옵션에 설정된 값을 argResults?['message']
을 통해 할당받았으며, print
를 사용하여 해당 내용을 출력하도록 하였습니다.
실제 명령어를 구현할 때에는 argParser
에 다양한 옵션을 추가할 것이고, run()
함수에서 옵션에 따른 다양한 기능을 구현하게 될 것입니다.
명령어 추가
lib
폴더 하위에 구현한 내용은, 패키지를 설치하고 Dart
파일안에서 패키지의 내용을 불러와 사용할 수 있습니다. 즉, 우리가 만든 Echo
는 명령어로써 사용이 불가능하고, *.dart
파일안에서만 사용이 가능합니다.
우리가 만든 Echo
명령어를 *.dart
파일이 아닌 명령어로 사용할 수 있게 하려면, bin
폴더를 만들고 Echo
명령어를 실행하는 파일을 만들어야 합니다.
그럼 bin/cli_example.dart
파일을 생성하고 다음과 같이 수정합니다.
import 'package:args/command_runner.dart';
import 'package:cli_example/cli_example.dart';
void main(List<String> arguments) {
CommandRunner(
"cli_example",
"Dart CLI example",
)
..addCommand(Echo())
..run(arguments);
}
사용자가 명령어를 실행하면 main
함수가 실행되고 arguments
에 사용자가 입력한 옵션과 값이 설정됩니다.
이 파일에는 args
패키지를 사용하여 우리가 만든 Echo
명령어를 등록(addCommand
)하고, 사용자가 입력한 값(arguments
)와 함께 실행(run
)하도록 구성되었습니다.
이렇게 만든 파일은 다음과 같은 명령어로 실행이 가능합니다.
dart run cli_example echo --message="test"
그럼 다음과 같은 결과를 얻을 수 있습니다.
Message: test
우리는 args
의 Command
클래스를 사용하였기 때문에, 다음 명령어과 같은 명령어를 사용할 수 있습니다.
dart run cli_example -h
그럼 다음과 같이 우리가 작성한 description
과 help
메시지가 잘 표시되는 것을 확인할 수 있습니다.
Dart CLI example
Usage: cli_example <command> [arguments]
Global options:
-h, --help Print this usage information.
Available commands:
echo Echo option
Run "cli_example help <command>" for more information about a command.
물론, 다음과 같이 Echo
명령어에 대한 help
메시지도 출력이 가능합니다.
dart run cli_example echo -h
그럼 다음과 같은 결과를 확인할 수 있습니다.
Echo option
Usage: cli_example echo [arguments]
-h, --help Print this usage information.
--message A message to echo
예제 작성
우리가 만든 명령어 패키지를 pub.dev
에 배포하기 위해서는, Echo
명령어를 사용하는 예제를 만들어야 합니다. example/cli_example_example.dart
파일을 열고 다음과 같이 수정합니다.
import 'package:args/command_runner.dart';
import 'package:cli_example/cli_example.dart';
void main() async {
final cmd = CommandRunner(
"cli_example",
"Dart CLI example",
)..addCommand(Echo());
await cmd.run(['echo', '--message', 'test message']);
}
우리는 현재 명령어 툴을 만들고 있기 때문에 사실 예제를 작성할 필요는 없습니다. 이 파일은 반대로 사용자에게 혼란을 줄 수 있으므로 제거하는 것을 추천합니다.
테스트 코드 작성
명령어를 실행하였을 때, 잘 동작하는지 확인하기 위한 테스트 코드를 작성할 수 있습니다. 테스트 코드를 작성하기 위해 test/cli_example_test.dart
파일을 만들고 다음과 같이 수정합니다.
import 'package:args/command_runner.dart';
import 'package:cli_example/cli_example.dart';
import 'package:run_with_print/run_with_print.dart';
import 'package:test/test.dart';
void main() {
final runner = CommandRunner('test', 'test')..addCommand(Echo());
test('Echo test message', () async {
await runWithPrint((logs) async {
await runner.run(['echo', '--message', 'test message']);
expect(logs, ['Message: test message']);
});
});
}
우리가 만든 Echo
명령어는 print
를 사용하여 결과를 출력합니다. 이를 확인하기 위해서 다음 명령어를 실행하여 run_with_print
패키지를 설치하였습니다.
dart pub add --dev run_with_print
설치가 완료되었다면, run_with_print
의 runWithPrint
함수를 통해 print
로 출력되는 내용을 체크하였습니다. run_with_print
패키지에 대해서는 다음 링크를 참고하시기 바랍니다.
이제 다음 명령어를 실행하여 테스트 코드를 실행합니다.
dart test test/cli_example_test.dart
그럼 다음과 같이 테스트가 잘 통과하는 것을 확인할 수 있습니다.
00:00 +1: All tests passed!
명령어 테스트
이제 이렇게 만든 명령어가 제대로 동작하는지 확인해 봅시다. 우선 명령어를 구현한 코드를 GitHub
에 올립니다. 그리고 다음 명령어를 실행하여 명령어를 활성화 시킵니다.
dart pub global activate --source git https://github.com/dev-yakuza/cli_example
이렇게 GitHub
에 올린 코드는 다음과 같이 실행할 수 있습니다.
dart pub global run cli_example echo --message="test messsage"
그럼 다음과 같이 명령어가 잘 실행되는 것을 확인할 수 있습니다.
Message: test messsage
빌드
이렇게 만든 명령어는 독립적인 실행 파일(Standalone executable)로 만들 수 있습니다. 다음 명령어를 실행하여 명령어를 독립적인 실행 파일로 만듭니다.
dart compile exe bin/cli_example.dart
그럼 bin
폴더에 cli_example.exe
파일이 생성된 것을 확인할 수 있습니다. 이렇게 생성된 명령어 파일은 다음과 같이 실행할 수 있습니다.
./bin/cli_example.exe echo --message="test message"
명령어 확장자가 exe
이지만, 이 명령어 파일은 윈도우뿐만 아니라, macOS
그리고 Linux
를 지원합니다.
pub.dev에 배포
이렇게 만든 명령어 패키지를 pub.dev
에 배포하여 사용할 수 있습니다. pub.dev
에 패키지를 배포하는 방법에 대해서는 다음 링크를 참고하시기 바랍니다.
공식 문서 참고
다음은 Dart
로 명령어를 만들때, 도움이 되는 공식 문서입니다.
Get started: Command-line and server apps
: https://dart.dev/tutorials/server/get-startedWrite command-line apps
: https://dart.dev/tutorials/server/cmdlineargs package
: https://pub.dev/documentation/args/latest/index.html
완료
이것으로 Dart
를 사용하여 명령어(CLI) 툴을 만드는 방법에 대해서 알아보았습니다. Flutter
로 앱을 개발하거나 Dart
로 프로젝트를 개발할 때, 특정 명령어를 만들어야 할 때가 있습니다. 이때, 이번 포스트를 참고하여 명령어(CLI) 툴을 만들어 보시기 바랍니다.
제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!
앱 홍보
Deku
가 개발한 앱을 한번 사용해보세요.Deku
가 개발한 앱은 Flutter로 개발되었습니다.관심있으신 분들은 앱을 다운로드하여 사용해 주시면 정말 감사하겠습니다.