CORS(Cross-Origin Resource Sharing) 오류는 브라우저의 보안 정책 때문에 발생한다.

현재 상황

  • 프론트엔드: http://localhost:3000
  • API 서버: http://localhost:3001
  • 포트가 다르면 다른 Origin으로 간주됩니다.

CORS 동작 방식

1. 브라우저가 요청을 보냅니다

  • 클라이언트에서 http://localhost:3001/api/test로 요청

2. 브라우저가 Origin을 확인합니다

  • 요청 출처: http://localhost:3000
  • 요청 대상: http://localhost:3001
  • Origin이 다르므로 CORS 정책 적용

3. API 서버의 응답 헤더를 확인합니다

  • 브라우저는 응답에 Access-Control-Allow-Origin 헤더가 있는지 확인
  • 없거나 현재 Origin(http://localhost:3000)을 허용하지 않으면 차단

4. 브라우저가 응답을 차단합니다

  • JavaScript에서 응답에 접근할 수 없음
  • 콘솔에 CORS 오류 표시

 

1. 원본 데이터 덤프

PGPASSWORD='{password}' pg_dump \
  -h {원본DB주소} \
  -p 5432 \
  -U {userName} \
  -d {dbName} \
  -n {schemaName} \
  -Fc \
  --clean \
  --no-owner \
  --no-acl \
  --verbose \
  -f neon_backup_20251212.dump

 

 

2. 새 DB 서버에 복사 (복사하기전에 해당 DB가 존재해야 함)

PGPASSWORD='{새DB비밀번호}' pg_restore \
  -h {새DB주소} \
  -U {userName} \
  -d {dbName} \
  --no-owner \
  --no-acl \
  --verbose \
  --jobs=6 \
  --clean \
  neon_backup_20251212.dump

 

 

 

3. api,batch,admin 등 DB 접속정보 변경

4. 1~3하는동안 이전DB에 쌓인 최근 데이터 수동배치로 새 DB에 복사

5. 정상 확인되면 구DB 내림

 

 

설치하기

# pip 설치 & 확인
sudo yum install -y python3-pip

# git-remote-codecommit 설치
sudo python3 -m pip install git-remote-codecommit

# 리젼 세팅 (서울 ap-northeast-2)
sudo aws configure set region ap-northeast-2 --profile default

# 연결된 레포 확인
sudo aws codecommit list-repositories --region ap-northeast-2

# 코드 디렉토리에 git clone (pet-batch는 예시)
cd /data/app
sudo git clone codecommit://pet-batch

# 디렉토리 권한 변경
ls -ld /data/app
sudo chown -R ssm-user:ssm-user /data/app

 

 

코드 레포 디렉토리 권한 변경

aws 콘솔에서 ssm으로 private ec2에 접속했다면 root 권한을 이용할 수 없으므로 ssm-user(aws 콘솔 접속중인 유저)가 수정할 수 있도록 권한 변경

cd {디렉토리}
ls -al
sudo chown -R ssm-user:ssm-user .

dnf 레포 업그레이드

sudo dnf upgrade -y

 

java17 설치

sudo yum install java-17-amazon-corretto
export JAVA_HOME=/usr/lib/jvm/java-17-amazon-corretto.x86_64
export PATH=$JAVA_HOME/bin:$PATH
source ~/.bashrc

# (참고) 코드 빌드
./gradlew build

 

 

Nodejs 설치

# node 24 레포 추가 (안 하면 자동으로 18버전이 설치됨)
curl -fsSL https://rpm.nodesource.com/setup_24.x | sudo bash -

# Node.js 다운로드 및 설치:
sudo dnf install nodejs -y

# Node.js 버전 확인:
node -v

 

 

git 설치

sudo yum install -y git

EC2에서 필요한 IAM 권한

  • AmazonS3FullAccess

EC2에서 기본 명령어

# 버킷 내용 목록 보기
aws s3 ls s3://{bucketName}/

# 파일 업로드
aws s3 cp ./local-file.txt s3://{bucketName}/

# 파일 이동
aws s3 mv s3://{bucketName}/{fileName} s3://{bucketName}/{fileName}
 

EC2에서 파일 다운로드 명령어

# 단일 파일 다운로드
aws s3 cp s3://{bucketName}/{fileName} ./

# 폴더 전체 다운로드 (재귀적)
aws s3 cp s3://{bucketName}/{folder}/ ./ --recursive

# 동기화 (변경된 파일만)
aws s3 sync s3://{bucketName}/{folder}/ ./
 

1. NAT 종류·위치 확인 (Gateway vs Instance)

  • NAT Gateway라면
    • 퍼블릭 서브넷에 있어야 하고, 그 서브넷 라우팅 테이블의 0.0.0.0/0는 Internet Gateway(IGW) 를 가리켜야 합니다.
    • NAT Gateway에 Elastic IP가 반드시 연결되어 있어야 합니다.
  • NAT Instance라면
    • 퍼블릭 서브넷에 있고, EIP 또는 Public IP를 가져야 하며, Source/Dest Check 비활성화, 보안 그룹·라우트 구성이 필요합니다.

2. 서브넷 / 라우팅 연관 상태

  • “라우팅 테이블은 오케이”라고 하셨으니 추가로:
    • private 서브넷이 정말 그 라우팅 테이블에 연관(Subnet associations) 되어 있는지 확인.
    • NAT Gateway와 EC2가 같은 AZ에 있는지 확인 (다른 AZ NAT을 타고 가도 되긴 하지만, 잘못된 설정이 없는지 체크).

3. 보안 그룹(Security Group) 설정

  • EC2 인스턴스 SG:
    • 아웃바운드에 0.0.0.0/0, TCP 80/443 등 필요한 포트가 허용되어 있는지.
  • NAT Instance를 쓰는 경우:
    • NAT 인스턴스 SG 인바운드가 private subnet CIDR 또는 EC2 SG에서 오는 트래픽을 허용하는지 확인.
  • NAT Gateway는 SG를 직접 붙일 수 없으니, NAT가 위치한 퍼블릭 서브넷에 붙은 NACL만 영향 줍니다.

4. 네트워크 ACL(NACL) 확인

  • private 서브넷과 NAT가 있는 퍼블릭 서브넷 둘 다:
    • 인바운드/아웃바운드에 Ephemeral Port(1024–65535) 와 80/443이 허용돼 있는지.
    • 0.0.0.0/0을 막는 규칙이 없는지 확인 (특히 커스텀 NACL 사용하는 경우 자주 막힘).

5. NAT / IGW 상태와 로그 확인

  • NAT Gateway 상태가 Available인지, 실패 상태가 아닌지 콘솔에서 확인.
  • VPC Flow Logs를 private 서브넷에 켜서, 외부 IP로 나가는 트래픽이 NAT까지 도달하는지, REJECT 되는지 확인.
  • NAT Instance일 경우, 직접 SSH 접속해서 curl https://www.google.com 등으로 NAT 자체의 인터넷 접속 여부를 먼저 확인합니다.

 

 

이 모~~~든걸 확인하고 또 확인했는데도 아웃바운딩이 되지 않았다.

 

그러다가 찾게 된 원인!!!!!

 

 

가용성 모드 리전별 / 영역별 (Regional / Zonal) 선택이 관건이었다.

 

 

 

 

설명으로는 가용성 NAT은 더 범용? 범위로 쓸 수 있어 고효율적이라고 되어있었는데...

 

실제로는 영역별 NAT을 선택해야 해당 서브넷에 연결이 되어 아웃바운딩이 될 수 있었음 아나

private EC2에 접속할 때 public IP가 없이 aws 콘솔에서 접속하고자 할 때, 아래 순서대로 진행되어야 한다.

  1. Private EC2에 AWS Systems Manager Agent(SSM Agent) 설치됨 (Ubuntu는 기본 설치됨)
  2. EC2 인스턴스 Role에 아래 정책 추가:
    1. AmazonSSMManagedInstanceCore
  3. Private Subnet이 NAT Gateway를 통해 인터넷 아웃바운드 가능해야 함
    (→ SSM 서비스에 접속하기 위해 필요)
  4. AWS 콘솔 → EC2 → 인스턴스 선택 → Connect → Session Manager 탭 → Connect

 

그런데 이런 에러 메시지가 떴다.

 

SSM 에이전트가 온라인 상태가 아닙니다

 

 

나를 포함한 많은 사람들이 맞닥뜨릴 상황 같아서 기록해본다.

 

더 자세하고 정확한 내용은 물론 도큐먼트에 있다! 하지만 좀더 내가 보기 편하게 정리해본다.

https://docs.aws.amazon.com/ko_kr/systems-manager/latest/userguide/session-manager-troubleshooting.html

 

 

 

 

➤ 체크리스트

 

1) EC2에 IAM Role이 붙어 있는지 확인

IAM Role에 반드시 아래 정책이 포함되어 있어야 함:

  • AmazonSSMManagedInstanceCore

확인 방법:

  • EC2 콘솔 → 인스턴스 선택 → Security → IAM Role

 

2) Routing Table에 RT 확인

Private Subnet에 연결된 RT 안에 아래 라우트가 등록되어 있어야 한다

  • 0.0.0.0/0   → nat
  • 10.0.0.0/16 → local          (VPC 내부 통신)

 

Public Subnet에 연결된 RT 안에 아래 라우트가 등록되어 있어야 한다

  • 0.0.0.0/0   → igw  (인터넷으로 나가는 기본 경로)
  • 10.0.0.0/16 → local          (VPC 내부 통신)

 

3) 엔드포인트 확인

VPC> PrivateLink 및 Lattice> endpoints 에서 다음 3개 엔드포인트 생성:

com.amazonaws.ap-northeast-2.ssm
com.amazonaws.ap-northeast-2.ec2messages
com.amazonaws.ap-northeast-2.ssmmessages

 

 

4) 보안그룹 규칙 확인

Inbound

  • https 443 10.0.0.0/16

Outbound

  • All type, All target, All port, 0.0.0.0/0

 

5) ACL 규칙 확인

Inbound

  • All type, All target, All port, 0.0.0.0/0

Outbound

  • All type, All target, All port, 0.0.0.0/0

 

6) DNS 호스트명 설정 문제

VPC 콘솔 → 해당 vpc 선택 → Actions(작업) → Edit VPC settings(VPC 설정 수정) → Enable DNS hostnames 체크

 

 

prometheus 연동 모듈

prometheus를 이용해 pm2 모니터링을 하도록 도와주는 모듈은 보통 pm2-metrics 또는 pm2-prometheus-exporter를 많이 쓴다.

 

pm2-metrics와 pm2-prometheus-exporter는 거의 비슷한 역할을 하지만, 차이점이 있다.

 

  • pm2-metrics (내장 기능): PM2의 핵심 기능 중 하나로, 다양한 시스템 및 프로세스 메트릭을 수집하여 보여주는 역할
  • pm2-prometheus-exporter: PM2가 수집한 메트릭을 Prometheus가 이해할 수 있는 형식(Prometheus exposition format)으로 변환하여 특정 포트(기본 9404)에 노출시켜주는 일종의 웹서버

간단히 말해, pm2-prometheus-exporter가 pm2-metrics의 데이터를 가져와 Prometheus용으로 제공하는 구조이다. 따라서 pm2-prometheus-exporter 하나만 설치하고 설정하면 된다.

 

pm2 install pm2-metrics

# 또는

pm2 install pm2-prometheus-exporter

 

 

 

만약 두 개의 모듈을 둘다 설치하게 되면 서로 충돌이 생긴다. 따라서 나는 pm2-prometheus-exporter를 선택했다.

 

정상적으로 설치가 완료되면 서버 로컬의 http://localhost:9209/metrics 에서 메트릭 정보를 확인할 수 있다.

 

 

 

 

prometheus 설정에 추가

모듈 설치를 완료하고 9209 포트에서 metric이 정상적으로 쌓이는 걸 확인했으면, prometheus가 설치된 서버에서 수집할 수 있도록 prometheus.yml 설정파일의 targets에 추가해주면 된다.

 

  - job_name: 'server_name'
    scheme: 'http'
    fallback_scrape_protocol: 'PrometheusText0.0.4'
    static_configs:
      - targets: ['xx.xx.xxx.xx:9209', 'xxx.xxx.xx.xxx:9209']

 

 

이제 grafana에서 prometheus 쿼리를 이용해 패널을 만들어 본다.

 

 

pm2_memory{job="server_name", name=~"^(server-01|server-02)$"} / 1024 / 1024 / 1024

 

위 예시 쿼리에서 server-01과 server-02는 pm2에 띄운 앱에 내가 지정해 준 이름이다. 서로 다른 프로세스 또는 병렬 서버를 구분하기 위해 pm2로 앱을 구동할 때 지정하는 이름을 서버별로 각기 다르게 지정해주었다. pm2 metric이 쌓일 때 이름도 같이 수집하기 때문에 이름을 쿼리할 수 있다. host ip명으로 구분할 때보다 더 가시적이고 관리가 편하다.

 

보통 리눅스 서버를 생성하고 나면, 시스템 패키지에 기본적으로 내장된 라이브러리들이 최신 버전이 아니다.

 

이번에 https3를 도입하려 보니 nginx 를 최신버전이 아니어서, 업데이트 하는 김에 포스팅

 

 

 

시스템 패키지 업데이트

sudo dnf clean all
sudo dnf makecache
sudo dnf update -y

 

 

nginx 레포 업데이트 (최신 버전의 nginx를 받아오도록 하는 과정)

sudo tee /etc/yum.repos.d/nginx.repo <<'EOF'
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
EOF

 

 

 

설치

# 설치
sudo dnf install -y nginx

# 기존에 설치된 nginx가 있다면
sudo dnf update nginx

# 기존에 이미 설치된 버전이 있어서 충돌이 난다면
sudo dnf remove -y nginx-core
sudo dnf install -y nginx

네이버 클라우드 플랫폼 VPC 환경에서 로드밸런서를 세팅해서 서버를 병렬로 운영하고자 로드밸런서를 생성해보았다.

 

아래 링크에서 로드밸런서 생성을 할 수 있다.

https://console.ncloud.com/vpc-load-balancer/loadBalancer/

 

 

 

 

 

 

 

로드밸런서 생성

 

위 페이지에서 [로드밸런서 생성] 버튼을 누르면 네 가지 옵션이 뜬다.

 

나는 L4에 만들 로드밸런서가 필요했기 때문에 [네트워크 로드밸런서]를 선택했다.

 

프록싱은 CloudFlare에서 할거기 때문에 프록시 옵션은 선택하지 않았다.

 

 

첫 시도때 이렇게 했더니 SSL 이용한 외부에서의 https 연결이 지원이 안되어서 [네트워크 프록시 로드밸런서]로 다시 생성했음

 

그런데 이렇게 했더니 health check가 tcp 만 지원되고 http 지원은 아예 안되어서 서버가 죽었을 때 디텍션이 안되었음.

 

결국엔 api 포트는 애플리케이션 로드밸런서로, mqtt 포트는 네트워크 프록시 로드밸런서로 나누어서 연결했다.

 

 

 

 

 

1. 로드밸런서 생성

 

네트워크를 Private, Public 어떤 걸로 선택하는지가 중요하다.

 

화면 차이는 공인 IP 설정란이 있냐없냐 이지만, 서브넷 선택을 할 때 이 네트워크 설정에 따라 목록에 뜨는 게 달라진다.

 

이걸 모르고 네트워크가 Private으로 선택되어있는데 내가 생성한 public 서브넷은 목록에 아예 뜨지 않아서 계속 헤맸다.

 

서브넷 설정을 먼저 하고 네트워크 위쪽 설정은 나중에 건드리려고 했는데 순서대로 했어야 했다.

 

 

 

 

 

2. 로드밸런서 생성 

(왜 STEP 1이랑 이름이 똑같지)

 

리스너 포트는 80으로 지정했다.

 

위에서 말한대로 나중에 https 접속을 위해 443로 변경했음

 

이유는 모르겠는데 이 단계에서는 포트를 여러 개 지정할 수 없다. 나중에 추가 가능

 

 

 

 

 

3. Target Group 선택

 

로드밸런싱 할 타겟 서버를 미리 그룹으로 만들어두고, Target Group 선택 스텝에서 지정할 수 있다.

 

미리 생성해 둔 타겟 그룹이 없다면 [Target Group 생성] 바로가기 링크를 눌러 생성할 수 있다.

 

상세 스텝은 아래에 따로 적어두었음

 

 

이 때 생성중인 로드밸런서 타입에 따라 특정 프로토콜이 적용된 타겟 서버만 목록에 나온다.

 

 

 

4. 설정 정보 보기

 

마지막으로 자신이 설정한 내용을 확인한다.

 

[로드 밸런서 생성] 버튼을 누르면 바로 생성되고, 1분도 안 되어서 운영 상태가 된다.

 

 

 

 

서브넷 생성

로드밸런서 전용 서브넷을 생성하고 적용해야 한다.

 

만약 로드밸런서 전용 서브넷에 서버인스턴스를 올리게 되면 로드밸런싱이 동작하지 않는다.

 

로드밸런서 생성 페이지에서 바로가기 링크도 있고, 따로 접속하려면 아래 링크에서 서브넷을 생성할 수 있다.

https://console.ncloud.com/vpc-network/subnet

 

 

서브넷 이름, VPC, IP 주소 범위, 가용 Zone, ACL을 선택한다.

 

생성한 VPC를 확인하고 선택하려면 View/getVPCDetail 권한이 필요하다.

 

만약 VPC 목록이 보이지 않는다면 자신이 서브 계정으로 접속중인지, 자신의 계정에 권한이 있는지 확인해야 한다.

 

마지막으로 [용도]에서 로드밸런서 를 선택해서 로드밸런서 전용 서브넷으로 지정하고 [생성] 버튼을 누르면 끝

 

 

 

 

 

타겟 그룹 생성

 

위의 3단계에서 설정한 타겟그룹도 바로가기 링크 또는 아래 링크에서 생성 가능하다 

https://console.ncloud.com/vpc-load-balancer/targetGroup

 

 

1. Target Group 생성

 

이름과 프로토콜, 포트를 지정해준다

 

 

2. Health Check 설정

 

헬스첵 설정을 집어넣고 끝

 

 

3. Target 추가

 

기존에 운영중이던 서버 목록이 뜨면 

 

 

 

 

 

4. 설정 정보 보기

 

마지막으로 정보를 확인하고 [Target Group 생성] 버튼을 누르면 끝!

 

이미 생성된 서버를 그룹으로 묶는 게 다이기 때문에 시간이 소요되지 않는다.

+ Recent posts