[TIL] 디자인 패턴
디자인 패턴, SOLID, KISS, DRY
디자인 패턴이란?
소프트웨어 개발에서 반복적으로 나타나는 문제에 대한 일반적 해결책
SOLID 원칙
- S(단일 책임 원칙): 클래스는 한 가지 책임만 가진다. 한 클래스가 많은 일을 하지 않는다.
- O(개방-폐쇄 원칙): 확장에는 열려 있고, 수정에는 닫혀있어야한다. = 기능을 추가할 때 기존 코드를 고치지 않고 코드 수정만 할 수 있다.
- L(리스코프 치환 원칙): 서브 클래스는 기본 클래스가 쓰이는 자리에서 문제없이 대체될 수 있어야한다. (인터페이스로 분리해서 기능 쪼개자)
- I(인터페이스 분리 원칙): 거대한 인터페이스 하나보다 작고 집중된 인터페이스 여러개가 좋다
- D(종속성 역전 원칙): 상위 모듈이 하위 모듈을 직접 가져오면 안된다. (양쪽 모두 추상화에 의존해야함)
KISS 원칙
KISS = Keep It Simple, Stupid
복잡도는 필요할 때만 높이라는 원칙
- 패턴은 필요할 때만 사용
- 패턴이 맞는지 확신이 안서면 자연스럽게 기다려보자
- 빠르고 쉬운 것이 곧 단순한 결과로 이어지진 않는다
DRY 원칙
DRY = Don't Repeat Yourself
반복하지 마라
- 같은 로직이 여러 곳에 중복되면, 한곳만 고쳐도 다른 곳이 깨질 위험이 있다
- 중복을 하나로 모으면 유지보수가 쉬워진다
- KISS 와 마찬가지로 과한 추상화는 오히려 복잡도를 키운다
10개 패턴의 흐름:
| 그룹 | 패턴 | 무엇을 해결하나 |
|---|---|---|
| 생성 | 팩토리 → 오브젝트 풀 → 싱글톤 | 객체를 어떻게 만들고/재사용하고/공유할까 |
| 행동 | 커맨드 → 상태 → 옵저버 | 객체의 행동·상태·소통을 어떻게 구조화할까 |
| 아키텍처 | MVP | UI와 데이터를 어떻게 분리할까 |
| 최적화·기타 | 전략 → 플라이웨이트 → 더티 플래그 | 알고리즘 교체, 메모리 절약, 불필요한 연산 줄이기 |
팩토리 패턴
게임 중 적, 아이템, 발사체, 장애물 등 다양한 생성 로직이 필요한 것들을 if/switch로 직접 관리하면 종류가 늘 때마다 분기가 늘어나고 코드가 비대해진다. 팩토리 패턴은 "공장"이라는 특수 객체에 제품 생성의 세부 사항을 맡긴다.
핵심 개념
- 제품(Product): 공장이 만드는 대상. 인터페이스 IProduct를 따른다
- 공장(Factory): 제품을 생성. 공통 기능이 필요하면 추상 클래스로 둔다
- 구체 공장(ConcreteFactory): 특정 제품을 실제로 만드는 공장. 공장을 서브 클래싱해 여러 개 생성
실전 활용 사례
- 적, 장애물 등 다양한 게임 플레이 요소의 런타임 생성
- 다이얼로그 상자의 UI 요소 구성 같은 더 큰 작업의 일부
팩토리 패턴 = 객체 생성을 공장에 캡슐화해 분기 코드 축소
오브젝트 풀 패턴
쓰는 이유
- 총알, 파티클, 적 처럼 짧은 수명의 객체를 대량 생성, 파괴하면 CPU 부담이 발생한다
- 메모리 할당/해제가 잦으면 순간적으로 끊기는 가비지 컬렉션 스파이크가 발생한다
오브젝트 풀은 객체를 미리 만들어 두고 재사용 한다. 필요하면 풀에서 꺼내 활성화하고 다 쓰면 파괴 대신 비활성화하여 풀로 반환한다
핵심 개념
- ObjectPool: 재사용할 객체들의 컬렉션을 보관한다
- PooledObject: 풀링되는 각 객체에 붙는 작은 컴포넌트. 자신이 속한 풀을 참조해 반환을 돕는다
- 흐름: 미리 채우기 -> 꺼내 쓰기 -> 돌려주기
- 재사용 전에 Rigidbody나 NavMesh Agent 같은 것들 초기화하는 것이 중요하다
실전 활용 사례
- 탄막 슈팅의 총알, 발사체
- 폭발, 타격, 파티클, 데미지 텍스트
- 적 스폰
싱글톤 패턴
- 전체 씬에서 행동을 조정하는 객체가 정확히 하나만 필요할때(다양한 Manager 스크립트들)
- 싱글톤은 불필요한 전역 상태 및 종속성을 유발할 수 있으니 조심해서 사용해야한다
핵심 개념
- 클래스가 자신의 인스턴스 하나만 만들도록 보장
- 하나의 인스턴스에 손쉬운 글로벌 액세스 제공
실전 활용 사례
- 게임 플로우 관리자, 오디오 관리자, 저장 관리자
- 오브젝트 풀 패턴을 싱글톤으로 만들면 풀링된 객체를 어디서든 쉽게 가져올 수 있다
커맨드 패턴
- 실행 취소/다시 실행, 입력 내역 기록, 턴제 게임의 사전 계획 등은 일련의 행동을 추적해야 함
- 메서드를 직접 호출하면 그 호출을 나중에 되돌리거나 재생하기 어려움
- 하나 이상의 메서드 호출을 "커맨드 객체"로 캡슐화 -> 컬렉션에 담아 실행 타이밍을 제어
핵심 개념
- 커맨드 객체(ICommand): 수행할 작업(Execute)과 되돌리는 방법(Undo)을 담음
- 커맨드 호출자(CommandInvoker): 커맨드를 실행하고, 실행취소 스택으로 추적
- 호출자는 커맨드의 내부를 모름 -> Execute/Undo만 호출
- Execute/Undo가 서로 정확히 짝을 이룬다(
커맨드 패턴의 핵심!!!!)
실전 활용 사례
- 실행취소/다시실행: 스택 하나만 더 두면 다시실행 추가. 커맨드 취소 시 redo 스택으로 push, 새 이동 시 redo 스택 비우기
- 콤보/연계기: 격투 게임에서 버튼 순서로 콤보를 트리거. 커맨드로 행동을 저장하면 콤보 설정이 간편
- 리플레이: 커맨드 버퍼를 순서대로/역순으로 재생



