바이브코딩 서비스를 Docker 컨테이너로 실전서비스 론칭하기 · 2부 — 이미지 레지스트리, AWS 인프라(콘솔)

9장. 이미지 태깅

이 장을 마치면: 이미지에 붙이는 "태그"가 무엇이고, 왜 태그를 잘 붙이는 것이 나중의 롤백과 직결되는지 이해하고, docker tag로 우리 이미지에 버전 태그(1.0)를 직접 붙일 수 있다.

8장에서 AWS 콘솔에 처음 들어가 계정·사용자·리전까지 준비했습니다. 이제 우리가 1부에서 만든 이미지를 AWS의 이미지 창고로 올릴 차례인데, 그 전에 꼭 짚고 갈 것이 하나 있습니다. 바로 이미지에 붙이는 태그입니다.

7장에서 docker build -t vote-api . 명령으로 이미지를 만들 때, -t vote-api 로 이름을 붙였습니다. 이 "이름표"를 태그라고 하는데, 사실 여기엔 배포와 운영의 핵심이 숨어 있습니다. 이번 장은 명령을 많이 치기보다, "태그를 어떻게 붙일 것인가"라는 생각의 틀을 잡고, 우리 이미지에 버전 태그를 직접 붙여 다음 장(창고에 올리기)을 준비하는 시간입니다. 이 틀이 3부 롤백 실습의 밑바탕이 됩니다.

[지금 여기]

우리는 22단계를 거쳐 "라이브 투표 서비스"를 직접 만들고, 컨테이너에 담아, AWS 클라우드에 배포합니다. 아래가 그 전체 여정 지도입니다. 지금은 2부의 두 번째, 9단계에 있습니다.

  1. 1장개발 환경 준비 (터미널 / Node.js / Git / GitHub / VS Code / Claude Code)
  2. 2장Claude Code 사용법 익히기
  3. 3장무엇을 만들지 기획 (라이브 투표 서비스)
  4. 4장로컬에서 서비스 만들기 (React + Express + PostgreSQL)
  5. 5장Docker 개념
  6. 6장Dockerfile 작성
  7. 7장컨테이너 빌드·실행
  8. 8장AWS 시작 (계정 / IAM / 리전 / 비용)
  9. 9장이미지 태깅
  10. 10장ECR에 이미지 올리기
  11. 11장VPC 네트워크 구성
  12. 12장RDS 데이터베이스 생성
  13. 13장SSM 터널로 RDS 초기화
  14. 14장ECS Fargate + ALB로 배포 (기본 완성)
  15. 15장CI/CD (GitHub Actions)
  16. 16장IaC (Terraform)
  17. 17장배포 자동화 연결
  18. 18장현업 운영 구조 이해
  19. 19장무중단 배포
  20. 20장신규 버전 배포 & 롤백
  21. 21장(심화) 프론트를 Vercel로 분리 배포
  22. 22장마무리 (회고 + 리소스 삭제)

이번 장에서 완성되는 것

한 문장으로: 로컬에 있는 vote-apivote-web 이미지에 1.0이라는 버전 태그를 붙여, docker images 목록에서 vote-api:1.0, vote-web:1.0 을 눈으로 확인할 수 있게 됩니다.

이렇게 되면 성공입니다: VS Code 터미널에서 docker images를 실행했을 때 vote-apivote-web이 각각 latest1.0 두 줄로 보입니다.

사전 조건

이 장은 1~8장을 마친 상태에서 시작합니다. 아래가 갖춰져 있어야 합니다.

시작하기 전에, 7장의 이미지가 지금도 로컬에 남아 있는지 먼저 확인합시다. VS Code에서 vote-app 폴더가 열려 있는 상태로 터미널을 열고(Terminal → New Terminal), 아래를 입력하세요.

docker images        # 지금 내 컴퓨터에 있는 이미지 목록을 보여 준다

출력 목록에 vote-apivote-web이 보이고, 각 TAG 칸이 latest로 되어 있으면 준비 완료입니다.

REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
vote-web     latest    9f2a1c3d4e5f   2 hours ago    52MB
vote-api     latest    7a1b2c3d4e5f   2 hours ago    180MB

(IMAGE ID, CREATED, SIZE 숫자는 사람마다 다릅니다. 이름과 태그만 맞으면 됩니다.)

주의

만약 목록에 vote-api·vote-web이 안 보인다면, 7장의 빌드가 안 된 상태입니다. 7장으로 돌아가 backend 폴더에서 docker build -t vote-api ., frontend 폴더에서 docker build -t vote-web .을 다시 실행한 뒤 이 장으로 오세요.

9.1 태그란 — 이미지에 붙이는 버전 이름표

(1) 개념 — 태그가 무엇인가

용어

태그(Tag) 이미지에 붙이는 이름 겸 버전 표시입니다. 형식은 보통 이미지이름:버전 입니다. 예를 들어 vote-api:1.0은 "vote-api 이미지의 1.0 버전"이라는 뜻입니다. 콜론(:) 앞이 이미지 이름, 뒤가 버전입니다.

7장에서 -t vote-api처럼 버전 없이 이름만 붙였는데, 사실 그때 Docker는 자동으로 vote-api:latest라는 태그를 붙였습니다. 콜론(:) 뒤에 버전을 안 적으면 latest가 기본으로 붙습니다. 방금 docker images에서 TAG 칸이 latest로 나온 게 그 때문입니다.

용어

latest "가장 최신"이라는 뜻으로, 버전을 생략하면 자동으로 붙는 기본 태그입니다. 편해 보이지만, 뒤(9.2)에서 설명하듯 배포·롤백에서는 오히려 위험할 수 있습니다.

태그는 한 이미지에 여러 개 붙일 수도 있습니다. 같은 이미지에 vote-api:1.0vote-api:latest를 동시에 매달 수 있죠. 하나의 상자에 이름표를 여러 개 매단 셈입니다. 태그를 새로 붙이는 명령은 docker tag입니다.

docker tag vote-api:latest vote-api:1.0
# 앞: 원본 이미지(vote-api:latest) / 뒤: 새로 붙일 이름표(vote-api:1.0)
# vote-api:latest 이미지에 vote-api:1.0 이라는 이름표를 하나 더 붙인다

여기서 중요한 사실 하나. docker tag는 이미지를 복사하지 않습니다. 같은 이미지에 이름표(참조)를 하나 더 다는 것뿐입니다. 그래서 vote-api:latestvote-api:1.0은 사실 똑같은 실체 하나를 가리킵니다. (뒤 맛보기에서 IMAGE ID가 같은 걸로 직접 확인합니다.)

또는 빌드할 때 처음부터 버전을 지정할 수도 있습니다.

docker build -t vote-api:1.0 .
# 빌드하면서 바로 1.0 버전 태그를 붙인다 (backend 폴더에서 실행할 경우)

(2) 활용 사례 — 버전 태그가 실무에서 어디에 쓰이나

이미지에 버전 태그를 붙이는 일은 우리 프로젝트에만 필요한 게 아니라, 회사에서 서비스를 운영할 때 늘 하는 일입니다. 몇 가지만 보겠습니다.

(3) 맛보기 — 아주 작은 이미지에 태그를 직접 붙여 보기

우리 프로젝트를 건드리기 전에, 태그가 어떻게 동작하는지 작은 예제 이미지 하나로 손으로 만져 봅시다. hello-world라는 아주 작은(수 KB) 공식 예제 이미지를 받아서, 거기에 1.0 태그를 붙여 볼 겁니다. 우리 vote-api·vote-web과는 상관없는, 연습용입니다.

VS Code 터미널(vote-app 폴더)에 아래를 한 줄씩 입력하세요.

docker pull hello-world              # 아주 작은 예제 이미지 하나를 받는다
docker tag hello-world:latest my-app:1.0   # 이 이미지에 my-app:1.0 이름표를 붙인다
docker images                        # 목록에서 확인한다

목록을 보면 hello-world(태그 latest)와 my-app(태그 1.0)이 각각 한 줄씩 나오는데, 두 줄의 IMAGE ID가 완전히 똑같습니다.

REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
hello-world   latest    d2c94e258dcb   3 months ago   13.3kB
my-app        1.0       d2c94e258dcb   3 months ago   13.3kB

IMAGE ID가 같다는 건, 앞에서 말한 그대로 docker tag가 이미지를 복사한 게 아니라 같은 실체에 이름표만 하나 더 달았다는 뜻입니다. 이름은 둘, 실체는 하나입니다.

연습이 끝났으니 이 연습용 이름표는 지워도 됩니다. (원본 hello-world는 그대로 두고, 방금 붙인 my-app:1.0 이름표만 뗍니다.)

docker rmi my-app:1.0                # my-app:1.0 이름표만 제거한다 (실체는 안 지워짐)
참고

docker rmi my-app:1.0은 이름표 하나만 떼는 것이라, 같은 실체를 가리키는 hello-world:latest는 그대로 남습니다. 이름표가 여러 개일 때 하나를 떼도 실체가 안 사라지는 것도, 태그가 "이름표"라는 증거입니다.

이제 태그가 무엇인지 손으로 확인했으니, 9.5에서 우리 프로젝트 이미지에 같은 방식으로 1.0을 붙입니다. 그 전에, "어떤 태그를 붙일지"라는 전략을 먼저 정리하겠습니다.

9.2 태깅 전략 — latest vs 고유한 태그

이제 중요한 질문입니다. 어떤 태그를 붙여야 할까요? 크게 두 방식이 있습니다.

방식 1. latest 하나만 쓰기

vote-api:latest로만 빌드하고 배포하는 방식입니다. 간단해 보이지만 문제가 있습니다. latest는 "지금 시점의 최신"을 가리키는 이름일 뿐, 어떤 코드로 만든 것인지 구분되지 않습니다. 어제의 latest와 오늘의 latest는 완전히 다른 이미지인데 이름이 같습니다.

이게 왜 문제일까요? 오늘 배포한 latest에 버그가 있어서 "어제 버전으로 되돌리고 싶다"고 할 때, 어제의 latest는 이미 오늘 것으로 덮여 사라졌을 수 있습니다. 되돌아갈 지점이 없는 것이죠.

방식 2. 구분되는 고유한 태그 붙이기 (권장)

배포할 때마다 서로 구분되는 태그를 붙이는 방식입니다. 대표적으로 두 가지를 씁니다.

(1) 개념 — 커밋 해시 태그

용어

커밋 해시(Commit Hash) 1부에서 배운 커밋(코드를 저장한 지점)마다 자동으로 붙는 고유한 식별 값입니다. a1b2c3d처럼 생겼습니다. 이걸 이미지 태그로 쓰면 "이 이미지는 정확히 이 코드로 만들어졌다"가 한 치의 애매함 없이 정해집니다.

버전 번호(1.0)는 사람이 정하는 이름이라 좋지만, 사람이 매번 손으로 정해야 합니다. 커밋 해시는 코드를 저장할 때 이미 자동으로 생기는 값이라, 코드가 바뀔 때마다 저절로 서로 다른 태그가 만들어집니다. 그래서 자동화된 배포에서 특히 잘 맞습니다.

(2) 활용 사례 — 커밋 해시 태그가 실무에서 어디에 쓰이나

참고

실무에서 흔한 조합 배포 자동화에서는 보통 커밋 해시를 태그로 자동으로 붙이고, latest는 "가장 최근 것을 편하게 가리키는 별칭"으로 곁들여 씁니다. 즉 latest를 아예 안 쓰는 게 아니라, 고유 태그와 함께 씁니다. 되돌아갈 지점(고유 태그)은 항상 남기면서, 최신을 가리키는 별칭도 유지하는 것이죠.

핵심 원칙 하나만 기억하세요.

배포에 쓰는 이미지는 "무엇으로 만든 것인지 구분되는" 고유한 태그를 가져야 한다. 그래야 필요할 때 정확히 그 버전으로 돌아갈 수 있다.

우리는 지금 손으로 실습하므로 읽기 쉬운 버전 번호(1.0)로 시작합니다. 커밋 해시를 자동으로 붙이는 방식은 3부에서 다룹니다.

9.3 태그가 롤백과 어떻게 연결되나

이 장에서 가장 중요한 부분입니다. 태그를 잘 붙이는 이유는 결국 롤백을 위해서입니다.

용어

롤백(Rollback) 새로 배포한 버전에 문제가 생겼을 때, 이전의 정상 버전으로 되돌리는 것입니다.

구체적인 상황으로 그려 봅시다.

  1. 지금 서비스가 vote-api:1.0으로 잘 돌고 있다.
  2. 새 기능(투표 결과를 퍼센트로 표시)을 추가해 vote-api:2.0으로 배포한다.
  3. 그런데 2.0에 버그가 있어서 서비스가 깨진다. (총 투표가 0표일 때 퍼센트 계산에서 0으로 나누기 오류가 난다.)
  4. 급히 이전 정상 버전인 vote-api:1.0으로 되돌린다. → 롤백 성공.

여기서 핵심은 4번이 가능하려면 3번 시점에도 1.0 이미지가 그대로 남아 있어야 한다는 것입니다. 만약 두 버전 모두 latest라는 같은 이름을 썼다면, 2.0을 배포하는 순간 1.0이 덮여 사라져서 되돌아갈 곳이 없어집니다.

즉, 고유한 태그는 "언제든 돌아갈 수 있는 안전한 세이브 포인트"를 여러 개 남겨 두는 것과 같습니다. 게임에서 세이브를 여러 슬롯에 나눠 저장해 두면 언제든 원하는 지점으로 불러올 수 있는 것과 똑같습니다.

참고

그래서 이 순서로 진행합니다 2부에서 이미지에 고유 태그(1.0)를 붙여 창고(다음 10장의 ECR)에 차곡차곡 쌓아 둡니다. 그러면 3부에서 문제가 생겼을 때, 창고에 남아 있는 이전 태그를 그대로 꺼내 롤백할 수 있습니다. 지금 태그를 신경 쓰는 이유가 여기 있습니다.

9.4 이미지 빌드 재현성

마지막으로 관련된 개념 하나를 짚습니다. "재현성"입니다.

(1) 개념 — 재현성이 무엇인가

용어

재현성(Reproducibility) 언제, 어느 컴퓨터에서 빌드하더라도 같은 코드에서는 같은 결과(이미지)가 나오는 성질입니다.

왜 중요할까요? 태그가 아무리 잘 붙어 있어도, 같은 코드에서 빌드할 때마다 결과가 들쭉날쭉하면 "이 태그가 정확히 무엇인지" 믿을 수 없습니다. 롤백해서 1.0을 다시 꺼냈는데 예전의 1.0과 미묘하게 다르면 곤란하겠죠.

재현성을 높이는 방법은 대부분 6장에서 이미 다뤘습니다.

(2) 활용 사례 — 재현성이 실무에서 왜 중요한가

참고

실무의 원칙: build once, deploy anywhere "한 번 만든 이미지를 여러 환경(테스트·운영)에 그대로 옮겨 배포한다"는 원칙입니다. 환경마다 다시 빌드하면 미묘한 차이가 생길 수 있으니, 검증한 이미지 그 자체를 옮깁니다. 이것이 컨테이너 배포의 큰 장점입니다.

9.5 [우리 프로젝트] vote-api·vote-web에 1.0 태그 붙이기

이제 배운 걸 우리 이미지에 적용합니다. 로컬에 있는 vote-apivote-web (지금은 latest 태그만 붙어 있음)에 1.0 버전 태그를 붙입니다. 이렇게 해 두면 다음 10장에서 이 1.0 이미지를 AWS 창고(ECR)에 그대로 올릴 수 있습니다.

1단계. 터미널 위치 확인

VS Code에서 vote-app 폴더가 열린 상태로 터미널을 엽니다(Terminal → New Terminal). docker tag 명령은 폴더 위치와 무관하게 동작하지만, 이 책은 언제나 이 터미널에서 실행합니다.

2단계. 두 이미지에 1.0 태그 붙이기

터미널에 아래 두 줄을 한 줄씩 입력하세요.

docker tag vote-api:latest vote-api:1.0
# vote-api:latest 이미지에 vote-api:1.0 이라는 이름표를 추가로 붙인다

docker tag vote-web:latest vote-web:1.0
# vote-web:latest 이미지에 vote-web:1.0 이라는 이름표를 추가로 붙인다

두 명령 모두 아무 메시지 없이 커서가 다시 깜빡이면 정상입니다. (docker tag는 성공해도 별도 출력이 없습니다.)

3단계. 붙었는지 확인

docker images        # 이미지 목록을 다시 본다

이제 vote-apivote-web이 각각 latest1.0, 두 줄씩 나옵니다. 같은 이름의 두 줄은 IMAGE ID가 서로 같습니다(같은 실체에 이름표 두 개).

REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
vote-web     1.0       9f2a1c3d4e5f   2 hours ago    52MB
vote-web     latest    9f2a1c3d4e5f   2 hours ago    52MB
vote-api     1.0       7a1b2c3d4e5f   2 hours ago    180MB
vote-api     latest    7a1b2c3d4e5f   2 hours ago    180MB

vote-api:1.0vote-web:1.0이 목록에 보이면, 이 장의 목표를 이룬 것입니다.

참고

Claude Code로 하고 싶다면 위 과정을 손으로 치는 대신 Claude Code에게 시켜도 됩니다. vote-app 폴더에서 claude를 실행한 뒤 아래처럼 지시하세요. 다만 무슨 일이 일어나는지 알 수 있게, 한 번쯤은 위 명령을 직접 쳐 보기를 권합니다.

프롬프트
로컬에 있는 vote-api:latest, vote-web:latest 이미지에 각각
1.0 버전 태그를 붙여 줘. 그다음 docker images로 결과를 보여 줘.

[확인]

이번 장이 제대로 끝났는지 눈으로 확인합니다. VS Code 터미널에서 docker images를 실행했을 때:

여기까지 보였다면, 우리 이미지에 버전 태그를 붙여 다음 10장(창고에 올리기) 준비를 마친 것입니다.

막히면

이 장에서 자주 나는 오류와 대처법입니다.

[체크리스트]

아래가 모두 됐는지 확인해 보세요. 하나라도 안 됐다면 해당 절로 돌아가 다시 진행합니다.

모두 체크됐다면, 우리 이미지에 버전 태그를 붙이는 일까지 끝난 것입니다.