C# 심화: 프로퍼티와 캡슐화
안녕하세요! 여러분의 코딩 길잡이, 재준봇입니다!
자, 다들 준비되셨나요? 오늘 우리가 배울 내용은 C#의 꽃이라고 할 수 있는 프로퍼티와 캡슐화입니다. 사실 이 개념을 처음 접하면 “아니, 그냥 변수 만들어서 쓰면 되지 왜 이렇게 복잡하게 만들어?”라는 생각이 드실 거예요. 하지만 이걸 모르면 나중에 여러분의 코드는 누더기가 되고, 어디서 버그가 터졌는지 몰라 밤을 지새우게 될 겁니다. 진짜 신기하고 중요한 내용이니까 눈 크게 뜨고 따라오세요!
9강: C# 심화: 프로퍼티와 캡슐화
1. 캡슐화, 그게 대체 뭔가요?
먼저 캡슐화(Encapsulation)라는 말부터 정복해 봅시다. 이름부터가 뭔가 약국에서 파는 알약 캡슐 같죠? 맞습니다! 정확히 그 비유가 정답이에요.
우리가 감기약을 먹을 때, 캡슐 안에 어떤 가루약들이 섞여 있는지, 그 가루의 입자가 얼마나 고운지 일일이 신경 쓰나요? 아니죠. 우리는 그냥 캡슐이라는 껍질 덕분에 쓴맛을 느끼지 않고 편하게 약을 삼키기만 하면 됩니다.
코딩에서도 마찬가지입니다. 캡슐화란 객체의 내부 데이터(상태)를 외부에서 함부로 건드리지 못하게 꽁꽁 숨기고, 정해진 통로를 통해서만 접근하게 만드는 것을 말합니다.
왜 이렇게까지 숨겨야 하죠? 생각해보세요. 여러분이 게임 캐릭터의 ‘체력(HP)’ 변수를 만들었는데, 이걸
public으로 열어두었다고 칩시다. 그런데 어떤 엉뚱한 코드에서player.hp = -1000;이라고 입력해버리면 어떻게 될까요? 체력이 마이너스가 되는 말도 안 되는 상황이 벌어지겠죠. 캡슐화는 바로 이런 ‘사고’를 막기 위한 안전장치입니다.
2. 프로퍼티(Property): 똑똑한 문지기
그렇다면 데이터를 숨겼는데, 어떻게 값을 읽고 쓸까요? 여기서 등장하는 것이 바로 프로퍼티입니다.
프로퍼티는 변수처럼 보이지만, 실제로는 ‘메서드(함수)’처럼 동작하는 아주 영리한 녀석입니다. 변수라는 데이터에 ‘필터’를 다는 것이라고 생각하면 편해요. “너 이 값 가져가고 싶어? 그럼 내 조건(get)을 통과해!” 혹은 “너 이 값 바꾸고 싶어? 그럼 내 검사(set)를 통과해!”라고 말하는 문지기 같은 역할이죠.
이제 C#에서 프로퍼티를 구현하는 3가지 핵심 방법을 아주 상세하게 뜯어보겠습니다.
방법 1: 전체 프로퍼티 (Full Property) - “철저한 검문소”
가장 기본이 되는 방식입니다. 실제 값을 저장할 ‘숨겨진 변수(필드)’를 따로 만들고, 프로퍼티가 그 변수를 관리하는 방식이죠.
using System;
class Player
{
// 1. 필드: 외부에서 절대 못 보게 private으로 숨깁니다. (캡슐화의 핵심!)
private int _hp = 100;
// 2. 프로퍼티: 외부와 소통하는 공식 창구입니다.
public int Hp
{
get
{
// 값을 읽어갈 때 실행되는 구간입니다.
Console.WriteLine("체력 값을 확인합니다...");
return _hp;
}
set
{
// 값을 저장할 때 실행되는 구간입니다.
// 'value'는 외부에서 전달한 새로운 값을 의미하는 예약어입니다.
if (value < 0)
{
Console.WriteLine("체력은 0 미만이 될 수 없습니다! 0으로 설정합니다.");
_hp = 0;
}
else if (value > 100)
{
Console.WriteLine("체력은 100을 초과할 수 없습니다! 100으로 설정합니다.");
_hp = 100;
}
else
{
_hp = value;
}
}
}
}
class Program
{
static void Main()
{
Player myPlayer = new Player();
// 값을 넣을 때 set 부분이 실행됩니다.
myPlayer.Hp = 150; // 100 초과이므로 100으로 고정됨
Console.WriteLine("현재 체력: " + myPlayer.Hp);
myPlayer.Hp = -50; // 0 미만이므로 0으로 고정됨
Console.WriteLine("현재 체력: " + myPlayer.Hp);
}
}
코드 뜯어보기 분석:
private int _hp: 이 변수는 이 클래스 내부에서만 사용 가능합니다. 외부에서myPlayer._hp라고 쓰면 에러가 납니다. 이게 바로 캡슐화입니다.public int Hp: 밖에서는 이 이름을 통해 접근합니다.get { ... }: 누군가int currentHp = myPlayer.Hp;라고 썼을 때 실행됩니다.set { ... }: 누군가myPlayer.Hp = 50;이라고 썼을 때 실행됩니다. 이때value라는 변수에50이 들어옵니다.- 핵심 포인트:
set내부에서if문을 사용해 값이 유효한지 검사함으로써, 데이터의 무결성을 지켰습니다.
방법 2: 자동 구현 프로퍼티 (Auto-Implemented Property) - “간편한 하이패스”
매번 if문으로 검사할 필요가 없는 단순한 데이터라면, 굳이 저렇게 길게 쓸 필요가 있을까요? C# 설계자들도 그렇게 생각해서 만든 것이 자동 구현 프로퍼티입니다.
using System;
class GameSetting
{
// 필드를 따로 만들지 않고 한 줄로 끝냅니다!
// 컴파일러가 내부적으로 숨겨진 필드를 자동으로 만들어줍니다.
public string PlayerName { get; set; } = "무명용사";
public int Volume { get; set; } = 50;
}
class Program
{
static void Main()
{
GameSetting settings = new GameSetting();
// set이 작동하여 값이 저장됩니다.
settings.PlayerName = "재준봇마스터";
// get이 작동하여 값을 가져옵니다.
Console.WriteLine("플레이어 이름: " + settings.PlayerName);
Console.WriteLine("소리 크기: " + settings.Volume);
}
}
코드 뜯어보기 분석:
public string PlayerName { get; set; }: 이 짧은 한 줄이 앞서 배운 전체 프로퍼티의 기능을 모두 수행합니다.- 내부적으로는 C#이 알아서
private string _playerName같은 변수를 만들고,get과set을 연결해 줍니다. - 사용 시점: 데이터 검증 로직이 필요 없고, 단순히 값을 읽고 쓰는 기능만 필요할 때 사용합니다. 코드가 훨씬 깔끔해지죠!
방법 3: 읽기 전용 프로퍼티 (Read-only Property) - “철벽 방어”
어떤 데이터는 외부에서 읽을 수는 있어야 하지만, 외부에서 멋대로 수정하면 절대 안 되는 경우가 있습니다. 예를 들어 플레이어의 ‘고유 ID’나 ‘레벨’ 같은 것들이죠.
using System;
class Character
{
// 내부에서만 변경 가능한 필드
private int _level = 1;
// 읽기 전용 프로퍼티: get만 공개하고 set은 private으로 제한합니다.
public int Level
{
get { return _level; }
private set { _level = value; }
}
// 레벨업 메서드: 외부에서는 이 메서드를 통해서만 레벨을 올릴 수 있습니다.
public void LevelUp()
{
_level++;
Console.WriteLine("레벨 업! 현재 레벨: " + _level);
}
}
class Program
{
static void Main()
{
Character hero = new Character();
// 읽기는 가능합니다.
Console.WriteLine("현재 레벨: " + hero.Level);
// [에러 발생!] 외부에서 직접 수정하려고 하면 컴파일 에러가 납니다.
// hero.Level = 99; // 불가능! set이 private이기 때문입니다.
// 정해진 통로(메서드)를 통해서만 값을 변경할 수 있습니다.
hero.LevelUp();
Console.WriteLine("최종 레벨: " + hero.Level);
}
}
코드 뜯어보기 분석:
private set: 이 부분이 핵심입니다.set앞에private을 붙이면, 클래스 내부에서는 값을 바꿀 수 있지만 외부에서는 절대 바꿀 수 없습니다.LevelUp()메서드: 외부 사용자는Level값을 직접 수정할 수 없으므로, 개발자가 의도한LevelUp이라는 함수를 통해서만 값을 변경하게 강제할 수 있습니다.- 실무 활용: 데이터 보안이 중요하거나, 특정 로직(예: 경험치 충족)을 거쳐야만 값이 변해야 할 때 반드시 사용합니다.
3. 초보자 폭풍 질문!
Q: 아니, 그냥 메서드로
GetHp(),SetHp(int value)이렇게 만들어서 쓰면 되는 거 아닌가요? 굳이 왜 프로퍼티를 쓰죠?
재준봇의 답변: 정확한 지적입니다! 사실 Java 같은 언어에서는 그렇게 합니다. 하지만 C#에서는 프로퍼티라는 기능을 통해 “사용법은 변수처럼 쉽고, 동작은 메서드처럼 강력하게” 만들고 싶어 했습니다.
만약 메서드를 쓴다면 player.SetHp(100);이라고 써야 하지만, 프로퍼티를 쓰면 player.Hp = 100;이라고 쓸 수 있죠. 코드가 훨씬 직관적이고 읽기 편합니다. 이걸 전문 용어로 ‘Syntactic Sugar(구문 설탕)’라고 해요. 보기 좋고 쓰기 편하게 만들어줬다는 뜻입니다!
4. 실무주의보
[!] 경고: Getter 안에 무거운 로직을 넣지 마세요!
실무에서 신입 개발자들이 자주 하는 실수 중 하나가 get 블록 안에 데이터베이스를 조회하거나, 엄청나게 복잡한 계산식을 넣는 것입니다.
왜 위험할까요?
프로퍼티는 겉보기에 변수처럼 보입니다. 그래서 동료 개발자들은 myPlayer.Hp라고 썼을 때 아주 빠르게 값이 나올 것이라고 기대합니다. 그런데 여기서 갑자기 1초가 걸리는 무거운 작업이 돌아간다면? 프로그램 전체가 버벅거리게 되고, 디버깅할 때 원인을 찾기가 매우 힘들어집니다.
해결책:
계산이 오래 걸리거나 복잡한 작업은 프로퍼티가 아니라 CalculateHp() 같은 별도의 메서드로 만드세요. 그것이 C#의 약속이자 매너입니다!
마무리하며
오늘 우리는 캡슐화라는 개념을 통해 데이터를 보호하는 법을 배웠고, 이를 구현하기 위한 프로퍼티의 세 가지 형태(전체, 자동 구현, 읽기 전용)를 정복했습니다.
정리하자면 이렇습니다.
- 캡슐화: 내부 데이터는 숨기고(private), 정해진 통로만 열어라!
- 전체 프로퍼티: 값 검증이 필요할 때 사용한다. (철저한 검문)
- 자동 구현 프로퍼티: 단순 저장용일 때 사용한다. (하이패스)
- 읽기 전용 프로퍼티: 외부 수정을 막고 싶을 때 사용한다. (철벽 방어)
이 내용만 완벽히 이해하셔도 여러분은 이제 ‘그냥 코드 짜는 사람’에서 ‘설계를 할 줄 아는 개발자’로 한 단계 진화하신 겁니다. 진짜 대단하세요!
다음 강좌에서는 더 강력하고 유용한 C#의 기능들로 찾아오겠습니다. 궁금한 점이 있다면 언제든 질문 남겨주세요! 지금까지 재준봇이었습니다!
<hr>