JS : 느슨한 타입의 동적 언어

Erica Gong·2022년 7월 20일
3

JS

목록 보기
1/4
post-thumbnail

들어가며

유명한 JS 문법서들의 서문에서는 항상 JS의 언어적 특징으로 느슨한 타입의 동적 언어를 언급한다. JS가 느슨한 타입의 언어라는 점과 동적 타입의 언어라는 사실은 아무리 강조해도 지나치지 않다. 실무에서 자신이 코드를 작성해 놓고도 JS 코드가 왜 이렇게 돌아가지? 하는 대부분의 순간의 해답이 느슨한 타입 언어와 동적 타입 언어라는 속성에 기인하기 때문이다.

하지만 프론트엔드 개발자를 꿈꾸며 JS 문법서의 첫 장을 열었을 병아리 개발자들이 JS가 느슨한 타입동적 타입 언어임을 이해하는 것은 결코 쉽지 않다.

이러한 맥락에서 이번 포스트에서는 느슨한 타입동적 타입 언어라는 특성에 대해 상세히 설명하여 개발자 꿈나무들의 이해를 돕고자 한다. 우선, 느슨한 타입은 대체 무엇이고, 동적 타입 언어란 무엇인지 살펴 본 뒤, 해당 특성으로 인한 JS의 한계에 대해 논의하며 이러한 맥락에서 나온 TypeScript, Flow에 대해 소개하려한다.

느슨한 타입이란

  • 느슨한 타입(loosely typed): 자료형 없이 변수를 선언하는 것
  • 강력한 타입(strong typed): 자료형과 함께 변수를 선언하는 것

강력한 타입의 언어는 자료형과 함께 변수를 선언해야한다. 자료형이란 데이터의 타입을 의미한다. 대표적인 강력한 타입의 언어로는 C, Java가 있다. 자료형과 함께 변수를 선언한 경우, 해당 자료형에 부합하지 않는 값이 변수에 할당되는 경우, 컴파일 단계에서 TypeError가 발생한다.

반면 느슨한 타입의 자료형 없이 변수를 선언한다. 느슨한 타입의 대표 언어로는 JavaScript, Python을 꼽을 수 있다. 자료형 없이 변수를 선언하고 특정 값으로 초기화한 경우, 런타임 시 변수 내부에 있는 값에 기반하여 해당 변수의 자료형이 내부적으로 지정된다.

/* Java Example (strong typing) */

int a = 1031; 		// int 선언되어 컴파일 타임시 체크됨
String b = "erica"; // String 선언되어 컴파일 타임시 체크됨


/* JavaScript Example (loose typing) */

let a = 1031; 		// 런타임 시 Number로 처리됨
let b = "erica"; 	// 런타임 시 String으로 처리됨

JS의 경우, 런타임JS 엔진이 변수에 할당된 값을 바탕으로 해당 변수의 자료형을 지정한다. 다들 알고 있듯 JS의 자료형은 크게 원시값(Primitive value)객체로 나눌 수 있으며, ES6에는 총 7개의 원시값이 존재(Number, String, Boolean, Null, Undefined, BigInt, Symbol)한다.

정리하면, JS가 느슨한 타입의 언어라는 것은, JS에서 변수를 선언할 때 자료형을 명시하지 않으며, 실제로 해당 변수가 어떤 자료형인지는 런타임 시에 JS 엔진이 해당 변수에 할당된 값에 따라 내부적으로 지정한다는 의미이다.

동적 타입이란 무엇인가

동적 타입 언어 밈

이 밈은 동적 타입 언어와 정적 타입 언어를 단적으로 묘사하고 있다. 단순한 네모 퍼즐을 맞추는 동적 타입 언어와는 달리 정적 타입 언어는 퍼즐의 나오고 들어간 방향, 즉, 자료형을 따져가며 맞춰야 한다는 점에서 살짝 복잡 할 수 있다. 하지만 동적 타입 언어가 퍼즐을 다 맞춰도 삐뚤빼뚤 한 것처럼 동적 타입 언어는 런타임 오류라는 치명적인 맹점이 있다.

  • 정적 타입(staic type) 언어: 컴파일 시 변수의 자료형이 결정되는 언어.
  • 동적 타입(dynamic type) 언어: 런타임 시 변수의 자료형이 결정되는 언어.
  • 만약 런타임컴파일 타임에 대해 헷갈린다면, 해당 링크를 참고하자!

정적 타입 언어를 사용할 때 개발자는 변수에 들어갈 값의 형태에 따라 변수를 선언할 때 미리 변수의 자료형을 지정한다. 정적 타입 언어는 소스 코드를 컴파일 할 때, 만약 선언된 자료형과 변수 내부의 값의 유형이 일치하지 않는다면 TypeError를 발생시킨다. 정적 타입 언어는 컴파일 시에 타입을 지정하기 때문에 실행 속도가 동적 타입 언어에 비해 빠르고, 컴파일 타임TypeError를 통해 문제가 발생한 지점을 바로 확인할 수 있다는 장점이 있다. 다만 처음 코드를 쓸 때 자료형을 적어주어야 해서 다소 번거로울 수 있다... (개인적으로는 오히려 자료형을 명시하지않는 느슨한 언어가 더 불편하다...)

동적 타입 언어는 컴파일 시 변수의 자료형이 결정되는 정적 타입 언어와 달리 런타임에 변수의 자료형이 결정된다. 코드를 작성할 때 변수가 특정 자료형과 연결되지 않기 때문에 모든 자료형의 값으로 재할당 될 수 있다는 유연성이 있다.

// JS는 쉽게 재할당 가능
let a = 28 // a가 Number
a = 'erica' // a가 이제 String
a = true // a가 이제 Boolean

변수 자료형을 명시하지 않으므로 코딩 속도가 살짝 빠를 수 있다. 하지만 이런 사소한 장점에 비해 치명적인 단점이 있다. 런타임 즉, 코드를 실제로 실행하며 점검하기 때문에 변수에 예상치 못한 타입이 들어왔을 때 코드 실행 중 에러가 발생한다는 점이다. 코드가 딱 멈췄을 때, 소스 코드에서 에러 발생 지점을 확인, 이를 수정하는 작업은 당연히 컴파일 타임에 비해 번거로울 수 밖에 없다. (이러한 맥락에서 많은 개발자들이 JS에서 TypeScript, Flow로 이주했다...)

느슨한 타입, 동적 타입 언어의 한계

앞서 살펴 본 바와 같이 느슨한 타입의 언어는 변수 선언 시 자료형을 지정하지 않는 것, 동적 타입 언어는 런타임 시 변수의 자료형을 결정하는 것을 지칭한다. JS는 느슨한 타입이면서 동적 타입의 언어이다.

런타임에 변수의 자료형이 JS 엔진에 의해 내부적으로 결정되기 때문에 실행 중에 예상치 못한 자료형이 해당 변수에 들어왔을 때, TypeError가 발생할 수 있다. 심지어 런타임 에러의 경우 컴파일 타임 에러에 비해 수정이 어렵다. (더 나아가서는 FE 코드의 에러가 서버에 영향을 끼칠 수도 있다...)

강력한 타입, 정적 타입 언어로의 이주

TS, FLOW 로고

이러한 불편함에서 개발자들은 과감히 다른 언어로의 이주를 택했다. JS의 문법에 자료형 선언을 덧붙인 TypeScript, Flow 등으로 개발 언어를 전환한 것이다.

TypeScript는 JS를 따르되, 변수 선언 시 자료형을 꼭 함께 쓰도록 수정한 정적 타입 언어이다. 정적 타입이기 때문에 TypeScript컴파일 타임에 해당 변수의 자료형이 올바른지 여부를 검사할 수 있다는 지점에서 JS 대비 특장점을 가지고 있다. (나도 얼른 TypeScript로 이주해야겠다.. 물론 단점이 없지는 않지만, 그래도 컴파일 타임 타입 체크는 못참지!)

1개의 댓글

comment-user-thumbnail
2022년 7월 24일

스크랩해도 될까요? 도움 많이 받았습니다.

답글 달기