Cobra 프레임워크 개요
Cobra는 Go 언어 기반 명령줄 도구 개발을 위한 포괄적 프레임워크다. 쿠버네티스, 도커 CLI, 휴고 등 대규모 오픈소스 프로트에서 검증된 이 라이브러리는 명령 구조 설계부터 쉘 자동완성, 문서 생성까지 CLI 개발 전 과정을 지원한다.
핵심 구성 요소
Cobra의 아키텍처는 세 가지 추상화 계층으로 이루어진다.
- 명령 객체(Command): 실행 가능한 작업 단위
- 위치 인자(Argument): 명령에 전달되는 필수/선택적 값
- 옵션 플래그(Flag): 명령 동작을 수정하는 키-값 쌍
프로젝트 초기 설정
go install github.com/spf13/cobra-cli@latest
cobra-cli init mytool
cd mytool && go mod tidy
명령 계층 구현
하위 명령을 포함하는 복합 구조를 구성하는 방법을 살펴본다.
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var deployCmd = &cobra.Command{
Use: "deploy [환경명]",
Short: "애플리케이션 배포 작업",
Long: `지정된 환경(dev/staging/prod)에 애플리케이션을 배포합니다.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, params []string) error {
targetEnv := params[0]
skipVerify, _ := cmd.Flags().GetBool("skip-validation")
fmt.Printf("🚀 %s 환경 배포 시작\n", targetEnv)
if !skipVerify {
fmt.Println("✓ 사전 검증 수행 중...")
}
return nil
},
}
func init() {
rootCmd.AddCommand(deployCmd)
deployCmd.Flags().BoolP("skip-validation", "s", false, "배포 전 검증 단계 생략")
}
지속적 플래그와 설정 연동
전역 옵션과 Viper 설정 라이브러리를 통합하여 환경별 설정을 관리할 수 있다.
var cfgPath string
var rootCmd = &cobra.Command{
Use: "mytool",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if cfgPath != "" {
viper.SetConfigFile(cfgPath)
return viper.ReadInConfig()
}
return nil
},
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgPath, "config", "", "설정 파일 경로")
}
쉘 자동완성 생성
사용자 경험 향상을 위해 다양한 쉘에 대한 탭 완성 스크립트를 생성한다.
var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "쉘 자동완성 스크립트 생성",
Args: cobra.ExactValidArgs(1),
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Run: func(cmd *cobra.Command, shellType []string) {
switch shellType[0] {
case "bash":
cmd.Root().GenBashCompletion(cmd.OutOrStdout())
case "zsh":
cmd.Root().GenZshCompletion(cmd.OutOrStdout())
case "fish":
cmd.Root().GenFishCompletion(cmd.OutOrStdout(), true)
case "powershell":
cmd.Root().GenPowerShellCompletionWithDesc(cmd.OutOrStdout())
}
},
}
동적 문서 생성
코드 기반으로 마크다운 문서와 man 페이지를 자동 생성하여 문서-코드 동기화 문제를 해결한다.
import "github.com/spf13/cobra/doc"
func generateDocs() error {
cmd := &cobra.Command{Use: "mytool"}
// 마크다운 문서 디렉터리 생성
if err := doc.GenMarkdownTree(cmd, "./docs/md"); err != nil {
return err
}
// man 페이지 생성 (섹션 1: 사용자 명령)
header := &doc.GenManHeader{
Title: "MYTOOL",
Section: "1",
Source: "MyTool 프로젝트",
}
return doc.GenManTree(cmd, header, "./docs/man")
}
고급 패턴: 미들웨어 체인
명 실행 전후에 공통 처리 로직을 주입하는 패턴을 적용할 수 있다.
func withTelemetry(next cobra.RunEFunc) cobra.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
start := time.Now()
traceID := uuid.New().String()
cmd.SetContext(context.WithValue(cmd.Context(), "trace.id", traceID))
err := next(cmd, args)
duration := time.Since(start)
recordMetrics(cmd.Name(), duration, err)
return err
}
}
// 사용 예시
var migrateCmd = &cobra.Command{
Use: "migrate",
RunE: withTelemetry(executeMigration),
}
테스트 가능한 구조 설계
의존성 주입을 통해 명령 로직을 격리하여 단위 테스트를 용이하게 한다.
type DeployService interface {
Validate(ctx context.Context, env string) error
Execute(ctx context.Context, env string, opts DeployOptions) error
}
func NewDeployCommand(svc DeployService) *cobra.Command {
return &cobra.Command{
Use: "deploy",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
if err := svc.Validate(ctx, args[0]); err != nil {
return fmt.Errorf("검증 실패: %w", err)
}
return svc.Execute(ctx, args[0], parseOptions(cmd))
},
}
}