CI/CD Practice 01

S3 버킷, 역할 분리의 이유

S3 버킷 두 개를 만들고 파일을 올리는 과정이 단순 저장소 실습처럼 보이지만, 왜 source와 production을 나누는지 잘 보이지 않는다.

근거 · CI/CD 교안 p3-p18, test-website.zip

S3 버킷, 역할 분리의 이유 대표 이미지

1장: 두 개의 S3 버킷, 무엇이 다를까?

솔라는 노트북 화면에 떠 있는 두 개의 S3 버킷 이름을 가만히 들여다보았다. 방금 전 끝낸 실습의 결과물이었다.

sola-source-website sola-prod-website

시키는 대로 버킷 두 개를 만들고, 각각에 파일을 올렸다. 과정 자체는 어렵지 않았다. 하지만 무언가 개운치 않은 구석이 있었다. S3는 파일을 저장하는 서비스라고 배웠다. 그렇다면 지금 솔라의 AWS 계정에는 그저 파일 저장 공간 두 개가 생긴 것뿐이다. 그런데 왜 굳이 두 개를 만들었을까? 하나에 다 넣으면 안 되는 이유라도 있는 걸까?

솔라는 고개를 갸웃하며 중얼거렸다.

“이상하네. 그냥 폴더 두 개 만든 거랑 뭐가 다르지?”

마치 책상 위에 ‘서류함 1’, ‘서류함 2’라고 이름 붙인 빈 상자를 두 개 가져다 놓은 기분이었다. 어느 쪽이든 서류를 넣을 수 있다는 점에서는 똑같다. 그런데 굳이 ‘source’와 ‘production’이라는, 의미심장해 보이는 이름을 붙여서 나누라고 한 의도를 도무지 알 수 없었다.

“그냥 저장소가 두 개 생긴 것뿐인데… 파일을 여기에 넣든 저기에 넣든 무슨 차이가 있다고.”

그때 옆에서 조용히 자신의 작업을 하던 언니 루나가 솔라 쪽으로 시선을 돌렸다. 솔라의 혼잣말을 들은 모양이었다.

“뭐가 잘 안 돼?”

“아니, 안 되는 건 아닌데… 언니, 이것 좀 봐봐. S3 버킷을 두 개 만들었거든. 하나는 이름이 ‘source’고 다른 하나는 ‘production’이야. 근데 왜 이렇게 나눠야 하는지 모르겠어. 어차피 둘 다 똑같은 S3 버킷이잖아.”

솔라가 화면을 가리키며 묻자, 루나는 의자 등받이에 몸을 기대며 화면을 잠시 바라보았다. 루나는 정답을 바로 알려주는 대신, 다른 질문을 던졌다.

“그 이름, 솔라 네가 직접 지은 거지?”

“응. 실습 가이드에 그렇게 하라고 나와 있었어. 내 이름 뒤에 ‘source-website’랑 ‘prod-website’를 붙이라고.”

“만약에 말이야,” 루나가 말을 이었다. “두 버킷의 역할이 정말 완벽하게 똑같았다면, 가이드에서 굳이 ‘source’와 ‘production’이라는 단어를 쓰라고 했을까? 그냥 ‘website-1’, ‘website-2’라고 해도 됐을 텐데.”

“아…”

솔라는 짧은 탄성을 뱉었다. 너무나 당연해서 오히려 생각하지 못했던 지점이었다. 만약 두 버킷이 기능적으로 아무런 차이가 없다면, 굳이 구별되는 의미를 가진 이름을 붙일 필요가 없다. 숫자나 알파벳으로 순서만 매겨도 충분했을 것이다.

솔라는 다시 화면 속 두 개의 버킷 이름을 보았다. 이전과는 다르게 보이기 시작했다.

sola-source-website sola-prod-website

이것은 단순한 이름표가 아니었다. 어쩌면 각자의 역할과 책임이 적힌 명패 같은 것일지도 모른다. 마치 회사의 팀 이름이 ‘기획팀’과 ‘개발팀’으로 나뉘어 있는 것처럼 말이다. 두 팀 모두 ‘회사 직원’이라는 점에서는 같지만, 맡은 역할과 결과물은 전혀 다르다.

“그러네. ‘source’랑 ‘production’… 이름부터 뭔가 다른 일을 할 거라고 암시하고 있었네.”

솔라의 목소리에는 작은 깨달음이 묻어났다. 이제 ‘왜 두 개나 만들었지?’라는 의문은 ‘각각 무슨 역할을 하지?’라는 새로운 질문으로 바뀌었다. 저장 공간이 두 개라는 사실 자체보다, 그 두 공간에 부여된 서로 다른 이름의 의미가 훨씬 더 중요하게 느껴졌다.

“단순히 파일을 보관하는 장소 이상이라는 거구나. 그럼 ‘source’ 버킷과 ‘production’ 버킷은 정확히 어떤 다른 목적을 가지고 있는 거야? 이건 그냥 개발자들 사이의 약속 같은 거야, 아니면 기술적으로도 뭔가 다른 점이 있는 거야?”

2장: ‘Source’ 버킷, 파이프라인의 시작점

솔라의 질문에 루나는 바로 대답하는 대신, 노트북 화면의 실습 가이드를 손가락으로 가리켰다. 다음 단계 지시사항이 적혀 있었다.

source 버킷(sola-source-website)에 test-website.zip을 업로드하고 버킷 버전 관리를 활성화합니다.

루나는 아무 말 없이 솔라가 지시를 따르기를 기다렸다. 솔라는 잠시 의아한 표정을 짓다가, 이내 마우스를 움직여 sola-source-website 버킷에 파일을 업로드하고 속성 탭에서 ‘버전 관리’를 활성화했다. 몇 번의 클릭으로 작업은 금방 끝났다. 버킷에는 test-website.zip 파일이 덩그러니 놓였고, 버전 관리 상태는 ‘활성화됨’으로 바뀌었다.

하지만 솔라의 표정은 여전히 물음표로 가득했다.

“파일 하나 올렸고… 버전 관리도 켰어. 그런데 언니, 이게 ‘source’라는 이름의 목적과 무슨 관련이 있는 거야? 그냥 압축 파일 하나 저장한 거잖아. 그리고 버전 관리는 또 왜 갑자기 켜라는 거지? 파일이 바뀔 때마다 기록해준다는 건 알겠는데, 지금 당장은 아무 의미 없어 보이는데.”

솔라에게 이 작업은 여전히 개별적인 점들의 나열처럼 보였다. 버킷을 만들고, 파일을 올리고, 설정을 켜는 것. 각각의 행동은 이해했지만, 그것들이 모여 어떤 그림을 만드는지는 보이지 않았다. 특히 test-website.zip 파일이 왜 하필 ‘source’ 버킷에 들어가야 하는지, 그냥 ‘파일 업로드’일 뿐이라는 생각에서 벗어나기 어려웠다.

“그럴 수 있겠네.” 루나가 조용히 말했다. “그럼 그 파일, 한번 열어볼까? 우리 컴퓨터에 다운로드해서 압축을 풀어봐.”

“파일을?”

솔라는 의아해하면서도 test-website.zip 파일을 자신의 노트북 바탕화면에 다운로드했다. 그리고 압축 풀기 메뉴를 선택했다. 압축이 풀리자 test-website라는 이름의 폴더가 나타났고, 솔라는 폴더를 더블클릭해서 열었다.

폴더 안에는 익숙한 파일들이 들어 있었다.

/test-website
├── index.html
├── error.html
└── css/
    └── styles.css

“아!”

파일 목록을 본 솔라의 입에서 짧은 탄성이 터져 나왔다. HTML 파일과 CSS 폴더. 이건 명백한 웹사이트의 구성 요소, 즉 ‘소스 코드’였다. 단순한 문서나 이미지가 아니었다. 웹사이트라는 결과물을 만들어내기 위한 원본 재료들이었다.

순간, 흩어져 있던 점들이 하나의 선으로 이어지는 느낌이 들었다.

“잠깐만. 이 파일 이름이 test-website.zip이고, 안에는 웹사이트 소스 코드가 들어있어. 그리고 내가 이 파일을 올린 곳은… sola-source-website 버킷이야.”

솔라는 스스로 말을 잇다가 놀란 듯 루나를 쳐다보았다.

“‘source’ 버킷에 ‘source’ 코드를 올린 거네!”

“맞아.” 루나가 고개를 끄덕였다. “source 버킷은 바로 그런 역할을 하는 거야. 최종 결과물이 아니라, 결과물을 만들기 위한 원재료, 즉 소스 코드를 보관하는 장소지. 요리에 비유하면, 완성된 파스타가 아니라 파스타 면, 토마토, 마늘 같은 재료들을 보관하는 찬장 같은 곳이야.”

그제야 ‘source’라는 이름의 무게가 다르게 느껴졌다. 그냥 붙인 이름이 아니라, ‘이곳은 소스 코드를 두는 곳입니다’라고 명확하게 선언하는 푯말이었던 것이다.

“그럼 버전 관리는?” 솔라가 물었다.

“개발자가 웹사이트를 수정한다고 생각해봐. styles.css 파일의 배경색을 파란색에서 초록색으로 바꿨어. 그럼 어떻게 해야 할까?”

“음… 수정한 파일들을 다시 압축해서 test-website.zip이라는 이름으로 버킷에 새로 올려야겠지?”

“바로 그거야. 만약 그때 버전 관리가 켜져 있으면 어떻게 될까? S3는 이전의 파란색 배경이었던 버전과 새로 올린 초록색 배경의 버전을 모두 자동으로 저장하고 관리해줘. 혹시라도 새로운 버전에 문제가 생기면 언제든 이전 버전으로 돌아갈 수 있지.”

솔라는 무릎을 탁 쳤다. 모든 조각이 제자리를 찾은 기분이었다. source 버킷은 자동화된 시스템, 즉 파이프라인의 입력을 위한 공간이었다. 파이프라인은 이 버킷에 새로운 버전의 소스 코드가 올라오는 것을 감지하고, 그것을 가져다가 웹사이트를 만들고 배포하는 모든 과정을 자동으로 처리할 것이다. test-website.zip 파일은 그 파이프라인이 사용할 ‘입력 재료(artifact)’였고, 버전 관리는 그 재료의 변경 이력을 안전하게 추적하는 장치였던 셈이다.

“알겠다! source 버킷은 그냥 파일 창고가 아니라, 자동화 파이프라인을 위한 ‘입력 재료 보관소’ 같은 거구나. 새로운 재료가 들어오면 파이프라인이 알아서 요리를 시작하는 거지. 그래서 이름이 source였던 거네.”

이제 솔라의 머릿속에서 sola-source-website 버킷은 더 이상 텅 빈 상자가 아니었다. 언제든 새로운 소스 코드가 들어와 다음 단계를 기다리는, 역동적인 파이프라인의 시작점으로 보였다.

그렇다면 남은 질문은 하나였다.

“그럼 production 버킷은… 파이프라인이 만들어낸 결과물, 그러니까 완성된 요리가 놓이는 곳인 건가?”

3장: ‘Production’ 버킷, 사용자를 만나는 곳

솔라는 자신의 가설, ‘완성된 요리가 놓이는 곳’이라는 생각을 증명해 보려는 듯, 망설임 없이 sola-prod-website 버킷을 클릭했다. source 버킷이 재료 창고라면, production 버킷은 손님에게 나갈 음식이 담긴 접시가 되어야 했다. 그 접시를 어떻게 손님 앞에 내놓을 수 있을까?

마침 화면의 다음 실습 지시사항이 눈에 들어왔다. 루나는 이번에도 말없이 그 부분을 가리켰다.

1. 'sola-prod-website' 버킷의 [속성] 탭으로 이동합니다.
2. 맨 아래로 스크롤하여 [정적 웹 사이트 호스팅] 섹션에서 [편집]을 클릭합니다.
3. 정적 웹 사이트 호스팅을 [활성화]하고, 인덱스 문서에 'index.html', 오류 문서에 'error.html'을 입력한 후 변경 사항을 저장합니다.

“아, 웹사이트 호스팅!”

솔라의 목소리가 살짝 들떴다. 이 설정이야말로 버킷을 단순한 파일 저장소에서 진짜 ‘웹사이트’로 만들어주는 기능이었다. S3가 인터넷 주소를 통해 파일을 보여줄 수 있게 해주는 마법. 솔라는 재빨리 지시대로 설정을 마쳤다. 버킷 속성 페이지에는 이제 버킷 웹 사이트 엔드포인트라는 항목과 함께 고유한 URL이 나타나 있었다.

http://sola-prod-website.s3-website.ap-northeast-2.amazonaws.com

이것이 바로 손님들이 찾아올 식당의 주소였다. 솔라는 자신의 예상이 정확히 들어맞았다고 생각했다. ‘production’ 버킷은 정적 웹 호스팅을 통해 최종 사용자와 만나는 창구 역할을 하는 것이다.

“자, 이제 완성된 웹사이트가 뜰 거야.”

기대에 찬 솔라는 주소를 복사해 새 브라우저 탭에 붙여넣고 엔터 키를 눌렀다. 잠시 후 화면에 나타난 것은 멋진 웹사이트가 아니었다.

404 Not Found

큼지막한 글자와 함께 ‘지정한 키가 존재하지 않습니다.’라는 차가운 메시지가 떠 있었다. 솔라의 미간이 좁혀졌다.

“어? 왜 안 되지? 웹사이트 호스팅 켰는데. 주소도 맞고… 분명히 index.html을 시작 페이지로 지정했잖아.”

솔라는 버킷 설정 화면과 404 오류 화면을 몇 번이고 번갈아 보았다. 설정은 분명 완벽했다. ‘웹사이트 띄우기’ 기능은 켰지만, 정작 보여줄 웹사이트가 없다는 상황. 마치 식당은 개업했는데 주방에 요리가 하나도 없는 것과 같았다.

그때, 옆에 있던 루나가 조용히 물었다.

“그 식당의 주방장이 쓸 index.html 파일은 지금 어디에 있지?”

“어…?”

루나의 질문에 솔라는 잠시 말을 잃었다. 그리고는 깨달았다는 듯, 탄식 섞인 목소리로 대답했다.

source 버킷. test-website.zip 파일 안에….”

그랬다. 솔라는 ‘정적 웹 호스팅’이라는 기능만 켜면 모든 게 해결될 거라 생각했다. 하지만 S3는 신이 아니었다. source 버킷에 있는 압축 파일을 알아서 풀어서 production 버킷으로 옮겨주지 않는다. 솔라는 식당 주소만 만들어놓고, 정작 가장 중요한 요리, 즉 index.html 파일을 그곳에 가져다 놓는 것을 잊고 있었다.

순간 머릿속에서 모든 것이 연결되었다.

“알겠다… source는 재료 창고, production은 손님에게 나갈 접시. 그리고 그 사이에는 ‘요리 과정’이 있어야 하는구나.”

솔라는 연필을 들어 노트에 무언가를 그리기 시작했다.

첫 번째 네모를 그리고 ‘Source 버킷’이라고 썼다. 그 안에는 test-website.zip 파일을 그려 넣었다. 그리고 오른쪽에 두 번째 네모를 그리고 ‘Production 버킷’이라고 썼다. 그 안에는 index.htmlerror.html 파일을 그렸다. 그리고 네모 바깥에서 사람 모양 아이콘이 화살표로 이 버킷을 가리키도록 했다. 여기가 바로 사용자가 접속하는 최종 종점, ‘배포 지점’이었다.

마지막으로, 두 네모 상자 사이에 커다란 화살표를 그렸다. 솔라는 잠시 고민하다가 그 화살표 위에 이렇게 적었다.

CI/CD 파이프라인 (압축 풀고, 파일 복사)’

그림을 완성한 솔라는 고개를 들어 루나를 보았다. 루나는 솔라의 노트를 가만히 들여다보더니, 작게 고개를 끄덕였다.

이제 솔라에게 두 개의 S3 버킷은 더 이상 분리된 저장 공간이 아니었다. source 버킷에 소스 코드를 업로드하면, 보이지 않는 자동화 파이프라인이 그것을 가져다가 빌드하고 테스트한 후, 최종 결과물을 production 버킷에 배포한다. 그리고 사용자는 오직 이 production 버킷에만 접근해 언제나 안정적인 최신 버전의 서비스를 이용하게 되는 것이다.

개발자는 source를 보고, 사용자는 production을 본다. 이 명확한 역할 분리와 흐름이야말로, ‘그냥 폴더 두 개’와는 비교할 수 없는 이 시스템의 핵심이었다. 솔라는 자신의 작은 다이어그램을 보며 만족스럽게 중얼거렸다.

“이름에 다 이유가 있었네.”