Java/Design Pattern

[디자인 패턴] 구조 패턴 - 퍼사드 패턴 (Facade Pattern)

민돌v 2023. 3. 9. 18:18
728x90
(인프런) 코딩으로 학습하는 GoF의 디자인 패턴 - 백기선, 강의를 보고 정리한 글입니다.
코드는 GitHub 에 있습니다

 

#1. 객체 생성 관련 패턴

#2. 구조 관련 패턴

 

#3. 행동 관련 패턴

 

 

 


✔️ 퍼사드 패턴 (facade patterns)

퍼사드 패턴의 퍼사드(Facade) 는 불어로 건물의 외벽 및 정면(정문)을 의미합니다.
소프트웨어공학적으로 이야기하자면, 모든 내부 로직을 facade 즉 정문을 이용해서 사용하겠다는 의미를 가지는 것 같습니다.
  • 퍼사드 패턴이란 : 복잡한 서브 시스템의 의존성을 최소화하기 위한 목적을 가지는 패턴입니다.
  • 클라이언트가 사용해야 하는 복잡한 서브 시스템의 의존성을 간단한 이터페이스로 추상화 할 수 있다고 합니다.
  • 퍼사드 패턴의 궁극적인 목표"객체간의 의존성을 느슨하게 만드는 것"이라고 생각합니다.

 

 


✔️ 퍼사드 패턴 예시

먼저 강의에 나오는 예시대로 정리해보겠습니다. 하나의 Client 에 모든게 의존적인 1번 예시와 이를 facade 로 리펙토링하는 2번예제의 순으로 정리합니다.

 

1. 메일을 전송하는 간단한 예제 코드 입니다.

  • 모든 로직과 호출하는 부분이 Client 에 의존적으로 묶여있기 때문에 Client 의 요구사항이 바뀌든, Message 양식이 바뀌든, host 세팅이 바뀌든 부분이 바뀌면 전체가 바뀌어야 합니다.
  • 이를 의존성이 강하게 묶여있다고 이야기합니다.
import jakarta.mail.Message.RecipientType;
import jakarta.mail.MessagingException;
import jakarta.mail.Session;
import jakarta.mail.Transport;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import java.util.Properties;

public class Client {

    public static void main(String[] args) {
        String to = "hmin@naver.com";
        String from = "google@naver.com";
        String host = "127.0.0.1";

        Properties properties = System.getProperties();
        properties.setProperty("mail.smtp.host", host);

        Session session = Session.getDefaultInstance(properties);

        try {
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(from));
            message.addRecipient(RecipientType.TO, new InternetAddress(to));
            message.setSubject("Test Mail from Java Program");
            message.setText("message hi!");

            Transport.send(message);
        } catch (MessagingException ex) {
            ex.printStackTrace();
        }
    }
}

 

2. Client 가 로직을 가지고있고 직접호출하는게 아닌, 로직 앞단에 "Facade (정면 입구)"를 두어 내부 로직을 알 지 못하도록 리펙토링하려 합니다.

  1. 이메일 전송 비지니스 로직
  2. 메세지 형식
  3. host 설정

이렇게 3가지로 의존성을 분리하겠습니다.

 

EmailMessage

  • 먼저 메세지 형식을 담을 역할을 가지는 DTO 클래스를 만들어 줍니다.
@Getter
public class EmailMessage {

    private String from;
    private String to;
    private String subject;
    private String text;

    public EmailMessage(String from, String to, String subject, String text) {
        this.from = from;
        this.to = to;
        this.subject = subject;
        this.text = text;
    }
}

EmailSetting

  • 실제로는 더 복잡하겠지만, 단순하게 host 정보를 가지고 Email Setting 의 역할을 가지는 클래스도 정의해줍니다.
@Getter
@AllArgsConstructor
public class EmailSettings {

    private String host;
}

EmailFacade

  • 이제 Facade를 정의합니다.
  • 강의에서는 EmailSender라 하지만, 저는 EmailFacade라 정의하겠습니다.
  • Clent 에서 가져가는 의존성을 Facade에서 가져가도록 변경합니다.
  • EmailMessage는 클라이언트의 요청에따라 달라지기 때문에 파라미터로 받아옵니다.
public class EmailFacade {

    private EmailSettings emailSettings;

    public EmailFacade(EmailSettings emailSettings) {
        this.emailSettings = emailSettings;
    }

    public void sendEmail(EmailMessage emailMessage) throws MessagingException {

        final Properties properties = getProperties();
        final Session session = Session.getDefaultInstance(properties);
        final MimeMessage message = getMimeMessage(emailMessage, session);

        Transport.send(message);
    }

    private static MimeMessage getMimeMessage(EmailMessage emailMessage, Session session)
        throws MessagingException {
        MimeMessage message = new MimeMessage(session);
        message.setFrom(new InternetAddress(emailMessage.getFrom()));
        message.addRecipient(RecipientType.TO, new InternetAddress(emailMessage.getTo()));
        message.setSubject(emailMessage.getSubject());
        message.setText(emailMessage.getText());
        return message;
    }

    private Properties getProperties() {
        Properties properties = System.getProperties();
        properties.setProperty("mail.smtp.host", emailSettings.getHost());
        return properties;
    }
}

Client

  • Client 는 Facade를 통해 sendEmail 이란 로직을 호출하기만 합니다.
  • Client 는 sendEmail 이란 기능만을 알고 내부에 어떤 로직이 존재하는지 알 지 못합니다.
public class Client {

    public static void main(String[] args) throws MessagingException {

        final EmailSettings emailSettings = new EmailSettings("127.0.0.1");
        final EmailFacade emailFacade = new EmailFacade(emailSettings);

        emailFacade.sendEmail(new EmailMessage(
                "hmin@namver.com",
                "google@naver.com",
                "Test Mail from Java Program",
                "message hi!"
            ));
    }
}

 


✔️ 현업에서의 퍼사드 패턴 적용기

사실 회사에 facade 패턴을 레이어에 적용해서 사용하고 있어 굉장히 반가웠습니다.

현업이라고 적어놓았지만, 정해놓은 룰에 불과하고 솔직히 해당 강의 예시만으로는 이해하기 힘들어서 추가적으로 적어놓으니 참고용으로만 보시면 될거같습니당

 

Facade Pattern 레이어

  • 그렇게 거창하지는 않고, 저희 팀은 spring 을 사용할 때 service 레이어와 repository 레이어의 의존성을 줄이기위해 사용했습니다.
  • 궁극적인 목표는 service : repositoy = 1 : 1 의 구조로 변경하기 위함이었습니다.

 

따라서 레이어는 아래 그림과 같아집니다.

레이어드 아키텍쳐

 

기존의 레이어드 아키텍쳐에서 Controller 와 Service 사이에 Facade (정문) 을 두어 1 개의 Service 레이어는 1개의 Repository 더 나아가서 1개의 Entity에 대한 로직만을 수행하게 됩니다.

[Facade 레이어의 역할]

  1. facade 에서는 비지니스 로직을 거의 수행하지 않고, service 레이어를 통해 호출하고
  2. 호출한 데이터를 담아 다른 service 레이어를 호출해서 데이터를 담아 Controller 에 응답만을 보냅니다.

→ 이렇게 함으로써, 요구사항 변경에대한 의존성을 Facade 레이어 하나만 변경하도록 하는 목적을 가지고 있습니다.

 

이렇게 정리하고 보면 위의 코드예시도 더 이해하기 쉽지 않을까하고.. 정리해보았습니다!!

 

 

facade 도 끝! 

 

 

 

반응형