Javascript 의 기본적인 내용이자 Scope 와 관련된 중요한 개념이다.

 

Javascript 는 많은 프로그래밍 언어와 다르게 Block-level Scope 가 아닌 Function-level Scope 를 따른다.

Block-level Scope 가 모든 코드 블록의 유효성 판단을 선언된 코드 블록({}) 단위로 하는 데 반면, Function-level Scope 는 함수 단위로 Scope 의 기준을 잡는다. 

 

Function-level Scope 의 중요한 특징은, 함수 내부에서 봤을 때 함수 외부는 Nested Scope 의 Outer function 이라 할지라도 Global 영역으로 취급된다는 점이다.

 

Hoisting 이란, Javascript 내에서 해당 변수 또는 함수의 "선언" 을 끌어올린다는 점이다.

여기서 주의할 점은 함수의 선언과 변수의 선언이 조금 다르다는 점이다.

위의 경우에 inner1 함수의 경우 정상적으로 호출이 된다. 해당 함수의 선언이 아래 있음에도 호출될 수 있는 이유는 해당 함수가 Hoisting 되기 때문이다.

반면 inner2 함수의 경우 Hoisting 되지 않는다. 그 이유는 변수의 선언과 함수의 선언이 다른 의미를 지니기 때문이다.

위와 같이 함수의 선언은 단순히 함수를 정의하면 되는데 반해 변수의 선언은 선언과 할당이 다르다.

 

이 때 의문이 생길 수 있다.

inner2 예제의 경우 "변수를 선언하고 함수를 할당한 것"이 아닌가?

 

정답은 "아니다." 이다. 정확히 예제의 inner2 의 경우 함수리터럴을 할당했다고 표현한다. 

함수 리터럴이란 함수의 이름없이 구현체만 연결시키는 방식으로, 이 경우에 Javascript Engine 은 이를 호이스팅시키지 않는다.

 

 

다른 언어들과 마찬가지로 Javascript 역시 Scope 를 갖고 있으며, 이를 기본적으로 이해하는 것이 아주 중요하다.

 

Javascript 의 Scope 에는 다음 2가지가 있다.

 

(1) Local scope

지역 스코프를 말하며, Braclet({}) 안에서 정의되는 항목으로 정의된 해당 범위 내에서만 변수의 사용을 허용한다.

다른 범위에서 참조가 불가능하다.

 

(2) Global scope

전역 스코프를 말하며, 바닐라 자바스크립트에서 일반적으로 Braclet({}) 에 포함되지 않도록 정의된다.

전역으로 선언된 변수는 Window 객체로 포함되어 웹페이지 내에서 어디서든 참조가 가능하다.

 

그리고 응용된 Scope 로 Javascript 에서는 Nested Scope 라는 개념을 가질 수 있다.

 

(3) Nested Scope

Scope 내에서 별도의 Scope 를 정의한 경우 바깥쪽 Scope 에서 안쪽 Scope 에 접근이 불가능하지만 안쪽 Scope 에서는 바깥쪽 Scope 의 변수에 접근이 가능하다. 가령 다음과 같은 경우를 보자.

 

outerFoo() 함수의 출력 결과는 함수 내에서 innerFoo를 호출하기 때문에 70이 된다. innerFoo 함수에서 outerFoo 의 변수에 접근해서 곱하고 있는 모습이다. Nested Scope 에 의해 그 역은 성립되지 않는다.

이를 Lexical Scope 라고 하는데, 이는 변수나 함수가 문맥적으로 정의된 곳(Callee) 의 Context 를 참조한다는 일종의 원칙이다.

(이에 대한 대칭점으로 오래된 언어들에는 Dynamic Scope 라는 개념이 있고, 이는 변수나 함수가 불려진 곳(Caller) 의 Context 를 참조하는 개념이다.)

 

 

Javascript 의 Scope 는 기본적인 개념이라 익숙하면서도 자바스크립트의 특징적인 개념 중 하나이며, 중요한 개념인 Closure 를 이해하는 데 필수적이므로 잘 익혀두도록 하자.

 




웹 페이지를 구성하는 DOM Element 노드들은 트리 모양의 계층 구조를 갖고 있으며 DOM Element 와 연결된 Javascript 내부 동작 역시 이 구조에 대한 로직이 고려되어 있다.


이벤트 버블링과 캡쳐링은 Javascript 에서 주로 사용되는 Event Delegation Pattern 을 이해하는 기본적인 개념이다.


- 이벤트 버블링(Event Bubbling) 


웹페이지 내의 한 엘리먼트에서 이벤트가 감지되었을 때, 해당 엘리먼트의 계층구조를 따라 올라가면서 Root Element 까지 이벤트가 전달되는 것을 Event Bubbling 이라 한다.


이해를 돕기 위해 다음 코드를 확인해보자.


<div class="col-sm-1" onclick="alert('c')">
<div onclick="alert('b')">
<div onclick="alert('a')">
HELLO
</div>
</div>
</div>


위의 코드를 웹 페이지에서 확인해보면, HELLO 를 감싸고 있는 DOM 엘리먼트를 클릭하면 a - b - c 순서대로 3개의 Alert 창을 확인해볼 수 있다.


이처럼 이벤트 버블링(Bubbling)이란, 이벤트가 발생하는 가장 안쪽부터 바깥쪽, 하위 노드에서 상위노드로 이벤트가 전파(Propagation)되는 과정인 것이다.


하위 DOM Element 부터 전파되는 이벤트는 최종적으로 Document 객체까지 전달되며, 상위 Element 에서 이벤트 핸들러를 재정의해서 전파를 제어할 수 있다.


가령, 다음과 같은 코드에서 이벤트는 두번째 div 이상으로 전파되지 않는다.



<div class="col-sm-1" onclick="alert('c')">
<div onclick="event.stopPropagation()">
<div onclick="alert('a')">
HELLO
</div>
</div>
</div>


전파되지 않는 이유는 이벤트가 발생한 target Element 의 상위 Element 에서 onclick 메서드를 재구현하는데, event.stopPropagation() 함수를 이용해서 전파를 중단했기 때문이다.


이렇게 전파를 제어하는 것을 Stop Bubbling 이라고 한다.



- 이벤트 캡쳐링(Event Capturing)


이벤트 캡쳐링은 이벤트 버블링과 반대방향으로 이벤트가 전달된다.


다음 코드를 통해 이해해보자.


<div id="parent">
<div id="child">
HELLO
</div>
</div>
<script>
var parent = document.querySelector('#parent');
var child = document.querySelector('#child');
parent.addEventListener('click', function(){
alert("Parent clicked");
},true);
child.addEventListener('click', function(){
alert("Child clicked");
});
</script>


똑같이 계층 구조로 Element 들이 배치되어 있지만 이번에는 Parent 노드부터 이벤트가 전달된다.


이처럼 이벤트 캡쳐링(Event Capturing)이란, 상위노드부터 하위노드로 이벤트가 전달되는 형태를 말한다.


이 경우에 이벤트는 window 객체부터 차례로 아래 노드로 전파되며 addEventListener 의 3번째 인자로 capture 플래그를 두어 탐색 방향을 맞출 수 있다.


이벤트 캡쳐링의 경우 버블링보다는 덜 사용된다.



이벤트 버블링(Event Bubbling) 과 이벤트 캡쳐링(Event Capturing) 의 개념은 간단하지만, 이해하고 있지 않으면 은근히 프론트엔드 개발 시 삽질을 할 수 있는 부분이다.


잘 이해해두어 복잡한 UI 이벤트 처리 시에 헷갈리는 일이 없도록 하자.




COMET 이란 2006년 알렉스 러셋(Alex Russel)이 정의한 용어로, 브라우저가 HTTP 요청에 대해 데이터를 푸시하는 방법을 고안한 웹 모델이다.


일반적인 웹모델은 널리 알려진 바 대로 서버와 클라이언트 브라우저간 상태를 유지한 통신이 불가능하다. 


COMET 은 이러한 HTTP 의 본질적 한계를 어느정도 극복하고자 만든 Web Application Model 이라고 생각하면 된다.


가령 브라우저가 지속적으로 서버에 데이터를 받아야할 필요가 있을 때 서버의 데이터가 업데이트 되지않았다면 이는 무의미하다.

즉, 클라이언트의 입장에서 갱신이 필요한 절대적인 시점은 무조건 서버의 데이터가 변경되었을 때가 된다.


COMET 은 간단하게는 Client 로 유의미한 메시지를 전달할 때까지 HTTP 응답을 지연시키는 기술이다.


좀 더 정확히는 서버가 클라이언트의 요청에 응답할 때 응답을 "늘어뜨리는 방법" 을 이용해서 긴 시간동안 브라우저가 접속을 끊지않고 서버의 응답을 대기하게 만든다. 


즉, Long polling 과 원리가 같다.

(Long Poll – 서버측에서 단순히 클라이언트 측에 대한 연결을 길게 유지함으로써 지정한 기간 내에 정보가 있으면 응답을 전달한다. 메시지 양이 많으면 Polling 과 차이가 없으며 지정한 시간이 초과되어 끊기면 다시 요청하는 말그대로 긴 – Polling 이다.)


클라이언트는 Long polling 을 해주고, 그 시간 동안 서버는 서버에서 발생한 이벤트 정보 등도 같이 끼워넣어줌으로써 이른바 "HTTP 푸시" 를 흉내내게끔 한다.


COMET 모델에서 서버는 비동기 방식을 취하고, 클라이언트가 다양한 테크닉으로 푸시를 받을 수 있는 구조를 취하는 경우가 일반적이다.


이에 Javascript 레벨에서 사용되는 테크닉으로, Hidden Iframe 영역(Forever frame)을 할당하고 서버측에서 발생하는 이벤트별로 <script> 태그 내에 데이터를 채워넣는 방식 또는 Ajax 의 long polling 방식, 그리고 Active X 를 이용하는 방식을 사용하기도 한다.


COMET 모델에서 서버와 클라이언트가 통신하기 위해 JSONP 라는 방식을 사용하는데, 이는 JSON 규격의 데이터로 통신을 하되 Client 로부터 전달받은 Callback 함수를 호출하게끔 하는 방식이다.


이렇게 되면 브라우저는 Callback 함수로 감싸여진 Json 데이터를 이용해서 <script> 또는 XHR 을 구성하며 해당 리소스를 읽고 수행하게 된다.


이처럼 COMET 모델을 이용하면 서버에서 발생한 이벤트를 Client 의 추가 요청없이 송신할 수 있다. 



현재는 Websocket 및 HTML5에서 표준화된 Server-sent Event 와 연관되어 발전해가고 있다.






 AngularJS에서의 핵심 개념인 $scope란 양방향 데이터 바인딩의 핵심이자, 뷰와 컨트롤러를 연결하는 개념이다. 

$scope 는 HTML과 Javascript 를 연결해주는 구조로, 이는 단순한 Javascript 객체에 불과하지만, AngularJS 엔진에 의해 이 객체는 연결된 DOM 요소에서 Data와 Function이 사용되는 공간이라고 할 수 있다.

$scope 는 Javascript Object 이며 view 와 controller 사이를 연결할 수 있도록 몇가지 attribute 들을 포함하고 있다.


모든 AngularJS 로 제작된 Application 은 $rootScope 를 갖고 있으며 이는 ng-app 태그가 포함된 HTML 문서 최상단에 위치하는 $scope 가 된다.

따라서 다른 모든 $scope 들은 글로벌하게 정의되는 $rootScope를 상속하는 계층적인 구조를 이루고 있다. 즉, $rootScope 는 많은 child scope 들을 갖고 있다.

$rootScope 만이 AngularJS App 전체에서 유일한 Global Scope 로 처리가 되기 때문에 다른 AngularJS 의 Feature 들, 가령 Service 나 Factory 등이 $rootScope 를 이용하는 경우가 종종 있다.




위의 그림과 같이 하나의 HTML 에서 여러개의 ng-controller 를 정의할 경우 계층적으로 $scope 를 갖게 되고 이는 자연스럽게 계층 구조를 갖게 된다.


$scope는 뷰와 컨트롤러를 이어주는 다리이면서 연결된 DOM에서 양방향 데이터 바인딩을 처리해준다. 또한 이벤트를 처리할 수도 있다.

이 때, AngularJS 의 각 Controller는 하나의 컨트롤러에 하나의 $scope만을 갖게 된다.

그렇기 때문에 만약 독립된 $scope간 참조가 필요하다면 컨트롤러 간 데이터를 공유해야할 경우에는 Service를 이용한다.


AngularJS 의 실 구현은 대부분 ng-controller 로 명시되는 Controller 에서 수행되는 경우가 많기 때문에, 현재 다루고 있는 $scope 를 이해하고 있는건 매우 중요하다.


다음은 Scope 의 라이프사이클을 설명한다.


$scope 의 LifeCycle


1. 브라우저가 Javascript 를 로딩할 때, AngularJS 의 $injector 를 통해 정의된 Application(ng-app) 에 scope 를 할당한다.


2. 링킹 과정에서 $scope 하위에 $watch 오브젝트들이 등록된다.


3. 연결된 model 의 변화가 감지되면 $scope.$apply() 메서드가 AngularJS 내부에서 호출된다. 작업은 $http, $timeout, $interval 모듈을 이용해서 비동기적으로 이루어진다.


4. 변화가 감지되면 AngularJs 는 $digest cycle 을 rootScope 에서 수행한다. 변경 내용은 모든 child Scope 로 전파되며 당연히 각 $scope 에 정의된 $watch 가 이벤트를 전달받는다.


5. $scope 혹은 child Scope 가 더이상 필요가 없어지면 scope.$destroy 가 이루어지며 root Scope 에 의한 전파가 중단된다. 중단된 $scope 는 향후 가비지 컬렉팅된다.



참조 : https://docs.angularjs.org/guide/scope




최근에 AngularJS 를 이용해서 작업을 하던 도중 알게된 작은 버그이다.

(참고로 버전은 AngularJS 1 버전이고, 크롬에서 발생한 버그이다.)


문제가 된 HTML 은 다음과 같다.



<button class="button btn-danger" ng-click="deleteElement($index)">
<span class="glyphicon glyphicon-remove" aria-hidden="true">Delete</span></button>


위의 코드는 단순히 버튼을 클릭하면 해당 Element 에 대한 정보를 이용해서 ng-click 이벤트를 발생시키는 HTML 코드이다.


이 코드는 아주 잘 작동하지만, HTML 에서 엔터키 입력 시, 아주 사소한 버그를 발견할 수 있었다.


위의 코드에서 같은 scope 로 묶인 Text Box 에서 엔터를 입력하자마자 맨 위 레코드가 삭제되는 버그를 발견하였다.


정확히는 Enter Key 를 입력받아 ng-click 이벤트가 작동한 것이다. 다만, 명시적으로 ng-click 이 호출되지 않은 탓에 Parameter인 $index 는 전달되지 않은듯 하다.


문제의 원인은 <button> 태그에 있었는데, 기본적으로 button 태그의 type 은 "submit" 형이기 때문에, 엔터키 입력에 반응할 수 있다.

(이는 브라우저 별로도 다소 다른 듯 하다.)


문제 해결을 위해서는 다음과 같이 명시적으로 타입을 지정해주면 간단히 해결된다.



<button type="button" class="button btn-danger" ng-click="deleteElement($index)">
<span class="glyphicon glyphicon-remove" aria-hidden="true">Delete</span></button>


혹은 어느정도 규모의 AngularJS 프로젝트를 운용중이라면 directive 로 묶어서 처리하는 것도 좋은 방법이다.



(참고자료 : http://www.jomendez.com/2015/02/16/prevent-ng-click-action-hitting-enter/)



 Ajax 라는 개념은 웹에 데이터를 갱신 시, 브라우저 새로고침 없이 서버로부터 데이터를 받는 게 좋다는 생각에서 출발하였다.


 Ajax 는 Asynchronous Javascript And XML 의 약어로 데이터를 이동하고 화면을 구성하는데 있어서 네트워크 Delay 에 따른 데이터의 구성과 View 의 표현을 비동기방식으로 처리하는 메커니즘이다.

(비동기에 대한 내용은 다음을 참고해보면 좋을듯하다.

http://jins-dev.tistory.com/entry/%EB%8F%99%EA%B8%B0Synchronous-%EC%9E%91%EC%97%85%EA%B3%BC-%EB%B9%84%EB%8F%99%EA%B8%B0Asynchronous-%EC%9E%91%EC%97%85-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EB%B8%94%EB%9D%BDBlocking-%EA%B3%BC-%EB%84%8C%EB%B8%94%EB%9D%BDNonBlocking-%EC%9D%98-%EA%B0%9C%EB%85%90?category=760150)

쉽게 말하자면 웹화면을 데이터를 받은 이후에 추가로 갱신하지 않고 서버와 통신하는 방식이다.




Ajax 메커니즘이 적용되지 않은 초기, 인터넷 사용자가 많지 않던 시절의 웹 사이트 구성 로직은 다음과 같다. (Old School)


(1) 웹사이트를 구성하는 Image 를 가져온다.


(2) 웹사이트의 중요 Data 들을 서버에서 가져온다.


(3) 웹사이트 표현을 위한 Javascript 를 가져온다.


(4) 리소스(Image, Data)가 모두 전송되면 데이터를 HTML 뷰에 뿌려준다.


가장 단순하면서도 명확한 기존의 방식은, 네트워크 딜레이를 사용자가 직접 맞부딪친다는 맹점을 갖고 있다. 

또한, 초창기의 SC 모델은 UI 의 표현 역시 서버에서 처리했기 때문에 모든 UI 의 갱신이 요청을 수반하며, 네트워크의 낭비라는 결과도 초래한다.

이 네트워크의 낭비는 사용자의 요구에 응답하는 상호작용에 있어서 큰 장애물이 될 수 있다.

웹이 서비스 상품으로 진화하면서, UI(User Interaction) 와 UX(User Experience) 가 중요해지면서, 서비스는 AJAX 메커니즘을 도입하게 된다.


Ajax 메커니즘을 적용시킨 형태의 구성로직이다. (New School)


(1) 웹사이트를 구성하는 Image 를 요청한다. (XMLHttpRequest)


(2) 웹사이트의 중요 Data 들을 서버에 요청한다. (XMLHttpRequest)


(3) Image와 Data를 제외한 HTML 뷰를 전부 그려준다.


(4) Data 와 Image 를 받는대로 필요한 만큼만 클라이언트에서 사용하고 저장한다. (HttpResponse + Javascript)


AJAX 메커니즘을 도입함으로써, 서버쪽에서 처리하던 UI를 위한 데이터 처리를 클라이언트에서 담당하게 되면서 네트워크 비용의 절감 및 비동기 통신의 이점으로 Delay 없는 화면 전환도 가능하게 된다.


다만, 초창기의 브라우저들은 Ajax 를 지원하지 않았고, 클라이언트 중심의 리소스 호출이 이루어지다보니 보안상의 몇몇 이슈가 있다는 단점이 있다.

개인적으로 생각하는 단점으로는... 무엇보다 디버깅이 쉽지가 않다.


사용자 입장에서는 굉장히 데이터를 빠르게 가져오는 것처럼 느껴지지만 동적으로 화면을 구성하기 때문에 구현은 복잡해진다. 

서버로 데이터를 요청하고 응답을 가져오는 동안 웹은 화면의 구성 등 다른 업무를 처리할 수 있으며 응답이 도착하면 콜백으로 이후의 작업을 하기 때문에 화면 전체의 갱신이 불필요하다.


Ajax 를 사용하는데 있어 주로 사용되는 데이터 포맷은 CSV, JSON, XML 형식이 있다.(XML 만 사용하는 것이 아니다.) 

GET, POST, DELETE, UPDATE 등 일반적인 HTTP 요청을 이용해 CRUD 를 처리하며 REST API 와 궁합이 잘맞는다. 


Ajax 는 방식 이지 프레임워크나 라이브러리가 아니기 때문에 구현은 여러가지가 있을 수 있다. 

Xmlhttp 통신을 이용한 Vanila Javascript 로 구현도 가능하지만, 일반적으로 angular 나 jquery 등의 framework에서 구현된 형태의 Ajax 를 이용한다.


Ajax 를 이용한 내부 동작은 위에 언급한 정해진 데이터 포맷 형태로 요청을 하고 나서 들어오는 응답에 대한 처리를 event loop 에서 받아 처리하는 방식이다. 




 에버그린 브라우저(Evergreen Browser), 자동적으로 브라우저가 사용자에 대한 별도의 재설치를 요구하지 않고도 업데이트 가 가능한 브라우저를 말한다


 초기 웹브라우저들은 Evergreen 방식을 사용하지 않으나, Web 기술의 발전에 따라 사용자의 편의성과 업데이트를 위해 설계된 방식이다.

Chrome은 대표적인 에버그린 브라우저이며, Firefox나 근래의 IE와 같은 많은 주요 브라우저들이 Evergreen 방식의 업데이트로 브라우저를 제공하고 있다.


 많은 개발자들이 인지하기 힘든 빠른 업데이트 정책에 대해 개발하는 데 있어서 호환의 어려움을 얘기하지만, 많은 브라우저들이 하위 호환성에 대한 고려는 충분히 하고 있기 때문에 우려할 수준은 아닌 것으로 보인다.

 

 웹 개발자들이 개발을 염두해둘 때, Javascript 신기술을 도입하는 데 있어서 체크해야할 사항이 되기도 한다. 

특히 ES6 와 같은 최신 Javascript 가 내장된 브라우저를 찾는 일을 하게 되는데, 주요 에버그린브라우저에서 사용할 목적이라면 브라우저가 알아서 Javascript 를 지원하는 업데이트를 해주기 때문에 이는 큰 걱정거리가 안된다.


+ Recent posts