constinner = () => { const b = 5; return a + b; };
return inner; };
const closure = outer();
closure();
소스코드 평가와 실행
1. 전역 코드 평가
전역 실행 컨텍스트 생성
var 키워드로 선언된 전역변수 outer는 전역 객체에 등록
const 키워드로 선언된 전역변수 closure는 선언적 환경 레코드에 등록
this는 전역 객체 바인딩
2. 전역 코드 실행 단계
outer 우항의 함수를 평가한 뒤, 메모리에 저장한 뒤 전역 객체에서 outer 식별자에 할당
closure 우항의 outer 함수를 전역 객체에서 찾아 호출
3. outer 함수 평가 단계
outer 함수 실행 컨텍스트 생성
outer 함수에서 선언된 변수 a, inner를 환경 레코드에 등록
outer 함수의 외부 렉시컬 환경 참조는 outer 변수에 할당된 익명함수 객체의 내부슬롯 [[Environment]]가 가리키는 외부 렉시컬 환경을 가리킨다.
4. outer 함수 실행 단계
outer 함수 환경 레코드의 변수 a에 값 3이 할당된다.
변수 inner에 익명 함수 객체의 참조값이 할당되고, 익명 함수 객체의 내부 슬롯 [[Environment]]은 정의된 outer 렉시컬 환경을 가리킨다.
5. 전역 코드 실행 단계 복귀
전역 렉시컬 환경의 선언적 환경 레코드에서 closure를 찾는다.
inner 함수 객체가 closure라는 식별자에 할당된다.
6. closure 함수 평가 단계
closure() 문이 실행되면서 closure의 실행 컨텍스트가 생성된다.
closure를 실행하기 위해 inner 함수 객체를 평가한다.
즉, closure() 평가단계 === inner() 평가단계
const로 선언된 변수 b가 closure 환경 레코드에 등록된다.
화살표 함수의 this는 함수가 정의될 때 상위 스코프의 this로 정적으로 결정된다.
7. closure 함수 실행 단계
closure 즉, inner 함수 내부 코드가 실행
b에 값 5가 할당되고 a+b를 수행하기 위해 스코프 체인에서 a를 찾는다.
closure 환경 레코드에는 a가 존재하지 않기 때문에 외부 렉시컬 환경 참조를 따라 익명함수 객체로 이동하고, 익명함수 객체의 [[Environment]]내부 슬롯이 가리키는 outer 함수 렉시컬 환경으로 이동된다. 결국 outer 환경 레코드에서 a를 찾을 수 있다.
a+b 연산을 수행하고 연산값을 반환한다.
8. closure 실행 컨텍스트 소멸
closure 함수 코드가 실행을 마치고 closure의 실행 컨텍스트가 pop 되어 더 이상 참조되지 않는 객체들은 Garbage Collector에 의해 메모리가 해제된다.
9. 전역 실행 컨텍스트 소멸
마지막으로 전역 실행컨텍스트가 pop 되어 참조될 수 있는 객체가 없기 때문에 모든 객체가 Garbage Collector에 의해 메모리가 해제된다.
이전에 선언을 하면 선언단계와 초기화 단계가 진행된다 그랬었는데, 이것도 초기화가 맞지만 const를 사용하기 위해서는 개발자가 직접 초기화를 해줘야한다. 만약 초기화를 해주지 않고 나중에 값을 할당한다는 것이 재할당으로 해석되기 때문이다.
재할당 금지
const 키워드로 선언한 변수에 원시값을 할당하면 값을 변경할 수 없다. 하지만 객체를 할당한 경우 값을 변경할 수 있다. 재할당 금지라는 말이 불변을 의미하지는 않는다. 왜냐하면 식별자가 가리키는 메모리 주소 공간은 참조값이 저장되어 있고 객체를 변경하여도 참조값은 변하지 않기 때문이다.
상수
변하지 않는 값을 사용하기 위해 우리는 상수를 사용한다.
주로 상수의 이름은 대문자로 사용한다. 원시값을 할당한 경우 원시값은 변경 불가능한 값이고 재할당이 금지되므로 할당된 값을 변경할 방법은 없다.
위 예시는 counter를 캡슐화한 예시이다. 이렇게 캡슐화를 하게되면 count라는 변수에 대해 참조하거나 변경하는 함수를 통해서만 간접적으로 접근이 가능하도록 하여 해당 데이터를 은닉하고 안전하게 보존하기 위해 사용한다.
2. 추상화
우리는 앞서 TV라는 데이터를 추상화하였다. 하지만 추상화는 예시처럼 간단하지만은 않다. 추상적으로 큰 틀에 공통적인 요소나 필수적인 요소를 담는 것을 말한다.
다시 TV의 예를 들어보겠다. 우리가 실생활의 모든 데이터(TV, 냉장고, 인덕션, 청소기 등)를 모두 개별적으로 만드는 것보단 이들의 공통적인 특성을 가진 큰 틀의 객체를 만들고 해당 객체의 상속을 받고 본인만의 특별한 기능을 추가하는 방법으로 객체를 생성해나가는 것이 중복을 줄이고 확장성의 장점을 살려 추상화를 할 수 있다.
SSR은 서버 사이드 렌더링의 줄임말로, 클라이언트가 서버에 요청을 보낼 때 서버에서 HTML,JS 등을 렌더링하고 클라이언트에게 완성된 HTML 파일을 응답해주는 방식이다.
CSR은 서버에 요청을 보내면 서버에서 HTML,JS 리소스 파일등을 받은 이후 브라우저에서 렌더링을 진행하는 것이다. 이후 클라이언트는 서버에게 데이터만 요청하면서 브라우저가 렌더링한 페이지에 서버의 응답으로 받은 데이터만 패칭해주면 된다.
😃 특징
✈️ 초기 로딩 속도
SSR은 클라이언트가 요청한 부분의 페이지만 렌더링해서 보내주면 되므로 초기 로딩 속도가 CSR에 비해서 빠른 반면, CSR은 HTML,JS, 모든 리소스를 한번에 로드하기 때문에 초기 로드 속도가 느리다.
❗️ 서버 부하
SSR은 View가 바뀔 때마다 서버에 요청을 보내고 서버는 그 때마다 응답해줘야하므로 서버의 부하가 높고 UX 측면에서는 깜빡임 현상이 있는 단점이 있다. 반면, CSR은 데이터 요청이 있을 때만 서버에 요청을 하기 때문에 서버 부하가 적다.
📚 SEO
SSR은 서버가 렌더링을 하고 완성된 페이지를 클라이언트에게 보내주는데, 그 안에는 SEO에 사용되는 meta 태그 등이 미리 정의 되어 있어 SEO 측면에서 유리하다. 반면 CSR은 초기에 비어있는 HTML 파일을 보내주고 JS를 다운로드하여 브라우저에서 렌더링이 완료되기 전까지는 빈 파일로 남아있기 때문에, SEO 측면에서 불리하다.
Next.js에서 SSR,CSR
이번 과제에서 Next.js를 사용하면서 새롭게 배우게 된 사실을 정리해본다. Next.js는 기본적으로 SSR을 지원한다. SSR을 지원하는 방법으로는 2가지 방법이 있는데,
첫번째, 클라이언트가 요청할 때마다 서버에서 렌더링을 해주는 진짜 SSR 방식
두번째, 미리 렌더링을 끝내놓은 페이지를 클라이언트가 요청할 때마다 캐싱하여 보여주는 SSG 방식
그리고 CSR까지 지원해준다.
Next.js를 사용하다보니 SSR, CSR, SSG에 대한 이해가 더 잘되는 것 같아서 마무리로 적어보았다.