어 나 갱수.

[Java] OCP를 적용시켜보자 ! 😝 본문

Java

[Java] OCP를 적용시켜보자 ! 😝

김경수 2024. 7. 11. 00:38
728x90

OCP를 통해 기능 확장에 강하고, 외부 변경에 강건한 소프트웨어를 만들고자 로버트 마틴이 명명한 객체지향 5대 원칙 중 하나입니다.

 

OCP는 소프트웨어를 설계하면서, 기능이나 모듈의 확장에 대해서는 개방(OPEN) 되어야 하고, 기존의 것의 변경에 대해서는 폐쇄(CLOSE) 되어야 한다는 원칙입니다.

간단하게 말하면, 새로운 기능이 추가되더라도 기존의 코드를 변경하지 않고도 확장이 가능해야 한다는 점입니다.

 

아래의 코드는 OCP를 적용시키지 못한 코드라고 말할 수 있습니다.

 

JavaMailSendPort는 제대로 추상화된 인터페이스라고 불릴 수 없습니다. 추상화된 인터페이스에서 구현 라이브러리인 JavaMail의 이름을 사용해 버렸기 때문에 1:1로 밖에 구현클래스를 사용할 수밖에 없기 때문입니다. 다양한 종류의 알림을 전송할 수 있도록 더 추상적인 인터페이스로 변경이 필요하다고 느꼈습니다.

interface JavaMailSendPort {

    fun sendMail(email: String, authCode: String)

}

 

아래 코드에서는 JavaMailSendPort 인터페이스를 구현하는 MailSendAdapter를 구현하고 있습니다. 

@Component
class MailSendAdapter(
    private val mailSender: JavaMailSender,
    private val templateEngine: SpringTemplateEngine
): JavaMailSendPort {

    companion object {
        val EMAIL_SUBJECT = "GOMS 이메일 인증"
    }

    @Async
    override fun sendMail(email: String, authCode: String) {
        val message = mailSender.createMimeMessage()
        val helper = MimeMessageHelper(message, "utf-8")
        helper.setTo(email)
        helper.setSubject(EMAIL_SUBJECT)
        val context = setContext(authCode)
        helper.setText(context, true)

        runCatching {
            mailSender.send(message)
        }.onFailure {
            throw EmailSendFailException()
        }
    }

    private fun setContext(authCode: String): String {
        val template = "certificationMailTemplate"
        val context = Context()

        context.setVariable("authenticationCode", authCode)

        return templateEngine.process(template, context)
    }


}

 

 

이제 OCP를 잘 적용시킨 코드를 살펴보겠습니다!

 

먼저 알림 전송이라는 추상화된 인터페이스를 만들어줍니다. 위에서 선언한 JavaMailSendPort에 비해서 좀 더 추상화된 느낌의 인터페이스입니다. 다양한 종류의 알림을 전송할 수 있도록 더 추상적인 인터페이스로 변경하는 것이 필요했습니다.


그래서, 기존의 JavaMailSendPort 인터페이스를 NotificationSendPort라는 보다 추상적인 인터페이스로 변경했습니다.

interface NotificationSendPort {

    fun sendNotification(email: String, authCode: String)

}

아래와 같이 NotificationSendPort라는 알림 전송 인터페이스를 구현하는 구현체인 MailSendAdapter를 만들어줍니다.
추상화된 인터페이스인 NotificationSendPort를 구체적으로 구현할 수 있는 MailSendAdapter를 만들 수 있습니다.

@Component
class MailSendAdapter(
    private val mailSender: JavaMailSender,
    private val templateEngine: SpringTemplateEngine
): NotificationSendPort {

    companion object {
        val EMAIL_SUBJECT = "GOMS 이메일 인증"
    }

    @Async
    override fun sendNotification(email: String, authCode: String) {
        val message = mailSender.createMimeMessage()
        val helper = MimeMessageHelper(message, "utf-8")
        helper.setTo(email)
        helper.setSubject(EMAIL_SUBJECT)
        val context = setContext(authCode)
        helper.setText(context, true)

        runCatching {
            mailSender.send(message)
        }.onFailure {
            throw EmailSendFailException()
        }
    }

    private fun setContext(authCode: String): String {
        val template = "certificationMailTemplate"
        val context = Context()

        context.setVariable("authenticationCode", authCode)

        return templateEngine.process(template, context)
    }


}

 

위와 같이 설계를 하면 OCP를 잘 적용한 코드라고 설명할 수 있습니다. 위와 같이 설계를 한다면 추후에 기능을 추가할 때 이메일 인증뿐 아니라 문자 인증을 구현하면서 휴대폰 문자로 인증번호를 전송해야 하는 상황에서 기존의 코드를 변경하지 않고도 NotificationSendPort를 구현하는 SmsSendAdapter라는 구현체를 만들어서 해당 클래스에서 휴대폰 문자 인증 서비스를 구현할 수 있습니다.

 

위와 같이 OCP를 적용해 볼 수 있습니다.

728x90