Skip to content

Commit

Permalink
feat: 예외메시지 구체화 (#161)
Browse files Browse the repository at this point in the history
* feat: 예외 핸들링 추가

* refactor: 예외 메시지 구체화 및 검증 역할 변경

* feat: 에러 코드 추가

* style: 개행 제거

* refactor: 멤버 액션 예외 ErrorCode 분리

* feat: 로깅 추가
  • Loading branch information
Arachneee authored Aug 1, 2024
1 parent fda3f0d commit 0739181
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import server.haengdong.application.request.MemberActionSaveAppRequest;
import server.haengdong.application.request.MemberActionsSaveAppRequest;
import server.haengdong.domain.action.Action;
import server.haengdong.domain.action.CurrentMembers;
import server.haengdong.domain.action.MemberAction;
import server.haengdong.domain.action.MemberActionStatus;
import server.haengdong.domain.action.MemberGroupIdProvider;
Expand All @@ -22,11 +23,11 @@ public class MemberActionFactory {

public List<MemberAction> createMemberActions(
MemberActionsSaveAppRequest request,
List<MemberAction> memberActions,
CurrentMembers currentMembers,
Action action
) {
validateMemberNames(request);
validateActions(request, memberActions);
validateActions(request, currentMembers);

Long memberGroupId = memberGroupIdProvider.createGroupId();
List<MemberAction> createdMemberActions = new ArrayList<>();
Expand All @@ -51,32 +52,12 @@ private void validateMemberNames(MemberActionsSaveAppRequest request) {
}
}

private void validateActions(MemberActionsSaveAppRequest request, List<MemberAction> memberActions) {
List<MemberAction> reverseSortedMemberActions = memberActions.stream()
.sorted(Comparator.comparing(MemberAction::getSequence).reversed())
.toList();

for (MemberActionSaveAppRequest action : request.actions()) {
validateAction(action, reverseSortedMemberActions);
}
}
private void validateActions(MemberActionsSaveAppRequest request, CurrentMembers currentMembers) {
List<MemberActionSaveAppRequest> actions = request.actions();

private void validateAction(MemberActionSaveAppRequest request, List<MemberAction> memberActions) {
MemberActionStatus memberActionStatus = MemberActionStatus.of(request.status());
if (isInvalidStatus(memberActions, request.name(), memberActionStatus)) {
throw new HaengdongException(HaengdongErrorCode.INVALID_MEMBER_ACTION);
for (MemberActionSaveAppRequest action : actions) {
MemberActionStatus memberActionStatus = MemberActionStatus.of(action.status());
currentMembers.validate(action.name(), memberActionStatus);
}
}

private boolean isInvalidStatus(
List<MemberAction> memberActions,
String memberName,
MemberActionStatus memberActionStatus
) {
return memberActions.stream()
.filter(action -> action.isSameName(memberName))
.findFirst()
.map(action -> action.isSameStatus(memberActionStatus))
.orElse(MemberActionStatus.IN != memberActionStatus);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ public void saveMemberAction(String token, MemberActionsSaveAppRequest request)
Event event = findEvent(token);

List<MemberAction> findMemberActions = memberActionRepository.findAllByEvent(event);
CurrentMembers currentMembers = CurrentMembers.of(findMemberActions);
Action action = createStartAction(event);
List<MemberAction> memberActions = memberActionFactory.createMemberActions(request, findMemberActions, action);
List<MemberAction> memberActions = memberActionFactory.createMemberActions(request, currentMembers, action);
memberActionRepository.saveAll(memberActions);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import server.haengdong.exception.HaengdongErrorCode;
import server.haengdong.exception.HaengdongException;

public class CurrentMembers {

Expand All @@ -14,7 +16,7 @@ public CurrentMembers() {
this(new HashSet<>());
}

private CurrentMembers(Set<String> members) {
protected CurrentMembers(Set<String> members) {
this.members = members;
}

Expand Down Expand Up @@ -52,6 +54,15 @@ public CurrentMembers addMemberAction(MemberAction memberAction) {
return new CurrentMembers(currentMembers);
}

public void validate(String memberName, MemberActionStatus memberActionStatus) {
if (memberActionStatus == MemberActionStatus.IN && members.contains(memberName)) {
throw new HaengdongException(HaengdongErrorCode.INVALID_MEMBER_IN_ACTION);
}
if (memberActionStatus == MemberActionStatus.OUT && !members.contains(memberName)) {
throw new HaengdongException(HaengdongErrorCode.INVALID_MEMBER_OUT_ACTION);
}
}

public boolean isEmpty() {
return members.isEmpty();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package server.haengdong.exception;

public record ErrorResponse(
String code,
String message
) {

public static ErrorResponse of(String message) {
return new ErrorResponse(message);
public static ErrorResponse of(HaengdongErrorCode errorCode) {
return new ErrorResponse(errorCode.getCode(), errorCode.getMessage());
}

public static ErrorResponse of(HaengdongErrorCode errorCode, String message){
return new ErrorResponse(errorCode.getCode(), message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,51 @@
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<ErrorResponse> haengdongException() {
@ExceptionHandler({HttpRequestMethodNotSupportedException.class, NoResourceFoundException.class})
public ResponseEntity<ErrorResponse> noResourceException() {
return ResponseEntity.badRequest()
.body(ErrorResponse.of(HaengdongErrorCode.BAD_REQUEST.getMessage()));
.body(ErrorResponse.of(HaengdongErrorCode.NO_RESOURCE_REQUEST));
}

@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<ErrorResponse> httpMessageNotReadableException() {
return ResponseEntity.badRequest()
.body(ErrorResponse.of(HaengdongErrorCode.MESSAGE_NOT_READABLE));
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.info(e.getMessage(), e);
String errorMessage = e.getFieldErrors().stream()
.map(error -> error.getField() + " " + error.getDefaultMessage())
.collect(Collectors.joining(", "));

return ResponseEntity.badRequest()
.body(ErrorResponse.of(errorMessage));
.body(ErrorResponse.of(HaengdongErrorCode.BAD_REQUEST, errorMessage));
}

@ExceptionHandler(HaengdongException.class)
public ResponseEntity<ErrorResponse> haengdongException(HaengdongException e) {
return ResponseEntity.status(e.getStatusCode())
.body(ErrorResponse.of(e.getMessage()));
log.info(e.getMessage(), e);
return ResponseEntity.badRequest()
.body(ErrorResponse.of(e.getErrorCode()));
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
log.error(e.getMessage(), e);
return ResponseEntity.internalServerError()
.body(ErrorResponse.of(HaengdongErrorCode.INTERNAL_SERVER_ERROR.getMessage()));
.body(ErrorResponse.of(HaengdongErrorCode.INTERNAL_SERVER_ERROR));
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
package server.haengdong.exception;

import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
public enum HaengdongErrorCode {
BAD_REQUEST(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."),
DUPLICATED_MEMBER_ACTION(HttpStatus.BAD_REQUEST, "올바르지 않은 인원 요청입니다."),
INVALID_MEMBER_ACTION(HttpStatus.BAD_REQUEST, "잘못된 맴버 액션입니다."),

NOT_FOUND_EVENT(HttpStatus.NOT_FOUND, "존재하지 않는 행사입니다."),

INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부에서 에러가 발생했습니다."),
BAD_REQUEST("R_001", "잘못된 요청입니다."),
NO_RESOURCE_REQUEST("R_002", "잘못된 엔드포인트입니다."),
MESSAGE_NOT_READABLE("R_003", "읽을 수 없는 요청 형식입니다."),
DUPLICATED_MEMBER_ACTION("MA_001", "중복된 인원이 존재합니다."),
INVALID_MEMBER_IN_ACTION("MA_002", "현재 참여하고 있는 인원이 존재합니다."),
INVALID_MEMBER_OUT_ACTION("MA_003", "현재 참여하고 있지 않는 인원이 존재합니다."),
NOT_FOUND_EVENT("EV_400", "존재하지 않는 행사입니다."),
INTERNAL_SERVER_ERROR("S_001", "서버 내부에서 에러가 발생했습니다."),
;

private final HttpStatus httpStatus;
private final String code;
private final String message;

HaengdongErrorCode(HttpStatus httpStatus, String message) {
this.httpStatus = httpStatus;
HaengdongErrorCode(String code, String message) {
this.code = code;
this.message = message;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package server.haengdong.exception;

import lombok.Getter;
import org.springframework.http.HttpStatusCode;

@Getter
public class HaengdongException extends RuntimeException {
Expand All @@ -18,10 +17,6 @@ public HaengdongException(HaengdongErrorCode errorCode, String message) {
this.message = message;
}

public HttpStatusCode getStatusCode() {
return errorCode.getHttpStatus();
}

@Override
public String getMessage() {
if (message == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import server.haengdong.application.request.MemberActionSaveAppRequest;
import server.haengdong.application.request.MemberActionsSaveAppRequest;
import server.haengdong.domain.action.Action;
import server.haengdong.domain.action.CurrentMembers;
import server.haengdong.domain.event.Event;
import server.haengdong.domain.action.MemberAction;
import server.haengdong.domain.action.MemberActionStatus;
Expand Down Expand Up @@ -56,10 +57,12 @@ void createMemberActionsTest() {

MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest(
List.of(new MemberActionSaveAppRequest("토다리", "OUT")));

List<MemberAction> unorderedMemberActions = List.of(memberAction2, memberAction1);
CurrentMembers currentMembers = CurrentMembers.of(unorderedMemberActions);
Action startAction = new Action(event, 3L);

assertThatThrownBy(() -> memberActionFactory.createMemberActions(request, unorderedMemberActions, startAction))
assertThatThrownBy(() -> memberActionFactory.createMemberActions(request, currentMembers, startAction))
.isInstanceOf(HaengdongException.class);
}

Expand All @@ -75,8 +78,10 @@ void createMemberActionsTest1() {
List.of(new MemberActionSaveAppRequest("토다리", "OUT")));
Action startAction = new Action(event, 2L);

CurrentMembers currentMembers = CurrentMembers.of(List.of(memberAction));
List<MemberAction> memberActions = memberActionFactory.createMemberActions(memberActionsSaveAppRequest,
List.of(memberAction), startAction);
currentMembers, startAction
);

assertThat(memberActions).hasSize(1)
.extracting(MemberAction::getAction, MemberAction::getMemberName, MemberAction::getStatus)
Expand All @@ -96,8 +101,9 @@ void createMemberActionsTest2() {
MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest(
List.of(new MemberActionSaveAppRequest("토다리", "OUT")));
Action startAction = new Action(event, 2L);
CurrentMembers currentMembers = CurrentMembers.of(List.of(memberAction));

assertThatCode(() -> memberActionFactory.createMemberActions(request, List.of(memberAction), startAction))
assertThatCode(() -> memberActionFactory.createMemberActions(request, currentMembers, startAction))
.doesNotThrowAnyException();
}

Expand All @@ -115,10 +121,9 @@ void createMemberActionsTest3() {
MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest(
List.of(new MemberActionSaveAppRequest("토다리", "IN")));
Action startAction = new Action(event, 3L);
CurrentMembers currentMembers = CurrentMembers.of(List.of(memberAction1, memberAction2));

assertThatCode(
() -> memberActionFactory.createMemberActions(request, List.of(memberAction1, memberAction2),
startAction))
assertThatCode(() -> memberActionFactory.createMemberActions(request, currentMembers, startAction))
.doesNotThrowAnyException();
}

Expand All @@ -133,8 +138,9 @@ void createMemberActionsTest4() {
MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest(
List.of(new MemberActionSaveAppRequest("쿠키", "IN")));
Action startAction = new Action(event, 2L);
CurrentMembers currentMembers = CurrentMembers.of(List.of(memberAction));

assertThatCode(() -> memberActionFactory.createMemberActions(request, List.of(memberAction), startAction))
assertThatCode(() -> memberActionFactory.createMemberActions(request, currentMembers, startAction))
.doesNotThrowAnyException();
}

Expand All @@ -146,8 +152,10 @@ void createMemberActionTest5() {
MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest(
List.of(new MemberActionSaveAppRequest("쿠키", "OUT")));
Action startAction = new Action(event, 2L);
CurrentMembers currentMembers = CurrentMembers.of(List.of());

assertThatCode(() -> memberActionFactory.createMemberActions(request, List.of(), startAction))
assertThatCode(
() -> memberActionFactory.createMemberActions(request, currentMembers, startAction))
.isInstanceOf(HaengdongException.class);
}

Expand All @@ -162,8 +170,9 @@ void createMemberActionTest6() {
MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest(
List.of(new MemberActionSaveAppRequest("쿠키", "IN")));
Action startAction = new Action(event, 2L);
CurrentMembers currentMembers = CurrentMembers.of(List.of(memberAction));

assertThatCode(() -> memberActionFactory.createMemberActions(request, List.of(memberAction), startAction))
assertThatCode(() -> memberActionFactory.createMemberActions(request, currentMembers, startAction))
.isInstanceOf(HaengdongException.class);
}

Expand All @@ -173,11 +182,15 @@ void createMemberActionTest7() {
Event event = eventRepository.save(new Event("test", "TOKEN"));

MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest(
List.of(new MemberActionSaveAppRequest("쿠키", "IN"),
new MemberActionSaveAppRequest("쿠키", "IN")));
List.of(
new MemberActionSaveAppRequest("쿠키", "IN"),
new MemberActionSaveAppRequest("쿠키", "IN")
));
Action startAction = new Action(event, 1L);
CurrentMembers currentMembers = CurrentMembers.of(List.of());

assertThatCode(() -> memberActionFactory.createMemberActions(request, List.of(), startAction))
assertThatCode(
() -> memberActionFactory.createMemberActions(request, currentMembers, startAction))
.isInstanceOf(HaengdongException.class);
}

Expand All @@ -190,11 +203,14 @@ void createMemberActionTest8() {
memberActionRepository.save(memberAction);

MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest(
List.of(new MemberActionSaveAppRequest("쿠키", "OUT"),
new MemberActionSaveAppRequest("쿠키", "OUT")));
List.of(
new MemberActionSaveAppRequest("쿠키", "OUT"),
new MemberActionSaveAppRequest("쿠키", "OUT")
));
Action startAction = new Action(event, 2L);
CurrentMembers currentMembers = CurrentMembers.of(List.of(memberAction));

assertThatCode(() -> memberActionFactory.createMemberActions(request, List.of(memberAction), startAction))
assertThatCode(() -> memberActionFactory.createMemberActions(request, currentMembers, startAction))
.isInstanceOf(HaengdongException.class);
}

Expand All @@ -207,11 +223,14 @@ void createMemberActionTest9() {
memberActionRepository.save(memberAction);

MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest(
List.of(new MemberActionSaveAppRequest("쿠키", "IN"),
new MemberActionSaveAppRequest("쿠키", "OUT")));
List.of(
new MemberActionSaveAppRequest("쿠키", "IN"),
new MemberActionSaveAppRequest("쿠키", "OUT")
));
Action startAction = new Action(event, 2L);
CurrentMembers currentMembers = CurrentMembers.of(List.of(memberAction));

assertThatCode(() -> memberActionFactory.createMemberActions(request, List.of(memberAction), startAction))
assertThatCode(() -> memberActionFactory.createMemberActions(request, currentMembers, startAction))
.isInstanceOf(HaengdongException.class);
}
}
Loading

0 comments on commit 0739181

Please sign in to comment.