---
title: "프로퍼티는 필드 앞에 세운 창구다"
description: "겉보기엔 필드처럼 쓰지만 읽고 쓸 때 코드가 실행됩니다. 캡슐화를 문법으로 만든 것입니다."
author: "유니얼"
pubDate: 2026-09-09
category: "programming/csharp/basics"
tags: ["C#", "기초"]
canonical: https://blog.unialgames.com/posts/csharp-property/
---
앞에서 필드를 `private` 으로 막고 메서드로 열었습니다.

```csharp
class Player
{
    private int hp = 100;

    public int GetHp() { return hp; }
    public void SetHp(int value)
    {
        if (value < 0) value = 0;
        hp = value;
    }
}
```

안전해졌는데 쓰기가 번거로워졌습니다.

```csharp
p.SetHp(p.GetHp() - 10);      // 10 깎기
```

`p.Hp -= 10` 이라고 쓰고 싶습니다. 그런데 그러려면 필드를 열어야 하고,
열면 확인할 기회가 사라집니다.

> **밖에서는 필드처럼 보이고, 안에서는 코드가 실행되게 하면 둘 다 됩니다.**

## 프로퍼티는 이렇게 생겼다

```csharp
class Player
{
    private int hp = 100;

    public int Hp
    {
        get { return hp; }
        set
        {
            if (value < 0) value = 0;
            hp = value;
        }
    }
}
```

```csharp
p.Hp = 50;                 // set 이 실행됩니다
Console.WriteLine(p.Hp);   // get 이 실행됩니다
p.Hp -= 10;                // get 하고 계산하고 set 합니다
```

**겉보기는 필드, 실제로는 메서드**입니다.

`value` 는 우리가 선언한 적이 없습니다. `set` 안에서만 쓸 수 있는 이름이고,
**대입한 값**이 여기 들어옵니다. `p.Hp = 50` 이면 `value` 가 `50` 입니다.

뒤에 숨어 있는 `hp` 를 백킹 필드라고 부릅니다. 실제로 값이 저장되는 자리입니다.

## 한쪽만 열 수 있다

읽기만 열고 쓰기는 막으려면 `set` 을 빼면 됩니다.

```csharp
public int Hp
{
    get { return hp; }
}
```

밖에서는 볼 수만 있고, 값을 바꾸는 것은 클래스 안의 메서드로만 합니다.
앞에서 `GetHp` 만 두고 `SetHp` 를 안 만들었던 그 방식입니다.

바깥에는 읽기만, 안에서는 쓰기도 열고 싶으면 `set` 에 접근 지정자를 붙입니다.

```csharp
public int Hp
{
    get { return hp; }
    private set { hp = value; }
}
```

이제 클래스 안에서는 `Hp = 50` 이라고 쓸 수 있고 밖에서는 못 씁니다.

## 계산해서 돌려줘도 된다

프로퍼티가 반드시 필드 하나를 그대로 돌려줄 필요는 없습니다.

```csharp
class Player
{
    private int hp;
    private int shield;

    public int TotalHp
    {
        get { return hp + shield; }
    }

    public bool IsAlive
    {
        get { return hp > 0; }
    }
}
```

`TotalHp` 라는 필드는 없습니다. 물어볼 때마다 계산합니다.
밖에서는 그냥 `p.TotalHp` 로 씁니다. 안에 필드가 몇 개인지 모릅니다.

앞에서 감추는 것은 값이 아니라 **안에서 어떻게 하는지**라고 했습니다. 이게 그 이야기입니다.
나중에 방어구를 추가해서 계산식이 바뀌어도 밖은 안 고칩니다.

읽기만 하는 프로퍼티는 화살표로 짧게 쓸 수 있습니다.

```csharp
public int TotalHp => hp + shield;
public bool IsAlive => hp > 0;
```

## 자동 프로퍼티

확인할 것이 없는 필드도 있습니다. 그래도 나중을 위해 프로퍼티로 두고 싶은데,
그때마다 백킹 필드를 만드는 것이 번거롭습니다.

```csharp
public string Name { get; set; }
```

중괄호 안에 `get;` 과 `set;` 만 적으면 컴파일러가 **백킹 필드를 자동으로 만들어 줍니다.**
우리 눈에는 안 보입니다.

지금은 `public` 필드와 하는 일이 같습니다. 그런데 다른 점이 있습니다.
나중에 확인이 필요해지면 **여기만 고치면 됩니다.**

```csharp
private string name;

public string Name
{
    get { return name; }
    set
    {
        if (string.IsNullOrEmpty(value)) return;
        name = value;
    }
}
```

밖에서 `p.Name` 이라고 쓰던 코드는 한 글자도 안 바뀝니다.
`public` 필드였다면 이 변경이 밖에 전부 영향을 줍니다.

**그래서 C# 에서는 `public` 필드를 거의 안 씁니다.**
공개할 것은 자동 프로퍼티로 두는 것이 관습입니다. 지금 당장 얻는 것은 없지만
나중에 고칠 자유를 남겨 둡니다.

## 만들고 나면 못 바꾸게

`set` 대신 `init` 을 쓰면 **객체를 만들 때만** 넣을 수 있습니다.

```csharp
class Point
{
    public int X { get; init; }
    public int Y { get; init; }
}

var p = new Point { X = 1, Y = 2 };
p.X = 99;      // 컴파일 에러
```

중괄호로 값을 넣는 문법을 개체 초기화자라고 합니다. 생성자 없이도 채울 수 있습니다.
`init` 을 쓰면 그 자리에서만 되고 그다음부터는 못 고칩니다.

앞에서 해시 테이블을 볼 때 **키로 쓴 값을 나중에 바꾸면 안 된다**고 했습니다.
`init` 이 그걸 문법으로 막는 방법입니다.

## 메서드와 무엇으로 나누나

전부 프로퍼티로 만들면 안 됩니다. 기준이 있습니다.

| | 프로퍼티 | 메서드 |
| --- | --- | --- |
| 무엇 | 상태를 읽고 쓴다 | 일을 시킨다 |
| 비용 | 싸야 한다 | 비싸도 된다 |
| 부작용 | 없어야 한다 | 있어도 된다 |
| 예 | `Name`, `Count`, `IsAlive` | `TakeDamage()`, `Save()` |

`p.Hp` 라고 쓰는 사람은 그게 그냥 값을 읽는 것이라고 생각합니다.
거기서 파일을 읽거나 네트워크를 타면 안 됩니다.

**괄호가 없으면 싸고 안전하다**는 것이 읽는 사람의 기대입니다.
비싸거나 뭔가를 바꾸는 일은 메서드로 두고 괄호를 붙여야 합니다.

`list.Count` 는 프로퍼티고 `list.Add(5)` 는 메서드인 것이 그래서입니다.

## 정리

- 프로퍼티는 겉보기엔 필드지만 읽고 쓸 때 코드가 실행됩니다.
- `get` 은 읽을 때, `set` 은 쓸 때 실행됩니다. `set` 안의 `value` 가 대입한 값입니다.
- 실제로 저장되는 필드를 백킹 필드라고 합니다.
- `set` 을 빼면 읽기만, `private set` 이면 밖에서는 읽기만 됩니다.
- 필드 없이 계산해서 돌려줘도 됩니다. 안이 어떻게 생겼는지 감춥니다.
- `{ get; set; }` 은 백킹 필드를 컴파일러가 만들어 주는 자동 프로퍼티입니다.
- `public` 필드 대신 자동 프로퍼티를 씁니다. 나중에 고칠 자유를 남깁니다.
- `init` 은 만들 때만 넣을 수 있게 합니다.
- 상태를 읽고 쓰는 것은 프로퍼티, 일을 시키는 것은 메서드입니다.