본문 바로가기
JS

[jQuery] jQuery 완전정복 스터디(1)

by 나나 (nykim) 2018. 6. 10.
jQuery 완전정복 스터디(1)

자바스크립트 DOM과 jQuery 소개

자바스크립트 DOM과 jQuery의 관계

자바스크립트는 크게 4가지로 구성되어 있습니다.

  • 자바스크립트 Core 문법: 기본 문법과 구조, 데이터 타입, 조건문, 반복문, 함수, 클래스 등
  • 자바스크립트 Core 라이브러리: setInterval, String, Date, Math, Array, 기타 전역함수 등
  • 자바스크립트 BOM(Browser Object Model): 브라우저와 관련된 Window, Navigator, Location, History, Document 객체 등
  • 자바스크립트 DOM(Document Object Model): 노드, 스타일, 속성, 이벤트 등을 다룰 수 있는 기능

jQuery는 자바스크립트 DOM 요소를 좀 더 쉽게 사용할 수 있도록 도와주는 라이브러리입니다. 예를 들면, 글자 색을 변경하는 스크립트는 다음과 같습니다.

/*Javascript*/
var menu = document.getElementById("menu");
var liList = menu.getelementByTagName("li");

for(var i=0; i<liList.length; i++){
	var li=liList[i];
	li.style.color = "#f00";
}
/*jQuery*/
$("#menu li").css("color", "#ff0");

jQuery 코드 내부는 자바스크립트 코어 문법과 자바스크립트 DOM으로 가득 차 있습니다. 변수 선언이나 반복문은 JS 기본 문법이고, getelementByID나 .style 등은 DOM 기능이죠.
이처럼 jQuery는 특정 기능을 하는 처리 코드를 포장만 하고 있을 뿐, 노드를 찾는다거나 스타일을 변경하는 중요한 작업은 모두 jQuery 내부의 자바스크립트 DOM이 처리합니다.


DOM 소개와 구조

1) DOM이란?

DOM은 웹 화면에 보이는 요소를 조작하기 위한 기능으로 가득찬 라이브러리 덩어리입니다.
DOM에서 제공하는 일반적인 기능은 여러 개의 DOM 객체에 나누어 구성됩니다. 이는 자바스크립트 코어 라이브러리에서 문자열 기능은 String 객체에, 배열 기능은 Array 객체에 들어있는 것과 비슷합니다.

2) W3C DOM vs. 브라우저 DOM

DOM은 정의 부분(명세서)구현 부분으로 나뉩니다. 정의 부분인 명세서에는 웹 페이지 문서를 조작할 때 지켜야할 규칙이 명시되어 있을 뿐, 실제 동작하는 구현 코드는 없습니다. 이 규약에 맞춰 각 브라우저 업체는 저마다의 기술력으로 실제 동작하는 DOM 기능을 구현합니다.
예를 들어 이벤트를 등록하는 기능은 반드시 addEventListener()라는 메서드에 구현되어야 하며, 이때 첫 번째 매개변수로는 이벤트 리스너로 사용할 함수가 와야합니다. 이게 W3C가 정한 정의입니다. 이 규칙대로 이벤트 기능을 구현한 경우 웹 표준을 지원하는 브라우저라고 합니다. 한편 IE7은 addEventListener() 대신 attachEvent()로 이벤트를 등록하기 때문에 규칙에 맞지 않고, 따라서 비 웹표준 브라우저에 해당합니다.


DOM과 HTML 페이지와의 관계

1) DOM과 HTML페이지와의 관계

브라우저가 웹 페이지를 처리하는 과정을 요약하면 다음과 같습니다.

  1. 웹페이지 읽기

    먼저 브라우저는 HTML 페이지를 읽습니다.

  2. 파싱 단계

    parsing 단계를 거쳐 웹 페이지 내용을 해석합니다. 마크업 태그와 1:1 매칭하는 DOM 클래스의 객체를 생성하는 단계입니다.

  3. 출력

    생성한 DOM 객체를 가지고 웹페이지 화면을 그려냅니다.

여기서 2단계를 좀더 자세히 살펴보면, 웹브라우저가 <div>를 보면 HTMLDivElement라는 클래스의 인스턴스(객체)를 생성하고, <img> 태그를 보면 HTMLImageElement 클래스의 인스턴스를 생성한다는 의미입니다.
결국 우리가 작성하는 마크업은 웹브라우저에게 '이렇게 DOM 객체를 만들어줘~'라고 설명하는 설계도 같은 거죠. 브라우저는 마크업 태그를 보고 'ㅇㅋ'하고 해당 태그와 1:1 매칭되는 DOM 클래스의 객체를 생성하는 거고요. 이제 우리는 이렇게 만들어진 DOM 객체에 접근해 동적인 효과를 줄 수 있습니다.

2) DOM과 노드와의 관계

노드는 HTML 웹페이지 구성 요소 중 가장 작은 단위입니다. 일반 태그 뿐만 아니라 텍스트, 주석(!)까지 모두 노드에 해당합니다.
앞에서도 살펴본 것처럼, 웹브라우저는 노드로 가득찬 웹페이지를 읽어 들어 해석한 뒤 각 노드와 1:1 대응하는 DOM 객체를 생성합니다.

웹브라우저는 가장 먼저 최상위에 해당하는 HTMLDocument 클래스의 객체를 생성합니다. 이후 생성하는 모든 DOM 객체는 HTMLDocument 객체의 자식 객체로 만들어집니다. 즉, 부모-자식 관계를 형성하며 일종의 트리 구조를 갖추게 되죠.


핵심 DOM 객체

앞서 언급했듯이, DOM은 여러 개의 DOM 객체로 구성되어 있고 각자 고유의 기능을 가지고 있습니다.

Node 객체

Node 객체는 노드를 조작하기 위한 가장 기본적인 프로퍼티와 메서드가 정의되어 있는 Node 인터페이스를 구현한 객체입니다. 노드 객체에서 제공하는 기능을 이용하면 노드 타입을 파악하거나, 부모/형제/자식 노드를 알아내 접근하거나, 자식 노드를 추가/삭제하는 것이 가능합니다.
Node 객체는 DOM 객체 가운데 가장 최상위 객체이자 모든 하위 노드 객체들이 상속받는 객체입니다. Node 객체에는 모든 DOM 객체들이 기본으로 가지고 있어야 하는 기능과 속성이 있습니다. 사람으로 따지자면 이름, 성별, 나이 등이 해당됩니다. 이는 자바스크립트 클래스 상속처럼 모든 클래스가 Object()라는 클래스에서 공통적으로 상속받는 것과 비슷합니다.

Element 객체

Element는 노드 중 주석 노드와 텍스트 노드를 제외한 나머지 노드를 가리킵니다.
Element 객체 역시 노드의 한 종류이며 Element 인터페이스를 구현한 객체입니다. 또한 Node 객체의 자식이므로 Node 객체가 가진 기능을 모두 사용할 수 있습니다. Node객체가 가진 기능이 '노드 타입을 알 수 있는 속성, 부모 노드에 접근하는 기능...' 등이었는데요, Element객체는 자신이 가진 기능에다 더해 Node 객체로부터 물려받은 기능을 사용할 수가 있습니다.
Element 객체는 태그 이름이 담긴 프로퍼티와 속성을 알아내고 설정하거나 이벤트츠를 추가/삭제/발생하는 기능 등이 있습니다.

HTMLElement 객체

HTMLElement는 오직 HTML 문서에만 있는 노드를 통합해 부르는 말로, HTML 태그와 동일합니다. HTMLElement 객체에는 Element 객체의 기능 외에도 HTML 태그에서만 쓸 수 있는 공통적인 속성과 기능이 포함되어 있습니다. id나 className 프토퍼티가 그 예입니다. 마찬가지로 Node 객체 + Element 객체로부터 기능을 상속받아 사용할 수 있습니다.
한편, HTMLElement 객채는 HTMLDivElement, HTMLImageElement, HTMLBodyElement와 같은 객체의 부모 객체이기도 합니다.

Document 객체

Document 객체 역시 Node 객체의 하위 객체이며, HTML(XMl) 문서의 루트 객체입니다. 노드를 생성하는 팩토리 기능, 특정 노드를 찾는 기능, 이벤트를 발생시키고 등록시키는 이벤트 모델 기능 등을 갖춘 중요한 객체입니다.

HTMLDocument 객체

HTMLDocument 객체는 HTML 문서 전용 Document 객체입니다. body와 같은 HTML 문서 전용 프로퍼티와 메서드가 포함되어 있습니다. HTMLDocument 객체에는 파싱 단계에서 만들어진 html, head, body 객체를 비롯해 페이지에 작성된 태그와 1:1로 매칭되는 모든 Node 객체를 가지고 있는 객체이기도 합니다.



jQuery의 정체

$()의 의미

jQuery에서 가장 많이 사용하는 기능은 $()입니다. 이건 대체 무엇을 의미할까요?
얘는 그냥 이름이 $인 함수를 호출한 것입니다. 우리가 어떤 함수를 호출할 때 함수이름()과 같이 쓰는 것처럼, 제이쿼리()하고 호출하는 게 됩니다.
jQuery 라이브러리 내부에 이렇게 정의된 것을 확인할 수 있습니다.

window.jQuery = window.$ = jQuery;

결국 $는 jQuery를 줄여 말하는 것입니다. 따라서 다음 두 코드는 동일합니다.

$("div").css("color", "#f00");
jQuery("div").css("color","#f00");

위 코드에서 $("div")div를 매개변수 값으로 $()함수를 호출한 것입니다.

$() 함수의 리턴값

$("div").css("color", "#f00");

위 코드에서 점(.)은 무슨 역할을 할까요? 특정 객체에서 제공하는 기능에 접근할 때 쓰는 접근 연산자입니다. css()는 이 특정 객체에서 제공하는 기능(메서드) 중 하나입니다. 즉, $("div") 함수의 리턴값인 특정 객체가 바로 jQuery 객체라는 것입니다.
jQuery API를 살펴보면, jQuery 함수의 리턴값은 jQuery입니다. 결국 코드를 해석하자면, $("div") 함수 호출 후 리턴된 jQuery 객체 내부의 css() 메서드를 .을 이용해 접근한 것입니다. 이는 달리 말하면 jQuery 객체 내부에 없는 메서드는 호출할 수 없다는 뜻이 됩니다.

예제를 봅시다. 다음 두 구문을 실행하면 어떤 결과가 나올까요?

//1번
("div").css("font-size").addClass("on");

//2번
$("div").css("font-size", 12).addClass("on");

1번은 에러가 나고 2번은 정상 동작합니다. 이유는 jQuery API에서 찾아볼 수 있습니다. css() 메서드는 매개변수가 하나이면, CSS 프로퍼티를 문자열로 리턴합니다. 그런데 이 문자열(String 클래스)에는 addClass() 메서드가 존재하지 않죠! 따라서 에러가 납니다. 한편, 매개변수가 두 개면 jQuery 객체를 리턴하므로 jQuery의 기능인 addClass() 메서드가 정상 호출됩니다.

jQuery의 정체

jQuery는 자바스크립트의 prototype이라는 클래스 제작 문법으로 만들어졌습니다. jQuery를 prototype으로 간단히 표현하면 다음과 같습니다.

function jQuery(){
}
jQuery.prototype.css=function(){}
jQuery.prototype.on=function(){}
jQuery.prototype.addClass=function(){}
jQuery.prototype.cick=function(){}
...

함수를 사용하려면 호출해야하는 것처럼 클래스를 사용하려면 해당 클래스의 인스턴스를 생성해줘야 합니다.

var 인스턴스 이름 = new 클래스 이름();

읭?! 그런데 우린 jQuery 인스턴스를 생성한 적이 없는데요?? 그럼에도 불구하고 jQuery를 쓸 수 있는 건 $()에서 jQuery의 인스턴스를 제공해주기 때문입니다.

function $(){
   ......
   return new jQuery()
}

이렇게 만들어진 jQuery 객체 내부에는 노드, 스타일 속성, 이벤트 등을 쉽게 다룰 수 있는 여러 기능들이 들어있습니다.

그럼 이제 jQuery가 어떤 원리로 동작하는지 알아볼 차례입니다. 아래 예제를 봅시다.

<body>
	<div>myDiv-1</div>
	<div>myDiv-2</div>
	<div>myDiv-3</div>
	<p>myP-1</p>
	<p>myP-2</p>
	<p>myP-3</p>
</body>

var $divs = $("div");
$divs.css("border", "1px solid #333");

위 구문이 실행되면 jQuery 인스턴스는 몇 개 만들어질까요? 답은 1개입니다. var $divs = $("div")로 만들어진 jQuery 객체 내부에는 css(), clone(), offset() 등등의 다양한 메서드 뿐만 아니라 $("div")의 결과 정보인 자바스크립트 HTMLDivElement DOM객체가 포함되어 있습니다. jQuery 인스턴스는 하나지만 이 내부에는 $()함수를 활용해 찾은 3개의 div 태그 객체(정확히는 HTMLDivElement 객체)를 가지게 됩니다.
이때 css() 메서드를 호출하면 jQuery 내부의 프로퍼티로 갖고 있는 div 태그 객체를 for문 등의 반복문을 이용해 노드에 하나씩 접근해 스타일을 변경합니다. 바로 다음처럼요!

function css(){
   ....
   for (var i=0; i<nodes.length i++){
       nodes[i].style.color="#333333";
   }
   ....
}

실제 스타일을 변경하는 기능은 jQuery가 아닌 jQuery 객체 내부에 있는 자바스크립트 DOM이 처리하는 것이죠. jQuery를 사용하는 우리는 루프를 돌리지 않지만 jQuery 내부에는 루프를 도는 로직이 들어 있기 때문에 가급적 jQuery 기능 호출을 줄이는 것을 추천합니다.

//비효율적
$("#traget").show();
$("#target").css("color","red");
$("#traget").fadeOut();
//최적화 (노드 캐싱)
var $target = $("#target");
$target.show();
$target.css("color","red");
$target.fadeOut();

마지막으로 배운 걸 정리해보죠! $("#header").css("background", "pink"); 구문은 #header를 매개변수 값으로 jQuery 함수를 호출한 것입니다. $() 함수는 #header 노드를 감싼 jQuery 객체를 리턴해주고, 접근 연산자를 이용해 jQuery 객체에서 제공하는 스타일 변경 메서드인 css() 메서드를 호출합니다.
참고로 변수 앞에 $를 붙이는 경우가 있는데요, 이는 jQuery 인스턴스를 담는 변수임을 나타내기 위함입니다. 따라서 $가 붙은 변수를 보면, jQuery 인스턴스가 담긴 변수니 jQuery 기능을 사용할 수 있다고 인지하면 됩니다.