Item 58 모던 자바스크립트로 작성하기
About
마이그레이션을 어디서부터 시작해야 할지 몰라 막막하다면 옛날 버전의 JS 코드를 최신 버전의 JS로 바꾸는 작업부터 시작해보면 좋다.
이번 Item 58은 모던 JS의 주요 기능 몇 가지를 간략히 다룬다.
ECMAScript 모듈 사용하기
ES2015부터는 임포트(import
)와 익스포트(export
)를 사용하는 ECMAScript가 표준이 되었다.
만약 마이그레이션 대상인 JS 코드가 단일 파일이거나 비표준 모듈 시스템을 사용 중이라면 ES 모듈로 전환하는 것이 좋다.
ES 모듈 시스템을 사용하기 위해서 프로젝트 종류에 따라 웹팩(webpack)이나 ts-node 같은 도구가 필요한 경우도 있다. ES 모듈 시스템은 TS에서도 잘 동작하며, 모듈 단위로 전환할 수 있게 해 주기 때문에 점진적 마이그레이션이 원활해진다. (Item 61)
다음은 CommonJS 모듈 시스템을 이용한 전형적인 예제이다.
이를 ES 모듈로 표현하면 다음과 같다.
프로토타입 대신 클래스 사용하기
과거에는 JS에서 프로토타입 기반의 객체 모델을 사용했으나, 개발자들의 선호도에 따라 결국 ES2015에 class
키워드를 사용하는 클래스 기반 모델이 도입되었다.
마이그레이션하려는 코드에서 단순한 객체를 다룰 때 프로토토타입을 사용하고 있었다면 클래스로 바꾸는 것이 좋다.
다음은 단순 객체를 프로토타입으로 구현한 예제이다.
다음은 프로토타입 기반 객체를 클래스 기반 객체로 바꾼 예시이다.
참고로 편집기에서 프로토타입 객체에 마우스를 올려 간단히 클래스 객체로 변환할 수 있다.
var
대신 let
/const
사용하기
var
대신 let
/const
사용하기JS var 키워드의 스코프(scope) 규칙에 문제가 있다는 것은 널리 알려진 사실이다.
스코프 문제를 자세히 알지 못하더라도 var
대신 let
/const
를 사용하면 스코프 문제를 피할 수 있다. let
과 const
는 제대로 된 블록 스코프 규칙을 가지며, 개발자들이 일반적으로 기대하는 방식으로 동작한다.
만약 var
키워드를 let
/const
로 변경하면 일부 코드에서 TS가 문제를 표시할 수도 있다. 오류가 발생한 부분은 잠재적으로 스코프 문제가 존재하므로 수정해야 한다.
중첩된 함수 구문에도 var
의 경우와 비슷한 스코프 문제가 존재한다. 예를 들어보면:
foo
함수를 호출하면 bar
함수의 정의가 호이스팅(hoisting)되어 가장 먼저 수행되기 때문에 bar
함수가 문제없이 호출되고 hello가 출력된다. 호이스팅은 실행 순서를 예상하기 어렵게 만들고 직관적이지 않다. 대신 함수 표현식(const bar = () => { ... }
)을 사용하여 호이스팅 문제를 피하는 것이 좋다.
for(;;)
대신 for-of
또는 배열 메서드 사용하기
for(;;)
대신 for-of
또는 배열 메서드 사용하기과거에는 JS에서 배열을 순회할 때 C-style for 루프를 사용했다.
모던 JS에는 for-of
루프가 존재한다.
for-of
루프는 코드가 짧고 인덱스 변수를 사용하지도 않기 때문에 실수를 줄일 수 있다.
인덱스 변수가 필요하면 다음과 같이 forEach
메서드를 사용하면 된다.
for-in
문법도 존재하지만 Item 16에서 설명했듯이 몇 가지 문제점이 있기 때문에 사용하지 않는 것이 좋다.
함수 표현식보다는 화살표 함수 사용하기
this 키워드는 JS에서 가장 어려운 개념 중 하나이다. 일반적인 변수들과는 다른 스코프 규칙을 갖기 때문이다.
일반적으로는 this
가 클래스 인스턴스를 참조할거라 생각하지만, 다음 예제처럼 예상치 못한 결과가 나올 수 있다.
대신 화살표 함수를 이용하면 상위 스코프의 this
를 유지할 수 있다.
인라인(또는 콜백)에서는 일반 함수보다 화살표 함수가 더 직관적이며 코드도 간결해지기 때문에 가급적 화살표 함수를 사용하는 것이 좋다.
컴파일러 옵션에
noImplicitThis
(또는strict
)를 설정하면 TS가this
바인딩 관련 오류도 표시해주므로 설정해주는 것이 좋다.this 바인딩 관련 자세한 내용은 Item 49 참고.
단축 객체 표현과 구조 분해 할당 사용하기
pt 객체를 생성하는 다음 코드가 있다.
변수와 객체 속성의 이름이 같다면, 간단하게 다음 코드처럼 작성할 수 있다.
이쪽 코드가 더 간결하고 중복된 이름을 생략하므로 가독성이 좋고 실수가 적다(Item 36).
Note: ESLint의 object-shorthand를 켜면 린터가 잡아준다.
화살표 함수 내에서 객체를 반환할 땐 소괄호로 감싸야 한다. 화살표 함수에서 함수의 구현부에는 블록이나 단일 표현식이 필요하기 때문에 소괄호로 감싸서 표현식으로 만들어 준 것이다.
객체의 속성 중 함수를 축약해 표현하는 방법은 다음과 같다.
단축 객체 표현(compact object literal)의 반대는 객체 구조 분해(object destructuring)이라고 한다. 다음 예제를 보자.
다음처럼 줄여서 작성이 가능하다.
또는 한 단계 더 줄여서 이렇게도 가능하다.
참고로 a
, b
는 변수로 선언되지만 props
는 변수 선언이 아니라는 것에 유의하자.
구조 분해 문법에서는 기본값을 지정하는 것도 가능하다. 다음은 if
구문으로 기본값을 지정하는 방식이다.
이렇게 구조 분해 문법으로 기본값을 할당할 수 있다.
배열에서도 구조 분해 문법이 가능하다. 배열을 튜플처럼 사용할 경우 특히 유용하다.
함수 매개변수에서도 가능하다.
단축 객체 표현과 마찬가지로 객체 구조 분해를 사용하면 문법이 간결해지고 변수 사용 간 실수를 줄일 수 있으므로 적극 사용하자.
함수 매개변수 기본값 사용하기
JS의 모든 매개변수는 선택적(생략 가능)이며, 매개변수를 지정하지 않으면 undefined로 간주된다.
옛날엔 매개변수의 기본값을 지정하고 싶을 때 다음 코드처럼 구현하곤 했다.
모던 JS에서는 매개변수의 기본값을 직접 지정할 수 있다.
이런 식으로 하면 코드가 간결해지고 base가 선택적 매개변수라는 것을 명시하는 효과도 준다.
기본값을 기본으로 타입 추론이 가능해져서, TS로 마이그레이션 할 때 매개변수에 타입 구문을 쓰지 않아도 된다(Item 19).
저수준 프로미스나 콜백 대신 async/await 사용하기
Item 25에서 설명했듯 콜백, 프로미스보다 async
/await
을 권장한다.
요점은 다음과 같다.
async
/await
을 사용하면 코드가 간결해져서 버그와 실수를 방지한다.비동기 코드에 타입 정보가 전달되어 타입 추론을 가능하게 한다.
위와 같이 콜백과 프로미스를 사용한 코드보다는, 아래의 async
/await
으로 작성한 코드가 더 깔끔하고 직관적이다.
연관 배열에 객체 대신 Map
과 Set
사용하기
Map
과 Set
사용하기Item 15에서 객체의 인덱스 시그니처를 사용하는 방법을 다루었다. 인덱스 시그니처는 편리하지만, 몇 가지 문제점이 존재한다. 문자열 내의 단어 개수를 세는 함수를 예로 들어본다.
별 문제가 없어 보이는 코드지만, constructor이라는 문자열이 주어지면 문제가 발생한다.
실행 결과는 다음과 같다.
constructor
의 초깃값은 undefined
가 아니라 Object.prototype
에 있는 생성자 함수이다. 원치 않는 값일 뿐 아니라, 타입도 number
가 아닌 string
이다. 이런 문제를 방지하려면 Map
을 사용하자.
타입스크립트에 use strict 넣지 않기
ES5에서는 버그가 될 수 있는 코드 패턴에 오류를 표시해주는 엄격 모드(strict
)가 도입되었다. 코드의 제일 처음에 'use strict'
를 넣으면 엄격 모드가 활성화된다.
그러나 TS에서 수행되는 안전성 검사(sanity check)가 엄격 모드보다 훨씬 더 엄격한 체크를 하므로, 이는 무의미하다.
실제로 TS 컴파일러에
alwaysStrict
(또는strict
) 옵션을 설정하면 엄격 모드로 코드를 파싱하고 생성하는 JS 코드에서'use strict'
가 추가된다.즉, TS 코드에는
'use strict'
를 넣지 말고,alwaysStrict
설정을 사용해야 한다.
Summary
TS 개발 환경은 모던 JS도 실행할 수 있으므로 모던 JS 최신 기능을 적극 사용하는 것이 좋다. 코드 품질을 향상시킬 수 있고, TS의 타입 추론도 더 나아진다.
TS 개발 환경에서는 컴파일러와 언어 서비스를 통해 클래스, 구조 분해,
async
/await
같은 기능을 쉽게 배울 수 있다.'use strict'
는 TS 컴파일러 수준에서 사용되므로 코드에서 제거해야 한다.TC39의 GitHub repository와 TS의 릴리스 노트를 통해 최신 기능을 확인할 수 있다.
Last updated