[Basic-2] 순수 자바 예제로 이해하는 DIP와 OCP 위반

2025. 12. 30. 12:53·Spring/Core

 스프링의 필요성을 체감하기 위해, 스프링 프레임워크의 도움 없이 순수 자바(Pure Java)만으로 비즈니스 요구사항을 구현해 본다. 이 과정에서 다형성을 활용한 설계가 실제 운영 환경에서 어떤 한계에 부딪히는지 확인하는 것이 핵심이다.


1. 비즈니스 요구사항과 설계

우리가 구현할 시스템은 회원 가입, 조회 기능과 등급별 할인 정책을 포함한 주문 서비스이다.

1.1. 회원 도메인 요구사항

  • 회원 등급: 일반(BASIC), VIP 두 가지 등급이 존재한다.
  • 데이터 저장: 자체 DB를 구축할 수도 있고, 외부 시스템과 연동할 수도 있다. (현재는 미확정 상태)

1.2. 주문 및 할인 정책 요구사항

  • 할인 정책: 모든 VIP 등급 회원에게는 1,000원을 할인해 주는 '고정 금액 할인'을 적용한다.
  • 유연성 확보: 할인 정책은 변경 가능성이 매우 높다. 오픈 직전까지 고민을 미루고 싶으며, 최악의 경우 할인을 적용하지 않을 수도 있다.

2. 도메인 설계: 역할과 구현의 분리

 요구사항이 미확정된 상태에서 개발을 무기한 미룰 수는 없다. 객체 지향의 다형성을 활용하여 인터페이스(역할)를 먼저 정의하고, 언제든지 갈아끼울 수 있는 구현체를 만들어 설계를 진행한다.

2.1. 회원 도메인 설계

  • 역할: MemberRepository (인터페이스)
  • 구현: MemoryMemberRepository (메모리 저장소 - 우선 개발용)

2.2. 주문 도메인 설계

  • 역할: DiscountPolicy (할인 정책 인터페이스)
  • 구현: FixDiscountPolicy (고정 금액 할인 구현체)

3. 순수 자바 코드 구현의 문제점

이제 설계한 내용을 바탕으로 실제 서비스 코드를 작성해 본다.

3.1. 회원 서비스 구현체 (MemberServiceImpl)

public class MemberServiceImpl implements MemberService {
    // 인터페이스뿐만 아니라 구현체 클래스에도 직접 의존하고 있다.
    private final MemberRepository memberRepository = new MemoryMemberRepository();

    public void join(Member member) {
        memberRepository.save(member);
    }
    // ... 생략
}

3.2. 주문 서비스 구현체 (OrderServiceImpl)

public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository = new MemoryMemberRepository();
    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);
        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

 위 코드는 겉으로 보기에 인터페이스를 사용하고 있어 객체 지향적인 것처럼 보인다. 하지만 실제로는 심각한 설계적 결함이 숨어 있다.


4. SOLID 원칙 위반 분석

구현된 코드를 클래스 의존관계 관점에서 분석해 보면 다음과 같은 문제점이 드러난다.

⓵ DIP(의존관계 역전 원칙) 위반

 OrderServiceImpl은 DiscountPolicy라는 추상화(인터페이스)에 의존하는 것처럼 보이지만, 코드 내부를 보면 new FixDiscountPolicy()를 통해 구체화(구현 클래스)에도 함께 의존하고 있다. 즉, 클라이언트가 구현체를 직접 선택하고 있는 것이다.

⓶ OCP(개방-폐쇄 원칙) 위반

 만약 할인 정책을 '고정 금액 할인'에서 '정률(%) 할인'으로 변경해야 한다면 어떻게 될까? DiscountPolicy의 구현체를 교체하기 위해 클라이언트 코드인 OrderServiceImpl의 소스 코드를 직접 수정해야만 한다.

public class OrderServiceImpl implements OrderService {
    // 정책 변경을 위해 기존 코드를 수정해야 함 (OCP 위반)
    // private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
    private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}
이는 "확장에는 열려 있으나 변경에는 닫혀 있어야 한다"는 OCP 원칙을 정면으로 위반하는 사례이다.

5. 결론: 무엇이 필요한가?

 현재의 설계는 다형성을 사용했음에도 불구하고, 객체를 생성하고 연결하는 책임까지 클라이언트 객체가 지고 있기 때문에 원칙을 지킬 수 없다. 이 문제를 해결하기 위해서는 객체의 생성과 연결을 전담하는 '제3의 존재'가 나타나야 한다.

 

 연극의 비유를 빌리자면, 배우(클라이언트)가 상대 배우(구현체)를 직접 섭외하는 것이 아니라, 별도의 공연 기획자가 배역에 맞는 배우를 지정해 주어야 하는 시점이다.

'Spring > Core' 카테고리의 다른 글

[Basic-4] 스프링 컨테이너의 생성과 빈 관리 메커니즘  (0) 2025.12.30
[Basic-3] 관심사의 분리와 의존관계 주입(DI)  (0) 2025.12.30
[Basic-1] 객체 지향 설계와 스프링의 탄생 배경  (0) 2025.12.30
[Practice-3] Spring Core: 깔끔하고 확장성 있는 예외 처리  (1) 2025.12.16
[Practice-2] 객체 생성: 빌더 패턴과 정적 팩터리 메서드  (0) 2025.09.02
'Spring/Core' 카테고리의 다른 글
  • [Basic-4] 스프링 컨테이너의 생성과 빈 관리 메커니즘
  • [Basic-3] 관심사의 분리와 의존관계 주입(DI)
  • [Basic-1] 객체 지향 설계와 스프링의 탄생 배경
  • [Practice-3] Spring Core: 깔끔하고 확장성 있는 예외 처리
h6bro
h6bro
백엔드 개발자의 기술 블로그
  • h6bro
    Jun's Tech Blog
    h6bro
  • 전체
    오늘
    어제
    • 분류 전체보기 (241) N
      • Java (18)
        • Core (9)
        • Design Pattern (9)
      • Spring (80)
        • Core (24)
        • MVC (6)
        • DB (10)
        • JPA (26)
        • Monitoring (3)
        • Security (11)
        • WebSocket (0)
      • Database (33)
        • Redis (15)
        • MySQL (18)
      • MSA (16)
        • MSA 기본 (11)
        • MSA 아키텍처 (5)
      • Kafka (30) N
        • Core (18) N
        • Connect (12)
      • ElasticSearch (11)
        • Search (11)
        • Logging (0)
      • Test (4)
        • k6 (4)
      • Docker (9)
      • CI&CD (10)
        • GitHub Actions (6)
        • ArgoCD (4)
      • Kubernetes (18)
        • Core (12)
        • Ops (6)
      • Cloud Engineering (4)
        • AWS Infrastructure (3)
        • AWS EKS (1)
        • Terraform (0)
      • Project (8)
        • LinkFolio (1)
        • Secondhand Market (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • Cloud Engineering 포스팅 정리
  • 인기 글

  • 태그

    ㅈ
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
h6bro
[Basic-2] 순수 자바 예제로 이해하는 DIP와 OCP 위반
상단으로

티스토리툴바