[로봇활용_10주차] C# 레코드(Record)

최윤호·2025년 10월 12일
post-thumbnail

레코드(Record): 불변의 마법

C#에서 class를 만들 때, 생성자와 속성(Property)들을 일일이 타이핑하곤 하죠.
그런데 "데이터 묶음이 필요할 뿐인데, 코드가 많지?"라고 생각해 본 적이 있나요?
C# 9.0부터 이런 고민을 한 방에 해결해 줄 레코드(Record)라는 기능이 도입되었습니다.
이번 글에서는 레코드가 무엇인지, 클래스와는 어떻게 다른지 알아보겠습니다!

1)레코드(Record)란?

레코드는 데이터를 캡슐화하기 위한 특별한 목적의 클래스(또는 구조체)입니다.
순수하게 데이터를 담는 용도의 객체(DTO, Data Transfer Object)로 사용됩니다.
핵심 철학은 "한 번 생성된 데이터는 변하지 않는다"라는 불변성(Immutability)에 있습니다.

기존 클래스로 DTO 만들기

먼저 기존 class로 데이터를 표현할 때의 모습을 봅시다.
이 클래스의 목적이 단지 '이름''나이'라는 데이터를 담아두는 것이라면,
클래스로 만들 때 해야 할 일이 은근히 많습니다.

// 레코드가 없던 시절...
public class PersonClass
{
    public string Name { get; set; }
    public int Age { get; set; }

    public PersonClass(string name, int age)
    {
        Name = name;
        Age = age;
    }

    // 데이터를 비교하려면 Equals, GetHashCode 등을 직접 재정의해야 함...
    // 객체 정보를 예쁘게 출력하려면 ToString을 직접 재정의해야 함...
    // ... 등등
}

단순히 데이터를 묶어두고 싶었을 뿐인데, 객체를 제대로 비교하고
출력하려면 이런 번거로운 코드(Boilerplate code)를 잔뜩 작성해야 했습니다.

레코드로 DTO 생성 시 간결함

똑같은 기능을 하는 Person을 레코드로 정의하면, 단 한 줄이면 충분합니다.

public record PersonRecord(string Name, int Age);

2)컴파일러가 주는 선물

record키워드 하나만으로 컴파일러는 다음과 같은 기능들을 자동으로 만들어줍니다.

1. 불변성(Immutability) 제공

레코드의 속성(Name, Age)은 내부적으로 init접근자를 사용합니다.
즉, 객체를 생성할 때 딱 한 번만 값을 할당할 수 있고, 그 이후에는 변경할 수 없습니다.

var person = new PersonRecord("김철수", 30);
// person.Age = 31; // 컴파일 오류! init-only 속성은 변경할 수 없습니다.

2. 값 기반의 동등성 비교

레코드의 가장 강력한 특징 중 하나입니다!

  • 클래스(class)는 참조(주소)가 같아야 true입니다. (참조 비교)
  • 레코드(record)는 내부 데이터의 값이 모두 같으면 true입니다. (값 비교)

[코드]

public record PersonRecord(string Name, int Age);

public class PersonClass
{
    public string Name { get; set; }
    public int Age { get; set; }

    public PersonClass(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

class Program
{
    static void Main()
    {
        // 클래스 비교
        var class1 = new PersonClass("홍길동", 25);
        var class2 = new PersonClass("홍길동", 25);
        Console.WriteLine($"클래스 동등성 비교: {class1 == class2}");

        // 레코드 비교
        var record1 = new PersonRecord("홍길동", 25);
        var record2 = new PersonRecord("홍길동", 25);
        Console.WriteLine($"레코드 동등성 비교: {record1 == record2}");
    }
}

[실행 결과]

클래스 동등성 비교: False
레코드 동등성 비교: True

3. 깔끔한 ToString() 재정의

디버깅할 때 Namespace.ClassName같은 의미 없는 문자열을 보지 않아도 됩니다.

[코드]

public record PersonRecord(string Name, int Age);

class Program
{
    static void Main()
    {
        var person = new PersonRecord("홍길동", 25);
        Console.WriteLine(person.ToString());
    }
}

[실행 결과]

PersonRecord { Name = 홍길동, Age = 25 }

4. 비파괴적 변형(with 표현식)

"불변이라면서요? 그럼 나이를 한 살 올리고 싶을 땐 어떡하죠?"

이럴 때 사용하는 것이 바로 with표현식입니다. 원본 객체는 그대로 둔 채,
특정 속성만 변경된 새로운 복사본 객체를 아주 간단하게 만들 수 있습니다.

[코드]

public record PersonRecord(string Name, int Age);

class Program
{
    static void Main()
    {
        var person1 = new PersonRecord("이영희", 20);

        // 'person1'을 기반으로 Age만 21로 바꾼 '새로운' 레코드를 생성
        var person2 = person1 with { Age = 21 };

        Console.WriteLine(person1); // (원본은 불변!)
        Console.WriteLine(person2); // (새로운 복사본)
    }
}

[실행 결과]

PersonRecord { Name = 이영희, Age = 20 }
PersonRecord { Name = 이영희, Age = 21 }

3)레코드 vs 클래스

레코드와 클래스는 사용 목적이 다릅니다.

구분레코드(record)클래스(class)
핵심 목적데이터 묶음 (What it is)행위와 상태를 가진 객체 (What it does)
불변성불변(Immutable)을 지향가변(Mutable)이 기본
동등성 비교값(Value) 기반참조(Reference) 기반
주 사용처DTO, API 모델, 읽기 전용 데이터 구조서비스, 로직 처리, 상태가 계속 변하는 객체

[간단한 기준]
"이 객체는 데이터 그 자체가 중요해!" → 레코드
"이 객체는 고유한 정체성을 갖고 상태가 변하며 일을 해야 해!" → 클래스

4)레코드 요약

C# 레코드는 데이터를 다루기 위한 현대적이고 강력한 방법입니다.

  • 간결한 선언: 단 한 줄로 데이터 객체를 정의할 수 있습니다.
  • 안전한 불변성: 데이터가 예기치 않게 변경되는 것을 막아줍니다.
  • 편리한 기능: 값 기반 비교, ToString(), with표현식 등을 자동으로 제공합니다.
profile
🚀 미래의 엔지니어를 꿈꾸는 훈련생의 기록 📝

0개의 댓글