회사의 서비스가 하나일 때는 Jenkinsfile 단 하나면 충분했습니다. 하지만 마이크로서비스 아키텍처(MSA)가 도입되면서 팀마다 관리해야 할 레포지토리가 수십, 수백 개로 늘어나면 어떨까요? 모든 프로젝트마다 100줄이 넘어가는 동일한 Jenkinsfile을 복사 & 붙여넣기 해야 하는 유지보수 악몽이 시작됩니다. 이런 중복의 고리를 끊고 파이프라인을 일관된 공통 모듈로 묶어 중앙 통제할 수 있게 해주는 기능이 바로 Jenkins Shared Library입니다
왜 Shared Library가 필요한가?
파이프라인이 각 레포지토리에 분산되어 있을 때 생기는 문제를 실무 관점에서 구체화해보겠습니다
| 문제점 | Shared Library 적용 전 (파편화) | Shared Library 적용 후 (중앙화) |
|---|---|---|
| 보안/정책 변경 | 수백 개의 레포지토리를 돌며 Jenkinsfile 일일이 수정 |
Shared Library 브랜치 소스 하나만 수정하면 일괄 전파 |
| 개발자 부담 | 앱 개발팀이 복잡한 Groovy 파이프라인 구조를 이해해야 함 | 앱 개발팀은 파라미터(이름, 프레임워크) 몇 개만 넘기면 끝남 |
| 코드 품질 | 템플릿 복붙으로 인한 오타, 누락, 설정 불일치 발생 빈번 | 중앙 플랫폼 엔지니어링 팀이 표준화되고 검증된 배포 스크립트 관리 |
물리적 디렉터리 아키텍처
Shared Library는 애플리케이션 레포지토리와는 별개의 독립된 Git 레포지토리로 분리해서 생성합니다. Jenkins가 이를 읽으려면 약속된 폴더 구조를 철저히 따라야 합니다
(Shared-Library Repository)
├── src/ # 복잡한 객체 지향 로직 및 클래스용 (src/org/foo/...)
├── vars/ # 파이프라인에서 직접 호출될 커스텀 함수(전역 변수) 모음
│ ├── standardPipeline.groovy
│ └── notifySlack.groovy
└── resources/ # 외부 템플릿 파일, json, yaml 등 일반 정적 파일
이 중 실무 파이프라인 공통화 작업에서 90% 이상 활용되는 핵심 폴더는 쉽게 함수를 꺼내 쓰는 vars/ 폴더입니다
로직 공통화 구현 흐름
애플리케이션 쪽 빌드 파이프라인에 있던 복잡한 설정 코드 덩어리를 뜯어내서, Shared Library 레포지토리의 커스텀 함수로 밀어넣는 흐름을 시각화해보겠습니다
flowchart LR
subgraph app["App Repo (수백 개)"]
JF["Jenkinsfile"]
end
subgraph lib["Shared Library Repo"]
VAR["vars/standardPipeline.groovy"]
end
subgraph jenkins["Jenkins Controller"]
RUN["Pipeline Execution"]
end
JF -.->|1. 라이브러리 Import| jenkins
jenkins -.->|2. 코드 Fetch| VAR
VAR -.->|3. 템플릿 매핑| RUN
JF -.->|4. 필수 파라미터 전달| RUN
classDef primary fill:#2563eb,stroke:#1e40af,color:#ffffff
classDef info fill:#0891b2,stroke:#0e7490,color:#ffffff
classDef neutral fill:#475569,stroke:#334155,color:#ffffff
class JF info
class VAR primary
class jenkins,RUN neutral
실전 작성 예시: 글로벌 알림 함수 분리하기
배포 성공 여부를 확인해 슬랙 알림을 보내는 코드를 공통 라이브러리로 분리해보겠습니다. 라이브러리 레포지토리 쪽 vars/notifySlack.groovy 파일을 다음과 같이 만듭니다
// Shared Library Repo: vars/notifySlack.groovy 위치
def call(String status) {
if (status == 'SUCCESS') {
slackSend(color: 'good', message: "빌드 성공: ${env.JOB_NAME}")
} else {
slackSend(color: 'danger', message: "빌드 실패: ${env.JOB_NAME}")
}
}
이제 실제 애플리케이션의 Jenkinsfile에서는 다음처럼 한 줄로 깔끔하게 호출만 하면 끝납니다. 핵심은 최상단의 @Library 어노테이션입니다
// App Repo: 프로젝트 최상단 Jenkinsfile
@Library('my-company-library@main') _
pipeline {
agent any
stages {
stage('Deploy') { steps { echo '배포 중...' } }
}
post {
always { notifySlack(currentBuild.currentResult) }
}
}
@Library('이름') _ 끝에 붙는 언더스코어 문자는 사실 단순한 오타나 장황한 여백 기호가 아닙니다. Groovy 컴파일러 문법상 어노테이션 뒤에는 문맥적으로 반드시 '가리킬 대상 클래스나 메서드' 본체가 와야 하는데, Declarative Script 환경상 그 시작점에 둘 코드가 애매하므로 문법 에러를 방지하기 위해 더미 문자 _ 를 집어넣는 약속된 우회 트릭이랍니다
한 걸음 더: 파이프라인 전체를 통째로 모듈화하기
알림 같은 특정 단위 액션뿐만 아니라, 앞선 포스트에서 본 것 같은 거대한 pipeline 골격 전체를 아예 vars/standardPipeline.groovy 템플릿으로 박아버릴 수도 있습니다
이 방식을 쓰면 애플리케이션 개발팀의 Jenkinsfile은 궁극적으로 다음 단어 몇 줄로 극도로 단순해집니다.
@Library('my-company-library') _
// 중앙에서 정의해 둔 파이프라인 명세 함수 호출
standardPipeline {
appName = "user-service"
framework = "spring-boot"
nodeVersion = "17"
deployTarget = "k8s-prod"
}
이제 일선 개발팀은 Docker 실행 환경 셋업, 타임아웃 방어로직, 예외 처리 트러블슈팅을 전혀 몰라도 됩니다! 중앙에서 통제하는 템플릿의 명세서 양식만 채우면 가장 안전한 베스트 프랙티스로 자동 배포할 수 있습니다
정리
| 역할 | 핵심 요약 |
|---|---|
| 목적 | 전체 조직의 파이프라인 로직 파편화 방지 및 보안/규격 정책의 중앙 일괄 제어 |
| 원리 | @Library 어노테이션을 통해 별도의 관리를 받는 Git 레포지토리 로직을 동적으로 파이프라인에 Import |
| 이점 | 애플리케이션 팀의 복잡한 Groovy 코드를 대거 소거하여 러닝커브와 부담을 줄이고 비즈니스 코드 개발 생산성 증대 기여 |
다음 글에서는 공통화라는 강력하고 편리한 무기까지 쥐어본 우리가 엔터프라이즈 환경에서 필수적으로 부딪히게 될 거대한 트래픽 한계 벽, 인프라 운영 측면에서의 대규모 분산 환경 Jenkins 스케일링 전략을 시리즈의 마지막으로 정리해 보겠습니다