무효 클릭 IP 추적 중...
c#

C# 필드(Field) vs. 프로퍼티(Property) 차이 비교

꼬예 2023. 12. 7.

필드(Field)와 프로퍼티(Property)는 객체 지향 프로그래밍, 특히 C#과 같은 언어에서 매우 중요한 개념입니다. 초보 개발자들은 두 개념 사이의 차이와 각각의 사용 상황에 대해 혼란을 겪기도 하는데요.

 

이번 포스팅에서는 각각의 쓰임새와 차이를 깔끔하게 정리해 보겠습니다.

 

필드와 프로퍼티의 기본 형태

필드 (Field)

필드는 클래스나 구조체에 속한 변수로, 데이터를 저장합니다. 필드는 기본적으로 클래스의 상태를 나타내는 데 사용됩니다.

public class MyClass
{
    public int myField;  // 공개 필드
    private int myPrivateField;  // 비공개 필드
}

 

프로퍼티 (Property)

프로퍼티는 필드에 값을 설정하거나 가져오는 방법을 제공합니다. 프로퍼티는 getset 접근자를 사용하여 필드의 값을 읽거나 변경할 수 있는 로직을 포함할 수 있습니다

.

크게 두 가지 형태로 작성되는데요.

 

자동 구현 프로퍼티 (Auto-Implemented Property)

public class MyClass
{
    public int MyProperty { get; set; }  // 자동 구현 프로퍼티
}

 

전통적인 프로퍼티 (Traditional Property)

public class MyClass
{
    private int myProperty;

    public int MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; }
    }
}

 

비교

  • 필드: 직접 데이터를 저장하며, 일반적으로는 클래스 내부 로직에서 사용됩니다.
  • 프로퍼티: 필드에 대한 접근 방식을 정의하며, 종종 유효성 검사, 로깅, 데이터 변환 등의 추가 로직을 포함할 수 있습니다.

여기서 의문이 하나 생깁니다.

"프로퍼티는 필드 값을 설정하거나 가져오는 방법뿐만 아니라 본인 자체로서 컨테이너 역할을 하지 않나? "

 

맞습니다.

 

자동 구현 프로퍼티는 명시적인 백업 필드를 선언하지 않고도 값을 저장하고 관리할 수 있습니다. 이는 내부적으로 컴파일러가 필드를 생성하며, 프로퍼티를 통해 이 필드에 접근하기 때문인데요.

 

이러한 자동 구현 프로퍼티(Auto-Implemented Properties)의 특징은 특히 초보 개발자들에게 필드(Field)와 프로퍼티(Property)의 차이를 혼동스럽게 만들 수 있습니다.

 

사실, 프로퍼티의 진가는 전통적인 프로퍼티가 만들어냅니다. 그럼 이 전통적 프로퍼티에 대해 파헤쳐보겠습니다.

 

다양한 전통적 프로퍼티의 형태

전통적인 프로퍼티(Traditional Properties)는 여러 가지 형태로 구현될 수 있는데, 각 형태에 대한 설명과 코드 예시를 통해 알아보겠습니다.

 

1. 기본 프로퍼티

기본 프로퍼티는 가장 일반적인 형태의 프로퍼티로, 백업 필드의 값을 읽거나 설정하는 기능을 합니다.

public class Employee
{
    private int _age;

    public int Age
    {
        get { return _age; }
        set { _age = value; }
    }
}
value 키워드의 역할
  • 내장 변수: value는 set 접근자에서 사용되는 내장 변수로, 프로퍼티에 할당된 실제 값을 나타냅니다.
  • 값 할당: 프로퍼티에 값을 할당할 때, value 변수는 그 할당된 값을 가지고 있습니다. 이 값을 사용하여 필드에 저장하거나 추가 로직을 수행할 수 있습니다.

 

2. 읽기 전용 프로퍼티

읽기 전용 프로퍼티는 get 접근자만을 가지며, 값의 수정은 허용하지 않습니다. 이는 주로 초기화 이후에 값이 변경되지 않아야 할 때 사용됩니다.

public class Employee
{
    private int _age;

    public int Age
    {
        get { return _age; }
    }

    public Employee(int age)
    {
        _age = age;
    }
}

 

3. 쓰기 전용 프로퍼티

쓰기 전용 프로퍼티는 set 접근자만을 가지며, 값의 읽기는 허용하지 않습니다. 이는 데이터를 외부로 노출시키지 않으면서도 값을 설정할 수 있게 합니다.

public class Employee
{
    private int _age;

    public int Age
    {
        set { _age = value; }
    }
}

 

4. 유효성 검사가 있는 프로퍼티

프로퍼티의 set 접근자에서 유효성 검사를 수행하여, 잘못된 값이 필드에 할당되는 것을 방지합니다.

public class Employee
{
    private int _age;

    public int Age
    {
        get { return _age; }
        set
        {
            if (value < 0 || value > 100)
                throw new ArgumentException("나이는 0과 100 사이여야 합니다.");
            _age = value;
        }
    }
}

 

5. 계산된 프로퍼티

계산된 프로퍼티는 다른 필드의 값을 기반으로 계산된 값을 반환합니다. 이들은 일반적으로 데이터를 저장하지 않고, 필요할 때 값을 계산합니다.

public class Rectangle
{
    public int Width { get; set; }
    public int Height { get; set; }

    public int Area
    {
        get { return Width * Height; }
    }
}

 

프로퍼티를 이용하는 이유

결론부터 말해 객체 무결성 보장을 위해서입니다.

 

객체 무결성이란?

  • 상태의 일관성:  객체의 속성이나 필드가 항상 예상 가능하고 정의된 규칙 또는 로직에 따라 유지되는 것을 의미합니다.
  • 데이터 보호와 유효성 검사:  객체의 필드나 속성에 잘못된 데이터가 할당되는 것을 방지하기 위해 적절한 유효성 검사를 수행합니다.
  • 캡슐화:  객체의 내부 데이터를 외부로부터 숨기고(숨겨진 내부 상태), 해당 데이터에 접근하거나 변경할 수 있는 메서드만을 외부에 노출시켜 데이터의 무결성을 보장합니다.

이해를 돕기 위해 예제와 함께 보시죠.

 

예시 코드: Account 클래스

public class Account
{
    private decimal balance;

    public decimal Balance
    {
        get { return balance; }
        private set
        {
            // [상태의 일관성] 잔액은 음수가 될 수 없다는 규칙을 적용
            if (value < 0)
            {
                throw new InvalidOperationException("잔액은 음수가 될 수 없습니다.");
            }
            balance = value;
        }
    }

    public void Deposit(decimal amount)
    {
        // [데이터 보호와 유효성 검사] 입금액은 양수여야 한다는 규칙을 적용
        if (amount <= 0)
        {
            throw new ArgumentException("입금액은 양수여야 합니다.");
        }
        Balance += amount;
    }

    public void Withdraw(decimal amount)
    {
        // [데이터 보호와 유효성 검사] 출금액은 양수여야 하며 잔액을 초과할 수 없다는 규칙을 적용
        if (amount <= 0)
        {
            throw new ArgumentException("출금액은 양수여야 합니다.");
        }
        if (amount > Balance)
        {
            throw new InvalidOperationException("잔액 부족");
        }
        Balance -= amount;
    }
}

 

객체 무결성 정리

  1. 상태의 일관성: Balance 프로퍼티의 set 접근자에서 잔액이 음수가 되지 않도록 검증합니다. 이는 Account 객체의 balance 필드가 항상 유효한 상태(음수가 아닌 값)를 유지하도록 보장합니다.
  2. 데이터 보호와 유효성 검사: Deposit과 Withdraw 메서드에서 입력된 금액이 유효한지(양수인지) 검사합니다. 또한,Withdraw에서는 잔액을 초과하는 출금을 시도하는 것을 방지합니다. 이러한 검사는 Account 객체의 데이터가 적절하게 보호되고 유효한 상태로 유지되도록 합니다.
  3. 캡슐화: balance 필드는 private 으로 선언되어 외부에서 직접 접근할 수 없습니다. 대신 Balance 프로퍼티와 Deposit, Withdraw 메서드를 통해 안전하게 데이터에 접근하고 변경할 수 있습니다. 이는 balance 필드의 내부 구현을 숨기고 필요한 기능만을 외부에 노출시킵니다.

이 예시는 객체 무결성이 어떻게 객체의 상태를 일관되고 안전하게 유지하는 데 도움이 되는지를 잘 보여줍니다. 객체의 메서드를 통해 데이터의 검증과 변경이 이루어지며, 이를 통해 객체의 상태가 예상된 방식으로만 변화하도록 보장합니다.

 

FAQ

필드를 사용하는 게 더 좋은 경우는 없나?

필드를 직접 사용하는 것이 더 적합한 경우도 있습니다. 이러한 상황은 주로 코드의 복잡성이 낮고, 성능이 중요한 상황, 또는 매우 단순한 로직을 처리할 때 발생합니다. 다음은 필드를 직접 사용하는 것이 더 유리할 수 있는 몇 가지 상황입니다:

 

1. 단순한 데이터 컨테이너

  • 예시: 데이터 구조가 단순한 POCO)(Plain Old CLR Object) 또는 DTO)(Data Transfer Object)와 같은 클래스에서는 필드를 직접 사용해도 괜찮을 수 있습니다. 이러한 클래스는 주로 데이터를 임시로 보관하거나 다른 계층 간에 데이터를 전달하는 데 사용됩니다.

 

2. 성능 최적화

  • 예시: 고성능이 필요한 애플리케이션, 특히 게임 개발이나 시스템 프로그래밍에서는 필드 접근의 오버헤드를 줄이기 위해 필드를 직접 사용할 수 있습니다. 이러한 상황에서는 성능이 가장 중요한 요소가 될 수 있으며, 프로퍼티 접근자의 추가 오버헤드가 문제가 될 수 있습니다.

 

3. 코드 간결성

  • 예시: 특히 프로토타입 제작이나 간단한 스크립트 작성 시에는 필드를 직접 사용하여 빠르고 간결하게 코드를 작성할 수 있습니다. 이 경우에는 프로퍼티의 추가적인 구현이 오히려 코드를 불필요하게 복잡하게 만들 수 있습니다.

 

4. 내부 구현에서의 사용

  • 예시: 클래스 내부에서만 사용되고 외부로 노출되지 않는 private 필드의 경우, 직접 접근을 사용하는 것이 간결하고 효율적일 수 있습니다. 이 경우 외부에서의 접근을 제어할 필요가 없으므로 프로퍼티의 이점이 크지 않을 수 있습니다.

 

결론

이처럼 필드를 직접 사용하는 것이 적절한 상황도 있지만, 대부분의 경우, 특히 복잡하거나 큰 규모의 소프트웨어 프로젝트에서는 캡슐화와 데이터 무결성을 위해 프로퍼티를 사용하는 것이 권장됩니다. 프로퍼티를 사용함으로써 데이터의 유효성 검증, 읽기 전용/쓰기 전용 접근 제어, 데이터 변경에 대한 로깅 및 이벤트 트리거링 등의 추가 기능을 손쉽게 구현할 수 있습니다.

  • 트위터 공유하기
  • 페이스북 공유하기
  • 카카오톡 공유하기
이 컨텐츠가 마음에 드셨다면 커피 한잔(후원) ☕

댓글