인터페이스

여러 종류를 같은 코드로 다루는 방법, 그리고 클래스용과 컴포넌트용의 구분.

업데이트 2026-09-07

인터페이스는 "무엇인가"가 아니라 "무엇을 할 수 있는가"에 대한 약속입니다. 약속을 향해 코드를 쓰면, 그 약속을 지키는 모든 것에서 동작합니다.

public interface IShape
{
    int Area();
}

public class Square : IShape
{
    public int side;

    public int Area()
    {
        return side * side;
    }
}

public class Rect : IShape
{
    public int width;
    public int height;

    public int Area()
    {
        return width * height;
    }
}

IShape를 들고 있는 쪽은 그것이 어떤 종류인지 모른 채 넓이를 물을 수 있고, 답은 실제로 거기 있는 클래스에서 나옵니다.

IShape shape = square ? (IShape)new Square() : new Rect();

int area = shape.Area();

이 선택은 월드가 돌아가는 도중에 할 수 있습니다. 이것이 일상적인 쓰임이고, 인터페이스가 값을 하는 자리입니다. 코드 하나로 여러 종류를 다룹니다.

효과가 나는 곳

타입을 나타내는 필드에 대고 긴 if를 쓰게 될 것 같은 모든 곳입니다.

  • 인벤토리 아이템. 사용했을 때 하는 일이 저마다 다름
  • 퀘스트 단계. 완료 판정이 저마다 다름
  • 플레이어에게 걸리는 효과. 규칙이 저마다 다름
  • 무기. 발사한다는 것의 의미가 무기마다 다름

인터페이스로 써 두면 새로운 종류의 아이템을 더하는 일은 클래스 하나를 더하는 일입니다. IItem을 쓰는 쪽은 아무것도 바뀌지 않습니다.

컴포넌트는 다릅니다: 베이스 클래스를 쓰세요

인터페이스는 코드에서 직접 만드는 클래스를 위한 것입니다. 컴포넌트는 사정이 다릅니다. GameObject에 끌어다 놓고 Unity가 그 참조를 저장해야 하는데, Unity는 인터페이스 필드를 저장하지 못하기 때문입니다. 인터페이스 타입으로 두면 그 필드는 인스펙터에 아예 나타나지 않습니다.

그래서 공유하려는 대상이 behaviour라면 추상 베이스 클래스를 쓰세요.

public abstract class Door : UdoniteBehaviour
{
    public abstract void Open();
}

public class SlidingDoor : Door
{
    public override void Open() { /* ... */ }
}

public class SwingingDoor : Door
{
    public override void Open() { /* ... */ }
}

이제 스위치는 필드 하나로 두 종류 모두를 다룹니다.

public class Switch : UdoniteBehaviour
{
    public Door door;      // SlidingDoor든 SwingingDoor든 여기에 끌어다 놓으세요

    public override void Interact()
    {
        door.Open();
    }
}

필드는 인스펙터에 나타나고, 두 문 모두 받아들이며, door.Open()은 맞는 쪽을 실행합니다. 베이스 클래스는 공유 상태와 공유 코드도 가질 수 있습니다. 인터페이스는 못 하는 일입니다.

이것은 평범한 Unity에서도 같은 이유로 같은 방식으로 하는 이야기입니다.

무엇을 쓸까

하고 싶은 것 쓸 것
코드에서 만드는 여러 클래스가 같은 약속을 갖게 하기 인터페이스
GameObject 위의 여러 behaviour가 같은 약속을 갖게 하기 추상 베이스 클래스
상태나 메서드 본문을 공유하기 추상 베이스 클래스

베이스 클래스가 인터페이스를 구현할 수도 있으므로, 정말 필요할 때는 behaviour가 둘 다 할 수 있습니다.

문제가 발생했습니다 새로 고침