CI/CD Practice 02

CodePipeline 파일 흐름 해부: Source S3에서 Production S3까지

파이프라인 생성 화면에서 source, deploy, artifact, 전환 비활성화 같은 말이 나오지만 실제 파일이 어디서 어디로 움직이는지 흐름이 선명하지 않다.

근거 · CI/CD 교안 p19-p35

CodePipeline 파일 흐름 해부: Source S3에서 Production S3까지 대표 이미지

1장: 파이프라인 생성: Source와 Deploy, 그리고 보이지 않는 ‘무엇’

솔라의 손가락이 노트북 트랙패드 위에서 잠시 멈췄다. 화면에는 AWS CodePipeline 생성 화면이 떠 있었다. 방금 읽은 가이드의 한 문장이 머릿속을 맴돌았다. “CodePipeline에서 Source 단계와 Deploy 단계를 구성해 정적 웹 사이트를 배포합니다.”

문장은 명쾌했다. 시작(Source)과 끝(Deploy). 솔라는 고개를 끄덕이며 자신 있게 각 단계를 설정하기 시작했다.

“언니, 이거 생각보다 간단한데? Source 단계에서는 내 소스 코드가 담긴 S3 버킷을 지정하고, Deploy 단계에서는 배포할 S3 버킷을 지정하면 끝나는 거 아냐?”

솔라가 화면을 가리키며 말했다. 그녀의 머릿속에는 명확한 그림이 그려지고 있었다. 소스 S3 버킷에 있는 test-website.zip 파일이 파이프라인을 타고 흘러가 최종 목적지인 운영 S3 버킷에 복사되는 그림. 마치 컨베이어 벨트의 한쪽 끝에서 물건을 올리면 다른 쪽 끝으로 그대로 옮겨지는 것처럼.

루나는 솔라의 어깨너머로 화면을 들여다보았다. 솔라의 손가락은 이미 Deploy 단계 설정을 향하고 있었다. 루나는 아무 말 없이 화면의 한 부분을 손가락으로 툭 쳤다. Source 단계 설정 영역의 아래쪽이었다.

“잠깐, 여기는 뭐라고 쓰여 있어?”

솔라는 루나의 손끝을 따라 시선을 옮겼다. ‘출력 아티팩트(Output artifacts)’라는 제목 아래, SourceArtifact라는 기본값이 입력된 텍스트 필드가 있었다.

“음… 출력 아티팩트? 그냥 SourceArtifact라고 되어있네. 이 단계의 결과물을 부르는 이름 같은 거겠지.”

솔라는 대수롭지 않게 여겼다. 곧이어 Deploy 단계 설정 화면에서도 비슷한 항목을 발견했다. ‘입력 아티팩트(Input artifacts)’. 그리고 그 필드에도 SourceArtifact라고 적혀 있었다.

“봐, 여기서도 똑같은 이름을 쓰잖아. Source 단계의 출력이 Deploy 단계의 입력이 된다는 걸 연결해주는 표시인가 봐. 그냥 이름표 같은 거 아닐까?” “이름표라… 한번 확인해볼까?”

루나가 조용히 제안했다. 솔라는 신나게 ‘파이프라인 생성’ 버튼을 눌렀다. 잠시 후, 두 단계로 이루어진 파이프라인이 화면에 나타났다. Source 단계는 초록색으로 ‘성공’ 표시가 떴고, 곧이어 Deploy 단계도 성공으로 바뀌었다.

“좋았어! 성공이네. 이제 운영 버킷에 파일이 잘 들어갔는지 확인해봐야지.”

솔라는 자신만만하게 S3 콘솔을 열어 운영용으로 지정했던 버킷을 클릭했다. 하지만 파일 목록은 텅 비어 있었다.

“어?”

솔라의 입에서 짧은 탄성이 터져 나왔다. 뭔가 잘못됐나 싶어 화면을 몇 번이고 새로고침했지만 결과는 같았다.

“왜 아무것도 없지? 파이프라인은 성공했는데… test-website.zip 파일이 여기로 복사되어야 하는 거 아니었어?”

솔라는 당황해서 소스 버킷도 확인해 보았다. 그곳에는 test-website.zip 파일이 원래 있던 그대로 남아 있었다. 파일이 이동한 흔적은 어디에도 없었다.

“파일이 복사된 게 아니야. 그렇다고 이동한 것도 아니고… 그럼 대체 Deploy 단계는 뭘 배포했다는 거지?”

혼란에 빠진 솔라의 시선이 다시 CodePipeline 화면으로 돌아갔다. 성공했다고 초록불을 띄운 파이프라인의 그림이 마치 자신을 놀리는 것처럼 보였다. Source에서 Deploy로 그어진 화살표가 전처럼 단순하게 느껴지지 않았다. 그저 시작과 끝을 잇는 선이 아니었다.

그때, 아까 무심코 지나쳤던 단어가 솔라의 눈에 다시 들어왔다.

‘Output artifacts’, ‘Input artifacts’. 그리고 그곳에 적힌 SourceArtifact라는 이름.

솔라는 마우스를 움직여 Source 단계의 성공 메시지 위로 가져갔다. 팝업 창에 출력 아티팩트의 위치 정보가 나타났다. Deploy 단계에서도 입력 아티팩트의 정보가 보였다. 두 정보는 정확히 일치했다.

“아…”

솔라는 그제야 깨달았다. Source 단계는 파일을 운영 버킷으로 ‘직접’ 복사하는 것이 아니었다. Source 단계는 소스 버킷에서 파일을 가져와 SourceArtifact라는 이름의 ‘무언가’를 만들어 어딘가에 두는 것으로 자기 역할을 끝낸다. 그리고 Deploy 단계는 운영 버킷을 쳐다보는 게 아니라, Source 단계가 만들어둔 바로 그 SourceArtifact를 가져다가 자기 일을 시작하는 것이었다.

컨베이어 벨트의 비유는 틀렸다. 이건 마치 택배 시스템과 비슷했다. 판매자(Source)가 물건을 구매자(Deploy)에게 바로 보내는 게 아니었다. 판매자는 물건을 ‘택배용 소포(Artifact)’로 포장해서 물류 센터에 보낸다. 그러면 택배 기사(Deploy)가 물류 센터에서 그 소포를 찾아 구매자에게 배달하는 것이다.

“언니, Source 버킷에서 Production 버킷으로 바로 가는 게 아니었어.”

솔라가 화면을 응시하며 말했다. 그녀의 목소리에는 이전의 자신감 대신 새로운 의문이 담겨 있었다.

“중간에… 우리가 모르는 단계가 숨어있어. 이 ‘아티팩트’라는 포장물이 전달되는 과정 말이야. 파이프라인은 성공했다고 하지만, 파일은 아직 내 눈에 보이지 않아. 그렇다면 질문은 이거네. 그 중간 저장소는 대체 어디고, SourceArtifact라는 것의 정체는 정확히 뭘까?”

2장: 아티팩트의 실체: CodePipeline의 숨겨진 S3 버킷

솔라는 자신의 AWS S3 콘솔 화면을 노려보고 있었다. 화면에는 그녀가 직접 만든 user-source-websiteuser-prod-website 두 개의 버킷이 선명하게 보였다. 하지만 그녀가 찾고 있는 것은 그 두 버킷이 아니었다. 전날 스스로 떠올렸던 택배 물류 센터, 그 ‘중간 저장소’를 찾고 있었다. 하지만 버킷 목록에는 익숙한 이름들 외에 낯설고 정체를 알 수 없는 버킷 몇 개만 보일 뿐이었다.

“분명 어딘가에 있을 텐데… CodePipeline이 사용하는 버킷이라면 codepipeline 같은 이름이 붙어있지 않을까?”

솔라는 필터 검색창에 codepipeline을 입력해 보았지만, 결과는 아무것도 나오지 않았다. 그녀는 한숨을 쉬며 필터를 지웠다. 마치 숨바꼭질하는 기분이었다. 파이프라인은 분명 SourceArtifact를 어딘가에 두었다고 했는데, 그 장소를 찾을 수가 없었다.

“아직도 물류 센터를 찾고 있구나.”

조용히 다가온 루나가 솔라의 노트북 화면을 들여다보며 말했다.

“응. 근데 보이지가 않아. 내가 만든 버킷도 아니고, 그렇다고 찾기 쉽게 이름이 붙어있는 것도 아닌 것 같아. ‘SourceArtifact’는 그냥 논리적인 이름일 뿐, 실제 파일은 없는 허상인가 하는 생각까지 들어.”

솔라의 말에 루나는 잠시 생각하더니, 파이프라인의 시각적인 흐름도 대신 다른 화면을 가리켰다.

“파이프라인의 각 단계를 보는 대신, 파이프라인 전체의 설정을 한번 확인해보는 건 어때? 택배 기사(Deploy)나 판매자(Source)가 아니라, 물류 센터(Artifact Store) 자체의 정보를 보는 거지.”

루나의 말에 솔라는 CodePipeline 화면으로 돌아가, 파이프라인 목록 옆의 ‘편집’ 버튼을 눌렀다. 익숙한 단계 편집 화면이 나타났다. 하지만 루나는 고개를 저으며 화면의 다른 곳을 가리켰다. 단계 설정이 아닌, 파이프라인 전체 설정 영역에 있는 ‘고급 설정’이었다.

솔라가 ‘고급 설정’을 클릭하자, 이전에는 무심코 지나쳤던 항목이 눈에 들어왔다.

아티팩트 스토어 (Artifact store)

  • 아티팩트 스토어: 기본 위치
  • 아티팩트 버킷: codepipeline-ap-northeast-2-123456789012

“이거…!”

솔라의 눈이 커졌다. 그녀가 찾던 정보가 바로 거기에 있었다. CodePipeline은 파이프라인을 생성할 때, 사용자가 특별히 지정하지 않으면 아티팩트를 보관할 S3 버킷을 ‘자동으로’ 생성해서 사용하고 있었던 것이다. 버킷 이름은 임의의 숫자가 붙어있어, 솔라는 그저 시스템이 생성한 의미 없는 버킷이라고 생각하고 지나쳤던 것이다.

솔라는 재빨리 S3 콘솔 화면으로 돌아가 버킷 목록을 다시 훑었다. 그리고 방금 화면에서 본, 길고 의미 없어 보이던 그 이름의 버킷을 찾아 클릭했다.

버킷 안은 텅 비어있지 않았다. 그녀가 생성한 파이프라인의 이름으로 된 폴더가 있었다. 솔라는 홀린 듯 폴더를 클릭했다. 그 안에는 SourceArtifact라는 이름의 폴더가 또 있었다.

마지막으로 그 폴더를 열자, 마침내 그토록 찾던 파일이 나타났다.

test-website.zip

“찾았다! 언니, 여기 있었어!”

솔라가 외쳤다. Source S3 버킷에 있던 원본 파일이 아니라, 파이프라인을 한번 통과하며 ‘아티팩트’라는 이름으로 포장된 결과물이 바로 이 숨겨진 버킷에 저장되어 있었던 것이다. ‘SourceArtifact’는 단순한 이름표나 논리적인 개념이 아니었다. 이 숨겨진 S3 버킷 안에, 파이프라인 이름과 아티팩트 이름으로 구성된 경로(pipeline-name/SourceArtifact/)에 실제로 저장된 파일 묶음을 가리키는, 명확한 주소였던 것이다.

“이제 알겠어. Source S3에서 Production S3로 바로 가는 게 아니었어. 모든 길은 이 ‘아티팩트 버킷’으로 통하는 거였어.”

솔라는 방금까지의 혼란이 말끔히 정리되는 것을 느꼈다. 파일의 여정이 명확하게 그려졌다.

  1. Source 단계: user-source-website 버킷에서 test-website.zip을 가져온다.
  2. 가져온 파일을 SourceArtifact라는 이름으로 포장하여, CodePipeline 전용 아티팩트 버킷(codepipeline-ap-northeast-2-...)에 저장한다.
  3. Deploy 단계: 아티팩트 버킷에 저장된 SourceArtifact(test-website.zip)를 가져온다.
  4. 가져온 아티팩트를 user-prod-website 버킷에 배포한다.

이제 모든 조각이 맞춰지는 것 같았다. 하지만 솔라의 얼굴에 다시 의문이 떠올랐다.

“그런데 언니, 이상한 점이 있어. Deploy 단계가 이 아티팩트 버킷에서 파일을 가져갔다는 것까지는 알겠어. 파이프라인도 성공이라고 나왔고. 그런데 왜 내 Production 버킷은 여전히 비어있는 거지?”

솔라가 Production S3 버킷(user-prod-website)의 텅 빈 화면을 가리키며 물었다.

“Deploy 단계는 아티팩트를 가져다가… 대체 뭘 한 걸까? 그냥 복사한 게 아니라면 말이야. 압축 파일이니까, 혹시 압축을 풀어서 넣어주거나 하는 뭔가 특별한 일을 하는 걸까?”

3장: Deploy 단계의 마법: 압축 해제와 웹사이트 공개

솔라의 노트북 화면에는 두 개의 브라우저 탭이 나란히 열려 있었다. 왼쪽 탭에는 파이프라인의 Deploy 단계가 ‘성공’했다고 선언하는 초록색 체크 표시가, 오른쪽 탭에는 user-prod-website 버킷의 텅 빈 파일 목록이 보였다. 성공과 텅 빔. 명백한 모순이었다.

솔라는 마른침을 삼키고 왼쪽 탭으로 돌아가, 파이프라인의 ‘편집’ 버튼을 다시 눌렀다. 이전에는 소스 버킷과 아티팩트 버킷 사이의 경로를 추적하는 데 집중했다면, 이제 그녀의 시선은 오직 한 곳, Deploy 단계의 설정 화면에 고정되어 있었다. 그곳에는 지난번 무심코 지나쳤던 몇 가지 설정값들이 자리 잡고 있었다.

“그냥 복사한 게 아니라면… 대체 뭘 한 걸까?”

솔라는 혼잣말을 하며 마우스 커서를 움직였다. 그녀의 질문은 허공을 향한 것이었지만, 답을 찾으려는 눈은 화면의 작은 체크박스들을 샅샅이 훑고 있었다. ‘배포 공급자’는 S3로 잘 되어 있고, ‘버킷 이름’도 user-prod-website로 정확했다. 그런데 그 아래, 추가 옵션들이 있었다.

“언니, 여기 좀 봐봐. 배포하기 전에 파일 압축 풀기라는 옵션이 있어.”

솔라가 화면을 가리키며 말했다. 그녀의 목소리에는 희미한 기대감이 섞여 있었다. 마치 잠긴 문 앞에서 맞는 열쇠를 찾았을지도 모른다는 예감 같았다.

“지난번에 우리가 아티팩트 버킷에서 찾은 건 test-website.zip 파일이었잖아. 압축 파일. Deploy 단계는 이 압축 파일을 가져가긴 했는데, 내가 아무런 지시도 내리지 않으니까… 그냥 아무것도 안 한 게 아닐까? ‘배포’라는 작업에 ‘압축 해제’가 포함될 거라고 내가 너무 당연하게 생각했던 거야.”

“웹사이트를 배포한다는 건, 방문자가 최종적으로 무엇을 보게 만드는 걸까?”

솔라 옆에 앉아 있던 루나가 조용히 물었다.

“음… index.html 같은 웹페이지 파일을 봐야지. zip 압축 파일을 다운로드하게 만들려는 건 아니니까.” “그렇다면 Deploy 단계는 아티팩트 버킷에서 가져온 소포를 최종 목적지에 어떻게 놓아주어야 할까?”

루나의 질문에 솔라는 답을 찾은 듯 고개를 끄덕였다. 그녀는 망설임 없이 배포하기 전에 파일 압축 풀기 체크박스를 클릭했다. 그러자 바로 아래에 있는 또 다른 옵션이 눈에 들어왔다. ‘미리 준비된 ACL(Canned ACL)’.

“이건 권한 설정 같은데… public-read? 아! 웹사이트니까 당연히 외부에서 누구나 파일을 읽을 수 있어야겠지. 이것도 설정해야겠네.”

솔라는 두 개의 옵션을 모두 선택하고, 파이프라인 설정을 저장했다.

[Deploy 단계 액션 편집]

- 배포하기 전에 파일 압축 풀기: [ ] -> [x]
- 미리 준비된 ACL(Canned ACL): [선택 안 함] -> [public-read]

“좋아. 이제 다시 해보는 거야.”

솔라는 ‘변경 사항 릴리스’ 버튼을 눌러 파이프라인을 다시 실행했다. Source 단계가 다시 한번 성공으로 바뀌고, 잠시 후 Deploy 단계도 초록불로 바뀌었다. 모든 과정은 이전과 똑같이 보였다.

심호흡 한번 한 솔라는 떨리는 손으로 user-prod-website 버킷이 열려 있는 오른쪽 브라우저 탭을 클릭했다.

“……!”

이번에는 텅 비어 있지 않았다. 버킷 안에는 index.html 파일이 얌전히 자리 잡고 있었다. 아티팩트 버킷에 있던 test-website.zip 파일이 아니라, 그 압축이 풀린 내용물이 들어와 있었던 것이다.

“됐어! 압축이 풀렸어!”

솔라가 작게 환호성을 질렀다. 그녀는 S3 콘솔에서 index.html 파일을 선택하고 ‘객체 URL’을 복사해 새 브라우저 탭에 붙여 넣었다. 엔터 키를 누르자, ‘Hello, World!’라는 문구가 화면에 선명하게 나타났다.

이제 모든 것이 명확해졌다. Deploy 단계는 단순히 파일을 한 장소에서 다른 장소로 옮기는 택배 기사가 아니었다. 그것은 목적지에 도착한 소포를 풀고, 내용물을 조립하고, 모든 사람이 볼 수 있도록 전시하는 ‘설치 전문가’에 가까웠다. 그리고 그 모든 작업은 사용자가 Deploy 단계 설정에서 내리는 구체적인 ‘지시’에 따라 이루어졌다. 체크박스를 선택하지 않았던 이전의 시도에서 Production 버킷이 비어 있었던 것은 오류가 아니었다. 그것은 ‘압축을 풀라’거나 ‘공개하라’는 지시가 없었기에 도출된, 지극히 논리적인 결과였다.

“이제 알겠어. 이 설정 하나하나가 다 의미가 있는 거였어. Deploy는 그냥 복사가 아니라, ‘변환’과 ‘설정’을 포함하는 과정이었네.”

솔라는 성공적으로 배포된 웹사이트 화면과 파이프라인의 설정 화면을 번갈아 보았다. 이제 Source에서 Deploy로 그어진 화살표는 그녀에게 전혀 다른 의미로 다가왔다. 그것은 숨겨진 아티팩트 버킷을 거쳐, Deploy 단계의 마법 같은 변환을 통해 최종 결과물로 탄생하는, 파일의 완전한 여정을 보여주는 지도였다.

만족스럽게 고개를 끄덕이던 솔라의 얼굴에 문득 새로운 궁금증이 스쳤다.

“이렇게 소스만 바꾸면 자동으로 웹사이트까지 업데이트되니 정말 편하긴 한데… 만약에, 배포하기 전에 마지막으로 확인해볼 게 있다면 어떡하지? 이 자동화의 흐름을 잠시 멈출 방법도 있을까?”

4장: 흐름 제어: ‘전환 비활성화’로 파이프라인 멈추기

솔라는 자신의 노트북 화면에 떠 있는 CodePipeline의 다이어그램을 물끄러미 바라보고 있었다. 왼쪽의 Source 단계와 오른쪽의 Deploy 단계는 모두 선명한 초록색 ‘성공’ 표시를 띄고 있었다. 그리고 두 단계를 잇는 굵은 화살표. 모든 것이 완벽하게 자동화된, 막힘없는 흐름을 자랑하는 듯했다. 소스 버킷에 파일을 올리기만 하면 순식간에 웹사이트가 배포되는 이 완벽함이, 바로 조금 전까지 솔라가 원했던 것이었다.

하지만 이제 그 완벽한 자동화가 오히려 불안하게 느껴졌다.

솔라는 index.html 파일의 내용을 약간 수정한 뒤, 다시 test-website.zip으로 압축해서 소스 버킷에 업로드했다. 거의 즉시 파이프라인이 반응했다. Source 단계가 ‘진행 중’으로 바뀌었다가 금세 ‘성공’으로 변했다. 그리고 곧바로 Deploy 단계로 흐름이 넘어가, 눈 깜짝할 사이에 웹사이트 배포가 완료되었다.

“너무 빨라.”

솔라가 혼잣말을 했다. 이 속도는 효율적이었지만, 한편으로는 통제 불가능한 폭주 기관차처럼 느껴졌다.

“언니, 이 파이프라인은 너무 완벽하게 자동화되어 있어. 내가 소스 코드를 올리는 순간, 내 의지와 상관없이 Deploy까지 그냥 달려가 버려. 만약에, 아주 중요한 변경이라서 배포하기 전에 마지막으로 점검할 시간이 필요하다면 어떡하지? 이 흐름을 잠시 멈출 방법은 없을까?”

솔라의 질문에 루나는 화면의 다이어그램을 가리켰다. SourceDeploy를 잇는 바로 그 굵은 화살표였다.

“이 파이프라인은 마치 역과 역 사이를 쉬지 않고 달리는 기차 같네. 하지만 보통 기찻길에는 다음 역으로 넘어가기 전에 기차를 잠시 멈추게 하는 신호등이 있지 않아?”

신호등. 그 말에 솔라는 다시 화면을 자세히 들여다보았다. Source 단계와 Deploy 단계 사이, 그 굵은 화살표 위로 마우스 커서를 가져가자 희미한 버튼 하나가 나타났다.

전환 비활성화 (Disable transition)

“어… 이런 게 있었네.”

솔라는 조심스럽게 버튼을 클릭했다. 그러자 두 단계를 잇던 화살표의 색이 회색으로 바뀌며, 그 위에 작은 자물쇠 아이콘이 나타났다. 마치 두 단계 사이의 문이 닫힌 것처럼 보였다.

“한번 실험해보자.”

솔라는 방금 전과 똑같이, index.html 파일의 내용을 다시 한번 수정하고 압축해서 소스 버킷에 업로드했다.

파이프라인이 즉시 변경을 감지하고 움직이기 시작했다. Source 단계는 이전처럼 ‘성공’으로 바뀌었다. 하지만 그 다음이 달랐다. Deploy 단계는 회색인 채 아무런 변화가 없었고, 두 단계 사이의 회색 화살표는 굳게 닫힌 채 미동도 하지 않았다. 파이프라인 전체 상태는 ‘진행 중’이었지만, 흐름은 Source 단계와 Deploy 단계 사이에서 명백히 멈춰 있었다.

“멈췄어! 내가 멈췄어!”

솔라의 목소리에 흥분이 묻어났다. 그녀가 원했던 바로 그 순간이었다. 자동화된 흐름에 개입하여 의도적으로 멈춰 세운 것이다. 이 상태라면 배포 전에 얼마든지 아티팩트 버킷의 내용을 확인하거나 다른 점검 작업을 수행할 수 있었다.

솔라가 다시 자물쇠가 걸린 회색 화살표를 클릭하자, 이번에는 ‘전환 활성화(Enable transition)’라는 버튼이 나타났다. 그녀가 버튼을 누르자 닫혔던 문이 열리듯 화살표가 파란색으로 바뀌며, 멈춰 있던 흐름이 다시 시작되었다. Deploy 단계가 즉시 실행되었고, 잠시 후 ‘성공’으로 바뀌었다.

솔라는 그제야 깨달았다. ‘전환 비활성화’는 파이프라인의 오류나 비상 정지 버튼이 아니었다. 그것은 자동화 과정에 인간의 검토와 승인 단계를 포함시킬 수 있도록 의도적으로 설계된 ‘제어 장치’였다. 자동화의 효율성은 그대로 누리면서도, 필요한 시점에는 흐름을 제어할 수 있는 권한을 사용자에게 주는, 일종의 신호등이자 관문이었던 것이다.

“이건 그냥 흐름을 막는 게 아니었어. 배포 전 최종 테스트를 하거나, 특정 시간에 맞춰 배포하고 싶을 때처럼, 의도적으로 시간을 벌기 위한 문이었구나.”

솔라는 화면의 다이어그램을 새로운 눈으로 바라보았다. Source, 숨겨진 아티팩트 버킷, 변환을 수행하는 Deploy, 그리고 그 사이의 흐름을 제어하는 전환 기능까지. 이제 파이프라인을 구성하는 핵심 요소들을 모두 직접 확인했다.

하지만 각 부분에 대한 이해가 깊어질수록, 그것들을 하나로 꿰어 설명해야 한다는 부담감이 느껴졌다.

“이제 각 단계가 뭘 하는지는 알겠는데… 만약 누가 CodePipeline이 어떻게 동작하는지 처음부터 끝까지 한번에 설명해달라고 하면… 아직 자신이 없네. Source 버킷에 있던 zip 파일 하나가 사용자 눈앞의 웹페이지가 되기까지의 전체 여정을, 막힘없이 그려낼 수 있을까?”

5장: CodePipeline의 전체 그림: Source에서 Production까지 파일의 여정

솔라의 책상 위, 하얀 노트 페이지에 어설픈 다이어그램이 그려져 있었다. 네모난 상자 두 개가 있었고, 각각 Source S3, Production S3라고 적혀 있었다. 그녀는 두 상자 사이에 화살표를 그으려다 펜을 멈췄다. 지난 며칠간의 탐험으로 알아낸 조각들이 머릿속을 맴돌았지만, 하나의 깔끔한 그림으로 합쳐지지가 않았다.

‘중간에 아티팩트 버킷이 있는데… 그건 어디에 그려야 하지? Deploy 단계는 변환을 하는데, 그건 어떻게 표현하지? 전환 비활성화는 또 뭐고?’

각각의 기능은 선명하게 이해했지만, 그것들을 연결하는 전체 이야기가 만들어지지 않았다. 마치 훌륭한 배우들은 모두 모였는데, 정작 시나리오가 없는 연극 같았다. 솔라는 결국 펜을 내려놓고 한숨을 쉬었다.

“언니, 이상해. Source 단계도 알고, 아티팩트 버킷도 찾았고, Deploy 단계가 압축을 푸는 것도 봤고, 중간에 멈추는 방법도 알았어. 그런데 이걸 전부 이어서 설명하려고 하니까 자꾸 막혀. Source 버킷에 있던 test-website.zip 파일 하나가 어떻게 내 브라우저에 ‘Hello, World!’를 띄우게 되는지, 그 여정 전체가 한눈에 그려지지가 않아.”

루나는 솔라의 노트를 들여다보았다. 텅 빈 공간이 솔라의 막막한 심정을 그대로 보여주는 듯했다. 루나는 다이어그램을 손가락으로 톡 치며 말했다.

“배우들(단계)에 집중하는 대신, 주인공의 여정을 따라가 보는 건 어때? 주인공은 딱 하나, test-website.zip 파일이야. 그 파일의 시점에서 여행기를 써보는 거지.”

“파일의 여행기?” “응. 제일 처음, 그 파일은 어디에 있지?”

솔라는 잠시 생각하더니 노트의 빈 곳에 다시 펜을 가져갔다.

“1. 시작: test-website.zip 파일이 user-source-website라는 S3 버킷에 있다.” 솔라의 펜이 움직이기 시작했다. 막혔던 흐름이 뚫리는 기분이었다.

“좋아. 그 다음은?” “2. Source 단계: 내가 그 파일을 버킷에 올리면, CodePipeline의 Source 단계가 그걸 감지해. 그리고 파일을 복사해서 SourceArtifact라는 이름의 ‘출력 아티팩트’로 만들어.”

“그럼 그 SourceArtifact는 어디로 가지?” “3. 아티팩트 저장소: 그건 Production 버킷으로 바로 가는 게 아니야. CodePipeline이 파이프라인을 만들 때 자동으로 생성해 둔, codepipeline-ap-northeast-2-... 같은 이름의 숨겨진 S3 버킷으로 가. 그 버킷 안의, 내 파이프라인 이름으로 된 폴더 아래에 저장되지.”

솔라는 점점 신이 나서 설명을 이어갔다. 머릿속에 흩어져 있던 정보들이 test-website.zip이라는 주인공을 따라 줄을 서기 시작했다.

[파일의 여정]

1. 출발지
   - 위치: `user-source-website` S3 버킷
   - 파일: `test-website.zip`

2. Source 단계 (포장)
   - 행동: 파일 변경 감지, `test-website.zip` 복사
   - 결과: `SourceArtifact` 라는 이름의 출력 아티팩트 생성

3. 중간 경유지 (물류 센터)
   - 위치: `codepipeline-*` 아티팩트 S3 버킷
   - 저장: `MyPipeline/SourceArtifact/test-website.zip` 형태로 저장됨

4. 흐름 제어 (신호등)
   - 조건: '전환 비활성화'가 켜져 있으면 여기서 대기.
   - 통과: '전환 활성화' 시 다음 단계로 진행.

5. Deploy 단계 (설치)
   - 행동: 아티팩트 버킷에서 `SourceArtifact`(`test-website.zip`)를 가져옴
   - 변환: '압축 풀기' 옵션에 따라 압축을 해제 -> `index.html` 파일 생성
   - 설정: 'public-read' 옵션에 따라 모든 사람이 파일을 읽을 수 있도록 권한 부여

6. 최종 목적지
   - 위치: `user-prod-website` S3 버킷
   - 결과: 압축 해제된 `index.html` 파일이 웹사이트로 배포됨

“그리고… 아! ‘전환 비활성화’ 기능이 켜져 있으면, 흐름은 이 중간 경유지에서 잠시 멈춰. 내가 수동으로 신호를 줘야만 다음 단계로 넘어가지.” “마지막으로, 설치 전문가가 소포를 풀 시간이야.” “응! 5. Deploy 단계! 이 단계는 아티팩트 버킷에서 SourceArtifact를 가져와. 그리고 내가 설정한 대로 마법을 부리지. ‘배포하기 전에 파일 압축 풀기’ 옵션을 켰으니까, test-website.zip의 압축을 풀어서 index.html 파일을 꺼내. 그리고 public-read 권한을 설정해서, 최종 목적지인 user-prod-website 버킷에 넣어줘. 이제 누구나 웹 브라우저로 이 파일을 볼 수 있는 거야!”

설명을 마친 솔라는 자신이 노트에 적은 여정을 보고 있었다. 막힘없이 써 내려간 글이었다. 각 단계가 어떻게 유기적으로 연결되는지, 파일이 어떤 형태로 어디에서 어디로 이동하고 변환되는지 명확하게 보였다. 더 이상 조각난 지식이 아니었다. 하나의 완결된 이야기였다.

솔라는 처음 이 모든 것을 시작하게 했던 가이드의 한 문장을 떠올렸다.

“CodePipeline에서 Source 단계와 Deploy 단계를 구성해 정적 웹 사이트를 배포합니다.”

예전에는 그저 A에서 B로 가는 단순한 명령처럼 들렸던 문장이었다. 하지만 이제 그 문장 뒤에 숨겨진 거대한 기계장치의 움직임이 눈에 선했다. 소스 파일을 감지하고, 숨겨진 버킷으로 포장해 옮기고, 신호등의 허락을 받아, 설치 전문가가 내용물을 꺼내 근사하게 전시하는 전체 과정 말이다.

솔라는 새로운 파이프라인 생성 화면을 열었다. 이전과는 완전히 다른 눈으로 화면을 보고 있었다.

“이제 알겠어. 여기서 내가 ‘출력 아티팩트’ 이름을 정하면, CodePipeline은 그 이름으로 아티팩트 버킷에 폴더를 만들겠지. 그리고 다음 단계에서 그 이름을 ‘입력 아티팩트’로 지정하면, 파이프라인은 정확히 그 폴더에 가서 파일을 찾아올 거야. 이건 그냥 이름표가 아니라, 파일이 지나갈 길을 내가 직접 설계하는 거였어.”

그녀는 더 이상 파이프라인을 두려워하지 않았다. 각 설정값이 어떤 물리적인 결과를 만들어낼지 예측할 수 있게 되자, 복잡한 자동화 시스템은 그녀의 의도대로 움직이는 강력한 도구가 되었다. 솔라는 자신감에 찬 미소를 지으며, 비어있던 노트의 다음 페이지를 펼쳤다. 이제 자신의 첫 번째 프로젝트를 설계할 차례였다.