AWS ALB 프라이빗 백엔드 구성

이건선·2026년 3월 10일

해결

목록 보기
63/65

프론트엔드(Next.js)와 백엔드(Spring Boot)를 분리하고, 백엔드를 외부에 노출하지 않는 구성

배경

  • 프론트엔드: Next.js (포트 3000)
  • 백엔드: Nginx(포트 8080) → Spring Boot(포트 8081/8082)
  • Next.js의 rewrites/api/* 요청을 백엔드로 프록시
  • 백엔드를 외부에 노출하지 않고, 회사 IP에서만 Swagger 접근 허용

최종 아키텍처

[일반 트래픽]
브라우저 → Route53 (example.com)
         → CloudFront
         → alb-front (internet-facing)
         → EC2: Next.js(:3000)
              │
              │ rewrites /api/* (VPC 내부, HTTP)
              ▼
         alb-internal (internal) 🔒
              │
              ▼
         EC2: Nginx(:8080) → Spring Boot(:8081/:8082)

[회사 Swagger 접근]
회사 IP → Route53 (api.example.com)
        → alb-public (internet-facing, 회사 IP만 허용) 🏢
        → EC2: Nginx(:8080) → Spring Boot(:8081/:8082)

단계별 구성 절차

1단계: 프라이빗 서브넷 생성

EC2가 위치한 AZ를 포함하여 프라이빗 서브넷을 생성한다.

생성한 서브넷:
  - private-subnet-01: ap-northeast-2a, 10.0.10.0/24
  - private-subnet-02: ap-northeast-2c, 10.0.11.0/24
  - private-subnet-03: ap-northeast-2d, 10.0.12.0/24

주의사항:
  - MapPublicIpOnLaunch: False (프라이빗)
  - EC2 인스턴스가 위치한 AZ를 반드시 포함해야 함
  - AZ를 잘못 선택하면 타겟이 "unused" 상태가 됨

2단계: Internal ALB 생성

설정:
  - 이름: alb-internal
  - Scheme: internal ← (생성 후 변경 불가)
  - VPC: 기존 VPC 선택
  - 서브넷: 1단계에서 만든 프라이빗 서브넷 선택 (EC2 AZ 포함 필수)
  - 보안 그룹: 새로 생성 (아래 참조)

리스너:
  - HTTP:80 → forward → 타겟 그룹
  - HTTPS:443 → forward → 타겟 그룹 (선택사항, 내부 통신이면 HTTP만으로 충분)

3단계: 타겟 그룹 생성

internal ALB용:
  - 이름: tg-internal
  - 프로토콜: HTTP, 포트: 8080
  - 헬스체크 경로: /api/v1/heartBeat
  - 타겟 등록: EC2 인스턴스 (포트 8080)

회사 전용 ALB용 (타겟 그룹은 ALB 간 공유 불가):
  - 이름: tg-public
  - 프로토콜: HTTP, 포트: 8080
  - 헬스체크 경로: /api/v1/heartBeat
  - 타겟 등록: 동일 EC2 인스턴스 (포트 8080)

4단계: 보안 그룹 설정

Internal ALB 보안 그룹 (sg-alb-internal)

인바운드:
  - TCP 80  ← 소스: EC2 보안 그룹 (sg-xxxxxxxxx)
  - TCP 443 ← 소스: EC2 보안 그룹 (sg-xxxxxxxxx)

아웃바운드:
  - 전체 허용 (0.0.0.0/0)

EC2 보안 그룹 — Internal ALB 허용 추가

인바운드 추가:
  - TCP 8080 ← 소스: Internal ALB 보안 그룹 (sg-xxxxxxxxx)

회사 전용 ALB 보안 그룹

인바운드:
  - TCP 443 ← 소스: 회사 IP/32 (예: xxx.xxx.xxx.xxx/32)

아웃바운드:
  - 전체 허용

5단계: Route53 설정

example.com (A 레코드, Alias):
  → CloudFront 배포 DNS

*.example.com (A 레코드, Alias):
  → CloudFront 배포 DNS

api.example.com (A 레코드, Alias):
  → alb-public (internet-facing, 회사 전용)

6단계: Next.js 프론트엔드 설정

# EC2 배포용 .env
BASE_URL=http://internal-alb-internal-xxxxxxxxxx.ap-northeast-2.elb.amazonaws.com
.env 변경 후 반드시 재빌드 필요:
  pnpm build (또는 npm run build)
  pm2 restart web

7단계: 회사 전용 ALB (alb-public) 리스너 설정

HTTPS(443) 리스너:
  - 기본 액션: forward → tg-public

HTTP(80) 리스너:
  - 기본 액션: forward → tg-public (또는 HTTPS 리다이렉트)

검증 방법

Internal ALB 동작 확인

# EC2에 SSH 접속 후
ssh my-dev-server

# Internal ALB를 통한 API 호출
curl -s -o /dev/null -w '%{http_code}' \
  http://internal-alb-internal-xxxxxxxxxx.ap-northeast-2.elb.amazonaws.com/api/v1/heartBeat
# 기대 결과: 200

타겟 헬스 확인

# AWS CLI로 확인
aws elbv2 describe-target-health \
  --target-group-arn <타겟그룹ARN> \
  --profile my-profile \
  --region ap-northeast-2
# 기대 결과: "State": "healthy"

회사에서 Swagger 접근 확인

브라우저에서: https://api.example.com/swagger-ui/index.html
- 회사 IP에서: 접근 가능
- 외부 IP에서: 보안 그룹에서 차단 (타임아웃)

트러블슈팅

타겟 상태: "unused" (Target.NotInUse)

원인: EC2가 위치한 AZ의 서브넷이 ALB에 연결되지 않음
확인: EC2의 AZ와 ALB의 AZ 목록 비교
해결: 해당 AZ에 프라이빗 서브넷 생성 후 ALB에 추가

타겟 상태: "unhealthy" (Target.Timeout)

원인 1: EC2 보안 그룹이 ALB 보안 그룹으로부터의 트래픽을 차단
확인: EC2 SG에 ALB SG → 8080 포트 인바운드 규칙 존재 여부
해결: EC2 SG에 인바운드 규칙 추가 (TCP 8080, 소스: ALB SG)

원인 2: 보안 그룹 포트가 0으로 설정됨
확인: SG 인바운드 규칙의 FromPort/ToPort 확인
해결: 올바른 포트 번호로 수정 (80, 443, 8080 등)

타겟 그룹이 ALB 리스너에서 선택 불가 (회색 처리)

원인: 해당 타겟 그룹이 이미 다른 ALB에서 사용 중
해결: 새 타겟 그룹을 동일한 설정으로 생성하여 사용
profile
멋지게 기록하자

0개의 댓글