로컬 웹훅 테스트 환경#
로컬 개발 환경에서 AWS를 이용하는 전체 웹훅 플로우를 테스트할 수 있는 환경을 구축함
운영 환경의 웹훅 처리 플로우:
1
| Cafe24 → Django → SQS → EventBridge Pipes → Step Functions → Lambda → ElastiCache
|
이 플로우를 로컬에서 테스트하기 어려웠던 이유:
- Lambda 함수가 ElastiCache(AWS)에만 접근 가능
- LocalStack Free에서 EventBridge Pipes 미지원
해결 방법#
LocalStack을 활용한 하이브리드 아키텍처:
- 웹훅 처리 → LocalStack (Step Functions + Lambda)
- 기타 AWS 서비스 (SQS, S3) → 실제 AWS 유지
💡 핵심 개념: LocalStack과 Endpoint URL#
이 프로젝트는 하이브리드 클라우드 환경을 구성하기 위해 LocalStack과 endpoint_url 개념을 적극적으로 사용합니다.
1. LocalStack이란?#
LocalStack은 내 컴퓨터(로컬 환경)에서 실행되는 AWS 클라우드 에뮬레이터입니다. 실제 AWS에 비용을 지불하거나 인터넷을 연결하지 않고도, 내 컴퓨터 안에서 S3, Lambda, DynamoDB 등의 AWS 서비스를 가짜로 띄워 테스트할 수 있게 해줍니다.
- 이 프로젝트에서의 역할: 비용이 들고 구성이 복잡한 웹훅 처리 로직(Step Functions, Lambda)을 실제 AWS 대신 LocalStack 컨테이너 안에서 실행합니다.
2. Endpoint URL이란?#
AWS SDK(Boto3 등)가 명령을 보낼 목적지 주소입니다.
- 기본값 (Real AWS): 설정하지 않으면
https://sqs.ap-northeast-2.amazonaws.com 같은 실제 AWS 공용 주소로 요청을 보냅니다. - LocalStack 설정:
endpoint_url을 LocalStack 주소로 강제 지정하면, 요청이 실제 AWS가 아닌 내 로컬 컨테이너로 향하게 됩니다.
3. 이 프로젝트의 하이브리드 구성 (Docker Network)#
이 환경에서는 어떤 서비스냐에 따라 요청을 보내는 곳(Endpoint)을 다르게 설정합니다.
| 구분 | 대상 서비스 | Endpoint URL 설정 값 | 설명 |
|---|
| 실제 AWS | SQS, S3 | None (기본값) | 실제 AWS 서버로 요청을 보냅니다. |
| LocalStack | Step Functions, Lambda | http://core-localstack:4566 | 도커 네트워크 내부의 LocalStack 컨테이너로 요청을 가로챕니다. |
⚠️ 주의: 로컬 PC(Host)에서 접속할 때는 localhost:4566을 쓰지만, 도커 컨테이너(core-backend) 내부끼리 통신할 때는 서비스명인 core-localstack:4566을 사용해야 합니다.
아키텍처#
전체 구조#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| ┌─────────────────────────────────────────────────────────────────────────┐
│ core-backend │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ [일반 AWS 작업] [웹훅 테스트] │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ SQS_CLIENT │ │ LOCALSTACK_ │ │
│ │ S3_CLIENT │ │ STEPFUNCTIONS_ │ │
│ │ (Endpoint: Default) │ │ CLIENT │ │
│ └──────────┬──────────┘ │ (Endpoint: Local) │ │
│ │ └──────────┬──────────┘ │
│ │ │ │
└──────────────┼─────────────────────────────────┼────────────────────────┘
│ │
▼ ▼
┌────────────┐ ┌────────────────┐
│ 실제 AWS │ │ LocalStack │
│ (SQS, S3) │ │ (웹훅 전용) │
│ 계정: 341..│ │ 계정: 000000.. │
└────────────┘ └────────────────┘
│
▼
┌────────────────┐
│ core-redis │
│ (토큰 + 결과) │
└────────────────┘
|
웹훅 처리 플로우 (로컬)#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| 1. 웹훅 수신
│
▼
2. Django 뷰 (views.py)
- LOCAL=True 확인
- SQS 대신 Step Functions 직접 호출 (endpoint_url: core-localstack)
│
▼
3. LocalStack Step Functions
- Core-System-Local-Webhook 상태 머신 실행
│
▼
4. Token Lambda (LocalStack)
- core-redis에서 OAuth 토큰 조회
- access_token을 payload에 추가
│
▼
5. 웹훅 종류별 Lambda (LocalStack)
- Cafe24 API 호출 (실제 외부 통신)
- 결과를 core-redis에 저장
│
▼
6. 결과 확인
docker exec core-redis redis-cli KEYS "webhook:*"
|
컨테이너 구성#
| 컨테이너 | 포트 | 용도 | 비고 |
|---|
core-backend | 8000 | Django 서버 | 웹훅 수신 |
core-localstack | 4566 | AWS 에뮬레이션 | Step Functions, Lambda |
core-redis | 6379 | OAuth 토큰 + 웹훅 결과 저장 | 통합 Redis |
core-db | 5432 | PostgreSQL | 기존 DB |
핵심 코드 변경#
1. AWS 클라이언트 분리 (core/config/aws.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # 기본 클라이언트는 항상 실제 AWS 사용 (endpoint_url 미지정 = 실제 AWS)
_AWS_DEFAULT_ENDPOINT = "https://sqs.ap-northeast-2.amazonaws.com"
SQS_CLIENT = boto3.resource(
"sqs",
region_name=REGION,
endpoint_url=_AWS_DEFAULT_ENDPOINT if AWS_ENDPOINT_URL else None,
)
# LocalStack 전용 클라이언트 (웹훅 테스트용)
# AWS_ENDPOINT_URL 환경변수가 'http://core-localstack:4566'으로 설정됨
LOCALSTACK_STEPFUNCTIONS_CLIENT = None
if LOCAL and AWS_ENDPOINT_URL:
LOCALSTACK_STEPFUNCTIONS_CLIENT = boto3.client(
"stepfunctions",
region_name=REGION,
endpoint_url=AWS_ENDPOINT_URL, # 여기서 LocalStack을 바라보게 됨
...
)
|
3. Lambda SQS/Step Functions 호환성 (webhook-*/lambda_function.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| def lambda_handler(event: dict, context: Any) -> None:
# SQS 형태(Records)와 Step Functions 직접 호출(Flat JSON) 모두 처리
if "Records" in event:
# SQS를 통해 트리거된 경우
body = event["Records"][0]["body"]
if isinstance(body, str):
body = json.loads(body)
else:
# Step Functions에서 직접 호출된 경우 (Local 테스트 환경 포함)
body = event
# platform_id와 platform_ids 모두 지원 (호환성)
platform_id = body.get("platform_id") or body.get("platform_ids")
...
|
4. Lambda Redis SSL 분기
1
2
3
4
5
6
7
8
| REDIS_SSL = os.getenv("REDIS_SSL", "true").lower() == "true"
r = Redis(
host=CORE_REDIS_HOST,
port=CORE_REDIS_PORT,
ssl=REDIS_SSL, # 로컬에서는 false
decode_responses=True,
)
|
트러블슈팅#
Lambda 함수가 생성되지 않음#
1
2
3
4
5
6
7
8
| # LocalStack 로그 확인
docker logs core-localstack | tail -50
# 수동으로 초기화 스크립트 실행
docker exec core-localstack bash /etc/localstack/init/ready.d/init.sh
# Lambda 함수 확인 (AWS CLI 사용 시 endpoint 필수)
aws --endpoint-url=http://localhost:4566 lambda list-functions
|
SQS 큐를 찾을 수 없음 오류#
증상: The specified queue does not exist
원인: AWS_ENDPOINT_URL 환경변수가 전역으로 설정되어 boto3가 SQS 요청마저 LocalStack으로 보내버림
해결: core/config/aws.py에서 SQS_CLIENT 생성 시 endpoint_url을 명시적으로 지정하여 실제 AWS를 바라보도록 수정
1
2
3
4
5
6
| SQS_CLIENT = boto3.resource(
"sqs",
region_name=REGION,
# AWS_ENDPOINT_URL이 있어도 강제로 실제 AWS 주소를 사용하거나 None으로 설정
endpoint_url="https://sqs.ap-northeast-2.amazonaws.com" if AWS_ENDPOINT_URL else None,
)
|
Redis 연결 오류 (Lambda)#
증상: Lambda에서 Redis 연결 실패
원인: 로컬 Redis는 SSL을 사용하지 않음
해결: Lambda 환경변수에 REDIS_SSL=false 설정 (init.sh에서 자동 설정됨)
Step Functions 실행 실패#
1
2
3
4
5
6
7
| # 실행 이력 확인
aws --endpoint-url=http://localhost:4566 stepfunctions list-executions \
--state-machine-arn arn:aws:states:us-east-1:000000000000:stateMachine:Core-System-Local-Webhook
# 특정 실행 상세 정보
aws --endpoint-url=http://localhost:4566 stepfunctions describe-execution \
--execution-arn <EXECUTION_ARN>
|
core-backend 시작 실패 (Secrets Manager 오류)#
증상: Service 'secretsmanager' is not enabled
원인: LOCAL 환경에서 Secrets Manager 호출 시도 (Endpoint가 LocalStack으로 잡혀있으나 LocalStack Free버전이거나 초기화 안됨)
해결: core/config/database.py에서 LOCAL 환경일 때 환경변수에서 직접 DB 정보 로드
관련 문서#