[C#] C#으로 공부하는 객체지향 프로그래밍 1 - 클래스란 무엇인가



1. 클래스란 무엇인가


객체지향의 가장 기초라고 볼 수 있는 클래스는 무엇이라고 정의할 수 있을까? 클래스에 대해서 설명하라고 한다면 항상 나오는 말이 있다. "예를 들어, 사람을 생각해보라..." 
그만큼 객체지향은 인간의 사고를 고려해서 설계한 방법이기 때문에 예를 들어 설명하는 방법이 가장 좋다고 볼 수 있다. 그렇다면 객체지향은 뭐고 클래스는 무엇인가? 여기서도 예전부터 많이 쓰던 예시를 들어서 설명해 보도록 하겠다.

사람을 예를 들어보자. 일단 모든 사람이 전부 같은 존재가 아니라는 것은 누구나 알고 있다. 즉, 전세계 70 ~ 80억의 사람이 전부 다른 사람이다. 모든 사람은 각각의 개성이 존재하며, 특징도 전부 다를 것이다. 따라서 만약 당신이 신과 같은 존재라면, 사람을 만들텐데, 이 사람을 객체 (Object) 또는 인스턴스 (Instance) 라고 부를 것이다.

비록 사람이 각자의 개성을 가지고 있다곤 하지만 공통적인 특성도 존재하게 된다. 숨을 쉰다던지, 뭔가 먹어야 한다던지, 잠을 잔다던지, 비단 사람 뿐만이 아니라 생물의 공통적인 특성 말이다. 따라서 그런 공통적인 특성까지 일일이 구현한다는 것은 매우 귀찮은 일이다. 아무리 당신이 전지전능한 신이라고 할지라도 말이다. 따라서 '사람'이라는 틀을 만들어서 붕어빵 만들듯이 찍어내고, 팥이라는 속을 넣을지, 슈크림이라는 속을 넣을지는 따로 지정해주면 될 것이다. (물론 사람이라는 틀은 어마어마한 데이터가 담겨있을 것이다.) 이 때 이 틀은 클래스라는 이름으로 부르게 되며, 이 틀로 찍어낸 존재를 객체 (Object) 또는 인스턴스 (Instance)라고 한다.
즉 당신은 '사람' 이라는 클래스로 찍어낸 'OOO' 라는 객체가 되는 것이다. (당신의 이름이 무엇인진 모르니 OOO로 대체하겠다.)




2. 클래스를 만들어보자


클래스를 만들어보기 앞서 몇가지 설명을 하자면, 객체 지향은 보통 프로그램을 개발하는데 많이 사용하는데, 특히 게임과 같은 응용 프로그램에 아주 유용하게 사용하게 된다. 게임을 개발하는 방법은 여러가지가 있겠지만, 요즘은 유니티나 언리얼 엔진과 같은 게임 엔진이 무료로 풀리면서 누구나 쉽게 게임을 만들 수 있게 되었다. 또한 이러한 엔진으로 객체 지향을 좀 더 직관적으로 공부할 수 있게 된다. 굳이 엔진을 다운로드 할 필요는 없지만, 어차피 무료인데 한번 해보는 것도 나쁘지 않다고 생각한다.

유니티는 기본적으로 C#JavaScript와 형태가 비슷한 독자적인 스크립트 언어로 개발하게 된다. 어떤 것이든 상관은 없지만 C#이 공부하기 더 편하므로 이것으로 만들어보겠다.

참고로 유니티 사용법을 어느 정도 알고 있다는 가정 하에 적기 때문에 혹시 잘 모른다면 기본적인 내용을 보고 오는 것을 추천한다.

유니티는 스크립트라고 부르는 파일로 동작하게 되는데, 스크립트를 생성하면 기본적인 형태의 클래스를 자동 생성해준다. 한 번 만들어보자.


project -> 우클릭 -> Create -> C# Script를 클릭하면 스크립트 파일이 하나 생성되는데, 이름을 지정하게 되면 그 이름으로 클래스가 생성된다. 코드는 아래와 같이 만들어질 것이다.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Human : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}


Start와 Update는 유니티의 내장함수인데, 일단 지금은 필요한 함수가 아니므로 지워준다.
이제 사람 클래스를 만들어보자. 사람이란 존재는 엄청나게 많은 특성을 가지고 있지만, 일단 몇개만 정해보자. 보통 사람을 처음 대면했을 때 물어보는 건 2가지이다. "이름이 뭐니?" 와 "몇살이니?" 가 되겠다.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Human : MonoBehaviour
{
    public string Hname;
    public int Hage;

    public Human()
    {

    }
    public Human(string s, int i)
    {
        Hname = s;
        Hage = i;
    }
    public void SetName(string s)
    {
        Hname = s;
    }
    public void SetAge(int i)
    {
        Hage = i;
    }
    public string GetName()
    {
        return Hname;
    }
    public int GetAge()
    {
        return Hage;
    }
}


클래스 이름과 같은 메소드를 생성자 (Constructor) 라고 하는데, 객체를 생성하는 데에 초기화하기 위한 메소드이다. 
GetName과 GetAge 메소드로 사람의 이름과 나이를 가져올 수 있다. 일단 이렇게 만들어 둔 후 아까처럼 새 스크립트를 만들어보자. GameManager라는 이름으로 하나 더 만들었다.
아래와 같이 작성해보자.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    Human Human = new Human("keykat", 25);

    private void Start()
    {
        Debug.Log(Human.GetName());
        Debug.Log(Human.GetAge());
    }
}


이제 빈 오브젝트를 하나 생성하고 GameManger라는 이름을 붙이고, 여기에 GameManager.cs를 붙여주고 실행해보자.


자, 실행은 되고 의도한 결과가 잘 나오긴 하는데, 이상한 경고 메세지가 나온다. 'new' 키워드를 사용할 수 없단다.
Monobehaviour를 상속 받는 클래스는 new 를 사용할 수 없는데, Monobehaviour는 어떤 오브젝트에 부착되어야 사용할 수 있게 되는데 그걸 무시한 채 new 키워드를 이용해 객체를 생성하려고 하니 일단 생성은 되지만 어떤 오브젝트에 부착되어 있는지는 정해지지 않으니 경고가 생기게 되는 것이다.
따라서 AddComponent()를 이용해서 특정 오브젝트에 원하는 클래스 스크립트를 부착시켜줘야한다.


혹시 Monobehaviour 상속을 포기한다면 어떻게 될까? 물론 상속을 안 받게 할 순 있지만, 유니티의 여러 기능을 사용하지 못하기 때문에 되도록이면 Monobehaviour를 상속 받거나, 이걸 상속 받은 다른 클래스를 상속 받게 만드는 것이 좋다.


게임에서의 객체는 어떤 캐릭터나 몬스터 같은 걸 이야기 할 텐데, 그렇다면 이것들은 어떻게 생성해주어야 할까? 위에서 얘기한 것 처럼 Empty Object를 특정 위치에 생성하고 AddComponent()로 클래스를 부착시켜줘도 되겠지만, 프리팹을 이용해서 생성할 수 있다.


1. 프리팹을 생성한다.

  • Hierarchy 창에 2D Sprite 오브젝트를 생성하고 Human이라는 이름을 붙인 후, Human 클래스를 부착시킨다. 
  • Assets -> resources -> prefabs 폴더를 생성하고 (resources 폴더를 생성하고 그 안에 prefabs 폴더를 생성한다. resources 폴더는 나중에 설명하겠다.) 만든 오브젝트를 이 폴더 안에 끌어다 놓는다.

2. GameManager.cs에서 해당 객체를 생성한다.
  • Human 클래스를 선언한다.
  • Start, Update 등의 함수를 이용해 객체를 생성해준다.


이 때, 어떤 오브젝트를 생성하기 위한 함수는 Instantiate 이다. 


그렇다면 사람을 만드는 함수를 작성해보자. 위의 GameManager.cs 를 다시 변경해보자.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{

    public void HumanInstantiate()
    {
        Vector2 humanVector = new Vector2(Random.Range(1, 50), Random.Range(1, 50));
        GameObject humanObject = Instantiate(Resources.Load("prefabs/human"), humanVector, Quaternion.identity) as GameObject;

        Human human = humanObject.GetComponent<Human>();
        human.SetName("keykat");
        human.SetAge(25);
    }
}


다른 내용은 당장은 중요하지 않고 여기서 중요한 내용은 GameObject humanObject를 생성하는 부분부터 되겠다. Instantiate가 직관적으로 new 키워드를 이용한 객체 생성과 비슷한 역할을 한다고 볼 수 있겠다.
원래대로라면 new 를 이용하여 human 객체를 생성하는 것이지만, 유니티는 클래스 스크립트를 오브젝트에 부착시키고 Instantiate로 human 오브젝트를 생성하는 과정이 객체 생성이라고 볼 수 있겠다. 물론 이 human의 내용 또한 가져와서 사용하거나 변경할 수 있게된다.

이제 버튼에 HumanInstantiate 함수를 연결 시켜서 버튼을 누를 때 해당 함수가 실행되게 만들어보자.
(혹시 유니티 UI의 사용법을 모른다면, 다음에 정리하도록 하겠다.)


 버튼을 누를 때마다 human 객체가 생성되는 것을 볼 수 있다!





정리


이 포스트는 유니티의 사용 방법에 대한 것이 아닌, 객체 지향 프로그래밍이 어떤 느낌인지 공부하기 위한 것이다. 따라서 중요한 내용을 간략하게 정리하자면 아래와 같다.

1. 클래스는 어떤 존재를 생성하기 위한 틀이며, 이 클래스를 이용해 만들어낸 존재를 객체, 혹은 인스턴스라고 부른다.

2. 객체를 생성하고자 한다면 new 키워드를 이용해 생성할 수 있다.

3. 유니티의 경우 new 키워드는 Monobehaviour에서 사용하면 애매해지는 경우가 많다. 따라서 객체를 생성하고자 한다면 원하는 오브젝트에 AddComponent()로 클래스 스크립트를 부착시키거나, 해당 클래스가 부착된 오브젝트를 Instantiate로 생성할 수 있다.





- 추가 -
위의 human 객체의 이름과 나이는 어떻게 알 수 있을까? 생성될 때 마다 전부 로그를 찍어서 확인할 수는 없는 노릇이다.
사실 위의 Human 클래스에서 Hname이나 Hage에는 public을 붙일 필요가 없다. 하지만 유니티에서 public은 특수한 역할을 하게 되는데, 바로 화면에 해당 변수의 내용을 바로 볼 수 있다는 점이다.


변수를 public으로 바꾸면 오른쪽의 Hierarchy 창에서 해당 오브젝트를 클릭했을 때 inspector 창에서 해당 오브젝트가 가진 클래스의 public 변수의 내용을 확인해 볼 수 있다.
(물론 생성되는 human 오브젝트의 이름과 나이는 모두 keykat, 25로 통일 했기 때문에 다 같게 나온다.)

댓글