-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ feat: API 응답 정규화 (#37)
- Loading branch information
Showing
10 changed files
with
289 additions
and
78 deletions.
There are no files selected for viewing
47 changes: 21 additions & 26 deletions
47
noti-service/src/main/java/com/waither/notiservice/api/NotificationController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,47 @@ | ||
package com.waither.notiservice.api; | ||
|
||
import com.waither.notiservice.api.response.NotificationResponse; | ||
import com.waither.notiservice.dto.LocationDto; | ||
import com.waither.notiservice.api.request.LocationDto; | ||
import com.waither.notiservice.global.response.ApiResponse; | ||
import com.waither.notiservice.service.NotificationService; | ||
import com.waither.notiservice.utils.RedisUtils; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.media.Content; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
import jakarta.validation.Valid; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
import java.util.List; | ||
|
||
@RequiredArgsConstructor | ||
@RequestMapping("/api/v1/noti") | ||
@RestController | ||
public class NotificationController { | ||
|
||
private final NotificationService notificationService; | ||
|
||
@Operation(summary = "get notification", description = "알림 목록 조회하기") | ||
@ApiResponses({ | ||
@ApiResponse(responseCode = "200", description = "OK", | ||
content = @Content(schema = @Schema(implementation = NotificationResponse.class))), | ||
@ApiResponse(responseCode = "400", description = "BAD REQUEST"), | ||
@ApiResponse(responseCode = "404", description = "NOT FOUND"), | ||
@ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR") | ||
}) | ||
private final RedisUtils redisUtils; | ||
|
||
@Operation(summary = "Get notification", description = "알림 목록 조회하기") | ||
@GetMapping("") | ||
public List<NotificationResponse> getNotifications(Long userId) { | ||
return notificationService.getNotifications(userId); | ||
public ApiResponse<?> getNotifications(Long userId) { | ||
return ApiResponse.onSuccess(notificationService.getNotifications(userId)); | ||
} | ||
|
||
@Operation(summary = "Delete notification", description = "알림 삭제하기") | ||
@DeleteMapping("") | ||
public ApiResponse<?> deleteNotification(@RequestParam("id") String notificationId) { | ||
notificationService.deleteNotification(notificationId); | ||
return ApiResponse.onSuccess(HttpStatus.OK); | ||
} | ||
|
||
@Operation(summary = "Send Go Out Alarm", description = "외출 알림 전송하기") | ||
@ApiResponses({ | ||
@ApiResponse(responseCode = "200", description = "OK"), | ||
@ApiResponse(responseCode = "400", description = "BAD REQUEST"), | ||
@ApiResponse(responseCode = "404", description = "NOT FOUND"), | ||
@ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR") | ||
}) | ||
@PostMapping("/goOut") | ||
public void sendGoOutAlarm(Long userId) { | ||
notificationService.sendGoOutAlarm(userId); | ||
} | ||
|
||
@PostMapping("/") | ||
public void checkCurrentAlarm(@RequestBody LocationDto locationDto) { | ||
@Operation(summary = "Current Location", description = "현재 위치 전송") | ||
@PostMapping("/location") | ||
public void checkCurrentAlarm(@RequestBody @Valid LocationDto locationDto) { | ||
notificationService.checkCurrentAlarm(locationDto); | ||
} | ||
|
||
} |
27 changes: 27 additions & 0 deletions
27
noti-service/src/main/java/com/waither/notiservice/api/request/LocationDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.waither.notiservice.api.request; | ||
|
||
import jakarta.validation.Valid; | ||
import jakarta.validation.constraints.DecimalMax; | ||
import jakarta.validation.constraints.DecimalMin; | ||
import jakarta.validation.constraints.NotBlank; | ||
import jakarta.validation.constraints.NotNull; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
public class LocationDto { | ||
|
||
@NotBlank(message = " 위도(x) 값은 필수입니다.") | ||
@DecimalMax(value = "132.0", inclusive = true, message = "위도(x)는 대한민국 내에서만 가능합니다.") | ||
@DecimalMin(value = "124.0", inclusive = true, message = "위도(x)는 대한민국 내에서만 가능합니다.") | ||
public double x; | ||
|
||
@NotBlank(message = " 경도(y) 값은 필수입니다.") | ||
@DecimalMax(value = "43.0", inclusive = true, message = "경도(y)는 대한민국 내에서만 가능합니다.") | ||
@DecimalMin(value = "33.0", inclusive = true, message = "경도(y)는 대한민국 내에서만 가능합니다.") | ||
public double y; | ||
} |
16 changes: 0 additions & 16 deletions
16
noti-service/src/main/java/com/waither/notiservice/dto/LocationDto.java
This file was deleted.
Oops, something went wrong.
15 changes: 15 additions & 0 deletions
15
noti-service/src/main/java/com/waither/notiservice/global/exception/CustomException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.waither.notiservice.global.exception; | ||
|
||
|
||
import com.waither.notiservice.global.response.status.BaseErrorCode; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
public class CustomException extends RuntimeException { | ||
|
||
private final BaseErrorCode errorCode; | ||
|
||
public CustomException(BaseErrorCode errorCode) { | ||
this.errorCode = errorCode; | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
...ervice/src/main/java/com/waither/notiservice/global/exception/GlobalExceptionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package com.waither.notiservice.global.exception; | ||
|
||
|
||
import com.waither.notiservice.global.response.ApiResponse; | ||
import com.waither.notiservice.global.response.ErrorCode; | ||
import com.waither.notiservice.global.response.status.BaseErrorCode; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.validation.FieldError; | ||
import org.springframework.web.bind.MethodArgumentNotValidException; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
|
||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
@Slf4j | ||
@RestControllerAdvice | ||
public class GlobalExceptionHandler { | ||
|
||
// @Valid 유효성 검사를 실패했을 시 | ||
@ExceptionHandler(MethodArgumentNotValidException.class) | ||
protected ResponseEntity<ApiResponse<Map<String, String>>> handleMethodArgumentNotValidException( | ||
MethodArgumentNotValidException ex | ||
) { | ||
// 실패한 validation 을 담을 Map | ||
Map<String, String> failedValidations = new HashMap<>(); | ||
List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors(); | ||
// fieldErrors 를 순회하며 failedValidations 에 담는다. | ||
fieldErrors.forEach(error -> failedValidations.put(error.getField(), error.getDefaultMessage())); | ||
ApiResponse<Map<String, String>> errorResponse = ApiResponse.onFailure( | ||
ErrorCode.VALIDATION_FAILED.getCode(), | ||
ErrorCode.VALIDATION_FAILED.getMessage(), | ||
failedValidations); | ||
return ResponseEntity.status(ex.getStatusCode()).body(errorResponse); | ||
} | ||
|
||
// Custom 예외에 대한 처리 | ||
@ExceptionHandler({CustomException.class}) | ||
public ResponseEntity<ApiResponse<Void>> handleCustomException(CustomException e) { | ||
log.warn("[WARNING] Custom Exception : {}", e.getErrorCode()); | ||
BaseErrorCode errorCode = e.getErrorCode(); | ||
return ResponseEntity. | ||
status(errorCode.getHttpStatus()) | ||
.body(errorCode.getErrorResponse()); | ||
} | ||
|
||
// 그 외의 정의되지 않은 모든 예외 처리 | ||
@ExceptionHandler({Exception.class}) | ||
public ResponseEntity<ApiResponse<String>> handleAllException(Exception e) { | ||
log.error("[WARNING] Internal Server Error : {} ", e.getMessage()); | ||
BaseErrorCode errorCode = ErrorCode.INTERNAL_SERVER_ERROR_500; | ||
ApiResponse<String> errorResponse = ApiResponse.onFailure( | ||
errorCode.getCode(), | ||
errorCode.getMessage(), | ||
e.getMessage() | ||
); | ||
return ResponseEntity | ||
.status(errorCode.getHttpStatus()) | ||
.body(errorResponse); | ||
} | ||
|
||
} |
45 changes: 45 additions & 0 deletions
45
noti-service/src/main/java/com/waither/notiservice/global/response/ApiResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.waither.notiservice.global.response; | ||
|
||
import com.fasterxml.jackson.annotation.JsonInclude; | ||
import com.fasterxml.jackson.annotation.JsonPropertyOrder; | ||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import org.springframework.http.HttpStatus; | ||
|
||
|
||
@Getter | ||
@AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
@JsonPropertyOrder({"code", "message", "result"}) | ||
public class ApiResponse<T> { | ||
|
||
// HTTP 상태 코드나 사용자 정의 코드 | ||
private final String code; | ||
// API 요청에 대한 설명이나 상태 메시지 | ||
private final String message; | ||
// result 값은 null이 아닐 때만 응답에 포함시킨다. | ||
@JsonInclude(JsonInclude.Include.NON_NULL) | ||
// 결과 데이터. (보통 dto -> json 파싱 될 예정) | ||
private T result; | ||
|
||
// 성공한 경우 응답 생성 | ||
public static <T> ApiResponse<T> onSuccess(T result) { | ||
return new ApiResponse<>(String.valueOf(HttpStatus.OK.value()), HttpStatus.OK.getReasonPhrase(), result); | ||
} | ||
|
||
// 성공한 경우 응답 생성 (상태 코드 지정 가능) | ||
public static <T> ApiResponse<T> onSuccess(HttpStatus status, T result) { | ||
return new ApiResponse<>(String.valueOf(status.value()), status.getReasonPhrase(), result); | ||
} | ||
|
||
// 실패한 경우 응답 생성 | ||
public static <T> ApiResponse<T> onFailure(String code, String message, T result) { | ||
return new ApiResponse<>(code, message, result); | ||
} | ||
|
||
// 실패한 경우 응답 생성 (데이터 없음) | ||
public static <T> ApiResponse<T> onFailure(String statusCode, String message) { | ||
return new ApiResponse<>(statusCode, message, null); | ||
} | ||
|
||
} |
53 changes: 53 additions & 0 deletions
53
noti-service/src/main/java/com/waither/notiservice/global/response/ErrorCode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package com.waither.notiservice.global.response; | ||
|
||
|
||
import com.waither.notiservice.global.response.status.BaseErrorCode; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import org.springframework.http.HttpStatus; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
public enum ErrorCode implements BaseErrorCode { | ||
|
||
// 일반적인 ERROR 응답 | ||
BAD_REQUEST_400(HttpStatus.BAD_REQUEST, | ||
"COMMON400", | ||
HttpStatus.BAD_REQUEST.getReasonPhrase()), | ||
UNAUTHORIZED_401(HttpStatus.UNAUTHORIZED, | ||
"COMMON401", | ||
HttpStatus.UNAUTHORIZED.getReasonPhrase()), | ||
FORBIDDEN_403(HttpStatus.FORBIDDEN, | ||
"COMMON403", | ||
HttpStatus.FORBIDDEN.getReasonPhrase()), | ||
NOT_FOUND_404(HttpStatus.NOT_FOUND, | ||
"COMMON404", | ||
HttpStatus.NOT_FOUND.getReasonPhrase()), | ||
INTERNAL_SERVER_ERROR_500( | ||
HttpStatus.INTERNAL_SERVER_ERROR, | ||
"COMMON500", | ||
HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()), | ||
|
||
// 유효성 검사 | ||
VALIDATION_FAILED(HttpStatus.BAD_REQUEST, "VALID400_0", "잘못된 파라미터 입니다."), | ||
|
||
// 데이터 관련 에러 | ||
NO_USER_MEDIAN_REGISTERED(HttpStatus.NOT_FOUND, "USER404_0", "사용자 설정값이 존재하지 않습니다."), | ||
NO_USER_DATA_REGISTERED(HttpStatus.NOT_FOUND, "USER404_1", "사용자 데이터 값이 존재하지 않습니다."), | ||
FIREBASE_TOKEN_NOT_FOUND(HttpStatus.NOT_FOUND, "TOKEN404", "푸시알림 토큰이 존재하지 않습니다."), | ||
|
||
//통신 과정 에러 | ||
COMMUNICATION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500_1", "통신 과정에서 문제가 발생했습니다.") | ||
|
||
; | ||
|
||
|
||
private final HttpStatus httpStatus; | ||
private final String code; | ||
private final String message; | ||
|
||
@Override | ||
public ApiResponse<Void> getErrorResponse() { | ||
return ApiResponse.onFailure(code, message); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
noti-service/src/main/java/com/waither/notiservice/global/response/status/BaseErrorCode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.waither.notiservice.global.response.status; | ||
|
||
import com.waither.notiservice.global.response.ApiResponse; | ||
import org.springframework.http.HttpStatus; | ||
|
||
public interface BaseErrorCode { | ||
|
||
HttpStatus getHttpStatus(); | ||
|
||
String getCode(); | ||
|
||
String getMessage(); | ||
|
||
ApiResponse<Void> getErrorResponse(); | ||
} |
Oops, something went wrong.