SOLID 5원칙의 의미와 위반 사례, GoF 23종 생성·구조·행위 분류, 싱글톤·팩토리·어댑터·옵서버·전략 등 기출 핵심 패턴을 완전히 이해한다.
🎯 학습 목표
이 강을 마치면 다음을 할 수 있습니다.
- SOLID 5원칙 각각의 의미와 위반 사례를 설명할 수 있습니다.
- GoF 디자인 패턴 23종을 생성·구조·행위 패턴으로 분류하고 각 패턴의 속하는 종류를 나열할 수 있습니다.
- 싱글톤·팩토리메서드·추상팩토리·어댑터·데코레이터·퍼사드·옵서버·전략·커맨드·템플릿메서드 패턴의 목적과 구조를 설명할 수 있습니다.
🏛️ SOLID — 객체지향 설계의 5가지 원칙
이 섹션에서는 좋은 객체지향 설계를 만드는 기반이 되는 SOLID 5원칙을 살펴보겠습니다.
SOLID는 로버트 C. 마틴(Uncle Bob)이 정리한 객체지향 설계의 5가지 원칙의 첫 글자를 모은 약어입니다. 이 원칙들을 따르면 변경에 유연하고 유지보수하기 쉬운 소프트웨어를 만들 수 있습니다.
S — 단일 책임 원칙(SRP, Single Responsibility Principle)
클래스는 변경해야 할 이유가 오직 하나여야 합니다. 다시 말해 하나의 클래스는 하나의 책임(기능)만 가져야 합니다. 예를 들어 User 클래스가 사용자 데이터 관리, 이메일 발송, 데이터베이스 저장을 모두 담당한다면 SRP를 위반한 것입니다. 이메일 발송 방식이 바뀌어도 User 클래스를 수정해야 하기 때문입니다. 분리하면 EmailService, UserRepository, User로 각자의 책임이 명확해집니다.
O — 개방-폐쇄 원칙(OCP, Open-Closed Principle)
소프트웨어 개체는 확장에는 열려 있고 수정에는 닫혀 있어야 합니다. 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있어야 한다는 뜻입니다. 인터페이스와 추상 클래스를 활용하면 OCP를 실현할 수 있습니다. 예를 들어 도형의 넓이를 계산하는 함수에서 새로운 도형(육각형)을 추가할 때 기존 함수를 수정하지 않고 새 클래스만 추가할 수 있다면 OCP를 만족합니다.
L — 리스코프 치환 원칙(LSP, Liskov Substitution Principle)
자식 클래스는 부모 클래스를 대체할 수 있어야 합니다. 즉, 부모 타입의 객체를 자식 타입의 객체로 교체해도 프로그램의 동작이 변하지 않아야 합니다. 사각형 클래스를 상속한 정사각형 클래스가 너비와 높이를 항상 같게 설정하도록 오버라이딩한다면, 사각형으로 동작하던 코드가 정사각형으로 교체되면 예상치 못한 결과가 나옵니다. 이것이 LSP 위반의 대표적 예입니다.
I — 인터페이스 분리 원칙(ISP, Interface Segregation Principle)
클라이언트는 자신이 사용하지 않는 메서드에 의존하도록 강요받으면 안 됩니다. 하나의 범용 인터페이스보다 여러 개의 구체적인 인터페이스가 낫습니다. 예를 들어 Printer 인터페이스에 print(), scan(), fax()가 모두 있다면, 프린트만 되는 기기도 scan()과 fax()를 구현해야 합니다. 이를 PrintInterface, ScanInterface, FaxInterface로 분리하면 ISP를 만족합니다.
D — 의존성 역전 원칙(DIP, Dependency Inversion Principle)
고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 둘 다 추상화에 의존해야 합니다. 구체적인 구현이 아닌 인터페이스·추상 클래스에 의존하라는 뜻입니다. 예를 들어 OrderService가 직접 MySQLRepository를 참조하면 DB를 바꿀 때 OrderService도 수정해야 합니다. OrderRepository 인터페이스를 사이에 두면 DB 구현체가 바뀌어도 OrderService는 수정할 필요가 없습니다. 이것이 DIP입니다.
🎨 GoF 디자인 패턴 23종 분류
이 섹션에서는 GoF(Gang of Four) 디자인 패턴 23종의 분류 체계를 살펴보겠습니다.
디자인 패턴(Design Pattern)은 소프트웨어 설계에서 자주 만나는 문제들에 대한 재사용 가능한 해결책입니다. 1994년 에리히 감마·리차드 헬름·랄프 존슨·존 블리시디스(Gang of Four)가 23가지 패턴을 체계화했습니다. 디자인 패턴을 알면 검증된 방법으로 설계할 수 있고, 팀원들과 공통된 언어로 소통할 수 있습니다.
23종 패턴은 생성(Creational), 구조(Structural), 행위(Behavioral)의 3가지 카테고리로 분류됩니다.
| 분류 | 목적 | 패턴 (총 23종) |
|---|---|---|
| 생성 패턴 (5종) | 객체 생성 방식 추상화 | 싱글톤, 팩토리메서드, 추상팩토리, 빌더, 프로토타입 |
| 구조 패턴 (7종) | 클래스·객체 구성으로 더 큰 구조 형성 | 어댑터, 브리지, 컴포지트, 데코레이터, 퍼사드, 플라이웨이트, 프록시 |
| 행위 패턴 (11종) | 객체 간 책임 분배와 알고리즘 캡슐화 | 책임연쇄, 커맨드, 인터프리터, 이터레이터, 미디에이터, 메멘토, 옵서버, 스테이트, 전략, 템플릿메서드, 비지터 |
기출에서는 "다음 중 생성 패턴이 아닌 것은?" 또는 "다음 패턴이 속하는 분류는?" 형식으로 출제됩니다. 생성 5종(싱팩추빌프), 구조 7종(어브컴데퍼플프), 행위 11종을 분류별로 암기하세요.
🔧 주요 패턴 상세 — 생성 패턴
이 섹션에서는 기출 빈출 생성 패턴의 목적과 구조를 살펴보겠습니다.
싱글톤(Singleton): 클래스의 인스턴스가 오직 하나만 생성되도록 보장하고, 어디서든 그 인스턴스에 접근할 수 있는 전역 접근점을 제공합니다. 생성자를 private으로 막고, static 메서드(getInstance())로만 인스턴스를 반환합니다. 데이터베이스 연결 풀, 로그 매니저, 설정 객체에 사용합니다. 단점은 테스트 어려움(전역 상태), 멀티스레드 환경에서의 동기화 문제입니다.
팩토리 메서드(Factory Method): 객체 생성을 서브클래스에서 결정하도록 위임합니다. 상위 클래스는 인터페이스를 정의하고, 실제 어떤 클래스의 객체를 생성할지는 하위 클래스가 결정합니다. "객체 생성 로직을 캡슐화"하는 것이 핵심입니다.
추상 팩토리(Abstract Factory): 관련된 객체들의 집합을 생성하는 인터페이스를 제공합니다. 팩토리 메서드가 단일 객체 생성이라면, 추상 팩토리는 서로 연관된 객체 군(Family)을 생성합니다. GUI 테마를 예로 들면, Dark 테마 팩토리는 DarkButton, DarkTextField, DarkScrollbar를 함께 생성하고, Light 테마 팩토리는 Light 계열 컴포넌트들을 함께 생성합니다.
빌더(Builder): 복잡한 객체의 생성 과정을 단계적으로 분리합니다. 같은 생성 프로세스로 서로 다른 표현(객체)을 만들 수 있습니다. 생성자의 매개변수가 너무 많을 때 가독성 있는 객체 생성을 위해 사용합니다. 예: new Pizza.Builder().size("large").cheese(true).pepperoni(true).build().
🔌 주요 패턴 상세 — 구조·행위 패턴
이 섹션에서는 기출 빈출 구조 패턴과 행위 패턴의 목적과 구조를 살펴보겠습니다.
어댑터(Adapter): 호환되지 않는 인터페이스를 가진 클래스들이 함께 동작할 수 있도록 변환해 줍니다. 220V 콘센트를 110V 기기에 연결할 때 어댑터를 쓰는 것과 같습니다. 레거시 코드와 신규 코드를 연결할 때 유용합니다. 기존 클래스를 수정하지 않고 새로운 인터페이스에 맞게 래핑합니다.
데코레이터(Decorator): 객체에 동적으로 새로운 기능을 추가합니다. 상속 대신 합성을 통해 기능을 확장하므로, 런타임에 유연하게 기능을 조합할 수 있습니다. Java의 InputStream → BufferedInputStream → DataInputStream처럼 감싸는 구조입니다. "래퍼(Wrapper)"라고도 부릅니다.
퍼사드(Facade): 복잡한 서브시스템에 단순한 인터페이스(창구)를 제공합니다. 클라이언트가 복잡한 내부 구조를 알 필요 없이 단순한 인터페이스만으로 기능을 사용할 수 있게 합니다. 홈시어터 시스템을 예로 들면, DVD플레이어·앰프·스크린을 각각 제어하는 복잡한 과정을 HomeTheaterFacade.watchMovie() 하나로 단순화합니다.
프록시(Proxy): 다른 객체에 대한 대리자·대변인 역할을 합니다. 원본 객체에 대한 접근을 제어하거나, 부가 기능(캐싱·로깅·접근 제어·지연 초기화)을 추가합니다. 가상 프록시(큰 이미지의 지연 로딩), 보호 프록시(접근 권한 체크), 캐싱 프록시(결과 캐시)로 활용됩니다.
옵서버(Observer): 한 객체의 상태 변화를 다수의 다른 객체에 자동으로 통지합니다. 출판-구독(Pub-Sub) 패턴이라고도 합니다. 주제(Subject)가 상태를 변경하면 등록된 모든 옵서버(Observer)에게 알림을 보냅니다. 이벤트 처리 시스템, MVC의 Model-View 동기화, 뉴스레터 구독 등에 활용됩니다.
전략(Strategy): 알고리즘을 캡슐화하고 상호 교환 가능하게 만듭니다. 클라이언트가 알고리즘의 구현 세부사항 없이 알고리즘을 선택하고 교체할 수 있습니다. 정렬 알고리즘(버블소트·퀵소트·머지소트)을 Strategy로 추상화하면, 정렬이 필요한 코드 변경 없이 알고리즘만 교체할 수 있습니다.
커맨드(Command): 요청을 객체로 캡슐화합니다. 실행·취소(Undo)·재실행(Redo)·큐잉·로깅이 가능해집니다. 리모컨 버튼을 예로 들면, 각 버튼은 커맨드 객체를 가지며 버튼 누름=execute(), 되돌리기=undo()가 됩니다. 텍스트 편집기의 Ctrl+Z(실행 취소)가 대표적인 커맨드 패턴 활용입니다.
템플릿 메서드(Template Method): 알고리즘의 골격을 상위 클래스에 정의하고, 일부 단계의 구현을 하위 클래스에 위임합니다. "알고리즘 구조는 고정, 세부 구현은 교체" 가능합니다. 예를 들어 커피·차 제조 과정(끓이기→넣기→따르기→첨가물추가)에서 "넣기"와 "첨가물추가" 단계만 하위 클래스(Coffee, Tea)가 다르게 구현합니다.
📝 핵심 요약
4강에서 배운 내용을 정리해 보겠습니다.
| SOLID | 원칙명 | 핵심 키워드 |
|---|---|---|
| S | 단일 책임 | 변경 이유 하나, 책임 하나 |
| O | 개방-폐쇄 | 확장 열림, 수정 닫힘 |
| L | 리스코프 치환 | 자식이 부모를 대체 가능 |
| I | 인터페이스 분리 | 사용 안 하는 메서드 의존 금지 |
| D | 의존성 역전 | 구현이 아닌 추상화에 의존 |
기출 빈출 패턴 암기법: 생성(싱팩추빌프) — 싱글톤·팩토리메서드·추상팩토리·빌더·프로토타입. 구조(어브컴데퍼플프) — 어댑터·브리지·컴포지트·데코레이터·퍼사드·플라이웨이트·프록시.
다음 강인 5강 — 인터페이스와 UI 설계에서는 REST API 6원칙, SOAP과의 비교, UI 설계의 핵심 원칙과 프로토타입 유형을 완전히 정복합니다!
관련 주제
- SOLID 5원칙
- 단일 책임 원칙
- GoF 디자인 패턴 23종
- 싱글톤 팩토리메서드 패턴
- 어댑터 데코레이터 패턴
- 옵서버 전략 패턴
- 자격증
- 자격증 강의
- 정보처리기사 필기 25강 — 핵심이론·기출 완전정복
- 무료강의
- 무료 온라인 강의
- NUGUNA
- 누구나
📚 시리즈 전체 공유
정보처리기사 필기 25강 — 핵심이론·기출 완전정복
이 강의가 속한 시리즈는 총 13강, 모두 무료입니다. 처음부터 배우려는 동료에게 시리즈 전체를 알려 주세요.
댓글
불러오는 중...
