typescript
같은 로직을 여러 번 반복해서 작성하다가 '이거 더 깔끔하게 만들 방법 없을까?'하고 고민하거나, 코드를 작성하면서 타입 안전성을 유지하며 재사용 가능한 방법이 없을까 고민하신 경험이 한 번쯤은 있으실 거라 생각합니다.
바로 이럴 때 TypeScript의 Generics(제네릭)이 빛을 발합니다. Generics는 코드의 재사용성을 극대화하고 타입 안정성까지 확실하게 보장해주는 강력한 도구입니다.
Generics는 간단히 말해서 "타입을 인수로 받는 함수 또는 클래스"라고 할 수 있습니다. 마치 JavaScript에서 매개변수로 데이터를 전달하듯, TypeScript에서는 함수나 클래스가 타입을 매개변수처럼 받을 수 있도록 만들어줍니다. 이렇게 하면 코드 작성 시점에 타입을 미리 지정하지 않고, 사용하는 순간에 원하는 타입을 지정하여 더 유연하고 안전한 코드를 작성할 수 있습니다.
가장 기본적인 예시를 함께 보겠습니다.
// Generics를 사용하지 않은 예시
function identity(value: any): any {
return value
}
const result = identity('hanpy')
console.log(result) // hanpy (하지만 타입 안정성 없음!)
위 코드처럼 any를 사용하면 타입 안정성을 보장받기 어렵습니다. 이를 Generics로 해결하면 이렇게 됩니다.
// Generics를 사용한 예시
function identity<T>(value: T): T {
return value
}
const result1 = identity<string>('hanpy') // T는 string으로 지정됨
const result2 = identity<number>(90) // T는 number로 지정됨
console.log(result1) // hanpy
console.log(result2) // 90
이제 함수 호출 시점에 타입이 명확해지고, 반환 타입도 그에 맞게 정확히 지정됩니다.
Generics를 사용하는 가장 큰 이유는 두 가지입니다.
실제로 API 호출이나 배열을 처리할 때 Generics는 매우 유용하게 쓰입니다.
조금 더 실무에서 자주 등장하는 예시를 살펴보겠습니다. API 데이터를 처리할 때 유용하게 Generics를 사용할 수 있습니다.
// API 응답을 처리하는 함수
async function fetchData<T>(url: string): Promise<T> {
const response = await fetch(url)
return await response.json()
}
// 사용자 정보를 가져올 때의 타입 정의
interface User {
id: number
name: string
email: string
}
// 함수 사용 시 타입 지정
fetchData<User>('SAMPLE_API')
.then((user) => {
console.log(user.name) // 타입이 User로 명확히 지정되어 자동 완성 및 타입 안전성 제공
})
.catch((err) => {
console.error(err)
})
이 예제에서 함수는 API 호출 로직을 한 번만 구현하고, 필요한 시점에 타입만 지정해주면 됩니다. 덕분에 코드는 간결해지고 타입 안정성도 확보됩니다.
Generics는 함수뿐 아니라 클래스에서도 매우 유용합니다. 클래스를 타입에 따라 재사용할 때 Generics를 적극 활용하면 좋습니다.
class Box<T> {
private content: T
constructor(content: T) {
this.content = content
}
getContent(): T {
return this.content
}
}
const numberBox = new Box<number>(123)
const stringBox = new Box<string>('Hanpy Blog')
console.log(numberBox.getContent()) // 123
console.log(stringBox.getContent()) // "Hanpy Blog"
이처럼 클래스에 타입을 지정하면, 각 인스턴스마다 명확한 타입 정보를 가지고 동작하기 때문에, 컴파일 단계에서 타입 안정성이 완벽히 보장됩니다.
Generics를 사용할 때 주의해야 할 점도 있습니다. 타입 매개변수를 지나치게 많이 사용하면 코드 가독성이 떨어질 수 있습니다. 따라서 명확히 필요한 경우에만 Generics를 사용하고, 너무 복잡해지지 않게 주의하는 것이 좋습니다.
또한, 타입 매개변수 이름은 관례상 다음과 같은 이름을 주로 사용합니다.
이러한 관례를 잘 따르는 것이 협업 시 코드 가독성 향상에 도움을 줍니다.