본문 바로가기

Today Learning _

Part 17. JS 라이프 사이클 이해하기

컴퓨터 시스템의 모든 자원은 라이프 사이클 즉, 생애 주기를 가집니다.
Javascript의 자원들 값, 변수, 함수 등 또한 생애주기를 가집니다.

생애주기란 생성되고 사용되며 소멸되는 메커니즘을 의미합니다.
이런 메커니즘이 필요한 이유는 컴퓨터 시스템의 자원은 유한한 것이기 때문입니다.
유한한 자원을 효과적으로 사용하기 위해 고안된 방식입니다.

Javascript에서 자원 = 값을 의미합니다.
그리고 값이란 문을 제외하고 거의 모든 것을 의미합니다.
Javascript의 값은 모두 생애주기 메커니즘의 영향을 받습니다.

모든 값은 저장되며 저장되기 위해선 저장소가 필요합니다. 
값이 저장되는 저장소를 스코프(지역)라 합니다.

Javascript에서 스코프는 세 가지가 존재합니다.
- 전역 스코프
- 함수 스코프
- 블럭 스코프(ES2015에서 추가된 것)

 

 

스코프의 범위를 나타낸 그림입니다 :

 


전역 스코프

전역 스코프는 프로그램의 생애 주기와 동일한 스코프입니다.
브라우저가 HTML을 로딩한 후 페이지를 벗어나 새로고침 할 때까지의 주기를 가집니다.
전역 스코프는 모든 스코프의 최상위 스코프이며 모든 지역 범위에서 참조 가능합니다.

ES2015 이전 버전에서 전역 스코프는 브라우저 환경이라면 window 객체와 동일했습니다.
전역 스코프는 임의로 생성하거나 삭제하는게 불가능합니다.

 



지역 스코프

지역 스코프는 전역 스코프 이외에 생성된 스코프입니다.

지역 스코프는 두가지 방법으로 만들 수 있습니다.

 


함수 스코프

함수가 호출되는 즉시 함수 스코프가 생성됩니다. 
함수 안에서 생성된 모든 변수들은(인수 포함) 함수가 실행되는 순간에 생성되며 함수가 종료되는 순간 함수와 함께 제거됩니다.

 


블록 스코프
블럭블록 스코프는 블록 안에서 생성됩니다.
블록 스코프는 ES2015 버전에서 지원되기 시작했으며 if, while, for 등의 코드 블록으로 생성됩니다.

그러나 블록 내에서 변수를 만들 때 var로 만든다면 함수 스코프에 변수가 만들어집니다.
새롭게 지원되는 블록 스코프에 변수를 만들기 위해서는 let이나 const가 필요합니다.

 

전역 및 함수 스코프를 나타내었습니다 :

 

displayTitle 1의 안의 console.log 의 값은 var에 지정된 값인 '함수 스코프' 입니다.
하지만 displayTitle2는 전역 변수인 title 의 값에 영향을 받으므로 '전역 스코프'가 출력됩니다.

 

블럭 스코프를 나타내었습니다 :

 

displayTitle 내의 if문(블럭) 에서의 값은 '블럭 스코프'가 될 것이고
displayTitle의 범위 안에 있지만 if의 범위를 벗어난 console.log의 값은 '함수 스코프'가 됩니다.
마지막으로 전역 변수의 영향을 받는 console.log 는 '전역 스코프'를 출력합니다.

 

함수 스코프와 블럭 스코프를 비교했습니다 :

var를 변수로 가진 for문 내의 console.log 값은 해당 배열의 각 값을 가져올 것이고
밖의 console.log는 i의 값인 3을 가져올 것입니다.
반면에 let을 변수로 가진 for문의 내에서는 해당 값을 가져올 수 있으나
범위를 벗어나면 오류가 생겨납니다. = 해당 값을 찾을 수 없다(j)

 

호이스팅의 결과를 node 값으로 확인했습니다 :

displayPerson의 var의 변수인 name의 값은 호이스팅 했으나 값을 가져오지 못해
결과가 undefined로 나타났습니다. 반면에 let의 경우 호이스팅 자체가 되지 않아
해당 결과가 오류가 납니다. 이 상황에서 undefined보다 해당 결과의 오류가
뚜렷하게 값을 가져다 주는 것이기에 let을 되도록 사용하도록 합니다.

 

반복문 내의 호이스팅을 나타내었습니다 :

 

 

 

 

클로저

지역 스코프에서 생성된 변수는 스코프가 소멸될 때 함께 소멸됩니다.
예외적으로는 함수는 생성시점에 참조 가능한 알려진 변수들을 캡처하는 능력을 가집니다.
이렇게 캡처된 지역을 클로저라 합니다.

보통의 경우 함수가 종료되면 클로저 영역도 사라져 특별할 게 없어 보이지만
함수 내에서 함수가 생성되어 리턴 값으로 반환될 때 생성된 함수에 캡쳐된
클로저 영역은 해당 함수 내에서만 접근 가능한 특별한 공간이 됩니다.
이 공간을 Javascript에서는 다양한 용도로 사용합니다.

 

클로저의 예시입니다 :

 

클로저를 arrow function으로 나타냈습니다 :




Javascript는 언어적으로 private 속성 및 메서드를 지원하지 않습니다.

 

**private 속성 및 메소드는 객체 외부에서 접근할 수 없으나 내부에서 접근하여
사용할 수 있는 속성이나 메소드를 말합니다.


객체가 어떤 상황에서도 동작할 수 있는 코드 구성은 복잡도가 높아질수록 중요합니다. 
그러나 Javascript가 언어적으로 제공하고 있지 않기에 이를 클로저를 이용해 흉내낼 수 있습니다.

 

 

보호받지 못하는 객체를 나타냈습니다 :

해당 코드의 마지막 console.log의 값이 보호되지 못하고 있습니다.
C는 배열 안에 없는 존재이기에 get의 값을 출력해야 하지만
그러지 못하고 있습니다. 

 

 

클로저로 private 값을 보호한 예시입니다 :

 

따라서 클로저를 이용해 해당 값을 보호할 수 있도록 만들어줍니다.

 

 

 

비동기 상황에서의 클로저 사용

이벤트 말고도 요청한 작업이 언제 끝날지 모르는 상황이 다양하게 생깁니다.
대표적으로 파일을 읽을 때를 예로 들 수 있습니다.

 

파일 시스템에서 파일을 읽을 때 파일의 크기에 따라 읽기가 

완료된 시점이 달라지며 이는 프로그램 내에서 예측할 수 없습니다. 

따라서 파일 읽기 API는 파일 읽기가 완료된 이벤트를 발행하고 

이를 구독하여 파일을 읽는 방식으로 디자인되어 있습니다. 

이를 비동기 방식이라고 합니다.

비동기 코드 흐름에 있어 고려해야 할 상태는 
동시에 실행되는 것 같은 코드 흐름이 존재한다는 것입니다.

 

ex) 병렬 실행의 예시 - 동시 실행하는 숫자 but 시간은 다르게

병렬 실행 흐름의 JS 코드입니다 :

 

병렬 실행 흐름의 HTML 코드입니다 :

 

병렬 실행 흐름의 CSS 코드입니다 :

 

 

병렬 실행 흐름의 결과 UI 입니다 - 시작을 누를 때 :

 

시작을 누르게 되면 해당 숫자들이 병렬적인 흐름으로 카운트 됩니다.
위의 코드를 보면 알 수 있듯이 서로 카운팅되는 속도가 다릅니다.

병렬 실행 흐름의 결과 UI 입니다 - 부스트를 누를 때 :

 

부스트를 누르게 되면 해당 숫자가 급속도로 빠르게 카운팅 됩니다.




ex) 비동기 방식으로 이미지 파일을 읽어 화면에 표시하는 예제

 

하나의 이미지를 읽는 JS 코드입니다 :
HTML 코드입니다 :

 

결과 UI 입니다 :



input 파일은 컨트롤은 여러 개의 파일을 담을 수 있습니다.
이미지를 여러 개 읽을 수 있도록 예제를 변경합니다.

 

 

여러개의 이미지를 읽는 JS 코드입니다(var 사용) :
HTML 코드입니다 :

 

결과 UI 입니다 :

 

해당 파일을 2개 선택했음에도 불구하고 UI에서는 
하나의 사진만 볼 수 있었습니다.



for문을 이용해 복수의 이미지 파일을 순회하며 읽어 들이는 코드입니다.
그러나 비동기 상황에선 이런 반복 상황이 간단하게 해결되지 않습니다. 

 

**비동기 상황이란 개념적으로는 동시에 실행되는 코드가 하나 이상이며
이는 실행되는 코드 간에 시간차 문제를 발생시킨다는 것을 의미합니다.


예제에서는 이미지 읽기가 끝난 후 실행되는 함수 내에서

참조되는 reader 객체가 어떤 이미지를 처리하고 있는지 보장되지 않습니다.
따라서 실행 결과를 보면 원하는 방식으로 작동되지 않는다는 것을 알 수 있습니다. 

비동기 상황에서 타이밍 이슈를 Javascript에선 전통적으로 클로저를 이 용한 변수 캡처 방식을 이용했습니다.
캡쳐가 필요한 상황에서 즉시 실행 함수를 만들어 해당 변수를 캡처하면 간단히 타이밍 이슈를 해결할 수 있습니다.

 

 

즉시 실행 함수를 이용해 문제를 해결했습니다 :

 

결과 UI 입니다 :


ES2015에서 제공되는 블록 스코프를 이용하면 이 문제를 클로저 없이 더 간단히 해결할 수 있습니다.
발생했던 타이밍 이슈는 근본적으로는 var 변수가 블럭 스코프를 지원하지 않고 
함수 수준에서 호이스팅 되기 때문입니다. 

블럭 스코프를 지원하지 않는 상황에선 이를 해결한 유일한 방법은 클로저를 사용하는 것입니다.
즉, let을 이용하는 것이 더 오류를 줄일 수 있습니다.

 

여러개의 이미지를 읽는 JS 코드입니다(let 이용) :

 

결과 UI 입니다 :

 

해당 사진을 계속 추가해도 이전의 사진이 사라지진 않습니다. 

 

 

 

 

참고자료 

 

{ Code } Playground

시작 부스트

woowabros-play-javascript.herokuapp.com

 

'Today Learning _' 카테고리의 다른 글

Part 18. JS 이벤트 시스템의 이해  (0) 2020.04.05
Part 16. GIT 다루기(심화)  (0) 2020.03.30
Part 15. Git 다루기  (0) 2020.03.30
Part 14. 배열 다루기  (0) 2020.03.26
Part 13. 브라우저 기본 객체 개요  (0) 2020.03.25