✅ 어댑터 패턴이란?
서로 다른 인터페이스를 가진 두 클래스가 함께 동작하도록 중간에 "변환기" 역할을 해주는 패턴
📌 한 줄 요약:
"호환되지 않는 인터페이스를 연결해주는 중간 어댑터 클래스"
✅ 언제 쓰일까?
- 기존 코드 or 라이브러리 인터페이스를 바꾸지 않고 사용하고 싶을 때
- 인터페이스 불일치 때문에 직접 호출이 안 될 때
- 예전 코드와 새로운 코드를 자연스럽게 연결하고 싶을 때
✅ 예제 시나리오: 충전기 어댑터
- 한국 콘센트는 220V
- 미국 전자제품은 110V
어댑터를 끼우면 한국 콘센트에 미국 기기를 연결할 수 있어!
✅ C# 코드 예제
1️⃣ 기존 시스템 (한국 방식)
interface ITarget
{
void Request(); // 우리가 원하는 방식
}
class KoreanCharger : ITarget
{
public void Request()
{
Console.WriteLine("220V로 충전합니다.");
}
}
2️⃣ 호환되지 않는 외부 클래스 (미국 방식)
class AmericanDevice
{
public void ConnectWith110V()
{
Console.WriteLine("110V에 연결되었습니다.");
}
}
3️⃣ 어댑터 클래스
class Adapter : ITarget
{
private AmericanDevice _device;
public Adapter(AmericanDevice device)
{
_device = device;
}
public void Request()
{
// 220V 요청을 110V 방식으로 변환
_device.ConnectWith110V();
}
}
4️⃣ 사용 코드
ITarget charger1 = new KoreanCharger();
charger1.Request(); // ✅ 220V로 충전합니다.
ITarget charger2 = new Adapter(new AmericanDevice());
charger2.Request(); // ✅ 110V에 연결되었습니다.
✅ 구조 요약 (클래스 관계)
[ITarget] <─── [Adapter] ───> [AmericanDevice]
↑
[KoreanCharger] (직접 구현)
✅ 어댑터 패턴 종류
방식 | 설명 |
객체 어댑터 | 어댑터가 기존 객체를 포함해서 호출 (💡 C#에서 주로 사용) |
클래스 어댑터 | 어댑터가 상속을 통해 연결 (C#은 다중 상속 안 되므로 제한적) |
✅ 실제 사용 예 (Unity에서도 유용)
- Unity의 InputSystem이 바뀌었을 때, 기존 방식과 연결할 때
- 외부 API 라이브러리와 내부 구조 연결할 때
- 레거시 시스템 유지하면서 새 구조 연동할 때
✅ 요약
항목 | 설명 |
목적 | 서로 다른 인터페이스를 연결 |
키워드 | "변환기", "중간 연결자" |
구조 | 기존 인터페이스 + 어댑터 클래스 + 호환 불가 객체 |
실용성 | 외부 시스템 통합, 레거시 코드 연동 시 유용 |
❓
흠..이렇게 봐서는 어댑터 패턴이 얼마나 중요한지 아직은 잘 모르겠다 좀 더 자세한 예시 코드를 보자.
일단 어댑터를 사용하는 방법을 제대로 알아보죠!
✅ 1. namespace Adapter가 왜 등장할까?
🔸 namespace는 이름 충돌을 방지하고 코드를 구조적으로 정리하기 위한 C#의 기능이야.
예를 들어, 이런 상황 생각해봐:
// Game 안에 있는 Enemy
namespace Game
{
public class Enemy { }
}
// 외부에서 받아온 LegacyEnemy도 Enemy 클래스가 있음
namespace Legacy
{
public class Enemy { }
}
이런 경우 Enemy가 둘이라 충돌해.
그래서 Game.Enemy, Legacy.Enemy처럼 네임스페이스를 명시해줘야 돼.
✅ 2. 어댑터 쓸 때도 마찬가지
보통 어댑터 코드는 별도의 Adapter 네임스페이스 안에 넣어 정리해.
namespace Adapter
{
public class LegacyEnemyAdapter : MonoBehaviour, IEnemy
{
...
}
}
그리고 다른 코드에서 사용할 때는 이렇게 써:
using Adapter;
// 또는 명시적으로 접근
Adapter.LegacyEnemyAdapter adapter = new Adapter.LegacyEnemyAdapter();
✅ 3. 언제 Adapter.를 붙여야 할까?
상황 | 설명 |
using Adapter; 있음 | 그냥 LegacyEnemyAdapter 라고만 써도 됨 |
using Adapter; 없음 | Adapter.LegacyEnemyAdapter 이렇게 전체 경로로 써야 함 |
이름 충돌 있을 때 | 반드시 Adapter. 붙여서 명확하게 구분해야 함 |
✅ Unity에서 실제로는?
Unity에서는 대부분 클래스가 MonoBehaviour를 상속하고
스크립트를 오브젝트에 붙여서 인스펙터에서 연결하니까
Adapter.LegacyEnemyAdapter 라고 직접 쓰는 일은 드물어.
하지만 코드에서 직접 생성하거나, 네임스페이스 충돌이 있을 땐 명시적으로 써줘야 해:
IEnemy enemy = new Adapter.LegacyEnemyAdapter();
✅ 한 줄 요약
어댑터 클래스를 namespace Adapter 안에 넣으면
구조를 명확히 하고, 다른 클래스 이름과 충돌하지 않게 도와준다.
필요할 땐 Adapter.ClassName 으로 명시적으로 접근하면 된다.
✅
실제로 어댑터를 사용할때는 namespace를 사용하는듯합니다.
그리고 어댑터가 적용된거는 using으로만 작성해도 충분한듯해요!
다음 어댑터 제대로된 예제 코드를 보죠!
🎮 예제 시나리오:
우리 게임은 IInputHandler 라는 인터페이스로 입력을 처리하고 있음.
하지만 외부에서 제공된 LegacyInput 클래스는 우리가 쓰는 구조와 다름.
→ 어댑터를 만들어서 LegacyInput을 IInputHandler처럼 사용할 수 있도록 하자.
✅ 1. 인터페이스: 우리가 사용하는 입력 방식
// IInputHandler.cs
public interface IInputHandler
{
void HandleInput();
}
✅ 2. 정상적인 Unity 입력 구현 (키보드로 움직임)
// KeyboardInputHandler.cs
using UnityEngine;
public class KeyboardInputHandler : MonoBehaviour, IInputHandler
{
public void HandleInput()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.Log("스페이스바 눌림 (키보드)");
}
}
}
✅ 3. 외부에서 가져온 입력 시스템 (호환 안 됨)
// LegacyInput.cs
using UnityEngine;
public class LegacyInput
{
public bool IsTouched()
{
// 가상의 외부 입력 방식 (예: 터치스크린)
return Input.touchCount > 0;
}
}
✅ 4. 어댑터 클래스 만들기
// Adapter/LegacyInputAdapter.cs
using UnityEngine;
using AdapterNamespace;
namespace AdapterNamespace
{
public class LegacyInputAdapter : MonoBehaviour, IInputHandler
{
private LegacyInput _legacyInput;
private void Awake()
{
_legacyInput = new LegacyInput();
}
public void HandleInput()
{
if (_legacyInput.IsTouched())
{
Debug.Log("터치 입력 감지됨 (어댑터)");
}
}
}
}
✅ 5. 플레이어 컨트롤러 — 어댑터든 키보드든 상관 없이 처리 가능!
// Player.cs
using UnityEngine;
public class Player : MonoBehaviour
{
public MonoBehaviour inputSource;
private IInputHandler _inputHandler;
private void Start()
{
_inputHandler = inputSource as IInputHandler;
if (_inputHandler == null)
{
Debug.LogError("inputSource는 IInputHandler를 구현해야 합니다!");
}
}
private void Update()
{
_inputHandler?.HandleInput();
}
}
✅ Unity 인스펙터 설정
- Player 오브젝트에 Player.cs 컴포넌트 추가
- inputSource 슬롯에 아래 중 하나를 드래그:
- KeyboardInputHandler 컴포넌트 붙인 오브젝트
- LegacyInputAdapter 컴포넌트 붙인 오브젝트
➡️ 어떤 걸 연결해도 작동 ✅
✅ 결과
연결된 컴포넌트 | 결과 |
KeyboardInputHandler | 키보드 스페이스 입력 처리 |
LegacyInputAdapter | 터치 입력 처리 (외부 시스템) |
✅ 요약 구조
[Player] → IInputHandler ← [KeyboardInputHandler]
← [LegacyInputAdapter → LegacyInput]
- LegacyInput 은 우리가 바꿀 수 없는 외부 시스템
- LegacyInputAdapter 가 어댑터 역할
- Player 입장에선 어떤 입력 시스템이든 같은 방식으로 처리함
✅
이렇게 예제 코드로 예시를 들어봤는데요 흠.. 사실 저도 아직까지 막 와닿진 않네요 ㅠㅜ
다음에 유니티로 직접 개발해봐야겠습니다! 사실 직접해보는게 제일 빠르고 이해가 좋은거같아요!
'C#' 카테고리의 다른 글
C# - C#과 .NET의 시작, 개발 환경 구축과 Hello World (0) | 2025.07.07 |
---|---|
C# - Adpater Pattern2 C# 코드예제 (2) | 2025.07.06 |
C# - goto (0) | 2025.07.04 |
C# - Delegate(델리게이트) + Lambda operator(람다 연산자) + Lambda(람다식) (4) | 2025.07.04 |
C# - Queue(큐) vs Stack(스택) + 메모리구조 (2) | 2025.07.04 |