[코드잇][위클리페이퍼][2주차] 객체지향 설계의 5가지 원칙

2026. 1. 12. 10:02·IT/코드잇

아래는 2주차 위클리페이퍼 주제이다.

  • 객체지향 프로그래밍에서 '단일 책임 원칙(SRP)'과 '개방-폐쇄 원칙(OCP)'에 대해 설명하고, 각각의 원칙을 적용한 코드 예시를 들어주세요.
  • Stream API의 map과 flatMap의 차이점을 설명하고, 각각의 활용 사례를 예시 코드와 함께 설명해주세요.

그 중 현 포스팅에는 첫번째 주제에 관해 설명을 하려고 한다.

먼저 5가지 설계 원칙을 간단하게 요약해서 설명하자면

  1. SRP (Single Responsibility Principle) : 단일 책임의 원칙
    • **”**하나의 클래스는 하나의 책임만 가져야 한다.”
  2. OCP (Open/Closed Principle) : 개방/폐쇄의 원칙
    • ”확장에는 열려있고, 수정에는 닫혀있어야 한다.”
  3. LSP (Liskov’s Substitution Principle) : 리스코브 치환의 원칙
    • ”자식 클래스는 반드시 부모 클래스를 대체할 수 있어야 한다.”
  4. ISP (Interface Segregation Principle) : 인터페이스 분리의 원칙
    • ”하나의 범용 인터페이스보다 다수의 구체적인 인터페이스가 낫다.”
    • “클라이언트는 사용하지 않는 메소드에 의존하면 안 된다.”
  5. DIP (Dependency Inversion Principle) : 의존성 역전의 원칙
    • ”구체적인 클래스 보다는 인터페이스나 추상 클래스와 관계를 맺어야 한다.”
    • ”상위 모듈은 하위 모듈에 의존하면 안 된다”
    • “추상화에 의존해야지, 구체화에 의존하면 안된다.”

이 SOLID 원칙이 왜 중요한지는 이 원칙이 지향하는 목표를 보면 알 수 있다.

 

유연하고 유지보수하기 쉬운 소프트웨어 설계를 목표로 합니다. 이 원칙들은 변경에 유연하고 확장 가능하며 이해하기 쉬운 시스템을 구축하는 데 도움을 주며, 각 원칙은 코드의 응집도를 높이고 결합도를 낮추는 것을 목표로 합니다. 

 

이 포스팅에서는 5가지 원칙 중에서 '단일 책임 원칙(SRP)'과 '개방-폐쇄 원칙(OCP)'을 다뤄보려고 한다.


먼저 단일 책임 원칙이 한번에 와닿지 않는 사람들을 위해 간단한 비유를 하겠다.

다들 아시다시피 자동차는 왼쪽 레버로 방향 지시등이나 상향등 같은 등화류 관련 조작을 할 수 있다. 오른쪽 레버로는 와이퍼 및 워셔액 관련 조작을 할 수 있다.


그런데 만약 이 두 레버를 왼쪽에 하나의 레버로 전부 조작할 수 있도록 바꾼다면 어떻게 될까? 방향지시등을 켜려고 했는데 실수로 와이퍼를 조작할 수도 있고, 그 반대의 상황도 일어날 수 있다.
그리고 만약 레버가 고장났다면? 두 개의 기능을 모두 사용할 수 없게 되는 것이다.

 

이 것을 코드에 빗대어 설명하면 다음과 같다.

class CarControlLever {
    private int wiperSpeed = 0; // 0: OFF, 1: LOW, 2: HIGH
    private String blinkerStatus = "OFF"; // OFF, LEFT, RIGHT

    // [책임 1] 와이퍼 제어 로직
    public void pushWiper(int level) {
        this.wiperSpeed = level;
        System.out.println("와이퍼 속도 조절: " + level);
    }

    // [책임 2] 방향지시등 제어 로직
    public void pushBlinker(String direction) {
        this.blinkerStatus = direction;
        System.out.println(direction + " 깜빡이 작동");
    }

    // 문제점: 
    // 1. 와이퍼 관련 하드웨어 사양이 바뀌어서 코드를 수정해야 하는데, 
    //    아무 상관 없는 깜빡이 코드가 같은 파일에 있어 가독성이 떨어집니다.
    // 2. 깜빡이 전용 테스트 코드를 짜고 싶은데, 와이퍼 변수까지 포함된 무거운 객체를 만들어야 합니다.
}

CarControlLever가 와이퍼와 방향지시등 로직을 전부 가지고 있다. 두 기능은 레버로 작동한다는 것 외에는 연관성이 전혀 없는데 하나의 클래스에 묶여서 클래스가 2개의 책임을 가지게 되었다.

 

이 때, 와이퍼에 빗물 감지 센서를 통한 자동 모드를 도입한다면? 또는 전방 카메라에 의한 상향등 보조 시스템을 도입한다면? 기능을 수정하기 위해 굳이 연관성이 없는 코드를 봐야하고 코드를 수정해서 오류가 발생한다면 멀쩡한 다른 기능이 오작동을 일으킬 수 있다.

 

SRP를 적용한 코드는 다음과 같다.

class WiperModule {
    private int speed = 0;

    public void operate(int level) {
        this.speed = level;
        System.out.println("와이퍼 시스템: 속도 " + level + "로 작동 중");
    }
    
    // 와이퍼와 관련된 추가 기능(예: 워셔액 분사)은 여기에만 추가하면 됩니다.
}
class TurnSignalModule {
    private String status = "OFF";

    public void blink(String direction) {
        this.status = direction;
        System.out.println("방향지시등 시스템: " + direction + " 점멸 중");
    }
    
    // 깜빡이와 관련된 추가 기능(예: 비상등)은 여기에만 추가하면 됨.
}

이제 각 클래스는 하나의 책임만을 가지게 되었다. 우리는 와이퍼 관련 로직을 수정할 때 방향지시등 로직을 전혀 신경 쓸 필요가 없어졌다.

즉, SPR를 적용하면 유지보수가 쉬워지고, 관련 없는 코드를 볼 필요가 없으므로 이해하기 쉬워졌다.


다음으로 개방 폐쇄 원칙에 대해서도 비유로 설명하겠다.

많은 사람들이 개방 폐쇄 원칙을 설명할 때 결제 수단을 예시로 드는데 그만큼 실제 구조와 유사하고 이해하기 쉽기 때문이다.

식당을 차렸는데 카드 포스기가 없어서 현금 결제만 받고 있다가 카드 포스기가 와서 카드로 결제가 가능해졌다. 그리고 중국인 여행객이 많이 와서 알리 페이나 위챗 페이가 가능한 토스 페이 단말기를 설치한 상황이다.

 

이 구조에서는 카드 포스기가 고장나도 현금 결제나 토스 페이 단말기는 정상적으로 작동하고 그 반대도 동일하다.

그리고 새로운 결제 수단이 필요하다면 지원하는 새로운 기기를 도입하면 된다.

그런데 만약 이 모든 걸 하나의 포스기가 담당한다면? 분명 결제라는 책임은 단 한개만 가지고 있음에도 불구하고(SRP 원칙 준수) 포스기가 고장나면 모든 결제를 할 수 없게 되고, 다른 결제 수단을 추가하려면 포스기를 통째로 바꿔야 한다.

 

이를 코드로 보면 다음과 같다.

먼저 OCP를 위반한 모든 결제 수단을 지원하는 포스기다.

class AllInOnePos {
    public void processPayment(String type) {
        if (type.equals("CASH")) {
            System.out.println("현금 결제를 진행합니다.");
        } else if (type.equals("CARD")) {
            System.out.println("카드 단말기를 통해 결제를 진행합니다.");
        } else if (type.equals("TOSS_PAY")) {
            // 알리/위챗 기능을 위해 기존 포스기 로직을 수정 중...
            System.out.println("토스 단말기를 통해 알리/위챗 페이 결제를 진행합니다.");
        }
        // 문제점: 새로운 결제 방식(예: 애플페이)이 나오면 
        // 이미 잘 돌아가던 이 클래스의 if-else 문을 또 건드려야 함 (수정에 열려 있음)
    }
}

우리는 새로운 결제 수단이 추가될 때마다 기존 코드를 수정해야 하므로 수정에 열려있고, 결제 수단을 추가할 때 기존 코드를 수정해야 하므로 확장에 닫혀있다.

 

이를 OCP를 적용하여 개선한 코드는 다음과 같다.

// 결제 기기의 표준 기능
interface PaymentTerminal {
    void pay();
}

// 각 결제 기기들
class CashBox implements PaymentTerminal {
    public void pay() { System.out.println("현금 결제 완료"); }
}

class CardMachine implements PaymentTerminal {
    public void pay() { System.out.println("카드 결제 완료"); }
}

class TossPayTerminal implements PaymentTerminal {
    public void pay() { System.out.println("토스 페이(알리/위챗) 결제 완료"); }
}

// 식당 시스템
class Restaurant {
    // 식당은 '어떤 기기'가 올지는 모르지만 'PaymentTerminal' 규격인 것은 압니다.
    public void acceptPayment(PaymentTerminal terminal) {
        terminal.pay(); // 어떤 기기든 꽂으면 작동함
    }
}

// 만약 여기에 애플페이 단말기를 추가하고 싶다면?
// 새로운 기기 클래스만 생성하면 확장 끝!
class ApplePayTerminal implements PaymentTerminal {
    public void pay() { System.out.println("애플페이 결제 완료"); }
}

 

 

이전에 모든 결제를 받는 포스기라면 식당 주인이 사용자가 어떤 결제 수단을 사용하는지에 따라 포스기에서 설정을 달리 해야 할 것이다. OCP를 적용하여 개선한 코드는 식당 주인이나 사용자는 결제를 할 때 각 포스기가 어떻게 동작하는지는 알 필요 없이 해당 포스기에서 결제만 하면 된다. 그리고 위에서 언급한 내용을 강조하자면 새로운 결제 수단이 추가될 때 포스기를 교체할 필요없이 새로운 단말기를 추가하면 된다.

즉, OCP를 적용하면 확장에 유연하고, 유지보수에서의 이점이 생기는 것이다.

 

이것으로 SOLID 원칙 중 SRP와 OCP에 대한 설명을 마친다.

 

'IT > 코드잇' 카테고리의 다른 글

[코드잇][위클리페이퍼][4주차] 프레임워크와 라이브러리의 차이점  (0) 2026.01.26
[코드잇][위클리페이퍼][4주차] Spring Framework의 탄생 배경  (0) 2026.01.26
[코드잇][위클리페이퍼][3주차] 알고리즘과 자료구조  (0) 2026.01.20
[코드잇][위클리페이퍼][2주차] JAVA의 Stream과 매핑 연산  (0) 2026.01.20
[코드잇][위클리페이퍼][1주차] Git 내용에 관한 정리  (0) 2026.01.05
'IT/코드잇' 카테고리의 다른 글
  • [코드잇][위클리페이퍼][4주차] Spring Framework의 탄생 배경
  • [코드잇][위클리페이퍼][3주차] 알고리즘과 자료구조
  • [코드잇][위클리페이퍼][2주차] JAVA의 Stream과 매핑 연산
  • [코드잇][위클리페이퍼][1주차] Git 내용에 관한 정리
PSG-00
PSG-00
개발자의 작고 소중한 공간
  • PSG-00
    PSG-00님의 블로그
    PSG-00
  • 전체
    오늘
    어제
    • 분류 전체보기 (14)
      • 잡담 (0)
        • 여행 (0)
        • 운동 (0)
      • IT (6)
        • 백엔드 (0)
        • 프론트엔드 (0)
        • 알고리즘(PS) (0)
        • 코드잇 (6)
  • 블로그 메뉴

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

  • 공지사항

  • 인기 글

  • 태그

    코드잇
    git
    SOLID
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
PSG-00
[코드잇][위클리페이퍼][2주차] 객체지향 설계의 5가지 원칙
상단으로

티스토리툴바