- 다양한 주제에 대해 자유롭게 글을 작성하는 게시판입니다.
Date 21/10/11 22:35:47
Name   ikuk
Subject   10년 전 책 "자바스크립트 핵심 가이드"를 읽고
오늘은 다소 기술중심적으로 적어보려 합니다.
웹개발에 관심이 없으신 분들에게는 미리 양해바랍니다.

저는 10여년 전 더글라스 크록포드의 Javascript: the good parts를 가지고 JS세계에 본격적으로 입문했습니다. 생활코딩의 이고잉님이 아직 수업을 준비하고 계시던 시절이라, 온라인 강의라고 부를 만한게 없던 시절이었기에, 코뿔소 책 영문판을 사서 달달 외우는 식으로 공부를 했죠. 참 이상한 언어인데, 왜 브라우저에 채택되었을까? 왜 많은 사람들이 좋아할까? 를 궁금해하던 무렵, 제가 가지고 있던 대부분의 궁금증을 해소해준 것이 바로 크록포드의 "자바스크립트 핵심 가이드" 였습니다.

https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=2608820

이 책을 지금 살 필요가 없으니 구매하려고 하지 마세요. 이 책의 저자는 JSLint의 저자임과 동시에 ECMAScript의 컨트리뷰터입니다. 이 책에 서술된 모든 후회와 고민은 새로운 자바스크립트에서 비로소 해결되었습니다. 새로운 버전이 나오지 않는 오래된 기술서를 읽는 것은 과거의 사람들이 가지고 있던 오래된 관념과 고민을 미래의 신이 된 것처럼 한없이 내려다보는 걸 즐기는 변태들이나 하는 짓입니다.

그럼에도 불구하고 크록포드의 이 책은 참 재미있는 내용들이 많습니다. 최근의 핵심 서비스 리팩토링 준비를 하면서 익힌 "JS 클린 아키텍처"로 여러 UI를 시험하다보니, 팩토리 패턴과 UseCase를 사용하는 설계가 아무래도 "JS적이지 않다"라는 위화감이 있었습니다. 거기서 "대체 JS적인게 뭐지?"라는 의문이 생겼고, 10년전에 샀던 위의 책을 다시 읽어보았더니, 그동안 잠시 잊고 있었던 것들을 생각하는 기회가 되었습니다.

이 책을 읽은 내용들을 정리해서, 개인적으로 인상깊었던 구절들을 다시 공유하고, 그 뒤에 제가 짧게 코멘트를 달았습니다(미리 말씀드리지만 제가 틀린 내용도 있었는데 여기에도 수정하지 않았습니다). 느슨한 자료형이나 프로토타입, 전역 변수과 같은 보편적인 이야기들은 가능한 덜어내고, 저자가 '나쁘지만 사용해야 하는 부분'이라고 소개한 글귀들에 집중했습니다. 그리고 이 내용을 기반으로 팀 내에 발표하는 자리를 만들었습니다. 재밌는 후일담이 많았지만, 일적인 것들이 많아서 그 부분은 공개하지 못하는 점도 양해 부탁드립니다.



## 자바스크립트의 좋고 나쁜 특징들

### p.14 자바스크립트 분석

자바스크립트의 함수는 어휘적 유효범위를 가진 일급 객체(first-class object) 입니다. 또한 주류가 된 첫 번째 람다(lambda) 언어며, 좀더 깊이 들어가면 이름처럼 자바에 가깝기보다는 Lisp 언어 그리고 Scheme 언어와 더 많은 공통점이 있습니다. 자바스크립트는 C의 옷을 입은 Lisp이라고 할 수 있습니다. 놀라울 정도로 강력한 언어적 특징은 바로 이러한 특성에서 기인한 것입니다.
...
자바스크립트에서 논란의 대상이 되는 기능은 프로토타입에 의한 상속입니다. 자바스크립트는 클래스가 필요 없는 객체 시스템이 있어서 특정 객체에 있는 속성들을 다른 객체에 직접 상속할 수 있습니다. 이러한 특성은 매우 강력하지만 클래스 기반의 언어에 익숙한 프로그래머에게는 친숙하지 않은 점입니다.

코멘트:
일급 객체는 Closure 패턴과, Anonymous Function의 설계가 편리하다는 장점을 가지고 있습니다. 우리 제어판의 UI라이브러리와 데이터 제어 및 검사 기능는 이러한 특성을 제대로 표현했으며, 가능하다면 다음 설계도 자료형 관리와 더불어 이러한 데이터 제어를 일원화하고 싶다고 생각합니다.
자바스크립트는 클래스가 필요 없는 프로토타입 언어이지만, 그와 별개로 클래스에 대한 대우가 많이 달라졌습니다. 13년 전과 비교하면 OOP와 FP가 양립하면서, 클래스와 상속의 유지보수가 불편하다는 의견이 주류가 되었고, golang, TS는 interface를, swift는 protocol를, rust의 경우 trait를 이용한 설계가 주목 받고 있습니다. (자연스레 클래스 기반 언어인 JAVA의 영향력도 약해지고 있습니다. )



### p.53 함수 호출 패턴


var value = 0;
var add = function (a, b) {
this.value = a + b //what the heck?
return a + b;
};
var sum = add(3, 4) // value = 7, sum = 7


함수를 이 패턴으로 호출할 때 this는 전역객체에 바인딩됩니다. 이런 특성은 언어 설계 단계에서의 실수입니다. 만약 언어를 바르게 설계했다면, 내부 함수를 호출할 때 이 함수의 this는 외부 함수의 this 변수에 바인딩되어야 합니다. 이러한 오류의 결과는 메소드가 내부 함수를 사용하여 자신의 작업을 돕지 못한다는 것입니다...

다행히도 이러한 문제를 해결하기 위한 쉬운 대안이 있습니다. 그 대안은 메소드에서 변수를 정의한 후 여기에 this를 할당하고, 내부 함수는 이 변수를(관례적으로 that을 사용함) 통해서 메소드의 this에 접근하는 방법입니다.

코멘트:
제어판의 많은 버튼을 만들어봤다면, _form 에서  const that = this; 와 같은 선언에 익숙할 것입니다. 이를 해결하기 위해 React와 같은 많은 프레임워크가 Props를 이용해 전달하는 등 특유의 라이프 사이클과 이를 이용한 여러 해결책을 제시하고 있지만, 아직 장벽이 높은 것은 사실입니다.



### p.54 생성자 호출 패턴

자바스크립트는 프로토타입에 의해서 상속이 이루어지는 언어입니다. 이 말은 객체가 자신의 속성들을 다른 객체에 바로 상속할 수 있다는 뜻입니다. 자바스크립트는 클래스가 없습니다.

이러한 특성은 현존하는 언어들의 경향과는 조금 다른 급진적인 것입니다. 오늘날 대부분의 언어는 클래스를 기반으로 하고 있습니다. 프로토타입에 의한 상속은 매우 표현적이지만 널리 알려져 있지 않습니다. 자바스크립트 자체도 자신의 프로토타입적 본성에 확신이 없었던지, 클래스 기반의 언어들을 생각나게 하는 객체 생성 문법을 제공합니다. 클래스 기반의 프로그래밍에 익숙한 프로그래머들에게 프로토타입에 의한 상속은 받아들여지지 못했고, 클래스를 사용하는 듯한 구문은 자바스크립트의 진정한 프로토타입적 속성을 애매하게 만들었습니다. 이는 양쪽에게 모두 최악의 결과라고 할 수 있습니다.


var Quo = function (string) {
this.status = string;
};

Quo.prototype.get_status = function() {
return this.status;
}

var myQuo = new Quo("VERY CONFUSING.");
console.log(myQuo.get_status()); // "VERY CONFUSING."


new라는 전치 연산자와 함께 사용하도록 만든 함수를 생성자라고 합니다. 일반적으로 생성자는 이니셜을 대문자로 표기하여 지정합니다. 생성자를 new없이 호출하면 컴파일 시간이나 실행시간에 어떠한 경고도 없어서 알 수 없는 결과를 초래합니다. 그러므로 대문자 표기법을 사용하여 해당 함수가 생성자라고 구분하는 것은 매우 중요합니다.

생성자 함수를 사용하는 스타일은 권장 사항이 아닙니다.

코멘트:
JS에서 생성자를 사용하는 변수만 이름에 PascalCase를 쓰는 전통적인 이유입니다. 또 JS 특유의 클래스 Constructor 역시 객체 함수의 일부임을 알 수 있습니다. JS의 클래스는 클래스가 아니고 함수입니다.



### p.66 클로저

Quo 생성자는 status라는 속성과 get_status라는 메소드를 가진 객체를 생성합니다. 하지만 status라는 변수를 바로 접근할 수 있기 때문에 getter 역할을 하는 get_status는 별 쓸모가 없어 보입니다. get_status가 쓸모가 있으려면 status 속성이 private이어야 할 것입니다. 그러면 이제 그렇게 되도록 quo라는 함수를 정의해 보겠습니다.


...
var quo = function (status) {
return {
get_status: function () {
return status;
}
}

var myQuo2 = quo("THIS IS CLOSURED.");
console.log(myQuo2.get_status()); // "THIS IS CLOSURED."


quo 함수는 new 키워드 없이 사용하게 설계됐습니다. 그래서 이름을 대문자로 표기하지 않았습니다. quo를 호출하면 get_status 메소드가 있는 객체를 반환합니다. 이 객체에 대한 참조는 myQuo2에 저장됩니다. get_status는 status 매개 변수의 복사본에 접근할 수 있는 권한을 갖는 것이 아니라 매개변수 그 자체에 대한 접근 권한을 갖습니다. 이러한 것이 가능한 것은 함수가 자신이 생성된 함수, 즉 자신을 내포하는 함수의 문맥(context)에 접근할 수 있기 때문입니다.

코멘트:
이 설계는 이후 TS의 인터페이스, 타입에도 크나큰 영향을 끼쳤습니다. 인터페이스, 타입, 그리고 인터페이스나 타입을 implements 하는 클래스도 생성자 선언이 불필요한 이유도 클로저를 사용하기 때문입니다.



### p.86 의사 클래스 방식

설상가상으로 생성자 함수를 사용하는데는 심각한 위험이 있습니다. 만약 생성자 함수를 호출할 때 new 연산자를 포함하는 것을 잊게 되면 this는 새로운 객체와 바인딩되지 않습니다. 불행히도 this는 전역 객체와 연결되고 이렇게 됨으로써 새로운 객체에 필요한 기능을 추가하게 되는 것이 아니라 전역변수에 영향을 미치게 됩니다. 가히 심각한 문제라고 밖에 할 수 없습니다. new를 누락해도 어떠한 컴파일 경고나 실행시간 경고가 발생하지 않습니다.

이러한 점은 언어에 있어서 심각한 설계 오류입니다. 이러한 문제점을 경감시키기 위한 한가지 방법은 단어 첫 글자를 대문자로 표기하는 방법을 모든 생성자 함수의 이름에 사용하고 그 외 다른 것들은 이 표기법을 사용하지 않는 것입니다. 이러한 방법을 사용함으로써 그나마 new를 빼먹은 것을 보다 쉽게 식별할 수 이 있습니다. 물론 더 나은 대안은 new를 사용하는 방식을 피하는 것 입니다.

의사 클래스를 사용하는 방법은 자바스크립트에 익숙하지 않은 프로그래머들에게 편안함을 제공합니다. 하지만 이 방법은 자바스크립트라는 언어가 가진 진정한 속성을 가리기도 합니다. 클래스에서 영감 받은 표기법은 프로그래머들에게 불필요하게 복잡하고 단계가 많은 구조를 만들도록 유도할 수 있습니다. 클래스 계층의 복잡함 대부분은 정적 타입 확인이라는 제약사항으로 인해 발생합니다. 자바스크립트는 이러한 제약사항으로부터 완전히 자유롭습니다. 클래스 기반의 언어에서는 클래스 상속이 코드를 재사용할 수 있는 유일한 방법이지만 자바스크립트는 더 좋은 방법들이 있습니다.

코멘트:
생성자가 JS에 도입된 이유는 아마도 클래스에 익숙한 프로그래머들에게 익숙한 가독성을 제공하기 위해서였을 것입니다. 그리고 클래스(와 상속)의 가장 중요한 이점은 자료형 검사를 통한 제약사항을 통해 코드 품질을 향상시킬 수 있기 때문입니다. 하지만 TS를 도입하게 되면 이런 많은 이점을 클래스를 사용하지 않고도 누릴 수 있습니다. Interface와 Type을 쓰면 됩니다.



### p.162 스타일

필자는 switch문의 case에서 하나의 case절을 실행한 후 벗어나지 않고 (break문) 다음 case로 내려가게 하지 않습니다. 예전에 다음 case 절까지 실행시키는 것이 때때로 좋은 이유에 관해 강력한 연설을 한 후에, 필자가 작성한 코드에서 이로 인해 발생한 의도하지 않는 버그를 찾게 됐습니다. 이러한 경험을 통해 다음 case 절까지 실행하는 것이 나쁘다는 것을 배우게 된 것은 어쩌면 행운이었습니다. 왜냐하면 이후로 언어의 기능들을 살펴볼 때, 때때로 유용하지만 어떤 경우에는 위험한 기능들에 특별한 주의를 기울이기 때문입니다. 이러한 것들은 제대로 사용됐는지 알기가 어렵기 때문에 가장 안 좋은 부분이라고 할 수 있습니다. 이런 부분들이 버그가 숨어있는 부분입니다.

코멘트:
대다수의 다른 언어의 switch-case는 1개의 case로 종료되지만, JS는 1:N으로 case문을 실행시킬 수 있기 때문에 반드시 break가 필요합니다. 이는 지금도 JS를 설계한 사람들이 많이 아쉬워하는 구조이기도 합니다.
또한 새로운 기능을 도입할 때 우리는 유용한 기능 뒷면의 위험성도 반드시 고려할 필요가 있습니다.



### p.169 유효범위(Scope)

자바스크립트의 구문은 C에서 가져온 것입니다. C 같은 구문을 가진 다른 언어들은 블록(중괄호로 묶인 문장들의 집합)이 유효범위를 가집니다. 블록 내에서 선언 된 변수들은 블록 바깥에서 보이지 않습니다. 자바스크립트는 블록 구문을 사용하기는 하지만, 블록 유효범위를 제공하지 않습니다. 즉 블록 내에서 선언한 변수는 블록을 포함하는 함수 내부 모든 곳에서 볼 수 있습니다. 이러한 속성은 다른 언어에 익숙한 프로그래머들에게는 놀랄만한 일입니다.

대부분의 언어에서는 일반적으로 변수가 처음 사용되는 지점에서 선언하는 것이 가장 좋습니다. 하지만 자바스크립트에서는 블록 유효범위를 갖지 않기 때문에 이렇게 하는 것이 좋지 않습니다. 대신에 함수에서 사용하는 모든 변수를 함수의 첫 부분에서 선언하는 것이 좋습니다.

코멘트:
ES2015, 그리고 많은 Bundle 도구가 등장하면서, 블록 범위의 코드를 작성할 수 있게 되었습니다. 실제로 거의 모든 bundle 스크립트의 첫부분은 함수표현식으로 block scope를 만들고난 뒤 변수를 선언하는 것으로 시작합니다.



### p. 188 함수 문장 vs 함수 표현식

함수 문장 `function foo(){};`은 위로 끌어올려지는 대상이 됩니다(hoisting). 이 말은 함수가 위치한 곳과 관계 없이 함수가 정의된 곳의 유효범위 가장 상위로 이동된다는 뜻입니다. 이러한 특징은 함수를 사용하기 전에 반드시 선언해야 한다는 요구사항을 경감시키는데, 결국 필자 생각에는 구조를 엉성하게 만들 뿐입니다. 또한 이런 특징은 if 문에서 함수 문장 사용을 금하게 됩니다. 밝혀진 바에 따르면 대부분의 브라우저에서는 if 문 내에서 함수 문장 사용을 허용하고 있습니다. 하지만 이렇게 사용된 함수 문장이 어떻게 해석되는지는 브라우저마다 제 각각입니다. 이런 점은 잠재적인 문제를 발생시킵니다.

공식적인 문법은 function이라는 단어로 시작하는 문장을 함수 문장이라고 가정하고 있기 때문에 문장의 첫 번째 부분에 함수 표현식을 사용할 수 없습니다. 이를 위한 대안은 함수 표현식을 다음의 예처럼 괄호로 묶는 것입니다.


(function () {
var hidden_variable;

// THIS AREA MIGHT AFFECTS WHOLE ENVIRONMENT,
        // BUT WON'T GENERATE ANY GLOBAL VARIABLES.
}) ();


코멘트:
과거 jquery나 script를 직접 코딩한 경험이 있다면, hoisting 현상으로 인해 브라우저에 따라 JS 동작이 바뀌는 경험을 한 적이 있을 것입니다. 위에서 말한 bundle이 이러한 문제를 해결해줍니다. 그리고 JS를 이런 애플리케이션 단위의 빌드가 가능하게 된 것 역시 프레임워크가 이를 고려한 거대한 클로저이기 때문입니다.



---

뭔가를 정하는 미팅이 아니더라도, 기술자들은 기술 그 것에 대해 순수히 논하는 자리에서 또 성장하고 새로운 고민 거리를 얻습니다. 위에 이야기된 7가지 외에도 정말정말 재밌는 코멘터리가 가득하지만, 직원들의 시간은 중요하기 때문에 추리고 추렸습니다. 그리고 대부분의 주니어들이 ESNext로 개발을 시작하기 때문에, 바닐라 상태의 자바스크립트나 jQuery를 다뤄본 경험이 매우 적었습니다. "그때는 그랬지" 같은 사담을 나눌 수 있는 건 잔뼈가 굵은 시니어들이 대부분입니다.

하지만 자바스크립트는 그대로 있습니다. 대부분의 번들 프로그램이 만드는 코드의 첫 줄을 보면 브라우저간 hoisting 차이를 해결하기 위해 바로 함수 블록 스코프를 만들고 시작합니다. 위의 문제가 근본적으로 해결이 되고 안되고의 영역이 아닙니다. 크록포드 선생님의 말 그대로 "나쁘지만 사용해야 하는 부분"이기 때문입니다. 그렇기 때문에 이러한 레거시를 이해함으로써 우리는 이 특이한 언어가 어떻게 생태계를 만들고 살아남았는지 근본적으로 이해할 수 있게 됩니다.



위의 코멘터리나 글들을 혹시 읽으시고, 아 이런거 한번 공부해보고싶다 라는 생각이 드셨다면 당신은 프론트엔드 개발자가 될 인재입니다.
이고잉님의 역작 WEB2수업을 통해 (WEB1은 HTML CSS입니다) JS를 공부해보시는 것을 강력 추천해드립니다.
https://opentutorials.org/course/3085

코멘터리에 대한 코멘트 혹은 비판도 주시면 열심히 소통하겠습니다.
얼기설기한 글 읽어주셔서 감사합니다.





8


    목록
    번호 제목 이름 날짜 조회 추천
    12180 일상/생각마치츄카町中華 5 向日葵 21/10/18 3923 34
    12179 게임[LOL] 10월 18일 월요일 오늘의 일정 3 발그레 아이네꼬 21/10/18 3061 1
    12178 게임펄어비스 - DokeV 게임 플레이 영상 공개 6 2막4장 21/10/17 4181 2
    12177 도서/문학삼국지를 지금 읽으면 다르게 보이는 점 7 rustysaber 21/10/16 4392 1
    12176 기타[번개] 온라인 번개입니다. 토요일 오후 1:30 부터 -종료 18 풀잎 21/10/16 3935 1
    12175 음악[팝송] 오 원더 새 앨범 "22 Break" 김치찌개 21/10/16 2846 1
    12174 게임[LOL] 10월 17일 일요일 오늘의 일정 5 발그레 아이네꼬 21/10/15 3199 1
    12173 게임[LOL] 10월 16일 토요일 오늘의 일정 9 발그레 아이네꼬 21/10/15 3073 1
    12172 기타성범죄자 조주빈 42년형 소감문 48 흑마법사 21/10/15 5045 1
    12171 음악[팝송] 제레미 주커 새 앨범 "CRUSHER" 김치찌개 21/10/15 2661 1
    12170 일상/생각X 같은 상사 vs X 같은 팀원 13 Picard 21/10/15 3892 12
    12169 정치윤석열 질타한 법원 "중대 비위행위, 정직 2개월도 가볍다" 외 2 33 구글 고랭이 21/10/15 4323 2
    12168 게임[LOL] 10월 15일 금요일 오늘의 일정 8 발그레 아이네꼬 21/10/14 2833 2
    12166 정치윤총장 징계취소소송 패소여파 26 매뉴물있뉴 21/10/14 4448 1
    12165 일상/생각만만한 팀장이 옆팀 꼰대 팀장을 보면서 드는 생각 17 Picard 21/10/13 4717 7
    12164 일상/생각미국 핀테크 스타트업 인터뷰 후기 19 개랑이 21/10/13 4115 5
    12163 게임[LOL] 10월 13일 수요일 오늘의 일정 5 발그레 아이네꼬 21/10/12 3707 1
    12162 게임[디2 레저렉션] 스타터 캐릭에 대한 고찰 - 해머딘 18 윤지호 21/10/12 5906 9
    12161 꿀팁/강좌여자 생활한복 경험담+코디팁+쇼핑추천(부제:남편이여 선물하라) 33 흑마법사 21/10/12 5357 26
    12160 정치민주당 결선투표와 무효표 논란에 대한 시각 26 Picard 21/10/12 3939 0
    12159 음악망해버렸네 7 바나나코우 21/10/12 3225 3
    12158 게임[LOL] 10월 12일 화요일 오늘의 일정 4 발그레 아이네꼬 21/10/11 2729 2
    12157 IT/컴퓨터10년 전 책 "자바스크립트 핵심 가이드"를 읽고 12 ikuk 21/10/11 3980 8
    12156 경제정부가 대출을 막는 이유 27 Folcwine 21/10/11 4560 8
    12155 일상/생각약간의 일탈과 음주 이야기 2 머랭 21/10/11 3218 15
    목록

    + : 최근 2시간내에 달린 댓글
    + : 최근 4시간내에 달린 댓글

    댓글