로그인게임하기
Unity C# 강좌

[Unity C# 1] 수업 하나가 클래스 하나, 배열에서 List 까지

2026.10.14

Unity C# 강좌 · 1편

이 강좌는 2024년 부산 콘텐츠 아카데미 퍼스널 트레이닝에서 강의한 원고로, Unity 실습 프로젝트 하나로 C# 기초 문법부터 게임 구조까지 배웁니다. 수업마다 클래스 하나를 만들고, 그 클래스만 갈아 끼우면 같은 화면에서 다른 수업이 실행됩니다. 첫 편에서는 이 실습 프로젝트의 틀을 보고, 이름 목록을 담는 그릇을 배열에서 List 까지 바꿔 봅니다.

수업 정보

2024년 부산 콘텐츠 아카데미 퍼스널 트레이닝 수업에서 MyLesson Unity 실습 프로젝트의 Strategy_01_Array.cs 로 가르친 내용입니다. 수업은 다섯 단계로 나뉘고, 단계마다 코드가 조금씩 바뀝니다.

PROJECT

수업 하나가 클래스 하나

실습 프로젝트의 씬에는 Program 오브젝트가 하나 있습니다. Inspector 의 classPath 칸에 Strategy_01_Array 처럼 수업 클래스 이름을 적고 실행하면, 그 수업의 코드가 돌아갑니다. 수업 클래스는 모두 Strategy 라는 약속(인터페이스)을 지킵니다.

Strategy.csAssets/Source/Strategy.cs1–5줄모든 수업이 지키는 약속
public interface Strategy
{
    public void Run();
    public void Update();
}

약속은 두 가지입니다. 처음 한 번 부르는 Run() 과 매 프레임 부르는 Update() 입니다. 이 약속만 지키면 StrategyRunner 는 어떤 수업이든 똑같이 실행합니다.

수업 프로젝트 구조: Program 이 수업 클래스를 골라 StrategyRunner 에 끼운다ProgramMonoBehaviour · classPathStrategyRunnerRun() · Update()«interface» StrategyRun() · Update()가짐실행Strategy_HelloStrategy_01_ArrayStrategy_02_GenericStrategy_05_Delegate… Strategy_15_QueryInspector 의 classPath 에 적은 이름으로 수업 클래스를 만들어(Activator.CreateInstance) Runner 에 끼운다Start() 에서이름으로 찾기
Program 이 classPath 의 이름으로 수업 클래스를 만들어 StrategyRunner 에 끼웁니다.

이렇게 실행할 대상을 같은 모양의 클래스로 만들어 바꿔 끼우는 구조를 전략 패턴(Strategy Pattern)이라고 부릅니다. 패턴 이름은 나중에 다시 나오니, 지금은 "갈아 끼우는 구조" 정도로 기억해 두면 됩니다.

REFLECTION

이름으로 클래스를 만드는 방법

Program 의 Start() 는 classPath 에 적힌 문자열로 클래스를 찾아 실행합니다. 이름 앞에 Strategy_ 를 빼먹었으면 붙여 줍니다.

Program.csAssets/Source/Program.cs26–37줄
private void Start()
{
    runner = new StrategyRunner();
    var findedStrategy = CreateStrategyFromClassName
        (classPath.Contains("strategy_", StringComparison.OrdinalIgnoreCase) ? classPath : "Strategy_" + classPath);

    if (findedStrategy != null)
    {
        runner.SetStrategy(findedStrategy);
        runner.Run();
    }
}
Program.csAssets/Source/Program.cs94–110줄
private Strategy CreateStrategyFromClassName(string className)
{
    Type type = Type.GetType(className);

    if (type != null)
    {
        object instance = Activator.CreateInstance(type);

        if (instance is Strategy myClassInstance)
        {
            return (Strategy)instance;
        }
    }

    Program.WriteLine("Type not found.");
    return null;
}
코드하는 일
Type.GetType(className)문자열 이름으로 타입(클래스 정보)을 찾습니다. 없으면 null 입니다.
Activator.CreateInstance(type)그 타입의 객체를 만듭니다. new 를 문자열로 하는 셈입니다.
instance is Strategy만든 객체가 Strategy 약속을 지키는지 확인합니다.

코드를 고치지 않고 Inspector 의 글자만 바꿔 수업을 고를 수 있는 이유가 여기에 있습니다. 첫 수업은 화면에 한 줄을 찍는 Strategy_Hello 입니다.

Strategy_Hello.csAssets/Source/Strategy_Hello.cs1–13줄
public class Strategy_Hello : Strategy
{
    public void Run()
    {
        // CTRL + R > G
        Program.WriteLine("Hello World!");
    }

    public void Update()
    {

    }
}
Program.WriteLine

Unity 의 Console 창 대신 게임 화면의 글 상자에 결과를 찍는 도우미입니다. 수업이 진행되면서 목록을 찍는 WriteList, 경로로 오브젝트를 찾는 FindObject 같은 도우미가 하나씩 늘어납니다.

STEPS

단계는 컴파일 기호로 나눈다

수업 하나는 여러 단계로 진행됩니다. 단계마다 파일을 따로 만들지 않고, 한 파일 안에 #if 로 단계를 나눠 둡니다. Unity 의 Project Settings → Player → Scripting Define Symbols 에 ARRAY_STEP2 같은 기호를 더하면 그 단계의 코드가 컴파일됩니다.

  1. ARRAY_STEP2 없음1단계 코드
  2. ARRAY_STEP2 추가2단계 코드
  3. ARRAY_STEP3 추가3단계 코드
  4. … STEP5마지막 단계

기호는 쌓아 가며 켭니다. 3단계를 보려면 STEP2 와 STEP3 를 함께 켭니다. 그래서 코드 안의 #if 가 양파처럼 겹쳐 있습니다.

STEP 1 · 2

배열로 이름 목록 만들기

첫 단계에서는 게임 캐릭터 이름 네 개를 문자열 배열에 담아 찍습니다.

Strategy_01_Array.csAssets/Source/Strategy_01_Array.cs42–60줄#if 줄 생략
static string[] GenerateNames()
{
    string[] names = new string[4];
    names[0] = "Periona";
    names[1] = "Leon";
    names[2] = "Heloon";
    names[3] = "Darkan";
    return names;
}

static void PrintNames(string[] names)
{
    foreach (string name in names)
    {
        Program.WriteLine("PrintNames: " + name);
    }
}

new string[4] 는 칸이 네 개인 배열을 만듭니다. 배열은 만들 때 크기가 정해지고, 나중에 늘릴 수 없습니다. 그리고 PrintNames 는 string[] 만 받습니다. 이름이 아니라 숫자 목록을 찍고 싶으면 함수를 또 만들어야 합니다.

2단계에서는 찍는 일을 Program 의 도우미로 옮기면서, 어떤 타입의 배열이든 받도록 바꿉니다.

Program.csAssets/Source/Program.cs139–147줄#if 줄 생략
public static void WriteList<T>(T[] array)
{
    foreach (T name in array)
    {
        WriteLine(name.ToString());
    }
}

WriteList<T> 의 T 는 "아직 정하지 않은 타입" 입니다. 부르는 쪽이 string[] 을 넘기면 T 는 string 이 되고, int[] 를 넘기면 int 가 됩니다. 이것이 다음 편에서 자세히 볼 제네릭입니다.

STEP 3 · 4 · 5

그릇을 바꿔도 받는 쪽은 그대로

3단계부터는 크기가 자동으로 늘어나는 그릇을 차례로 써 봅니다.

Strategy_01_Array.csAssets/Source/Strategy_01_Array.cs31–39줄3단계 ArrayList
static ArrayList GenerateNames()
{
    ArrayList names = new ArrayList();
    names.Add("S3: Periona");
    names.Add("S3: Leon");
    names.Add("S3: Heloon");
    names.Add("S3: Darkan");
    return names;
}
Strategy_01_Array.csAssets/Source/Strategy_01_Array.cs20–28줄4단계 StringCollection
static StringCollection GenerateNames()
{
    StringCollection names = new StringCollection();
    names.Add("S4: Periona");
    names.Add("S4: Leon");
    names.Add("S4: Heloon");
    names.Add("S4: Darkan");
    return names;
}
Strategy_01_Array.csAssets/Source/Strategy_01_Array.cs10–18줄5단계 List<string>
static List<string> GenerateNames()
{
    List<string> names = new List<string>();
    names.Add("S5: Periona");
    names.Add("S5: Leon");
    names.Add("S5: Heloon");
    names.Add("S5: Darkan");
    return names;
}
그릇크기담는 것꺼낼 때
string[]고정string 만그대로 string
ArrayList자동으로 늘어남무엇이든(object)형 변환이 필요하고, 틀린 타입이 섞여도 컴파일러가 모름
StringCollection자동으로 늘어남string 만그대로 string, 하지만 string 전용 클래스
List<string>자동으로 늘어남string 만그대로 string. 다른 타입은 List<int> 처럼 쓰면 됨

그릇이 네 번 바뀌는 동안 Run() 은 거의 바뀌지 않습니다. 비결은 받는 변수의 타입입니다.

Strategy_01_Array.cs3~5단계에서 컴파일되는 Run() (63–78줄)
public void Run()
{
    IEnumerable names = GenerateNames();
    Program.WriteList(names);
}

배열, ArrayList, StringCollection, List 는 모두 IEnumerable 을 구현합니다. "foreach 로 하나씩 꺼낼 수 있다" 는 약속입니다. 받는 쪽이 이 약속 하나만 요구하면, 그릇의 종류가 바뀌어도 코드를 고칠 필요가 없습니다.

Program.csAssets/Source/Program.cs149–157줄#if 줄 생략
public static void WriteList<T>(T array) where T : IEnumerable
{
    foreach (var name in array)
    {
        WriteLine(name.ToString());
    }
}
배열에서 List<T> 까지, 이름 목록을 담는 네 가지 그릇string[]크기 고정 · 형 안전1 · 2단계ArrayList크기 자동 · object 저장3단계StringCollection크기 자동 · string 전용4단계List<string>크기 자동 · 형 안전 (제네릭)5단계넷 모두 IEnumerable 을 구현한다그래서 받는 쪽은 IEnumerable 하나로 받고, foreach 로 돌면 된다Program.WriteList<T>(T array) where T : IEnumerable
네 가지 그릇은 모두 IEnumerable 이라, 받는 쪽은 IEnumerable 하나로 받습니다.
요즘은 List<T>

ArrayList 와 StringCollection 은 제네릭이 나오기 전의 그릇입니다. 새 코드에서는 크기가 정해진 목록이면 배열, 늘어나는 목록이면 List<T> 를 씁니다. 수업에서 옛 그릇을 거쳐 가는 이유는 List 가 무엇을 해결했는지 보기 위해서입니다.

RETRO

돌아보며

이번 편에서 얻은 것

  • 수업을 갈아 끼우는 구조: 같은 약속(Strategy)을 지키는 클래스는 서로 바꿔 끼울 수 있다
  • 이름 문자열로 클래스를 만드는 리플렉션(Type.GetType, Activator)
  • 받는 쪽이 IEnumerable 만 요구하면 그릇이 바뀌어도 코드가 그대로다

다음에 풀 질문

  • WriteList<T> 의 T 는 정확히 무엇이고, where 는 무엇을 막는가
  • ArrayList 에 int 를 넣으면 object 로 바뀌는데, 그때 무슨 일이 일어나는가(박싱)

정리

  1. 약속이 같으면 바꿔 끼울 수 있습니다. 수업 클래스는 모두 Strategy 의 Run · Update 를 지키고, Program 은 classPath 의 이름으로 수업을 고릅니다.
  2. 단계는 #if 기호로 나눕니다. Scripting Define Symbols 에 ARRAY_STEP2 같은 기호를 쌓아 켜면 그 단계의 코드가 컴파일됩니다.
  3. 그릇보다 약속에 기대세요. 배열에서 List 까지 그릇이 바뀌어도, IEnumerable 로 받는 코드는 고칠 필요가 없습니다.

다음 편에서는 WriteList<T> 에 나온 제네릭과, 타입을 값처럼 다루는 typeof 를 봅니다.

← 개발 노트 목록