제이쿼리 객체(jQuery Object)에 대해 알아보자.

본 스토리는 learn.jquery.com의 Using jQuery Core 중 한 꼭지인 The jQuery Object를 번역한 스토리입니다. 원문은 이곳에서 확인하실 수 있습니다.

제이쿼리 객체

새로운 요소(element)를 만들거나 기존의 요소를 선택할 때 제이쿼리는 요소를 제이쿼리 컬렉션(jQuery collection)이라는 형태로 다룹니다. 제이쿼리를 처음 접하는 개발자는 제이쿼리 컬렉션과 자바스크립트 배열을 혼동합니다. 제이쿼리 컬렉션이 DOM 요소를 인덱스가 0부터 시작하는 배열(역주: 유사 배열)에 저장하고, 몇몇 유명한 배열 함수도 지원하면서 프로퍼티(property)까지 제공해 주기 때문이죠. 하지만 제이쿼리 객체는 생각보다 복잡한 녀석입니다.

DOM 과 DOM 요소

문서 객체 모델(Document Object Model, DOM)은 HTML 문서를 구조화해 표현해줍니다. 수많은 DOM 요소가 모여서 HTML 문서가 만들어지죠. 높은 차원에서 보면, 각각의 DOM 요소는 웹 페이지의 한 “조각”을 차지하고 있다고 볼 수 있습니다. 이렇게 웹 페이지는 문자(text) 등의 노드와 요소 노드가 조합되어 만들어집니다. DOM의 요소 노드는 , , or 와 같은 태그 종류에 따라 설명되고 , , 등과 같은 속성(attribute)을 기준으로 설명되기도 합니다. 이에 관한 더 자세한 설명은 W3C의 DOM 공식 스펙을 확인해 보시길 권합니다.

요소 노드에도 자바스크립트 객체처럼 프로퍼티(property)가 있습니다. 프로퍼티와 메서드가 대표적인 프로퍼티입니다. 자바스크립트로 웹페이지와 상호작용하려면 이런 프로퍼티를 통해서만 가능합니다.

제이쿼리 객체

DOM 요소를 직접 조작하는 건 불편하고 때로는 곤란한 상황을 유발합니다. 제이쿼리는 다양한 메서드를 만들어 이런 문제를 없애려고 했습니다. 제이쿼리 객체와 그 메서드를 사용하면 다음과 같은 이점이 있습니다.

호환성(Compatibility) — 브라우저마다, 또는 브라우저 버전마다 요소 메서드의 구현방식이 다릅니다. 아래는 요소를 참조하는 변수 target의 내부 HTML을 변경하는 코드입니다.

var target = document.getElementById( "target" );target.innerHTML = "<td>Hello <b>World</b>!</td>";

대부분의 경우 이 코드는 잘 작동하겠지만, 거의 모든 인터넷 익스플로러 버전에서 코드는 동작하지 않습니다. (역주: IE9과 그 이전 버전의 IE에선 innerHTML은 테이블 요소에 대해 읽기 전용이기 때문에 값을 쓰는 게 불가능합니다.) 이런 경우는 순수 DOM 메서드를 사용하는 걸 권장합니다. 또는 요소를 제이쿼리 객체로 감싸 브라우저에 따른 예외상황을 커버하면, 모든 브라우저에서 원하는 기능을 구현할 수 있습니다.

// jQuery를 이용하여 내부 HTML 대체하기var target = document.getElementById( "target" );$( target ).html( "<td>Hello <b>World</b>!</td>" );

편리성(Convenience )— 순수 DOM 메서드만으로 DOM을 조작하기엔 뭔가 어색 경우가 상당히 많습니다. 새로운 요소를 만들어라는 변수에 저장하고, 이 요소를 요소 다음에 끼워넣는 예시를 살펴봅시다. 코드가 뭔가 장황해 보이네요.

// 순수 DOM API만을 사용해 새로운 요소를 끼워 넣기var target = document.getElementById( "target" );var newElement = document.createElement( "div" );target.parentNode.insertBefore( newElement, target.nextSibling );

요소를 제이쿼리객체로 감싸면 훨씬 간단한 코드로 같은 기능을 구현할 수 있습니다.

// jQuery를 사용하여 새로운 요소를 끼워 넣기var target = document.getElementById( "target" );var newElement = document.createElement( "div" );$( target ).after( newElement );

제이쿼리 객체는 이렇게 DOM 조작을 쉽게 해주는 도구로 사용할 수 있습니다.

제이쿼리 객체 기초

제이쿼리 함수를 CSS 선택자와 함께 호출하면, 해당하는 선택자와 일치하는 요소나 요소들이 제이쿼리 객체로 반환됩니다. 예를 들면 다음과 같습니다.

// 모든 <h1> 태그 선택하기var headings = $( "h1" );

변수는 문서 내의 모든 태그를 담고 있는 제이쿼리 객체를 참조하게 됩니다. 의 프로퍼티인 를 사용하여 이를 확인해 보도록 합시다.

// 페이지 내에 <h1> 태그가 몇 개나 있는지 확인하기var headings = $( "h1" );alert( headings.length );

페이지 내에 태그가 여러 개 있다면 2 이상의 숫자가 alert 창에 뜰 것입니다. 만약 태그가 하나도 없다면 프로퍼티는 0이 될 것입니다.

프로퍼티를 이용하면 선택자가 의도대로 원하는 요소를 선택했는지 확인할 수 있습니다. 자주 쓰이는 방법이기도 합니다.

제일 첫 번째 heading 요소만 선택하려면 추가적인 작업이 더 필요합니다. 방법은 여러가지 이지만, 가장 쉬운 방법은 함수를 사용하는 것입니다.

// 페이지에서 가장 처음 나타나는 <h1> 요소만 선택하기(jQuery 객체 안에서)var headings = $( "h1" );var firstHeading = headings.eq( 0 );

변수 은 페이지 내에 가장 처음 나타나는 요소를 담는 제이쿼리 객체를 참조하게 됩니다. 이 제이쿼리 객체이기 때문에 이 변수에 같은 유용한 함수도 사용할 수 있게 됩니다. 같은 함수를 사용하면 제이쿼리로 감싸진 DOM 요소가 아닌 DOM 요소를 직접 리턴받을 수도있습니다.

// 페이지 내의 가장 첫 번째 <h1> 요소 선택하기var firstHeadingElem = $( "h1" ).get( 0 );

대괄호를 사용하여 요소를 직접 선택할 수도 있습니다. jQuery 객체가 유사배열객체이기 때문입니다:

// 페이지 내의 가장 첫 번째 <h1> 요소 선택하기(대안)var firstHeadingElem = $( "h1" )[ 0 ];

어느 방법이던, 은 이제 DOM 요소를 참조하게 됩니다. 이제 와 같은 DOM 프로퍼티나 같은 DOM 메서드를 사용할 수 있게 되었습니다. 제이쿼리 메서드인 는 더이상 사용하지 못지만 말이죠.

요소는 이제 순수 DOM 요소가 되어 다루기 힘들어졌지만 이렇게 만든 데는 이유가 있습니다. 비교를 해야 하는 경우 등에서 DOM 요소가 필요하기 때문이죠.

동등비교가 불가능한 제이쿼리 객체

요소를 제이쿼리 객체로 “감싸면” 그 객체는 유일무이한 객체가 됩니다. 동일한 선택자를 사용했거나 실제 동일한 DOM 객체를 선택했더라도 말이죠.

// 동일한 요소를 참조하는 두 개의 jQuery 객체 만들기var logo1 = $( "#logo" );var logo2 = $( "#logo" );

가 완전히 동일한 방법으로 만들어졌고, 참조하는 요소도 같은 DOM 요소이지만, 는 다른 객체가 됩니다. 아래 코드를 통해 확인해보죠.

// jQuery 객체 비교하기alert( $( "#logo" ) === $( "#logo" ) ); // "false(거짓)"이 출력

비교 시 거짓이 출력되지만, 두 객체는 동일한 DOM 객체를 담고 있습니다. 이런 경우에 메서드를 사용해 객체를 비교하면 jQuery객체가 같은 요소를 담고 있는지 아닌지를 확인할 수 있습니다.

// DOM 요소 비교하기var logo1 = $( "#logo" );
var logo1Elem = logo1.get( 0 );
var logo2 = $( "#logo" );
var logo2Elem = logo2.get( 0 );
alert( logo1Elem === logo2Elem ); // "true(참)"가 출력됨

많은 개발자가 변수 앞에 를 붙여 해당 변수가 jQuery 객체를 참조하는지를 표시해줍니다. 이 네이밍 컨벤션엔 뭔가 특별한 게 있는 건 아닙니다. 단지 변수가 뭘 담고있는지를 사람들이 쉽게 알 수 있도록 해주죠. 이 컨벤션을 이용해 위 코드를 다시 작성해 봅시다.

// DOM 요소 비교하기(가독성이 높은 네이밍 컨벤션 사용하기)var $logo1 = $( "#logo" );
var logo1 = $logo1.get( 0 );
var $logo2 = $( "#logo" );
var logo2 = $logo2.get( 0 );
alert( logo1 === logo2 ); // "true(참)"가 출력됨

전전 코드와 완전히 동일한 기능을 하지만, 좀 더 읽기 쉬운 코드가 되었습니다.

이런 네이밍 컨벤션을 사용하건 사용하지 안건 간에 제이쿼리 객체와 순수 DOM 요소를 구분하는 건 매우 중요합니다. 순수 DOM 메서드나 프로퍼티를 제이쿼리객체에 사용했을 때 원하는 대로 동작하지 않고, 그 반대도 마찬가지이기 때문입니다. “event.target.closest is not a function” 이나 “TypeError: Object [object Object] has no method ‘setAttribute’”같은 에러메시지는 개발자가 제이쿼리 객체와 DOM요소를 헷갈렸을 때 자주 발생하는 에러입니다.

영원하지 않은 제이쿼리 객체

페이지 내의 모든 paragraph(문단) 요소를 담고 있는 제이쿼리 객체가 있다고 해봅시다.

// 페이지 내의 모든 <p> 요소 선택하기var allParagraphs = $( "p" );

시간이 지나면서 문단의 내용이 더해지고 삭제되면 요소도 같이 늘어나고 줄어들 것 같아 보입니다. 하지만 제이쿼리 객체는 이런 방식으로 작동하지 않습니다. 제이쿼리 객체가 담고 있는 요소는 명시적으로 재선언하거나 수정해 주지 않는 이상 한번 선언되면 변하지 않습니다. 제이쿼리 컬렉션은 살아있는 생명체처럼 동작하지 않습니다. 문서가 수정되어도 자동으로 업데이트되지 않죠. 따라서 제이쿼리 객체가 만들어진 후 문서에 수정이 가해지면, 새로운 객체를 만들어서 컬렉션을 업데이트해 줘야만 합니다. 동일한 선택자를 사용해서 만들면 되기 때문에 아주 쉽습니다.

// 선택사항 갱신하기allParagraphs = $( "p" );

정리

DOM요소가 제공하는 다양한 기능을 이용하면 인터렉티브한 웹페이지를 만들 수 있습니다. 하지만 이 기능을 다루는 게 쉽지만은 않습니다. 제이쿼리 객체는 이런 단점을 극복해줍니다. 요소를 감싸서 개발자가 원하는 기능을 좀 더 쉽게 구현할 수 있도록 해주죠. 제이쿼리를 사용해 요소를 만들거나 선택할 때 반환 값은 항상 새로운 제이쿼리 객체가 된다는 것을 잊지 마세요. 만약 순수 DOM 객체에 뭔가 조작을 가해야 하는 상황이 발생하면 메서드를 사용하거나 대괄호를 이용하여 요소에 직접 접근할 수 있습니다.

Software Engineer, Microsoft MVP

Software Engineer, Microsoft MVP