튜플 ( Tuple )
게임을 만들다보면 여러 값을 한 번에 묶여서 다뤄야 하는 경우가 있다.
예를 들어 캐릭터의 좌표라면 x , y 값을 함께 다뤄야 하고, 체력 정보라면 hp , maxHp 값을 함께 전달해야 할 수 있다.
이럴 때마다 클래스를 만들거나 구조체를 정의하면 코드가 복잡해질 수 있다.
반대로 값들을 따로따로 전달하면 관련 있는 데이터라는 의미가 약해진다.
이런 상황에서 간단하게 여러 값을 하나로 묶어 사용할 수 있는 기능이 바로 튜플 ( Tuple )이다.
튜플은 여러 개의 값을 하나의 데이터처럼 묶어서 사용하는 자료형이다.
서로 다른 타입의 값도 함께 묶을 수 있으며, 간단한 데이터 묶음을 만들 때 클래스나 구조체를 새로 정의하지 않고도 사용할 수 있는 가벼운 방법이라고 볼 수 있다.
예를 들어 다음과 같은 값들을 하나로 묶을 수 있다.
- 캐릭터 위치 : ( x , y )
- 체력 정보 : ( hp , maxHp )
- 아이템 정보 : ( id , name , price )
- 메서드 결과 : ( success , message )
C# 에서는 두 가지 방식의 튜플을 제공한다.
- 기존 방식인 System.Tupl
- C# 7.0 이후부터 권장되는 ValueTuple
보통 최신 C# 에서는 사용하기 편하고 가독성이 좋은 ValueTuple 을 더 많이 사용한다.
ValueTuple 사용법
ValueTuple 은 C# 7.0 부터 도입된 튜플 방식이다.
기존 System.Tuple 보다 사용하기 편하고 성능 면에서도 더 유리한 방식이다.
ValueTuple 은 여러 값을 괄호 () 로 묶어서 만들 수 있다.
기본적인 튜플 생성
var playerInfo = (100, 50, "Warrior");
▲ 플레이어 정보를 하나의 튜플로 묶음
각 값은 기본적으로 Item1 , Item2 , Item3 같은 이름으로 접근할 수 있다.
Console.WriteLine(playerInfo.Item1); // 체력
Console.WriteLine(playerInfo.Item2); // 마나
Console.WriteLine(playerInfo.Item3); // 직업
다만 Item1 , Item2 처럼 접근하면 각 값이 무엇을 의미하는지 바로 알기 어렵다.
그래서 튜플을 사용할 때는 가능하면 값에 이름을 붙여 사용하는 방식이 더 좋다.
명시적 타입 지정
튜플은 var 로 간단하게 만들 수도 있지만, 직접 타입과 이름을 지정할 수도 있다.
(int hp, int mp, string job) characterData = (100, 50, "Mage");
이렇게 작성하면 각 값에 hp , mp , job 이라는 이름이 생긴다.
Console.WriteLine(characterData.hp);
Console.WriteLine(characterData.mp);
Console.WriteLine(characterData.job);
이 방식은 Item1 , Item2 보다 코드의 의미가 훨씬 명확하다.
예를 들어 characterData.Item1 보다 characterData.hp 가 체력 값을 사용하고 있다는 것을 더 쉽게 알 수 있다.
튜플에 이름 붙이기
선언할 때 이름 지정
튜플은 선언할 때 각 값에 이름을 붙일 수 있다.
var position = (x: 10, y: 20);
이렇게 하면 position.Item1 , position.Item2 대신 position.x , position.y 로 접근할 수 있다.
Console.WriteLine(position.x);
Console.WriteLine(position.y);
좌표처럼 의미가 분명한 값은 이름을 붙이면 가독성이 좋아진다.
타입과 함께 이름 지정
튜플은 타입을 명시하면서 이름도 함께 지정할 수 있다.
(int health, int mana, string className) playerStat = (100, 50, "Paladin");
이 경우 playerStat.health , playerStat.mana , playerStat.className 처럼 접근할 수 있다.
이 방식은 데이터의 의미가 명확해서, 나중에 코드를 다시 볼 때도 이해하기 쉽다.
함수에서 튜플 사용하기
튜플은 함수에서 여러 값을 한 번에 반환할 때 유용하다.
보통 함수는 하나의 값만 반환하지만, 튜플을 사용하면 여러 값을 묶어서 반환할 수 있다.
예를 들어 플레이어의 현재 상태를 가져올 때 체력, 마나, 레벨을 한 번에 반환할 수 있다.
(int hp, int mp, int level) GetPlayerInfo()
{
return (80, 35, 12);
}
var playerInfo = GetPlayerInfo();
Console.WriteLine($"체력: {playerInfo.hp}");
Console.WriteLine($"마나: {playerInfo.mp}");
Console.WriteLine($"레벨: {playerInfo.level}");
위 코드에서 GetPlayerInfo() 는 hp , mp , level 세 값을 하나의 튜플로 묶어서 반환한다.
반환받은 값은 playerInfo.hp , playerInfo.mp , playerInfo.level 처럼 이름으로 접근할 수 있다.
튜플 분해 ( Deconstruction )
분해해서 개별 변수에 할당
튜플 분해는 튜플에 들어 있는 값을 각각의 변수로 나누어 받는 기능이다.
(string itemName, int price, bool isEquipped) itemData = ("Iron Sword", 1500, false);
(string name, int cost, bool equipped) = itemData;
Console.WriteLine($"아이템 이름: {name}");
Console.WriteLine($"가격: {cost}");
Console.WriteLine($"장착 여부: {equipped}");
위 코드에서는 itemData 튜플에 들어 있던 값들이 name , cost , equipped 변수에 각각 나누어 저장된다.
즉, 아래처럼 접근하지 않아도 된다.
Console.WriteLine(itemData.itemName);
Console.WriteLine(itemData.price);
Console.WriteLine(itemData.isEquipped);
튜플 분해를 사용하면 필요한 값을 개별 변수처럼 바로 사용할 수 있다.
일부만 분해하고 나머지는 무시
튜플을 분해할 때 모든 값이 항상 필요한 것은 아니다.
필요 없는 값은 _ 를 사용해서 무시할 수 있다.
(string monsterName, int attackPower, int rewardGold) monsterData =
("Slime", 8, 20);
(string name, _, int gold) = monsterData;
Console.WriteLine($"몬스터 이름: {name}");
Console.WriteLine($"보상 골드: {gold}");
위 코드에서는 attackPower 값은 사용하지 않기 때문에 _ 로 무시했다.
튜플을 언제 사용하면 좋을까?
튜플은 간단한 데이터 묶음이 필요할 때 사용하기 좋다.
예를 들어 잠깐 사용할 데이터인데, 이를 위해 별도의 클래스나 구조체를 만들기에는 과다하다고 느껴질 때 튜플을 사용할 수 있다.
대표적으로 다음과 같은 상황에서 유용하다.
- 간단한 값들을 임시로 묶어서 사용해야 할 때
- 함수에서 여러 값을 한 번에 반환해야 할 때
- 별도의 클래스나 구조체를 만들 정도로 복잡하지 않을 때
- 한 메서드 내부처럼 제한된 범위에서만 사용할 때
예를 들어 플레이어의 현재 상태를 간단히 반환하는 경우라면 튜플이 잘 어울린다
(int hp, int mp) GetPlayerResource()
{
return (80, 30);
}
이 정도의 간단한 데이터는 클래스를 따로 만드는 것보다 튜플로 처리하는 편이 더 간결하다.
사용하지 않는 것이 좋은 경우
튜플은 편리하지만, 모든 상황에 적합한 것은 아니다.
데이터가 복잡하거나, 여러 곳에서 반복해서 사용되는 구조라면 튜플보다 클래스나 구조체를 사용하는 것이 좋다.
특히 다음과 같은 경우에는 튜플 사용을 피하는 편이 좋다.
- 데이터에 포함되는 값이 너무 많을 때
- 해당 데이터가 여러 클래스나 시스템에서 반복적으로 사용될 때
- 데이터 자체에 메서드나 기능이 필요할 때
- 외부 API 나 인터페이스처럼 명확한 구조가 필요한 곳에서 사용할 때
예를 들어 몬스터의 이름, 체력, 공격력, 방어력, 이동속도, 보상 골드 등을 계속 관리해야 한다면 튜플보다는 클래스나 구조체가 더 적합하다.
public struct MonsterInfo
{
public string name;
public int hp;
public int attack;
public int defense;
public int rewardGold;
}
구버전 System.Tuple 과 ValueTuple 의 차이점
C# 7.0 이전에는 주로 System.Tuple 을 사용했다.
하지만 C# 7.0 이후부터는 ValueTuple 을 더 많이 사용한다.
두 방식의 가장 큰 차이는 참조 타입인지 값 타입인지, 그리고 이름 지정이 편한지이다.
System.Tuple
System.Tuple 은 구버전 튜플 방식이며, 참조 타입 클래스이다.
값을 꺼낼 때에는 Item1, Item2 같은 이름을 사용한다.
Tuple<int, int, string> oldTuple = Tuple.Create(100, 50, "Warrior");
Console.WriteLine(oldTuple.Item1);
Console.WriteLine(oldTuple.Item2);
Console.WriteLine(oldTuple.Item3);
이 방식은 값에 의미 있는 이름을 붙이기 어렵기 때문에 각각 무엇을 뜻하는지 코드를 보고 바로 알기 힘들다.
ValueTuple
ValueTuple 은 C# 7.0 이후에 주로 사용하는 튜플 방식이며 , 값 타입 구조체이다.
값에 이름을 붙일 수 있어 코드의 의미가 더 명확해진다.
(int health, int mana, string job) playerData = (100, 50, "Warrior");
Console.WriteLine(playerData.health);
Console.WriteLine(playerData.mana);
Console.WriteLine(playerData.job);
playerData.Item1 보다 playerData.health 가 훨씬 읽기 쉽다.
그래서 최신 C# 에서는 보통 ValueTuple 사용을 권장한다.
구 튜플의 불변 특성
구버전 튜플인 System.Tuple 은 한 번 값을 넣고 만들면, 내부 값을 직접 수정할 수 없다.
즉, 불변 Immutable 객체이다.
Tuple<int, int> oldPosition = Tuple.Create(10, 20);
// oldPosition.Item1 = 15;
// System.Tuple은 내부 값을 직접 수정할 수 없다.
값을 바꾸고 싶다면 기존 튜플을 수정하는 것이 아니라, 새로운 튜플 객체를 다시 만들어서 대입해야 한다.
oldPosition = Tuple.Create(15, 20);
정리
튜플은 여러 값을 간단하게 하나로 묶어서 사용할 수 있는 기능이다.
복잡한 클래스나 구조체를 만들기에는 과하지만, 여러 값을 함께 다루어야 할 때 튜플을 사용하면 코드를 더 간결하게 만들 수 있다.
특히 함수에서 여러 값을 반환하거나, 잠깐 사용할 간단한 데이터 묶음을 만들 때 유용하다.
다만 데이터가 복잡하거나 여러 곳에서 계속 사용된다면, 튜플보다는 클래스나 구조체로 명확한 타입을 정의하는 것이 좋다.
C# 에서는 구버전 방식은 System.Tuple 도 사용할 수 있지만, 최신 코드에서는 보통 ValueTuple 사용을 권장한다.
참고자료
튜플 형식 : https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/builtin-types/value-tuples
튜플 클래스 : https://learn.microsoft.com/ko-kr/dotnet/api/system.tuple?view=netcore-3.1
'⭐C Sharp > 07. 메서드(함수)' 카테고리의 다른 글
| in ( 매개변수 키워드 , 참조 전달 ) (0) | 2025.09.28 |
|---|---|
| out ( 매개변수 키워드 , 참조 전달 ) (0) | 2025.09.28 |
| ref ( 매개변수 키워드 , 참조 전달 ) (0) | 2025.09.28 |
| 함수 ( Function ) (0) | 2025.09.07 |
| Convert 를 이용한 변환 (0) | 2025.09.07 |