1. 프로젝트 진행 상황 및 계획[깃허브 링크]
🥇 Spring 심화 프로젝트 도전 과제 6단계 끝내기 (진행 중, 25.01.06 완료 목표)
🥈 Spring 심화 프로젝트 리드미(README) 작성하기 (진행 전, 25.01.06 완료 목표)
🥉 Spring 심화 프로젝트 필수 과제 2단계 끝내기 (완료)
2. 한꺼번에 코드 정렬하기 (맥북(MAC OS) 기준)
먼저 패키지를 한 번 클릭하고 command + option + L을 누르면 Reformat Code 창이 뜬다. 팀에서는 우선 불필요한 import를 한꺼번에 제거하고 줄 맞춤 또한 한꺼번에 진행하고자 두 가지를 선택했다. 이렇게 하면 일일이 파일을 눌러서 다듬을 때 드는 시간과 노력을 아낄 수 있다.
3. 회고
구현하고 싶은 기능은 참 많은데 시간이 부족하다. 정확히는 시간 안에 구현할 실력이 아직 한참 부족하다. 머릿속 구상과 실제 실력 사이에 생긴 거리가 길수록 괴리감이라고 해야 할까, 실력이 얼마나 부족한지 확 느껴지곤 한다. 집채만 한 크기에 투명한 파도를 철썩 소리 나게 정통으로 맞은 듯이.
사실 도전 단계 과제 안 해도 뭐라 할 사람은 아무도 없다. 여태까지 제출한 개인 과제 점수는 팀 배정에 영향을 줄 뿐이라서. 특히 이번에 주어진 과제 6단계 중 필수 과제인 1단계는 기존에 제출한 일정 관리 앱을 보완하기였고, 2단계는 '유효성 검사 추가', '예외 처리 강화', '서비스의 도메인 로직(domain logic)을 엔티티(entity)에 위임하기' 중 2가지 시도하기였다. 필수 과제만 했으면 하루 안에 끝내고 여유로운 한 주를 보낼 수 있었다. 이 점을 너무나도 잘 아는 지금, 해보고 싶은 기능이 머릿속을 떠날 줄 몰라서 도전 과제 6단계를 붙잡는 중이고.
정말 쉬엄쉬엄하는 한 주가 고팠지만, 좀처럼 포기할 용기가 나지 않았다.
해보고 싶은 게 참 많은지라, 프로젝트를 진행하는 내내 잠을 아낄 듯싶다.
지금 많이 헤매야 나중에 헤매지 않을 거라는 욕심에 과제며 머리를 부여잡은 채 한숨 쉬며 뜯어고치는 중이다. 언제쯤 직접 작성한 코드에 만족하는 날이 오려나.
package com.example.plan.exception;
public class ErrorMessage {
public static final String MEMBER_NOT_FOUND = "Member does not exist";
public static final String PLAN_NOT_FOUND = "Plan does not exist";
public static final String COMMENT_NOT_FOUND = "Comment does not exist";
public static final String EMAIL_NOT_MATCH = "Email does not Match";
public static final String PASSWORD_NOT_MATCH = "Password does not match";
public static final String DATA_ALREADY_DELETED = "Requested data has already been deleted";
public static final String INVALID_PATH = "Please check input path";
public static final String ERROR_MEMBER_NOT_FOUND = "ERROR_MEMBER_NOT_FOUND";
public static final String ERROR_PLAN_NOT_FOUND = "ERROR_PLAN_NOT_FOUND";
public static final String ERROR_COMMENT_NOT_FOUND = "ERROR_COMMENT_NOT_FOUND";
public static final String ERROR_EMAIL_NOT_MATCH = "ERROR_EMAIL_NOT_MATCH";
public static final String ERROR_PASSWORD_NOT_MATCH = "ERROR_PASSWORD_NOT_MATCH";
public static final String ERROR_DATA_ALREADY_DELETED = "ERROR_DATA_ALREADY_DELETED";
public static final String ERROR_INVALID_PATH = "ERROR_INVALID_PATH";
public static final String ERROR_INVALID_INPUT = "ERROR_INVALID_INPUT";
}
package com.example.plan.exception;
public class AlreadyDeletedException extends RuntimeException {
public AlreadyDeletedException(String message) {
super(message);
}
}
package com.example.plan.exception;
public class CommentNotFoundException extends RuntimeException {
public CommentNotFoundException(String message) {
super(message);
}
}
package com.example.plan.exception;
public class EmailMismatchException extends RuntimeException {
public EmailMismatchException(String message) {
super(message);
}
}
package com.example.plan.exception;
public class MemberNotFoundException extends RuntimeException {
public MemberNotFoundException(String message) {
super(message);
}
}
package com.example.plan.exception;
public class PasswordMismatchException extends RuntimeException {
public PasswordMismatchException(String message) {
super(message);
}
}
package com.example.plan.exception;
public class PlanNotFoundException extends RuntimeException {
public PlanNotFoundException(String message) {
super(message);
}
}
package com.example.plan.exception;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>> handleValidationException(
MethodArgumentNotValidException e
) {
List<String> errors = new ArrayList<>();
errors = e.getFieldErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage
)
.toList();
return handleException(
new Exception(String.join(". ", errors)),
ErrorMessage.ERROR_INVALID_INPUT,
HttpStatus.BAD_REQUEST
);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleOtherException() {
return handleException(
new Exception(ErrorMessage.INVALID_PATH),
ErrorMessage.ERROR_INVALID_PATH,
HttpStatus.NOT_FOUND
);
}
@ExceptionHandler(EmailMismatchException.class)
public ResponseEntity<Map<String, Object>> handleEmailMismatchException(
EmailMismatchException ex
) {
return handleException(
ex,
ErrorMessage.ERROR_EMAIL_NOT_MATCH,
HttpStatus.UNAUTHORIZED
);
}
@ExceptionHandler(PasswordMismatchException.class)
public ResponseEntity<Map<String, Object>> handlePasswordMismatchException(
PasswordMismatchException ex
) {
return handleException(
ex,
ErrorMessage.ERROR_PASSWORD_NOT_MATCH,
HttpStatus.UNAUTHORIZED
);
}
@ExceptionHandler(MemberNotFoundException.class)
public ResponseEntity<Map<String, Object>> handleMemberNotFoundException(
MemberNotFoundException ex
) {
return handleException(
ex,
ErrorMessage.ERROR_MEMBER_NOT_FOUND,
HttpStatus.NOT_FOUND
);
}
@ExceptionHandler(PlanNotFoundException.class)
public ResponseEntity<Map<String, Object>> handlePlanNotFoundException(
PlanNotFoundException ex
) {
return handleException(
ex,
ErrorMessage.ERROR_PLAN_NOT_FOUND,
HttpStatus.NOT_FOUND
);
}
@ExceptionHandler(CommentNotFoundException.class)
public ResponseEntity<Map<String, Object>> handleCommentNotFoundException(
CommentNotFoundException ex
) {
return handleException(
ex,
ErrorMessage.ERROR_COMMENT_NOT_FOUND,
HttpStatus.NOT_FOUND
);
}
@ExceptionHandler(AlreadyDeletedException.class)
public ResponseEntity<Map<String, Object>> handleAlreadyDeletedException(
AlreadyDeletedException ex
) {
return handleException(
ex,
ErrorMessage.ERROR_DATA_ALREADY_DELETED,
HttpStatus.CONFLICT
);
}
private ResponseEntity<Map<String, Object>> handleException(
Exception ex,
String errorCode,
HttpStatus status
) {
String errorMessage = ex.getMessage();
Map<String, Object> errorResponse = new LinkedHashMap<>();
errorResponse.put("errorCode", errorCode);
errorResponse.put("errorMessage", errorMessage);
return new ResponseEntity<>(errorResponse, status);
}
}
필수 과제 2단계를 할 때 일일이 메시지를 입력했다가 오탈자가 발생할 가능성이 높아 보여서 ErrorMessage라는 클래스(class)를 별도로 만들었다. 사람마다 다를 테지만 일단 내가 '좋다, 나쁘다'라고 생각할 때 기준이 바로 '오탈자가 있느냐, 없느냐'이었기 때문에. 무엇보다 이렇게 직접 입력할 부분이 여러 번 눈에 띄면 꼭 한 곳에서 관리하고 싶다는 마음이 들었다. 이 방향이 맞는지는 확인해야 하겠지만, 우선 머릿속에 그린 대로 구현하는 데에 성공해서 기분이 좋았다. 그러니 또 구현하고 싶은 기능이 생겼으면 어떻게 해야겠는가. 남은 시간 동안 최대한 집중력을 발휘해서 시도해 볼 수밖에.
'끝을 보는 용기' 카테고리의 다른 글
Day 093 - Baro5Nda(바로온다) 프로젝트 10%, 목표는 인후염에서 벗어나기 (0) | 2025.01.07 |
---|---|
Day 092 - Spring 심화 프로젝트 6단계 완료 및 과제 제출, 54 commits, 4,765 ++, 2,938 -- (0) | 2025.01.06 |
Day 090 - Spring 심화 프로젝트 5단계 완료, 첫 테스트 코드 작성 (0) | 2025.01.04 |
Day 089 - Spring 심화 프로젝트 3단계 및 4단계 완료, 단순한 개발자가 아니라 창작자가 되고 싶다. (0) | 2025.01.03 |
Day 088 - Spring 심화 프로젝트 3단계 진행 중, 팀 프로젝트 전에 다 같이 코드 작성 규칙을 정하다. (0) | 2025.01.02 |