조아마시

쓸모 있는 상세페이지 만들기

웹개발/typescript

[타입스크립트] 튜플 타입

joamashi 2024. 8. 30. 22:00

*튜플(Tuple)**은 타입스크립트에서 길이와 각 요소의 타입이 고정된 배열을 의미합니다. 즉, 미리 정해진 순서대로 다양한 타입의 값을 가질 수 있는 특별한 배열 형태라고 생각하면 됩니다.

왜 튜플을 사용할까요?

  • 명확한 데이터 구조 표현: 각 요소의 타입을 명시적으로 지정하여 코드 가독성을 높이고, 예상치 못한 오류를 방지할 수 있습니다.
  • 함수 인자 및 반환값 정의: 함수의 입력 값과 출력 값의 형태를 정확하게 지정하여 함수의 동작을 명확히 하고, 타입 안전성을 높일 수 있습니다.

튜플 사용 예시

// 튜플 선언
let person: [string, number] = ['Alice', 30]; // 이름(문자열), 나이(숫자)

// 튜플 요소 접근
console.log(person[0]); // Alice
console.log(person[1]); // 30

// 튜플 타입 사용
function greet(person: [string, number]) {
    console.log(`Hello, ${person[0]}! You are ${person[1]} years old.`);
}

greet(['Bob', 25]); // Hello, Bob! You are 25 years old.

다양한 튜플 예시

  • 좌표: [number, number]
  • 색상: [number, number, number] (RGB 값)
  • 사용자 정보: [string, number, boolean] (이름, 나이, 활성 상태)
  • 함수 반환값: [number, string] (값, 오류 메시지)

튜플의 장점

  • 타입 안전성: 잘못된 타입의 값을 할당하려고 하면 컴파일 시 오류가 발생합니다.
  • 코드 가독성 향상: 변수의 의미를 명확하게 나타낼 수 있습니다.
  • 함수의 입력 및 출력 값 제한: 함수의 동작을 예측 가능하게 만들어줍니다.

튜플의 단점

  • 유연성 부족: 요소의 개수나 타입을 변경하기 어렵습니다.
  • 복잡한 데이터 구조 표현에는 한계가 있을 수 있습니다.

더 자세한 예시

// 튜플 안에 튜플
let address: [string, [number, number]] = ['Seoul', [123, 456]];

// 튜플과 rest 파라미터
function sum(...numbers: [number, ...number[]]) {
    return numbers.reduce((total, n) => total + n, 0);
}

// 제네릭과 튜플
interface Tuple<T, U> {
    0: T;
    1: U;
}

let myTuple: Tuple<string, number> = ['hello', 42];

다양한 선언 방식과 활용

튜플 타입은 타입스크립트에서 데이터의 구조를 명확하게 정의하고, 코드의 안정성을 높이는 데 유용한 도구입니다. 앞서 기본적인 튜플 선언 방식을 살펴보았고, 이번에는 좀 더 다양하고 심화된 튜플 선언 방식과 활용 예시를 살펴보겠습니다.

1. 읽기 전용 튜플 (Readonly Tuple)

튜플의 값을 변경할 수 없도록 만들어 데이터의 무결성을 보장할 수 있습니다.

let readonlyTuple: readonly [string, number] = ['Alice', 30];
// readonlyTuple[0] = 'Bob'; // 에러: 읽기 전용이므로 값 변경 불가

2. 옵셔널 요소 (Optional Element)

튜플의 마지막 요소에만 ?를 붙여 옵셔널로 만들 수 있습니다.

let optionalTuple: [string, number?] = ['Alice', 30];
optionalTuple = ['Bob']; // 두 번째 요소는 생략 가능

3. Rest 파라미터와 함께 사용

튜플의 마지막 요소를 rest 파라미터로 지정하여 가변적인 길이의 튜플을 만들 수 있습니다.

let restTuple: [string, ...number[]] = ['Alice', 1, 2, 3];

4. 제네릭과 함께 사용

튜플의 요소 타입을 일반화하여 재사용성을 높일 수 있습니다.

interface Tuple<T, U> {
    0: T;
    1: U;
}

let genericTuple: Tuple<string, number> = ['hello', 42];

5. 인터페이스와 함께 사용

튜플을 인터페이스의 속성으로 사용하여 더 복잡한 데이터 구조를 표현할 수 있습니다.

interface Person {
    name: string;
    age: number;
    address: [string, number]; // 튜플을 속성으로 사용
}

6. 튜플 타입 가드

typeof 연산자를 사용하여 값이 특정 튜플 타입인지 확인할 수 있습니다.

function isPersonTuple(obj: any): obj is [string, number, boolean] {
    return Array.isArray(obj) && obj.length === 3 && typeof obj[0] === 'string' && typeof obj[1] === 'number' && typeof obj[2] === 'boolean';
}

7. 스프레드 연산자와 함께 사용

튜플을 다른 배열에 펼쳐서 사용할 수 있습니다.

let arr1 = [1, 2];
let arr2: [string, ...number[]] = ['hello', ...arr1];

튜플 활용 예시

  • 함수의 인자와 반환값: 함수의 입력 값과 출력 값의 형태를 정확하게 지정하여 함수의 동작을 명확히 하고, 타입 안전성을 높일 수 있습니다.
  • 객체의 속성: 객체의 속성을 튜플로 표현하여 데이터 구조를 명확하게 정의할 수 있습니다.
  • React 컴포넌트의 props: 컴포넌트의 props를 튜플로 정의하여 props의 타입을 강제하고, 오류를 줄일 수 있습니다.

더 다양한 선언 방식과 활용

앞서 튜플 타입의 다양한 선언 방식과 활용 예시를 살펴보았습니다. 이번에는 좀 더 심화된 내용과 실제 개발에서 자주 사용되는 패턴들을 중심으로 튜플 타입을 다루어 보겠습니다.

1. 인덱스 시그니처와 함께 사용

튜플의 요소에 접근할 때 인덱스 시그니처를 사용하여 더 유연하게 타입을 지정할 수 있습니다.

interface NamedTuple {
  [index: number]: string;
}

let names: NamedTuple = ['Alice', 'Bob', 'Charlie'];

2. 조건부 타입과 함께 사용

조건부 타입을 활용하여 튜플의 요소에 대한 더 복잡한 타입 검사를 수행할 수 있습니다.

type IsStringArray<T> = T extends (string | number)[] ? T extends string[] ? true : false : false;

type StringTuple = IsStringArray<typeof names> extends true ? typeof names : never;

3. 맵드 타입과 함께 사용

맵드 타입을 사용하여 튜플의 모든 요소에 동일한 변환을 적용할 수 있습니다.

type ReadonlyTuple<T> = ReadonlyArray<T>;

type ReadonlyStringTuple = ReadonlyTuple<string>;

4. 튜플 유틸리티 타입

자주 사용되는 튜플 관련 작업을 위한 유틸리티 타입을 만들어 사용할 수 있습니다.

type TupleToObject<T extends readonly any[]> = {
  [K in keyof T]: T[K];
};

5. 튜플과 제네릭의 조합

튜플과 제네릭을 함께 사용하여 더욱 강력하고 재사용 가능한 타입을 만들 수 있습니다.

type TupleToObject<T extends readonly any[]> = {
  [K in keyof T]: T[K];
};

function mapTuple<T extends readonly any[], U>(
  tuple: T,
  mapper: (value: T[number], index: number) => U
): U[] {
  return tuple.map(mapper);
}

6. 실제 개발에서의 활용 예시

  • React 컴포넌트 props: 컴포넌트의 props를 튜플로 정의하여 props의 타입을 강제하고, 오류를 줄일 수 있습니다.
  • Redux action creators: 액션 생성 함수의 인자와 반환값을 튜플로 정의하여 액션의 형태를 명확하게 하고, 타입 안전성을 높일 수 있습니다.
  • API 응답 데이터 모델링: API에서 받아오는 데이터의 구조를 튜플로 모델링하여 데이터 처리를 용이하게 할 수 있습니다.
728x90