Menu
Coddy logo textTech

불변성 패턴

Coddy C# 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 70개 중 34번째.

불변성은 캡슐화를 논리적 극한으로 끌어올립니다. 일단 객체가 생성되면 결코 변할 수 없습니다. 이는 데이터가 예기치 않게 수정되는 것을 걱정할 필요가 없기 때문에 전체 범주의 버그를 제거합니다.

불변 클래스를 만드는 가장 간단한 방법은 모든 값을 설정하는 생성자와 함께 readonly 필드를 사용하고, setter를 제공하지 않는 것입니다:

public class Point
{
    public readonly int X;
    public readonly int Y;
    
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

Point가 생성되면, 그 좌표는 변경될 수 없습니다. 하지만 수정된 버전이 필요하다면 어떻게 해야 할까요? 기존 객체를 변경하는 대신, 새로운 객체를 반환합니다:

public Point Move(int deltaX, int deltaY)
{
    return new Point(X + deltaX, Y + deltaY);
}

원본 포인트는 변경되지 않은 상태로 유지되는 반면, 업데이트된 값을 가진 새로운 포인트를 얻게 됩니다. 이 패턴은 .NET 전반에서 사용됩니다. 예를 들어, 문자열(strings)은 불변(immutable)입니다. ToUpper()를 호출하면 원본을 수정하는 대신 새로운 문자열을 얻게 됩니다.

불변 객체는 생성 후 어떤 코드도 이를 수정할 수 없으므로 본질적으로 스레드 안전(thread-safe)합니다. 또한 상태가 예기치 않게 변경되지 않을 것임을 알기 때문에 추론하기 더 쉽습니다.

challenge icon

챌린지

쉬움

생성 후 절대 변경되지 않도록 설계된 객체를 보여주는 불변(immutable) 머니 시스템을 구축해 보겠습니다. 모든 연산이 원본을 수정하는 대신 새로운 객체를 반환하는 Money 클래스를 만들게 됩니다. 이 패턴은 코드를 더 안전하고 이해하기 쉽게 만들어 줍니다.

코드는 두 개의 파일로 구성됩니다:

  • Money.cs: Finance 네임스페이스 안에 불변 Money 클래스를 생성합니다. 이 클래스는 다음을 포함해야 합니다:
    • 두 개의 readonly 필드: Amount (decimal) 및 Currency (string)
    • 두 값을 설정하는 생성자 - 한 번 생성되면 Money 객체는 절대 변경될 수 없습니다.
    • 증가된 금액(동일한 통화)을 가진 새로운 Money 객체를 반환하는 Add(decimal value) 메서드
    • 감소된 금액(동일한 통화)을 가진 새로운 Money 객체를 반환하는 Subtract(decimal value) 메서드
    • 금액에 환율을 곱하고 새로운 통화를 적용한 새로운 Money 객체를 반환하는 Convert(string newCurrency, decimal rate) 메서드
    • "{Amount} {Currency}" 형식의 문자열을 반환하는 GetDisplay() 메서드
    주의: 이 메서드들 중 어느 것도 원본 객체를 수정해서는 안 됩니다. 모두 완전히 새로운 Money 인스턴스를 반환해야 합니다!
  • Program.cs: 메인 파일에서 불변성이 실제로 작동하는 모습을 보여줍니다. 초기 Money 객체를 생성한 다음, 원본과 결과 모두에 대한 참조를 유지하면서 연산을 수행합니다. 이를 통해 여러 번의 연산 후에도 원본이 절대 변하지 않음을 증명합니다.

네 가지 입력을 받게 됩니다:

  • 초기 금액 (decimal)
  • 초기 통화 (string)
  • 추가할 금액 (decimal)
  • EUR로의 환율 (decimal)

출력은 다음 형식으로 인쇄하십시오:

Original: {GetDisplay()}
After add: {GetDisplay()}
Original unchanged: {GetDisplay()}
Converted: {GetDisplay()}

예를 들어, 입력값이 100.00, USD, 50.00, 0.85인 경우 출력은 다음과 같아야 합니다:

Original: 100.00 USD
After add: 150.00 USD
Original unchanged: 100.00 USD
Converted: 127.50 EUR

Add()를 호출한 후에도 원본 Money 객체가 여전히 100.00 USD를 표시하는 방식에 주목하세요. 이것이 바로 불변성입니다! Add() 메서드는 원본을 건드리지 않고 150.00 USD를 가진 완전히 새로운 객체를 생성했습니다. 환전은 더하기 연산의 결과에 대해 수행됩니다.

치트 시트

불변성(Immutability)은 객체가 한 번 생성되면 그 상태를 절대 변경할 수 없음을 의미합니다. 이는 예기치 않은 데이터 수정과 관련된 버그를 제거합니다.

생성자가 있고 세터(setter)가 없는 readonly 필드를 사용하여 불변 클래스를 생성합니다:

public class Point
{
    public readonly int X;
    public readonly int Y;
    
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

불변 객체를 수정하려면 기존 객체를 변경하는 대신 새로운 인스턴스를 반환합니다:

public Point Move(int deltaX, int deltaY)
{
    return new Point(X + deltaX, Y + deltaY);
}

불변 객체의 장점:

  • 생성 후 어떤 코드도 수정할 수 없으므로 설계상 스레드 안전(Thread-safe)합니다.
  • 상태가 예기치 않게 변하지 않으므로 코드를 파악하기가 더 쉽습니다.
  • .NET 전반에서 사용됩니다 (예: 문자열(string)은 불변입니다).

직접 해보기

using System;
using Finance;

class Program
{
    public static void Main(string[] args)
    {
        // 입력 읽기
        decimal initialAmount = Convert.ToDecimal(Console.ReadLine());
        string initialCurrency = Console.ReadLine();
        decimal amountToAdd = Convert.ToDecimal(Console.ReadLine());
        decimal conversionRate = Convert.ToDecimal(Console.ReadLine());

        // TODO: 초기 금액과 통화로 초기 Money 객체 생성

        // TODO: GetDisplay()를 사용하여 원본 금액 출력

        // TODO: 원본에 Add()를 호출하고 결과를 새 변수에 저장

        // TODO: 더한 후의 결과를 GetDisplay()를 사용하여 출력

        // TODO: 원본이 변경되지 않았음을 증명하기 위해 원본을 다시 출력

        // TODO: 환율을 사용하여 더해진 결과를 EUR로 변환

        // TODO: 변환된 금액을 GetDisplay()를 사용하여 출력
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨