본문 바로가기

Yonsei Golf

코드 개선기 - YonseiGolf Email API

1. 문제 정의

연세대학교 골프동아리는 지원자의 합격 결과를 이메일로 공지하고 있습니다.

그런데 기존 API는 합격, 불합격자의 api uri를 다르게 사용하고 있어서 불필요한 코드의 중복이 발생하였습니다.

@PostMapping("/admin/forms/documentPassEmail")
    public ResponseEntity<CustomResponse> sendDocumentPassEmail() {

        applicationService.sendDocumentPassEmail();

        return ResponseEntity
                .ok()
                .body(new CustomResponse(
                        "success",
                        200,
                        "연세골프 지원서 서류 합격자 이메일 전송 성공"
                ));
    }

    @PostMapping("/admin/forms/finalPassEmail")
    public ResponseEntity<CustomResponse> sendFinalPassEmail() {

        applicationService.sendFinalPassEmail();

        return ResponseEntity
                .ok()
                .body(new CustomResponse(
                        "success",
                        200,
                        "연세골프 지원서 최종 합격자 이메일 전송 성공"
                ));
    }

    @PostMapping("/admin/forms/documentFailEmail")
    public ResponseEntity<CustomResponse> sendDocumentFailEmail() {

        applicationService.sendDocumentFailEmail();

        return ResponseEntity
                .ok()
                .body(new CustomResponse(
                        "success",
                        200,
                        "연세골프 지원서 서류 불합격자 이메일 전송 성공"
                ));
    }

    @PostMapping("/admin/forms/finalFailEmail")
    public ResponseEntity<CustomResponse> sendFinalFailEmail() {

        applicationService.sendFinalFailEmail();

        return ResponseEntity
                .ok()
                .body(new CustomResponse(
                        "success",
                        200,
                        "연세골프 지원서 최종 불합격자 이메일 전송 성공"
                ));
    }

2. 개선 방안

기존 코드

public void sendDocumentPassEmail() {
        List<Application> applications = findApplicationsByPassFail(true, null);

        applications.stream()
                .forEach(application -> emailService.sendEmail(application.getEmail(),
                        "안녕하세요. 연세대학교 골프동아리 결과 메일입니다.",
                        application.getName() + "님 서류 합격 축하드립니다. \n" +
                                "면접 일정은 추후 공지될 예정입니다. \n" +
                                "감사합니다."));
    }

    public void sendFinalPassEmail() {
        List<Application> applications = findApplicationsByPassFail(true, true);

        applications.stream()
                .forEach(application -> emailService.sendEmail(application.getEmail(),
                        "안녕하세요. 연세대학교 골프동아리 결과 메일입니다.",
                        application.getName() + "님 최종 합격 축하드립니다. \n" +
                                "추후 일정은 문자로 공지될 예정입니다. \n" +
                                "감사합니다."));
    }

    public void sendDocumentFailEmail() {
        List<Application> applications = findApplicationsByPassFail(false, null);

        applications.stream()
                .forEach(application -> emailService.sendEmail(application.getEmail(),
                        "안녕하세요. 연세대학교 골프동아리 결과 메일입니다.",
                        application.getName() + "님 연세골프에 지원해주셔서 감사합니다. \n\n\n" +
                                "안타깝게도 " + application.getName() + "님께 이번 연골 모집에서 합격의 소식을 전해드리지 못하게 되었습니다." +
                                application.getName() + "님의 뛰어난 열정에도 불구하고, 연세골프는 한정된 인원으로만 운영되는 만큼 아쉽게도 이런 소식을 전해드리게 됐습니다." +
                                "비록 이번 모집에서 " + application.getName() + "님과 함께하지 못하지만, 다음에 함께 할 수 있기를 바라겠습니다. \n\n" +
                                "바쁘신 와중에 지원해주셔서 감사합니다. \n\n" +
                                "연세 골프 운영진 드림")
                );
    }

    public void sendFinalFailEmail() {
        List<Application> applications = findApplicationsByPassFail(true, false);

        applications.stream()
                .forEach(application -> emailService.sendEmail(application.getEmail(),
                        "안녕하세요. 연세대학교 골프동아리 결과 메일입니다.",
                        application.getName() + "님 연세골프에 지원해주셔서 감사합니다. \n\n\n" +
                                "안타깝게도 " + application.getName() + "님께 이번 연골 모집에서 합격의 소식을 전해드리지 못하게 되었습니다." +
                                application.getName() + "님의 뛰어난 열정에도 불구하고, 연세골프는 한정된 인원으로만 운영되는 만큼 아쉽게도 이런 소식을 전해드리게 됐습니다." +
                                "비록 이번 모집에서 " + application.getName() + "님과 함께하지 못하지만, 다음에 함께 할 수 있기를 바라겠습니다. \n\n" +
                                "바쁘신 와중에 지원해주셔서 감사합니다. \n\n" +
                                "연세 골프 운영진 드림")
                );
    }

합격 결과를 분석해보면

서류 합격 : documentPass : true, finalPass : null

최종 합격 : documentPass : true, finalPass : true

면접 탈락 : documentPass : false, finalPass : null

최종 탈락 : docuemtnPass : false, finalPass : false

위와 같이 분포된 것을 알 수 있습니다.

탈락 메일의 경우 메일 양식이 같이 때문에 분기점은 3개로 나눌 수 있는 것을 알 수 있습니다.

if (isDocumentPass && isFinalPass == null) {
            return 서류 합격
    }
if (isDocumentPass && isFinalPass == true) {
            return 최종 합격
    }
return 서류 or 최종 불합격

따라서 코드를 다음과 같이 개선할 수 있습니다.

public void sendEmailNotification(boolean isDocumentPass, Boolean isFinalPass) {

    final NotificationType type = getNotificationType(isDocumentPass, isFinalPass);
    final String subject = "안녕하세요. 연세대학교 골프동아리 결과 메일입니다.";

    findApplicationsByPassFail(isDocumentPass, isFinalPass)
            .stream()
            .forEach(application -> {
                final String message = type.generateMessage(application.getName());
                emailService.sendEmail(application.getEmail(), subject, message);
            });
}

private NotificationType getNotificationType(boolean isDocumentPass, Boolean isFinalPass) {

    if (isDocumentPass && isFinalPass == null) {
        return NotificationType.DOCUMENT_PASS;
    }
    if (isDocumentPass && isFinalPass == true) {
        return NotificationType.FINAL_PASS;
    }
    return NotificationType.FAIL;
}
package yonseigolf.server.email.dto;

public enum NotificationType {
    DOCUMENT_PASS {
        @Override
        public String generateMessage(String name) {
            return name + "님 서류 합격 축하드립니다. \n면접 일정은 추후 공지될 예정입니다. \n감사합니다.";
        }
    },
    FINAL_PASS {
        @Override
        public String generateMessage(String name) {
            return name + "님 최종 합격 축하드립니다. \n추후 일정은 문자로 공지될 예정입니다. \n감사합니다.";
        }
    },
    FAIL {
        @Override
        public String generateMessage(String name) {
            return name + "님 연세골프에 지원해주셔서 감사합니다. \n\n\n" +
                    "안타깝게도 " + name + "님께 이번 연골 모집에서 합격의 소식을 전해드리지 못하게 되었습니다." +
                    name + "님의 뛰어난 열정에도 불구하고, 연세골프는 한정된 인원으로만 운영되는 만큼 아쉽게도 이런 소식을 전해드리게 됐습니다." +
                    "비록 이번 모집에서 " + name + "님과 함께하지 못하지만, 다음에 함께 할 수 있기를 바라겠습니다. \n\n" +
                    "바쁘신 와중에 지원해주셔서 감사합니다. \n\n" +
                    "연세 골프 운영진 드림";
        }
    },
    CLUB_RECRUITMENT{
        @Override
        public String generateMessage(String name) {
            return "연세대학교 골프동아리입니다. \n" +
            "연세대학교 골프동아리 모집이 시작되었습니다.\n " +
                    "https://yonseigolf.site/apply 에서 확인해주세요";
        }
    };

    public abstract String generateMessage(String name);

    public static NotificationType decideNotificationType(boolean isDocumentPass, Boolean isFinalPass) {
        if (isDocumentPass && isFinalPass == null) {
            return DOCUMENT_PASS;
        }
        if (isDocumentPass && isFinalPass) {
            return FINAL_PASS;
        }
        return FAIL;
    }
}

위와 같이 NotificationType으로 메시지 본문을 분리하고,

합격 불합격에 따른 분기로 나누어주면 코드의 가독성이 좋아진 것을 알 수 있습니다.

@PostMapping("/admin/forms/results")
    public ResponseEntity<CustomResponse> sendEmailNotification(@RequestBody ResultNotification request) {

        applicationService.sendEmailNotification(request.isDocumentPass(), request.getFinalPass());

        return ResponseEntity
                .ok()
                .body(new CustomResponse(
                        "success",
                        200,
                        "연세골프 지원서 결과 이메일 전송 성공"
                ));
    }

따라서 기존 합격 불합격에 따른 4개의 api uri로 요청을 보내는 것이 아닌, 하나의 api에 request body만 수정해서 코드의 재사용성을 높일 수 있었습니다.

3. 마무리

기존 개발을 진행할 당시, 개발 속도에만 신경쓰며 클린 코드를 지키지 못한 모습이 부끄럽게 느껴집니다. 개발 속도도 중요하지만, 추후에 다른 부원이 동아리 사이트 유지보수를 진행하게 된다면 같이 진행할 수 있도록 클린코드에 신경쓰며 개발을 진행하겠습니다.

'Yonsei Golf' 카테고리의 다른 글

모니터링 with Docker  (0) 2023.12.09
JWT Token + Refresh Token  (1) 2023.11.27
CloudFront, S3 배포 자동화  (2) 2023.11.26
Alert 개선기  (0) 2023.11.18
CloudFront , S3 , Route 53 을 통한 Vue 프로젝트 배포  (0) 2023.10.24