- 다양한 주제에 대해 자유롭게 글을 작성하는 게시판입니다.
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 함수 호출 패턴
함수를 이 패턴으로 호출할 때 this는 전역객체에 바인딩됩니다. 이런 특성은 언어 설계 단계에서의 실수입니다. 만약 언어를 바르게 설계했다면, 내부 함수를 호출할 때 이 함수의 this는 외부 함수의 this 변수에 바인딩되어야 합니다. 이러한 오류의 결과는 메소드가 내부 함수를 사용하여 자신의 작업을 돕지 못한다는 것입니다... 다행히도 이러한 문제를 해결하기 위한 쉬운 대안이 있습니다. 그 대안은 메소드에서 변수를 정의한 후 여기에 this를 할당하고, 내부 함수는 이 변수를(관례적으로 that을 사용함) 통해서 메소드의 this에 접근하는 방법입니다. 코멘트: 제어판의 많은 버튼을 만들어봤다면, _form 에서 const that = this; 와 같은 선언에 익숙할 것입니다. 이를 해결하기 위해 React와 같은 많은 프레임워크가 Props를 이용해 전달하는 등 특유의 라이프 사이클과 이를 이용한 여러 해결책을 제시하고 있지만, 아직 장벽이 높은 것은 사실입니다. ### p.54 생성자 호출 패턴 자바스크립트는 프로토타입에 의해서 상속이 이루어지는 언어입니다. 이 말은 객체가 자신의 속성들을 다른 객체에 바로 상속할 수 있다는 뜻입니다. 자바스크립트는 클래스가 없습니다. 이러한 특성은 현존하는 언어들의 경향과는 조금 다른 급진적인 것입니다. 오늘날 대부분의 언어는 클래스를 기반으로 하고 있습니다. 프로토타입에 의한 상속은 매우 표현적이지만 널리 알려져 있지 않습니다. 자바스크립트 자체도 자신의 프로토타입적 본성에 확신이 없었던지, 클래스 기반의 언어들을 생각나게 하는 객체 생성 문법을 제공합니다. 클래스 기반의 프로그래밍에 익숙한 프로그래머들에게 프로토타입에 의한 상속은 받아들여지지 못했고, 클래스를 사용하는 듯한 구문은 자바스크립트의 진정한 프로토타입적 속성을 애매하게 만들었습니다. 이는 양쪽에게 모두 최악의 결과라고 할 수 있습니다.
new라는 전치 연산자와 함께 사용하도록 만든 함수를 생성자라고 합니다. 일반적으로 생성자는 이니셜을 대문자로 표기하여 지정합니다. 생성자를 new없이 호출하면 컴파일 시간이나 실행시간에 어떠한 경고도 없어서 알 수 없는 결과를 초래합니다. 그러므로 대문자 표기법을 사용하여 해당 함수가 생성자라고 구분하는 것은 매우 중요합니다. 생성자 함수를 사용하는 스타일은 권장 사항이 아닙니다. 코멘트: JS에서 생성자를 사용하는 변수만 이름에 PascalCase를 쓰는 전통적인 이유입니다. 또 JS 특유의 클래스 Constructor 역시 객체 함수의 일부임을 알 수 있습니다. JS의 클래스는 클래스가 아니고 함수입니다. ### p.66 클로저 Quo 생성자는 status라는 속성과 get_status라는 메소드를 가진 객체를 생성합니다. 하지만 status라는 변수를 바로 접근할 수 있기 때문에 getter 역할을 하는 get_status는 별 쓸모가 없어 보입니다. get_status가 쓸모가 있으려면 status 속성이 private이어야 할 것입니다. 그러면 이제 그렇게 되도록 quo라는 함수를 정의해 보겠습니다.
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이라는 단어로 시작하는 문장을 함수 문장이라고 가정하고 있기 때문에 문장의 첫 번째 부분에 함수 표현식을 사용할 수 없습니다. 이를 위한 대안은 함수 표현식을 다음의 예처럼 괄호로 묶는 것입니다.
코멘트: 과거 jquery나 script를 직접 코딩한 경험이 있다면, hoisting 현상으로 인해 브라우저에 따라 JS 동작이 바뀌는 경험을 한 적이 있을 것입니다. 위에서 말한 bundle이 이러한 문제를 해결해줍니다. 그리고 JS를 이런 애플리케이션 단위의 빌드가 가능하게 된 것 역시 프레임워크가 이를 고려한 거대한 클로저이기 때문입니다. --- 뭔가를 정하는 미팅이 아니더라도, 기술자들은 기술 그 것에 대해 순수히 논하는 자리에서 또 성장하고 새로운 고민 거리를 얻습니다. 위에 이야기된 7가지 외에도 정말정말 재밌는 코멘터리가 가득하지만, 직원들의 시간은 중요하기 때문에 추리고 추렸습니다. 그리고 대부분의 주니어들이 ESNext로 개발을 시작하기 때문에, 바닐라 상태의 자바스크립트나 jQuery를 다뤄본 경험이 매우 적었습니다. "그때는 그랬지" 같은 사담을 나눌 수 있는 건 잔뼈가 굵은 시니어들이 대부분입니다. 하지만 자바스크립트는 그대로 있습니다. 대부분의 번들 프로그램이 만드는 코드의 첫 줄을 보면 브라우저간 hoisting 차이를 해결하기 위해 바로 함수 블록 스코프를 만들고 시작합니다. 위의 문제가 근본적으로 해결이 되고 안되고의 영역이 아닙니다. 크록포드 선생님의 말 그대로 "나쁘지만 사용해야 하는 부분"이기 때문입니다. 그렇기 때문에 이러한 레거시를 이해함으로써 우리는 이 특이한 언어가 어떻게 생태계를 만들고 살아남았는지 근본적으로 이해할 수 있게 됩니다. 위의 코멘터리나 글들을 혹시 읽으시고, 아 이런거 한번 공부해보고싶다 라는 생각이 드셨다면 당신은 프론트엔드 개발자가 될 인재입니다. 이고잉님의 역작 WEB2수업을 통해 (WEB1은 HTML CSS입니다) JS를 공부해보시는 것을 강력 추천해드립니다. https://opentutorials.org/course/3085 코멘터리에 대한 코멘트 혹은 비판도 주시면 열심히 소통하겠습니다. 얼기설기한 글 읽어주셔서 감사합니다. 8
이 게시판에 등록된 ikuk님의 최근 게시물
|
자바스크립트의 가장 큰 강점은 독보적일 정도로 느슨한 타입이 아닐까 싶습니다.
뭐든 아무거나 추가하면 들어가는 오브젝트에 구버전 JS에선 콜백 함수가 일상일 정도로 아무데나 익명 함수가 들어갈 수 있는 상당히 동적인 언어입니다.
심지어 이게 어디까지 동적인지 테스트를 해봤는데 class 구문으로 정의된 클래스 원형 자체를 변수로 받아서 다른 클래스의 extends 뒤에 넣을 수 있더라구요.
요컨데 동적으로 클래스 정의와 상속이 가능하다는 의미인데 이게 가능한 언어는 지금까지 JS밖에 못봤습니다.
파이썬, 루비처럼 나름 동적... 더 보기
뭐든 아무거나 추가하면 들어가는 오브젝트에 구버전 JS에선 콜백 함수가 일상일 정도로 아무데나 익명 함수가 들어갈 수 있는 상당히 동적인 언어입니다.
심지어 이게 어디까지 동적인지 테스트를 해봤는데 class 구문으로 정의된 클래스 원형 자체를 변수로 받아서 다른 클래스의 extends 뒤에 넣을 수 있더라구요.
요컨데 동적으로 클래스 정의와 상속이 가능하다는 의미인데 이게 가능한 언어는 지금까지 JS밖에 못봤습니다.
파이썬, 루비처럼 나름 동적... 더 보기
자바스크립트의 가장 큰 강점은 독보적일 정도로 느슨한 타입이 아닐까 싶습니다.
뭐든 아무거나 추가하면 들어가는 오브젝트에 구버전 JS에선 콜백 함수가 일상일 정도로 아무데나 익명 함수가 들어갈 수 있는 상당히 동적인 언어입니다.
심지어 이게 어디까지 동적인지 테스트를 해봤는데 class 구문으로 정의된 클래스 원형 자체를 변수로 받아서 다른 클래스의 extends 뒤에 넣을 수 있더라구요.
요컨데 동적으로 클래스 정의와 상속이 가능하다는 의미인데 이게 가능한 언어는 지금까지 JS밖에 못봤습니다.
파이썬, 루비처럼 나름 동적인 언어들도 이건 안됐는데 말이죠. 그나마 C#에서 제한적인 동적 클래스를 프리뷰 단계에서 지원중인 걸로 알고 있습니다.
솔직히 개인적인 의견으로는 타입스크립트를 쓰면 자바스크립트를 쓰는 의미가 없지않나 싶습니다.
웹 환경에선 자바스크립트가 표준이니 어쩔수 없이 자바스크립트를 써야하는 환경에선 타입스크립트를 쓰는게 낫겠지만 nodejs처럼 자바스크립트로 프로그램을 만드는 경우라면 타입스크립트의 역할은 그냥 인텔리센스가 이뤄지도록 타입 정의만 하는 수준에서 끝나는게 좋다고 생각합니다.
실제로도 전 nodejs에선 클래스를 거의 사용하지 않구요.
그나저나 코멘트에서 요즘에 많이 개선됐다고 하는 문제들은 nodejs로 백엔드 개발할때는 여전히 유효한게 많네요. 읽으면서 제가 옛날부터 자바스크립트를 써온거 아닐까 의심스러울 정도였습니다. 입문한지 2년밖에 안됐는데...
개인적으로는 자바스크립트의 가장 큰 발전은 async await를 지원한것 아닐까 싶네요. async await가 아니라 콜백으로만 작동하는 비동기 모듈들은 코드 작동 순서도 꼬이고 가독성도 떨어지는지라 이걸 Promise로 감싸야하는 귀찮음이 있습니다.
뭐든 아무거나 추가하면 들어가는 오브젝트에 구버전 JS에선 콜백 함수가 일상일 정도로 아무데나 익명 함수가 들어갈 수 있는 상당히 동적인 언어입니다.
심지어 이게 어디까지 동적인지 테스트를 해봤는데 class 구문으로 정의된 클래스 원형 자체를 변수로 받아서 다른 클래스의 extends 뒤에 넣을 수 있더라구요.
요컨데 동적으로 클래스 정의와 상속이 가능하다는 의미인데 이게 가능한 언어는 지금까지 JS밖에 못봤습니다.
파이썬, 루비처럼 나름 동적인 언어들도 이건 안됐는데 말이죠. 그나마 C#에서 제한적인 동적 클래스를 프리뷰 단계에서 지원중인 걸로 알고 있습니다.
솔직히 개인적인 의견으로는 타입스크립트를 쓰면 자바스크립트를 쓰는 의미가 없지않나 싶습니다.
웹 환경에선 자바스크립트가 표준이니 어쩔수 없이 자바스크립트를 써야하는 환경에선 타입스크립트를 쓰는게 낫겠지만 nodejs처럼 자바스크립트로 프로그램을 만드는 경우라면 타입스크립트의 역할은 그냥 인텔리센스가 이뤄지도록 타입 정의만 하는 수준에서 끝나는게 좋다고 생각합니다.
실제로도 전 nodejs에선 클래스를 거의 사용하지 않구요.
그나저나 코멘트에서 요즘에 많이 개선됐다고 하는 문제들은 nodejs로 백엔드 개발할때는 여전히 유효한게 많네요. 읽으면서 제가 옛날부터 자바스크립트를 써온거 아닐까 의심스러울 정도였습니다. 입문한지 2년밖에 안됐는데...
개인적으로는 자바스크립트의 가장 큰 발전은 async await를 지원한것 아닐까 싶네요. async await가 아니라 콜백으로만 작동하는 비동기 모듈들은 코드 작동 순서도 꼬이고 가독성도 떨어지는지라 이걸 Promise로 감싸야하는 귀찮음이 있습니다.
현장감있는 댓글 감사합니다! 말씀하신 내용 대부분에 공감하는 바입니다...
동적 클래스 정의/상속이 가능하다고도 표현할 수 있지만, 거꾸로 JS의 클래스는 클래스가 아니라고 말할 수도 있습니다.
실제로 클래스가 아닙니다. 함수도 new연산자를 이용해 동적 정의/상속이 가능합니다. 그런 프로토타입이 존재할 뿐이죠.
TS의 경우 type, interface, abstract class에서 전부 extend할 수 있으며 변태적으로 implements를 거는 것도 허용합니다. 한술 더 뜹니다.
그리고 이런 설계가 요새 굉장... 더 보기
동적 클래스 정의/상속이 가능하다고도 표현할 수 있지만, 거꾸로 JS의 클래스는 클래스가 아니라고 말할 수도 있습니다.
실제로 클래스가 아닙니다. 함수도 new연산자를 이용해 동적 정의/상속이 가능합니다. 그런 프로토타입이 존재할 뿐이죠.
TS의 경우 type, interface, abstract class에서 전부 extend할 수 있으며 변태적으로 implements를 거는 것도 허용합니다. 한술 더 뜹니다.
그리고 이런 설계가 요새 굉장... 더 보기
현장감있는 댓글 감사합니다! 말씀하신 내용 대부분에 공감하는 바입니다...
동적 클래스 정의/상속이 가능하다고도 표현할 수 있지만, 거꾸로 JS의 클래스는 클래스가 아니라고 말할 수도 있습니다.
실제로 클래스가 아닙니다. 함수도 new연산자를 이용해 동적 정의/상속이 가능합니다. 그런 프로토타입이 존재할 뿐이죠.
TS의 경우 type, interface, abstract class에서 전부 extend할 수 있으며 변태적으로 implements를 거는 것도 허용합니다. 한술 더 뜹니다.
그리고 이런 설계가 요새 굉장히 트렌디한거라 비단 JS만의 이야긴 아닙니다 (10여년 전 만하더라도 이런 개념이 커뮤니티에서 그리 환영받지 못했습니다)
그리고 JS로 전부 다 할 수 있으면 굳이TS를 쓸 필요는 없을...지도 모르겠지만, 유지보수 차원에서라도 TS를 미리 도입해두는 편이 좋을지도 모릅니다! 대부분의 유명한 npm 패키지들은 타입스크립트용 @types패키지를 추가로 배포합니다.
nodejs를 이용한 express axios등의 API서버를 구축하고 계시다면 필연 TS의 도움을 많이 받으실 겁니다. 모듈 설계가 훨씬 깔끔해지며 declare namespace를 더이상 쓰지 않아도 됩니다.
async await는 비동기적인 이벤트 루프를 해결하기 위한 획기적인 방법이고, 자바스크립트의 이벤트 루프를 이해하는 가장 중요한 실마리기도 하죠.
그렇지만 콜백의 가독성에서 획기적으로 진화했다고 느끼긴 어려웠습니다.
또 실제로 미묘하게 콜백이 await보다 리스폰스가 더 빠른 경우가 많았어서 극한의 퍼포먼스가 필요한 경우에는 콜백을 다시 찾는 경우도 많았습니다.
deno를 설계한 라이언 역시 (개인적인 호불호로) node 엔진에 promise를 도입하지 않고 async/await를 쓴 것에 대해 후회하기도 합니다.
그리고 결국 JS를 버리고 nginx openresty를 뒤적거리는거죠. lua가 연산이 더 빠르니까요. 결국 JS는 가독과 빠른 유지보수가 아니면 실용성이 적어집니다.
그럼에도 불구하고 많은 사람들이 JS를 계속 배우고 쓴다는게 참 재밌습니다. 결국 "나한테는" 효용과 편리가 극한의 퍼포먼스보다 더 중요한거니까요.
동적 클래스 정의/상속이 가능하다고도 표현할 수 있지만, 거꾸로 JS의 클래스는 클래스가 아니라고 말할 수도 있습니다.
실제로 클래스가 아닙니다. 함수도 new연산자를 이용해 동적 정의/상속이 가능합니다. 그런 프로토타입이 존재할 뿐이죠.
TS의 경우 type, interface, abstract class에서 전부 extend할 수 있으며 변태적으로 implements를 거는 것도 허용합니다. 한술 더 뜹니다.
그리고 이런 설계가 요새 굉장히 트렌디한거라 비단 JS만의 이야긴 아닙니다 (10여년 전 만하더라도 이런 개념이 커뮤니티에서 그리 환영받지 못했습니다)
그리고 JS로 전부 다 할 수 있으면 굳이TS를 쓸 필요는 없을...지도 모르겠지만, 유지보수 차원에서라도 TS를 미리 도입해두는 편이 좋을지도 모릅니다! 대부분의 유명한 npm 패키지들은 타입스크립트용 @types패키지를 추가로 배포합니다.
nodejs를 이용한 express axios등의 API서버를 구축하고 계시다면 필연 TS의 도움을 많이 받으실 겁니다. 모듈 설계가 훨씬 깔끔해지며 declare namespace를 더이상 쓰지 않아도 됩니다.
async await는 비동기적인 이벤트 루프를 해결하기 위한 획기적인 방법이고, 자바스크립트의 이벤트 루프를 이해하는 가장 중요한 실마리기도 하죠.
그렇지만 콜백의 가독성에서 획기적으로 진화했다고 느끼긴 어려웠습니다.
또 실제로 미묘하게 콜백이 await보다 리스폰스가 더 빠른 경우가 많았어서 극한의 퍼포먼스가 필요한 경우에는 콜백을 다시 찾는 경우도 많았습니다.
deno를 설계한 라이언 역시 (개인적인 호불호로) node 엔진에 promise를 도입하지 않고 async/await를 쓴 것에 대해 후회하기도 합니다.
그리고 결국 JS를 버리고 nginx openresty를 뒤적거리는거죠. lua가 연산이 더 빠르니까요. 결국 JS는 가독과 빠른 유지보수가 아니면 실용성이 적어집니다.
그럼에도 불구하고 많은 사람들이 JS를 계속 배우고 쓴다는게 참 재밌습니다. 결국 "나한테는" 효용과 편리가 극한의 퍼포먼스보다 더 중요한거니까요.
요즘 구글 검색으로 파이썬을 조금씩 익히면서 텔레그램 봇을 만들어서 활용하고 있는데 개발에 관련된 글을 읽게되어서 너무 반갑네요. 개발에 대해서 교육을 받은적은 없지만 깃헙과 스택오버플로우 답변에 있는 코드를 가져다가 이리저리 붙이고 작동하는 것을 보며 레고를 조립하는 것과 같은 신기함을 느끼며 보내는 요즘입니다. 써주신 내용중에서 제가 어떤 내용인지 짐작할 수 있는 부분은 switch문 정도네요;;ㅎㅎ 비슷(?)하게 파이썬에서는 if와 elif를 사용했는데요 이게 비슷한 거 맞겠죠?ㅎㅎ 흥미있게 읽었습니다. 감사합니다 \^^/
재미있네요.. 하는 일 따라서 c, old c++, Java, c++11, python 2,3 등으로 계속 언어에 좀 익숙해질만 하면 옮겨다니길 계속하고 있는데, 또 얼마전 간단한 웹앱 제작때문에 Javascript 와 typescript 를 뒤늦게 속성으로 공부하고 보니 자바스크립트가 생각보다 근본없는(?) 변태같은 언어라고 느낀 이유가 본문에 잘 정리되 있어서 참고가 됩니다. 특히 c++11에서는 람다함수 정의시 캡처되는 context variable의 변수 t... 더 보기
재미있네요.. 하는 일 따라서 c, old c++, Java, c++11, python 2,3 등으로 계속 언어에 좀 익숙해질만 하면 옮겨다니길 계속하고 있는데, 또 얼마전 간단한 웹앱 제작때문에 Javascript 와 typescript 를 뒤늦게 속성으로 공부하고 보니 자바스크립트가 생각보다 근본없는(?) 변태같은 언어라고 느낀 이유가 본문에 잘 정리되 있어서 참고가 됩니다. 특히 c++11에서는 람다함수 정의시 캡처되는 context variable의 변수 type을 사용자가 auto 혹은 type을 지정할수 있었는데 비해, 자바스크립트에서는 context variable capture 도 어떤 경우엔 선언시 캡처되는 경우가 잇고 어떤 경우엔 호출시 캡처되는 방식도 있고..특히 본문에서 언급한 this capture가 경우에 따라 ㅣ다르게 규격이 추가되면서 케이스별로 외우도록 해서 헷갈리는듯 합니다.
클래스처럼 생겼는데 알고보니 다 클래스가 아니고 다 객체 속성에 함수 할당하고 함수 context variable capture 에 함수 호출 규칙 추가해서 흉내내고 있는것도 공감되고요.. 물론 역사적으로 초창기 나온 스크립트언어라 그렇게 된거겠지만요..
그리고 헷갈리게 만드는 또다른 쪽이 콜백과 이벤트 루프의 조합인듯합니다. 공식 매뉴얼 보면 자바스크립트 이벤트 처리 방식과 콜백방식, 거기에서 파생발전된 async, await, pipe 등과 rxjs 방식 등 사용법이 비슷하지만 조금씩 다른 것들이 뒤섞여 쓰이다보니 다른 언어를 쓰던 용법이 익숙하지 않은 저 같은 사람들 입장에서 어려운듯 합니디
클래스처럼 생겼는데 알고보니 다 클래스가 아니고 다 객체 속성에 함수 할당하고 함수 context variable capture 에 함수 호출 규칙 추가해서 흉내내고 있는것도 공감되고요.. 물론 역사적으로 초창기 나온 스크립트언어라 그렇게 된거겠지만요..
그리고 헷갈리게 만드는 또다른 쪽이 콜백과 이벤트 루프의 조합인듯합니다. 공식 매뉴얼 보면 자바스크립트 이벤트 처리 방식과 콜백방식, 거기에서 파생발전된 async, await, pipe 등과 rxjs 방식 등 사용법이 비슷하지만 조금씩 다른 것들이 뒤섞여 쓰이다보니 다른 언어를 쓰던 용법이 익숙하지 않은 저 같은 사람들 입장에서 어려운듯 합니디
재야의 고수가 계셨군요...
CPU를 직접 다루는 언어가 아니라, 웹 브라우저라는 환경에서 동작하는 스크립트 언어다보니 워터폴식 이벤트 흐름(먼저 로딩된 것이 먼저 표시된다라는 대전제)이 필연적인 설계가 될 수 밖에 없었지요. 모던JS가 이벤트루프를 신경써야하는 수준의 애플리케이션이 되다보니 콜백 지옥이나 pipe 처리를 빌린 rxjs(이거도 진짜 오랜만이네요! 요새 주니어들은 뭔지 잘 모를겁니다.)같은 여러 대안이 있었지만, 이제 그냥 프로미스로 통일되었고요.
이제 정말 오래된 2014년 영상이지만, 이벤트루프에 대한 예시를 너무 적절하게 사용한 훌륭한 강의입니다(한글 자막이 있습니다). ... 더 보기
CPU를 직접 다루는 언어가 아니라, 웹 브라우저라는 환경에서 동작하는 스크립트 언어다보니 워터폴식 이벤트 흐름(먼저 로딩된 것이 먼저 표시된다라는 대전제)이 필연적인 설계가 될 수 밖에 없었지요. 모던JS가 이벤트루프를 신경써야하는 수준의 애플리케이션이 되다보니 콜백 지옥이나 pipe 처리를 빌린 rxjs(이거도 진짜 오랜만이네요! 요새 주니어들은 뭔지 잘 모를겁니다.)같은 여러 대안이 있었지만, 이제 그냥 프로미스로 통일되었고요.
이제 정말 오래된 2014년 영상이지만, 이벤트루프에 대한 예시를 너무 적절하게 사용한 훌륭한 강의입니다(한글 자막이 있습니다). ... 더 보기
재야의 고수가 계셨군요...
CPU를 직접 다루는 언어가 아니라, 웹 브라우저라는 환경에서 동작하는 스크립트 언어다보니 워터폴식 이벤트 흐름(먼저 로딩된 것이 먼저 표시된다라는 대전제)이 필연적인 설계가 될 수 밖에 없었지요. 모던JS가 이벤트루프를 신경써야하는 수준의 애플리케이션이 되다보니 콜백 지옥이나 pipe 처리를 빌린 rxjs(이거도 진짜 오랜만이네요! 요새 주니어들은 뭔지 잘 모를겁니다.)같은 여러 대안이 있었지만, 이제 그냥 프로미스로 통일되었고요.
이제 정말 오래된 2014년 영상이지만, 이벤트루프에 대한 예시를 너무 적절하게 사용한 훌륭한 강의입니다(한글 자막이 있습니다). 참고가 되었길 바랍니다:
https://www.youtube.com/watch?v=8aGhZQkoFbQ
CPU를 직접 다루는 언어가 아니라, 웹 브라우저라는 환경에서 동작하는 스크립트 언어다보니 워터폴식 이벤트 흐름(먼저 로딩된 것이 먼저 표시된다라는 대전제)이 필연적인 설계가 될 수 밖에 없었지요. 모던JS가 이벤트루프를 신경써야하는 수준의 애플리케이션이 되다보니 콜백 지옥이나 pipe 처리를 빌린 rxjs(이거도 진짜 오랜만이네요! 요새 주니어들은 뭔지 잘 모를겁니다.)같은 여러 대안이 있었지만, 이제 그냥 프로미스로 통일되었고요.
이제 정말 오래된 2014년 영상이지만, 이벤트루프에 대한 예시를 너무 적절하게 사용한 훌륭한 강의입니다(한글 자막이 있습니다). 참고가 되었길 바랍니다:
https://www.youtube.com/watch?v=8aGhZQkoFbQ
What the heck is the event loop anyway? | Philip Roberts | JSConf EU
조금 묵은 글(?)이지만 뒤늦게 쭉 읽어봤습니다.
프론트엔드 개발자인 친구가 남겼던 "이 언어는 1995년에 만들어진 주제에 어떤 디자인은 995년 수준이고 추상화는 3995년 수준이다" 코멘트가 생각나네요ㅋㅋㅋ 저는 js라고는 메신저 봇 만든다고 ts+node 좀 끼적거린게 다라서 그때는 진짜 온갖 불평불만을 늘어놓으며 썼지만(그런 주제에 제 메인언어는 만만찮게 구린 python...), 이렇게 보니 진짜 의도적 설계와 의도적이지 않은 설계미스가 뒤엉킨 언어같네요ㅋㅋㅋㅋ 계속 파면 그 뒷면의 철학을 좀 더 이해할 수 있으려나요ㅎㅎㅎ
프론트엔드 개발자인 친구가 남겼던 "이 언어는 1995년에 만들어진 주제에 어떤 디자인은 995년 수준이고 추상화는 3995년 수준이다" 코멘트가 생각나네요ㅋㅋㅋ 저는 js라고는 메신저 봇 만든다고 ts+node 좀 끼적거린게 다라서 그때는 진짜 온갖 불평불만을 늘어놓으며 썼지만(그런 주제에 제 메인언어는 만만찮게 구린 python...), 이렇게 보니 진짜 의도적 설계와 의도적이지 않은 설계미스가 뒤엉킨 언어같네요ㅋㅋㅋㅋ 계속 파면 그 뒷면의 철학을 좀 더 이해할 수 있으려나요ㅎㅎㅎ
디아2하고 노느라 이제 댓글답니다! 늦어서 죄송합니다...
세상의 많은 사람들이 미래의 컴퓨팅 환경에 대안을 제시했지만, 돌고 돌아 세상은 웹으로 돌아왔습니다. 브라우저는 로드가 완료되지 않은 데이터도 자기 나름대로 표시합니다. a11y를 위한 표시도 별도로 지원합니다. 눈이 없고 귀가 없어도 쓸수있도록 웹은 세상을 표현하기 위한 기술들의 표준을 만들어갑니다.
웹의 발전이 결국 JS의 발전이기 때문에, 브라우저를 이해하면 JS를 이해하기 쉽습니다. 선생님은 기초가 탄탄하시기 때문에, 웹어셈블리라던지 Rust처럼 로우 레벨의 CP... 더 보기
세상의 많은 사람들이 미래의 컴퓨팅 환경에 대안을 제시했지만, 돌고 돌아 세상은 웹으로 돌아왔습니다. 브라우저는 로드가 완료되지 않은 데이터도 자기 나름대로 표시합니다. a11y를 위한 표시도 별도로 지원합니다. 눈이 없고 귀가 없어도 쓸수있도록 웹은 세상을 표현하기 위한 기술들의 표준을 만들어갑니다.
웹의 발전이 결국 JS의 발전이기 때문에, 브라우저를 이해하면 JS를 이해하기 쉽습니다. 선생님은 기초가 탄탄하시기 때문에, 웹어셈블리라던지 Rust처럼 로우 레벨의 CP... 더 보기
디아2하고 노느라 이제 댓글답니다! 늦어서 죄송합니다...
세상의 많은 사람들이 미래의 컴퓨팅 환경에 대안을 제시했지만, 돌고 돌아 세상은 웹으로 돌아왔습니다. 브라우저는 로드가 완료되지 않은 데이터도 자기 나름대로 표시합니다. a11y를 위한 표시도 별도로 지원합니다. 눈이 없고 귀가 없어도 쓸수있도록 웹은 세상을 표현하기 위한 기술들의 표준을 만들어갑니다.
웹의 발전이 결국 JS의 발전이기 때문에, 브라우저를 이해하면 JS를 이해하기 쉽습니다. 선생님은 기초가 탄탄하시기 때문에, 웹어셈블리라던지 Rust처럼 로우 레벨의 CPU언어들이 어떻게 브라우저에서 동작하기 위해 (멀티스레딩) 비동기를 도입했는지 이해하시면 거꾸로 JS의 실마리를 잡기 좋을 것입니다. 왜 바보같이 글로벌에 this를 강제로 연결하지? 라고 생각하지만, 세상이 절반만 로드되었을 때 브라우저가 어떻게 scope를 문맥적으로 이해하겠습니까? 저는 브라우저 위 JS가 가진 이벤트 루프와 비동기에서 양자세계가 관측을 통해 세상을 이루는 과정을 어렴풋이 봅니다. 그렇기 때문에 거지 같지만 고치지 않는, (고칠 수 없는) good part인겁니다. 995년에서 39950년까지 느낄 수 있는 언어인 셈이죠 ㅎㅎ
세상의 많은 사람들이 미래의 컴퓨팅 환경에 대안을 제시했지만, 돌고 돌아 세상은 웹으로 돌아왔습니다. 브라우저는 로드가 완료되지 않은 데이터도 자기 나름대로 표시합니다. a11y를 위한 표시도 별도로 지원합니다. 눈이 없고 귀가 없어도 쓸수있도록 웹은 세상을 표현하기 위한 기술들의 표준을 만들어갑니다.
웹의 발전이 결국 JS의 발전이기 때문에, 브라우저를 이해하면 JS를 이해하기 쉽습니다. 선생님은 기초가 탄탄하시기 때문에, 웹어셈블리라던지 Rust처럼 로우 레벨의 CPU언어들이 어떻게 브라우저에서 동작하기 위해 (멀티스레딩) 비동기를 도입했는지 이해하시면 거꾸로 JS의 실마리를 잡기 좋을 것입니다. 왜 바보같이 글로벌에 this를 강제로 연결하지? 라고 생각하지만, 세상이 절반만 로드되었을 때 브라우저가 어떻게 scope를 문맥적으로 이해하겠습니까? 저는 브라우저 위 JS가 가진 이벤트 루프와 비동기에서 양자세계가 관측을 통해 세상을 이루는 과정을 어렴풋이 봅니다. 그렇기 때문에 거지 같지만 고치지 않는, (고칠 수 없는) good part인겁니다. 995년에서 39950년까지 느낄 수 있는 언어인 셈이죠 ㅎㅎ
목록 |
|