반응형

✅ 어댑터 패턴이란?

서로 다른 인터페이스를 가진 두 클래스가 함께 동작하도록 중간에 "변환기" 역할을 해주는 패턴

📌 한 줄 요약:
"호환되지 않는 인터페이스를 연결해주는 중간 어댑터 클래스"


✅ 언제 쓰일까?

  • 기존 코드 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 인스펙터 설정

  1. Player 오브젝트에 Player.cs 컴포넌트 추가
  2. inputSource 슬롯에 아래 중 하나를 드래그:
    • KeyboardInputHandler 컴포넌트 붙인 오브젝트
    • LegacyInputAdapter 컴포넌트 붙인 오브젝트

➡️ 어떤 걸 연결해도 작동 ✅


✅ 결과

연결된 컴포넌트 결과
KeyboardInputHandler 키보드 스페이스 입력 처리
LegacyInputAdapter 터치 입력 처리 (외부 시스템)
 

✅ 요약 구조

[Player] → IInputHandler ← [KeyboardInputHandler]
                          ← [LegacyInputAdapter → LegacyInput]
  • LegacyInput 은 우리가 바꿀 수 없는 외부 시스템
  • LegacyInputAdapter 가 어댑터 역할
  • Player 입장에선 어떤 입력 시스템이든 같은 방식으로 처리함

이렇게 예제 코드로 예시를 들어봤는데요 흠.. 사실 저도 아직까지 막 와닿진 않네요 ㅠㅜ

다음에 유니티로 직접 개발해봐야겠습니다! 사실 직접해보는게 제일 빠르고 이해가 좋은거같아요!

반응형