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

10장. ECR에 이미지 올리기

앞 장까지 우리는 내 노트북 안에서 이미지를 만들고, 태그(이름표)를 붙였습니다. 그런데 이 이미지는 지금 내 노트북 안에만 있습니다. 나중에 AWS 서버가 이 이미지로 컨테이너를 실행하려면, 서버가 이미지를 가져갈 수 있는 "인터넷 창고"에 올려 둬야 합니다. 그 창고가 레지스트리이고, 우리가 쓸 AWS의 레지스트리가 ECR입니다. 이번 장에서는 레지스트리가 무엇인지 이해하고, AWS ECR에 창고를 만들어, 내 이미지를 실제로 그 창고에 올립니다.

[지금 여기]

우리는 22단계를 거쳐 "라이브 투표 서비스"를 만들고, 컨테이너에 담아, AWS 클라우드에 배포하고 있습니다. 아래가 그 전체 여정 지도입니다. 지금은 10단계, 이미지를 AWS 창고(ECR)에 올리는 자리입니다.

  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에 이미지 올리기 (vote-api, vote-web)
  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장마무리 (회고 + 리소스 삭제)

이번 장에서 완성되는 것

한 문장으로: AWS ECR에 vote-api, vote-web 두 개의 리포지토리를 만들고, 내 노트북의 이미지를 그 안에 올려 둘 수 있게 됩니다.

이렇게 되면 성공입니다: AWS 콘솔의 ECR 화면에서 vote-api, vote-web 리포지토리를 열었을 때, 각각 안에 1.0 태그가 붙은 이미지가 올라가 있는 것을 눈으로 볼 수 있습니다.

사전 조건

이 장은 앞의 여러 장 결과 위에서 시작합니다. 시작하기 전에 아래가 갖춰져 있는지 확인하세요.

docker images        # 내 노트북에 있는 이미지 목록을 보여 줍니다

출력의 REPOSITORY 칸에 vote-apivote-web이 보이고, TAG 칸에 1.0이 있어야 합니다. 만약 1.0은 없고 latest만 보인다면, 9장에서 배운 대로 아래로 1.0 태그를 추가로 붙입니다.

docker tag vote-api:latest vote-api:1.0   # vote-api:latest 에 1.0 이름표 추가
docker tag vote-web:latest vote-web:1.0   # vote-web:latest 에 1.0 이름표 추가

다시 docker images로 두 이미지 모두 1.0 태그가 보이면 준비 완료입니다.

Docker(도커)는 켜져 있어야 합니다. 작업 표시줄(Windows)이나 메뉴 막대(macOS)에 Docker Desktop 고래 아이콘이 떠 있는지 확인하세요. 없으면 Docker Desktop을 실행합니다. (7장에서 하던 그대로입니다.)

10.1 레지스트리란 — 이미지 창고

먼저 오늘의 주인공 개념부터 정리합니다.

용어

레지스트리(Registry) 컨테이너 이미지를 보관하고 공유하는 창고입니다. 내가 만든 이미지를 여기에 올려 두면, 다른 컴퓨터나 서버가 그 창고에서 내려받아 실행할 수 있습니다. 5장에서 "이미지·컨테이너·레지스트리 3형제"로 이미 이름만 만난 개념인데, 이 장에서 직접 써 봅니다.

왜 필요한지 문제 상황부터 봅시다. 지금 내 이미지는 노트북 안에만 있습니다. 그런데 실제로 서비스를 돌릴 곳은 내 노트북이 아니라 AWS의 서버입니다. 그 서버는 내 노트북 하드디스크를 들여다볼 수 없습니다. 그러니 둘 다 접근할 수 있는 중간 창고가 있어야, 내가 창고에 올려 두고 서버가 창고에서 꺼내 쓸 수 있습니다. 그 창고가 레지스트리입니다.

레지스트리에 이미지를 주고받는 동작에는 이름이 있습니다.

용어

푸시(Push)와 풀(Pull) - 푸시(push): 내 이미지를 레지스트리(창고)에 올리는 것. - 풀(pull): 레지스트리에서 이미지를 내려받는 것.

1부에서 Git으로 코드를 다룰 때 쓰던 push/pull과 방향이 같습니다 (올리기 / 내려받기). 다만 그때는 "코드"였고, 여기서는 "이미지"입니다.

그림으로 보면 흐름은 단순합니다. 내 노트북 → (push) → 레지스트리 → (pull) → AWS 서버. 이번 장에서 우리는 이 그림의 앞부분, "내 노트북 → push → 레지스트리"까지를 합니다. 뒷부분(서버가 pull)은 14장 배포에서 AWS가 알아서 합니다.

10.2 레지스트리는 어디에 쓰이나 — 활용 사례

레지스트리는 우리 프로젝트에만 쓰는 특별한 것이 아닙니다. 컨테이너를 쓰는 거의 모든 현장에서 매일 쓰입니다. 실제로 어떻게 쓰이는지 몇 가지 보겠습니다.

정리하면, 레지스트리는 이미지를 만든 쪽과 실행하는 쪽 사이의 공용 창고 입니다. 공개로 열어 두면 Docker Hub 같은 앱스토어가 되고, 비공개로 잠그면 회사·팀 전용 창고가 됩니다.

10.3 [맛보기] Docker Hub에서 공개 이미지 내려받아 보기

레지스트리에서 이미지를 내려받는(pull) 느낌을 우리 프로젝트와 분리해서 아주 작게 한 번 손으로 겪어 봅시다. 공개 레지스트리인 Docker Hub에서 아주 작은 공개 이미지 하나를 내려받아 보는 것입니다.

1단계. Docker Hub가 어떤 창고인지 눈으로 보기

웹브라우저에서 https://hub.docker.com 에 접속합니다. 위쪽 검색창에 alpine 을 입력해 검색하면, 전 세계가 내려받아 쓰는 아주 작은 리눅스 공식 이미지 페이지가 나옵니다. 이게 "공개 창고에 올라가 있는 이미지"의 모습입니다. (로그인·가입은 하지 않아도 구경할 수 있습니다.)

2단계. 그 공개 이미지를 내 노트북으로 pull 하기

VS Code 터미널(vote-app 폴더에서 열린)에 아래를 입력합니다.

docker pull alpine      # Docker Hub 공개 창고에서 alpine 이미지를 내려받는다

여러 줄의 다운로드 메시지가 지나가고 Status: Downloaded ... 또는 Pull complete 같은 문구가 나오면, 방금 여러분은 레지스트리에서 이미지를 pull 한 것입니다. 남이 공용 창고에 올려 둔 이미지를 내 노트북으로 내려받는 경험을 한 것이죠. 확인은 아래로 합니다.

docker images alpine    # 방금 받은 alpine 이미지가 목록에 있는지 확인

alpine 이 한 줄 보이면 성공입니다. 지금 겪은 pull의 반대 방향이, 이 장에서 우리 이미지로 할 push입니다. 이번엔 공개 창고가 아니라 우리 비공개 창고(AWS ECR)에 올린다는 점만 다릅니다.

참고

alpine 이미지는 맛보기용이라 나중에 안 써도 됩니다. 그냥 둬도 용량이 아주 작아 상관없습니다. 지우고 싶다면 docker rmi alpine 으로 지울 수 있습니다.

10.4 AWS ECR — 우리가 쓸 레지스트리

이제 우리가 쓸 레지스트리를 정합니다. 우리는 서비스를 AWS에 배포하므로, 이미지 창고도 AWS 안에 있는 것을 씁니다. 그게 ECR입니다.

용어

AWS ECR(Elastic Container Registry) AWS가 제공하는 비공개 이미지 창고 서비스입니다. 우리는 여기에 vote-api, vote-web 이미지를 올려 두고, 뒤에서(14장) ECS(컨테이너 실행 서비스)가 이 창고에서 이미지를 꺼내(pull) 실행하게 합니다.

왜 Docker Hub가 아니라 ECR을 쓰나

앞에서 본 Docker Hub도 훌륭한 레지스트리입니다. 그런데 우리는 AWS에 배포하기 때문에, 이미지도 AWS 안(ECR)에 두는 편이 여러모로 유리합니다.

한 문장으로: AWS에 배포할 거라면, 이미지 창고도 AWS(ECR)에 두는 게 자연스럽다. 그럼 실제로 만들어 봅시다. 그전에 창고에 이미지를 올리려면 명령어 도구(AWS CLI)가 하나 필요합니다.

10.5 AWS CLI 설치

지금까지 AWS는 웹 화면(콘솔)에서 클릭으로 다뤘습니다. 그런데 이미지를 창고에 올리는 일은 클릭이 아니라 명령어로 합니다. 그 명령어를 쓰게 해 주는 도구가 AWS CLI입니다.

용어

AWS CLI(Command Line Interface) AWS를 웹 화면(콘솔) 대신 터미널 명령어로 다루는 도구입니다. 예를 들어 "ECR 창고에 로그인" 같은 작업을 aws ... 로 시작하는 명령 한 줄로 합니다. 이미지 푸시처럼 명령으로 해야 하는 작업에 씁니다.

설치 방법 (운영체제별)

자신의 운영체제에 맞는 쪽만 따라 하세요. 모든 명령은 VS Code 터미널에서 실행합니다.

brew install awscli      # Homebrew로 AWS CLI 설치

(Homebrew가 없다면 아래 방법 B를 쓰세요. brew 를 쳤을 때 command not found 가 나오면 Homebrew가 없는 것입니다.)

참고

설치가 막히면 Claude Code에게 맡겨도 됩니다. VS Code 터미널에서 claude 를 실행한 뒤 "내 운영체제에 AWS CLI를 설치해 줘" 라고 요청하면 됩니다. 다만 이 장은 흐름을 눈으로 익히기 위해 직접 설치해 보길 권합니다.

[맛보기] 설치가 됐는지 확인하기

설치가 끝나면 터미널을 새로 열어야 방금 설치한 명령이 인식됩니다. 터미널 영역 오른쪽 위의 휴지통 아이콘으로 터미널을 닫고, Terminal → New Terminal로 새 터미널을 연 뒤 아래를 입력합니다.

aws --version      # 설치된 AWS CLI 버전을 보여 줍니다

aws-cli/2.x.x ... 처럼 버전이 한 줄 나오면 설치 성공입니다. (숫자는 사람마다 다를 수 있습니다. 나오기만 하면 됩니다.)

주의

command not found: aws 가 나오면, 터미널이 아직 새 명령을 인식하지 못한 경우가 가장 많습니다. 터미널을 닫고 Terminal → New Terminal로 새로 연 다음 다시 aws --version 을 입력해 보세요. 그래도 안 되면 위 설치 단계를 다시 진행합니다.

10.6 aws configure로 내 자격 증명 등록

AWS CLI를 깔았으니, 이제 "나는 8장에서 만든 그 IAM 사용자다" 라는 것을 CLI에 등록해야 합니다. 그래야 AWS가 "이 사람이 우리 창고에 접근할 권한이 있다"고 알아봅니다. 등록은 aws configure 명령으로 합니다.

VS Code 터미널에 아래를 입력하고 Enter를 누릅니다.

aws configure      # AWS CLI에 내 자격 증명과 기본 리전을 등록

그러면 네 가지를 차례로 하나씩 물어봅니다. 각 줄에 값을 입력하고 Enter를 누르면 다음 질문으로 넘어갑니다. 8장에서 적어 둔 값을 입력하세요.

주의

시크릿 키를 못 찾겠다면 시크릿 액세스 키는 8장에서 발급하는 그 순간에만 화면에 보이고, 그 뒤로는 다시 볼 수 없습니다. 어딘가에 적어 두지 않았다면, 8장 방식대로 IAM 콘솔에서 액세스 키를 새로 발급받아 그 값을 쓰면 됩니다. (기존 키는 그대로 두거나 비활성화해도 됩니다.)

[맛보기] 자격 증명이 등록됐는지 확인하기

제대로 등록됐는지, "지금 내가 누구로 인증되는지"를 AWS에 물어봅시다.

aws sts get-caller-identity      # 지금 CLI가 누구로 인증되는지 AWS에 물어본다

아래처럼 세 값이 나오면 성공입니다.

{
    "UserId": "AIDA...",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/내이름"
}

여기서 Account 값(예: 123456789012)이 여러분의 AWS 계정 번호입니다. 잠시 뒤 이미지를 올릴 때 창고 주소에 이 번호가 들어가니, 이 값을 기억해 두거나 따로 적어 두세요.

주의

Unable to locate credentials 나 권한 오류가 나오면, aws configure 의 키 값이 잘못 입력됐을 가능성이 큽니다. aws configure 를 다시 실행해 액세스 키 ID와 시크릿 키를 정확히 붙여넣으세요. (앞뒤 공백이 섞여 들어가지 않도록 주의합니다.)

10.7 ECR 리포지토리 만들기 (콘솔)

이제 이미지를 담을 창고를 만듭니다. ECR에서 이미지 한 종류를 담는 칸을 "리포지토리"라고 부릅니다.

용어

리포지토리(Repository) 하나의 이미지 종류를 보관하는 입니다. vote-api용 칸, vote-web용 칸을 따로 만듭니다. 각 칸 안에는 그 이미지의 여러 태그(1.0, 2.0 …)가 차곡차곡 쌓입니다. 1부에서 본 GitHub의 리포지토리와 이름은 같지만, 여기서는 "이미지 보관 칸"을 뜻합니다.

우리 서비스는 이미지가 둘(API 서버 vote-api, 프론트엔드 vote-web)이므로, 리포지토리도 둘 만듭니다.

[콘솔] ECR 리포지토리 생성

주의

시작 전에 AWS 콘솔 오른쪽 위 리전이 서울(ap-northeast-2) 인지 확인하세요. (8장에서 강조한 습관입니다. 리전이 다르면 나중에 CLI에서 만든 창고를 못 찾습니다.)

  1. 웹브라우저에서 AWS 콘솔(https://console.aws.amazon.com)에 로그인합니다.
  2. 상단 검색창에 ECR 을 입력하고, 결과에서 Elastic Container Registry를 클릭해 들어갑니다.
  3. 왼쪽 사이드바에서 리포지토리(Repositories)를 클릭합니다.
    • 왼쪽 사이드바가 안 보이면, 화면 왼쪽 위의 메뉴(≡) 아이콘을 눌러 펼칩니다.
  4. 오른쪽 위 리포지토리 생성(Create repository) 버튼을 누릅니다.
  5. 설정 화면에서 아래처럼 입력합니다.
    • 표시 여부(Visibility settings): 프라이빗(Private) 을 선택합니다. (우리 이미지를 아무나 못 보게 비공개로 둡니다.)
    • 리포지토리 이름(Repository name): vote-api 라고 정확히 입력합니다.
    • 그 아래 나머지 옵션(태그 변경 방지, 이미지 스캔, 암호화 등)은 모두 기본값 그대로 둡니다. 손대지 않아도 됩니다.
  6. 화면 맨 아래 리포지토리 생성(Create repository) 버튼을 눌러 만듭니다.
  7. 목록 화면으로 돌아오면, 같은 방법으로 리포지토리를 하나 더 만듭니다. 4~6단계를 반복하되, 이번엔 이름을 vote-web 으로 입력합니다.
참고

콘솔 화면은 조금씩 바뀝니다 AWS 콘솔의 버튼 위치나 문구는 시기에 따라 조금 달라질 수 있습니다. 하지만 "리포지토리 만들기 → 프라이빗 선택 → 이름 입력 → 생성" 이라는 큰 흐름은 같습니다. 버튼 이름이 조금 다르더라도 뜻이 같은 것을 찾아 누르세요.

확인

리포지토리 목록에 vote-apivote-web 두 개가 보이면 성공입니다. 각 줄에는 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/vote-api 같은 주소(URI)가 붙어 있는데, 이 주소가 바로 이미지를 올릴 목적지 주소 입니다. (123456789012 자리에는 여러분의 실제 계정 번호가 들어갑니다.)

용어

리포지토리 URI 리포지토리의 인터넷 주소입니다. 형식은 계정번호.dkr.ecr.리전.amazonaws.com/리포지토리이름 입니다. 이미지를 올리거나(push) 내려받을(pull) 때 이 주소를 씁니다.

10.8 로컬 이미지를 ECR에 올리기 (로그인 → 태그 → 푸시)

이제 내 노트북의 이미지를 방금 만든 ECR 리포지토리로 올립니다(push). 순서는 로그인 → 태그 → 푸시 세 단계입니다.

가장 쉬운 길: 콘솔의 "푸시 명령 보기"

ECR은 초보자를 위해, 이미지를 올리는 데 필요한 명령어를 여러분 계정 번호까지 채워서 그대로 만들어 보여 줍니다. 이걸 복사해 쓰면 계정 번호를 손으로 적을 필요가 없어 가장 확실합니다.

  1. ECR 콘솔의 리포지토리 목록에서 vote-api 를 클릭해 들어갑니다.
  2. 오른쪽 위 푸시 명령 보기(View push commands) 버튼을 누릅니다.
  3. 팝업에 macOS/Linux 탭과 Windows 탭이 있습니다. 내 운영체제에 맞는 탭을 고르면, 그 환경에 맞는 명령 네 줄이 나옵니다. 이 네 줄에는 이미 내 계정 번호와 리전이 채워져 있습니다.

이 네 줄이 무슨 뜻인지 알아 두면 좋습니다. 아래는 vote-api 기준 예시입니다 (123456789012 자리에는 실제 내 계정 번호가 들어갑니다). 실제로는 위 팝업이 보여 주는, 내 계정 번호가 채워진 명령을 복사해 쓰세요.

참고

올리기 전에 태그 확인 사전 조건에서 확인했듯, vote-api:1.0 이미지가 내 노트북에 있어야 합니다. (없다면 docker tag vote-api:latest vote-api:1.0.) 콘솔의 "푸시 명령 보기"는 기본으로 latest 를 올리도록 안내하는데, 우리는 9장에서 배운 대로 1.0 버전 태그로 올릴 것이므로, 아래 예시의 태그 부분을 1.0 으로 맞춰 사용합니다.

VS Code 터미널(vote-app 폴더에서 열린)에서 아래를 실행합니다.

# 1) ECR 창고에 로그인한다 (내 자격 증명으로 창고 문을 연다)
aws ecr get-login-password --region ap-northeast-2 \
  | docker login --username AWS --password-stdin \
    123456789012.dkr.ecr.ap-northeast-2.amazonaws.com

# 2) 로컬 이미지에 ECR 주소가 포함된 태그를 붙인다
docker tag vote-api:1.0 \
  123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/vote-api:1.0

# 3) 그 태그로 ECR에 올린다 (push)
docker push \
  123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/vote-api:1.0

각 단계의 의미는 이렇습니다.

  1. 로그인: 내 AWS 자격 증명으로 ECR 창고에 접근 권한을 얻습니다. get-login-password 가 임시 비밀번호를 만들어 파이프(|)로 docker login 에 넘겨 줍니다. 성공하면 Login Succeeded 라고 나옵니다.
  2. 태그 붙이기: 9장에서 배운 docker tag 입니다. 이미지를 어느 창고로 보낼지가 태그(이름)에 들어 있어야 하므로, ECR 주소가 붙은 이름을 새로 답니다. (vote-api:1.0계정번호.dkr.ecr.ap-northeast-2.amazonaws.com/vote-api:1.0)
  3. 푸시: 그 주소로 이미지를 올립니다. 업로드 진행 막대가 여러 줄 지나가고, 끝에 다이제스트 값과 함께 완료되면 ECR에 이미지가 저장된 것입니다.

이제 vote-web 이미지도 똑같이 올립니다. ECR 콘솔에서 vote-web 리포지토리로 들어가 푸시 명령 보기를 누르고, 위와 같은 방식으로 하되 이름만 vote-web 으로 바꿔 실행합니다.

# vote-web 로그인은 1)과 같은 명령을 그대로 씁니다 (같은 계정의 같은 ECR이므로,
# 방금 로그인했다면 다시 안 해도 됩니다)

# 2) vote-web 에 ECR 주소 태그 붙이기
docker tag vote-web:1.0 \
  123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/vote-web:1.0

# 3) vote-web 올리기
docker push \
  123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/vote-web:1.0
주의

Windows 사용자 — 명령 줄바꿈 기호(\) 이 책의 긴 명령 예시는 macOS/리눅스 방식대로 줄 끝에 \ 를 붙여 여러 줄로 나눠 적었습니다. Git Bash에서는 이대로 동작합니다. 하지만 혹시 PowerShell을 쓴다면 \ 줄바꿈이 안 되니, \ 를 빼고 전체를 한 줄로 이어서 입력하세요. 가장 확실한 방법은, 콘솔의 "푸시 명령 보기"에서 Windows 탭을 골라 거기 나오는 명령을 그대로 복사해 쓰는 것입니다. [프롬프트] Claude Code에게 맡기려면

로컬의 vote-api:1.0, vote-web:1.0 이미지를 서울 리전(ap-northeast-2)의 ECR
리포지토리(vote-api, vote-web)에 각각 올려 줘. 로그인부터 푸시까지 필요한
명령을 실행하고, 각 단계가 뭘 하는지 설명해 줘.

Claude Code가 명령을 실행하며 승인을 요청합니다. 내용을 확인하며 승인하면 됩니다. 다만 처음 한 번은 위 명령을 직접 쳐 보며 흐름을 눈으로 익히길 권합니다.

10.9 올라간 이미지 확인

푸시가 끝났으면, 콘솔에서 이미지가 실제로 창고에 들어갔는지 눈으로 확인합니다.

[콘솔] 확인

  1. ECR 콘솔에서 vote-api 리포지토리로 들어갑니다. (리포지토리 목록에서 vote-api 이름을 클릭.)
  2. 이미지(Images) 목록에 태그 1.0 이 붙은 이미지가 한 줄 보이면 성공입니다.
    • 그 옆에 올라간 시각(Pushed at), 이미지 크기(Image size), 다이제스트 (Image digest, 고유 식별 값)도 함께 표시됩니다.
  3. 뒤로 가서 vote-web 리포지토리도 같은 방식으로 열어, 1.0 태그 이미지가 있는지 확인합니다.
확인

두 리포지토리(vote-api, vote-web)에 각각 1.0 태그 이미지가 보이면, 우리 서비스 이미지가 이제 AWS 창고에 안전하게 보관된 것입니다. 앞으로 새 버전을 만들 때마다 여기에 태그가 하나씩 쌓이고, 이 쌓인 태그들이 3부 롤백 실습의 재료가 됩니다.

참고

이미지가 쌓이면 정리도 필요합니다 배포를 반복하면 ECR에 오래된 이미지가 계속 쌓여 보관 비용이 조금씩 늘 수 있습니다. ECR에는 "오래된 이미지를 자동으로 정리하는 규칙(수명 주기 정책, Lifecycle policy)"을 걸 수 있는데, 지금은 개념만 알아 두고 실습에서는 넘어갑니다.

막히면

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

[확인] 10장 체크리스트

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

여기까지 됐다면, 우리 서비스 이미지가 AWS 창고에 들어간 것입니다.