젠킨스 사용법과 실제 문법에 초점을 두고 어떻게 설계되고 사용하는지 핸드북 형식으로 요약 정리. nodejs
Jenkins 사용법과 실제 문법 핸드북
목차
- Jenkins Pipeline 기본 구조
 - Declarative Pipeline 문법
 - Node.js 프로젝트 실제 예제
 - 주요 Directive 상세 문법
 - 조건부 실행과 병렬 처리
 - 환경 변수와 자격 증명
 - 에러 처리와 후처리
 - Docker 통합
 - 모범 사례와 보안
 - Shared Library 활용
 
1. Jenkins Pipeline 기본 구조
1.1 Declarative Pipeline의 기본 틀
모든 유효한 Declarative Pipeline은 pipeline 블록 내에 정의되어야 합니다12.
pipeline { {
        stage('Build') {
            steps {
                echo 'Building...'
            }
        }
    }
}1.2 필수 구성 요소
Declarative Pipeline은 다음 필수 요소들을 포함해야 합니다3:
- agent: 파이프라인이 실행될 위치 지정
 - stages: 하나 이상의 stage 블록 포함
 - stage: 개별 작업 단위 정의
 - steps: stage 내에서 실행될 구체적인 명령어들
 
2. Declarative Pipeline 문법
2.1 Pipeline 블록 구조
pipeline {
    /* Declarative Pipeline 내용 */
}기본 규칙2:
- 최상위 레벨은 반드시 
pipeline { }블록이어야 함 - 세미콜론 없음 (각 문장은 별도 줄에 작성)
 - 블록은 Sections, Directives, Steps, 또는 할당문으로만 구성
 
2.2 Agent 지시문
Agent는 Jenkins 환경에서 파이프라인이 실행될 위치를 지정합니다45:
pipeline {
    agent any                    // 사용 가능한 모든 agent
    // 또는
    agent none                   // 글로벌 agent 없음 (각 stage에서 정의)
    // 또는
    agent {
        label 'linux-machine'    // 특정 라벨의 agent
    }
    // 또는
    agent {
        docker {
            image 'node:18'      // Docker 컨테이너에서 실행
        }
    }
}2.3 Stages와 Steps
pipeline {
    agent any
    stages {
        stage('Checkout') {
            steps {
                git 'https://github.com/username/repository.git'
            }
        }
        stage('Build') {
            steps {
                sh 'npm install'
                sh 'npm run build'
            }
        }
        stage('Test') {
            steps {
                sh 'npm test'
            }
        }
    }
}3. Node.js 프로젝트 실제 예제
3.1 기본 Node.js Pipeline
Node.js 프로젝트를 위한 완전한 파이프라인 예제입니다67:
pipeline {
    agent any
    
    tools {
        nodejs 'NodeJS-18'  // Jenkins에서 설정한 Node.js 도구명
    }
    
    environment {
        CI = 'true'
        NODE_ENV = 'production'
    }
    
    stages {
        stage('Checkout') {
            steps {
                git branch: 'main', 
                    url: 'https://github.com/username/nodejs-app.git'
            }
        }
        
        stage('Install Dependencies') {
            steps {
                sh 'npm install'
            }
        }
        
        stage('Lint') {
            steps {
                sh 'npm run lint'
            }
        }
        
        stage('Test') {
            steps {
                sh 'npm test'
            }
            post {
                always {
                    publishTestResults testResultsPattern: 'test-results.xml'
                }
            }
        }
        
        stage('Build') {
            steps {
                sh 'npm run build'
            }
        }
        
        stage('Archive Artifacts') {
            steps {
                archiveArtifacts artifacts: 'dist/**/*', 
                                allowEmptyArchive: true
            }
        }
    }
    
    post {
        always {
            cleanWs()
        }
        success {
            echo 'Pipeline completed successfully!'
        }
        failure {
            echo 'Pipeline failed!'
        }
    }
}3.2 Docker를 활용한 Node.js Pipeline
pipeline {
    agent {
        docker {
            image 'node:18-alpine'
            args '-v /tmp/.npm:/tmp/.npm'  // npm 캐시 마운트
        }
    }
    
    environment {
        HOME = '.'  // Docker 권한 문제 해결
        NPM_CONFIG_CACHE = '/tmp/.npm'
    }
    
    stages {
        stage('Install & Test') {
            parallel {
                stage('Install Dependencies') {
                    steps {
                        sh 'npm install'
                    }
                }
                stage('Security Audit') {
                    steps {
                        sh 'npm audit --audit-level moderate'
                    }
                }
            }
        }
        
        stage('Build & Package') {
            steps {
                sh 'npm run build'
                sh 'npm pack'
            }
        }
    }
}4. 주요 Directive 상세 문법
4.1 Environment 지시문
pipeline {
    agent any
    
    environment {
        // 글로벌 환경 변수
        APP_NAME = 'my-node-app'
        VERSION = '1.0.0'
        DATABASE_URL = credentials('db-url')  // 자격 증명 사용
    }
    
    stages {
        stage('Deploy') {
            environment {
                // 스테이지별 환경 변수
                DEPLOY_ENV = 'production'
                API_KEY = credentials('api-key')
            }
            steps {
                sh 'echo "Deploying ${APP_NAME} version ${VERSION}"'
                sh 'echo "Environment: ${DEPLOY_ENV}"'
            }
        }
    }
}4.2 Tools 지시문
자동으로 설치하거나 PATH에 추가할 도구를 정의합니다10:
pipeline {
    agent any
    
    tools {
        nodejs 'NodeJS-18'     // NodeJS 플러그인에서 설정한 이름
        maven 'Maven-3.8'      // Maven 도구
        jdk 'JDK-11'          // JDK 도구
    }
    
    stages {
        stage('Build') {
            steps {
                sh 'node --version'
                sh 'npm --version'
                sh 'mvn --version'
                sh 'java --version'
            }
        }
    }
}4.3 Parameters 지시문
pipeline {
    agent any
    
    parameters {
        string(
            name: 'BRANCH_NAME',
            defaultValue: 'main',
            description: 'Git branch to build'
        )
        choice(
            name: 'ENVIRONMENT',
            choices: ['dev', 'staging', 'production'],
            description: 'Target environment'
        )
        booleanParam(
            name: 'SKIP_TESTS',
            defaultValue: false,
            description: 'Skip test execution'
        )
        password(
            name: 'API_TOKEN',
            description: 'API token for deployment'
        )
    }
    
    stages {
        stage('Build') {
            steps {
                echo "Building branch: ${params.BRANCH_NAME}"
                echo "Target environment: ${params.ENVIRONMENT}"
                script {
                    if (!params.SKIP_TESTS) {
                        sh 'npm test'
                    }
                }
            }
        }
    }
}4.4 Options 지시문
파이프라인 옵션을 설정합니다5:
pipeline {
    agent any
    
    options {
        timeout(time: 1, unit: 'HOURS')           // 1시간 타임아웃
        retry(3)                                  // 실패 시 3회 재시도
        skipDefaultCheckout()                     // 기본 체크아웃 건너뛰기
        buildDiscarder(                          // 빌드 기록 관리
            logRotator(
                daysToKeepStr: '30',
                numToKeepStr: '10'
            )
        )
        disableConcurrentBuilds()                // 동시 빌드 비활성화
        timestamps()                             // 로그에 타임스탬프 추가
    }
    
    stages {
        stage('Build') {
            steps {
                echo 'Building with timeout protection'
            }
        }
    }
}5. 조건부 실행과 병렬 처리
5.1 When 지시문 - 조건부 실행
특정 조건에서만 stage를 실행합니다12:
pipeline {
    agent any
    
    stages {
        stage('Deploy to Production') {
            when {
                branch 'main'  // main 브랜치에서만 실행
            }
            steps {
                sh 'npm run deploy:prod'
            }
        }
        
        stage('Run Integration Tests') {
            when {
                expression { 
                    return env.BRANCH_NAME != 'main' 
                }
            }
            steps {
                sh 'npm run test:integration'
            }
        }
        
        stage('Deploy to Staging') {
            when {
                allOf {
                    branch 'develop'
                    environment name: 'DEPLOY_STAGING', value: 'true'
                }
            }
            steps {
                sh 'npm run deploy:staging'
            }
        }
        
        stage('Feature Branch Build') {
            when {
                anyOf {
                    branch 'feature/*'
                    changeRequest()
                }
            }
            steps {
                sh 'npm run build:feature'
            }
        }
    }
}5.2 Parallel 지시문 - 병렬 처리
pipeline {
    agent any
    
    stages {
        stage('Parallel Testing') {
            parallel {
                stage('Unit Tests') {
                    steps {
                        sh 'npm run test:unit'
                    }
                }
                stage('Integration Tests') {
                    steps {
                        sh 'npm run test:integration'
                    }
                }
                stage('Lint Check') {
                    steps {
                        sh 'npm run lint'
                    }
                }
                stage('Security Scan') {
                    steps {
                        sh 'npm audit'
                    }
                }
            }
        }
        
        stage('Cross-Platform Build') {
            parallel {
                stage('Linux Build') {
                    agent {
                        label 'linux'
                    }
                    steps {
                        sh 'npm run build:linux'
                    }
                }
                stage('Windows Build') {
                    agent {
                        label 'windows'
                    }
                    steps {
                        bat 'npm run build:windows'
                    }
                }
            }
        }
    }
}5.3 실패 시 빠른 중단
pipeline {
    agent any
    
    stages {
        stage('Quality Checks') {
            parallel failFast: true {  // 하나라도 실패하면 모든 병렬 작업 중단
                stage('Test Coverage') {
                    steps {
                        sh 'npm run test:coverage'
                    }
                }
                stage('Code Quality') {
                    steps {
                        sh 'npm run sonar'
                    }
                }
                stage('Performance Test') {
                    steps {
                        sh 'npm run test:performance'
                    }
                }
            }
        }
    }
}6. 환경 변수와 자격 증명
6.1 환경 변수 사용법
Jenkins는 다양한 내장 환경 변수를 제공합니다89:
pipeline {
    agent any
    
    stages {
        stage('Environment Info') {
            steps {
                echo "Job Name: ${env.JOB_NAME}"
                echo "Build Number: ${env.BUILD_NUMBER}"
                echo "Build URL: ${env.BUILD_URL}"
                echo "Workspace: ${env.WORKSPACE}"
                echo "Git Commit: ${env.GIT_COMMIT}"
                echo "Git Branch: ${env.BRANCH_NAME}"
                
                // 모든 환경 변수 출력
                sh 'printenv | sort'
            }
        }
    }
}6.2 자격 증명 관리
Jenkins Credentials를 안전하게 사용하는 방법입니다1516:
pipeline {
    agent any
    
    environment {
        // credentials() 함수 사용
        DOCKER_CREDS = credentials('docker-hub-credentials')
        API_KEY = credentials('api-key-secret')
    }
    
    stages {
        stage('Login & Deploy') {
            steps {
                // Username/Password 자격 증명
                withCredentials([
                    usernamePassword(
                        credentialsId: 'docker-hub-credentials',
                        usernameVariable: 'DOCKER_USER',
                        passwordVariable: 'DOCKER_PASS'
                    )
                ]) {
                    sh 'echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin'
                    sh 'docker push myapp:latest'
                }
                
                // SSH 키 사용
                withCredentials([
                    sshUserPrivateKey(
                        credentialsId: 'deployment-ssh-key',
                        keyFileVariable: 'SSH_KEY',
                        usernameVariable: 'SSH_USER'
                    )
                ]) {
                    sh '''
                        ssh -i $SSH_KEY $SSH_USER@server.com '
                            pm2 restart myapp
                        '
                    '''
                }
                
                // Secret Text 사용
                withCredentials([
                    string(
                        credentialsId: 'github-token',
                        variable: 'GITHUB_TOKEN'
                    )
                ]) {
                    sh 'gh auth login --with-token <<< $GITHUB_TOKEN'
                }
            }
        }
    }
}6.3 동적 환경 변수 설정
pipeline {
    agent any
    
    stages {
        stage('Set Dynamic Variables') {
            steps {
                script {
                    // 동적으로 환경 변수 설정
                    env.BUILD_VERSION = sh(
                        script: "npm version --json | jq -r '.version'",
                        returnStdout: true
                    ).trim()
                    
                    env.GIT_SHORT_COMMIT = sh(
                        script: "git rev-parse --short HEAD",
                        returnStdout: true
                    ).trim()
                    
                    env.DOCKER_TAG = "${env.BUILD_VERSION}-${env.GIT_SHORT_COMMIT}"
                }
                
                echo "Version: ${env.BUILD_VERSION}"
                echo "Docker Tag: ${env.DOCKER_TAG}"
            }
        }
    }
}7. 에러 처리와 후처리
7.1 Post 지시문
pipeline {
    agent any
    
    stages {
        stage('Build') {
            steps {
                sh 'npm run build'
            }
            post {
                success {
                    echo 'Build stage completed successfully'
                }
                failure {
                    echo 'Build stage failed'
                    archiveArtifacts artifacts: 'logs/**/*', allowEmptyArchive: true
                }
                always {
                    echo 'Build stage finished'
                }
            }
        }
    }
    
    post {
        always {
            echo 'Pipeline finished'
            cleanWs()  // 워크스페이스 정리
        }
        success {
            emailext (
                subject: "SUCCESS: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
                body: "Good news! The build succeeded.",
                to: "${env.CHANGE_AUTHOR_EMAIL}"
            )
        }
        failure {
            emailext (
                subject: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
                body: "Build failed. Please check the console output.",
                to: "${env.CHANGE_AUTHOR_EMAIL}"
            )
        }
        unstable {
            echo 'Pipeline marked as unstable'
        }
        changed {
            echo 'Pipeline result changed from previous run'
        }
    }
}7.2 CatchError - 에러 처리
특정 stage가 실패해도 파이프라인을 계속 진행합니다1920:
pipeline {
    agent any
    
    stages {
        stage('Optional Tests') {
            steps {
                catchError(
                    buildResult: 'SUCCESS',
                    stageResult: 'FAILURE',
                    message: 'Tests failed but continuing pipeline'
                ) {
                    sh 'npm run test:optional'
                }
            }
        }
        
        stage('Critical Build') {
            steps {
                catchError(
                    buildResult: 'FAILURE',
                    stageResult: 'FAILURE'
                ) {
                    sh 'npm run build'
                }
            }
        }
        
        stage('Continue Anyway') {
            steps {
                echo 'This stage runs even if previous stages failed'
            }
        }
    }
}7.3 Try-Catch 블록
더 세밀한 에러 처리가 필요한 경우:
pipeline {
    agent any
    
    stages {
        stage('Complex Error Handling') {
            steps {
                script {
                    try {
                        sh 'npm run risky-command'
                        currentBuild.result = 'SUCCESS'
                    } catch (Exception e) {
                        echo "Error occurred: ${e.getMessage()}"
                        
                        // 에러 유형별 처리
                        if (e.getMessage().contains('timeout')) {
                            echo 'Handling timeout error'
                            currentBuild.result = 'UNSTABLE'
                        } else {
                            echo 'Handling general error'
                            currentBuild.result = 'FAILURE'
                        }
                        
                        // 에러 정보 저장
                        writeFile file: 'error.log', text: e.getMessage()
                        archiveArtifacts artifacts: 'error.log'
                    } finally {
                        echo 'Cleanup operations'
                        sh 'npm run cleanup'
                    }
                }
            }
        }
    }
}8. Docker 통합
8.1 Docker Agent 사용
pipeline {
    agent {
        docker {
            image 'node:18-alpine'
            label 'docker'
            args '-v /var/run/docker.sock:/var/run/docker.sock'
        }
    }
    
    stages {
        stage('Build in Container') {
            steps {
                sh 'node --version'
                sh 'npm --version'
                sh 'npm install'
                sh 'npm run build'
            }
        }
    }
}8.2 다단계 Docker 빌드
pipeline {
    agent any
    
    environment {
        DOCKER_REGISTRY = 'your-registry.com'
        IMAGE_NAME = 'myapp'
        DOCKER_BUILDKIT = '1'
    }
    
    stages {
        stage('Build Application') {
            agent {
                docker {
                    image 'node:18'
                    args '-v /tmp/.npm:/root/.npm'
                }
            }
            steps {
                sh 'npm ci --only=production'
                sh 'npm run build'
                stash includes: 'dist/**/*,package*.json', name: 'built-app'
            }
        }
        
        stage('Build Docker Image') {
            steps {
                unstash 'built-app'
                script {
                    def image = docker.build("${IMAGE_NAME}:${env.BUILD_NUMBER}")
                    docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry-credentials') {
                        image.push()
                        image.push("latest")
                    }
                }
            }
        }
        
        stage('Deploy') {
            steps {
                sh """
                    docker run -d --name myapp-${env.BUILD_NUMBER} \
                        -p 3000:3000 \
                        ${DOCKER_REGISTRY}/${IMAGE_NAME}:${env.BUILD_NUMBER}
                """
            }
        }
    }
}8.3 Multi-stage Dockerfile 활용
// Dockerfile
/*
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
 
FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
*/
 
pipeline {
    agent any
    
    stages {
        stage('Build Multi-stage Image') {
            steps {
                script {
                    def image = docker.build(
                        "myapp:${env.BUILD_NUMBER}",
                        "--target runtime ."
                    )
                    
                    // 이미지 취약점 스캔
                    sh "docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
                        aquasec/trivy image myapp:${env.BUILD_NUMBER}"
                }
            }
        }
    }
}9. 모범 사례와 보안
9.1 보안 모범 사례
Jenkins 파이프라인 보안을 위한 핵심 사항들입니다2324:
pipeline {
    agent {
        label 'restricted-agent'  // 제한된 agent 사용
    }
    
    options {
        skipDefaultCheckout()     // 불필요한 체크아웃 방지
        timeout(time: 30, unit: 'MINUTES')  // 타임아웃 설정
    }
    
    environment {
        // 민감한 정보는 credentials() 함수 사용
        DB_PASSWORD = credentials('database-password')
        API_KEY = credentials('api-key')
    }
    
    stages {
        stage('Secure Checkout') {
            steps {
                // 특정 브랜치와 깊이 제한
                git branch: env.BRANCH_NAME,
                    url: 'https://github.com/company/secure-repo.git',
                    credentialsId: 'github-token',
                    depth: 1
            }
        }
        
        stage('Security Scans') {
            parallel {
                stage('Dependency Check') {
                    steps {
                        sh 'npm audit --audit-level moderate'
                        sh 'npm run security:scan'
                    }
                }
                stage('Code Security Analysis') {
                    steps {
                        sh 'npm run lint:security'
                        // SonarQube 보안 분석
                        withSonarQubeEnv('SonarQube') {
                            sh 'sonar-scanner'
                        }
                    }
                }
            }
        }
        
        stage('Secure Build') {
            steps {
                // 빌드 중 네트워크 접근 제한
                sh '''
                    export NODE_ENV=production
                    npm ci --only=production
                    npm run build
                '''
            }
        }
    }
    
    post {
        always {
            // 민감한 파일 정리
            sh 'rm -f .env .env.local'
            cleanWs()
        }
        failure {
            // 보안 관련 실패 시 알림
            emailext (
                subject: "Security Issue in ${env.JOB_NAME}",
                body: "Security scan failed. Please review immediately.",
                to: "security-team@company.com"
            )
        }
    }
}9.2 성능 최적화
pipeline {
    agent any
    
    options {
        // 빌드 기록 관리로 디스크 공간 절약
        buildDiscarder(logRotator(
            numToKeepStr: '10',
            daysToKeepStr: '30'
        ))
        // 동시 빌드 제한
        disableConcurrentBuilds()
    }
    
    stages {
        stage('Optimized Build') {
            steps {
                // npm 캐시 활용
                sh '''
                    export NPM_CACHE_DIR=/tmp/npm-cache-${BUILD_NUMBER}
                    mkdir -p $NPM_CACHE_DIR
                    npm ci --cache $NPM_CACHE_DIR
                    npm run build
                '''
                
                // 병렬 처리로 시간 단축
                parallel (
                    "Unit Tests": {
                        sh 'npm run test:unit'
                    },
                    "Lint Check": {
                        sh 'npm run lint'
                    },
                    "Type Check": {
                        sh 'npm run type-check'
                    }
                )
            }
        }
    }
    
    post {
        always {
            // 캐시 정리
            sh 'rm -rf /tmp/npm-cache-${BUILD_NUMBER}'
        }
    }
}9.3 코드 품질 체크
pipeline {
    agent any
    
    stages {
        stage('Quality Gates') {
            steps {
                script {
                    // 테스트 커버리지 체크
                    sh 'npm run test:coverage'
                    def coverage = readFile('coverage/coverage-summary.json')
                    def coverageJson = readJSON text: coverage
                    def linesCoverage = coverageJson.total.lines.pct
                    
                    if (linesCoverage < 80) {
                        error("Code coverage ${linesCoverage}% is below threshold 80%")
                    }
                    
                    echo "Code coverage: ${linesCoverage}%"
                }
                
                // 복잡도 분석
                sh 'npm run complexity:check'
                
                // 보안 취약점 검사
                sh 'npm audit --level moderate'
            }
            post {
                always {
                    publishHTML([
                        allowMissing: false,
                        alwaysLinkToLastBuild: true,
                        keepAll: true,
                        reportDir: 'coverage/lcov-report',
                        reportFiles: 'index.html',
                        reportName: 'Coverage Report'
                    ])
                }
            }
        }
    }
}10. Shared Library 활용
10.1 Shared Library 기본 구조
Jenkins Shared Library는 공통 코드를 재사용하기 위한 강력한 도구입니다2526:
// vars/buildNodeApp.groovy
def call(Map config) {
    pipeline {
        agent any
        
        tools {
            nodejs config.nodeVersion ?: 'NodeJS-18'
        }
        
        stages {
            stage('Checkout') {
                steps {
                    git branch: config.branch ?: 'main',
                        url: config.repoUrl
                }
            }
            
            stage('Install Dependencies') {
                steps {
                    sh 'npm ci'
                }
            }
            
            stage('Test') {
                when {
                    expression { config.skipTests != true }
                }
                steps {
                    sh 'npm test'
                }
                post {
                    always {
                        publishTestResults testResultsPattern: 'test-results.xml'
                    }
                }
            }
            
            stage('Build') {
                steps {
                    sh 'npm run build'
                }
            }
            
            stage('Deploy') {
                when {
                    expression { config.deploy == true }
                }
                steps {
                    deployApp(config.deployTarget)
                }
            }
        }
        
        post {
            always {
                cleanWs()
            }
            success {
                notifySuccess(config.notificationChannel)
            }
            failure {
                notifyFailure(config.notificationChannel)
            }
        }
    }
}10.2 Shared Library 사용 예제
// Jenkinsfile
@Library('company-shared-library@v1.0') _
 
// 간단한 사용법
buildNodeApp([
    repoUrl: 'https://github.com/company/my-app.git',
    branch: 'develop',
    nodeVersion: 'NodeJS-18',
    skipTests: false,
    deploy: true,
    deployTarget: 'staging',
    notificationChannel: '#deployments'
])10.3 고급 Shared Library 함수
// vars/deployToKubernetes.groovy
def call(String environment, Map config = [:]) {
    def namespace = config.namespace ?: environment
    def image = config.image ?: "${env.IMAGE_NAME}:${env.BUILD_NUMBER}"
    def replicas = config.replicas ?: 3
    
    script {
        withKubeConfig([credentialsId: "k8s-${environment}"]) {
            // Deployment 업데이트
            sh """
                kubectl set image deployment/${config.appName} \
                    ${config.appName}=${image} \
                    --namespace=${namespace}
            """
            
            // Rollout 상태 확인
            sh """
                kubectl rollout status deployment/${config.appName} \
                    --namespace=${namespace} \
                    --timeout=300s
            """
            
            // 헬스 체크
            def healthCheck = sh(
                script: """
                    kubectl get pods -l app=${config.appName} \
                        --namespace=${namespace} \
                        --field-selector=status.phase=Running \
                        --no-headers | wc -l
                """,
                returnStdout: true
            ).trim() as Integer
            
            if (healthCheck < replicas) {
                error("Deployment failed: only ${healthCheck}/${replicas} pods are running")
            }
            
            echo "Successfully deployed ${config.appName} to ${environment}"
        }
    }
}
 
// vars/notifySlack.groovy
def call(String message, String color = 'good') {
    slackSend(
        channel: '#deployments',
        color: color,
        message: """
            *${env.JOB_NAME}* - Build #${env.BUILD_NUMBER}
            ${message}
            
            <${env.BUILD_URL}|View Build> | <${env.GIT_URL}|Repository>
        """
    )
}10.4 실제 프로젝트에서 Shared Library 활용
// Jenkinsfile
@Library('company-pipeline-library@main') _
 
pipeline {
    agent any
    
    environment {
        APP_NAME = 'my-node-api'
        DOCKER_REGISTRY = 'registry.company.com'
    }
    
    stages {
        stage('Build & Test') {
            steps {
                // Shared Library 함수 사용
                buildNodeApplication([
                    nodeVersion: '18',
                    testCommand: 'npm run test:ci',
                    buildCommand: 'npm run build:prod'
                ])
            }
        }
        
        stage('Docker Build') {
            steps {
                script {
                    def image = buildDockerImage([
                        imageName: "${DOCKER_REGISTRY}/${APP_NAME}",
                        tag: env.BUILD_NUMBER,
                        dockerfile: 'Dockerfile.prod'
                    ])
                    
                    // 이미지 보안 스캔
                    scanDockerImage(image)
                    
                    // 레지스트리에 푸시
                    pushDockerImage(image, 'docker-registry-creds')
                }
            }
        }
        
        stage('Deploy') {
            parallel {
                stage('Deploy to Staging') {
                    when {
                        branch 'develop'
                    }
                    steps {
                        deployToKubernetes('staging', [
                            appName: env.APP_NAME,
                            image: "${DOCKER_REGISTRY}/${APP_NAME}:${env.BUILD_NUMBER}",
                            namespace: 'staging',
                            replicas: 2
                        ])
                    }
                }
                
                stage('Deploy to Production') {
                    when {
                        branch 'main'
                    }
                    steps {
                        // 승인 프로세스
                        input message: 'Deploy to production?', 
                              submitter: 'admin,deploy-team'
                        
                        deployToKubernetes('production', [
                            appName: env.APP_NAME,
                            image: "${DOCKER_REGISTRY}/${APP_NAME}:${env.BUILD_NUMBER}",
                            namespace: 'production',
                            replicas: 5
                        ])
                    }
                }
            }
        }
    }
    
    post {
        success {
            notifySlack("✅ Deployment successful!", 'good')
        }
        failure {
            notifySlack("❌ Deployment failed!", 'danger')
        }
        always {
            archiveArtifacts artifacts: 'logs/**/*', allowEmptyArchive: true
            cleanWs()
        }
    }
}이 핸드북은 Jenkins Pipeline의 실제 문법과 사용법을 Node.js 프로젝트를 중심으로 종합적으로 다뤘습니다. 기본 구조부터 고급 기능인 Shared Library까지, 실무에서 바로 활용할 수 있는 실용적인 예제들을 포함하고 있습니다. 각 섹션의 코드 예제들을 참고하여 프로젝트에 맞는 파이프라인을 구성해보시기 바랍니다.
Footnotes
- 
https://www.blazemeter.com/blog/jenkins-declarative-pipeline ↩
 - 
https://www.lambdatest.com/blog/jenkins-declarative-pipeline-examples/ ↩ ↩2
 - 
https://cloud-centric.hashnode.dev/jenkins-pipeline-for-automating-the-deployment-of-a-nodejs-application ↩
 - 
https://www.linkedin.com/pulse/cicd-pipeline-nodejs-jenkins-docker-windows-linux-gantyada-nx6wc ↩
 - 
https://spacelift.io/blog/jenkins-environment-variables ↩ ↩2
 - 
https://stackoverflow.com/questions/37690920/conditional-step-stage-in-jenkins-pipeline ↩
 - 
https://www.baeldung.com/ops/running-stages-in-parallel-jenkins-workflow-pipeline ↩
 - 
https://dev.to/vandana_babshetti_91df8eb/parallel-stages-with-declarative-pipeline-in-jenkins-cicd-11fk ↩
 - 
https://stackoverflow.com/questions/64875989/how-to-get-a-jenkins-credential-in-a-pipeline-based-on-username-part-of-the-desi ↩
 - 
https://passwd.tistory.com/entry/Jenkins-Pipeline-Credentials-사용-2 ↩
 - 
https://kiranpawar.hashnode.dev/harnessing-post-build-actions-in-jenkins-pipelines ↩
 - 
https://docs.cloudbees.com/docs/cloudbees-ci/latest/pipeline-syntax-reference-guide/declarative-pipeline ↩
 - 
https://stackoverflow.com/questions/66705021/jenkins-catch-error-to-skip-to-next-stage-but-still-show-errored-stage-has-fai ↩
 - 
https://weezip.treefeely.com/post/building-with-docker-for-jenkins-pipeline ↩
 - 
https://www.geeksforgeeks.org/devops/jenkins-security-best-practices/ ↩
 - 
https://www.jenkins.io/doc/book/pipeline/shared-libraries/ ↩