선언형(Declarative) vs. 스크립트형(Scripted): Jenkins 파이프라인 구문 선택하기

Jenkins CI/CD의 강력한 기능을 잠금 해제하고 두 가지 주요 파이프라인 구문인 선언형과 스크립트형을 마스터하세요. 이 포괄적인 가이드는 핵심적인 차이점, 구문 구조, 장점 및 한계점을 깊이 탐구합니다. 간단하고 읽기 쉬운 선언형을 간단한 워크플로우에 활용해야 할 때, 또는 복잡하고 동적인 자동화를 위해 스크립트형의 모든 강력한 기능과 유연성을 활용해야 할 때를 알아보십시오. 실용적인 예제와 측면 비교를 통해, 이 문서는 귀하의 특정 CI/CD 요구 사항에 가장 적합한 파이프라인 접근 방식을 자신 있게 선택하고 개발 및 배포 프로세스를 간소화하는 데 필요한 통찰력을 제공합니다.

35 조회수

선언형 대 스크립트형: 젠킨스 파이프라인 문법 선택하기

선도적인 오픈소스 자동화 서버인 젠킨스는 전 세계 수많은 지속적 통합 및 지속적 배포(CI/CD) 파이프라인의 핵심입니다. 본질적으로 젠킨스 파이프라인은 딜리버리 파이프라인을 "코드로" 모델링하기 위한 강력하고 확장 가능한 도구 모음을 제공합니다. 이 접근 방식을 통해 개발 팀은 전체 CI/CD 워크플로우를 소스 제어 리포지토리의 애플리케이션 코드와 함께 존재하는 Jenkinsfile에 정의할 수 있습니다.

코드형 파이프라인(Pipeline as Code) 개념은 버전 관리, 반복성, 가시성과 같은 엄청난 이점을 제공하지만, 젠킨스는 이러한 파이프라인을 정의하기 위한 두 가지 고유한 문법인 선언형(Declarative)스크립트형(Scripted)을 제공합니다. 이 두 문법 간의 근본적인 차이점을 이해하는 것은 복잡한 CI/CD 워크플로우를 효과적으로 조율하고, 유지보수성을 최적화하며, 젠킨스의 모든 기능을 활용하는 데 중요합니다. 이 글에서는 각 문법을 자세히 살펴보고, 그 특성, 장점, 한계를 탐구하며, 팀과 프로젝트 요구 사항에 가장 적합한 접근 방식을 결정하는 데 도움을 줄 것입니다.

젠킨스 파이프라인 이해하기

문법에 대해 자세히 알아보기 전에, 젠킨스 파이프라인이 무엇인지 간략히 다시 한번 살펴보겠습니다. 파이프라인은 지속적 딜리버리 파이프라인을 젠킨스에 구현하고 통합하는 것을 지원하는 플러그인 모음입니다. 본질적으로 코드 커밋부터 배포에 이르기까지 전체 소프트웨어 딜리버리 프로세스를 정의하는 일련의 자동화된 단계입니다. 이러한 단계는 일반적으로 Groovy로 작성된 Jenkinsfile에 정의되며, 복잡한 빌드, 테스트 및 배포 시나리오를 관리하는 강력한 방법을 제공합니다.

코드형 젠킨스 파이프라인은 몇 가지 주요 이점을 제공합니다:

  • 버전 관리: Jenkinsfile은 애플리케이션 코드와 마찬가지로 소스 제어에 저장되어 버전 관리, 감사 및 협업을 가능하게 합니다.
  • 반복성: 다양한 환경과 실행 전반에 걸쳐 딜리버리 프로세스의 일관된 실행을 보장합니다.
  • 가시성: 전체 딜리버리 프로세스를 명확하고 이해하기 쉽게 보여줍니다.
  • 내구성: 젠킨스 마스터가 재시작되어도 파이프라인이 중단되지 않고 지속될 수 있습니다.
  • 확장성: 공유 라이브러리를 통해 복잡한 로직을 추상화하고 재사용할 수 있습니다.

선언형 파이프라인

파이프라인 버전 2.5와 함께 도입된 선언형 파이프라인은 파이프라인을 더 쉽게 작성하고 이해할 수 있도록 설계된 보다 현대적이고 독단적인(opinionated) 문법입니다. 미리 정의된 블록 구조를 통해 구조화된 접근 방식을 제공하여, 특히 젠킨스나 Groovy에 익숙하지 않은 사용자에게 매우 가독성이 좋고 직관적입니다.

특성 및 문법

선언형 파이프라인은 pipeline, agent, stages, steps, post, environment, parameters, options, triggers, tools, input, when과 같은 최상위 블록으로 정의된 특정 구조를 강제합니다. 이 구조는 워크플로우의 다양한 부분에 대한 명확한 경계를 제공하여 파이프라인 정의를 단순화합니다.

선언형 파이프라인의 기본 구조는 다음과 같습니다:

pipeline {
    agent any // 또는 'label', 'docker' 등

    stages {
        stage('Build') {
            steps {
                echo 'Building the application...'
                sh 'mvn clean install'
            }
        }
        stage('Test') {
            steps {
                echo 'Running tests...'
                sh 'mvn test'
            }
        }
        stage('Deploy') {
            when {
                branch 'main'
            }
            steps {
                echo 'Deploying to production...'
                script {
                    // 절대적으로 필요한 경우 여기에 스크립트형 로직을 넣을 수 있습니다
                    // 예: 공유 라이브러리 함수 호출
                    // mySharedLibrary.deployApplication()
                }
            }
        }
    }

    post {
        always {
            echo 'Pipeline finished.'
        }
        success {
            echo 'Pipeline succeeded!'
        }
        failure {
            echo 'Pipeline failed :('
        }
    }
}

선언형 파이프라인의 장점

  • 단순성 및 가독성: 미리 정의된 구조로 인해 비전문가도 파이프라인을 쉽게 읽고 이해할 수 있습니다. 이는 구성 파일처럼 느껴집니다.
  • 구조화된 접근 방식: 파이프라인 전반에 걸쳐 모범 사례와 일관성을 적용하여 학습 곡선과 오류 가능성을 줄입니다.
  • 내장 기능: 조건부 실행(when), 빌드 후 작업(post), 병렬 스테이지 실행, 파이프라인 흐름 관리를 위한 다양한 옵션 등 일반적인 CI/CD 패턴에 대한 풍부한 내장 기능을 제공합니다.
  • 배우기 쉬움: 독단적인 문법 덕분에 Groovy 지식이 많지 않은 개발자도 빠르게 시작할 수 있습니다.
  • 검증: 젠킨스는 선언형 파이프라인에 대한 더 나은 정적 분석 및 검증을 제공하여 실행 전에 일반적인 오류를 잡아냅니다.

선언형 파이프라인의 한계

  • 유연성 부족: 미리 정의된 블록 외부의 사용자 지정 Groovy 로직이 필요한 고도로 복잡하거나 동적인 워크플로우의 경우, 엄격한 구조가 제한적일 수 있습니다.
  • Groovy 직접 접근 제한: script 블록을 사용하여 스크립트형 파이프라인 문법을 삽입할 수 있지만, 과도하게 사용하면 선언형 문법의 이점을 저해하고 파이프라인을 읽기 어렵게 만들 수 있습니다.

선언형 파이프라인을 언제 사용해야 하는가

선언형 파이프라인은 대부분의 일반적인 CI/CD 시나리오에 권장되는 선택입니다. 다음 경우에 이상적입니다:

  • 젠킨스 또는 코드형 파이프라인을 처음 접하는 팀.
  • 간단하거나 중간 정도의 복잡성을 가진 빌드, 테스트 및 배포 프로세스를 가진 프로젝트.
  • 많은 파이프라인에서 일관성과 유지보수성을 보장하려는 경우.
  • 병렬 실행, 조건부 스테이지 및 알림과 같은 일반적인 패턴에 젠킨스의 내장 기능을 활용하려는 경우.

스크립트형 파이프라인

Groovy 프로그래밍 언어 위에 직접 구축된 스크립트형 파이프라인은 코드형 젠킨스 파이프라인의 원래 문법이었습니다. 이는 최대의 유연성과 강력함을 제공하여 개발자가 고도로 사용자 정의되고 동적인 자동화 흐름을 구현할 수 있도록 합니다.

특성 및 문법

스크립트형 파이프라인은 전통적인 Groovy 스크립트와 마찬가지로 위에서 아래로 순차적으로 실행됩니다. 이들은 Groovy의 전체 문법을 사용하고 node, stage, checkout, sh, git 등의 메서드를 통해 젠킨스 파이프라인 DSL(도메인 특정 언어)을 활용합니다. 이는 젠킨스 API와 Groovy 언어의 모든 기능에 직접 접근할 수 있도록 합니다.

스크립트형 파이프라인의 기본 구조는 다음과 같습니다:

node('my-agent-label') {
    stage('Prepare') {
        echo 'Preparing the workspace...'
        checkout scm
    }

    stage('Build') {
        echo 'Building the application...'
        try {
            sh 'mvn clean install'
        } catch (err) {
            echo "Build failed: ${err}"
            // 사용자 정의 오류 처리
            currentBuild.result = 'FAILURE'
            throw err
        }
    }

    stage('Test') {
        echo 'Running tests...'
        // 테스트 스위트 동적 결정
        def testSuites = sh(script: 'find tests -name "*.test"', returnStdout: true).trim().split('\n')
        if (testSuites.isEmpty()) {
            echo 'No tests found.'
        } else {
            for (suite in testSuites) {
                echo "Running test suite: ${suite}"
                sh "./run-test.sh ${suite}"
            }
        }
    }

    stage('Deploy') {
        // 복잡한 조건부 로직
        if (env.BRANCH_NAME == 'main' && currentBuild.currentResult == 'SUCCESS') {
            echo 'Deploying to production...'
            sh './deploy-prod.sh'
        } else if (env.BRANCH_NAME == 'develop') {
            echo 'Deploying to staging...'
            sh './deploy-staging.sh'
        } else {
            echo 'No deployment for this branch.'
        }
    }

    // 빌드 후 작업은 try-finally 블록 또는 사용자 정의 로직으로 구현할 수 있습니다
    // 예: 알림 보내기
    if (currentBuild.result == 'SUCCESS') {
        echo 'Pipeline completed successfully!'
        // notifySuccess()
    } else {
        echo 'Pipeline failed.'
        // notifyFailure()
    }
}

스크립트형 파이프라인의 장점

  • 최대 유연성: Groovy의 모든 기능을 제공하여 고도로 복잡하고 동적인 로직, 사용자 정의 루프, 오류 처리 및 데이터 조작을 가능하게 합니다.
  • 젠킨스 API 직접 접근: 전체 젠킨스 API에 직접 접근할 수 있어 작업 매개변수, 빌드 상태 및 통합에 대한 세분화된 제어를 가능하게 합니다.
  • 동적 동작: 런타임 조건에 기반한 동적 에이전트 할당, 병렬 실행 또는 고급 리소스 관리가 필요한 워크플로우에 이상적입니다.
  • 확장성: 선언형 파이프라인을 위한 재사용 가능한 복잡한 로직을 캡슐화하는 정교한 공유 라이브러리를 만드는 데 탁월합니다.

스크립트형 파이프라인의 한계

  • 가파른 학습 곡선: Groovy에 대한 깊은 이해가 필요하며, 이는 해당 언어에 익숙하지 않은 팀에게 장벽이 될 수 있습니다.
  • 덜 독단적(Opinionated): 엄격한 구조가 없으므로 파이프라인이 여러 프로젝트나 개발자 간에 일관성이 없어지고 읽거나 유지보수하기 어려워질 수 있습니다.
  • 오류 발생 가능성 높음: Groovy의 유연성은 코딩 오류의 기회를 더 많이 제공하며, 선언형에 비해 내장된 검증 기능이 적습니다.
  • 가독성 문제: 복잡한 스크립트형 파이프라인은 빠르게 파악하고 이해하기 어려워져 협업 및 문제 해결을 방해할 수 있습니다.
  • 파이프라인 특정 문법 부족: 많은 일반적인 CI/CD 패턴(post 작업 또는 when 조건 등)은 Groovy 구성 요소(예: try-catch-finally, if 문)를 사용하여 수동으로 구현해야 합니다.

선언형 대 스크립트형: 비교

차이점을 요약하기 위해 다음 비교표를 참조하십시오:

기능 선언형 파이프라인 스크립트형 파이프라인
문법 구조 독단적(Opinionated), 미리 정의된 최상위 블록. 유연한 Groovy 기반, 순차적 실행.
학습 곡선 초보자에게 더 쉽고, Groovy 지식 요구 사항 적음. 더 가파르며, Groovy 전문 지식 필요.
가독성 구조화된 블록과 명확한 문법으로 인해 높음. 복잡한 스크립트의 경우 낮을 수 있으며, 개발자 스타일에 따라 다름.
유연성 미리 정의된 구조로 제한; Groovy를 위한 script 블록. 무제한, Groovy의 모든 기능 활용.
내장 기능 일반적인 CI/CD 패턴(post, when, parallel)을 위한 풍부한 세트. Groovy 구성 요소를 사용한 수동 구현 필요.
오류 처리 전역 또는 스테이지별 작업을 위한 post 블록. 수동 try-catch-finally 블록.
확장성 복잡한 Groovy 로직을 위해 공유 라이브러리 활용. 복잡한 Groovy 로직을 직접 작성. 종종 공유 라이브러리를 생성함.
에이전트 제어 전역 agent 또는 스테이지 수준 agent. node 블록, 어디에서든 에이전트 정의 가능.
사용 사례 표준 CI/CD 워크플로우, 단순하거나 중간 정도의 복잡성. 고도로 동적, 복잡한 사용자 정의 워크플로우; 공유 라이브러리 개발.
JSON/YAML 느낌 구성 언어와 더 유사. 순수한 프로그래밍 언어.

올바른 문법 선택하기

선언형 파이프라인과 스크립트형 파이프라인 중 하나를 결정할 때 다음 요소를 고려하십시오:

  1. 팀의 Groovy 전문성: 팀에 강력한 Groovy 기술이 부족하다면, 선언형이 훨씬 더 얕은 학습 곡선을 가지며 더 빠른 채택을 촉진할 것입니다.
  2. 워크플로우 복잡성: 대부분의 표준 CI/CD 워크플로우(빌드, 테스트, 배포)의 경우, 선언형은 가독성과 내장 기능 덕분에 완벽하게 적절하며 종종 더 우수합니다. 고도로 동적이거나 조건부이거나 사용자 정의 리소스 집약적인 작업의 경우 스크립트형이 필요할 수 있습니다.
  3. 유지보수성 및 가독성: 선언형 파이프라인은 일반적으로 읽고 유지보수하기 더 쉽습니다. 특히 많은 파이프라인과 개발자가 있는 대규모 조직의 경우 더욱 그렇습니다. 이러한 일관성은 인지 부하를 줄여줍니다.
  4. 기존 파이프라인 생태계: 기존 스크립트형 파이프라인이나 스크립트형 문법으로 구축된 강력한 공유 라이브러리 세트가 있다면, 일관성을 위해 계속 사용하거나 적절한 경우 선언형으로 점진적으로 마이그레이션할 수 있습니다.
  5. 미래 성장: 선언형 파이프라인은 일반적으로 충분하며, 공유 라이브러리를 통해 사용자 정의 로직으로 확장될 수 있습니다. 공유 라이브러리 자체는 일반적으로 스크립트형 Groovy로 작성됩니다. 이는 종종 최상의 하이브리드 접근 방식입니다.

의사 결정 모범 사례

  • 선언형으로 시작: 새로운 파이프라인의 경우 선언형을 기본으로 사용하십시오. 이는 CI/CD 사용 사례의 대부분을 커버하며 일관성과 가독성을 높입니다.
  • 공유 라이브러리 활용: 선언형 파이프라인에서 반복적이거나 복잡한 로직을 발견하면, 해당 로직을 공유 라이브러리로 추상화하십시오. 공유 라이브러리는 주로 스크립트형 Groovy로 작성되므로 선언형의 구조와 스크립트형의 유연성이라는 두 가지 장점을 결합할 수 있습니다.
  • 선언형에 스크립트 과도하게 사용 지양: 선언형이 script 블록을 허용하더라도 최소한으로 유지하도록 노력하십시오. script 블록이 너무 커지거나 복잡해지면 해당 로직을 공유 라이브러리 함수로 이동해야 한다는 강력한 지표입니다.
  • 마이그레이션 고려: 유지보수하기 어려워지는 레거시 스크립트형 파이프라인이 있다면, 이를 선언형 문법으로 리팩토링하고 복잡한 부분을 공유 라이브러리로 옮기는 것을 고려하십시오.

결론

선언형과 스크립트형 젠킨스 파이프라인 문법은 모두 CI/CD 워크플로우를 정의하는 강력한 도구입니다. 선언형은 대부분의 표준 CI/CD 요구 사항과 사용 편의성 및 일관성을 우선시하는 팀에게 이상적인 구조화되고 독단적이며 매우 가독성 높은 접근 방식을 제공합니다. 반면 스크립트형은 비할 데 없는 유연성과 제어력을 제공하여, 고도로 복잡하고 동적인 시나리오와 선언형 파이프라인을 강화하는 기초 공유 라이브러리를 개발하는 데 필수적입니다.

현대적인 권장 사항은 단순성과 유지보수성을 위해 선언형 파이프라인을 선호하고, 재사용 가능한 복잡한 로직을 캡슐화하기 위해 주로 공유 라이브러리 내에서 스크립트형 파이프라인을 활용하는 것입니다. 각 문법의 장점과 한계를 이해함으로써 프로젝트, 팀의 기술 세트 및 장기적인 CI/CD 전략에 가장 적합한 정보에 입각한 결정을 내릴 수 있으며, 궁극적으로 더 견고하고 효율적이며 유지보수 가능한 자동화로 이어질 것입니다. 즐거운 파이프라이닝 되세요!