Chapter 02 - 강사용 답안

EC2 서버 배포 - 정답 및 해설

이 문서의 목적

  • 각 실습 단계의 웹 콘솔 방법AWS CLI 방법을 모두 제공합니다
  • 학생들이 자주 겪는 문제와 해결 방법을 정리했습니다
  • 각 단계의 핵심 포인트와 강의 시 설명할 내용을 포함합니다
강사 전용 문서

이 문서는 강사용 답안입니다. 학생에게 직접 공유하지 마세요. 학생용 가이드는 여기를 참고하세요.

사전 확인
  • Chapter 1의 VPC가 정상적으로 구축되어 있는지 확인하세요
  • 리전이 ap-northeast-2 (서울)인지 확인하세요
  • CLI 답안에서 사용하는 VPC ID, 서브넷 ID 등은 Chapter 1에서 생성한 리소스의 실제 ID로 교체해야 합니다

Step 1. 보안 그룹 생성 (EC2용)

정답

웹 콘솔 방법

  1. AWS 콘솔 > EC2 > 왼쪽 메뉴 네트워크 및 보안 > 보안 그룹
  2. "보안 그룹 생성" 버튼 클릭
  3. 기본 설정 입력:
    • 보안 그룹 이름: ShopEasy-EC2-SG
    • 설명: ShopEasy EC2 보안 그룹 - SSH, API 서버 포트 허용
    • VPC: 드롭다운에서 ShopEasy-VPC 선택
  4. 인바운드 규칙에서 "규칙 추가" 2회 클릭:
    유형프로토콜포트 범위소스설명
    SSHTCP22내 IPSSH 관리 접속
    사용자 지정 TCPTCP5000Anywhere-IPv4 (0.0.0.0/0)API 서버 포트
  5. 아웃바운드 규칙: 기본값 유지 (모든 트래픽 허용)
  6. 태그 추가 (선택):
    • Key: Name, Value: ShopEasy-EC2-SG
    • Key: Project, Value: ShopEasy
  7. "보안 그룹 생성" 버튼 클릭

AWS CLI 방법

먼저 Chapter 1에서 생성한 VPC ID를 확인합니다:

bash
# VPC ID 확인
VPC_ID=$(aws ec2 describe-vpcs \
  --filters "Name=tag:Name,Values=ShopEasy-VPC" \
  --query "Vpcs[0].VpcId" \
  --output text \
  --region ap-northeast-2)

echo "VPC ID: $VPC_ID"

보안 그룹을 생성합니다:

bash
# 보안 그룹 생성
SG_ID=$(aws ec2 create-security-group \
  --group-name ShopEasy-EC2-SG \
  --description "ShopEasy EC2 보안 그룹 - SSH, API 서버 포트 허용" \
  --vpc-id $VPC_ID \
  --query "GroupId" \
  --output text \
  --region ap-northeast-2)

echo "Security Group ID: $SG_ID"

인바운드 규칙을 추가합니다:

bash
# 내 퍼블릭 IP 확인
MY_IP=$(curl -s https://checkip.amazonaws.com)

# SSH (22번 포트) - 내 IP만 허용
aws ec2 authorize-security-group-ingress \
  --group-id $SG_ID \
  --protocol tcp \
  --port 22 \
  --cidr "${MY_IP}/32" \
  --region ap-northeast-2

# API 서버 (5000번 포트) - 모든 IP 허용
aws ec2 authorize-security-group-ingress \
  --group-id $SG_ID \
  --protocol tcp \
  --port 5000 \
  --cidr 0.0.0.0/0 \
  --region ap-northeast-2

태그를 추가합니다:

bash
# 태그 추가
aws ec2 create-tags \
  --resources $SG_ID \
  --tags Key=Name,Value=ShopEasy-EC2-SG Key=Project,Value=ShopEasy \
  --region ap-northeast-2

생성된 보안 그룹을 확인합니다:

bash
# 보안 그룹 확인
aws ec2 describe-security-groups \
  --group-ids $SG_ID \
  --query "SecurityGroups[0].{Name:GroupName,ID:GroupId,InboundRules:IpPermissions}" \
  --region ap-northeast-2
해설: 보안 그룹의 핵심
  • SSH(22) - 내 IP만: 보안상 SSH는 반드시 접속할 IP만 허용해야 합니다. "Anywhere"로 열면 전 세계에서 접속 시도가 들어옵니다
  • 5000 - Anywhere: API 서버는 고객이 접속해야 하므로 모든 IP에서 접근 가능하게 합니다. 실무에서는 로드 밸런서 뒤에 배치합니다
  • 아웃바운드 기본 허용: EC2에서 외부로 나가는 트래픽(npm 패키지 다운로드, git clone 등)을 위해 필요합니다
  • 보안 그룹은 Stateful: 인바운드로 들어온 요청의 응답은 아웃바운드 규칙 없이 자동으로 나갑니다

Step 2. 키 페어 생성

정답

웹 콘솔 방법

  1. AWS 콘솔 > EC2 > 왼쪽 메뉴 네트워크 및 보안 > 키 페어
  2. "키 페어 생성" 버튼 클릭
  3. 설정 입력:
    • 이름: ShopEasy-Key
    • 키 페어 유형: RSA
    • 프라이빗 키 파일 형식: .pem
  4. "키 페어 생성" 버튼 클릭
  5. ShopEasy-Key.pem 파일이 자동 다운로드됨
  6. 다운로드 폴더에서 안전한 위치로 이동:
    bash
    # Mac/Linux
    mkdir -p ~/.ssh
    mv ~/Downloads/ShopEasy-Key.pem ~/.ssh/
    chmod 400 ~/.ssh/ShopEasy-Key.pem
    
    # Windows (Git Bash)
    mkdir -p ~/.ssh
    mv ~/Downloads/ShopEasy-Key.pem ~/.ssh/
    chmod 400 ~/.ssh/ShopEasy-Key.pem

AWS CLI 방법

bash
# 키 페어 생성 및 .pem 파일 저장
aws ec2 create-key-pair \
  --key-name ShopEasy-Key \
  --key-type rsa \
  --query "KeyMaterial" \
  --output text \
  --region ap-northeast-2 > ~/.ssh/ShopEasy-Key.pem

# 권한 설정
chmod 400 ~/.ssh/ShopEasy-Key.pem

# 확인
ls -la ~/.ssh/ShopEasy-Key.pem

키 페어 목록 확인:

bash
aws ec2 describe-key-pairs \
  --key-names ShopEasy-Key \
  --region ap-northeast-2
해설: 키 페어와 권한
  • chmod 400: 소유자에게만 읽기 권한을 부여합니다. SSH 클라이언트는 .pem 파일의 권한이 너무 개방적이면 접속을 거부합니다
  • RSA vs ED25519: RSA는 호환성이 높고, ED25519는 더 안전합니다. 실습에서는 RSA를 사용합니다
  • .pem vs .ppk: .pem은 OpenSSH(Mac/Linux/Git Bash)용, .ppk는 PuTTY(Windows)용입니다
  • CLI로 생성 시 주의: CLI에서 키를 생성하면 프라이빗 키가 콘솔 출력으로 나오므로 반드시 파일로 리다이렉트해야 합니다

Step 3. EC2 인스턴스 생성

정답

웹 콘솔 방법

  1. AWS 콘솔 > EC2 > 인스턴스 > "인스턴스 시작" 버튼 클릭
  2. 이름 및 태그:
    • 이름: ShopEasy-API
  3. 애플리케이션 및 OS 이미지 (AMI):
    • "Amazon Linux" 탭 선택
    • Amazon Linux 2023 AMI 선택 (Free tier eligible 표시 확인)
    • 아키텍처: 64비트 (x86)
  4. 인스턴스 유형:
    • t2.micro 선택 (Free tier eligible)
  5. 키 페어 (로그인):
    • 드롭다운에서 ShopEasy-Key 선택
  6. 네트워크 설정에서 "편집" 버튼 클릭:
    • VPC: ShopEasy-VPC 선택
    • 서브넷: ShopEasy-Public-Subnet-1 선택 (ap-northeast-2a)
    • 퍼블릭 IP 자동 할당: 활성화
    • 방화벽(보안 그룹): "기존 보안 그룹 선택" 클릭
    • 보안 그룹: ShopEasy-EC2-SG 선택
  7. 스토리지 구성:
    • 8 GiB, gp3 (기본값 유지)
  8. "인스턴스 시작" 버튼 클릭
  9. 인스턴스 목록에서 상태가 "실행 중"으로 변경될 때까지 대기 (1~2분)
  10. 인스턴스를 클릭하여 퍼블릭 IPv4 주소를 확인하고 메모

AWS CLI 방법

먼저 필요한 정보를 수집합니다:

bash
# 퍼블릭 서브넷 ID 확인
SUBNET_ID=$(aws ec2 describe-subnets \
  --filters "Name=tag:Name,Values=ShopEasy-Public-Subnet-1" \
  --query "Subnets[0].SubnetId" \
  --output text \
  --region ap-northeast-2)

echo "Subnet ID: $SUBNET_ID"

# 보안 그룹 ID 확인
SG_ID=$(aws ec2 describe-security-groups \
  --filters "Name=group-name,Values=ShopEasy-EC2-SG" \
  --query "SecurityGroups[0].GroupId" \
  --output text \
  --region ap-northeast-2)

echo "Security Group ID: $SG_ID"

# Amazon Linux 2023 최신 AMI ID 확인
AMI_ID=$(aws ec2 describe-images \
  --owners amazon \
  --filters \
    "Name=name,Values=al2023-ami-2023.*-x86_64" \
    "Name=state,Values=available" \
  --query "sort_by(Images, &CreationDate)[-1].ImageId" \
  --output text \
  --region ap-northeast-2)

echo "AMI ID: $AMI_ID"

EC2 인스턴스를 생성합니다:

bash
# EC2 인스턴스 생성
INSTANCE_ID=$(aws ec2 run-instances \
  --image-id $AMI_ID \
  --instance-type t2.micro \
  --key-name ShopEasy-Key \
  --subnet-id $SUBNET_ID \
  --security-group-ids $SG_ID \
  --associate-public-ip-address \
  --tag-specifications \
    'ResourceType=instance,Tags=[{Key=Name,Value=ShopEasy-API},{Key=Project,Value=ShopEasy}]' \
  --query "Instances[0].InstanceId" \
  --output text \
  --region ap-northeast-2)

echo "Instance ID: $INSTANCE_ID"

인스턴스가 실행 상태가 될 때까지 대기합니다:

bash
# 실행 상태까지 대기
aws ec2 wait instance-running \
  --instance-ids $INSTANCE_ID \
  --region ap-northeast-2

echo "인스턴스가 실행 중입니다!"

# 퍼블릭 IP 확인
PUBLIC_IP=$(aws ec2 describe-instances \
  --instance-ids $INSTANCE_ID \
  --query "Reservations[0].Instances[0].PublicIpAddress" \
  --output text \
  --region ap-northeast-2)

echo "Public IP: $PUBLIC_IP"
해설: EC2 인스턴스 생성 핵심
  • AMI 선택: Amazon Linux 2023은 AWS 최적화 리눅스입니다. Ubuntu도 많이 사용하지만, AWS 서비스와의 연동이 기본 설정되어 있어 실습에 적합합니다
  • t2.micro: 프리 티어에서 월 750시간 무료입니다. 실습 후 반드시 인스턴스를 종료하세요
  • 퍼블릭 IP 자동 할당: 이 설정을 빼먹으면 인터넷에서 EC2에 접속할 수 없습니다. 가장 흔한 실수입니다
  • 퍼블릭 서브넷 선택: 반드시 Chapter 1에서 만든 퍼블릭 서브넷에 배치해야 합니다. 프라이빗 서브넷에 배치하면 인터넷 접속이 안 됩니다
  • 보안 그룹: "새 보안 그룹 생성"이 아닌 "기존 보안 그룹 선택"을 클릭해야 Step 1에서 만든 보안 그룹을 사용할 수 있습니다

Step 4. SSH로 EC2 접속

정답

웹 콘솔 + 터미널 방법

퍼블릭 IP 확인:

  1. EC2 콘솔 > 인스턴스 > ShopEasy-API 클릭
  2. "퍼블릭 IPv4 주소" 항목에서 IP 복사 (예: 3.34.xxx.xxx)

.pem 파일 권한 설정 및 SSH 접속:

bash
# .pem 파일 권한 설정 (최초 1회)
chmod 400 ~/.ssh/ShopEasy-Key.pem

# SSH 접속 (IP를 실제 값으로 교체)
ssh -i ~/.ssh/ShopEasy-Key.pem ec2-user@3.34.xxx.xxx

처음 접속 시 fingerprint 확인 메시지가 나타나면 yes를 입력합니다:

text
The authenticity of host '3.34.xxx.xxx' can't be established.
ED25519 key fingerprint is SHA256:xxxxx.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

정상 접속 시 다음과 같은 프롬프트가 표시됩니다:

text
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\           AL2023
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
[ec2-user@ip-10-0-1-xxx ~]$
웹 콘솔에서 직접 접속하기 (EC2 Instance Connect)

SSH 설정이 어려운 학생에게 대안으로 안내할 수 있습니다:

  1. EC2 콘솔 > 인스턴스 > ShopEasy-API 선택
  2. 상단 "연결" 버튼 클릭
  3. "EC2 Instance Connect" 탭에서 "연결" 클릭
  4. 브라우저에서 바로 터미널이 열립니다

단, 보안 그룹에서 SSH(22)가 AWS IP 대역에서도 접근 가능해야 합니다.

AWS CLI로 퍼블릭 IP 확인 후 SSH 접속

bash
# 인스턴스 퍼블릭 IP 확인
PUBLIC_IP=$(aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=ShopEasy-API" "Name=instance-state-name,Values=running" \
  --query "Reservations[0].Instances[0].PublicIpAddress" \
  --output text \
  --region ap-northeast-2)

echo "Public IP: $PUBLIC_IP"

# SSH 접속
ssh -i ~/.ssh/ShopEasy-Key.pem ec2-user@$PUBLIC_IP
해설: SSH 접속 원리
  • chmod 400: SSH 클라이언트는 키 파일의 권한이 644(다른 사용자도 읽기 가능)이면 WARNING: UNPROTECTED PRIVATE KEY FILE! 에러를 발생시킵니다
  • ec2-user: Amazon Linux의 기본 사용자입니다. Ubuntu AMI는 ubuntu, CentOS는 centos입니다
  • -i 옵션: Identity file을 지정합니다. .pem 파일의 경로를 정확하게 입력해야 합니다
  • fingerprint 확인: 첫 접속 시 서버의 공개 키 지문을 확인하는 과정입니다. yes를 입력하면 ~/.ssh/known_hosts에 저장됩니다

Step 5. Node.js 20 LTS 설치

정답

EC2에 접속한 상태에서 실행

방법 1: NodeSource 저장소 사용 (권장)

bash
# NodeSource Node.js 20.x 저장소 설정
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -

# Node.js 설치
sudo dnf install -y nodejs

# 버전 확인
node --version
# 출력 예: v20.x.x

npm --version
# 출력 예: 10.x.x

방법 2: Amazon Linux 2023 기본 저장소 사용

bash
# Amazon Linux 2023 기본 저장소에서 설치 가능한 Node.js 확인
sudo dnf list available nodejs*

# Node.js 설치 (버전은 저장소에 따라 다를 수 있음)
sudo dnf install -y nodejs

# npm이 별도인 경우
sudo dnf install -y npm

# 버전 확인
node --version
npm --version

방법 3: NVM(Node Version Manager) 사용

bash
# NVM 설치
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

# NVM 활성화
source ~/.bashrc

# Node.js 20 LTS 설치
nvm install 20

# 기본 버전으로 설정
nvm use 20
nvm alias default 20

# 버전 확인
node --version
npm --version

AWS CLI + User Data 방법 (인스턴스 생성 시 자동 설치)

EC2 생성 시 User Data 스크립트를 사용하면 Node.js를 자동 설치할 수 있습니다. 참고용으로 제공합니다:

bash
# User Data 스크립트 (EC2 생성 시 --user-data 옵션으로 전달)
#!/bin/bash
curl -fsSL https://rpm.nodesource.com/setup_20.x | bash -
dnf install -y nodejs git
su - ec2-user -c "cd /home/ec2-user && git clone https://github.com/shopeasydeploy/ecommerce-app.git"
su - ec2-user -c "cd /home/ec2-user/ecommerce-app/api-server && npm install"
User Data 참고 사항

User Data는 인스턴스 최초 생성 시에만 실행됩니다. 실습에서는 직접 설치하는 것을 권장합니다 (학습 효과가 높음).

해설: Node.js 설치 방법 비교
방법장점단점추천 상황
NodeSource 특정 버전 정확히 설치, 간단 외부 저장소 의존 실습, 프로덕션
dnf 기본 Amazon 공식 패키지 최신 버전이 아닐 수 있음 버전이 크게 중요하지 않을 때
NVM 여러 버전 관리 가능 설정이 조금 복잡 개발 환경

Step 6. ShopEasy 코드 가져오기

정답

EC2에 접속한 상태에서 실행

bash
# git이 설치되어 있는지 확인
git --version
# 출력 예: git version 2.40.x

# 없다면 설치
sudo dnf install -y git

# 홈 디렉토리로 이동
cd ~

# ShopEasy 코드 클론
git clone https://github.com/shopeasydeploy/ecommerce-app.git

# 디렉토리 구조 확인
ls -la ecommerce-app/

클론 후 디렉토리 구조:

text
ecommerce-app/
├── api-server/          # Node.js Express API 서버
│   ├── src/
│   │   ├── app.js       # 메인 진입점
│   │   ├── config/      # 설정 파일 (DB, AWS 등)
│   │   ├── routes/      # API 라우트
│   │   ├── middleware/   # 미들웨어
│   │   └── services/    # 비즈니스 로직
│   ├── data/            # SQLite DB 파일
│   ├── uploads/         # 업로드 파일 임시 저장
│   ├── package.json     # npm 패키지 정보
│   ├── seed.js          # 초기 데이터 시드
│   └── .env             # 환경 변수 (기본값 포함)
└── frontend/            # React 프론트엔드
    ├── public/
    └── src/

AWS CLI (참고: SSM Run Command로 원격 실행)

SSH 없이 AWS Systems Manager로 EC2에 명령을 전송할 수도 있습니다 (IAM 역할 필요):

bash
# 참고: SSM으로 EC2에 git clone 명령 전송
# (이 방법은 IAM 역할 설정이 필요하므로 Chapter 4 이후에 가능합니다)
aws ssm send-command \
  --instance-ids $INSTANCE_ID \
  --document-name "AWS-RunShellScript" \
  --parameters 'commands=["su - ec2-user -c \"cd /home/ec2-user && git clone https://github.com/shopeasydeploy/ecommerce-app.git\""]' \
  --region ap-northeast-2
참고

현재 단계에서는 SSH로 직접 접속하여 실행하는 것이 가장 간단합니다.

해설: 코드 배포
  • git clone: GitHub에서 코드를 EC2 서버로 복사합니다. 실무에서는 CI/CD 파이프라인을 사용하지만, 학습 단계에서는 직접 clone하는 것이 이해하기 좋습니다
  • .env 파일: 환경 변수 설정 파일입니다. 현재는 기본값(SQLite 모드)으로 동작합니다. Chapter 3에서 RDS 연결 정보로 수정할 예정입니다
  • api-server vs frontend: 이번 챕터에서는 api-server만 실행합니다. frontend는 Chapter 6에서 S3에 배포합니다

Step 7. API 서버 시작

정답

EC2에 접속한 상태에서 실행

포그라운드 실행 (기본):

bash
# api-server 디렉토리로 이동
cd ~/ecommerce-app/api-server

# npm 패키지 설치
npm install

# 초기 데이터 시드 (선택 - 샘플 상품 데이터 생성)
npm run seed

# API 서버 시작
npm start

정상 시작 시 출력:

text
Server is running on port 5000
Database initialized (SQLite mode)
Sample data seeded successfully

백그라운드 실행 (SSH 종료 후에도 서버 유지):

bash
# 방법 1: nohup 사용
cd ~/ecommerce-app/api-server
nohup npm start > app.log 2>&1 &

# 프로세스 확인
ps aux | grep node

# 로그 확인
tail -f app.log

# 서버 중지가 필요할 때
kill $(lsof -t -i:5000)
bash
# 방법 2: PM2 사용 (프로세스 매니저)
sudo npm install -g pm2

cd ~/ecommerce-app/api-server
pm2 start npm --name "shopeasy-api" -- start

# PM2 상태 확인
pm2 status

# 로그 확인
pm2 logs shopeasy-api

# 서버 중지
pm2 stop shopeasy-api

# 서버 재시작
pm2 restart shopeasy-api

AWS CLI (참고)

이 단계는 EC2 내부 작업이므로 AWS CLI가 아닌 SSH 접속 후 직접 실행합니다.

참고로, 실무에서는 User Data 스크립트나 CodeDeploy를 사용하여 자동화합니다.

해설: 서버 실행 방법 비교
방법SSH 종료 시자동 재시작로그 관리추천
npm start 서버 종료됨 없음 터미널 출력 테스트용
nohup 계속 실행 없음 파일로 저장 실습용
PM2 계속 실행 있음 자동 관리 프로덕션

Step 8. API 서버 헬스 체크

정답

웹 브라우저에서 확인

브라우저 주소창에 다음 URL을 입력합니다:

text
http://3.34.xxx.xxx:5000/api/health

3.34.xxx.xxx를 실제 EC2 퍼블릭 IP로 교체합니다.

예상 응답:

json
{
  "status": "ok",
  "timestamp": "2026-03-03T09:30:00.000Z",
  "uptime": 45.23,
  "database": "sqlite"
}

또는 EC2 내부에서 curl로 확인:

bash
# EC2 내부에서 (새 SSH 세션 또는 백그라운드 실행 시)
curl http://localhost:5000/api/health

# 외부에서 (로컬 터미널에서)
curl http://3.34.xxx.xxx:5000/api/health

AWS CLI로 IP 확인 후 curl

bash
# EC2 퍼블릭 IP 확인
PUBLIC_IP=$(aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=ShopEasy-API" "Name=instance-state-name,Values=running" \
  --query "Reservations[0].Instances[0].PublicIpAddress" \
  --output text \
  --region ap-northeast-2)

# 헬스 체크
curl http://${PUBLIC_IP}:5000/api/health
해설: 헬스 체크의 중요성
  • /api/health 엔드포인트는 서버의 상태를 빠르게 확인하는 용도입니다
  • 실무에서는 로드 밸런서가 이 엔드포인트를 주기적으로 호출하여 서버 상태를 모니터링합니다
  • 응답에 DB 연결 상태, 메모리 사용량 등을 포함하면 더 유용합니다
  • http가 아닌 https로 접속하면 SSL 인증서가 없으므로 접속이 안 됩니다

Step 9. 상품 목록 API 확인

정답

웹 브라우저에서 확인

브라우저 주소창에 다음 URL을 입력합니다:

text
http://3.34.xxx.xxx:5000/api/products

예상 응답 (샘플):

json
[
  {
    "id": 1,
    "name": "무선 블루투스 이어폰",
    "price": 45000,
    "description": "고음질 무선 이어폰",
    "category": "전자기기",
    "stock": 100,
    "image_url": "/uploads/earphone.jpg"
  },
  {
    "id": 2,
    "name": "스테인리스 텀블러 500ml",
    "price": 25000,
    "description": "보온보냉 텀블러",
    "category": "주방용품",
    "stock": 200,
    "image_url": "/uploads/tumbler.jpg"
  },
  ...
]

curl로 보기 좋게 출력:

bash
# EC2 내부에서
curl -s http://localhost:5000/api/products | python3 -m json.tool

# 외부에서
curl -s http://3.34.xxx.xxx:5000/api/products | python3 -m json.tool

# 상품 개수 확인
curl -s http://localhost:5000/api/products | python3 -c "import json,sys; print(len(json.load(sys.stdin)))"

AWS CLI로 IP 확인 후 curl

bash
# EC2 퍼블릭 IP 확인
PUBLIC_IP=$(aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=ShopEasy-API" "Name=instance-state-name,Values=running" \
  --query "Reservations[0].Instances[0].PublicIpAddress" \
  --output text \
  --region ap-northeast-2)

# 상품 목록 확인
curl -s http://${PUBLIC_IP}:5000/api/products | python3 -m json.tool
해설: API 동작 확인
  • 상품 목록이 반환되면 EC2 + Node.js + API 서버가 모두 정상적으로 동작하는 것입니다
  • 현재는 SQLite(내장 DB)를 사용하고 있어 EC2를 종료하면 데이터가 사라집니다
  • Chapter 3에서 RDS MySQL로 전환하면 데이터가 영구적으로 보존됩니다
  • npm run seed를 실행하지 않았다면 빈 배열 []이 반환될 수 있습니다

문제 해결 (Troubleshooting)

자주 발생하는 문제와 해결 방법

SSH 접속 실패: "Connection timed out"

원인: 보안 그룹, 서브넷, 라우트 테이블 설정 문제

확인 사항:

  • 보안 그룹에 SSH(22번 포트)가 내 IP로 허용되어 있는지 확인
  • 내 IP가 변경되었을 수 있음 (카페, VPN 사용 시) - 보안 그룹에서 "내 IP"로 다시 설정
  • EC2가 퍼블릭 서브넷에 있는지 확인
  • 퍼블릭 서브넷의 라우트 테이블에 IGW(0.0.0.0/0 → IGW)가 있는지 확인
  • EC2에 퍼블릭 IP가 할당되어 있는지 확인
bash
# 현재 내 IP 확인
curl https://checkip.amazonaws.com

# 보안 그룹 규칙 확인
aws ec2 describe-security-groups \
  --filters "Name=group-name,Values=ShopEasy-EC2-SG" \
  --query "SecurityGroups[0].IpPermissions" \
  --region ap-northeast-2
SSH 접속 실패: "Permission denied (publickey)"

원인: .pem 파일 경로 오류, 권한 문제, 또는 잘못된 사용자 이름

해결 방법:

  • .pem 파일 경로가 정확한지 확인: ls -la ~/.ssh/ShopEasy-Key.pem
  • 파일 권한 확인: chmod 400 ~/.ssh/ShopEasy-Key.pem
  • 사용자 이름이 ec2-user인지 확인 (Ubuntu AMI는 ubuntu)
  • 키 페어가 EC2 생성 시 선택한 것과 동일한지 확인
SSH 접속 실패: "WARNING: UNPROTECTED PRIVATE KEY FILE!"

원인: .pem 파일의 권한이 너무 개방적임

해결 방법:

bash
# Mac/Linux/Git Bash
chmod 400 ~/.ssh/ShopEasy-Key.pem

# Windows PowerShell (Git Bash에서 chmod가 안 될 때)
icacls "C:\Users\%USERNAME%\.ssh\ShopEasy-Key.pem" /inheritance:r /grant:r "%USERNAME%:R"
Node.js 설치 실패: "No match for argument: nodejs"

원인: NodeSource 저장소 설정이 안 됨

해결 방법:

bash
# NodeSource 저장소 설정
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -

# 캐시 클리어 후 재시도
sudo dnf clean all
sudo dnf install -y nodejs

# 그래도 안 되면 NVM 사용
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install 20
npm install 실패: "EACCES permission denied"

원인: npm 글로벌 패키지 디렉토리 권한 문제

해결 방법:

bash
# npm install은 sudo 없이 실행 (프로젝트 로컬 설치)
cd ~/ecommerce-app/api-server
npm install

# 글로벌 설치(pm2 등)만 sudo 사용
sudo npm install -g pm2
포트 5000 접속 불가: 브라우저에서 응답 없음

원인: 여러 가지 가능성이 있습니다

체크리스트:

  1. 서버가 실행 중인지 확인:
    bash
    # EC2에서 실행
    curl http://localhost:5000/api/health
    # 이것이 동작하면 서버는 정상, 네트워크 문제
    
    # 프로세스 확인
    ps aux | grep node
    
    # 5000 포트 사용 확인
    sudo lsof -i :5000
  2. 보안 그룹 5000 포트가 열려 있는지 확인: EC2 콘솔 > 인스턴스 > 보안 탭 > 인바운드 규칙에 5000/tcp가 있는지 확인
  3. URL에 https 대신 http를 사용하고 있는지 확인: 일부 브라우저가 자동으로 https로 전환합니다. 주소창에 http://를 명시적으로 입력하세요
  4. 서버가 0.0.0.0에서 리슨하고 있는지 확인: 127.0.0.1에서만 리슨하면 외부에서 접속이 안 됩니다. app.js의 listen 부분이 app.listen(5000, '0.0.0.0')인지 확인
  5. 퍼블릭 IP가 할당되어 있는지 확인
git clone 실패: "Could not resolve host"

원인: EC2의 인터넷 연결 문제

해결 방법:

bash
# DNS 확인
nslookup github.com

# 인터넷 연결 확인
ping -c 3 google.com

# VPC DNS 설정 확인 (AWS 콘솔)
# VPC > ShopEasy-VPC > 작업 > DNS 호스트 이름 편집 > 활성화

문제가 지속되면 서브넷의 라우트 테이블에 IGW(0.0.0.0/0)가 설정되어 있는지 확인합니다.

npm start 실패: "Error: Cannot find module"

원인: npm install이 제대로 완료되지 않았거나, 올바른 디렉토리가 아님

해결 방법:

bash
# 현재 디렉토리 확인
pwd
# /home/ec2-user/ecommerce-app/api-server 여야 합니다

# node_modules 삭제 후 재설치
rm -rf node_modules package-lock.json
npm install

# 다시 시작
npm start
EC2 인스턴스 생성 시 "InsufficientInstanceCapacity" 오류

원인: 해당 가용 영역에 t2.micro 용량이 부족

해결 방법:

  • 다른 가용 영역의 서브넷(ShopEasy-Public-Subnet-2)을 선택하여 시도
  • 몇 분 후 다시 시도
  • t3.micro로 변경 시도 (프리 티어에 포함될 수 있음)

강의 노트

강의 진행 팁
  • 시간 배분 (약 90분):
    • 개념 설명: 20분 (EC2, 보안 그룹, 키 페어)
    • Step 1~3 (보안 그룹, 키 페어, EC2 생성): 25분
    • Step 4~5 (SSH 접속, Node.js 설치): 20분
    • Step 6~9 (코드 배포, 서버 실행, 확인): 20분
    • 트러블슈팅 및 Q&A: 5분
  • 학생들이 가장 많이 막히는 부분:
    1. SSH 접속 - .pem 파일 경로와 권한 설정
    2. 네트워크 설정 - 퍼블릭 IP 자동 할당을 빼먹음
    3. 보안 그룹에서 VPC를 ShopEasy-VPC로 선택하지 않음
    4. 브라우저에서 https 대신 http를 입력해야 하는 것
  • 핵심 강조 포인트:
    • 보안 그룹의 Stateful 특성 (인바운드로 들어온 요청의 응답은 자동으로 나감)
    • SSH 키 페어의 중요성 (.pem 파일을 분실하면 접속 불가)
    • 퍼블릭 vs 프라이빗 서브넷의 차이 (API 서버는 왜 퍼블릭인지)
    • 현재 SQLite 사용 → Chapter 3에서 RDS로 전환 예정
보충 설명 자료

보안 그룹 vs NACL 비교

특성보안 그룹 (Security Group)NACL (Network ACL)
적용 레벨인스턴스 레벨서브넷 레벨
상태Stateful (응답 자동 허용)Stateless (양방향 규칙 필요)
규칙허용 규칙만허용 + 거부 규칙
평가 방식모든 규칙 평가번호 순서대로 평가
기본 동작모든 인바운드 거부모든 트래픽 허용 (기본 NACL)

인스턴스 타입 네이밍 규칙

t2.micro에서:

  • t = 패밀리 (범용 버스트형)
  • 2 = 세대 (2세대)
  • micro = 크기 (nano < micro < small < medium < large)

리소스 정리 (수업 종료 시)

과금 주의!

수업이 끝나면 EC2 인스턴스를 반드시 중지(Stop)하세요. 종료(Terminate)는 Chapter 3 이후에도 사용하므로, 아직은 중지만 합니다.

t2.micro는 프리 티어에서 월 750시간 무료이지만, 습관적으로 중지하는 것이 좋습니다.

EC2 인스턴스 중지

  1. EC2 콘솔 > 인스턴스
  2. ShopEasy-API 인스턴스 선택
  3. 인스턴스 상태 > 인스턴스 중지
  4. 확인 대화 상자에서 "중지" 클릭
중지 vs 종료
  • 중지(Stop): 전원을 끄는 것. EBS 볼륨(디스크)은 유지됨. 다시 시작 가능. 퍼블릭 IP는 변경될 수 있음
  • 종료(Terminate): 인스턴스를 완전히 삭제. EBS도 기본적으로 삭제됨. 복구 불가
bash
# 인스턴스 중지
INSTANCE_ID=$(aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=ShopEasy-API" "Name=instance-state-name,Values=running" \
  --query "Reservations[0].Instances[0].InstanceId" \
  --output text \
  --region ap-northeast-2)

aws ec2 stop-instances \
  --instance-ids $INSTANCE_ID \
  --region ap-northeast-2

echo "인스턴스 중지 중: $INSTANCE_ID"

# 중지 완료 대기
aws ec2 wait instance-stopped \
  --instance-ids $INSTANCE_ID \
  --region ap-northeast-2

echo "인스턴스가 중지되었습니다."
다음 수업 시작 시

다음 수업(Chapter 3)에서는 EC2 인스턴스를 다시 시작해야 합니다:

  1. EC2 콘솔 > 인스턴스 > ShopEasy-API 선택 > 인스턴스 상태 > 인스턴스 시작
  2. 퍼블릭 IP가 변경될 수 있으므로 새 IP를 확인하세요
  3. SSH 접속 후 cd ~/ecommerce-app/api-server && npm start로 서버를 다시 시작합니다