프론트엔드 개발자 이직하기

Jacob You·2021년 7월 6일
2

퇴사

태생적으로 작은 회사는 항상 경영문제에 시달리게 되어 있다. 다니던 회사를 2년 5개월 만에 나와야 했다.

Caution

철저히 저 개인이 이해한 방식을 정리하는 곳입니다. 100% 맞다고 장담할 수 없는 부분도 있으니 유의하시기 바랍니다.
그리고 계속 업데이트 되고 있습니다.

받았던 질문들

클로저

const x = 1;
function foo() {
  const x = 10;
  const inner = function() {
  	console.log(x);
  }
  return inner
}

const b = foo();
b();

이렇게 생겼다. return 을 만나는 순간 foo()는 라이프사이클이 끝난다. 그러면서 데리고 있던 const x = 10도 사라진다.(실행 컨텍스트 스택에서 제거된다.) 그러면 그 밑에 리턴되는 inner 의 경우는 x 를 찾고 있는데 당연히 저 위에 있는 const x = 1 을 찾을 것이고 그렇게 알고 있다. 하지만 코드를 돌려보면 결과는 10 이 찍힌다.

클로저의 정의를 MDN에서는

클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.

라고 되어 있다. (이게 뭔 개소리야 짤 넣고 싶다.) 진짜 이게 뭔소리인가 하면 함수는 호출되는 곳이 중요한 것이 아니고 선언된 곳. 즉, 코드에 쓰여있는 곳이 중요하고 그 위치에 따라서 렉시컬 환경을 기억하게 되는데 위 예제 코드로 볼 때, 리턴되는 inner는 자신이 쓰여있는 foo 내부를 렉시컬 환경으로 기억(참조)하고 있다. 그래서 foo가 작동하고 리턴을 만나며 inner를 반납(리턴)할 때, foo는 종료된다.(실행 컨텍스트 스택에서 제거) 하지만 inner 함수에서 foo의 렉시컬 환경을 저장하고 있고(정확히는 [[Enviornment]]라는 것에 렉시컬 환경을 저장한다.) b변수에 의해 참조되고 있기 때문에 가비지 콜렉터 대상에서 빠지게 된다. 그러니까 메모리에서 해제되지 않게 되고 사용하게 되며 계속적으로 상위 스코프의 식별자에 접근할 수 있게 되는 것이다.

오해

예전에 클로저는 기능은 좋은데 위험한 것이라고 했었다. 위에서 언급한대로 외부 함수의 생명주기는 끝났는데 참조하고 있는 식별자의 내용들이 메모리에서 해제되지 않기 때문이다. 그래서 잘못 사용할 경우 메모리 누수가 생긴다고 많이 배웠었다. 하지만 요즘 모던 자바스크립트 엔진은 그때(그때는 IE가 한참 득세하던 그런 시절이다.)랑 달라서 상위 스코프의 모든 식별자를 기억하는 것이 아니고 참조된 식별자들만 기억하고 있어서 예전과 같은 성능문제에 대해서 말하는 것은 나는 아재다 라고 선언하는 것과 다름없는 것이다. 근데 난 그렇게 얘기하고 다녔다.(미친..)

어디에 쓰이는가?

내가 알기론 useState hooks 의 경우도 클로저라고 알고 있다. 즉, 상태값을 들고 이리저리 굴리는데 써먹기 아주 좋은 그런 녀석이다. 그리고 다들 얘기하는 정보은닉에도 활용된다.

결론

외부함수보다 오래 살아남게 된 중첩함수의 경우, 그리고 상위 스코프의 식별자를 참조하고 있는 경우, 자신이 정의된 상위 스코프를 [[Enviornment]] 라는 곳에 저장하고 그래서 외부함수는 리턴을 만나서 사라져도 같이 데리고 있던 상위 스코프의 식별자에 접근할 수 있는 함수를 클로저 라고 한다.

REST API

this

CORS

정의

  • 이게 뭐냐? => 요청하는 곳의 도메인과 응답을 줄 곳의 도메인이 다를 경우 흔히들 서버에서 거부 혹은 거절하는 걸로 알고 있음.
  • 정확히는 브라우저 에서 "야 임마 니 컴퓨터 위험해진다. 어디 근본도 모르는 곳에 api 를 찌르려고 하냐" 하고 막는 것이다.

푸는 법

  • 원인은 브라우저에 있다고 했는데 희안하게도 서버에서 풀어주는게 제일 간편하다. 그래서 다들 오해를 하고 있는 거 같다. 하지만 이 방법은 보안에 문제가 생길 수 있기에 잘 협의해야 한다.
  • 그 외에는 JSONP 를 사용한다던지, 미들웨어를 둔다던지 등의 방법이 있지만
    - JSONP 는 get 요청 밖에 안되고
    • 미들웨어는 솔직히 잘 모르겠다.
  • 프록시 서버를 쓰는 법이 제일 확실하지만 이 역시나 뭔가 좀 번거롭다.
  • 내가 쓰는 방법은 로컬에서 작업할 때 host 설정을 해서 api 를 주는 서버와 동일하게 맞추고 작업한다 : ) 어지간하면 거의 다 된다.

Promise

async/await

정의

  • ES7의 나름 드러나는 특징 중 하나
  • Promise의 then에 지친 자들을 위로 하는 좋은 문법이다. (물론 써보면 같이 쓰게되는 경우도 많다.)
  • 결국 다 비동기 코드를 쉽게 갖고 놀기 위해 나온 개념

제네레이터

정의

  • async/await 보다 뭔가 더 어썸한 것이 쓰고 싶다 할 때 쓰는 것(은 장난).
  • 함수가 작동 도중에 탈주하여 다른 함수와 놀다가 돌아와서 나머지 작업을 진행하다 또 나갔다 오고를 반복한다.(는 내 개념)
  • 함수 이름에 * 모양을 달아주면 그게 제네레이터 함수의 시작이다. 함수의 실행 도중에 멈췄다가 다른 함수를 돌리고 돌아와서 또 남은 작업을 돌리고 하는 능력을 갖고 있다.
  • 역시나 그래서 비동기 작업을 편하게 할 수 있게 해준다.

브라우저 작동원리

SSR / CSR

이벤트 루프

호이스팅

console.log(add); // undefined 출력 (에러가 아님)
console.log(sub); // 아마 Reference Error가 날 것임.

var add;

let sub;

위 코드를 보면 add의 경우는 잘 실행이 될 것이다(?). 그리고 sub의 경우는 잘 실행이 안되고 에러가 날 것이다. 원래대로라면 인터프리터인 자바스크립트는 순차적으로 돌아가기 때문에 함수가 등장하기 전에 console.log() 로 불러내면 당연히 에러가 날 거라고 생각한다. 하지만 자바스크립트 엔진은 소스코드를 평가하는 과정에서 이미 그들이 어디에 있는지 다 알고 있기 때문에 코드의 위치가 어디가 되던간에 다 불러들일 수 있다.

var 키워드로 선언하는 변수는 코드평가 과정에서 암묵적으로 undefined로 값이 들어가 있다. 그래서 코드 흐름상으로 볼 때 add가 저 아래 있지만 코드를 평가하는 과정에서 어디있는지 알고 있고 할당이 없으니 undefined 초기화를 해버린 것이다. var키워드를 사용하면 선언과 할당을 한번에 코드에 넣는다고 해도 무조건 undefined 한번 깔고 그리고 값을 할당한다. 그리고 실제 값의 할당은 코드평가 과정이 아닌 런타임(우리가 아는 그 인터프리터가 돌아가는 과정)에서 일어난다.

호이스팅은 변수의 선언과 생성과정, 그리고 자바스크립트 엔진의 동작원리까지 파고들어야 명확하게 이해하기 쉽다. 그렇지 않으면 그냥 단순히 저 밑에 있는 코드를 끌어올려서(hoist) 실행하는 걸로 밖에 안보이는데 이래서는 설명을 명확히 하기가 어렵다.

변수의 선언

변수는 선언 초기화 할당의 단계를 갖는다. 그래서 여기가 값을 쓰고 읽고 지지고 볶고 그렇게 사용할 수 있게 된다. 문제는 변수를 선언하는 키워드에 따라서 그 방법이 좀 다르다.

var 키워드

var 키워드는 선언과 초기화단계가 한번에 일어난다.

var a = 10;

이라고 해놔도 자바스크립트 엔진은

var a; // 이때 선언이 되고 undefined로 초기화가 되어버림
a = 10; // 우리가 모두 초기화처럼 알고 있지만 var 키워드로 변수를 만들면 무조건 undefined 로 초기화가 되기에 무조건 재할당이다.

이런 식으로 작동하게 된다. (도대체가...;;;)

이렇기에 위에서 장황하게 설명된 호이스팅 같은게 생기게 되는 것이다.

대신 let키워드는 다르게 동작한다.

let 키워드

일단 자바스크립트의 모든 키워드는 호이스팅이 발생한다. 이유는 자바스크립트 엔진 때문이다. 다만 let키워드의 경우 선언 에서 초기화로 넘어가기 전에 TDZ 가 존재해서 보통 var 키워드로 했으면 undefined 가 출력될법한 상황에서 ReferenceError 를 내버린다. 코드가 평가되는 과정에서 var 와 다르게 암묵적으로 undefined를 깔아놓고 시작하지 않는다. 물론 인터프리터적인 관점에서 바라볼 때, 값이 없이 선언만 있다면 역시나 undefined를 깔아주지만 그걸 코드를 만나기 전에 가져다 쓰려면 var 처럼 되지는 않는다는 것이다.

함수의 경우

console.log(a); // a 함수의 내용이 출력된다. 함수 a는 저 밑에 있는데;;
console.log(b); // undefined가 나온다. var 키워드를 사용한 함수표현식을 사용했기 때문이다.

console.log(a(1,2)); // 3, 잘 돌아간다;;;
console.log(b(1,2)); // TypeError 가 난다. 돌아가지 않는다. 왜나면 아직 undefined가 변수 b에 꽂혀있기 때문이다. 코드 평가할 때는 b 변수에 undefined 가 할당된 것까지만 알지 실제 돌아갈 함수의 내용은 런타임에서 변수 b에 할당되기 때문에 돌려볼 함수가 없는 것이다.

function a(x,y){
  return x+y;
}

var b = function(x,y) {
  return x+y
};

호이스팅은 함수의 케이스도 있는데 원리는 비슷하다. 함수 선언문 으로 함수를 만들 경우 비슷하게 작동하고 함수표현식으로 만들 경우에는 에러가 나버린다. 물론 함수를 들여다 보면 선언문의 경우는 함수의 내용이 잘 들여다 보이고 표현식으로 선언한 케이스는 undefined가 나온다. 함수를 돌려보면 선언문의 경우는 훌륭하게 작동하고(...) 표현식의 경우는 타입에러를 내고 죽는다. 이유는 주석에 써놓았다.

결론

사실 해결법은 간단하다. 런타임 기준으로 생각하고 코드를 작성하면 문제될 것이 전혀 없다. 그렇다고 이게 클로저처럼 어떤 코딩의 기법으로 사용할 수 있을지도 잘 모르겠다. 그러니 그냥 인터프리터가 돌아가는 런타임 기준이라고 생각하고 코드를 작성하면 깔끔하게 될 것 같다. 여담으로 수천줄의 자바스크립트 코드가 있는데 혹여나 함수들이 죄다 호이스팅 되는 상황이라면 성능에 좀 문제가 생기지 않을까 싶다. 이유는 함수의 생성과정을 보면 알 수 있을 것이다.

profile
야매로 먹고사는 프론트엔드 개발자

0개의 댓글