강한 타입 vs 정적 타입 코드 사용하기

강한 타입 vs 정적 타입 코드 사용하기

원문: Debjyoti Banerjee, "Using strongly typed vs. statically typed code"

강한 타입과 정적 타입 언어는 서로 관련되어 있는 프로그래밍 개념입니다. 특히 프런트엔드 개발에서 더욱 그러합니다. 하지만 그 둘은 꽤 다르며 각각의 뚜렷한 장단점이 존재합니다. 두 가지 모두 타입 안정성을 강제하지만 강한 타입 언어는 변수를 사용할 때 정의된 타입이 일관되게 사용되도록 보장하여 타입 안정성을 더욱 강화합니다.

언어는 두 분류 모두에 속할수도 혹은 둘 다에 속하지 않을 수도 있다는 사실을 알아야 합니다. 넓은 의미에서 언어는 일반적으로 사분면 차트에 속하게 됩니다. 한 축에는 정적 타입과 동적 타입 언어가 속하고, 다른 한 축에는 강한 타입과 약한 타입 언어가 속하게 됩니다.

https://blog.logrocket.com/wp-content/uploads/2023/12/Axis-chart-strong-weak-static-dynamic.png

따라서 언어는 다음과 같이 분류될 수 있습니다.

  • 자바나 타입스크립트 같은 강한 타입과 정적 타입

  • 파이썬이나 루비 같은 동적 타입

  • C나 C++ 같은 약한 정적 타입

  • PHP나 자바스크립트 같은 약한 동적 타입

"약한 타입"과 "강한 타입"이라는 용어는 보편적으로 합의된 정의가 없습니다. 일반적으로 "약한 타입"과 "강한 타입"은 타입 규칙의 엄격성을 의미합니다.

이 용어들을 상대적으로 사용하는 것이 더 합리적입니다. 예를 들어 자바는 C보다 강한 타입을 가지고, C는 자바스크립트보다 강한 타입을 가진다고 할 수 있습니다.

이 개념들에 대해 더 자세히 알아보겠습니다. 이 가이드의 코드 샘플을 분석한 깃허브 저장소도 확인하실 수도 있습니다.

정적 타입 언어

정적 타입 언어에서는 변수를 선언할 때 명시적으로 데이터 타입을 지정해야 합니다. 이는 타입 검사가 컴파일 단계에서 이루어진다는 것을 의미합니다. 동적 타입 언어에서 타입 검사가 런타임에 발생하는 것과는 대조적입니다.

타입 불일치가 있으면 스크립트나 코드는 컴파일되지 않습니다. 이는 개발 초기 단계에서 오류를 감지하여 더 신뢰할 수 있고 예측 가능한 코드를 작성하는 데 도움이 됩니다.

C는 모든 변수의 타입을 명시적으로 언급해야 하는 정적 타입 언어입니다. 다음은 그 예시입니다.

#include <stdio.h>int main() {
  int a = 5; // int type 변수
  float c = 9.000; // float type 변수
}

C와 같은 정적 타입 언어에서는 변수를 사용하기 전에 반드시 선언해야 합니다.

비교를 위해 동적 타입 언어인 파이썬 코드 예시를 살펴보겠습니다.

example = 3
exmple = example + 9 // 오타
print(example)

위의 예시에서 변수 example은 값 3으로 초기화되었습니다. 다음 줄에서는 변수 exmple이 다른 값으로 할당됩니다.

파이썬은 동적 타입 언어이므로 exmple을 새로운 변수로 처리하고 오류를 발생시키지 않습니다. 이건 프로그래머가 example 값에 다시 접근하려고 할 때 문제가 될 수 있습니다.

파이썬과 자바스크립트 같은 동적 타입 언어는 더 많은 유연성을 제공하지만 예기치 않은 타입 불일치로 인해 런타임 오류가 발생할 수 있습니다. 반면 정적 타입 언어는 더 자세한 코드를 요구하고, 이는 빠른 프로토타입 제작 시 불편할 수 있습니다.

몇몇 경우에 정적 타입 언어는 더 많은 보일러플레이트 코드를 사용할 수 있는데 이는 동적 타입 언어 배경을 가진 새로운 개발자에게는 어려울 수 있습니다.

정적 타입 언어가 돋보이는 또 다른 주요 영역은 IDE(통합 개발 환경)입니다. 이 언어들은 보통 견고한 개발 환경을 제공합니다. 정적 타입 언어의 예측 가능성은 코드 자동 완성, 더 나은 리팩토링 등의 혜택을 제공합니다.

강한 타입 언어

강한 타입 언어는 엄격한 타입 규칙을 가지고 있으며 일반적으로 타입에 대한 엄격한 준수를 요구합니다. "강한 타입"과 "약한 타입" 언어의 용어는 널리 합의된 정의가 없어서 언어를 어느 쪽으로 분류하기는 어렵습니다.

상대적으로 어떤 언어가 더 나은 타입 안정성을 제공하는지에 대해 이야기하는 것이 더 합리적입니다. 일반적으로 자바스크립트와 C는 약한 타입 언어로 간주됩니다. 그러나 C는 자바스크립트보다는 더 강한 타입 언어입니다.

다음은 C 언어의 예시입니다.

int variable1 = 10; // 예를 들어 C에서는 variable1에 배열을 저장할 수 없습니다.

다음은 자바스크립트로 작성된 동일한 예시입니다.

let variable1 = 10; // 반면 자바스크립트에서는 variable1에 배열을 할당할 수 있습니다.
variable1 = []; // 오류 미발생

C는 강한 타입 시스템을 가지고 있지만 이를 우회할 수 있는 방법도 제공합니다. 많은 프로그래머는 이를 약한 타입 언어의 특징으로 간주할 수 있습니다. 다음은 타입 변환을 사용하여 엄격한 타입을 우회하는 C 코드 예시입니다.

#include <stdio.h>
int main() {
  int a = 17;
  float b = 21.5;
  void *ptr;

  ptr = &a;
  printf("Value of a: %d\\n", *(int *)ptr); // void*를 int*로 변환하기

  ptr = &b;
  printf("Value of b: %f\\n", *(float *)ptr); // void*를 float*로 변환하기

  return 0;
}

이 코드에서는 void* 포인터(ptr)를 사용하여 변수 intfloat의 주소를 저장하고 있습니다. 이는 void* 포인터가 어떤 타입의 데이터도 저장할 수 있으며 실제 데이터 타입은 런타임에 해석된다는 점에서 약한 타입에 속합니다.

그러나 위에 나오는 C 코드는 자바스크립트보다 더 강한 타입 시스템을 가진다고 말할 수 있습니다.

강한 타입과 정적 타입 언어 요약하기

강한 타입 언어와 정적 타입 언어에 대해 배운 내용을 요약해 보겠습니다.

강한 타입 언어정적 타입 언어
사용엄격한 타입 규칙을 적용합니다.변수를 선언할 때 변수의 데이터 타입을 명시해야 합니다. 타입 검사는 컴파일 단계에서 수행합니다.
장점타입 불일치와 관련된 런타임 오류 가능성을 줄입니다. 코드의 가독성과 예측 가능성을 높이며 일부 경우 더 나은 최적화를 제공합니다. 개선된 IDE를 지원합니다.개발 초기 단계에서 오류를 더 쉽게 감지합니다. 개선된 IDE를 지원하고 코드의 가독성과 예측 가능성을 높입니다.
단점일반적으로 학습 곡선이 더 가파르고 코드 중복이 증가합니다.장황하고 더 많은 보일러플레이트 코드를 사용하는 경향이 있어 빠른 개발에 불편하고 새로운 개발자에게 어려울 수 있습니다.

두 카테고리 사이에 많은 중복이 있지만 이 요약 표는 각 타입 언어의 장점과 사용상 고려해야 할 사항을 더 잘 이해하는 데 도움이 될 것입니다.

타입스크립트로 강한 타입과 정적 타입의 장점 활용하기

타입스크립트는 정적 타입 언어이면서 강한 타입 시스템을 제공합니다. 이 균형은 프런트엔드 개발 생태계에서 중요한 역할을 하고 있습니다.

더불어 타입스크립트의 뛰어난 IDE 지원은 작업을 편리하게 하며 자바스크립트의 타입 안정성을 강화합니다. 이는 특히 대규모 프로젝트에서 유용합니다.

다음은 자바스크립트 코드 예시입니다.

function add(a, b) {
  return "$" + a + b; // 타입 검사 없음
}

add('5' + '6') // $56undefined

타입스크립트로 작성된 동일한 코드 예시입니다.

add(a : number, b: number) {
  return "$" + (a + b).toString();
}

add('5', '6'); // number 타입에 string 타입 할당이 불가능합니다.

강한 타입 시스템 덕분에 타입스크립트는 컴파일 시 잠재적인 버그를 찾아낼 수 있습니다. 위의 코드 예시에서 이를 확인할 수 있습니다. 타입스크립트에서의 런타임 타입 검사 방법도 주목하세요.

게다가 타입스크립트는 특정 상황에서 약한 타입을 보일 수도 있습니다. 주로 타입 어서션(assertions)을 수행하거나 any 타입을 다룰 때입니다. 다음 예시를 참고하세요.

let x: number = 5;
let y: any = "10";

x = y; // y가 any 타입이지만 오류 미발생

console.log(x); // 결과: "10"

타입스크립트는 더 구체적인 타입(number)을 가진 변수에 다른 타입(any) 값을 할당하도록 허용하여 약한 타입의 행동을 보입니다. 이 경우는 any 타입으로 인해 타입스크립트의 타입 검사를 우회하여 할당된 타입이 호환되지 않는 경우 런타임 오류를 발생시킬 수 있습니다.

타입스크립트는 다양한 데이터 타입을 다룰 때 타입 변환이나 타입 어서션을 수행하는 여러 방법을 제공합니다. 예를 들어 다음과 같은 방법이 있습니다.

let value: any = "Hello";
let length: number = (<string>value).length;

그러나 any를 사용하는 것은 타입스크립트의 엄격한 타입 검사기를 우회하는 방법일 뿐입니다. 이는 프로그래머가 타입을 얼마나 편안하게 느끼는지에 따라 달라지며 자바스크립트와 유사한 유연한 접근 방식을 허용합니다.

전반적으로 타입스크립트는 타입 안정성을 향상시키고 컴파일 단계에서 잠재 오류를 포착하는 것을 목표로 합니다. 약한 타입을 사용하는 경우가 있을 수 있지만 언어는 이러한 경우를 최소화하고 더 나은 코드 신뢰성을 보장하기 위해 강한 타입 사용을 권장하고 촉진합니다.

다른 실제 상황을 고려해 보세요.

class Person extends Animal {
  speak() {
  }
}

class Dog extends Animal {
  bark() {
  }
}

여기서 두 클래스가 같은 기본 Animal 클래스를 상속받지만 각각 다른 메서드를 가집니다. 특히 이벤트 기반 자바스크립트 생태계의 대규모 프로젝트에서 이와 같은 경우가 흔하게 발생합니다. 객체가 메서드에 전달될 때 동작을 수행하는 예시입니다.

function test(a: Dog) {
  a.bark();
}

이렇게 하면 이 함수가 호출될 때마다 Dog 객체가 전달되도록 보장합니다. 애플리케이션에서 타입 규칙을 상당히 엄격하게 만들 수 있습니다.

조금 더 유연성을 제공하면서도 강한 타입을 유지하기 위해 타입스크립트는 유니언 타입을 제공하여 변수가 다른 시기에 서로 다른 타입을 가질 수 있다는 것을 표현할 수 있습니다. 유니언 타입을 생성하는 구문은 타입 사이에 파이프 | 문자를 사용합니다.

find 같은 자바스크립트 메서드는 undefined를 반환할 수 있기 때문에 이러한 경우 유니언 타입을 사용하는 것이 편리합니다. 위의 예시를 유니언 타입으로 사용하는 방법을 고려해 보세요.

function test(a: Dog | Person) {
  a.speak(); // 컴파일 오류 - speak는 Dog 타입에 없습니다.
}

타입스크립트는 Dogspeak 메서드가 없기 때문에 speak 메서드를 호출할 수 없도록 합니다. 그러나 자바스크립트처럼 이를 우회할 수 있습니다. 코드 구조에 따라 타입스크립트는 값에 대해 더 구체적인 타입을 유추할 수 있습니다. 다음과 같은 코드는 작동합니다.

function test(a: Person | Dog) {
  if (a instanceof Person) {
    a.speak() // person일 경우 speak
  }  else {
    a.bark(); // dog일 경우 bark
  }
}

any 키워드와 타입 어서션 같은 기능은 복잡한 구조를 다룰 때 유용합니다. 이는 타입 정의를 만들기 어렵거나 타입스크립트가 타입을 알 수 없는 경우에 특히 유용합니다. 다음은 그 예시입니다.

interface ApiResponse {
  id: number;
  name: string;
  // 다른 속성들...
}

// (실제 API 호출에서 올 수 있는) 시뮬레이션된 API 응답
const response: any = {
  id: 1,
  name: "",
  //  실제 응답에 다른 속성이 있을 수도 없을 수도 있습니다.
};

// 타입스크립트에 응답 구조를 알리기 위해 타입 어서션 사용하기
const safeResponse = response as ApiResponse;

이전 예시에서 본 타입 추론 기능은 자바스크립트와 같은 약한 타입 언어에서 발견되는 유연성과 유사합니다. 타입스크립트의 타입 추론은 명시적인 타입 주석의 필요성을 줄이고 필요할 때 더 많은 유연성을 제공합니다.

이렇게 해서 타입스크립트는 기존 자바스크립트 코드베이스에 점진적으로 타입을 도입할 수 있게 합니다. 최소한의 타입으로 시작하고 필요에 따라 점진적으로 더 많은 타입을 추가할 수 있습니다. 이는 약한 타입의 자바스크립트에서 강한 타입의 타입스크립트 코드베이스로 원활한 전환을 제공합니다.

결론

강한 타입 언어와 정적 타입 언어는 각각 고유한 장단점을 가지고 있습니다. 두 카테고리가 종종 겹치기 때문에 타입 검사가 언제 발생하는지와 언어가 타입 선언을 강제하는지 여부를 기준으로 언어를 비교하는 것이 더 합리적입니다.

C와 같은 정적 타입 언어는 컴파일 중에 변수 타입을 명시적으로 선언해야 하므로 오류를 조기에 감지하고 견고한 코드를 개발하는 데 도움이 됩니다. 이 접근 방식은 더 장황할 수 있지만 더 나은 IDE 지원과 예측 가능성을 제공합니다.

타입스크립트는 강한 타입과 정적 타입의 장점을 모두 결합한 언어로 돋보입니다. 약한 타입의 경우가 일부 있지만 타입스크립트의 타입 안정성 강조는 더 나은 코드 신뢰성과 유지 관리를 촉진합니다.

강한 타입 코드와 정적 타입 코드에 대한 이 가이드가 도움이 되었기를 바랍니다! 추가 질문이 있으면 아래에 댓글로 남겨주세요. 또한 깃허브 저장소에서 이 글 전반에 사용된 코드 샘플을 살펴볼 수 있습니다.