"디자인 패턴(Design Pattern)"
개발을 하다 보면 비슷한 문제가 계속 반복된다.
예를 들어 “객체 생성이 복잡해졌네”, “알고리즘을 바꿔야 하네”, “상태에 따라 동작이 달라지네” 같은 상황들...
디자인 패턴(Design Pattern)은 이런 반복 문제를 해결하기 위해 검증된 해결방안을 정리해둔 것이다.
오늘은 자주 쓰이는 패턴 네가지
「싱글톤 패턴, 팩토리 패턴, 상태 패턴, 전략 패턴」
에 대해 알아볼 예정이다.
----------------------------------------------------------------------------------------------------------------------------------------------------------------
디자인 패턴을 왜 쓰는가?
1) 유지보수/확장성 향상
기능이 늘어나도 기존 코드를 크게 흔들지 않도록 구조를 잡아준다.
2) 재사용성과 테스트 용이성
구현을 갈아끼우기 쉬워지고(특히 전략/팩토리), 테스트 더블(목 객체)도 넣기 쉬워진다.
3) 팀 커뮤니케이션 비용 감소
“이거 전략 패턴으로 갈까요?”처럼 공통 언어가 생긴다.
----------------------------------------------------------------------------------------------------------------------------------------------------------------
1) 싱글톤 패턴(Singleton)
하나의 클래스에 하나의 인스턴스만 가지는 패턴.
장점
전역 인스턴스 공유
(한 번만 생성해서 사용가능 – 메모리 낭비 방지)
인스턴스 관리 편의
(어디서나 동일 객체를 관리)
단점
의존성이 높아진다.
(싱글톤 인스턴스 변경시 참조된 모든 클래스를 수정)
단위 테스트에 문제가 된다.
(미리 생성된 하나의 인스턴스를 기반으로 구현하므로 각 테스트마다 독립적인 인스턴스를 만들기 어려움)
코드 예시(Unity, GameManager)
using UnityEngine;
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
public int Score { get; private set; }
private void Awake()
{
// 이미 존재하면 새로 생성된 자신은 제거
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject); // 씬 전환에도 유지
}
public void AddScore(int amount)
{
Score += amount;
Debug.Log($"Score: {Score}");
}
}
GameManager.Instance.AddScore(10);

----------------------------------------------------------------------------------------------------------------------------------------------------------------
2) 팩토리 패턴(Factory)
객체 생성 로직을 “사용자 코드(Client)”에서 분리하고, Factory가 생성 책임을 전담하도록 만드는 패턴이다..
장점
더 많은 유연성
(새로운 객체를 추가/변경할 때 기존 코드를 변경하지 않아도 되므로 확장성이 좋음)
유지보수성 증가
(객체 생성 부분이 따로 있어서 코드 리팩토링시 한 곳만 고치면 된다)
재사용성 향상
(동일한 생성 패턴은 여러 곳에서 재사용 할 수 있다)
단점
코드 수 증가(복잡성 증가)
(새로운 객체 타입마다 새로운 클래스가 필요하기 떄문에 코드 구조가 복잡해지거나 비대해 질 수 있다.)

코드 예시(Unity, EnemyFactory.cs)
using UnityEngine;
public enum EnemyType
{
Slime,
Goblin
}
public class EnemyFactory : MonoBehaviour
{
[SerializeField] private GameObject slimePrefab;
[SerializeField] private GameObject goblinPrefab;
public GameObject Create(EnemyType type, Vector3 position)
{
GameObject prefab = type switch
{
EnemyType.Slime => slimePrefab,
EnemyType.Goblin => goblinPrefab,
_ => slimePrefab
};
return Instantiate(prefab, position, Quaternion.identity);
}
}
코드 예시(Unity ,Spawner.cs[Factory 사용])
using UnityEngine;
public class Spawner : MonoBehaviour
{
[SerializeField] private EnemyFactory factory;
private void Start()
{
factory.Create(EnemyType.Slime, new Vector3(0, 0, 0));
factory.Create(EnemyType.Goblin, new Vector3(2, 0, 0));
}
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------
3) 전략 패턴(Strategy)
하나의 기능에 여러가지 전략(캡슐화한 알고리즘)이 필요할 때,
각 전략을 별도 클래스로 캡슐화해 교체 가능하게 설계하는 패턴

코드 예시(이동 알고리즘 교체[IMoveStrategy])
(이동 방식을 캡슐화해서, 상황에 따라 교체 가능하도록 설계)
using UnityEngine;
public interface IMoveStrategy
{
Vector3 GetMoveDirection(Transform owner);
}
IMoveStrategy.cs
using UnityEngine;
public class KeyboardMoveStrategy : IMoveStrategy
{
public Vector3 GetMoveDirection(Transform owner)
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
return new Vector3(h, 0f, v).normalized;
}
}
public class ChaseMoveStrategy : IMoveStrategy
{
private readonly Transform target;
public ChaseMoveStrategy(Transform target)
{
this.target = target;
}
public Vector3 GetMoveDirection(Transform owner)
{
if (target == null) return Vector3.zero;
return (target.position - owner.position).normalized;
}
}
전략 구현 2개 (Keyboard / Chase)
using UnityEngine;
public class PlayerMover : MonoBehaviour
{
[SerializeField] private float speed = 3f;
[SerializeField] private Transform chaseTarget;
private IMoveStrategy moveStrategy;
private void Start()
{
// 초기 전략 선택
moveStrategy = new KeyboardMoveStrategy();
}
private void Update()
{
// 예시: T 키를 누르면 전략을 바꿔 끼움
if (Input.GetKeyDown(KeyCode.T))
{
moveStrategy = new ChaseMoveStrategy(chaseTarget);
}
if (Input.GetKeyDown(KeyCode.Y))
{
moveStrategy = new KeyboardMoveStrategy();
}
Vector3 dir = moveStrategy.GetMoveDirection(transform);
transform.position += dir * speed * Time.deltaTime;
}
}
PlayerMover.cs (교체 및 사용)
4) 상태 패턴(State)
객체가 여러 상태에 따라 동작을 달리해야 할 때 사용하는 패턴
주문 상태(대기→결제완료→배송→완료),
게임 캐릭터 상태(기본/공격/피격) 등
상태가 늘어나고 전이가 복잡해지는 시스템에 사용한다.

코드 예시(Unity, 플레이어 상태 전이[Idle/Run/Jump])
public interface IPlayerState
{
void Enter(PlayerStateMachine ctx);
void Tick(PlayerStateMachine ctx);
void Exit(PlayerStateMachine ctx);
}
IPlayerState.cs
using UnityEngine;
public class PlayerStateMachine : MonoBehaviour
{
public float moveSpeed = 3f;
public float jumpForce = 5f;
private IPlayerState current;
private void Start()
{
ChangeState(new IdleState());
}
private void Update()
{
current?.Tick(this);
}
public void ChangeState(IPlayerState next)
{
current?.Exit(this);
current = next;
current.Enter(this);
}
// 간단 입력 유틸
public Vector3 InputDir()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
return new Vector3(h, 0f, v).normalized;
}
public bool JumpPressed() => Input.GetKeyDown(KeyCode.Space);
}
PlayerStateMachine.cs(상태 전이 및 사용)
using UnityEngine;
public class IdleState : IPlayerState
{
public void Enter(PlayerStateMachine ctx) { }
public void Tick(PlayerStateMachine ctx)
{
if (ctx.JumpPressed())
{
ctx.ChangeState(new JumpState());
return;
}
if (ctx.InputDir() != Vector3.zero)
{
ctx.ChangeState(new RunState());
}
}
public void Exit(PlayerStateMachine ctx) { }
}
public class RunState : IPlayerState
{
public void Enter(PlayerStateMachine ctx) { }
public void Tick(PlayerStateMachine ctx)
{
Vector3 dir = ctx.InputDir();
ctx.transform.position += dir * ctx.moveSpeed * Time.deltaTime;
if (ctx.JumpPressed())
{
ctx.ChangeState(new JumpState());
return;
}
if (dir == Vector3.zero)
{
ctx.ChangeState(new IdleState());
}
}
public void Exit(PlayerStateMachine ctx) { }
}
public class JumpState : IPlayerState
{
private float timer;
public void Enter(PlayerStateMachine ctx)
{
timer = 0f;
// 실제 점프는 Rigidbody/AddForce 등을 써야 하지만, 예시는 단순화
Debug.Log("Jump!");
}
public void Tick(PlayerStateMachine ctx)
{
timer += Time.deltaTime;
// 점프 지속 시간 흉내(0.4초 후 착지했다고 가정)
if (timer >= 0.4f)
{
// 입력이 있으면 Run, 없으면 Idle로 전이
ctx.ChangeState(ctx.InputDir() == Vector3.zero ? new IdleState() : new RunState());
}
}
public void Exit(PlayerStateMachine ctx) { }
}
IdleState.cs(상태 구현)
----------------------------------------------------------------------------------------------------------------------------------------------------------------
오늘의 한 줄 요약
『 디자인 패턴은 개발하면서 흔히 발생하는 반복적인 문제들을 해결하기 위한
일반적이고 재사용이 가능한 해결 방안이다. 』
'1일 1 cs' 카테고리의 다른 글
| 6. Unity의 생명주기(Life Cycle)란? (0) | 2026.01.21 |
|---|---|
| 5. 동기/비동기 프로그래밍이란? (0) | 2026.01.19 |
| 4. 제네릭 이란? (+ 오브젝트 풀링) (0) | 2026.01.18 |
| 2. 설계 원칙(SOLID)이란? (0) | 2026.01.16 |
| 1. 객체지향 프로그래밍(OOP)이란? (0) | 2026.01.15 |
