Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e98536e
[FEAT] SNS 메시지 파싱을 위한 TOKEN 상수 추가
sung-silver Dec 21, 2025
2b993e5
[FEAT] 디바이스 토큰으로 엔티티 조회 메서드 추가 및 상수 적용
sung-silver Dec 21, 2025
f59e5da
[FEAT] SNS 핸들러를 위한 사용자 및 토큰 조회 메서드 추가
sung-silver Dec 21, 2025
67ca374
[FEAT] SnsHandler를 위한 서비스 의존성 추가
sung-silver Dec 21, 2025
ae2084d
[FEAT] SNS 실패 이벤트 처리 핸들러 구현
sung-silver Dec 21, 2025
2c48bbc
[FEAT] SnsHandler template.yaml에 추가
sung-silver Dec 21, 2025
df55b21
[DOCS] SnsHandler 테스트 문서 작성
sung-silver Dec 21, 2025
fdfb0e4
[FIX] InvalidEndpointCleaner 파라미터 순서 수정
sung-silver Dec 21, 2025
348ba92
[REFACTOR] 코드 포매팅
sung-silver Dec 21, 2025
3c0c4a8
[DOCS] evnets 하위 json 파일 수정 가이드 작성
sung-silver Dec 21, 2025
51111cb
[TEST] test-sns-handler에 프로필 명시 추가
sung-silver Dec 21, 2025
14b7293
[DOCS] SNSHANDLER_LOCAL_TESTING_GUIDE params-dev.json 파일 설명 통일
sung-silver Dec 21, 2025
ce3c726
[DEL] local test 가이드로 내용 통합을 위해 삭제
sung-silver Dec 22, 2025
cd79279
[DOCS] 테스트 가이드 수정
sung-silver Dec 22, 2025
4970227
[DOCS] event 가이드문서 수정
sung-silver Dec 22, 2025
7ececb1
[REFACTOR] SnSHandler 리뷰 반영
sung-silver Dec 22, 2025
a20cd5b
[DOCS] SnsHandler 가이드라인에 실행파일 내용 추가
sung-silver Dec 22, 2025
75c87bf
[REFACTOR] UserService 코드 리뷰 반영
sung-silver Dec 22, 2025
51725b9
[REFACTOR] UserService 내 DeviceTokenRepository 접근 로직을 DeviceTokenServ…
sung-silver Dec 22, 2025
ca59374
[CHORE] Evnets 구독 설정 추가 및 IAM 권한 명시
sung-silver Dec 22, 2025
debaaff
[FIX] AppFactory 생성자 수정 및 코드 포매팅
sung-silver Dec 22, 2025
d828422
[MERGE] develop -> feat/#13
sung-silver Dec 22, 2025
c5d5199
[CHORE] SnsHandler CodeUri 경로 .으로 수정
sung-silver Dec 22, 2025
d480f0e
[FIX] SnsHandler 예외처리 문자열 반환으로 수정
sung-silver Dec 22, 2025
e693046
[REFACTOR] EventBridgeHandler 예외처리 Slf4j 사용하도록 변경
sung-silver Dec 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 181 additions & 0 deletions SNSHANDLER_LOCAL_TESTING_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# SnsHandler 로컬 테스트 가이드

이 가이드는 SAM CLI를 사용하여 로컬에서 SnsHandler Lambda 함수를 테스트하는 방법을 설명합니다.

## 사전 요구사항

1. **SAM CLI 설치**
```bash
# macOS
brew install aws-sam-cli

# 또는 pip 사용
pip install aws-sam-cli
```

2. **AWS 자격 증명 설정**
```bash
aws configure
```
또는 환경 변수 설정:
```bash
export AWS_ACCESS_KEY_ID=your-access-key
export AWS_SECRET_ACCESS_KEY=your-secret-key
export AWS_DEFAULT_REGION=ap-northeast-2
```

3. **Java 21 설치 확인**
```bash
java -version
```

## 1. 프로젝트 빌드

```bash
cd sopt-push-notification
./gradlew shadowJar
```

빌드가 완료되면 `build/libs/app.jar` 파일이 생성됩니다.

## 2. SAM 빌드

```bash
cd sopt-push-notification # 이미 프로젝트 경로라면 생략 가능
sam build
```

## 3. 로컬 테스트 방법

### 방법 1: SAM CLI로 직접 invoke (권장)

```bash
# 단일 레코드 테스트
sam local invoke SnsHandlerFunction \
--event events/sns-event-single.json \
--env-vars params-dev.json

# 여러 레코드 테스트
sam local invoke SnsHandlerFunction \
--event events/sns-event-sample.json \
--env-vars params-dev.json
```

### 방법 2: 환경 변수 파일 확인

`params-dev.json` 파일에 `SnsHandlerFunction` 섹션이 있는지 확인합니다. 다른 handler 개발자들과 통일된 형식을 사용합니다.

기존 `params-dev.json`에 `SnsHandlerFunction` 섹션을 추가하세요:

```json
{
"SnsHandlerFunction": {
"DYNAMODB_TABLE": "your-dynamodb-table-name",
"PLATFORM_APPLICATION_iOS": "arn:aws:sns:...",
"PLATFORM_APPLICATION_ANDROID": "arn:aws:sns:...",
"ALL_TOPIC_ARN": "arn:aws:sns:...",
"STAGE": "dev",
"MAKERS_APP_SERVER_URL": "https://...",
"MAKERS_OPERATION_SERVER_URL": "https://..."
}
}
```

### 방법 3: DynamoDB Local 사용 (선택사항)

로컬 DynamoDB를 사용하려면:

```bash
# DynamoDB Local 실행
docker run -p 8000:8000 amazon/dynamodb-local

# SAM local invoke 시 DynamoDB 엔드포인트 지정
sam local invoke SnsHandlerFunction \
--event events/sns-event-single.json \
--env-vars params-dev.json \
--docker-network host \
--parameter-overrides DynamoDbEndpoint=http://localhost:8000
```

## 4. 테스트 이벤트 파일 커스터마이징

`events/sns-event-single.json` 파일을 수정하여 실제 테스트 시나리오에 맞게 변경할 수 있습니다:

- `Message` 필드의 `Token` 값을 실제 DynamoDB에 존재하는 디바이스 토큰으로 변경
- 여러 레코드를 추가하여 배치 처리 테스트
- 잘못된 형식의 메시지로 에러 처리 테스트

## 5. 실제 AWS 환경에서 테스트

### SNS Topic에 Lambda 함수 구독

1. AWS 콘솔에서 SNS Topic 생성
2. Lambda 함수를 구독자로 추가
3. 푸시 알림 실패 시 자동으로 Lambda 함수가 트리거됨

### 수동으로 SNS 이벤트 발행

```bash
aws sns publish \
--topic-arn arn:aws:sns:ap-northeast-2:123456789012:push-failures \
--message '{"Token":"test-device-token-12345","EndpointArn":"arn:aws:sns:ap-northeast-2:123456789012:endpoint/APNS/test-app/test-endpoint-123"}'
```

### test-sns-handler.sh 실행파일을 통한 테스트
```bash
cd sopt-push-notification
./test-sns-handler.sh
```

## 6. 디버깅 팁

### 로그 확인

```bash
# SAM local invoke 실행 시 로그가 콘솔에 출력됩니다
sam local invoke SnsHandlerFunction \
--event events/sns-event-single.json \
--env-vars params-dev.json \
--debug
```

### CloudWatch Logs 확인 (배포 후)

AWS 콘솔에서 Lambda 함수의 CloudWatch Logs를 확인하여 실행 로그를 볼 수 있습니다.

## 7. 테스트 시나리오

### 시나리오 1: 정상 처리
- DynamoDB에 존재하는 디바이스 토큰으로 테스트
- 실패 로그가 생성되고 토큰이 정리되는지 확인

### 시나리오 2: 존재하지 않는 토큰
- DynamoDB에 존재하지 않는 디바이스 토큰으로 테스트
- 핸들러가 정상적으로 처리하고 에러 없이 종료되는지 확인

### 시나리오 3: 잘못된 메시지 형식
- `Message` 필드에 `Token`이 없는 경우
- 핸들러가 안전하게 처리하는지 확인

### 시나리오 4: 배치 처리
- 여러 레코드를 포함한 이벤트로 테스트
- 각 레코드가 독립적으로 처리되는지 확인

## 8. 문제 해결

### 문제: "Handler not found"
- `Handler` 경로가 정확한지 확인: `com.sopt.push.lambda.SnsHandler::handleRequest`
- JAR 파일이 올바르게 빌드되었는지 확인: `./gradlew shadowJar`

### 문제: "ClassNotFoundException"
- `shadowJar` 태스크가 모든 의존성을 포함하는지 확인
- `build/libs/app.jar` 파일 크기가 충분한지 확인

### 문제: "DynamoDB 연결 실패"
- AWS 자격 증명이 올바르게 설정되었는지 확인
- DynamoDB 테이블이 존재하고 접근 권한이 있는지 확인

### 문제: "SNS 권한 오류"
- Lambda 실행 역할에 필요한 SNS 권한이 있는지 확인
- `template.yaml`의 IAM 역할 설정 확인

146 changes: 146 additions & 0 deletions events/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# SNS 이벤트 파일 수정 가이드

## 필드별 수정 가이드

### 🔴 필수 수정 항목 (실제 테스트를 위해)

#### 1. `Message` 필드 내부의 `Token` (가장 중요!)
```json
"Message": "{\"Token\":\"실제-디바이스-토큰-값\",...}"
```

**수정 방법:**
- DynamoDB의 `DeviceTokenEntity` 테이블에서 실제 디바이스 토큰 조회
- `pk`가 `d#`로 시작하는 레코드에서 `d#`를 제거한 값이 디바이스 토큰
- 예: `pk = "d#abc123def456"` → `Token = "abc123def456"`

**확인 방법:**
```bash
# AWS CLI로 확인
aws dynamodb query \
--table-name notification-dev \
--key-condition-expression "pk = :pk" \
--expression-attribute-values '{":pk":{"S":"d#your-device-token"}}'
```

#### 2. `Message` 필드 내부의 `EndpointArn` (선택사항)
```json
"Message": "{...,\"EndpointArn\":\"실제-엔드포인트-ARN\",...}"
```
- DynamoDB의 `DeviceTokenEntity`에서 `endpointArn` 필드 값 사용
- 또는 `UserEntity`에서 `endpointArn` 필드 값 사용

**예시:**
```json
"EndpointArn": "arn:aws:sns:ap-northeast-2:379013966998:endpoint/APNS/Makers-test-iOS/12345678-1234-1234-1234-123456789012"
```

### 🟡 선택적 수정 항목 (로컬 테스트에서는 예시 값으로도 가능)

#### 3. `MessageId`
- UUID 형식의 메시지 ID
- 로컬 테스트에서는 예시 값으로도 가능
- 실제 값으로 변경하려면: `uuidgen` 명령어 사용

#### 4. `TopicArn`
- SNS Topic ARN (푸시 실패 알림을 받는 Topic)
- 로컬 테스트에서는 예시 값으로도 가능
- 실제 값: AWS 콘솔에서 SNS Topic ARN 확인

#### 5. `EventSubscriptionArn`
- Lambda가 SNS Topic을 구독할 때 생성되는 구독 ARN
- 로컬 테스트에서는 예시 값으로도 가능
- 형식: `arn:aws:sns:{region}:{account-id}:{topic-name}:{subscription-id}`

#### 6. `Timestamp`
- 이벤트 발생 시간 (ISO 8601 형식)
- 로컬 테스트에서는 예시 값으로도 가능
- 현재 시간으로 변경: `date -u +"%Y-%m-%dT%H:%M:%S.000Z"`

### 🟢 수정 불필요 항목

- `EventSource`: 항상 `"aws:sns"`
- `EventVersion`: 항상 `"1.0"`
- `Type`: 항상 `"Notification"`
- `Subject`: 항상 `"Amazon SNS Notification"`
- `SignatureVersion`, `Signature`, `SigningCertUrl`, `UnsubscribeUrl`: 로컬 테스트에서는 `"EXAMPLE"`로 유지 가능
- `MessageAttributes`: 빈 객체 `{}`로 유지

## 실제 수정 예시

### 시나리오 1: 최소 수정 (핵심 테스트)
```json
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:ap-northeast-2:123456789012:push-failures:12345678-1234-1234-1234-123456789012",
"Sns": {
"Type": "Notification",
"MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
"TopicArn": "arn:aws:sns:ap-northeast-2:123456789012:push-failures",
"Subject": "Amazon SNS Notification",
"Message": "{\"Token\":\"실제-DynamoDB에-존재하는-디바이스-토큰\",\"EndpointArn\":\"arn:aws:sns:ap-northeast-2:379013966998:endpoint/APNS/Makers-test-iOS/실제-엔드포인트-ID\",\"MessageId\":\"95df01b4-ee98-5cb9-9903-4c221d41eb5e\"}",
"Timestamp": "2024-01-15T12:00:00.000Z",
"SignatureVersion": "1",
"Signature": "EXAMPLE",
"SigningCertUrl": "EXAMPLE",
"UnsubscribeUrl": "EXAMPLE",
"MessageAttributes": {}
}
}
]
}
```

### 시나리오 2: 완전한 실제 값 사용
```json
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:ap-northeast-2:12345678912:SOPT-PUSH-FAILURES-DEV:실제-구독-ID",
"Sns": {
"Type": "Notification",
"MessageId": "실제-UUID-생성",
"TopicArn": "arn:aws:sns:ap-northeast-2:12345678912:SOPT-PUSH-FAILURES-DEV",
"Subject": "Amazon SNS Notification",
"Message": "{\"Token\":\"실제-디바이스-토큰\",\"EndpointArn\":\"실제-엔드포인트-ARN\",\"MessageId\":\"실제-UUID\"}",
"Timestamp": "2024-12-19T10:30:00.000Z",
"SignatureVersion": "1",
"Signature": "EXAMPLE",
"SigningCertUrl": "EXAMPLE",
"UnsubscribeUrl": "EXAMPLE",
"MessageAttributes": {}
}
}
]
}
```

## 테스트 시나리오별 권장 값

### 1. 정상 처리 테스트
- `Token`: DynamoDB에 존재하는 실제 디바이스 토큰
- `EndpointArn`: 해당 토큰과 연결된 실제 엔드포인트 ARN

### 2. 존재하지 않는 토큰 테스트
- `Token`: DynamoDB에 존재하지 않는 임의의 값
- 핸들러가 안전하게 처리하는지 확인

### 3. 잘못된 메시지 형식 테스트
- `Message`에서 `Token` 필드 제거
- 핸들러가 null을 안전하게 처리하는지 확인

## 주의사항

⚠️ **로컬 테스트 시:**
- `Token`만 실제 값으로 변경해도 핵심 로직 테스트 가능
- 나머지 필드는 예시 값으로도 동작 (로컬에서는 실제 SNS와 연결되지 않음)

⚠️ **실제 AWS 환경 테스트 시:**
- 모든 ARN 값들을 실제 값으로 변경 필요
- SNS Topic에 Lambda 함수가 구독되어 있어야 함

41 changes: 41 additions & 0 deletions events/sns-event-sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:ap-northeast-2:123456789012:push-failures:12345678-1234-1234-1234-123456789012",
"Sns": {
"Type": "Notification",
"MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
"TopicArn": "arn:aws:sns:ap-northeast-2:123456789012:push-failures",
"Subject": "Amazon SNS Notification",
"Message": "{\"Token\":\"test-device-token-12345\",\"MessageId\":\"95df01b4-ee98-5cb9-9903-4c221d41eb5e\"}",
"Timestamp": "2024-01-15T12:00:00.000Z",
"SignatureVersion": "1",
"Signature": "EXAMPLE",
"SigningCertUrl": "EXAMPLE",
"UnsubscribeUrl": "EXAMPLE",
"MessageAttributes": {}
}
},
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:ap-northeast-2:123456789012:push-failures:87654321-4321-4321-4321-210987654321",
"Sns": {
"Type": "Notification",
"MessageId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"TopicArn": "arn:aws:sns:ap-northeast-2:123456789012:push-failures",
"Subject": "Amazon SNS Notification",
"Message": "{\"Token\":\"test-device-token-67890\",\"MessageId\":\"a1b2c3d4-e5f6-7890-abcd-ef1234567890\"}",
"Timestamp": "2024-01-15T12:00:01.000Z",
"SignatureVersion": "1",
"Signature": "EXAMPLE",
"SigningCertUrl": "EXAMPLE",
"UnsubscribeUrl": "EXAMPLE",
"MessageAttributes": {}
}
}
]
}

23 changes: 23 additions & 0 deletions events/sns-event-single.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:ap-northeast-2:123456789012:push-failures:12345678-1234-1234-1234-123456789012",
"Sns": {
"Type": "Notification",
"MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
"TopicArn": "arn:aws:sns:ap-northeast-2:123456789012:push-failures",
"Subject": "Amazon SNS Notification",
"Message": "{\"Token\":\"test-device-token-12345\",\"MessageId\":\"95df01b4-ee98-5cb9-9903-4c221d41eb5e\"}",
"Timestamp": "2024-01-15T12:00:00.000Z",
"SignatureVersion": "1",
"Signature": "EXAMPLE",
"SigningCertUrl": "EXAMPLE",
"UnsubscribeUrl": "EXAMPLE",
"MessageAttributes": {}
}
}
]
}

Loading