게임 개발을 하다 보면 변수 하나 선언하는 것도 신중해야 할 때가 많습니다.
특히, 게임 데이터(ex_ HP, 경험치, 레벨 등)는 외부에서 무분별하게 변경되면 심각한 버그를 초래할 수 있습니다. 이번 글에서는 유니티에서 데이터 보호를 위한 캡슐화, 보안, 유지보수, 그리고 데이터 무결성 개념을 정리해보습니다.
캡슐화란 무엇인가?
캡슐화(Encapsulation)는 객체지향 프로그래밍(OOP)의 중요한 원칙 중 하나로, 데이터를 외부에서 직접 접근하지 못하게 하고, 반드시 정해진 인터페이스(메서드나 프로퍼티)를 통해서만 변경할 수 있도록 하는 기법입니다.
잘못된 예시 (캡슐화 X)
public class Player
{
public float HP = 100; // 누구나 직접 변경 가능
}
// 다른 스크립트에서...
player.HP = -9999; // 치트 가능, 비정상적인 값이 들어갈 수 있음
📋
이렇게 하면 어디서든 HP를 마음대로 수정할 수 있어 데이터가 엉망이 될 위험이 있습니다.
또한, HP가 어디서 수정되는지 디버깅하기 어려워집니다.
올바른 예시 (캡슐화 적용)
public class Player
{
private float hp = 100; // 외부에서 직접 접근 불가
public float HP => hp; // 읽기만 가능 (public get, private set)
public void TakeDamage(float amount)
{
hp = Mathf.Clamp(hp - amount, 0, 100); //예시
}
}
📋
이제 hp 값을 변경하려면 반드시 TakeDamage()를 호출해야 하므로, 잘못된 값이 들어갈 가능성을 차단할 수 있습니다.
보안(Security) 관점에서 캡슐화가 중요한 이유
보안(Security)이라는 개념이 단순히 네트워크 해킹 방지만을 의미하는 것은 아닙니다.
게임 개발에서는 데이터 보호와 무결성을 유지하는 것도 보안의 일환입니다.
보안 위험: 치트 엔진(CE) 같은 툴에 취약
멀티플레이어 게임에서는 public 필드로 HP를 선언하면 치트 엔진을 통해 쉽게 조작 가능합니다.
player.HP = 99999; // 이런식으로..
📋
보안 강화: 캡슐화 적용
private float hp;
public float HP { get => hp; private set => hp = Mathf.Clamp(value, 0, 100); }
📋
이런식으로 변수를 캡슐화하면 치트 엔진을 이용해 HP 값을 조작하려 해도, 클라이언트에서 직접 변경할 수 없기 때문에 변조가 어렵게 됩니다.
유지보수성과 확장성을 높이는 방법
변수에 직접 접근하는 방식은 코드가 복잡해질수록 유지보수가 어려워집니다. 만약 HP를 단순 숫자가 아니라 방어력, 버프 효과 등을 반영해야 하는 계산식으로 바꿔야 한다면...
캡슐화를 안 하면 유지보수가 어려움
player.HP -= 10; // 여기저기서 직접 HP를 변경하면 수정이 어려움
📋
으악...여기저기서 HP를 막 수정해버리네요.. 저희는 아까 언급했다시피 방어력, 버프 효과등을 반영해서 계산해야하는데...저렇게 접근하게 된다면 계산식이 무의미해지게 되기 때문이죠..
캡슐화를 하면 유지보수가 쉬움
public void ChangeHP(float amount, bool isCritical)
{
if (isCritical) amount *= 1.5f;
HP = Mathf.Clamp(HP + amount, 0, 100);
}
📋
이제 모든 HP 변경이 ChangeHP()를 통해 이루어지므로, 한 곳만 수정하면 전체 시스템이 적용됩니다.
데이터 무결성(Data Integrity) 유지하기
데이터 무결성(Data Integrity)이란, 데이터가 올바르게 유지되고 변조되지 않는 것을 의미합니다.
게임에서 HP, 레벨 같은 값이 비정상적으로 변하면 버그가 발생할 수 있기 때문입니다.
데이터 무결성이 깨지는 사례
player.HP = -50; // 체력이 마이너스가 된다?!
player.Level = -1; // 레벨이 음수가 된다?!
📋
위와 같은 문제를 방지하려면 데이터가 비정상적인 값이 되지 않도록 해야 합니다.
데이터 무결성을 유지하는 방법
private float hp;
public float HP
{
get => hp;
private set => hp = Mathf.Clamp(value, 0, 100);
}
📋
이제는 HP가 절대로 0 미만으로 내려가지 않으므로, 데이터가 정상적으로 유지됩니다.