[TIL] 할당문, CSS Preprocessor, Event loop

devAnderson·2022년 5월 9일
0

TIL

목록 보기
94/103

0. 아무 일에든지 다툼이나 허영으로 하지 말고, 오직 겸손함으로

항상 나는 복습을 하는 것을 좋아한다. 이 블로그를 쓰는 이유도 내 스스로 복습하고 정리하기 위한 목적이 아주 크다.
누군가는 왜 아는것을 또 보고 있냐고 말할 수 있고, 나 역시도 이제는 너무 많이 봐서 당연스럽게 안다고 생각했던 내용들인데
다시금 복습하면 새롭게 깨닫는 것들과, 심지어 기존에 내 생각이 틀렸다는 것을 알게 되는 순간이 너무 많아서 기쁘고 감사하다.

오늘 내용은 내가 복습을 하면서 잘 정립하지 못했던 것과, 착각했던 것들을 재정리하려고 한다

1. 🚍 할당문

할당이라는 것을 말하기 전에 우선 정리해야 할 것은 "변수 선언, 초기화, 할당" 의 개념을 정립하는 것일 것이다.

  • 변수 선언 : 식별자를 생성하고 이것을 심볼 테이블에 연결하는 작업을 뜻한다. 심볼 테이블에는 해당 식별자가 미래에 할당받게 될 데이터 구조인 "데이터 타입" 과 "선두 메모리 주소" 를 할당할 영열을 가지고 있다. 이 부분은 할당이 이루어지는 순간 채워진다.
  • 초기화 : 메모리 공간을 확보한 후, 평가된 "값" 의 2진 데이터를 저장하는 작업을 뜻한다. var과 같은 변수는 첫 컴파일 시점에서 undefined를 초기화한 후 할당하게 된다.
  • 할당 : 타겟인 식별자의 심볼 테이블 영역 내용을 피연산자의 값 정보와 바인딩하는 과정을 의미한다.

위와 같은 심볼 테이블이 존재해야 하는 이유는, 자바스크립트 엔진이 식별자를 통해 메모리 공간에 접근을 할 때 얼마만큼의 메모리 공간을 읽어들여야하는지에 대한 힌트가 필요하기 때문이다.

식별자의 심볼테이블에 바인딩되는 값의 "타입" 에 따라 자바스크립트 엔진은 읽어들여야할 메모리의 공간을 "선두 메모리 주소"로부터 시작하여 "~까지" 읽어들여 불러오게 된다. 불러들여온 이진수 데이터는 타입에 의해 해석되어 변환된다.

이 할당 연산자의 특이한 점은, 할당문 자체가 표현식이라는 점이다.

문과 표현식에 대한 정의를 하자면

문 : 토큰의 집합이 모여 프로그래밍적으로 의미있는 실행이 가능해지는 실행단위를 뜻한다. 예를 들어, "let name" 이라는 문자열의 조합은 파싱이 되면서 해석이 될 때, let이라는 토큰과 name이라는 토큰의 조합으로 인식하게 된다. 이 두개의 토큰이 모여서 컴퓨터의 입장에서는 "식별자 name이라는 명칭을 가진 변수를 위한 심볼테이블을 생성해라" 라는 명령어로 인식되게 된다.

표현식 : 문이 자바스크립트 엔진에 의해 평가가 될 때 "값" 으로 평가가 되는 대상을 표현식이라고 한다. 자바스크립트에서의 "값" 이란, 데이터타입으로 평가가 될 수 있는 대상을 뜻한다. 대표적으로 String, Number, Boolean, Undefined, Null, Symbol과 같은 원시타입과 Object, Array, Function과 같은 객체 타입을 뜻한다.

할당문은 그 자체가 값으로 평가되는 표현식이기 때문에, 자바스크립트 엔진에 의하여 평가가 될 때 일정한 타입의 "값" 이 암묵적으로 리턴된다는 의미와 같아진다.

그래서 실제로 이런 것이 가능하다

let value1 = 1;
let value2 = value1 = 2; // "value1 = 2" 자체가 평가가 되어 어떠한 값으로 인식된다.

그렇다면 할당문에서 암묵적으로 리턴되는 값은 무엇일까? 그것은 바로 우측에 있는 피연산자가 평가가 된 후에 만들어지는 그 값 자체가 불변하게 복사되어 리턴된다는 점이다.

따라서 위와 같은 내용은 해석하자면 아래와 같다
1. value1 식별자에 대해서 1이 평가가 된 이후 메모리 주소와 바인딩되는 작업이 이루어진다.
2. value2 식별자에 대해서 무언가 메모리 주소의 바인딩을 하려고 한다
3. 그런데 그 대상이 표현식이다. 따라서 해당 표현식을 평가한다. 표현식에서는 2를 위한 새로운 메모리 주소를 확보하여 이진데이터를 삽입한 후, value1의 심볼테이블과 재연결하는 작업을 가진다. 그 후, 암묵적으로 원시값 2가 불변하게 복사되어 리턴된다.
4. value2의 심볼테이블에는 원시값 2의 메모리 주소가 바인딩된다.

하지만, 객체는 레퍼런스 값을 따르기 때문에 표현식이 평가가 되면 불변하게 복사된 객체가 리턴되는 것이 아닌, 레퍼런스 주소 그 자체가 리턴된다.
따라서

let value1 = { name : "anderson" };
let value2 = value1 = { name : "jully" }
value2.name = "changed"

console.log(value1.name); // changed

즉, value2 에 우측에 있는 표현식 "value1 = { name : "jully" }" 은 평가가 완료되면 { name : "jully" } 에 대한 레퍼런스 주소가 리턴된다.

따라서 value2 역시 할당되는 대상은 해당 레퍼런스 주소를 보게 되므로 고치게 되면 서로가 바뀌는 것이다.

사실, 저렇게 할당을 하는 사람은 없기 떄문에 이렇게 알 필요는 없지만, 표현식과 문 그리고 할당문에 대해서 정리할 수 있는 좋은 기회가 되었다.

2. 🚍 CSS Preprocessor

이른바 css 전처리기라고 불리우는 해당 내용은 CSS의 불편함을 해결하기 위해 탄생한 기술이다.

pure CSS로 개발을 하다 보면 여러모로 어려움을 겪게 되는 경우가 많은데 그 대표적인 이유는 아래와 같다.

1. 한 파일에 class 에 대한 모든 스타일링을 설정하기 때문에, 클래스의 중복으로 인한 css overlap이 일어날 상황이 많아진다.

2. 이로 인해서 css 파일을 서로 분리하여 모듈화해서 작업하기가 어렵고 위험하므로 협업 및 유지보수에 치명적이다.

그래서 이를 해결하기 위해 SCSS와 같은 CSS의 전처리기가 생겨나게 되었다.

CSS 전처리기는 pure CSS에서 문제가 되었던 클래스 중복으로 인한 디자인이 덮어씌워지는 행위를 방지하기 위한 nesting 기능과,
불필요한 스타일링 코드들의 반복선언을 줄이기 위한 Mixin과 같은 재활용성을 도입했다는 점에 큰 의의가 있다.

하지만 여기서 재미난 점은 CSS 전처리기의 문법은 사실상 있는 그대로 브라우저에서 처리할 수 없다는 점이다.
해당 내용을 브라우저가 인식하게 만들기 위해서는, 브라우저가 인식할 수 있는 언어인 CSS로 컴파일한 후 실행시키는 방법이 있다.

이와 같은 행위는 Babel의 트랜스파일과 매우 유사한 내용이라고 인식되는데, 말 그대로 비슷하다.

그리고 더욱 알아야 할 점은, 위와 같은 전처리기의 컴파일을 webpack의 loader에서 해결하고 있었다는 점이다.

왜 CRA으로 만든 프로젝트에서 손쉽게 SCSS를 사용할 수 있었는지에 대한 물음에 해결이 되었다.

loader란, 자바스크립트만 인식이 가능한 웹팩에게 이미지, css, 비디오 등 다양한 타입의 파일들을 처리하여 번들링할 수 있도록 변환시켜주는 코드들의 집합을 의미한다.

로더의 형태는 아래와 같다.

// webpack.config.js
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/app.js',
  output: {
    path: __dirname + '/dist',
    filename: 'app.bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.scss$/, // 만약 scss 확장자를 가진 파일을 만나게 된다면
        use: [
          'sass-loader' // 해당 로더를 이용해서 css로 변환해서 적용해주세요
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Hello Webpack Project!',
      template: './src/index.ejs'
    })
  ]
};

즉, SCSS가 pure CSS에 비해서 가지는 단점이라고 한다면 이처럼 css로 처리되기 위한 전처리기를 미리 다운받은 후 변환시키는 과정이 추가되기 때문에 상대적으로 느리다는 drawback을 가지게 된다. (물론, 그 단점을 훨씬 넘어서는 장점들이 많아서 사용하게 되지만)

3. 🚍 Event loop

이벤트 루프는 자바스크립트 공부를 하면서 수도없이 봤다고 생각하였던 내용인데, 여기서 착각을 많이 하고 있는 부분이 있어서 정정한다.

이벤트 루프는 자바스크립트에서 싱글 스레드에 의해 하나의 실행 컨텍스트 스텍프레임만을 처리할 수 있다는 단점을 보완하기 위한 기능이다.

여기서 내가 했던 착각은, 실행 컨텍스트의 스텍프레임에 존재하는 "Environment Record" 즉, 함수가 실행되면서 만들어지는 컨텍스트의 환경 변수들에 선언되는 값들이 런타임 시점에서 할당을 진행할 때, 객체 값도 역시 실행 컨텍스트의 프레임 안의 환경변수 안에 등록되는 것이라고 생각했으나,

위의 이미지에 따르면 객체의 내용물 값은 변동될 수 있기 때문에 heap에 저장되고 레퍼런스 주소만 환경 레코드에 등록된다는 점이었다.

두번째 착각은 "setTimeout" 의 실행 메커니즘이었는데

function foo() {
    console.log('foo');
}

function bar() {
    console.log('bar');
}

setTimeout(foo, 0);

bar();

실행 컨텍스트의 환경 레코드에는 foo식별자, bar 식별자이다.

그리고 나서 런타임에 setTimeout과 bar이 실행되는데, 여기서 조금 착각하고 있던 내용을 수정하고 싶었다.

setTimeout이 호출이 되는 순간, 자바스크립트 엔진은 해당 실행 컨텍스트의 스코프 체인을 따라 이 "setTimeout"이라는 식별자를 찾아나선다. 해당 내용은 브라우저의 web API가 제공하는 메서드로, 윈도우 객체에 존재한다.

해당 식별자를 확인했으므로 이것과 바인딩된 메모리 주소를 찾아서 함수를 호출시킨다.

그러면 setTimeout의 실행 컨텍스트가 형성되어 콜스텍에 들어가게 되는데, 여기서 해당 함수가 호출되면 하는 행위는

브라우저의 멀티 쓰레드 공간에 해당 첫번째 인자의 콜백함수를 등록하고 타이머 ID를 리턴한 뒤에 호출이 종료되어 콜스텍에서 pop 된다는 것이다.
해당 작업이 브라우저의 멀티쓰레드에 전달되어 작업되는 이유는, 만약 여러개의 setTimeout이 존재할 경우 이것이 싱글쓰레드로 타이머 ID가 부여되는 것이 처리된다면 그 처리과정이 다 될때까지 실행이 지연되는 문제가 발생하므로 멀티쓰레드로 처리되는 것이라고 한다.

그리고 나서 "bar()" 이 호출이 되면, 해당 실행 컨텍스트가 스텍에 등록된 후, 콘솔에 bar을 찍고 pop 이 된다.

하지만 그 뒤편에서 브라우저는, 멀티 쓰레드로 처리하고 있는 타이머 ID 부여를 마친 후, 두번째 인자에 설정되어 있는 타이머의 ms가 지날 때까지 기다린다.

여기서 내가 착각했던 부분은, 0이라고 되어 있으면 0ms 이후 바로 테스크 큐에 전달되어 대기했다가 콜스텍이 비워지면 해당 콜백함수의 실행 컨텍스트가 들어가서 실행되는것이라고 생각했는데, 자료에 따르면 4ms 이하인 경우라면 디폴트 값이 4ms이기때문에 이 타이머 시간이 지난 후 테스크 큐에 등록된다고 한다.

결론

오늘 TIL을 통해 여러가지 오해를 바로잡았으며, 불확실했던 개념들을 다시금 정리하고 재정립할 수 있었던 소중한 기회가 되었기에 너무나 감사하다.

profile
자라나라 프론트엔드 개발새싹!

0개의 댓글