[9] 디자인패턴: 옵저버 패턴 (Observer)
·
Java/Design Pattern
1. 들어가며: "강한 결합"에서 "이벤트"로 기존의 전통적인 방식에서는 하나의 비즈니스 로직이 성공하면 그에 따른 부가 작업을 직접 호출했다. 예를 들어 '회원 가입'이 완료되면, 회원 서비스가 직접 EmailService.send()와 SmsService.send()를 호출하는 식이다.1.1. 문제 상황: 연쇄적인 수정의 늪 이러한 구조는 회원 서비스가 이메일과 SMS 서비스의 존재를 너무 자세히 알고 있어야 한다는 강한 결합(Strong Coupling) 문제를 야기한다. 만약 나중에 "가입 축하 포인트 지급" 기능이 추가된다면 회원 서비스의 코드를 또 수정해야 한다. 비즈니스가 복잡해질수록 회원 서비스는 감당할 수 없을 정도로 거대해지며, 하나를 고치면 열 군데가 고장 나는 위험한 상태가 되는 것..
[8] 디자인패턴: 퍼사드 패턴 (Facade)
·
Java/Design Pattern
1. 들어가며 '퍼사드(Facade)'는 프랑스어로 건물의 정면을 뜻한다. 우리가 건물을 볼 때 내부의 복잡한 배관이나 전기 배선은 보지 않고 정면 외벽만 보듯이, 소프트웨어 설계에서도 내부의 복잡한 의존 관계를 감추고 단순한 '창구'를 마련하는 것이 핵심인 것이다.1.1. 문제 상황: 복잡성의 전이 만약 '상품 주문'이라는 기능을 구현하기 위해 클라이언트(Controller)가 주문 서비스, 재고 서비스, 결제 서비스, 배송 서비스를 각각 직접 호출해야 한다면 어떻게 될까?클라이언트 코드가 너무 비대해진다 (Fat Controller).내부 서비스 구조가 변경될 때마다 클라이언트 코드도 함께 수정해야 한다.클라이언트가 내부 시스템의 동작 순서나 의존성을 모두 파악하고 있어야 하므로 결합도가 극도로 높아..
[7] 디자인패턴: 어댑터 패턴 (Adapter)
·
Java/Design Pattern
1. 들어가며 소프트웨어 개발 과정에서 우리는 이미 잘 만들어진 외부 라이브러리나 과거에 작성된 유산(Legacy) 코드를 마주하게 된다. 하지만 우리가 새로 설계한 시스템의 인터페이스와 이러한 기존 코드의 인터페이스가 서로 맞지 않는 경우가 빈번히 발생한다. 이때 기존의 코드를 수정하지 않고도 두 인터페이스를 연결하여 함께 작동하게 만드는 것이 바로 어댑터 패턴(Adapter Pattern)인 것이다.1.1. 어댑터 패턴의 정의와 필요성 어댑터 패턴은 이름 그대로 우리가 일상에서 사용하는 '변환 젠더'와 같은 역할을 수행한다. 220V 플러그를 110V 콘센트에 꽂기 위해 콘센트를 통째로 뜯어고치는 대신, 그 사이를 이어주는 어댑터를 사용하는 것과 같은 이치이다.1.2. 왜 어댑터가 필요한가? 객체지향..
[6] 디자인패턴: 팩토리 패턴 (Factory)
·
Java/Design Pattern
1. 들어가며: 객체지향 프로그래밍에서 객체를 생성하는 것은 가장 기본적인 행위이지만, 동시에 가장 조심스러운 행위이기도 하다. 클래스 내부에서 new 키워드를 사용하여 직접 객체를 생성하는 순간, 해당 클래스는 생성될 객체의 구체적인 타입과 강하게 결합(Coupling)되기 때문이다. 팩토리 패턴은 이러한 '객체 생성의 책임'을 별도의 클래스나 메서드로 분리하여 시스템의 유연성을 확보하는 패턴인 것이다.1.1. 문제 상황: 복잡한 생성 로직의 파편화만약 컨트롤러나 서비스 로직 곳곳에서 조건문을 통해 직접 객체를 생성한다면 어떻게 될까?구체적인 클래스 이름이 코드 전반에 노출되어 수정 시 변경 범위가 넓어진다.객체 생성 로직이 복잡해질 경우(예: 의존성 설정, 초기화 작업 등), 중복 코드가 발생한다.생..
[5] 디자인패턴: 데코레이터 패턴 (Decorator)
·
Java/Design Pattern
1. 들어가며: 상속의 한계와 합성의 탄생 새로운 기능을 추가할 때 가장 먼저 떠오르는 방법은 '상속'이다. 하지만 상속은 부모와 자식 클래스가 컴파일 시점에 강하게 결합된다는 치명적인 약점이 있다. 만약 여러 기능을 조합해야 한다면 어떻게 될까?1.1. 클래스 폭발 (Class Explosion) 문제예를 들어 '기본 알림 전송' 기능에 '로그 추가', '암호화 추가'라는 두 가지 확장이 필요하다고 가정하자. 상속으로 해결하려면 다음과 같은 클래스들이 필요해진다.기본 알림로그 알림 (기본 상속)암호화 알림 (기본 상속)로그 + 암호화 알림 (어디를 상속해야 하는가?) 기능이 하나만 늘어나도 조합의 가짓수는 기하급수적으로 늘어나며, 이를 클래스 폭발이라 부른다. 데코레이터 패턴은 '합성(Compositi..
[4] 디자인패턴: 프록시 패턴 (Proxy)
·
Java/Design Pattern
1. 들어가며 스프링 프레임워크의 핵심 기술인 AOP(관점 지향 프로그래밍)를 이해하기 위해 반드시 넘어야 할 산이 바로 프록시 패턴이다. 프록시(Proxy)는 사전적 의미로 '대리인'을 뜻하며, 객체지향 세계에서는 실제 객체를 직접 호출하는 대신 그 앞에 대리 객체를 두어 제어 흐름을 가로채거나 접근 권한을 제어하는 역할을 수행한다. 일반적인 객체 호출 구조는 클라이언트가 필요한 서비스를 직접 호출하는 방식이다. 하지만 실무에서는 핵심 비즈니스 로직 외에도 권한 체크, 로그 기록, 캐싱, 트랜잭션 처리와 같은 부가적인 기능(Cross-cutting Concerns)이 필요할 때가 많다. 이러한 부가 기능을 실제 서비스 코드에 직접 작성하면 코드가 복잡해지고 유지보수가 어려워진다. 이때 프록시 패턴을..
[3] 디자인패턴: 템플릿 패턴 (Template Method/Callback)
·
Java/Design Pattern
1. 들어가며 백엔드 개발의 숙명 중 하나는 '반복되는 코드(Boilerplate Code)'와의 싸움이다. 로그 기록, 트랜잭션 시작과 종료, 자원 해제(try-catch-finally) 등 비즈니스 로직과는 직접적인 상관이 없지만 반드시 거쳐야 하는 일련의 흐름들이 존재한다. 이러한 알고리즘의 뼈대를 고정하고 변하는 부분만 선택적으로 구현하게 해주는 것이 바로 템플릿 패턴이다. 소프트웨어 설계에서 가장 중요한 원칙 중 하나는 '변하는 것'과 '변하지 않는 것'을 엄격히 분리하는 것이다. 템플릿 패턴은 전체적인 작업 순서(변하지 않는 것)는 부모 클래스나 템플릿에 정의해두고, 구체적인 세부 로직(변하는 것)만 자식 클래스나 외부 콜백에서 구현하도록 설계한다. 스프링 프레임워크에서는 이 패턴을 통해 ..
[2] 디자인패턴: 전략 패턴 (Strategy)
·
Java/Design Pattern
1. 들어가며 디자인 패턴의 세계에 발을 들이면 가장 먼저, 그리고 가장 빈번하게 마주치는 것이 바로 전략 패턴(Strategy Pattern)이다. 이는 단순히 객체지향 패턴 중 하나라기보다는, 변경에 대응하는 소프트웨어를 만드는 가장 기본적인 사고방식에 가깝다. 특히 스프링 부트의 핵심 철학인 의존성 주입(DI) 은 전략 패턴을 프레임워크 차원에서 일반화한 것이라 해도 과언이 아니다. 이번 장에서는 전략 패턴의 정의를 넘어, 왜 실무에서 이 패턴이 자연스럽게 등장하는지, 그리고 스프링 부트 환경에서 어떻게 활용되는지를 중심으로 살펴본다.2. 전략 패턴의 정의와 필요성전략 패턴이란 객체가 수행할 수 있는 행위들을 각각의 전략(Strategy) 으로 분리하고, 실행 시점에 어떤 전략을 사용할지 결정하도..