ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 자바스크립트는 왜 그 모양일까?
    자바스크립트 끄적끄적 2023. 12. 20. 16:33
    728x90

    시작

    • 이 책은 자바스크립트를 철저하게 ‘프로그래밍 언어’ 관점에서 접근하고 있다. 엔진이나 웹의 구동 환경 등을 다루지 않고, 언어 자체에 대해 모든 개발자가 알아야 할 것들을 다룬다.
    • 오히려 배우는 사람이 그런 것을 알지 않기를 바란다. “언어의 간결하고 분명한 부분만 가까이 하라” 는 것이 필자의 주장이며, 자바스크립트는 그렇지 않은 부분이 충분히 많다. 
    • 책 전반에 걸쳐서 이야기하는 부분 중 하나가 “다음 세대의 프로그래밍 패러다임은 ‘분산 비동기 프로그래밍’ 이 될 것이다” 이다. 그리고 자바스크립트는 그 과도기적 언어로서 충실하게 역할을 수행하고 있다고 볼 수 있다.

    챕터별 메모

    너무 어려워서 개념적인 부분만 그리고 내가 납득 가능한건만 정리 했다. 납득이 가지 않거나 하는건 혹시 내가 이해를 제대로 못함에 비롯될 수 있기에... 납득한건만 정리하고자 한다.

    이름

    • “개발자들은 코드를 읽기만 해도 프로그램을 설명할 수 있어야 한다.” => 주석은 필요 없다??!?! 변수명만 가지고 무슨 코드인지 알아야 한다!

    Boolean 타입

    • “Truthy, Falsy 값 때문에 불명확한 부분이 있으니, 조건문에는 꼭 불린 값을 사용하라. 

    객체

               =====>

      • 문장에서 언급된 컴파일러 옵션은 "strictPropertyInitialization"이라는 TypeScript의 옵션입니다. 이 옵션은 객체의 속성 초기화를 강제하며, 속성에 대한 초기화가 되지 않은 경우 컴파일러가 경고를 발생시킵니다.
      • strictPropertyInitialization 옵션을 활성화하면, TypeScript 컴파일러가 다음과 같은 상황에서 경고를 발생시킬 수 있습니다.
    • ”Object.create(null) 을 사용하여 객체가 아무것도 상속받지 않게 만들 수 있다. 객체에 명시적으로 집어넣는 속성 외에는 아무것도 없다.”
      • 객체 리터럴 선언 ({}) 은 Object 를 프로토타입으로 가지기 때문에 위의 객체 생성과 엄연히 다르다.
    • “언젠가 불변 객체는 아주 귀중한 성능상의 특징을 가지게 될지도 모른다. 객체가 절대 변하지 않는다는 사실을 알면 언어에 아주 강력한 최적화가 구현될 수도 있다.”
      • “불변성은 여러분이 안전한 프로그램을 만드는데 언젠가 도움이 될 것이며, 불변성을 통해 스스로를 지킬 수 있는 인터페이스를 갖춘 좋은 객체를 만들 수 있다.”
      • 대신 객체가 Object.freeze 로 동결되어있을 경우 프로토타입과 함께 쓰는 것은 어렵다.

    빈 값

    • ”undefined 는 엄격히 말하면 null 을 쓰는 것 보단 낫지만 위에서 언급했던 객체의 속성 접근 시 발생할 수 있는 문제 등의 골칫거리가 있다.”
    • 하지만 가능하면 일관되게 둘 중 하나를 사용하는 것이 좋을 것이다.
     

    Intent to stop using `null` in my JS code · sindresorhus/meta · Discussion #7

    I intend to stop using null in my code, in favor of undefined. There are many reasons for doing so. Feedback wanted Tweet: https://twitter.com/sindresorhus/status/1114894287387258886 Background htt...

    github.com

    문장

    • “자바스크립트는 조건문이 ‘Bool인 척하는 값’ 이길 기대한다. 이는 실수라고 생각한다.”
      • 불명확한 부분 때문에 예기치 못한 버그가 발생할 때도 있으니 가능하면 조건문에 명확하게 불린 값을 전달해주고 싶긴 하다.
    • “블록을 생략할 수도 있지만, 한 문장짜리라도 블록을 쓰는 것을 권장한다. 코드를 더 탄력적으로 만들 수 있고, 애러 없이 코드를 더 쉽게 개선시킬 수 있기 때문이다.”

    함수

    • “함수가 호출되면 활성 객체(activation object)가 만들어진다. 숨겨진 데이터 구조로서 호출된 함수의 반환 주소와 실행에 필요한 정보를 저장하고, 이를 호출된 함수에 바인딩해준다.”
      • “함수는 중첩될 수 있으며, 내부 함수 객체가 생성되면 이 객체는 자신을 생성한 외부 함수에 대한 활성 객체의 참조를 가지게 된다. 이를 클로저(closure)라 한다. 클로저 덕분에 자바스크립트는 더 흥미로운 언어가 되었다.

    제네레이터

    • “실행 흐름이란 쉽고 예측이 가능해야 한다고 배웠지만, 제네레이터는 실행 흐름을 멈추거나 재개할 수 있어 복잡해진다. 그리고 제네레이터는 반복문 사용을 조장한다.”
    • “제네레이터 자체는 좋은 기능이다. ES6의 제네레이터를 사용하는 대신 클로저를 이용하여 제네레이터를 만들어내는 팩토리를 구성하는 것을 더욱 권장한다.”
      • 예제 코드를 찬찬히 읽어보면서 클로저의 활용을 재미있게 배울 수 있다.
    function generatorFactory() {
      let count = 0;
    
      return function* generate() {
        while (true) {
          yield count++;
        }
      };
    }
    
    // 제네레이터 생성
    const myGenerator = generatorFactory()();
    
    // 5번 호출할 때까지 값을 출력
    for (let i = 0; i < 5; i++) {
      console.log(myGenerator.next().value);
    }
    • “제네레이터는 순수 함수와 비순수 함수의 경계에 있다. 상태를 가지고 있을 수 있지만, 그 상태는 팩토리 클로저에 숨겨져 있다. 상태는 제네레이터를 호출하는 경우에만 변경된다.”

    ** 예시 코드: 제네리이터 **

    function* simpleGenerator() {
      console.log('Start'); // 첫 호출에서 출력
    
      yield 1; // 첫 번째 yield: 1을 반환하고 일시 중단
      console.log('After first yield'); // 두 번째 호출에서 출력
    
      yield 2; // 두 번째 yield: 2를 반환하고 일시 중단
      console.log('After second yield'); // 세 번째 호출에서 출력
    
      return 3; // 함수 종료: 3을 반환하면서 종료
    }
    
    // 제네레이터 객체 생성
    const generator = simpleGenerator();
    
    // 첫 번째 호출: 제네레이터 함수 실행 시작
    console.log(generator.next()); // { value: 1, done: false }
    
    // 두 번째 호출: 일시 중단된 부분부터 다시 실행
    console.log(generator.next()); // { value: 2, done: false }
    
    // 세 번째 호출: 함수 종료
    console.log(generator.next()); // { value: 3, done: true }
    
    // 네 번째 호출: 함수가 이미 종료되었으므로 { value: undefined, done: true } 반환
    console.log(generator.next());

    예외

    • “예외 처리를 잘못 쓰는 가장 흔한 경우는 정상적인 결과를 처리할 때도 쓰는 것이다. 예외 처리는 예상하지 못한 문제를 처리할 때만 사용해야 한다.”
    • “실행 흐름은 예외 객체를 만들어 낸 메서드에 의해 결정되는데, 이는 깔끔한 모듈화 설계방식에 반대된다. 예외 생성 주체와 예외 처리 주체간의 단단한 결합을 만들기 때문이다.”
    • “비동기 프로그래밍에서 스택은 매번 비워지기 때문에 존재하지 않는 스택을 거슬러가서 예외 값을 전달할 수 없다. 이런 상황에서 예외는 아주 제한된 쓰임새만 가지게 된다.”
    • “호출한 함수가 제대로 동작하지 않는다면? 예외 객체를 통해 함수에게 알려줄 수 있지만, 현재의 기술 수준에서 함수들이 알아서 이런 실수를 고칠 수 있으리라 기대하긴 어렵다.”
      • “예외 객체의 세부 사항은 아주 중요하며 프로그래머가 알아야 할 유용한 정보이다. 하지만 이 객체를 제대로 활용할 수 없는 함수로 전달이 되어 정보의 흐름이 오염된다. 이런 형태의 정보는 로그 등의 형태로 프로그래머에게 전달되어야 할 것이지만, 콜스택으로 전달되어 잘못 이해되기 일쑤이다. 예외 처리 방법 자체를 신뢰할 수 없게 만드는 것이다.”

    프로그램

    • “거시적 수준에서 좋은 프로그래밍은 좋은 모듈 설계에 달렸다.”
      • 좋은 모듈은 응집도가 높다. 모듈의 모든 요소가 어우러져 하나의 목적을 이루기 위해 함께 동작한다. 모듈에서 자바스크립트 함수를 강력하게 만들려면, 모듈에서 신경 쓰면 안되는 세부 사항들을 모듈의 함수로 전달하고 처리할 수 있도록 만들어야 한다.
      • 좋은 모듈은 약하게 결합되어 있다. 모듈의 인터페이스를 제대로 사용하기 위해 모듈에서 꼭 필요한 내용만 알면 되고, 어떻게 구현되었는지 알 필요는 없다.”
      • “모듈 인터페이스를 간단 명료하게 만들고, 의존성을 최소화해야 한다. 프로그램이 혼란에 빠지지 않고도 충분히 커지기 위해서 좋은 구조가 꼭 필요하다.”

    this

    • ”this 를 완전히 사용하지 않을 것을 권장한다. this 없이 프로그래밍하는 법을 배우면 더 나은, 더 행복한 프로그래머가 될 수 있다.” => ㄹㅇㅋㅋ

    클래스 없는 자바스크립트

    • 클로저를 활용하여 생성자를 구축하는 방법을 소개한다.
      • 오래 전에는 클로저를 남용하는 것이 메모리 누수를 일으킬 수 있다고 이야기가 나오는 편이었지만 과연 요즘은 그런 걱정을 할 필요가 있을까?
    • “객체의 인터페이스는 오직 그 자신의 메서드여야만 하고, 아주 단단하게 보호되어야 한다. 데이터에 직접 접근할 방법이 없어야 하고, 이것이 바로 좋은 모듈화 설계이다.”
    • “메서드는 트랜젝션으로 구성되어야 하며 각각의 속성을 변경하는 여러 함수가 있는 것보다 객체 리터럴을 받아 private 속성을 한꺼번에 바꾸는 하나의 메서드를 구현하는 것을 권장한다.”
      • 같은 권장사항은 생성자의 매개변수에도 해당된다. 매개변수로 하나의 객체 리터럴 혹은 JSON 페이로드를 전달받는 것이다. 코드 자체를 문서화해줄 수 있고, 인자의 전달 순서도 상관없다. 또한 새로운 인자를 추가하거나 삭제하는 것도 용이하다.
    • “자바스크립트의 장점 중 하나가 객체 리터럴이다. 정보를 묶고 쓰기 쉽고 읽기 쉽게 만들기도 좋다.”
    • 함수 합성을 통한 믹스인 예시도 보여주었다.
    • “이 챕터에서 설명한 대로 객체를 생성하고 사용한다면 프로토타입을 쓰는 방법보다 메모리를 더 많이 사용할 것이다. 프로토타입 객체가 메서드를 가진 프로토타입에 대한 참조만 가지고 있는 반면, 여기서 설명한 방법은 개별 객체가 각자의 메서드를 다 가지고 있기 때문이다.”
      • “하지만 최근의 메모리 용량의 증가를 보았을 때 메모리 사용량의 차이는 티끌에 불과하다. 또한 메모리 사용량이 증가할지라도 모듈화에서 더 큰 이득을 얻을 수 있다. 속성 값 처리를 트랜젝션으로 만들어 메서드의 수는 줄이고 응집도를 더 높일 수 있다.”

    순수함

    • 순수함, 순수 함수라는 것으로 얻을 수 있는 이득에 대해서는 이미 이 책 외에도 다양한 자료들이 다루고 있다. 간단히 요약하면 다음과 같은 이득을 얻을 수 있다.
      • 각 함수의 응집도는 높으나 결합도는 낮다.
      • 테스트하기 쉽다.
      • 사이드 이펙트가 없고 외부 의존성이 낮기 때문에 순수 함수들을 모아서 더 크고 복잡한 함수들을 만들 수 있다.
      • thread safe하며 효과적이기 때문에 코어가 많을 수록 성능 향상의 이득을 볼 수도 있다.
    • “순수함은 추가할 수 있는 기능 따위가 아니라 신뢰성, 보안성과 비슷하다. 따라서 순수하지 않은 것들을 제거해야 한다.”
      • “ECMA는 자바스크립트의 안 좋은 부분을 제거할 힘이 없어서, 계속 좋지 않은 부분만 커지고 있는 상태다. 우리가 나쁜 것을 쓰지 않으면 된다. 개발 과정에서 많은 연습을 통해 언어에 순수함을 부여할 수 있따. 순수하지 않은 기능을 쓰지 않기로 한다면, 그 기능들은 더 이상 함수 몸체를 약화시키거나 순수하지 않게 만들 수 없다.”
    • “세상은 순수하지 않다. 이 세상은 비동기적이고, 완전히 분산되어있으며, 고도로 병렬화되어 있다. 하지만 순수함으로 얻을 수 있는 이득은 확실하므로 가능하면 프로그램을 최대한 순수하게 만들어야 한다. 객체는 변경이 가능한 상태 값을 가지고 있음에도 불구하고 여전히 가치가 있다. 객체를 제대로 설계해서 상태 값을 엄격하고 단단하게 관리해야 한다.”
      • “함수 몸체는 순수해 보이지 않지만, 그 영혼은 순수하다. 블랙박스처럼 그 속을 들여다보지 않으면 순수하다.”
      • “즉, 순수함에는 연속성이 있다. 수학적인 함수가 최상위이고, 병렬 애플리케이션에도 쓸 수 있을 만큼 순수한 함수, 순수한 고차 함수, 상태를 가지는 고차 함수, 등등… 제일 아래에 전역 변수를 사용하는 모든 것이다.”

    비동기 프로그래밍

    • “일반적으로 순차적 언어는 입출력을 블록(block) 방식으로 처리한다. 프로그램이 파일을 읽거나 네트워크에서 데이터를 가져오려고 하면, 데이터를 다 가져올 때까지 프로그램은 실행을 멈춘다. 오늘날 대부분의 프로그래밍 언어 역시 이런 I/O 모델을 구현하여 사용하고 있지만, 자바스크립트는 그렇지 않다.”
      • “자바스크립트는 탄생 목적 자체가 사용자와의 상호작용이었기 때문에 다른 언어보다 순차적 모델에 영향을 덜 받는다.”
    • “스레드로 인해 발생하는 버그는 버그들 중에서도 그 대가가 비싸다. 타입 검사로 찾을 수도 없고, 테스트로 발견하기도 힘들다. 에러는 아주 드물게 발생할 수 있으므로 디버깅하기 극도로 어렵고, 수정한다고 해도 문제가 해결된다는 확신을 가지기도 어렵다.”
    • “운영체제에서의 스레드는 필요악이지만, 애플리케이션에서의 스레드는 그냥 악이다. 다행히도 자바스크립트는 이런 식으로 스레드를 쓰지 않고 동시성을 더 나은 방법으로 구현할 수 있다.”
    • “비동기 방식은 애플리케이션에서 스레드를 쓰지 않고도 많은 일을 처리할 수 있게 해 준다.”
      • “비동기 프로그래밍은 두 가지 아이디어에 뿌리를 두고 있다. 바로 콜백 함수와 프로세싱 루프이다.”
      • (콜백 함수에 대해서는 생략하고) “프로세싱 루프(이벤트 루프)는 큐에서 가장 높은 우선순위를 가지는 이벤트 혹은 메세지를 가져와서 해당 이벤트나 메세지를 처리하도록 등록된 콜백 함수를 호출한다. 그리고 콜백 함수가 작업을 완료하면 반환한다. 그래서 콜백 함수는 메모리 잠금이나 상호 배제가 필요 없다. 콜백 함수가 끝나면 프로세싱 루프는 큐에서 그 다음 이벤트나 메세지를 꺼내와서 등록된 콜백 함수를 호출하고 이 과정을 계속 반복한다.”
      • “자바스크립트 프로그램과 메인 스레드간에 통신하는 방법은 큐가 되며, 이로 인해 상호 배제가 하나의 접점에만 사용되므로 비동기 시스템을 더 효율적이고 신뢰할 수 있게 만든다.”
    • “비동기 모델에도 규칙이 있다. 바로 턴의 법칙이다. - 기다리지 말라. 블록하지 말라. 빨리 끝내라.
      • “함수는 절대로 어떤 일이 일어나길 마냥 기다려서는 안된다.”
      • “함수는 절대 메인 스래드를 블록해서는 안된다.”
      • “작업을 끝내는 데 오랜 시간이 걸리는 함수를 호출해서도 안된다.”
      • “턴의 법칙을 위반하면 높은 성능을 자랑하는 비동기 시스템이 아주 낮은 성능을 보이게 될 것이다. 현재 콜백을 지연할 뿐 아니라 큐에 있는 모든 것을 지연시키기 때문이다.”
      • “따라서 턴의 법칙을 위반하는 함수는 수정되거나 별도의 프로세스로 격리되어야 한다. 자바스크립트는 프로세스에 대해서 알지 못한다. 프로세스는 자바스크립트가 동작하는 시스템에 의해서 제공되는 서비스이다. 프로세스는 비동기 프로그래밍 모델에서 아주 중요한 부분을 차지하고 있으므로, 다음 세대 언어에서 가장 중요한 기능 중 하나가 될 가능성이 크다.”
    • “서버 사이드의 경우 그 흐름이 훨씬 복잡하기 떄문에 자바스크립트가 이벤트 루프는 아주 잘 처리하지만 메세지 루프에서 허우적대고 있다.”
      • “Callback Hell, Promise, Async/Await 모두 각자의 문제를 갖고 있다. 특히 Async/Await은 옛날 패러다임과 동일한 형태로 코드를 작성해도 비동기 프로그래밍을 구현할 수 있다는 장점을 가지고 있다지만, 이게 제일 큰 문제이다.” => 동의 못함
      • 새로운 패러다임은 아주 중요하다. 새로운 패러다임이 무엇인지 이해하는 것은 어려울 수 있지만, 그렇다고 피하기만 한다면 더 이상 발전할 수 없다. 반면 Async/Await은 패러다임에 이해와 발전 없이도 생산적인 코드를 만들 수 있게 해 준다.” => 동의 못함222

    Date

    • “자바스크립트는 자바의 Date 객체를 베끼는 실수를 저지르지 말았어야 했다.”
    • 현재 JS의 Date 객체를 그대로 쓸 때의 어려운 점과, 그 대안으로 사용할만한 도구들은 이미 웹에 충분히 널리고 널렸으니 생략.

    테스팅

    • “완성된 프로그램은 어느 관점에서 봐도 완벽해야 한다. 우리가 컴퓨터와 맺은 계약은 우리가 컴퓨터에게 완벽하지 않은 프로그램을 제공할 경우, 컴퓨터는 가능한 최악의 시간 동안 가능한 최악의 일을 권리가 있다는 점이다. 이는 컴퓨터의 잘못이 아닌 우리의 잘못이다.”
    • “프로그램에서 혼란이 발생할만한 부분은 최대한 없애야 한다. 혼란을 줄이기 위해서는 프로그램을 최대한 단순명료하게 만들어야 한다. 버그는 혼란의 또 다른 말이다. 혼란을 없애는 것이 테스트하는 것보다 훨씬 더 생산적이다.”
    • “비대해지지 않게 만들려면, 처음부터 비대하지 않게 만들어야 한다. 설계와 개발에서 소프트웨어의 단순함을 최우선으로 삼아야한다.”
      • “소프트웨어를 비대하게 만들 수 있는 툴이나 패키지는 지양하고, 클래스도 지양하고, 더 적지만 훨씬 뛰어난 개발자들로 팀을 꾸리고, 코드를 제거하는 연습을 적극적으로 하라. 개발 사이클의 일부를 불필요한 코드 제거와 문제 있는 패키지 제거에 할당하라. 프로젝트의 코드 수가 줄어들면 축하하라. 최소한의 크기라는 철칙을 지켜라.”
    • “테스트에만 의존해 모든 버그를 찾을 수는 없다. 그보다 버그를 피하는 것에 더 주력해야한다. 좋은 코딩 습관은 프로그램의 질을 향상시킬 수 있는 아주 쉬운 방법이다.”

    최적화

    • “단순 반복문 실행 결과 따위의 성능 측정의 결과는 의미 없을 수도 있다. 그 대신 더 읽기 쉽고 더 유지보수하기 쉬운 기능을 골라야 한다.”
    • “최적화를 시도한다면 최적화하기 전헤 최적화하고 싶은 코드의 성능을 측정해야 한다. 기준을 정하고, 최적화하고 싶은 코드가 전체 프로그램을 느리게 한다는 사실을 알기 위해 측정한다. 측정한 코드가 생각보다 느리지 않다면 다른 곳을 찾아서 최적화하고 다시 측정한다. 새로운 코드가 눈에 띌 정도의 향상을 보이지 않는다면 변경된 코드를 되돌린다. 실패한 코드(원하는 만큼의 향상을 보이지 않은 코드)를 반영해서는 안 된다.”
      • “만약 성능이 눈에 띌 정도로 향상되지 않았다면 코드 변경은 버그라도 불러도 된다. 이득이 아주 미미하거나 거의 없는데 코드 품질은 떨어지기 때문이다. 깔끔한 코드는 추론하기도 쉽고 유지하기도 좋다. 깔끔한 코드를 절대 포기해서는 안 된다.
    • “속도 저하의 근본 원인으로 꼽히는 대표적인 경우는 다음과 같다.”
      • 병렬화 실패
      • 턴의 법칙 위반 - 프로세싱 루프가 중간에 멈추는 경우 그 뒤로 처리되어야 하는 큐의 모든 작업이 지연된다.
      • 낮은 응집도
      • 강한 결합도
      • 잘못된 알고리즘
      • 스래싱(Thrashing)
      • 비대한 소프트웨어
      • 다른 사람이 만든 코드 - 다른 패키지, 라이브러리, 플랫폼 등에 의존할 수 있는데 자신의 코드를 아무리 만지작거린다고 외부 의존성 코드가 빨리 실행되진 않는다.
    • “언어의 엔진 수준의 최적화가 이루어진다면 모두가 이득을 볼 수 있을 것이다. 하지만 자바스크립트라는 언어가 복잡해지면서 최적화도 점점 힘들어지고 있다. 단순하고, 깔끔하고, 균형 잡힌 언어가 최적화하기 더 쉬울 것이다.”

    트랜스파일링

    • ‘다음 세대 프로그래밍 언어는 어떤 형태여야 하는가?’ 라는 생각으로 만들어진 Neo라는 실험적 언어를 소개하며 저자는 ‘다음 세대의 언어에게 바라는 것’ 을 나열했다.
      • 내장 JSON 인코딩/디코딩
      • 덜 난해한 방식의 문맥 자유 언어 지원
      • 더 나은 유니코드 지원
      • 내부 문자 표현 방식으로 UTF-32 사용
      • Blob(Binary Large Object)의 직접 지원
      • 더 나은 비동기 프로그래밍 지원
      • 더 안전한 네트워킹 지원
      • 시작, 통신, 파괴와 같은 프로세스 관리 지원
      • 순수 함수의 병렬 처리 지원
    728x90
Designed by Tistory.