EC2 서버 배포 - 정답 및 해설
이 문서의 목적
- 각 실습 단계의 웹 콘솔 방법과 AWS CLI 방법을 모두 제공합니다
- 학생들이 자주 겪는 문제와 해결 방법을 정리했습니다
- 각 단계의 핵심 포인트와 강의 시 설명할 내용을 포함합니다
이 문서는 강사용 답안입니다. 학생에게 직접 공유하지 마세요. 학생용 가이드는 여기를 참고하세요.
- Chapter 1의 VPC가 정상적으로 구축되어 있는지 확인하세요
- 리전이 ap-northeast-2 (서울)인지 확인하세요
- CLI 답안에서 사용하는 VPC ID, 서브넷 ID 등은 Chapter 1에서 생성한 리소스의 실제 ID로 교체해야 합니다
Step 1. 보안 그룹 생성 (EC2용)
정답
웹 콘솔 방법
- AWS 콘솔 > EC2 > 왼쪽 메뉴 네트워크 및 보안 > 보안 그룹
- "보안 그룹 생성" 버튼 클릭
- 기본 설정 입력:
- 보안 그룹 이름:
ShopEasy-EC2-SG - 설명:
ShopEasy EC2 보안 그룹 - SSH, API 서버 포트 허용 - VPC: 드롭다운에서 ShopEasy-VPC 선택
- 보안 그룹 이름:
- 인바운드 규칙에서 "규칙 추가" 2회 클릭:
유형 프로토콜 포트 범위 소스 설명 SSH TCP 22 내 IP SSH 관리 접속 사용자 지정 TCP TCP 5000 Anywhere-IPv4 (0.0.0.0/0) API 서버 포트 - 아웃바운드 규칙: 기본값 유지 (모든 트래픽 허용)
- 태그 추가 (선택):
- Key:
Name, Value:ShopEasy-EC2-SG - Key:
Project, Value:ShopEasy
- Key:
- "보안 그룹 생성" 버튼 클릭
AWS CLI 방법
먼저 Chapter 1에서 생성한 VPC ID를 확인합니다:
# 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"
보안 그룹을 생성합니다:
# 보안 그룹 생성
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"
인바운드 규칙을 추가합니다:
# 내 퍼블릭 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
태그를 추가합니다:
# 태그 추가
aws ec2 create-tags \
--resources $SG_ID \
--tags Key=Name,Value=ShopEasy-EC2-SG Key=Project,Value=ShopEasy \
--region ap-northeast-2
생성된 보안 그룹을 확인합니다:
# 보안 그룹 확인
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. 키 페어 생성
정답
웹 콘솔 방법
- AWS 콘솔 > EC2 > 왼쪽 메뉴 네트워크 및 보안 > 키 페어
- "키 페어 생성" 버튼 클릭
- 설정 입력:
- 이름:
ShopEasy-Key - 키 페어 유형: RSA
- 프라이빗 키 파일 형식: .pem
- 이름:
- "키 페어 생성" 버튼 클릭
ShopEasy-Key.pem파일이 자동 다운로드됨- 다운로드 폴더에서 안전한 위치로 이동:
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 방법
# 키 페어 생성 및 .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
키 페어 목록 확인:
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 인스턴스 생성
정답
웹 콘솔 방법
- AWS 콘솔 > EC2 > 인스턴스 > "인스턴스 시작" 버튼 클릭
- 이름 및 태그:
- 이름:
ShopEasy-API
- 이름:
- 애플리케이션 및 OS 이미지 (AMI):
- "Amazon Linux" 탭 선택
- Amazon Linux 2023 AMI 선택 (Free tier eligible 표시 확인)
- 아키텍처: 64비트 (x86)
- 인스턴스 유형:
- t2.micro 선택 (Free tier eligible)
- 키 페어 (로그인):
- 드롭다운에서 ShopEasy-Key 선택
- 네트워크 설정에서 "편집" 버튼 클릭:
- VPC: ShopEasy-VPC 선택
- 서브넷: ShopEasy-Public-Subnet-1 선택 (ap-northeast-2a)
- 퍼블릭 IP 자동 할당: 활성화
- 방화벽(보안 그룹): "기존 보안 그룹 선택" 클릭
- 보안 그룹: ShopEasy-EC2-SG 선택
- 스토리지 구성:
- 8 GiB, gp3 (기본값 유지)
- "인스턴스 시작" 버튼 클릭
- 인스턴스 목록에서 상태가 "실행 중"으로 변경될 때까지 대기 (1~2분)
- 인스턴스를 클릭하여 퍼블릭 IPv4 주소를 확인하고 메모
AWS CLI 방법
먼저 필요한 정보를 수집합니다:
# 퍼블릭 서브넷 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 인스턴스를 생성합니다:
# 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"
인스턴스가 실행 상태가 될 때까지 대기합니다:
# 실행 상태까지 대기
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"
- AMI 선택: Amazon Linux 2023은 AWS 최적화 리눅스입니다. Ubuntu도 많이 사용하지만, AWS 서비스와의 연동이 기본 설정되어 있어 실습에 적합합니다
- t2.micro: 프리 티어에서 월 750시간 무료입니다. 실습 후 반드시 인스턴스를 종료하세요
- 퍼블릭 IP 자동 할당: 이 설정을 빼먹으면 인터넷에서 EC2에 접속할 수 없습니다. 가장 흔한 실수입니다
- 퍼블릭 서브넷 선택: 반드시 Chapter 1에서 만든 퍼블릭 서브넷에 배치해야 합니다. 프라이빗 서브넷에 배치하면 인터넷 접속이 안 됩니다
- 보안 그룹: "새 보안 그룹 생성"이 아닌 "기존 보안 그룹 선택"을 클릭해야 Step 1에서 만든 보안 그룹을 사용할 수 있습니다
Step 4. SSH로 EC2 접속
정답
웹 콘솔 + 터미널 방법
퍼블릭 IP 확인:
- EC2 콘솔 > 인스턴스 > ShopEasy-API 클릭
- "퍼블릭 IPv4 주소" 항목에서 IP 복사 (예:
3.34.xxx.xxx)
.pem 파일 권한 설정 및 SSH 접속:
# .pem 파일 권한 설정 (최초 1회)
chmod 400 ~/.ssh/ShopEasy-Key.pem
# SSH 접속 (IP를 실제 값으로 교체)
ssh -i ~/.ssh/ShopEasy-Key.pem ec2-user@3.34.xxx.xxx
처음 접속 시 fingerprint 확인 메시지가 나타나면 yes를 입력합니다:
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
정상 접속 시 다음과 같은 프롬프트가 표시됩니다:
, #_
~\_ ####_ Amazon Linux 2023
~~ \_#####\ AL2023
~~ \###|
~~ \#/ ___ https://aws.amazon.com/linux/amazon-linux-2023
~~ V~' '->
~~~ /
~~._. _/
_/ _/
_/m/'
[ec2-user@ip-10-0-1-xxx ~]$
SSH 설정이 어려운 학생에게 대안으로 안내할 수 있습니다:
- EC2 콘솔 > 인스턴스 > ShopEasy-API 선택
- 상단 "연결" 버튼 클릭
- "EC2 Instance Connect" 탭에서 "연결" 클릭
- 브라우저에서 바로 터미널이 열립니다
단, 보안 그룹에서 SSH(22)가 AWS IP 대역에서도 접근 가능해야 합니다.
AWS CLI로 퍼블릭 IP 확인 후 SSH 접속
# 인스턴스 퍼블릭 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
- 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 저장소 사용 (권장)
# 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 기본 저장소 사용
# 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) 사용
# 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를 자동 설치할 수 있습니다. 참고용으로 제공합니다:
# 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는 인스턴스 최초 생성 시에만 실행됩니다. 실습에서는 직접 설치하는 것을 권장합니다 (학습 효과가 높음).
| 방법 | 장점 | 단점 | 추천 상황 |
|---|---|---|---|
| NodeSource | 특정 버전 정확히 설치, 간단 | 외부 저장소 의존 | 실습, 프로덕션 |
| dnf 기본 | Amazon 공식 패키지 | 최신 버전이 아닐 수 있음 | 버전이 크게 중요하지 않을 때 |
| NVM | 여러 버전 관리 가능 | 설정이 조금 복잡 | 개발 환경 |
Step 6. ShopEasy 코드 가져오기
정답
EC2에 접속한 상태에서 실행
# 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/
클론 후 디렉토리 구조:
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 역할 필요):
# 참고: 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에 접속한 상태에서 실행
포그라운드 실행 (기본):
# api-server 디렉토리로 이동
cd ~/ecommerce-app/api-server
# npm 패키지 설치
npm install
# 초기 데이터 시드 (선택 - 샘플 상품 데이터 생성)
npm run seed
# API 서버 시작
npm start
정상 시작 시 출력:
Server is running on port 5000
Database initialized (SQLite mode)
Sample data seeded successfully
백그라운드 실행 (SSH 종료 후에도 서버 유지):
# 방법 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)
# 방법 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을 입력합니다:
http://3.34.xxx.xxx:5000/api/health
3.34.xxx.xxx를 실제 EC2 퍼블릭 IP로 교체합니다.
예상 응답:
{
"status": "ok",
"timestamp": "2026-03-03T09:30:00.000Z",
"uptime": 45.23,
"database": "sqlite"
}
또는 EC2 내부에서 curl로 확인:
# EC2 내부에서 (새 SSH 세션 또는 백그라운드 실행 시)
curl http://localhost:5000/api/health
# 외부에서 (로컬 터미널에서)
curl http://3.34.xxx.xxx:5000/api/health
AWS CLI로 IP 확인 후 curl
# 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을 입력합니다:
http://3.34.xxx.xxx:5000/api/products
예상 응답 (샘플):
[
{
"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로 보기 좋게 출력:
# 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
# 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
- 상품 목록이 반환되면 EC2 + Node.js + API 서버가 모두 정상적으로 동작하는 것입니다
- 현재는 SQLite(내장 DB)를 사용하고 있어 EC2를 종료하면 데이터가 사라집니다
- Chapter 3에서 RDS MySQL로 전환하면 데이터가 영구적으로 보존됩니다
npm run seed를 실행하지 않았다면 빈 배열[]이 반환될 수 있습니다
문제 해결 (Troubleshooting)
자주 발생하는 문제와 해결 방법
원인: 보안 그룹, 서브넷, 라우트 테이블 설정 문제
확인 사항:
- 보안 그룹에 SSH(22번 포트)가 내 IP로 허용되어 있는지 확인
- 내 IP가 변경되었을 수 있음 (카페, VPN 사용 시) - 보안 그룹에서 "내 IP"로 다시 설정
- EC2가 퍼블릭 서브넷에 있는지 확인
- 퍼블릭 서브넷의 라우트 테이블에 IGW(0.0.0.0/0 → IGW)가 있는지 확인
- EC2에 퍼블릭 IP가 할당되어 있는지 확인
# 현재 내 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
원인: .pem 파일 경로 오류, 권한 문제, 또는 잘못된 사용자 이름
해결 방법:
- .pem 파일 경로가 정확한지 확인:
ls -la ~/.ssh/ShopEasy-Key.pem - 파일 권한 확인:
chmod 400 ~/.ssh/ShopEasy-Key.pem - 사용자 이름이
ec2-user인지 확인 (Ubuntu AMI는ubuntu) - 키 페어가 EC2 생성 시 선택한 것과 동일한지 확인
원인: .pem 파일의 권한이 너무 개방적임
해결 방법:
# 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"
원인: NodeSource 저장소 설정이 안 됨
해결 방법:
# 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 글로벌 패키지 디렉토리 권한 문제
해결 방법:
# npm install은 sudo 없이 실행 (프로젝트 로컬 설치)
cd ~/ecommerce-app/api-server
npm install
# 글로벌 설치(pm2 등)만 sudo 사용
sudo npm install -g pm2
원인: 여러 가지 가능성이 있습니다
체크리스트:
- 서버가 실행 중인지 확인:
bash
# EC2에서 실행 curl http://localhost:5000/api/health # 이것이 동작하면 서버는 정상, 네트워크 문제 # 프로세스 확인 ps aux | grep node # 5000 포트 사용 확인 sudo lsof -i :5000 - 보안 그룹 5000 포트가 열려 있는지 확인: EC2 콘솔 > 인스턴스 > 보안 탭 > 인바운드 규칙에 5000/tcp가 있는지 확인
- URL에 https 대신 http를 사용하고 있는지 확인:
일부 브라우저가 자동으로 https로 전환합니다. 주소창에
http://를 명시적으로 입력하세요 - 서버가 0.0.0.0에서 리슨하고 있는지 확인:
127.0.0.1에서만 리슨하면 외부에서 접속이 안 됩니다. app.js의 listen 부분이app.listen(5000, '0.0.0.0')인지 확인 - 퍼블릭 IP가 할당되어 있는지 확인
원인: EC2의 인터넷 연결 문제
해결 방법:
# DNS 확인
nslookup github.com
# 인터넷 연결 확인
ping -c 3 google.com
# VPC DNS 설정 확인 (AWS 콘솔)
# VPC > ShopEasy-VPC > 작업 > DNS 호스트 이름 편집 > 활성화
문제가 지속되면 서브넷의 라우트 테이블에 IGW(0.0.0.0/0)가 설정되어 있는지 확인합니다.
원인: npm install이 제대로 완료되지 않았거나, 올바른 디렉토리가 아님
해결 방법:
# 현재 디렉토리 확인
pwd
# /home/ec2-user/ecommerce-app/api-server 여야 합니다
# node_modules 삭제 후 재설치
rm -rf node_modules package-lock.json
npm install
# 다시 시작
npm start
원인: 해당 가용 영역에 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분
-
학생들이 가장 많이 막히는 부분:
- SSH 접속 - .pem 파일 경로와 권한 설정
- 네트워크 설정 - 퍼블릭 IP 자동 할당을 빼먹음
- 보안 그룹에서 VPC를 ShopEasy-VPC로 선택하지 않음
- 브라우저에서 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 인스턴스 중지
- EC2 콘솔 > 인스턴스
- ShopEasy-API 인스턴스 선택
- 인스턴스 상태 > 인스턴스 중지
- 확인 대화 상자에서 "중지" 클릭
- 중지(Stop): 전원을 끄는 것. EBS 볼륨(디스크)은 유지됨. 다시 시작 가능. 퍼블릭 IP는 변경될 수 있음
- 종료(Terminate): 인스턴스를 완전히 삭제. EBS도 기본적으로 삭제됨. 복구 불가
# 인스턴스 중지
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 인스턴스를 다시 시작해야 합니다:
- EC2 콘솔 > 인스턴스 > ShopEasy-API 선택 > 인스턴스 상태 > 인스턴스 시작
- 퍼블릭 IP가 변경될 수 있으므로 새 IP를 확인하세요
- SSH 접속 후
cd ~/ecommerce-app/api-server && npm start로 서버를 다시 시작합니다