[Unity 입문 강좌 - 5] C# 유니티 접근 지정자와 접근자 활용하기 : 데이터 보호 및 캡슐화

1. 접근 지정자와 접근자의 기본 이해

 

1.1 접근지정자 or 접근제어자(Access Modifiers)의 개념 설명

 

유니티에서 사용되는 접근지정자는 클래스 멤버의 접근 수준을 정의하는데 사용됩니다. 각 접근 제한자는 클래스, 변수, 메서드 및 다른 멤버의 가시성과 접근성을 다르게 설정합니다. 여기서 클래스 멤버란 변수, 메서드(함수), 프로퍼티, 이벤트 등을 의미합니다. 다음은 각각의 접근 제한자에 대한 설명입니다.

 

public : 어떤 클래스에서든 접근할 수 있습니다. public 멤버는 어디서든, 즉 같은 어셈블리(프로젝트) 내부 또는 다른 어셈블리에서도 접근할 수 있습니다.

 

private : 오직 같은 클래스 내부에서만 접근할 수 있습니다. 다른 클래스에서는 접근할 수 없습니다. private은 해당 클래스의 구현 세부 정보에만 관련되어 있을 때 사용합니다.

 

internal : 같은 어셈블리(프로젝트) 내의 모든 클래스에서 접근할 수 있지만, 다른 어셈블리의 클래스에서는 접근할 수 없습니다. 프로젝트 내부에서만 사용되어야 하는 멤버에 적합합니다.

 

protected internal: protectedinternal의 조합으로, 같은 어셈블리 내의 모든 클래스 또는 파생 클래스에서 접근할 수 있습니다. 다시 말해, 어셈블리 내부에서는 internal과 같은 접근 수준을, 어셈블리 외부에서는 protected와 같은 접근 수준을 갖습니다.

 

private protected: C# 7.2에서 추가된 접근 수준으로, 같은 어셈블리 내의 파생 클래스에서만 접근할 수 있습니다. 다른 어셈블리의 파생 클래스에서는 접근할 수 없습니다. 이는 더 엄격한 protected 접근 수준을 필요로 할 때 유용합니다.

 

// 어셈블리 내부에서 사용하는 기본 클래스
public class BaseClass
{
   
   // public 멤버: 어디서든 접근 가능
    public int PublicMember = 1;

    // private 멤버: 오직 BaseClass 내부에서만 접근 가능
    private int PrivateMember = 2;

    // internal 멤버: 같은 어셈블리 내에서만 접근 가능
    internal int InternalMember = 3;

    // protected internal 멤버: 같은 어셈블리 내의 모든 클래스 또는 파생 클래스에서 접근 가능
    protected internal int ProtectedInternalMember = 4;

    // private protected 멤버: 같은 어셈블리 내의 파생 클래스에서만 접근 가능
    private protected int PrivateProtectedMember = 5;

}

 

파생 클래스란? : 객체 지향 프로그래밍에서 상속을 통해 하나의 클래스의 속성과 메서드을 다른 클래스가 받아들이는 메커니즘을 사용하여 정의된 클래스

 

// 기본 클래스
public class Vehicle {
    public void StartEngine() {
        // 엔진을 시작하는 코드
    }
}

// 파생 클래스
public class Car : Vehicle {
    public void OpenTrunk() {
        // 트렁크를 여는 코드
    }
}

 


1.2 속성(Property)과 접근자(Accessor)의 개념 설명

 

속성은 객체가 가지고 있는 데이터를 의미하며, 객체의 상태를 정의합니다. 예를 들어, '차량'이라는 객체가 있다면 '색상', '모델', '제조사' 등이 속성이 될 수 있습니다. 접근자는 이러한 속성에 접근하는 메서드를 의미합니다. get 접근자는 속성 값을 읽어오는 데 사용되며, set 접근자는 속성 값을 설정하는 데 사용됩니다. C#에서는 이를 아래와 같이 표현할 수 있습니다.

 

private string _color; // 속성

public string Color // 접근자
{
    get { return _color; }
    set { _color = value; }
}

 


1.3 유니티 내 접근자 사용의 필요성

 

유니티에서 접근자는 주로 데이터의 캡슐화 및 데이터 보호에 사용됩니다. 예를 들어, 플레이어의 체력을 나타내는 데이터가 있을 때, 이 데이터는 게임 로직 상 직접적으로 변경되어서는 안 되는 값일 수 있습니다. 이럴 때 접근자를 사용하여 이 데이터에 대한 '접근 규칙'을 정의함으로써 데이터를 보호할 수 있습니다. 이런 식으로 더욱 안정적인 코드를 작성할 수 있습니다.

 

private int _hp; // 플레이어의 체력

public int HP
{
    get { return _hp; }
    set 
    { 
        if(value <= 0) // 체력이 0 이하로 내려가지 않게 한다
        {
            _hp = 0;
        }
        else
        {
            _hp = value; 
        }
    }
}

 

 


2. C#의 접근자

 

C#에서 접근자는 속성에 대한 접근을 제어하는 특별한 메서드로, get과 set 두 가지로 구성됩니다. 이들은 클래스나 구조체 내부의 변수에 간접적으로 접근할 수 있는 방법을 제공합니다.

 


2.1 get 접근자

 

get 접근자는 해당 속성의 값을 반환하는 데 사용됩니다. 이는 읽기 전용(read-only) 속성을 생성하는 데도 사용할 수 있습니다. 읽기 전용 속성은 get 접근자만 가지고, set 접근자는 없습니다. 코드에서, _name 필드의 값을 반환하는 Name 속성의 get 접근자를 볼 수 있습니다.

 

private string _name;

public string Name
{
    get { return _name; } // Name 속성의 값을 반환하는 get 접근자
}

 

2.2 set 접근자


set 접근자는 속성에 값을 할당하는 데 사용됩니다. 이는 쓰기 전용(write-only) 속성을 생성하는 데도 사용할 수 있습니다. 쓰기 전용 속성은 set 접근자만 가지고, get 접근자는 없습니다. set 접근자 내에서 value라는 키워드를 사용하면, 속성에 할당하려는 값을 참조할 수 있습니다. 코드에서, _name 필드에 값을 할당하는 Name 속성의 set 접근자를 볼 수 있습니다.

 

private string _name;

public string Name
{
    set { _name = value; } // Name 속성에 값을 할당하는 set 접근자
}



대부분의 경우, 속성은 get 접근자와 set 접근자를 모두 포함하며, 이를 통해 해당 속성의 값을 읽거나 쓸 수 있습니다. 코드에서 Name 속성은 get 접근자와 set 접근자를 모두 가지고 있습니다. 이를 통해 _name 필드의 값을 읽거나 쓸 수 있습니다.

 

private string _name;

public string Name
{
    get { return _name; }
    set { _name = value; }
}

 

 


3. 유니티에서 접근자 사용하기

 

3.1 접근자를 이용한 데이터 보호


접근자는 클래스의 내부 데이터를 외부로부터 보호하는 데 사용될 수 있습니다. 이를 통해 내부 데이터가 잘못된 방법으로 변경되는 것을 방지할 수 있습니다. 이를 통해 프로그램의 안정성을 높일 수 있습니다.

 

"외부"라는 용어는 접근 지정자(access modifier)의 Context에서 사용됩니다. 여기서 "외부"는 특정 클래스 또는 객체 외부를 가리킵니다. 예를 들어, 게임에서 플레이어의 체력을 나타내는 필드가 있다고 가정해 보겠습니다. 이 필드는 0 이하의 값이 되면 안 됩니다. 이럴 때 set 접근자를 사용하면 이런 조건을 강제로 적용할 수 있습니다. (아래 코드 참조)

 

Context는 특정 상황이나 환경을 의미합니다. 접근자와 관련하여 Context는 특정 부분(클래스, 메소드, 변수 등)이 어떤 조건이나 범위 내에서 사용될 수 있는지를 설명하는 배경이나 환경을 가리킵니다. private 접근자가 사용된 멤버는 선언된 클래스 내부에서만 접근할 수 있는 Context에 있습니다. 반면 public 접근 지정자가 사용된 멤버는 어떤 외부 클래스나 객체에서도 접근할 수 있는 더 넓은 Context에 있습니다. 

 


3.2 캡슐화와 접근자


캡슐화는 객체 지향 프로그래밍의 기본 원칙 중 하나로, 데이터와 이를 처리하는 메서드를 하나로 묶는 것을 의미합니다. 이를 통해 클래스의 내부 구현을 숨기고, 사용자에게는 간단한 인터페이스만을 제공합니다.



접근자는 이러한 캡슐화를 지원하는 도구 중 하나입니다. 클래스의 내부 필드에 대한 직접적인 접근을 제한하고, 대신 접근자를 통한 간접적인 접근을 허용함으로써 데이터의 보호를 실현합니다.

 

코드에서, _health 필드는 private 접근 지정자로 선언되어 클래스 외부에서 직접적으로 접근할 수 없습니다. 대신 Health 속성을 통해 간접적으로 접근할 수 있습니다. 이렇게 접근자를 사용하면, _health 필드에 대한 접근 규칙을 명확하게 정의할 수 있습니다. 

 

private int _health;

public int Health
{
    get { return _health; }
    set
    {
        if (value < 0) // 체력이 0 이하로 내려가지 않도록 하는 조건문
        {
            _health = 0;
        }
        else
        {
            _health = value;
        }
    }
}

 

 


4.  유니티 접근자 예시


유니티에서는 여러 내장 객체와 컴포넌트를 제공합니다. 이들에는 다양한 프로퍼티와 접근자가 포함되어 있습니다. 여기서는 transform과 position 등의 주요 접근자를 살펴보겠습니다.

 

모든 GameObject는 transform 컴포넌트를 가지며, 이를 통해 객체의 위치(position), 회전(rotation) 및 크기(scale)를 가져오거나 설정할 수 있습니다. 이들은 모두 프로퍼티로, get 접근자와 set 접근자를 사용하여 읽고 쓸 수 있습니다.

 

// 객체의 위치를 가져옴
Vector3 position = gameObject.transform.position;

// 객체의 위치를 설정함
gameObject.transform.position = new Vector3(0, 0, 0);

 

transform 컴포넌트의 하위 프로퍼티로, 객체의 위치를 나타냅니다. 이 역시 get 접근자와 set 접근자를 사용하여 읽고 쓸 수 있습니다.

 

// 객체의 위치를 가져옴
Vector3 position = gameObject.transform.position;

// 객체의 위치를 설정함
gameObject.transform.position = new Vector3(10, 0, 5);