[TYPESCRIPT] 함수형 프로그래밍과 TypeScript - 불변성과 타입으로 만드는 안정적인 코드

 

TypeScript는 객체지향뿐 아니라 함수형 프로그래밍(FP, Functional Programming) 스타일과도 매우 잘 어울리는 언어입니다. 특히 강력한 타입 시스템 덕분에 함수형 프로그래밍의 장점인 예측 가능성, 테스트 용이성, 사이드 이펙트 를 감소 시킬 수 있습니다.

 

 

함수형 프로그래밍이란?

함수형 프로그래밍은 다음 원칙을 중심으로 합니다.

  • 순수 함수(Pure Function)
  • 불변성(Immutability)
  • 함수 합성(Function Composition)
  • 상태 변경보다 데이터 변환 중심

TypeScript는 이 원칙들을 타입 레벨에서 보조해 주기 때문에 JavaScript보다 훨씬 안전한 함수형 코드 작성이 가능합니다.

 

순수 함수와 타입 안정성

순수 함수란 같은 입력 → 항상 같은 출력을 반환하고, 외부 상태를 변경하지 않는 함수를 말합니다.

function add(a: number, b: number): number {
  return a + b;
}

TypeScript에서는 함수 시그니처를 통해 입력과 출력이 명확하게 고정되므로 순수 함수의 계약(contract)을 코드로 표현할 수 있습니다.

 

불변성(Immutable)과 Readonly

함수형 프로그래밍에서는 기존 데이터를 변경하지 않고 새로운 데이터를 생성하는 방식을 사용합니다.

type User = {
  readonly id: number;
  readonly name: string;
};

또는 유틸리티 타입을 활용할 수 있습니다.

type ReadonlyUser = Readonly<{
  id: number;
  name: string;
}>;

이렇게 하면 함수 내부에서 실수로 상태를 변경하는 것을 컴파일 단계에서 차단할 수 있습니다.

 

map / filter / reduce와 타입 추론

함수형 프로그래밍의 핵심은 컬렉션을 반복문이 아닌 고차 함수로 다루는 것입니다.

const numbers = [1, 2, 3, 4];

const result = numbers
  .filter(n => n % 2 === 0)
  .map(n => n * 2);

TypeScript는 각 단계마다 타입을 정확히 추론합니다.

  • numbers → number[]
  • filter 결과 → number[]
  • map 결과 → number[]

이 덕분에 함수형 파이프라인에서도 타입 안정성이 유지됩니다.

 

고차 함수(Higher-Order Function)

고차 함수는 함수를 인자로 받거나 함수를 반환하는 함수입니다.

function withLogging<T>(fn: () => T): () => T {
  return () => {
    console.log("start");
    const result = fn();
    console.log("end");
    return result;
  };
}

제네릭을 사용하면 입력/출력 타입을 유지한 채 함수 기능만 확장할 수 있습니다.

 

함수 합성(Function Composition)

작은 함수를 조합해 큰 동작을 만드는 것이 함수형 프로그래밍의 핵심입니다.

const toNumber = (v: string) => Number(v);
const double = (n: number) => n * 2;

const composed = (v: string) => double(toNumber(v));

TypeScript에서는 각 함수의 입출력 타입이 맞지 않으면 컴파일 단계에서 바로 오류를 알려줍니다.

 

Discriminated Union과 함수형 분기

함수형 스타일에서는 if/switch 분기 대신 명확한 타입 모델링을 선호합니다.

type Result =
  | { type: "success"; data: string }
  | { type: "error"; message: string };

function match(result: Result): string {
  switch (result.type) {
    case "success":
      return result.data;
    case "error":
      return result.message;
  }
}

타입 자체가 분기 조건이 되기 때문에 런타임 오류 가능성이 크게 줄어듭니다.

 

함수형 프로그래밍이 실무에서 좋은 이유

  • 테스트가 쉬움 (순수 함수)
  • 사이드 이펙트 감소
  • 리팩토링 안정성 증가
  • 병렬 처리 및 확장성에 유리
  • 타입 시스템과 궁합이 매우 좋음

특히 대규모 코드베이스에서는 함수형 + TypeScript 조합이 유지보수 비용을 크게 낮춰줍니다.

 

함수형 스타일 적용 시 주의점

  • 모든 코드를 함수형으로 만들 필요는 없음
  • 과도한 추상화는 가독성을 해칠 수 있음
  • 팀의 이해 수준과 코드 스타일을 고려해야 함

중요한 것은 패러다임이 아니라 문제 해결에 적합한 방식을 선택하는 것입니다.

 


 

 

TypeScript는 함수형 프로그래밍을 위한 훌륭한 도구입니다. 타입 시스템을 활용하면 함수형 프로그래밍의 핵심 가치인 안정성, 예측 가능성, 조합성을 실무에서 자연스럽게 구현할 수 있습니다.

  • 순수 함수 + 명확한 타입
  • 불변성(Readonly, as const)
  • 고차 함수 + 제네릭
  • Discriminated Union 기반 분기