|
| 1 | +# Zuul의 추측 기반 병합(Speculative Merge)과 Cross-project 의존성(Dependencies) 정리본 |
| 2 | + |
| 3 | +------------------------------------------------------------------------ |
| 4 | + |
| 5 | +## 0. 들어가며 |
| 6 | + |
| 7 | +최근 **Zuul 프로젝트의 `concepts.po` 파일을 한글로 번역**하는 컨트리뷰션 |
| 8 | +작업을 진행하면서, 'Speculative Merge(추측 기반 병합)'라는 용어를 접하게 |
| 9 | +되었습니다. |
| 10 | + |
| 11 | +처음에는 단순히 "가상 병합" 정도로 이해했지만, 이 개념이 실제로 어떻게 |
| 12 | +동작하며 왜 대규모 협업 환경에서 중요한지 정확히 이해하고 싶어 추가로 |
| 13 | +학습하게 되었습니다. |
| 14 | + |
| 15 | +특히 실습을 진행하는 과정에서\ |
| 16 | +**Zuul Hands-on Part 6: Cross-project Dependencies** (https://www.softwarefactory-project.io/zuul-hands-on-part-6-cross-project-dependencies.html) 문서를 참고하여\ |
| 17 | +추측 기반 병합(Speculative Merge)와 Cross-project 의존성(Dependencies) 기능을 직접 실험해 보았습니다. |
| 18 | + |
| 19 | +------------------------------------------------------------------------ |
| 20 | + |
| 21 | +## 1. 기존 CI/CD 도구(Jenkins)의 한계 |
| 22 | + |
| 23 | +전통적인 Jenkins 기반 CI는 주로 **Post-merge 방식**으로 동작합니다. |
| 24 | + |
| 25 | +즉, 코드가 `main` 브랜치에 병합된 이후 테스트가 수행됩니다. |
| 26 | + |
| 27 | +### 문제 상황 |
| 28 | + |
| 29 | +- 개발자 A와 B가 각각 테스트를 통과 |
| 30 | +- 두 패치가 함께 존재할 때 충돌 발생 |
| 31 | +- main 브랜치가 **Broken State** 상태가 됨 |
| 32 | + |
| 33 | +### 결과 |
| 34 | + |
| 35 | +- 빌드 복구 전까지 전체 팀 생산성 저하 |
| 36 | +- 병합 이후 문제 발견 → 롤백 비용 증가 |
| 37 | + |
| 38 | +------------------------------------------------------------------------ |
| 39 | + |
| 40 | +## 2. Zuul의 Speculative Merge |
| 41 | + |
| 42 | +Zuul은 **Pre-merge Gating 모델**을 사용합니다. |
| 43 | + |
| 44 | +> 변경 사항이 실제 병합되기 전에 모든 검증을 완료합니다. |
| 45 | +
|
| 46 | +### 핵심 아이디어 |
| 47 | + |
| 48 | +- 대기열(queue)에 있는 패치들이 이미 병합되었다고 가정 |
| 49 | +- 미래의 가상 상태를 시뮬레이션하여 테스트 수행 |
| 50 | + |
| 51 | +### 동작 예시 |
| 52 | + |
| 53 | +1. A 패치 테스트 |
| 54 | +2. B 패치 테스트 시 → A가 이미 병합되었다고 가정 |
| 55 | +3. C 패치 테스트 시 → A + B 병합 상태 가정 |
| 56 | + |
| 57 | +내부적으로는 다음과 같은 가상 병합을 수행합니다: |
| 58 | + |
| 59 | +``` bash |
| 60 | +git checkout main |
| 61 | +git merge A |
| 62 | +git merge B |
| 63 | +``` |
| 64 | + |
| 65 | +이 방식이 바로 **Speculative Merge**입니다. |
| 66 | + |
| 67 | +------------------------------------------------------------------------ |
| 68 | + |
| 69 | +## 3. Dependent vs Independent Pipeline |
| 70 | + |
| 71 | +### Independent Pipeline |
| 72 | + |
| 73 | +- 각 변경 사항을 독립적인 workspace에서 테스트 |
| 74 | +- 다른 패치와 상호 영향 없음 |
| 75 | + |
| 76 | +### Dependent Pipeline |
| 77 | + |
| 78 | +- 변경 사항을 큐에 묶어 순차적으로 speculative merge 수행 |
| 79 | +- 대규모 협업 환경에 적합 |
| 80 | + |
| 81 | +------------------------------------------------------------------------ |
| 82 | + |
| 83 | +## 4. Cross-project Dependencies |
| 84 | + |
| 85 | +여러 프로젝트가 서로 의존하는 경우, 단일 프로젝트 테스트만으로는 |
| 86 | +안정성을 보장할 수 없습니다. |
| 87 | + |
| 88 | +예를 들어, 라이브러리와 애플리케이션이 분리된 구조에서는 다음과 같은 문제가 발생할 수 있습니다. |
| 89 | +* 라이브러리 변경은 테스트를 통과 |
| 90 | +* 애플리케이션도 기존 버전 기준으로는 저앙 |
| 91 | +* 그러나 두 변경이 함께 존재하면 오류 발생 |
| 92 | + |
| 93 | +Zuul은 다음 두 가지 방식으로 이를 해결합니다: |
| 94 | + |
| 95 | +1. `required-projects` : 정적(구조적) 의존성 |
| 96 | +2. `Depends-On` : 동적(변경 단위) 의존성 |
| 97 | +구 기능은 목적과 동작 범위가 다릅니다. |
| 98 | +------------------------------------------------------------------------ |
| 99 | + |
| 100 | +## 5. required-projects 설정 예제 |
| 101 | +이제 Cross-project 테스트를 가능하게 만드는 실제 설정을 살펴보겠습니다. |
| 102 | + |
| 103 | +`.zuul.yaml` |
| 104 | + |
| 105 | +``` yaml |
| 106 | +- job: |
| 107 | + name: tox-demorepo |
| 108 | + parent: tox-py39 |
| 109 | + required-projects: |
| 110 | + - demo-lib |
| 111 | + - demo-repo |
| 112 | +``` |
| 113 | +
|
| 114 | +### 설명 |
| 115 | +
|
| 116 | +- demo-lib와 demo-repo를 동일 workspace에 체크아웃 |
| 117 | +- demo-lib 변경이 demo-repo 테스트에 반영됨 |
| 118 | +- 라이브러리 변경이 앱에 어떤 영향을 미치는지 병합 전에 확인 |
| 119 | +
|
| 120 | +즉, required-projects는 “어떤 프로젝트를 함께 테스트할 것인가”를 정의합니다. |
| 121 | +
|
| 122 | +------------------------------------------------------------------------ |
| 123 | +
|
| 124 | +## 6. 실습 예제 구성 |
| 125 | +구조를 단순화한 예제를 통해 동작 방식을 살펴보겠습니다 |
| 126 | +
|
| 127 | +### demo-lib |
| 128 | +
|
| 129 | +``` python |
| 130 | +# demo_lib/math_utils.py |
| 131 | + |
| 132 | +def add(a, b): |
| 133 | + return a + b |
| 134 | +``` |
| 135 | +
|
| 136 | +### demo-repo |
| 137 | +
|
| 138 | +``` python |
| 139 | +# app.py |
| 140 | + |
| 141 | +from demo_lib.math_utils import add |
| 142 | + |
| 143 | +def calculate(): |
| 144 | + return add(3, 4) |
| 145 | +``` |
| 146 | +
|
| 147 | +### 기본 Zuul 설정 |
| 148 | +
|
| 149 | +``` yaml |
| 150 | +- project: |
| 151 | + check: |
| 152 | + jobs: |
| 153 | + - tox-py39 |
| 154 | + gate: |
| 155 | + jobs: |
| 156 | + - tox-py39 |
| 157 | +``` |
| 158 | +
|
| 159 | +------------------------------------------------------------------------ |
| 160 | +
|
| 161 | +## 7. 실패 시나리오 |
| 162 | +그렇다면 required-projects 설정이 없으면 어떤 문제가 발생할까요? |
| 163 | +
|
| 164 | +### demo-lib 변경 |
| 165 | +
|
| 166 | +``` python |
| 167 | +def add(a, b): |
| 168 | + return str(a + b) |
| 169 | +``` |
| 170 | +라이브러리 단독 테스트는 통과할 수 있습니다. |
| 171 | +
|
| 172 | +### demo-repo 코드 |
| 173 | +그러나 demo-repo에서는: |
| 174 | +
|
| 175 | +``` python |
| 176 | +result = add(3, 4) |
| 177 | +print(result + 1) |
| 178 | +``` |
| 179 | + |
| 180 | +### 결과 |
| 181 | + |
| 182 | + TypeError 발생 |
| 183 | + |
| 184 | +이 경우: |
| 185 | + |
| 186 | +* 라이브러리는 통과 |
| 187 | + |
| 188 | +* 애플리케이션도 기존 기준으로는 통과 |
| 189 | + |
| 190 | +* 하지만 함께 배포되면 장애 발생 |
| 191 | + |
| 192 | +required-projects가 있다면, |
| 193 | + |
| 194 | + 라이브러리 변경을 적용한 상태로 애플리케이션 테스트를 먼저 수행 |
| 195 | + |
| 196 | +하여 병합 전에 문제를 차단합니다. |
| 197 | + |
| 198 | +------------------------------------------------------------------------ |
| 199 | + |
| 200 | +## 8. Depends-On 사용 예제 |
| 201 | +한 단계 더 나아가 보겠습니다. |
| 202 | + |
| 203 | +이번에는 라이브러리와 애플리케이션을 동시에 수정하는 상황입니다. |
| 204 | + |
| 205 | +### 상황 |
| 206 | + |
| 207 | +- demo-lib에 `multiply` 함수 추가 |
| 208 | +- demo-repo에서 해당 함수 사용 |
| 209 | +- 두 변경 모두 아직 merge되지 않음 |
| 210 | + |
| 211 | +이때 단순히 require-projects만으로는 부족할 수 있습니다. |
| 212 | + |
| 213 | +왜냐하면: |
| 214 | +- 라이브러리의 특정 변경과 |
| 215 | +- 애플리케이션의 특정 변경을 |
| 216 | +- 하나의 논리적 변경 세트로 묶어야 하기 때문입니다. |
| 217 | +### demo-repo 커밋 메시지 |
| 218 | + |
| 219 | + Depends-On: https://review.example.com/c/demo-lib/+/12345 |
| 220 | + |
| 221 | +### Zuul 동작 |
| 222 | + |
| 223 | +1. demo-lib의 해당 변경 사항 추측 기반 병합 |
| 224 | +2. 그 위에 demo-repo 변경 사항 추측 기반 병합 |
| 225 | +3. 통합 테스트 실행 |
| 226 | + |
| 227 | +즉, Depends-On은 “이번 변경이 어떤 변경 위에서 동작해야 하는지”를 선언합니다. |
| 228 | + |
| 229 | +### 정리 |
| 230 | + |
| 231 | +- `required-projects`는 프로젝트 단위 |
| 232 | + |
| 233 | +- `Depends-On`은 변경 사항 단위 |
| 234 | + |
| 235 | +- 전자는 구조적 의존성 |
| 236 | + |
| 237 | +- 후자는 일시적, 동적 의존성 |
| 238 | + |
| 239 | +한 문장으로 정리하면: |
| 240 | + |
| 241 | + required-projects는 프로젝트를 묶고, |
| 242 | + Depends-On은 변경 사항을 묶습니다. |
| 243 | +------------------------------------------------------------------------ |
| 244 | + |
| 245 | +## 9. 결론 |
| 246 | + |
| 247 | +추측 기반 병합(Speculative Merge)는 단순한 가상 병합이 아닙니다. |
| 248 | + |
| 249 | +- 병합될 가능성이 있는 미래 상태를 먼저 검증하고 |
| 250 | + |
| 251 | +- 프로젝트 간 의존성을 사전에 확인하며 |
| 252 | + |
| 253 | +- 변경 간 관계까지 추적합니다. |
| 254 | + |
| 255 | + 이를 통해 `main` 브랜치는 항상 배포 가능한 상태를 유지합니다. |
| 256 | + |
| 257 | +대규모 협업 환경, 특히 멀티 레포 구조에서는 |
| 258 | + |
| 259 | +- required-projects로 구조를 묶고 |
| 260 | + |
| 261 | +- Depends-On으로 변경을 묶는 전략이 필수적입니다. |
| 262 | + |
| 263 | +------------------------------------------------------------------------ |
| 264 | + |
| 265 | +## 핵심 키워드 |
| 266 | + |
| 267 | +- Speculative Merge |
| 268 | +- Dependent Pipeline |
| 269 | +- Gate |
| 270 | +- required-projects |
| 271 | +- Depends-On |
| 272 | +- Cross-repo CI |
0 commit comments