이펙티브 타입스크립트: 5장 요약 및 핵심 정리

2025. 1. 29. 22:08Programming Language/Typescript

반응형

타입스크립트에서 any 타입은 유용하면서도 조심해야 할 요소입니다. any를 잘못 사용하면 타입스크립트의 강력한 타입 시스템이 무용지물이 될 수 있지만, 적절히 사용하면 코드의 유연성을 높일 수도 있습니다. 이번 글에서는 any 타입을 효과적으로 활용하는 방법을 정리해보겠습니다.

1. any 타입은 가능한 한 좁은 범위에서만 사용하기

any 타입은 타입 검사를 피하는 역할을 하기 때문에 광범위하게 사용하면 코드의 안정성이 떨어질 수 있습니다. 특히 함수의 반환 타입으로 any를 사용하면 이후 코드에서 예측 불가능한 오류가 발생할 가능성이 높아집니다.

function fetchData(): any {
  return JSON.parse('{ "name": "Henry", "age": 30 }');
}

const user = fetchData();
console.log(user.toUpperCase()); // 런타임 오류 발생 가능

위 코드에서 fetchData 함수는 any를 반환하므로, user의 정확한 타입을 보장할 수 없습니다. 따라서 any를 사용할 때는 최소한으로 범위를 제한해야 합니다.

2. any를 구체적인 타입으로 변형하기

가능한 한 any를 직접 사용하기보다는 보다 구체적인 타입으로 변형하여 사용해야 합니다.

function fetchUser(): { name: string; age: number } {
  return JSON.parse('{ "name": "Henry", "age": 30 }');
}

const user = fetchUser();
console.log(user.name.toUpperCase()); // 정상 동작

위 코드처럼 반환 타입을 명확하게 지정하면 타입스크립트가 정확한 타입 검사를 수행할 수 있습니다.

3. 함수 내부에서만 any를 사용하기

어쩔 수 없이 any를 사용해야 하는 경우, 함수 내부에서만 사용하고 함수의 반환 타입은 정확하게 지정하는 것이 좋습니다.

function processInput(input: any): string {
  if (typeof input === 'string') {
    return input.toUpperCase();
  } else {
    return String(input);
  }
}

위 예제에서는 any 타입의 값을 함수 내부에서만 처리하고, 외부로는 string 타입을 반환하도록 보장하고 있습니다.

4. any의 암시적 진화를 이해하기

암시적으로 할당되는 any 타입은 타입스크립트가 타입을 추론하는 방식에 영향을 미칠 수 있습니다. 따라서 명시적인 타입 지정을 활용하는 것이 좋습니다.

let data: any;
data = 'Hello';
data = 42;

위처럼 any 타입을 암시적으로 사용하면 타입이 예측 불가능해질 수 있습니다. 대신 명시적인 타입을 지정하여 코드의 안정성을 유지하는 것이 바람직합니다.

5. any 대신 unknown 사용하기

unknown 타입은 any와 유사하지만 더 안전합니다. unknown 타입을 사용하면 타입 검사를 강제할 수 있습니다.

function parseData(data: unknown) {
  if (typeof data === 'string') {
    return data.toUpperCase();
  } else {
    throw new Error('Invalid data type');
  }
}

위 코드처럼 unknown 타입을 사용하면 타입 체크를 강제할 수 있어 예상치 못한 오류를 방지할 수 있습니다.

6. 몽키패치보다는 안전한 타입으로 사용하기

몽키패치(Monkey Patching)는 기존 객체나 라이브러리의 프로토타입을 변경하는 기법으로, 특정 기능을 빠르게 확장할 수 있지만, 예상치 못한 부작용을 초래할 수 있습니다. 타입스크립트에서는 보다 안전한 방식으로 기능을 확장하는 것이 중요합니다.

1) 타입 안전성을 유지하는 확장 방법

타입스크립트에서는 몽키패치를 대체할 수 있는 여러 가지 방법이 있습니다. 예를 들어, 기존 객체의 타입을 확장하는 경우 인터페이스 병합(Interface Merging)을 활용하는 것이 안전한 방법입니다.

interface User {
  name: string;
  age: number;
}

interface User {
  isAdmin?: boolean;
}

const user: User = { name: "Henry", age: 30, isAdmin: true };
console.log(user.isAdmin); // true

위처럼 타입스크립트의 인터페이스 병합을 사용하면 기존 타입을 안전하게 확장할 수 있습니다.

2) any를 사용하지 않는 안전한 프로퍼티 추가 방법

기존 객체의 프로퍼티를 동적으로 추가해야 할 경우, any를 사용하지 않고 확장 가능한 타입을 정의하는 것이 좋습니다.

type FlexibleObject<T> = T & { [key: string]: unknown };

const user: FlexibleObject<{ name: string; age: number }> = { name: "Henry", age: 30 };
user.role = "admin"; // 안전하게 동적 프로퍼티 추가 가능

이처럼 유연한 타입을 정의하면 any를 사용하지 않고도 동적 프로퍼티를 추가할 수 있습니다.

3) 클래스 기반 확장 활용

기존 객체나 라이브러리를 수정하는 대신, 클래스를 사용하여 확장하는 방법도 안전한 대안이 될 수 있습니다.

class User {
  constructor(public name: string, public age: number) {}
}

class AdminUser extends User {
  isAdmin: boolean = true;
}

const admin = new AdminUser("Henry", 30);
console.log(admin.isAdmin); // true

이 방법을 사용하면 원본 객체를 수정하지 않고도 원하는 기능을 확장할 수 있습니다.

7. 타입 커버리지를 추적하여 타입 안정성 유지

타입스크립트에서는 프로젝트 내 any 타입의 사용을 추적하고 줄여나가는 것이 중요합니다. 이를 통해 타입 안정성을 지속적으로 개선할 수 있습니다.

1) any 타입 사용 위치 찾기

타입스크립트의 strict 모드를 활성화하면 암시적인 any 사용을 방지할 수 있습니다.

{
  "compilerOptions": {
    "strict": true
  }
}

또한, tsc --noEmit --watch 명령어를 활용하면 any가 사용된 부분을 빠르게 찾을 수 있습니다.

2) 타입 커버리지 도구 활용

타입스크립트 커버리지 도구를 활용하면 프로젝트 내 타입 안정성을 평가할 수 있습니다. 예를 들어, type-coverage 같은 도구를 사용하면 타입 안전성을 시각적으로 확인하고, any가 포함된 부분을 쉽게 파악할 수 있습니다.

npx type-coverage

3) 점진적인 타입 변환 전략

any를 점진적으로 줄이기 위해서는 우선순위를 정하고 작은 단위로 변환하는 것이 좋습니다. 예를 들어, 가장 많이 사용되는 함수나 주요 로직에서 anyunknown이나 구체적인 타입으로 변환하는 방법을 사용할 수 있습니다.

마무리

any 타입은 타입스크립트의 유연성을 제공하지만, 무분별한 사용은 타입 안정성을 해칠 수 있습니다. any를 사용할 때는 범위를 최소화하고, 구체적인 타입을 지정하며, 가능하면 unknown 활용하는 것이 중요합니다. 이를 통해 타입스크립트의 장점을 최대한 활용하면서 안정적인 코드를 작성할 수 있습니다.

앞으로 타입스크립트를 사용할 때 any를 신중하게 다루며 안전한 코드 작성을 목표로 해보세요!

반응형