Cloud Modernization 08

CI/CD 파이프라인: 코드 변경을 서비스 배포까지 연결하는 법

CI와 CD가 둘 다 자동화라서 빌드, 테스트, 릴리스, 배포, 모니터링 중 무엇을 맡는지 섞인다.

근거 · 교안 p27-p29

CI/CD 파이프라인이 코드 변경을 서비스 배포까지 연결하는 방식 ?? ???

1장: CI/CD, 그 모호한 ‘자동화’의 경계

1. CI/CD, 그 모호한 ‘자동화’의 경계

솔라는 노트북 화면에 떠 있는 한 문장을 노려보았다. 방금 스터디 자료에서 발견한 문장이었다.

“CI/CD는 Application 개발부터 배포까지의 모든 단계를 자동화를 통해 효율적이고 빠르게 최종 사용자에게 서비스할 수 있도록 만드는 파이프라인이다.”

분명 모든 단어가 아는 단어인데, 문장은 머릿속에서 겉돌기만 했다. ‘자동화된 파이프라인.’ 너무 크고 막연한 말이었다. 솔라는 미간을 찌푸리며 거실에 있는 언니 루나에게 외치듯 물었다.

“언니! 나 CI/CD 때문에 머리가 터질 것 같아.”

소파에서 조용히 책을 읽던 루나가 고개를 들었다.

“왜? 어떤 부분이?”

“CI랑 CD 둘 다 ‘자동화’라는 건 알겠어. 그런데 빌드, 테스트, 릴리스, 배포, 모니터링… 이 모든 게 그냥 ‘자동화’라는 말로 뭉뚱그려져 있으니까, 도대체 CI가 어디까지고 CD가 어디부터인지 모르겠어. 역할이 다 섞여서 하나처럼 보여.”

솔라는 자포자기한 심정으로 덧붙였다. “결국 그냥 다 자동으로 해준다는 거 아냐? 왜 굳이 CI, CD 나눠서 부르는 거야?”

루나는 책을 덮고 잠시 생각하더니, 솔라를 불렀다.

“솔라, 이리 와봐. 정의는 잠시 잊고, 우리가 직접 한번 해보자.”

루나는 테이블 위에 포스트잇 한 묶음과 펜을 올려놓았다.

“네가 방금 버그 하나를 고친 개발자라고 상상해 봐. 그 코드 변경 사항이 사용자들한테 전달되기까지 어떤 과정을 거쳐야 할까?”

뜬금없는 역할극이었지만, 막연한 정의보다는 나았다. 솔라는 잠시 고민하다 입을 열었다.

“음… 일단 내가 고친 코드를 저장소에 올려야지. 다른 사람들이 작업한 거랑 합쳐야 하니까.”

루나는 말없이 포스트잇 한 장에 ‘Code’라고 적어 테이블에 붙였다.

“그 다음은?”

“합친 코드가 문제없이 돌아가는지 확인해야지. 일단 컴퓨터가 실행할 수 있는 파일로 만들어야 하고… 이걸 ‘빌드(Build)‘라고 하던가? 그리고 내가 고친 것 때문에 다른 기능이 망가지진 않았는지 ‘테스트(Test)‘도 해봐야 해.”

루나는 ‘Code’ 포스트잇 옆에 ‘Build’와 ‘Test’를 차례로 붙이고 화살표로 이었다. 어느새 작은 흐름이 만들어지고 있었다.

“좋아. 테스트까지 통과했어. 그럼 이제?”

“이제 사용자들한테 보내줄 준비가 된 거네? 이걸 ‘릴리스(Release)‘라고 부르나? 그리고 실제로 서버에 딱 올려서 사용자들이 쓸 수 있게 만드는 게 ‘배포(Deploy)’.”

‘Release’와 ‘Deploy’ 포스트잇이 뒤를 이었다.

“마지막으로 배포한 다음에 서비스가 잘 돌아가는지 계속 지켜봐야지. ‘모니터링(Monitor)’.”

루나가 마지막 ‘Monitor’ 포스트잇을 붙이자, 테이블 위에는 코드 변경부터 서비스 운영까지의 긴 여정이 한눈에 들어오는 파이프라인이 완성되었다.

Code → Build → Test → Release → Deploy → Monitor

“바로 이거야.” 루나가 포스트잇으로 만들어진 길을 가리키며 말했다. “이 전체 흐름이 네가 아까 봤던 ‘파이프라인’이야. 그럼 다시 원래 질문으로 돌아가 볼까? CI와 CD는 이 파이프라인의 어디부터 어디까지를 책임지는 걸까?”

솔라는 포스트잇들을 뚫어지라 쳐다보았다. 막연하게 ‘자동화’라고만 생각했던 과정이 단계별로 나뉘어 눈앞에 펼쳐지자, 이전에는 보이지 않던 경계가 보이기 시작했다.

“아…”

솔라의 입에서 나직한 감탄사가 흘러나왔다.

“코드를 고치고, 다른 사람 코드랑 합치고, 이게 잘 돌아가는지 빌드하고 테스트하는 것까지. 여기까지가 한 묶음 같아. 새로운 코드 조각들을 계속해서 기존 코드에 ‘통합’하면서 ‘검증’하는 과정이니까.”

솔라는 손가락으로 ‘Code’부터 ‘Test’까지를 쓱 그었다.

루나가 그 부분을 가리키며 고개를 끄덕였다. “맞아. 그게 바로 ‘지속적 통합(Continuous Integration)’, CI야. 여러 개발자의 코드 변경 사항을 주기적으로 통합하고, 빌드와 테스트를 자동화해서 통합으로 인한 문제를 최대한 빨리 찾아내는 과정이지.”

“아! 그럼 CI의 핵심은 ‘통합’과 ‘검증’이었구나! 빌드랑 테스트는 그 통합된 코드가 괜찮은지 검증하는 자동화된 수단이었던 거고.”

이제야 CI라는 약자가 제자리를 찾은 느낌이었다. 솔라는 뿌듯한 표정으로 파이프라인을 바라보았다. 전체를 뭉뚱그려 보았을 땐 보이지 않던 역할이, 파이프라인을 펼쳐놓으니 명확하게 드러났다.

“그럼… 테스트까지 무사히 통과한 이 ‘검증된 코드’를 사용자에게 전달하는 나머지 과정이 CD겠네?”

솔라는 ‘Release’와 ‘Deploy’ 포스트잇을 가리켰다. 하지만 이내 새로운 궁금증이 고개를 들었다.

“그런데 언니, CI가 ‘지속적 통합’인 건 이제 알겠어. 그럼 CD는 왜 그냥 ‘배포’가 아니라 ‘지속적 배포’라고 부르는 거야? 그리고 릴리스랑 배포는 엄밀히 말하면 뭐가 다른 거지? CI가 끝난 다음부터는 그냥 쭉 자동으로 가면 되는 거 아니었어?”

솔라는 방금 이해한 CI 영역 너머, 아직 미지의 영역으로 남은 CD의 세계를 바라보며 질문을 던졌다. CI와 CD의 경계는 찾았지만, 그 경계 너머의 땅은 여전히 안개에 싸여 있었다.

2장: CI: 잦은 병합으로 문제를 조기에 찾아내다

루나는 말없이 테이블 위에 놓인 포스트잇 파이프라인을 바라보았다. 잠시 후, 솔라가 CI의 영역이라고 선 그었던 ‘Code’, ‘Build’, ‘Test’ 포스트잇을 떼어내 한쪽에 모아두었다. 그러고는 펜꽂이에서 펜 두 자루를 꺼내, 솔라가 상상 속에서 앉아있을 법한 자리 양옆에 하나씩 놓았다.

이 갑작스러운 무대 장치 변경에 솔라의 눈이 동그래졌다. 이제 테이블 위에는 개발자 한 명이 아니라, 마치 세 명이 나란히 앉아있는 듯한 구도가 만들어졌다. 루나가 만든 이 새로운 풍경이 무엇을 의미하는지, 솔라는 선뜻 짐작할 수 없었다.

“언니, 이건 갑자기 왜?”

솔라가 묻자, 루나는 질문 대신 질문으로 답했다.

“방금 전까지 우리는 ‘한 명의 개발자’가 만든 코드가 여행하는 과정을 따라갔어. 하지만 실제 프로젝트는 혼자 하지 않지. 여기 솔라, 그리고 두 명의 동료 개발자가 더 있다고 상상해 보자. 세 사람이 같은 프로젝트를 진행하는 거야.”

“음… 팀 프로젝트구나.”

“맞아. 자, 여기 ‘중앙 저장소’가 있어.” 루나는 테이블 한가운데에 빈 컵을 가져다 놓았다. “세 사람 모두 이 중앙 저장소의 코드를 각자 컴퓨터로 복사해서 작업을 시작해. 솔라는 버그를 수정하고, 다른 한 명은 새로운 기능을 만들고, 나머지 한 명은 코드 구조를 개선하는 작업을 맡았다고 해보자.”

솔라는 고개를 끄덕였다. 여기까지는 익숙한 협업 풍경이었다.

“만약 CI, 즉 ‘지속적 통합’이 없다면 어떻게 될까? 세 명 모두 일주일 동안 열심히 자기 맡은 부분만 작업하는 거야. 서로의 코드는 전혀 보지 않고.”

루나의 말에 솔라는 상상하기 시작했다. 일주일간 로그인 페이지의 버그를 완벽하게 잡은 나. 같은 기간, 회원가입 절차에 새로운 기능을 추가한 동료. 그리고 그 둘이 사용하는 회원 정보 시스템의 구조를 완전히 뜯어고친 또 다른 동료.

일주일 뒤, 드디어 각자의 작업을 중앙 저장소에 합치려는 순간.

“아…”

솔라의 입에서 얕은 탄식이 터져 나왔다. 상상만으로도 머리가 지끈거렸다.

“완전 재앙이겠네. 내가 수정한 로그인 코드는 동료가 바꿔버린 회원 시스템 때문에 먹통이 될 거고, 새로운 기능을 추가한 동료의 코드도 마찬가지일 거야. 서로 자기가 만든 건 문제가 없다고 우기면서, 누구 코드 때문에 전체가 망가졌는지 원인을 찾느라 며칠 밤을 새우게 되겠지. 이게 바로 ‘통합 지옥’이라는 거구나.”

솔라는 그제야 깨달았다. 자신이 ‘통합’이라는 단어를 얼마나 얕게 이해하고 있었는지를. 단순히 여러 코드를 합치는 기술적인 행위라고만 생각했다. 하지만 루나가 보여준 상상 속의 재앙은 ‘통합’이 단순한 합치기가 아니라, 협업의 성패를 좌우하는 핵심적인 과정이라는 사실을 명확히 보여주었다.

루나는 말없이 컵 주변에 흩어져 있던 세 개의 펜을 다시 컵 안으로 하나씩 넣으며 말했다.

“바로 그거야. CI의 핵심은 그 ‘통합 지옥’을 피하는 데 있어. 일주일 동안 각자 숨어서 작업하다가 마지막에 한 번에 합치는 게 아니라, 아주 작은 변경이라도 생길 때마다 즉시 중앙 저장소에 합치고 테스트하는 거지.”

“매일, 아니 몇 시간에 한 번씩이라도?”

“응. 가능한 한 자주. 내가 고친 작은 코드 조각 하나를 중앙 저장소에 통합(Integrate)하면, CI 서버가 즉시 알아채고 자동으로 빌드와 테스트를 수행해. 만약 내 코드가 다른 사람의 코드와 충돌을 일으키거나 예상치 못한 문제를 만들면, 나는 즉시 그 결과를 통보받아. 문제가 더 커지기 전에, 내가 방금 뭘 바꿨는지 생생하게 기억하고 있을 때 바로 해결할 수 있는 거야.”

루나는 CI 영역으로 묶어두었던 ‘Code’, ‘Build’, ‘Test’ 포스트잇을 다시 가리켰다.

“CI(Continuous Integration)는 개발자가 작은 단위로 코드를 통합하고, 이를 자동으로 빌드와 테스트하는 프로세스다. 이 문장에서 핵심은 ‘자동으로 빌드와 테스트’가 아니라, ‘작은 단위로 자주 통합한다’는 원칙 그 자체야. 자동화는 그 원칙을 고통 없이 지키기 위한 수단일 뿐이고.”

설명을 듣고 나니, 아까는 그냥 지나쳤던 ‘작은 단위’라는 표현과 ‘주기적으로 빈번하게’라는 말이 전혀 다른 무게로 다가왔다. 그것은 단순한 권장 사항이 아니라, 복잡한 현대 소프트웨어 개발에서 팀을 ‘통합 지옥’으로부터 구해내는 핵심적인 전략, 즉 ‘지속적 통합 원칙’이었던 것이다.

“알겠다. CI는 단순히 코드를 검증하는 자동화 시스템이 아니라, 팀 전체가 안전하고 빠르게 나아가기 위한 협업 규칙이었네. 문제가 생기면 나중에 한꺼번에 수습하는 게 아니라, 생기는 즉시, 작은 단위일 때 바로바로 해결하는 거구나.”

솔라는 이제 CI 파이프라인이 왜 ‘Code’, ‘Build’, ‘Test’까지 묶이는지 명확히 이해했다. 코드 변경이 통합될 때마다 즉시 검증해서 그 안정성을 ‘지속적으로’ 보장하는 것. 그것이 CI의 역할이었다.

뿌듯함도 잠시, 솔라의 시선은 자연스럽게 파이프라인의 다음 단계로 옮겨갔다. CI를 통과한, 이제는 ‘안정성이 검증된’ 코드 조각들이 놓여있는 그 너머의 땅.

“좋아. 그럼 이제 CI 덕분에 우리 팀의 코드는 언제나 건강한 상태를 유지하게 됐어. 그럼 이 검증된 코드를 사용자에게 전달하는 건… CD의 몫인가? 언니가 아까 CD는 그냥 ‘배포’가 아니라 ‘지속적 배포’라고 했잖아. 이건 또 뭐가 다른 거야? 릴리스랑 배포는 그냥 버튼 하나 누르면 자동으로 되는 거 아니었어?“

3장: CD: 검증된 코드를 사용자에게 전달하기

솔라의 시선은 CI의 영역을 지나, 파이프라인의 다음 단계인 ‘Release’와 ‘Deploy’ 포스트잇에 머물러 있었다. CI를 통해 안정성이 검증된 코드 뭉치. 이제 이것을 사용자에게 전달하는 여정만이 남았다. 하지만 솔라의 마음속에는 풀리지 않은 매듭이 있었다. ‘지속적 배포’라는 말과, 그저 ‘버튼 하나 누르는’ 과정처럼 보였던 릴리스와 배포의 차이.

그때, 루나가 테이블 위 파이프라인에 조용히 손을 뻗었다. 언니는 ‘Release’와 ‘Deploy’ 포스트잇 사이에 작은 장난감 스위치를 하나 가져다 놓았다. 마치 두 단계 사이에 작은 관문이 생긴 것 같았다. 이전까지는 화살표로 매끄럽게 이어져 있던 흐름이 그 작은 스위치 하나로 인해 잠시 멈칫하는 느낌을 주었다. 이 갑작스러운 단절이 솔라의 마지막 질문에 대한 답을 담고 있는 듯했다.

“CI를 통과한, 이제는 건강하고 믿을 수 있는 코드야.” 루나가 CI의 마지막 단계인 ‘Test’ 포스트잇을 툭 치며 말했다. “이걸 사용자에게 전달하는 과정을 상상해 보자. 예전에는 이 모든 과정을 사람이 직접 했어.”

루나는 ‘Release’와 ‘Deploy’ 포스트잇을 가리켰다.

“개발팀이 몇 주에 한 번, 날을 잡아서 테스트를 통과한 코드들을 모아. 그리고 수십 페이지짜리 배포 설명서를 보면서 밤새도록 서버에 하나하나 명령어를 입력하지. 그러다 작은 실수라도 하면 서비스 전체가 멈추고, 사용자들은 오류 화면을 봐야 했어. 끔찍한 일이지.”

상상만 해도 피곤한 풍경이었다. 솔라는 고개를 저었다. “그 과정이 너무 힘들고 위험하니까 CD라는 자동화가 필요한 거구나.”

“맞아. CD, 즉 ‘지속적 전달(Continuous Delivery)’ 또는 ‘지속적 배포(Continuous Deployment)‘는 바로 그 고통스러운 과정을 자동화하는 거야.” 루나는 ‘Release’ 포스트잇을 가리켰다. “CI가 끝난 코드를 가져다가, 실제 서버에 배포할 수 있는 완벽한 패키지로 만드는 ‘릴리스’ 과정을 자동화해. 여기까지는 대부분의 CD가 공통적으로 하는 일이야.”

“그럼… 이 스위치는 뭐야?” 솔라는 마침내 핵심을 찔렀다. “릴리스까지 자동으로 준비됐으면, 바로 배포하면 되는 거 아니었어?”

루나는 스위치를 톡 건드리며 말했다. “바로 그 지점에서 ‘지속적 전달(Delivery)‘과 ‘지속적 배포(Deployment)‘가 갈라져.”

루나는 스위치를 ‘OFF’ 상태로 두었다. “CI를 통과한 코드가 자동으로 릴리스 패키지까지 만들어졌어. 이제 버튼 하나만 누르면 언제든 사용자에게 배포할 수 있는 ‘준비’가 끝난 상태지. 하지만 실제 배포는 이 스위치를 누르는 사람의 결정에 달려있어. 사업적인 판단에 따라 신기능 출시일을 조절하거나, 마케팅 캠페인에 맞춰 배포할 수도 있겠지. 이렇게, 배포 가능한 릴리스를 지속적으로 제공하되, 실제 배포는 수동으로 제어하는 것. 이게 ‘지속적 전달(Continuous Delivery)‘이야.”

솔라는 ‘아’ 하는 표정으로 스위치를 바라보았다. Delivery. ‘전달’이라는 단어의 의미가 새롭게 와닿았다. 택배 기사가 문 앞까지 물건을 가져다주었지만, 문을 열고 받는 것은 집주인의 몫인 것처럼.

“그럼…” 솔라가 말을 이었다. “그 스위치를 아예 없애버리고, 릴리스가 준비되자마자 자동으로 배포까지 해버리는 건…”

“그게 바로 ‘지속적 배포(Continuous Deployment)‘지.”

루나는 스위치를 치워버리고 ‘Release’와 ‘Deploy’를 다시 화살표로 직접 이었다. 이제 파이프라인은 CI의 끝에서부터 사용자를 만나는 마지막 순간까지 아무런 막힘이 없었다.

“CI의 모든 자동화 테스트를 통과한 코드 변경 사항은, 사람의 개입 없이 즉시 운영 서버에 배포돼. 하루에도 몇 번씩, 심지어 수십 번씩. 개발자가 코드를 중앙 저장소에 통합하는 순간, 그 변경 사항이 몇 분 안에 실제 사용자에게 영향을 미치는 거야. 엄청난 속도지만, 그만큼 CI 단계의 테스트가 아주 튼튼해야 한다는 신뢰가 깔려 있어야 가능해.”

이제야 모든 조각이 맞춰졌다. CD는 단순히 ‘자동 배포’가 아니었다. ‘어디까지 자동화할 것인가’에 대한 팀의 정책이자 선택이었다. 솔라는 고개를 끄덕이며 중얼거렸다.

“CD(Continuous Delivery, Deployment)는 코드 변경 사항을 자동으로 준비하고, 필요 시 자동 배포까지 이어지는 과정이다… 이제야 이 문장이 제대로 이해돼. ‘필요 시’라는 말의 의미가 바로 그 스위치의 유무였구나.”

CI가 ‘통합 지옥’에서 팀을 구출하는 협업 규칙이었다면, CD는 검증된 가치를 사용자에게 ‘얼마나 빠르고 안전하게’ 전달할 것인지에 대한 전략이었다.

“그럼 나도 이제 파이프라인을 진단할 수 있겠는데?”

자신감이 붙은 솔라가 말했다. 루나는 기다렸다는 듯 솔라 앞에 새로운 시나리오가 적힌 메모지를 내밀었다.

새 기능 개발팀: Code → Build → Test (CI 통과) → [QA팀 수동 승인] → Release → [매주 금요일 오후 4시, 일괄 Deploy]

“이 팀의 한 개발자가 ‘내 코드가 테스트를 통과했는데 왜 아직도 서비스에 반영이 안 됐는지 모르겠다’고 불평하고 있어. 이 파이프라인을 보면, 뭐가 문제일까? CI가 문제일까, CD가 문제일까?”

솔라는 메모지를 유심히 들여다보았다. 예전 같았으면 그저 ‘자동화가 느린가 보네’라고 뭉뚱그려 생각했을 것이다. 하지만 이제는 파이프라인의 각 단계가 가진 역할과 경계가 명확하게 보였다.

“CI는 문제가 아니야. ‘Test’까지는 통과했으니까. 문제는 CD 단계에 있어. 이 팀은 ‘지속적 배포’가 아니라 ‘지속적 전달’ 방식을 쓰고 있네. 그것도 두 개의 수동 관문이 있어. QA팀이 승인해줘야 하고, 배포도 매주 금요일에만 하니까. 개발자 입장에서는 코드가 CI를 통과한 순간 일이 끝났다고 생각하겠지만, 사용자를 만나기까지는 한참 기다려야 하는 구조인 거지. 이건 기술의 문제가 아니라 정책의 문제네.”

솔라는 파이프라인의 ‘QA팀 수동 승인’과 ‘금요일 일괄 Deploy’ 부분을 손가락으로 짚으며 확신에 찬 목소리로 말했다. 더 이상 CI와 CD는 뒤섞인 자동화 덩어리가 아니었다. 각자의 역할을 가진, 명확히 구분된 두 개의 엔진이었다. 그리고 솔라는 이제 그 엔진들이 어떻게 서로 맞물려 돌아가는지, 그리고 어디서 삐걱거리는지 진단할 수 있는 렌즈를 갖게 되었다.