IT/JAVA

🧱 1. 브리지 패턴이란?

밥알이 2025. 5. 31. 23:54

브리지 패턴(Bridge Pattern) 완전 정복 - 자바 실무 예제로 이해하기

디자인 패턴 중에서 기능 계층과 구현 계층을 분리하여 구조를 유연하게 만들어주는 브리지 패턴(Bridge Pattern)에 대해 들어보셨나요?

이 글에서는 어댑터 패턴(Adapter Pattern)과 비교되는 브리지 패턴의 개념부터 자바 실무 예제까지 쉽게 정리해드립니다.


✅ 브리지 패턴이란?

브리지 패턴(Bridge Pattern)은 기능(추상)과 구현(구체)을 독립적으로 분리하여 확장이 용이하도록 돕는 구조 디자인 패턴입니다.

  • 클래스 간 결합도를 낮추기 위해 상속 대신 위임을 활용
  • 새로운 기능이나 구현이 추가될 때 기존 클래스에 영향 없이 확장 가능

📌 Adapter Pattern과의 차이점

어댑터 패턴(Adapter Pattern)은 기존 클래스를 다른 인터페이스에 맞게 변환할 때 사용되고, 브리지 패턴은 처음부터 구조적으로 기능과 구현을 나누어 설계할 때 사용됩니다.


 

🎨 브리지 패턴을 리모컨과 TV로 쉽게 이해하기

리모컨 (기능) TV (구현)
켜기 / 끄기 LG, 삼성, 소니 등
  • 리모컨은 동작만 정의하고, 어떤 TV를 켤지는 리모컨이 내부적으로 알고 있음
  • 새로운 리모컨 or TV 추가 시 기존 코드 수정 없이도 확장 가능

📌 브리지 패턴이 필요한 시점

  • 구현 클래스가 다양하고, 기능과 구현을 각각 독립적으로 확장해야 할 때
  • UI 테마, OS별 처리, 디바이스 종류 등 조합이 많을 때
  • 클래스 수가 폭발하는 상황을 방지하고 싶을 때

🎯 예제 시나리오: 메시지 전송 시스템

전송 수단은 Email, Slack, 메시지 유형은 긴급, 일반으로 구성된 시스템이 있다고 가정해봅시다.

기존 방식대로라면 각 조합마다 클래스를 만들거나 if-else 조건문이 난무할 수 있죠. 이럴 때 브리지 패턴을 적용하면, 각 계층을 독립적으로 구현하고 조합할 수 있어 훨씬 깔끔한 구조가 됩니다.


🔧 실무 자바 예제로 보는 브리지 패턴

1️⃣ 구현체 인터페이스

public interface MessageSender {
    void send(String message);
}

2️⃣ 구현체 클래스: Email / Slack

public class EmailSender implements MessageSender {
    @Override
    public void send(String message) {
        System.out.println("[EMAIL] " + message);
    }
}

public class SlackSender implements MessageSender {
    @Override
    public void send(String message) {
        System.out.println("[SLACK] " + message);
    }
}

3️⃣ 추상 클래스: 메시지

public abstract class Message {
    protected MessageSender sender;

    public Message(MessageSender sender) {
        this.sender = sender;
    }

    public abstract void sendMessage(String content);
}

4️⃣ 구체 메시지 클래스

public class UrgentMessage extends Message {
    public UrgentMessage(MessageSender sender) {
        super(sender);
    }

    @Override
    public void sendMessage(String content) {
        sender.send("[긴급] 🚨 " + content);
    }
}

public class NormalMessage extends Message {
    public NormalMessage(MessageSender sender) {
        super(sender);
    }

    @Override
    public void sendMessage(String content) {
        sender.send("[일반] 📩 " + content);
    }
}

5️⃣ 클라이언트 코드

public class Main {
    public static void main(String[] args) {
        MessageSender email = new EmailSender();
        MessageSender slack = new SlackSender();

        Message urgentViaEmail = new UrgentMessage(email);
        Message normalViaSlack = new NormalMessage(slack);

        urgentViaEmail.sendMessage("결제 오류 발생!");
        normalViaSlack.sendMessage("정기 리포트 발송");
    }
}

✅ 실행 결과

[EMAIL] [긴급] 🚨 결제 오류 발생!
[SLACK] [일반] 📩 정기 리포트 발송

📦 UML 구조 요약


        [MessageSender] ◄────────────┐
             ▲                      │
[EmailSender], [SlackSender]        │
                                    │
        [Message] ◄─────────────────┘
             ▲
[UrgentMessage], [NormalMessage]

💡 장점과 단점 비교

항목 설명
✅ 장점 기능과 구현을 독립적으로 확장 가능
조합 수가 많아도 클래스 수 최소화
SRP, OCP 등 SOLID 원칙 준수
❌ 단점 구조가 익숙하지 않으면 복잡하게 느껴질 수 있음
간단한 기능엔 과한 설계가 될 수 있음

🛠 실무에서의 사용 예

  • Spring Framework에서 전략 객체 분리
  • 다양한 OS 환경에서 UI 컴포넌트 분리
  • PDF/HTML/Excel 등 다양한 형식의 보고서 출력기

📝 마무리 정리

항목 내용
패턴 이름 브리지 패턴 (Bridge Pattern)
주요 목적 기능 계층과 구현 계층을 분리하여 독립적 확장
주요 구성 요소 Abstraction, Implementor, RefinedAbstraction, ConcreteImplementor
적용 시점 조합 수가 많고, 클래스 수가 늘어날 때

📌 관련 디자인 패턴도 함께 알아두세요

  • 어댑터 패턴(Adapter Pattern): 기존 코드를 다른 인터페이스에 맞게 변환할 때 사용
  • 데코레이터 패턴: 기능을 런타임에 동적으로 확장할 때 유용
  • 전략 패턴: 알고리즘을 캡슐화하고 동적으로 교체 가능