튜플

황현중·2025년 12월 11일

1. 튜플이 뭐야? (한 줄 정의)

서로 다른 타입의 값 여러 개를 “작은 묶음”으로 만들어서, 한 변수 / 한 반환값으로 다루게 해주는 타입이다.

예를 들어,

  • 이름: string
  • 나이: int
  • 키: double

이런 정보를 위해 Person 클래스를 굳이 만들지 않고, 그냥 한 번에 묶어서 쓰고 싶다면 튜플을 사용할 수 있다.

(string Name, int Age, double Height) person = ("홍길동", 30, 175.5);

이렇게 하면 person 하나에 세 가지 값이 함께 들어간다.


2. C#에서 쓰는 두 종류의 튜플

C#에는 크게 두 세대의 튜플이 있다.

  1. 옛날 스타일 튜플: Tuple<T1, T2, ...>
    → 참조 타입(클래스), Item1, Item2로 접근
  2. 요즘 스타일 튜플: 값 튜플(ValueTuple)
    (int, string) 이런 식 문법, C# 7부터 본격 도입

현재는 보통 2번 ValueTuple 문법을 쓰는 것이 일반적이다.


3. 기본 문법 – 값 튜플(ValueTuple)

3-1. 만들기

var t = (10, "Hello");   // (int, string) 타입으로 추론됨

타입을 직접 명시해도 된다.

(int, string) t = (10, "Hello");

3-2. 꺼내서 쓰기 (Item1, Item2)

Console.WriteLine(t.Item1); // 10
Console.WriteLine(t.Item2); // Hello

이름 없이 사용할 경우 Item1, Item2와 같은 기본 이름으로 접근할 수 있다.


4. “이름 있는 튜플”이 훨씬 편하다

튜플 요소에는 직접 이름을 붙일 수 있다. 이 방식을 많이 사용한다.

(string Name, int Age) person = ("홍길동", 30);

Console.WriteLine(person.Name); // "홍길동"
Console.WriteLine(person.Age);  // 30

이렇게 쓰면 Item1, Item2 대신 의미 있는 이름으로 접근할 수 있어 코드 가독성이 좋아진다.

var와 함께 쓰는 경우도 흔하다.

var person = (Name: "홍길동", Age: 30);

Console.WriteLine(person.Name);
Console.WriteLine(person.Age);

5. 가장 많이 쓰는 용도 1 – 메서드에서 여러 값 반환

C# 메서드는 원래 반환값을 하나만 돌려줄 수 있다. 그래서 예전에는 다음과 같은 방법을 많이 썼다.

  • out 매개변수
  • ref 매개변수
  • 별도의 DTO/클래스 생성 후 반환

튜플을 쓰면 “반환값은 하나이지만, 그 안에 여러 개를 묶어서 보내는 것”이 가능해진다.

5-1. 예시: 나누기 결과 + 나머지를 동시에 반환

// 몫과 나머지를 한 번에 반환하는 메서드
static (int Quotient, int Remainder) Divide(int a, int b)
{
    int q = a / b;
    int r = a % b;
    return (q, r);  // 튜플로 반환
}

사용 예:

var result = Divide(17, 5);

Console.WriteLine(result.Quotient);  // 3
Console.WriteLine(result.Remainder); // 2

5-2. 더 깔끔하게: 분해(deconstruction) 문법

튜플을 그대로 받지 않고, 변수 두 개로 바로 쪼개서 받을 수도 있다.

var (q, r) = Divide(17, 5); // 튜플을 두 변수로 분해

Console.WriteLine(q); // 3
Console.WriteLine(r); // 2

실무에서 자주 쓰는 패턴이다.

  • 메서드는 (T1, T2, ...) 형태의 튜플을 반환하고
  • 호출하는 쪽에서는 var (x, y) = ...; 형태로 깔끔하게 받는다

6. 가장 많이 쓰는 용도 2 – LINQ 결과에서 임시로 묶기

LINQ를 사용할 때, “이 값과 저 값을 같이 들고 다니고 싶다”는 상황이 자주 나온다.

var numbers = new[] { 3, 5, 7, 10 };

// 숫자와 그 숫자의 제곱을 한꺼번에 묶어서 리스트로 만들기
var list = numbers
    .Select(n => (Number: n, Square: n * n))
    .ToList();

foreach (var item in list)
{
    Console.WriteLine($"값: {item.Number}, 제곱: {item.Square}");
}

여기서 (Number: n, Square: n * n) 부분이 튜플이다.

클래스를 만들기 애매한 간단한 상황에서, “필드 2~3개짜리 임시 DTO 역할”로 자주 사용된다.


7. 가장 많이 쓰는 용도 3 – 작은 데이터 묶음 운반용

이 정도면 굳이 클래스를 만들긴 애매한데… 싶은 상황에서 튜플이 딱 맞다.

(string Id, string Name, bool IsActive) userInfo = ("U001", "홍길동", true);

Console.WriteLine(userInfo.Id);
Console.WriteLine(userInfo.Name);
Console.WriteLine(userInfo.IsActive);

예를 들어:

  • 테스트 코드
  • 샘플 데이터
  • 함수 내부에서 잠깐 전달하는 중간 결과

이럴 때 튜플은 아주 간편한 자료 구조가 된다.

반대로, 아래처럼 의미 있는 도메인 개념일 경우:

  • Order, Customer, Product, Invoice

이런 것들은 별도의 클래스/레코드 타입을 만드는 편이 훨씬 좋다. 튜플은 말 그대로 “가벼운 묶음” 정도로 생각하면 된다.


8. 옛날 스타일 Tuple<T1, T2, ...>와의 차이

예전에는 이런 방식의 튜플을 썼다.

var t = Tuple.Create(10, "Hi");
Console.WriteLine(t.Item1); // 10
Console.WriteLine(t.Item2); // Hi
  • Tuple<T1, T2> : 참조 타입(클래스)
  • 값 튜플 (int, string) : struct(값 타입)

값 튜플은 C# 7부터 본격 도입되었고, 문법이 훨씬 깔끔하며 성능도 더 좋다.
요즘에는 특별한 이유가 없다면 Tuple<...>보다는 (int, string) 같은 값 튜플 스타일을 쓰는 것이 일반적이다.


9. 튜플 vs 클래스를 언제 쓸까?

간단하게 다음 기준으로 생각하면 편하다.

튜플을 쓰기 좋은 경우

  • 잠깐 쓰는 데이터 묶음
  • 메서드에서 반환값 여러 개를 한 번에 돌려주고 싶을 때
  • 테스트/샘플/로컬 데이터
  • 코드 한 함수 또는 한 파일 내부에서만 쓰는 임시 DTO 느낌

클래스를 만드는 게 좋은 경우

  • 해당 데이터가 도메인 개념인 경우 (Order, User, Customer 등)
  • 여러 군데에서 재사용되는 타입일 때
  • 필드/기능이 앞으로 더 늘어날 가능성이 있을 때
  • 의미를 명확하게 표현하고 싶을 때

10. 한 번에 정리

  • 튜플 = 여러 값을 하나로 묶는 작은 패키지
  • C# 7 이후 스타일 예:
    (int Id, string Name) user = (1, "홍길동");
    Console.WriteLine(user.Id);
    Console.WriteLine(user.Name);
  • 메서드에서 여러 값을 반환할 때:
    (int q, int r) Divide(int a, int b) => (a / b, a % b);
    var (q, r) = Divide(17, 5);
  • LINQ/컬렉션에서 간단한 DTO 역할로 매우 자주 사용된다.

튜플은 “클래스를 만들기엔 너무 무겁고, 값 하나로는 부족한 상황”에서 빛을 발한다.
가볍게 여러 값을 묶고 싶을 때, 먼저 떠올려볼 만한 도구라고 보면 된다.

0개의 댓글