이전 포스팅에서 OOP란 무엇인지, 왜 사용해야 하는지, 그리고 OOP의 핵심개념들에는 무엇이 있는지 알아 보았다.
https://nybot-house.tistory.com/104
이번 포스팅에서는 OOP(Object Oriented Programming)의 핵심 개념 중 하나인 캡슐화와 은닉성에 대해 자세히 알아 보자.
캡슐화(은닉성)란?
캡슐화는 객체의 세부 구현 내용을 숨기고(은닉성), 사용자에게는 필요한 인터페이스만을 제공하는 것을 말한다. 이를 통해 객체의 내부 구현이 외부에 노출되지 않게 하여 객체의 데이터와 메서드를 보호하고(보안), 사용상의 실수를 줄이며, 코드의 재사용성과 유지보수성을 높일 수 있다.
C# Unity 예시: Player Class
Unity 게임 개발에서 C#을 사용하여 캡슐화와 은닉성을 어떻게 구현할 수 있는지 예시를 통해 자세히 살펴보자.
밑의 코드는 게임 플레이어의 체력을 관리하는 간단힌 'Player' 클래스이다.
public class Player
{
// 캡슐화를 위해 체력(health)을 private 변수로 선언합니다.
private int health;
// 생성자에서 플레이어의 초기 체력을 설정합니다.
public Player(int initialHealth)
{
health = initialHealth;
}
// 체력을 안전하게 조정할 수 있는 public 메서드를 제공합니다.
public void TakeDamage(int damage)
{
if (damage < 0)
{
throw new ArgumentException("Damage cannot be negative");
}
health -= damage;
// 체력이 0 이하가 되면 플레이어가 사망했다고 가정합니다.
if (health <= 0)
{
Die();
}
}
// 체력을 안전하게 회복시킬 수 있는 public 메서드를 제공합니다.
public void Heal(int amount)
{
if (amount < 0)
{
throw new ArgumentException("Heal amount cannot be negative");
}
health += amount;
}
// 플레이어의 현재 체력을 확인할 수 있는 public 메서드를 제공합니다.
public int GetHealth()
{
return health;
}
// 사망 처리를 위한 private 메서드입니다.
private void Die()
{
Debug.Log("Player Died");
// 사망 처리 로직...
}
}
위의 예시에서 health 변수는 private으로 선언되어 클래스 외부에서 직접 접근할 수 없다.
이는 health 변수의 값이 클래스 내부의 메서드를 통해서만 변경될 수 있도록 하여, health 의 값이 의도치 않게 외부에서 변경되는 것을 방지한다. (다른 클래스나 메서드에서 부르려고 해도 외부에서는 결코 부를 수 없다.)
또한, 'TakeDamage' 메서드는 플레이어가 피해를 받을 때 'health'값을 감소시키고, 플레이어의 체력이 0이하가 되면 'Die'메서드를 호출한다. 'Heal' 메서드는 플레이어의 체력을 회복시킨다. 'Die'메서드는 private으로 선언되어 클래스 외부에서 직접 호출할 수 없으며, 오직 클래스 내부에서만 호출될 수 있다.
private 이 아닐 경우 발생할 수 있는 문제
private 으로 변수와 메서드를 숨기지 않을 경우, 별 문제가 발생하지 않을 거라고 생각할 수도 있다. 하지만 이럴 경우, 클래스의 내부 구현이 외부로 노출되어 예기치 않은 방식으로 변경될 수가 있다. 이는 코드의 안정성을 해칠 수 있게 만들고, 유지보수를 어렵게 만든다.
예를 들어 'health'변수를 'public'으로 선언했다고 가정해 보자.
public class Player
{
public int health; // 외부에서 직접 접근 가능
public void TakeDamage(int damage)
{
health -= damage;
if (health <= 0)
{
Die();
}
}
private void Die()
{
Debug.Log("Player Died");
}
}
이 경우, 'health'변수는 외부에서 직접 접근이 가능해져, 클래스 외부의 다른 코드에서 다음과 같이 'health'값을 부적절하게 변경할 수 있다.
Player player = new Player();
player.health = -100; // health를 직접 조작하여 예상치 못한 값으로 설정
이렇게 'health'값이 직접 조작되면, 'TakeDamage'메서드를 통한 정상적인 흐름을 벗어나, 'health'값이 음수가 되거나, 'Die'메서드가 적절한 시점에 호출되지 않는 등의 문제가 발생할 수 있다. 이는 버그와 예측 불가능한 동작을 초래할 수 있다. 당신은 안 그럴 거라고? 천만에! 복잡한 상호작용의 총 집합체인 게임을 개발하다 보면 어떤 일이든 발생할 수 있다는 것을 명심해야 한다.
따라서 변수와 메서드를 'private'으로 설정하고, 필요한 경우에만 제한된 인터페이스(ex: 'TakeDamage' 메서드)를 통해 접근을 허용하는 것이 좋다. 이를 통해 클래스의 책임과 인터페이스가 명확해지며, 코드의 안정성과 유지보수성이 향상된다.
Unity의 [SerializeField] 속성
Unity에서는 [SerializeField] 속성을 사용하여 private 필드를 인스펙터에서 편집할 수 있게 만들 수 있다. 이는 캡슐화 원칙을 유지하면서도, Unity의 시각적 편집 환경에서 직접적으로 개발자가 값을 조정할 수 있게 해준다. [SerializeField] 속성은 해당 필드를 private으로 유지하면서도 Unity 에디터에 노출시켜, 에디터 내에서 값을 수정할 수 있도록 한다. 이는 게임 개발 과정에서 굉장한 편의성을 제공하는 동시에 코드의 안정성과 캡슐화 원칙을 해치지 않는다.
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField]
private int health; // Unity 에디터에서 접근 가능하지만 코드에서는 private
public void TakeDamage(int damage)
{
health -= damage;
if (health <= 0)
{
Die();
}
}
private void Die()
{
Debug.Log("Player Died");
}
}
결론
위의 예시처럼 캡슐화를 통해 은닉함으로써 클래스의 내부 구현을 숨기고, 객체의 데이터를 보호하며, 사용자에게는 클래스를 사용하는 데 필요한 인터페이스만을 노출시키게 된다. 이렇게 함으로써 코드의 안정성과 유지보수성이 향상되며, 클래스 사용자는 클래스 내부 구현에 대해 신경 쓰지 않고도 기능을 이용할 수 있게 된다.
'C# 기초 프로그래밍 > C# Basic' 카테고리의 다른 글
[C# Basics] 재귀 함수 (0) | 2024.06.11 |
---|---|
[C# Basics] 다형성 (polymorphism) - OOP의 핵심 개념 (0) | 2024.04.15 |
[C# Basics] 객체지향 OOP란 무엇인가? - 핵심 4개념과 개론 (0) | 2024.04.12 |
[C# Basics] 생성자 Constructor (0) | 2024.04.02 |
[C# Basics] 스택과 힙 정리 - feat.복사와 참조, value, reference (0) | 2024.04.01 |
<C# Basics> 복사와 참조 (struct, class의 차이, 깊은 복사) (0) | 2023.12.23 |
<C# Basics> ref, out 키워드 (0) | 2023.12.11 |
<C# Basics> 함수/메서드 (2) | 2023.12.11 |