본문 바로가기

개발/JavaScript

Javascript 기초

728x90
반응형

🔴 학습목표

실제로 눈으로 보고 작성하는 코드와 달리 컴퓨터 내부에서 동작하는 소스 및 원리는 눈에 보이지 않기에 추상적일 수 밖에 없다. 이 추상적인 개념과 원리를 이해하고 , 코드로 구체화할 수 있는 능력도 길러야한다.

🟡 JavaScript의 동작원리

자바스크립트를 실행하기 위해서는 자바스크립트 엔진이 필요하다. 자바스크립트 엔진은 싱글스레드 기반 언어인데, 싱글이라는 이름에서 알 수 있듯이 작업을 처리하는 공간이 하나기에 다른 작업을 수행하려면 현재 수행중인 작업이 끝날때까지 기다려야하는 문제점이 있다. 이러한 문제점을 해결하기위해 자바스크립트 런타임환경이 생겨났는데, 이는 오랜 시간이 걸리는 작업들은 백그라운드에서 처리하고, 간단하게 처리할 수 있는 작업들만 스택에서 처리하는 방식이다.
싱글스레드로써 하나의 call stack에서 일을 처리하지만 브라우저가 멀티 스레드로 동작하기에, 멀티쓰레드 처럼 보이게 동작할 수 있으며, 이는 web api, callback queue, event loop 등에 의해 가능해진다.

engine : 다른 프로그램들을 위해 또는 특정프로그램 내부에서 해당 프로그램을 위해 핵심적이고 본질적인 기능을 수행해주는 프로그램.
runtime : 프로그램이 실행되는 것에 의미를 갖고 실행되는 서버 혹은 컴퓨터 내에서 프로그램이 실행되는 동작
runtime환경 : 프로그램이 실행되는 환경

🟠 Call stack ?? Memory heap ??

call stack 은 간단하게 얘기하면 코드를 상단좌측에서부터 읽어내려가면서 수행할 작업들을 stack구조 ( Last-In-First-Out ) 로 쌓아가며, 필요한 정보는 Memory heap 에서 찾아 작업을 수행하는 공간이다.

Memory heap은 변수의 선언, 함수의 저장, 호출 등의 작업이 발생하는 공간이다. 즉 메모리 할당이 일어나는 곳이다. 여러 가지 정보들을 마치 창고에 보관하는 택배처럼 저장해두고, 운송장을 통해 필요한 정보를 찾아 택배를 꺼내오는 것이다.

🟢 JavaScript Runtime 환경

싱글스레드 기반인 js언어를 마치 멀티스레드와 같은 성능으로 보이게끔 눈속임을 만들어주는 친구이다.

  • Web API
    웹 브라우저 자체에서 제공하는 기능들을 이야기한다. (Ajax요청, setTimeout(),  window. 과 같은 기능들) 
    이 기능들을 사용하여 백그라운드에서 비동기적으로 작업을 처리할 수 있고, 이러한 작업이 끝나면 js엔진에게 해당 작업이 끝났음을 알려 작업을 수행해 나가게 한다.
  • Callback Queue
    비동기 함수의 callback 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역이다.
    이름은 queue로 무조건 선입선출을 할 거 같지만 사실은
    크게 3가지의 세부 queue 로 구성되어 있고, 세부 queue의 종류에 따라 call stack으로 옮겨지는 우선순위가 다르다.
    우선순위는 아래 설명대로 Microtask > Animation Frames, Task 순이다.
    - Microtask Queue : process.nextTick, Promises, queueMicrotask(f), MutationObserver, async/await 등과 같은 비동기 호출을 넘겨 받는 queue
    - Animation Frames : requestAnimationframe 과 같이 브라우저 렌더링과 관련된 task를 넘겨받는 queue

    - Task Queue : setTimeout, setInterval, setImmediate과 같은 task를 넘겨받는 queue

  • Event Loop
    콜 스택에 현재 실행 중인 실행 컨텍스트가 있는지, 그리고 콜백 큐에 대기 중인 함수가 있는지는 반복 확인한다. call stack이 비어있고 callback queue 에 대기 중인 함수가있다면 이벤트 루프가 순차적으로 콜백큐에 대기 중인 함수를 call stack으로 이동시켜 실행하게 만든다.
API (Application Programming Interface) : 응용 프로그램에서 사용할 수 있도록, 운영체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만드는 인터페이스
Ajax (Asynchronous Javascript And XML) : 비동기식 자바스크립트와 xml의 약자로, 브라우저가 가진 XMLHttpRequest 객체를 이용해서 전체 페이지를 새로 고치지 않고도 페이지의 일부만의 데이터를 로드하는 기법이다. 즉 javascript를 사용한 비동기 통신, 클라이언트와 서버간의 XML 통신기술이다. 요약하면 js를 통해 서버에 데이터를 요청하는 것
이벤트 핸들러 : 웹페이지에서는 수많은 이벤트가 발생한다. ( 마우스 움직임, 요소 클릭 등 ) 이러한 이벤트를 처리하기위해 작성하는 함수들을 이야기한다.

🔵 작동순서

코드는 상단좌측부터 읽어내려가는데 위 사진에선 1~4 줄은 foo(b) 함수 선언, 6~8 줄은 bar(x) 함수 선언이고, 처리해야하는 함수는 console.log(bar(6)) 이다. 따라서 console.log(bar(6)) 이 제일 먼저 콜스택에 담기고, console.log 함수를 실행하기 위해서 필요한 bar(6) 을 호출하고, bar(6)을 리턴하기위해서 필요한 foo (b) 를 호출하는 순서 인것이다.

여기서 짚고 가야할 점과 이상한 점이 있는데 우선 짚고 가야할 점은 함수 선언이라고 막연하게 건너 뛰는 것이 아니라, 해당 함수에 대한 정보를 상자에 담아 foo,bar 라는 이름의 라벨을 붙이고 memory heap 이라는 창고에 보관을 하는 동작이 눈에 보이지 않지만 일어나고 있는 것이다. 

그런데 이상한 점은 맨 아래에 main() 이라는 함수가 깔려있는 것인데, 이는 추상적으로 붙인 이름일 뿐 사실은 실행할 코드들을 담은 전체 코드로써 이름이 없는 함수 Anonymous function 이 존재하게 되는 것이다. 추상적이긴하지만 코드로 정리하자면 아래함수가 실행되어 전체적인 흐름이 실행된다는 이야기는

function foo(b) {
	var a= 5;
	return a*b +10;
}

function bar(x) {
	var y = 3;
    return foo(x*y);
}

console.log(bar(6));

아래의 이야기와 같은 이야기이다. 따라서 전체적인 코드가 실행되었다는 뜻으로 익명함수 또는 main 함수가 제일 하단에 위치하고, 더 이상 돌아가는 코드가 없으면 stack에서 pop 되어 실행이 종료되는 것이다.

즉, 익명함수가 push된다는 것은 프로그램이 실행되었다는 이야기이고, pop 되었다는 것은 프로그램이 종료되었다는 것을 의미한다. 또한, 프로그램이 종료가 되었다는 것은 그 프로그램을 실행하기 위해 선언했던 함수들, 변수들이 더 이상 사용되지 않으므로 memory heap 창고에서 빼내어 소각처리 하게 되고, 이는 메모리 할당 해제라는 뜻이 된다.

(function foo(b) {
	var a= 5;
	return a*b +10;
}

function bar(x) {
	var y = 3;
    return foo(x*y);
}

console.log(bar(6));)();
//()(); : 즉시실행함수이자 익명함수

 

🟣 추상화를 코드화하기

멋도 모르고 일단 타이핑하고 짜는 바람에 수 많은 오류를 겪고 이해하고 쓴게 아니라 무식하게 때려박고 경험적으로 짠 코드들을 call stack 원리를 적용하여 다시 생각해보는 과정이 필요하다. 다음 포스트에는 router 통신 하나를 작동원리에 따라 그려보아야하겠다.

 

🟢 메모리힙이 먼저? 콜스택이 먼저?

공부를 하다보니 선언을 하면 콜스택에 담기진 않고, heap 공간 속에 저장이 되지만 선언과 동시에 값을 할당해주면, 즉 초기화해주면 초기화 해줄 값을 먼저 처리해야 메모리에 저장이 쏙 들어가는지 아니면 아무 공간이나 잡아놓고 있다가 값을 내려주는 건지 헷갈리기 시작했다.

결론:

javascript 변수를 선언하면 메모리 할당이 즉시 발생한다. 그러나 선언은 호출 스택에 들어가지 않는다.

변수를 선언하면 undefined 메모리를 할당하고, 이런 할당은 변수 선언의 "생성", "초기화" 단계에서 발생한다.

선언 자체는 호출 스택에 즉시 들어가지않고, 초기화 단계 이후에 발생하는 "실행" 단계에서 처리가 된다. 실행 단계에서 javascript 엔진이 변수에 실제로 의도된 값을 할당한다.

요약:

메모리할당은 변수 선언의 초기화 단계에서 발생하지만, 선언 자체는 실행 단계까지 호출 스택에 들어가지 않는다.

-> 이는 JS가 동적인 언어이기 때문에 발생하는 것 같다. 동적언어 설명은 아래에

 

🔵 정적언어와 동적언어

정적언어는 변수의 data type을 컴파일 시에 결정하는 것이고, 동적 언어는 실행 시에 결정하는 것이다.

정적언어는 변수에 들어갈 값의 형태에 따라 자료형을 미리 코드로 지정해주어야하기에, 자료형에 맞지 않은 값이 들어있으면 컴파일 에러가 발생한다. 따라서 타입 에러로 인한 문제점을 초기에 발견할 수 있어 안정성이 높다.

int num = 1;
float num2 = 1.03;
bool num3 = 1; // 컴파일 에러

동적언어는 코드상에서 정해주는 type 없이 변수만 선언하여 값을 지정하기에 Runtime 까지 타입에 대한 결정을 끌고 갈 수 있어 전체적인 개발 속도는 빠를 수 있고, 배우는데 쉽기도 하겠지만, 실행 중 발생하는 runtime오류는 원인을 분석하기가 힘들다는 큰 단점이 있다.

let num = 10;
num = "강낭콩"; //에러 x

🟡 Execution context

자바스크립트가 실행되기 위해서는 scope Chain, variable object , this 값 등 이런 것들을 묶어서 실행 컨텍스트라고 이야기한다. 크게 3가지로 나눈다.

scope chain : 일종의 리스트로 전역 객체와 중첩된 함수의 스코프의 reference 를 차례로 저장하고, 각각의 스코프가 어떻게 연결되어 있는지 보여주는 것이다. -> 자기 자신의 scope를 제외한 자신과 가장 가까운 변수 객체의 모든 스코프들을 이야기한다.
variable object : 실행에 필요한 정보를 담고 있고, 코드가 실행될 때 엔진에 의해 참조되며, 코드에서는 접근할 수 없다. 구성요소로는 변수 선언, 함수 선언, 매개변수와 인수정보가 있다.

- global Execution context : 무조건 1개만 존재하는 (싱글 스레드) 자바스크립트 실행환경.

- Functional execution context : 함수가 호출되고, 실행이 될 때 실행되고 있는 함수에 대해서 생성되는 context, 지역변수, 지역 scope

- eval : eval function을 사용할때 발생하는 context

let a = 10;

function functionA {
	console.log("start function A");
    
    function functionB(){
    	console.log("In function B");
    }
    functionB();
}
functionA();

let a=10; 이 실행될때 stack 최하단에 global exection context, 후 functionA() 호출할 때 functional context, functionB() 호출할 때도 functional context 해서 총 3개의 context가 쌓여있다가 함수가 종료됨에 따라 하나씩 pop 되고 맨 마지막엔 global exection context도 나가게 된다.

## 생성과정

  1. Creation phase : 3가지 단계로 나뉜다.
    - creates the activation object or the variable object : activation object는 global execution context의 variable object를 이야기한다.
    - creates the scope chain : 변수가 동작하는 영역(유효한) 을 가장 가까운 local스코프부터 상위 scope 최후엔 global scope 까지 연결하는 것
    - Determines the value of this : this 값을 결정하게 된다.
  2. Execution phase

 

 

 

🟣 Reference

https://yunamom.tistory.com/98  >> Ajax 
https://velog.io/@hang_kem_0531/JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC >> JS의 동작원리
https://velog.io/@baek1008/JS-JS%EC%9D%98-%EB%8F%99%EC%9E%91-%EB%B0%8F-Queueu%EC%9D%98-%EC%A2%85%EB%A5%98%EC%97%90%EB%94%B0%EB%A5%B8-%EC%8B%A4%ED%96%89Microtask-QueueAnimation-Frames-Task-Queue >> Call queue 의 구성
https://itmining.tistory.com/65 >> 정적언어와 동적언어
https://ljtaek2.tistory.com/140 >> scope chain

 

 

 

 

 

 

 

 

기타

1. 콜스택엔 원시형 데이터 저장된다.

let a = 10; let b= 20; 이면 a에는 10이저장된 콜 스택 메모리의 주소값이 b에는 20이 저장된 콜스택 메모리의 주소값이 저장된다.
a = 20; 으로 재할당을 하게 되면 위에서 b=20 이라는 초기화를 통해 콜스택 메모리에는 20을 저장하고 있는 공간이 존재하고 있으므로, 이 공간을 가리키던 주소값인 b와 같은 값을 가지게 된다.
a= 30; 으로 재할당을 하게되면 20이 저장되어 있던 메모리를 직접 수정하는 것이 아니라, 새로운 메모리를 확보하여 30을 저장하고, 변수 b에 저장된 주소 값을 해당 주소 값으로 교체한다.
이후 메모리를 가리키는 변수가 없는 메모리는 가비지컬렉터에 의해 적절한 시점에 메모리에서 해제된다.

원시 타입 데이터는 string, number, boolean, null, undefined , + symbol(es6부터 추가된 타입) 이 있고 값 자체에 대한 변경이 불가능하지만, 변수에 데이터를 재할당할 수 있다. 하나의 메모리에 하나의 데이터를 보관한다. 변수를 재할당해도 변수의 할당 값에 영향을 주지 않는다.

 

2. 힙엔 원시 타입이 아닌 타입들이 저장되는 공간이다.

갑자기 데이터가 커질수도 있고, 배열과 객체와 같은 정렬되지 않은 데이터들을 저장할 수 있다는 점이 핵심이다.

변수에 할당될 때 값이 아닌 주소를 저장하는 타입. 변수는 주소를 저장하고, 주소는 메모리힙에 저장이 된다. 값을 재할당 할 경우 주소는 참조한 모든 값이 영향을 받는다. -> 공유가 된다.

let obj = {
  a: 1,
  b: 2,
  c: 3
}

let newObj = obj;
newObj.a = 100; // 참조된 값 중 a의 값에 100을 재할당

console.log(obj.a); // 100
// 해당 주소를 참조한 newObj의 a값을 바꾸었기 때문에 두 객체 모두 반환하는 a 값이 변경됨.

배열 myArray=[] ; 를 선언하면 ([]같은 참조 타입을 할당하면) 아래와 같은 순서로 일이 발생한다.

1. 변수의 고유 식별자를 생성 (myArray)
2. 콜 스택의 메모리에 주소를 할당한다(런타임할당)
3. 힙에 할당된 메모리 주소를 콜스택의 값으로 저장한다 (런타임할당)
4. 힙의 메모리 주소에 할당된 값( 빈 배열[] ) 을 저장한다.

 

콜스택은 원시데이터를 저장하면서 실행중인 코드를 트래킹하는 공간이고
메모리 힙은 정보를 저장하는 공간

 

 

---
런타임이란 이고

런타임 환경은 프로그램이 실행되는 환경이다.
즉 자바스크립트 런타임이란 javascript 가 구동되는 환경을 말한다.
정적 언어와 달리 js, phyton 등과 같은 동적 언어는 컴파일 시가 아니라 런타임시 변수의 타입을 결정하게 되는데, 따라서 고정된 타입이 없어서 같은 변수에 여러 타입의 값을 자유롭게 할당할 수 있다.
장점: 런타임시 타입이 결정되기에 유연성이 높고, 타입을 명시하지 않기에 개발 속도가 향상된다.
단점 : 런타임시 타입 자동 변환을 하기에 타입 에러를 발생시킬 수 있다. 런타임시 확인할 수 밖에 없기 때문에, 코드가 길고 복잡해질 경우 타입 에러를 찾기가 어려워진다.

웹브라우저 프로그램(크롬, 파이어폭스) 와 node Js 같은 프로그램이 있다.

프로그램이 컴파일이라는 과정을 통해 기계어코드로 변환 되어 실행가능한 프록램이 되고, 이때 이런 편집과정을 컴파일 타임이라고한다. 컴파일 타임을 지난 프로그램은 사용자에 의해 실행되어지며, 이런 응용프로그램이 동작되어지는 때를 런타임이라고 부른다.

컴파일 타임에는 변환과정을 방해하는 syntax error 나 파일참조 오류와 같은 문제를 말하고, 일반적으로 문제를 일으킨 소스코드 라인을 제시해준다. 하지만 어떤 소스코드가 이미 실행가능한 프로그램으로 컴파일이 완료 되었다해도 여전히 프로그램의 실행중에 버그를 일으킬 수 있다. 프로그램이 실행 중에 발생하는 예상치 못한 오류 또는 충돌로 동작하지 않는 형태의 오류를 런타임 오류라고 한다.
런타임 오류의 유형: 0나누기 오류, null 참조 오류, 메모리 부족 오류

728x90
반응형

'개발 > JavaScript' 카테고리의 다른 글

FormData  (0) 2023.04.19
코드 동작원리 예제  (0) 2023.04.04


Calendar
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Archives
Visits
Today
Yesterday