무작정 프론트로 프로젝트를 시작하며 느낀 JS 지식 및 활용-1
백단만 개발하다 본격적으로 두달동안 Vue로 프로젝트를 하며 Javascript에 대한 코어 지식이 부족하다는 것을 뼈저리게 느꼈다. 이번 프로젝트를 하며 JS의 코어 지식들이, 단순히 "이론적으로 암기할 것"이 아닌 효율성과 유지보수, 안정적인 프로덕트를 위해 필수적으로 알아야 한다는 것을 깨달았다. 다음은 실제로 프로덕트를 개발하며 느낀 JS의 문제점들, 문제를 해결하기 위해 도입하고자 하는 것들에 대한 내용이다. 그중 호이스팅과 타입안정성에 대해 먼저 말해보고자 한다.
Javascript로 개발한다는 것은 생각보다 쿨하지 못한 과정이었다. 동적 타이핑 언어로 런타임에러를 뱉는 점, 타입안정성이 떨어지는 점, 관용적인 문법 표현들은 오히려 개발단계에 있어서 공수가 더 추가되는 결과를 얻게 되었다. 물론 빠르고 낮은 러닝커브가 JS의 장점일 수 있지만 프로젝트의 사이즈가 커지고 컴포넌트 depth가 깊어질 수록 앞의 단점이 더욱 부각되는 결과를 낳게 되었다.
때문에 프로젝트 기간동안 내린 결론은, 더 엄격하게 JS를 활용하는 것이었다. 좁게는 let과 const만을 활용한 변수 선언, 호이스팅 최소화부터 넓게는 타입안정성 및 빠른 Error Tracing을 위한 타입스크립트 도입등이 그것이다.
호이스팅 최소화
호이스팅은 함수 안에 있는 선언들을 모두 끌어올려 해당 함수 유효 범위의 최상단에 선언하는 것을 말한다. 언뜻 편리한 기능 같지만 협업을 하며 치명적인 문제점들이 발견됬다.
기존 java와 같은 컴파일 언어에 익숙한 개발자들에겐 생소한 JS 만의 독특한 기능인 것은 장점일 수 있지만, 개인적으로 단점으로 크게 다가왔다. 함수 선언식과 표현식을 예를들면
console.log(x); // undefined
console.log(getTen()); // 10
var x = 10;
function getTen() {
return 10;
}
변수 생성 단계에서 렉시컬 환경이 구성되고, x의 값은 undefined로, getTen()은 실제 함수 객체 값이 렉시컬 환경에 저장되어 있으므로 10을 정상적으로 출력한다.
함수 표현식으로 들어가면
console.log(getTen()); // TypeError: getTen is not a function
console.log(typeof getTen); // undefined
var getTen = function () {
return 10;
};
var로 선언한 객체 변수 생성 단계에서는 undefined로 할당하고 이는 함수호출이 불가능하므로 TypeError가 발생하게 된다.
물론 호이스팅이 편리한 점도 있지만, 변수 선언에 너무 많은 공수가 들어가는 것이 사실이다. 이론에 입각해 사용하면 되겠지만 좀 더 "엄격한" 환경을 원하게 되었다. 때문에 ES6에 도입된 let과 const만을 사용해 변수를 핸들링하기로 했다.
let / const는 근본적으로 선언부 상단에서 참조가 불가능하며 타입안정적이다. 하지만 호이스팅이 전혀 발생하지 않는 것은 아니다. 이전 var는 function-scoped 로써 함수를 범위로 한다면, let / const는 block-scoped 단위로 변수의 범위가 정해진다.
프로젝트를 하며 상호간에 규칙을 정했는데, 호이스팅을 제대로 모르더라도, 가급적 코드 상단부에 함수와 변수를 선언할 것, 이를통해 호이스팅으로 인한 스코프 꼬임 현상을 방지할 수 있었다.
또한 var를 최대한 지양하여, 혼란스럽고 쓸모없는 코드를 막고 혹시 모를 전역변수 선언과 그것에 따른 메모리 누수를 방지할 수 있었다.
타입스크립트 도입 이유
컴파일 타임에 발견되던 에러와 다르게 런타임에서의 에러 발생은 빠르게 에러를 발견하고 해결하기에 문제가 있었다. 항상 서버 동작 이후에 런타임 환경에서 에러를 확인할 수 있었고, 정확한 에러 원인을 파악하기에 시간이 더 투자되었다. 이는 개발 기간을 딜레이 시키고 퀄리티도 저하되는 결과로 이어졌다.
단기간의 이벤트(올림픽, 선거)관련 서비스는 크게 문제 안됬지만, 장기간 유지보수할 가능성이 높아질 수록 JS 개발에서 안정성은 문제요인으로 다가왔다.
사실 많은 사람들이 타입스크립트가 런타임에서의 에러 발생확률을 줄여준다고 말하지만, 개인적으로 이것이 애플리케이션의 안정성으로 이어지진 않는다고 생각한다. 안정성으로 이어지는 것은 테스트에서 비롯되고, 타입스크립트를 쓴다고 하더라도 테스트가 없어도 되는 것은 아니다.
때문에 타입스크립트의 진짜 강점은 런타임 에러에서 오는 안정성이 아닌 컴파일 에러에서 에러를 잡음으로써 정확하고 빠른 에러 트레이싱에 있다고 생각한다. 같은 타입 에러라도 런타임에서 발견하는 것, 컴파일 타임에 발견하는 것은 다르다. 더 정확히, 빠르게 에러를 발견하고 해결함으로써 생산성 및 유지보수에 탁월할 것이라고 판단하고 다음 앱 프로젝트에 도입하게 되었다.
use strict의 확장
기존에 ES6이전에는 "use strict"로 엄격 모드가 권장되었다. 처음에 입사하고 모든 코드에 엄격모드를 사용함으로써 당연시하게 되어왔던 엄격모드를 SPA와 ES6를 도입하며 이에 대한 필요성을 직접 느끼게 되었다. 당연하게 써왔던 것에 이유를 찾고 더 나은 형태를 고안하는 작업에서 코드에 대해 좀 더 심도깊은 생각을 하게 되었다.
엄격 컨텍스트를 통해 특정 액션, 많은 예외를 예방할 수 있고, 이것이 실수에서 비롯되는 것이라고 할지라도 에러를 최소화하는 코드를 짤 수 있다는 점에서 큰 이점이 있다. 변수를 재선언하고 재할당하는 경우나 같은 변수 이름을 전부 열어두는 것은 에러가 나올 확률이 높다.
이는 필수가 아니라 선택적 사항이긴 하지만, 타입스크립트, let/const가 대두되어 호이스팅을 지양하는 코드가 대세가 된 것은 당연한 흐름이라고 생각된다.