Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
538f012
[FEAT] DeviceTokenException 추가
JungYoonShin Dec 23, 2025
8323f67
[REFACTOR] 기본 생성자 롬복으로 대체
JungYoonShin Dec 23, 2025
3a16f11
[FIX] DeviceTokenService 구현(토큰 등록, 유저 저장)
JungYoonShin Dec 23, 2025
5298b81
[FEAT] pk 기반 디바이스 토큰 조회 추가
JungYoonShin Dec 23, 2025
46ab2e7
[FIX] 추가 구현에 따른 AppFactory 수정
JungYoonShin Dec 23, 2025
4f8a3ef
[FEAT] SnsFactory 구현
JungYoonShin Dec 23, 2025
e8d4aae
[FEAT] 토큰 등록 & 삭제 관련 DTO 구현
JungYoonShin Dec 23, 2025
8384d59
[FEAT] 알림 전송 관련 DTO 구현
JungYoonShin Dec 23, 2025
2ae5631
[FEAT] ApiGatewayHandler 구현
JungYoonShin Dec 23, 2025
8a6563a
[FIX] template.yml 수정
JungYoonShin Dec 23, 2025
be09093
[MERGE] 충돌 해결
JungYoonShin Dec 23, 2025
1f629b6
[MERGE] 충돌 머지
JungYoonShin Dec 23, 2025
baf67c2
[REFACTOR] 불필요한 파일 삭제
JungYoonShin Dec 23, 2025
deb22af
[REFACTOR] 코드 스타일 적용
JungYoonShin Dec 24, 2025
2504a92
[REFACTOR] header 정보 상수화
JungYoonShin Dec 26, 2025
1461ae9
[REFACTOR] 필요없는 파일 정리
JungYoonShin Dec 26, 2025
7e9e109
[FEAT] TokenRegisterFacade 구현
JungYoonShin Dec 26, 2025
09cee50
[FEAT] EnvConfig env 추가
JungYoonShin Dec 26, 2025
4593796
[REFACTOR] 불필요한 코드 제거
JungYoonShin Dec 26, 2025
75b9e44
[REFACTOR] ApiGatewayHandler 리팩토링
JungYoonShin Dec 26, 2025
97bd0e7
[FIX] 잘못된 role 수정
JungYoonShin Dec 26, 2025
a46e638
[REFACTOR] spotleess 적용
JungYoonShin Dec 26, 2025
29568fe
[REFACTOR] 필요없는 의존성 및 메서드 제거
JungYoonShin Dec 26, 2025
e68e22d
[FIX] 토큰 등록 실패 시, 토큰 삭제 실패시 성공 응답이 반환되는 문제 수정
JungYoonShin Dec 26, 2025
40547b5
[REFACTOR] actualUserId 사용하도록 수정
JungYoonShin Dec 26, 2025
303c72f
[FIX] AppFactory - DeviceTokenService 의존성 정리
JungYoonShin Dec 26, 2025
4a52d91
[REFACTOR] validate > validateDto 메서드명 변경
JungYoonShin Dec 28, 2025
976862e
[REFACTOR] InvalidEndpointCleaner & TokenRegisterFacade 통합
JungYoonShin Dec 28, 2025
8dc2eb2
[REFACTOR] ApiGatewayHandler - response를 ResponseUtil로 이동
JungYoonShin Dec 28, 2025
0327c5b
[REFACTOR] 토큰 관련 부분 DeviceTokenService로 이동
JungYoonShin Dec 28, 2025
953518d
[REFACTOR] SNS_PROTOCOL_APPLICATION 상수화
JungYoonShin Dec 28, 2025
5566389
[REFACTOR] DeviceTokenService - registerToken 메서드 삭제
JungYoonShin Dec 28, 2025
75ffea2
[REFACTOR] DeviceTokenRepository에서 사용하지 않는 메서드 삭제
JungYoonShin Dec 28, 2025
770d86e
[REFACTOR] 코드 포맷팅
seoyeonjin Dec 28, 2025
d298340
[REFACTOR] 변수명 messageFactoryDto로 수정
JungYoonShin Dec 29, 2025
773c2d5
Merge branch 'feat/#7' of https://github.com/sopt-makers/sopt-push-no…
JungYoonShin Dec 29, 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
6 changes: 6 additions & 0 deletions src/main/java/com/sopt/push/common/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public class Constants {
public static final String HEADER_CONTENT_TYPE = "Content-Type";
public static final String MEDIA_TYPE_APPLICATION_JSON = "application/json";

public static final String HEADER_ACTION = "action";
public static final String HEADER_PLATFORM = "platform";
public static final String HEADER_TRANSACTION_ID = "transactionId";
public static final String HEADER_SERVICE = "service";

public static final String HTTP_METHOD_PATCH = "PATCH";
public static final String HTTP_METHOD_POST = "POST";
public static final String URL_PATH_FORMAT_ID = "%s/%s";
Expand All @@ -53,4 +58,5 @@ public class Constants {

public static final int HTTP_CLIENT_CONNECT_TIMEOUT_SECONDS = 10;
public static final int HTTP_REQUEST_TIMEOUT_SECONDS = 5;
public static final String SNS_PROTOCOL_APPLICATION = "application";
}
24 changes: 24 additions & 0 deletions src/main/java/com/sopt/push/common/DeviceTokenException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.sopt.push.common;

import lombok.Getter;

@Getter
public class DeviceTokenException extends RuntimeException {

private final ErrorMessage errorMessage;

public DeviceTokenException(ErrorMessage errorMessage) {
super(errorMessage.getMessage());
this.errorMessage = errorMessage;
}

public DeviceTokenException(ErrorMessage errorMessage, String detail) {
super(errorMessage.getMessage() + ": " + detail);
this.errorMessage = errorMessage;
}

public DeviceTokenException(ErrorMessage errorMessage, String detail, Throwable cause) {
super(errorMessage.getMessage() + ": " + detail, cause);
this.errorMessage = errorMessage;
}
}
13 changes: 12 additions & 1 deletion src/main/java/com/sopt/push/common/ErrorMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,21 @@ public enum ErrorMessage {
INVALID_REQUEST(StatusCode.BAD_REQUEST, "잘못된 요청입니다."),
NULL_VALUE(StatusCode.BAD_REQUEST, "필요한 값이 없습니다."),
TOKEN_NOT_EXIST(StatusCode.BAD_REQUEST, "존재하지 않는 토큰입니다."),
USER_ID_REQUIRED(StatusCode.BAD_REQUEST, "userId가 필요합니다."),
TOKEN_NOT_FOUND(StatusCode.BAD_REQUEST, "토큰을 찾을 수 없습니다."),
ARN_UNDEFINED(StatusCode.BAD_REQUEST, "arn 또는 topicArn이 정의되지 않았습니다."),
INVALID_ENDPOINT(StatusCode.BAD_REQUEST, "유효하지 않은 SNS 엔드포인트입니다."),
ENDPOINT_ARN_UNDEFINED(StatusCode.INTERNAL_SERVER_ERROR, "endpointArn이 정의되지 않았습니다."),
SUBSCRIPTION_ARN_UNDEFINED(StatusCode.INTERNAL_SERVER_ERROR, "subscriptionArn이 정의되지 않았습니다."),
PLATFORM_APP_ARN_NOT_SET(StatusCode.INTERNAL_SERVER_ERROR, "플랫폼 애플리케이션 ARN이 설정되지 않았습니다."),

/** 500 Internal Server Error */
SEND_FAIL(StatusCode.INTERNAL_SERVER_ERROR, "메시지 전송 실패."),
INTERNAL_SERVER_ERROR(StatusCode.INTERNAL_SERVER_ERROR, "서버 내부 오류");
INTERNAL_SERVER_ERROR(StatusCode.INTERNAL_SERVER_ERROR, "서버 내부 오류"),
REGISTER_USER_ERROR(StatusCode.INTERNAL_SERVER_ERROR, "토큰 등록 중 오류가 발생했습니다."),
DELETE_TOKEN_ERROR(StatusCode.INTERNAL_SERVER_ERROR, "토큰 삭제 중 오류가 발생했습니다."),
SNS_PUBLISH_FAILED(StatusCode.INTERNAL_SERVER_ERROR, "SNS 발행 실패."),
UNKNOWN_PUSH_ERROR(StatusCode.INTERNAL_SERVER_ERROR, "알 수 없는 푸시 전송 오류가 발생했습니다.");

private final int httpStatus;
private final String message;
Expand Down
19 changes: 7 additions & 12 deletions src/main/java/com/sopt/push/config/AppFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import com.sopt.push.repository.HistoryRepository;
import com.sopt.push.repository.UserRepository;
import com.sopt.push.service.DeviceTokenService;
import com.sopt.push.service.EndpointFacade;
import com.sopt.push.service.HistoryService;
import com.sopt.push.service.InvalidEndpointCleaner;
import com.sopt.push.service.NotificationService;
import com.sopt.push.service.SendPushFacade;
import com.sopt.push.service.UserService;
Expand All @@ -22,12 +22,12 @@ public class AppFactory {
private static final AppFactory INSTANCE = new AppFactory();

private final SendPushFacade sendPushFacade;
private final EndpointFacade endpointFacade;
private final WebHookService webHookService;
private final UserService userService;
private final HistoryService historyService;
private final DeviceTokenService deviceTokenService;
private final NotificationService notificationService;
private final InvalidEndpointCleaner invalidEndpointCleaner;

private AppFactory() {

Expand All @@ -50,9 +50,8 @@ private AppFactory() {
this.historyService = new HistoryService(historyRepository);
this.deviceTokenService = new DeviceTokenService(tokenRepository);
this.notificationService = new NotificationService(snsClient, envConfig);
this.invalidEndpointCleaner =
new InvalidEndpointCleaner(
this.userService, this.deviceTokenService, this.notificationService);
this.endpointFacade =
new EndpointFacade(this.deviceTokenService, this.userService, this.notificationService);

this.webHookService = new WebHookService(httpClient, envConfig);
this.sendPushFacade =
Expand All @@ -62,7 +61,7 @@ private AppFactory() {
this.historyService,
this.userService,
this.deviceTokenService,
invalidEndpointCleaner);
this.endpointFacade);
}

public static AppFactory getInstance() {
Expand All @@ -89,11 +88,7 @@ public DeviceTokenService deviceTokenService() {
return deviceTokenService;
}

public NotificationService notificationService() {
return notificationService;
}

public InvalidEndpointCleaner invalidEndpointCleaner() {
return invalidEndpointCleaner;
public EndpointFacade endpointFacade() {
return endpointFacade;
}
}
6 changes: 6 additions & 0 deletions src/main/java/com/sopt/push/config/EnvConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,23 @@ public final class EnvConfig {
private static final String ALL_TOPIC_ARN_ENV_VAR = "ALL_TOPIC_ARN";
private static final String MAKERS_APP_SERVER_URL = "MAKERS_APP_SERVER_URL";
private static final String MAKERS_OPERATION_SERVER_URL = "MAKERS_OPERATION_SERVER_URL";
private static final String PLATFORM_APPLICATION_IOS_ENV = "PLATFORM_APPLICATION_iOS";
private static final String PLATFORM_APPLICATION_ANDROID_ENV = "PLATFORM_APPLICATION_ANDROID";

private final String dynamoDbTableName;
private final String allTopicArn;
private final String makersAppServerUrl;
private final String makersOperationServerUrl;
private final String platformApplicationIosArn;
private final String platformApplicationAndroidArn;

public EnvConfig() {
this.dynamoDbTableName = getRequiredEnv(DYNAMODB_TABLE_ENV_VAR);
this.allTopicArn = getRequiredEnv(ALL_TOPIC_ARN_ENV_VAR);
this.makersAppServerUrl = getRequiredEnv(MAKERS_APP_SERVER_URL);
this.makersOperationServerUrl = getRequiredEnv(MAKERS_OPERATION_SERVER_URL);
this.platformApplicationIosArn = getRequiredEnv(PLATFORM_APPLICATION_IOS_ENV);
this.platformApplicationAndroidArn = getRequiredEnv(PLATFORM_APPLICATION_ANDROID_ENV);
}

private static String getRequiredEnv(String key) {
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/sopt/push/dto/ApiGatewayRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.sopt.push.dto;

import java.util.Map;

public record ApiGatewayRequestDto(RegisterHeaderDto header, Map<String, Object> body) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요기 Body는 Map으로 꼭 써야하나요? 요부분 제가 잘 몰라서..ㅎㅎ

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extractRequest에서 아래 액션을 확인하기 전에 body를 파싱하는데, 어떤 타입으로 변환할지 알 수가 없어서 Map<String, Object>로 받아서, 각 핸들러(handleRegister, handleCancel 등)에서 필요한 타입으로 mapper.convertValue()를 사용해서 변환하고 있습니다 !!

      switch (action) {
        case REGISTER -> handleRegister(request);
        case CANCEL -> handleCancel(request);
        case SEND -> handleSend(request);
        case SEND_ALL -> handleSendAll(request);
        default -> throw new BusinessException(ErrorMessage.INVALID_REQUEST);
      }

8 changes: 8 additions & 0 deletions src/main/java/com/sopt/push/dto/RegisterHeaderDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.sopt.push.dto;

import com.sopt.push.enums.Actions;
import com.sopt.push.enums.Platform;
import com.sopt.push.enums.Services;

public record RegisterHeaderDto(
String transactionId, Services service, Platform platform, Actions action) {}
Loading