1. 웹 브라우저 스크립팅
자바스크립트 같은 스크립트 언어로 브라우저에서 실행될 수 있는 프로그램을 작성하는 작업을 웹 브라우저 스크립팅이라 한다.
웹 브라우저 스크립팅 환경
클라이언트 측 자바스크립트 프로그램 환경에서는 웹 브라우저, 웹 브라우저에 출력되는 웹 문서, 그리고 문서를 구성하는 HTML 요소 등 모든 것을 객체로 표현할 수 있다.
브라우저를 나타내는 객체 : Window
HTML 문서 및 요소를 나타내는 객체 : HTML DOM(Document Object Model)
브라우저 객체 Window
웹 브라우저의 기본 기능은 HTML 문서를 윈도우에 출력하는 것이다.
자바스크립트에서 Document 는 HTML 문서를 나타내고 HTML 문서를 출력하는데 사용하는 웹 브라우저 또는 프레임을 나타내는 객체를 Window 라고 한다.
Window 객체는 웹 페이지를 표현하는 객체구조상 최상단에 존재하는 루트 객체다.
Window 객체를 시작으로 웹 페이지를 표현하는 계층구조상의 어떤 객체에도 접근할 수 있다.
윈도우와 프레임은 웹 페이지가 실행될 수 있는 동일한 실행 환경을 제공한다. 그래서 하나의 웹 페이지는 윈도우에서 실행될수도 있고, 프레임 안에서 실행될 수도 있다. 이렇게 웹 페이지의 실행 환경으로서의 윈도우나 프레임을 나타내는 객체가 바로 Window다.
하나의 페이지에는 여러개의 프레임이 포함될 수 있다. 이것은 Window 객체 간에도 부모/자식 계층 관계가 구성될 수 있음을 의미한다.
부모/자식 계층 관계를 나타내기 위해 Window 객체의 속성으로 frames, parent, self, top 등이 정의되어 있다.
프레임에 로딩된 웹 페이지에서 다시 프레임을 사용하고 있는 구조라면 현재 페이지를 호스팅 하고 있는 윈도우 self와 최상위 윈도우 객체를 나타내는 top은 서로 다른 값을 가지고 있을 것이다.
HTML DOM 구조
웹 페이지의 HTML 문서의 구조를 객체로 표현하기 위한 계층구조로서 HTML문서를 나타내는 최상위 객체가 Document다.
각 HTML 요소 각각에 대응되는 자바스크립트 버전의 객체가 정의되어 있다.
예를 들어, 자바스크립트 객체 가운데 Button 이라는 것이 있는데, 이것은 HTML 요소인 <Button>에 해당하는 객체다.
기본적으로 HTML 요소에서 정의되어 있는 id, value, onclick 같은 어트리뷰트는 Button 객체에서 그에 대응되는 속성으로 정의되어 있다.
Window 객체는 웹 브라우저를 나타내는 객체지만 HTML DOM 구조는 새로운 HTML 문서를 표현하는 객체이기 때문에 다른 문서가 로딩될 때 마다 새롭게 구성된다.
<table>
<tbody>
<tr>
<td>Hello</td>
<td>World</td>
</tr>
<tr>
<td>Hello</td>
<td>World</td>
</tr>
</tbody>
</table>
위와 같은 페이지 로딩되고 나면 Document 객체에 아래와 같은 테이블 구조를 나타내는 계층구조가 포함될 것이다.
자바스크립트를 이용하면 HTML 요소를 나타내는 DOM 객체에 접근해서 요소가 가지고 있는 값, 텍스트 등 요소의 모든 정보를 얻을 수 있다. 또한 DOM의 트리 구조를 따라서 노드 객체에 순차적으로 접근할 수도 있다. 특정 노드 객체를 검색하거나 해당 객체를 삭제하거나, 또는 새로운 객체를 추가하는 작업 등을 할 수도 있다.
자바스크립트와 HTML DOM 구조를 이용하면 동적으로 트리 노드를 검색, 추가, 제거, 변경할 수 있다.
HTML DOM 객체는 완전한 자바스크립트 객체로서 자바스크립트 객체의 모든 특징을 그대로 따른다.
이는 웹 페이지가 로딩된 후 동적으로 사용자 화면을 변경할 수 있다는 의미이고, 사용자와 인터랙티브한 자바스크립트 프로그램을 만들 수 있다는 의미이다.
HTML DOM 객체의 속성값이 변경되면 HTML 요소의 값에도 바로 반영된다. 반대로 HTML 요소 값이 바뀌어도 변경 내용은 HTML DOM 객체에 바로 반영된다.
HTML DOM의 트리구조를 검색하고 추가, 제거, 변경할 수 있는 자바스크립트 API의 예는 다음과 같다.
구분 |
API |
설명 |
순회 |
childNodes, firstchild, lastChild, nextSibling, previousSibling 속성 |
노드를 순차적으로 순회할 수 있다. |
선택 |
getElementByid(), getElementsByTagName(), getElements() |
노드를 선택할 수 있다. |
문서 수정 |
appendChild(), removeChild(), |
Document 하위 객체를 추가, 제거 할 수 있다. |
내용 추가 |
innerHTML 속성 |
멤버의 내부 HTML 내용을 조회, 설정할 수 있다. |
자바스크립트에서 제공하는 API를 그대로 사용하려면 크로스 브라우징을 고려한 코드를 추가해야 한다.
그러나 그렇게 하기가 쉽지는 않다. 따라서 jQuery 처럼 크로스 브라우징을 보장하는 라이브러리를 사용하는 편이 낫다.
또한 사용법에 있어서도 HTML DOM에서 제공하는 원래의 API를 이용하기 보다는 jQuery를 이용하는 편이 훨씬 더 간편하고 코드의 양 또한 획기적으로 줄일 수 있다.
HTML DOM에서 제공하는 API를 사용하기 보다는 jQuery 같은 라이브러리를 사용하는 것이 편리하다.
DOM 요소의 이벤트 핸들러 속성
이벤트 핸들러는 함수로서, 이벤트가 발생하기 전까지 해당 함수는 실행되지 않는다.
마우스 이벤트
이벤트 |
핸들러 속성 |
설명 |
click |
onclick |
사용자가 요소를 클릭할때 발생한다. |
dbclick |
ondbclick |
요소를 더블클릭할때 발생한다. |
mousedown |
onmousedown |
요소 위에서 마우스 버튼을 누르면 발생한다. |
mousemove |
onmousemove |
요소 위에서 마우스 버튼을 움직이면 발생한다. |
mouseover |
onmouseover |
요소 위에 마우스 포인터를 두면 발생한다. |
mouseout |
onmouseout |
요소 밖으로 마우스 포인터를 움직이면 발생한다. |
mouseup |
onmouseup |
요소에서 누른 마우스를 놓으면 발생한다. |
키보드 이벤트
이벤트 |
핸들러 속성 |
설명 |
keydown |
onkeydown |
사용자가 키보드 키를 누를 때 발생한다. |
keypress |
onkeypress |
사용자가 키보드 키를 누러거나 키를 누른 채로 있다가 놓으면 keydown과 keyup 사이에 발생한다. |
keyup |
onkeyup |
사용자가 누른 키보드 키를 놓으면 발생한다. |
키 입력을 하게 되면 이 세 타입의 이벤트가 모두 발생되는데, 이때 이벤트는 keydown, keypress, keyup 순으로 발생한다.
키를 계속 누른 상태로 있다가 놓으면 keydown과 keyup 사이에서 반복적으로 계속 keypress 이벤트가 발생한다.
그러나 이것은 운영체제나 브라우저에 따라 달리 구현될 수 있다.
프레임/객체 이벤트
이벤트 |
핸들러 속성 |
설명 |
load |
onload |
HTML 문서, 프레임셋, 객체의 로딩이 완료되면 발생한다. |
resize |
onresize |
문서의 뷰가 리사이징되면 발생한다. |
scroll |
onscroll |
문서의 뷰가 스크롤되면 발생한다. |
unload |
onunload |
문서가 윈도우나 프레임에서 제거되면 발생한다. (<body>,<frameset>) |
폼 이벤트 (<input>,<select>,<textarea>,<form> 등)
이벤트 |
핸들러 속성 |
설명 |
blur |
onblur |
폼 요소가 포커스를 잃으면 발생한다. |
change |
onchange |
폼 요소의 내용, 선택, 체크박스 상태가 변경되면 발생한다. (<input>,<select>,<textarea>) |
focus |
onfocus |
폼 요소가 포커스를 받으면 발생한다. |
select |
onselect |
사용자가 텍스트를 선택하면 발생한다. (<input>,<textarea>) |
submit |
onsubmit |
폼이 서버로 제출될 때 발생한다. |
자바스크립트는 특정 이벤트가 발생하면 해당 이벤트를 처리하거나 필요하다면 서버 측과의 통신을 통해 데이터를 가져오는 등 브라우저 스크립팅 환경을 구성하는 요소를 전체적으로 통합하는 작업을 한다.
이벤트 기반 프로그래밍
브라우저 스크립팅 환경을 구성하는 요소를 이용하면 다음과 같은 이벤트 기반의 브라우저 스크립팅이 가능해진다.
HTML 문서가 브라우저에 로딩되면 위 그림처럼 브라우저에는 HTML의 시각적인 표현이 출력되고 내부적으로 브라우저와 HTML 문서를 나타내는 객체가 계층적인 모습으로 구성된다.
이후 브라우저를 통해 직접 이벤트를 발생시키거나 프로그램적으로 이벤트를 발생시킬 수 있다. 개발자는 자바스크립트를 이용해 관심 있는 이벤트에 등록하고 나중에 이벤트가 발생하면 그 이벤트에 맞게 실행될 로직을 구현할 수 있다.
DOM 요소의 관심 있는 이벤트별로 핸들러를 등록해서 원하는 작업을 할 수 있는데,
이런 식으로 프로그래밍 하는 방법을 이벤트 기반 프로그래밍 모델(event-driven programming model) 이라고 한다.
웹 페이지 구성 요소
HTML 웹 페이지를 구성하는 요소의 구체적인 모습을 보면 다음과 같다.
내용/구조(HTML)
표현/스타일(CSS)
동작/프로그래밍(javascript)
내용 (구조)
HTML은 페이지의 내용 및 구조를 정의하는 태그로 구성된다. 태그는 내용의 의미를 알 수 있게 이름을 가진다. 사용할 태그를 선택할 때 가급적 의미(semantic)에 맞는 태그를 선택하도록 한다. HTML5 에서는 HTML 태그가 갖는 의미가 더욱더 중요해졌다.
표현 (스타일)
HTML 태그로 만드는 내용은 요소의 시각적인 모습을 정희하는 것과는 다르다. 요소의 시각적인 모습, 즉 스타일을 정의할 때는
CSS (Cascading Style Sheets)를 이용한다.
동작
웹 페이지의 프로그램 코드는 주로 <script> 태그를 사용해서 포함시킨다. 이 태그를 이용해 외부 파일에 있는 스크립트 코드를 포함시킬 수도 있다. 자바스크립트와 HTML DOM(Document Object Model)을 사용할수 있는 영역이다.
웹 페이지가 브라우저에 로딩되면 페이지르 구성하는 내용/구조(HTML)와 표현/스타일(CSS)를 나타내는 객체가 계층적으로 구성된다.
페이지 구성요소가 DOM 게층구조로 모두 로딩이 완료되고 나면 자바스크립트로 이러한 구성 요소에 접근할 수 있다.
자바스크립트 코드 구성
외부 파일 사용
하나의 웹 페이지에 세가지 구성요소가 모두 포함되는 구조로 개발할 수도 있지만 서로 물리적인 파일이 분리되어 존재하고 링크만 HTML 웹페이지에 추가하도록 구성하는 것이 좋다.
특히 HTML, CSS 작업은 디자이너가 주로 하고 자바스크립트 코딩은 개발자가 담당하는 협업 체계가 필요한 개발 환경에서는 이런 구조가 더욱더 적합하다.
HTML 태그의 style 어트리뷰트를 사용할수도 있고 <font> 같은 HTML 태그를 사용해서 스타일 표현을 정의할 수 있지만 HTML, CSS, 자바스크립트를 최대한 원래의 역할대로 분리해서 사용하는 것이 바람직 하다.
HTML, CSS, Javascript를 최대한 원래의 역할대로 분리해서 사용하면 추후 유지/보수에도 많은 도움이 된다.
<script/> 태그 이용
<html>
<head>
<script type="text/javascript" src="demo.js"></script>
<script>
var a = 1;
a++;
</script>
</head>
<body>
</body>
</html>
위와 같이 두 가지 방법으로 자바스크립트 코드를 페이지에 포함 시키도 있다.
<script/> 의 src 어트리뷰트에 자바스크립트 코드가 담긴 외부 파일에 대한 경로를 지정해주는 방법과 직접 자바스크립트 코드를 페이지에 포함시키는 방법이 있다.
첫번째 경우에 <script> 태그에 type 어트리뷰트를 지정했는데, 이것은 XHTML 1.0에서 필요한 요소다. type 어트리뷰트가 없더라도 작동하는데 문제는 없다.
이벤트 핸들러 어트리뷰트 값
HTML 요소의 태그에서 onclick 같은 어트리뷰트의 값으로 이벤트를 핸들링 하는 코드를 포함시킬 수 있다.
이때 세미콜론(;)을 이용해 여러문장을 추가할 수 있다.
<input type="checkbox" name="options" value="true" onclick="this.value=this.checked; alert(this.value);"/>
url 값에 javascript: 구문 지정
페이지에 자바스크립트 코드를 포함시키는 마지막 방법이다.
URL이 들어갈 자리에 javascript:로 시작하는 자바스크립트 구문을 넣을 수 있다.
<a href="javascript:var e='http://jusungpark.tistory.com'; e=prompt('URL 입력',e); window.open(e); return;">새창으로</a>
브라우저는 페이지에서 자바스크립트 코드를 찾는 순서대로 실행한다. <script> 코드 블록에 변수를 선언하면 이벤트 핸들러 어트리뷰트의 값으로 포함된 스크립트 문자열에서 해당 변수를 사용할 수 있다.
<html>
<head>
<script>
var a = 100;
</script>
</head>
<body>
<input type="checkbox" name="options" onclick="alert(a);"/>
</body>
</html>
웹 페이지 로딩
웹 페이지에는 하나 이상의 <script> 코드 블록이 포함될 수 있는데, <script> 코드 블록은 웹 페이지의 문서가 파싱되고 로딩되는 절차의 일부로 실행된다.
즉, HTML 파서는 HTML 문서를 파싱하다 <script> 태그를 만나면 자바스크립트 파서에게 파싱을 위임한다.
자바스크립트 파싱이 끝나면 다시 HTML 파싱이 계속된다.
스크립트 파싱 연기
HTML 태그가 모두 파싱되고 나서 스크립트 코드가 실행되기를 바라는 경우 또는 외부 .js 파일의 크기가 커서 파일을 로딩하고 스크립트를 파싱하는데 시간이 오래 걸리고 그동안 HTML 파서는 대기하고 있어야 하는경우에 사용될수 있다.
보통 동적으로 스크립트를 로딩하는 방법을 사용한다.
IE 에서는 defer 라는 어트리뷰트를 사용할수 있다.
<script src="demo_defer.js" defer="defer"></script>
load 이벤트
문서의 HTML이 파싱되고 이미지 같은 외부 콘텐츠의 로딩이 모두 완료되고 나면 브라우저는 load 이벤트를 발생시킨다.
load 이벤트가 발생하면 onload 이벤트 핸들러로 등록된 Window 객체의 함수가 실행된다.
웹페이지의 load 이벤트는 모든 스크립트 코드가 실행되고 HTML이 모두 로딩되고 나서야 발생하기 때문에 onload 이벤트 핸들러에서는 정의된 변수나 함수, 그리고 DOM 구조에 자유롭게 접근해서 HTML 요소를 조작할 수 있다.
load 이벤트가 발생하고 나서부터 자바스크립트 프로그램은 이벤트 기반의 실행 단계로 들어가게 된다.
문제는 웹 문서가 로딩되거나 파싱되는 동안이다. 웹 문서가 로딩되거나 파싱되는 동안 <script> 태그 안에 있는 자바스크립트 코드가 DOM 객체에 접근할수 있을까? 대부분의 브라우저에서는 <script> 태그 이전에 있는 HTML 요소에는 접근하는 것을 허락한다. 따라서 많은 경우 개발자들은 <script> 태그의 코드를 요소 뒤에 둔다. (HTML 파서가 먼저실행 되는것을 보장하여 체감 속도를 향상시키는 역할도 한다)
그러나 이를 가능하게 하는 공식적인 규약은 없다.
유일하게 확실한 것은 load 이벤트가 발생하고 나면 자바스크립트로 모든 HTML 요소에 안전하게 접근할 수 있다는 것이다.
자바스크립트 프로그램은 단일 스레드(single-threaded) 모델을 사용한다. 따라서 문서를 파싱하다가 스크립트를 로딩하고 파싱하면 문서 파싱은 중단된다.
이런 스레드 모델 때문에 이벤트 핸들리으이 경우 하나의 이벤트 핸들러가 실행되면서 동시에 또 다른 이벤트 핸들러가 실행되지는 않는다.
그런데 이미지가 많거나 용량이 큰 이미지가 포함되어 있는 문서를 생각해보자 HTML 파싱은 다 됬는데, 이런 외부 이미지 로딩이 아직 끝나지 않았다면 load 이벤트가 발생하지 않을것이다.
만약 load 이벤트가 발생하지 않더라도 문서를 조작할 수 있는 방법은 없을까?
jQuery를 사용할 수 있다면
$(function(){ ... });
or
$(document).ready(function(){ ... });
사용할 수 없다면
첫 번째 방법은 <script>를 HTML 문서의 제일 나중에 두는 것이다. 이 방법은 앞에서 언급한 대로 표준은 아니다. 다만 대부분의 브라우저에서 지원하기 때문에 가능한 방법이다.
두 번째는 인터넷 익스플로러에서만 사용 가능한 방법으로서 <script> 태그에 defer 어트리뷰트를 사용하는 것이다.
세 번째는 인터넷 익스플로러 9를 비롯한 대부분의 브라우저에서 사용가능한 방법으로서 이러한 브라우저에서는 표준에는 없는 이벤트 DOMContentLoaded를 구현해 놓고 있다.
document.addEventListener("DOMContentLoaded", function (event) {
console.log("DOM fully loaded and parsed");
});
이제 문서가 다 로딩되고 나면 내부적으로는 Window 객체를 루트로 하는 트리 구조가 모두 완성된 상태다.
이런 상태에서 동일한 브라우저의 운도우에 새로운 문서가 로딩되면 원래의 Window 객체에 추가되거나 변경된 멤버는 사라지고 다시 원래의 멤버만 가진 Window 객체가 된다. 즉, 문서가 새롭게 로딩되면 이전의 Window 객체는 그대로 유지되면서도 원래의 상태로 다시 초기화 된다. Window 객체에 대한 참조는 변경되지 않는다.
2. 이벤트 핸들링
브라우저에는 키입력, 마우스 이동 등 다양한 종류의 이벤트가 정의되어 있다. 인터랙티브한 브라우저 프로그램을 만들려면 이벤트 핸들러를 정의하고 시스템의 이벤트에 등록해야한다. 그래야 브라우저가 적절한 때에 호출하게 된다.
이벤트 핸들링
HTML 태그 어트리뷰트로 등록 #1
onclick 값은 어트리 뷰트에 핸들링 코드를 포함시키는 방법.
<input type="checkbox" name="options" value="true" onclick="this.value=this.checked; alert(this.value);"/>
구문을 여러 개 넣어야 한다면 세미콜론(;)으로 구분하여 처리하면 되지만, 이런 식으로 개발을 하면 코드가 웹 페이지의 이곳저곳에 산재하게 되어 코드를 중앙집중형으로 관리하는데 바람직하지 않다.
HTML 태그 어트리뷰트로 등록 #2
function handler(){
this.value=this.checked;
console.log(this.value)
}
</script>
<input type="checkbox" name="options" value="true" onclick="handler();"/>
그나마 실전에서 자주 사용되는 방법이다. 자바스크립트 코드와 HTML 태그를 어느 정도 분리하고는 있으나 아직 완전하지는 않다.
요소 객체의 메서드로 등록
마지막 방법은 HTML 요소에 해당하는 DOM 요소의 속성을 이용해 핸들러를 등록하는 방법이다.
document.getElementById("options").onclick = handler;
document.getElementById("options").onclick = function(){
this.value=this.checked;
console.log(this.value)
};
첫 번째 표현은 onclick 이벤트 핸들러로 이름있는 함수를 사용해 참조를 주입해주었고, 두 전째 표현은 익명 함수를 사용하고 있다.
HTML 코드와 자바스크립트 코드를 완전히 분리하는 방법으로서 이렇게 하면 추후 자바스크립트 코드만 별도의 파일로 저장해서 관리할 수 있다.
jQuery 같은 라이브러리를 사용하면 훨씬 간단한 표현으로 요소를 선택하고 이벤트를 핸들링하게 해줄수 있다.
이벤트 핸들러 반환값
이벤트를 취소시키기 위해 이벤트 핸들러에서는 반환값을 이용한다.
예를 들어, onsubmit 이벤트 핸들러에서는 서버로 폼의 입력값을 전달하기 전에 주로 입력값의 유효성 검사를 수행한다.
유효성 검사가 실패하면 onsubmit 핸들러에서는 false를 반환해서 서버로 입력값을 제출하는 작업을 취소할 수 있다.
일반적으로 웹 브라우저는 특정 이벤트에 대해 기본적으로 정의된 액션을 수행한다. 일부 이벤트의 경우에는 핸들러에서 false를 반환하면 기본적인 액션이 취소 될 수 있다.
이벤트 핸들러에서 명시적으로 값을 반환할 필요는 없다. 값을 반환하지 않으면 기본적인 액션을 수행한다.
웹 이벤트 핸들러의 this
this는 객체가 new 에 의해ㅔ 메모리에 생성될 때 생성된 인스턴스에 대한 참조를 갖게 되지만 이 this는 런타임에 계속 변할 수 있는 속성을 가지고 있다. 이러한 속성 때문에 웹 페이지에서 사용되는 this가 어떤 객체를 가리키는지 이해하기 어려울 때가 있다.
<script>
function clickHandler(obj) {
this.value = "this.value 값 변경?"; // this 는 Window 객체를 가리킨다.
alert(obj.vlaue);
}
</script>
<input type="button" id="button" value="click" onclick="this.value='this id:'+this.id; clickHandler(this);"/>
"변수 스코프" 에서 정리했던 것처럼 자바스크립트의 함수의 변수 스코프는 렉시컬한 특성을 가진다.
즉 함수 내부에서 사용하는 변수는 실행 영역에서 찾는 것이 아니라 정의된 영역에서찾는다.
위 예시처럼 HTML 요소의 어트리뷰트에 할당되는 코드는 해당 코드를 내용으로 정의한 익명 함수를 어트리뷰트에 할당하는 것과 같다.
function () {
this.value = 'this.id:'+this.id;
clickHandler(this);
}
그럼 clickHandler 내부에서 사용하는 this는 어떤 객체를 가리킬까? 이 경우 this는 브라우저 환경의 루트 객체인 Window 객체를 가리킨다.
document.getElementById("option").onclick = function() {
this.value = this.checked;
alert(this.value);
};
// this는 option 객체를 가르킴.
button.onclick = o.mymethod;
o.mymethod = fucntion (){
this.value = "";
}
button.onclick은 o.mymethod가 가리키는 동일한 함수를 가리킨다.
브라우저가 이벤트를 발생시켜 onclick이 가리키는 함수를 mymethod 실행주체가 button 이라면 이때 this는 button객체를 가리키게 된다.
button.onclick = function() {
o.mymethod();
}
위 코드처럼 실행이 된다면 mymethod 실행의 주체가 객체 o 이기 때문에 이때 mymethod 내부의 this는 객체 o를 가리킨다.
참고.
자바스크립트 객체지향프로그래밍.
'Javascript' 카테고리의 다른 글
ES6, ECMAScript 6 의 새로운 것. 자바스크립트2015(javascript 2015) (0) | 2017.06.20 |
---|---|
웹팩(Webpack) 이란, 웹팩 간단 정리 및 리액트(React) 기본 개발환경 세팅. [1] (6) | 2017.06.20 |
자바스크립트(javascript) 객체 응용 (2) | 2017.05.30 |
자바스크립트(javascript) 내장 객체 - JSON 객체 (0) | 2017.05.29 |
자바스크립트(javascript) 내장 객체 - 정규식 객체 (0) | 2017.04.04 |
댓글