jQuery는 파편화된 브라우저 환경에서 크로스 브라우징 이슈를 쉽게 처리할 수 있도록 관련 API를 제공하는 자바스크립트 라이브러리이다. 2006년 존 레식에 의해 처음 공개된 이후 10년이 넘도록 자바스크립트에서 가장 인기있는 라이브러리로써 널리 사용되었다. 최근에는 표준 API만으로도 DOM이나 Ajax 요청 등을 편리하게 다룰 수 있게 되었고 브라우저 간의 차이가 크게 줄어들면서 사용이 감소하는 추세이다. 또한 SPA(Single-page Application)가 대중화되면서 리액트, 뷰 등의 프레임워크가 jQuery의 역할을 상당 부분 대신하고 있다. 하지만 SPA로 작성할 필요가 없거나 구형 브라우저를 지원해야 하는 경우 jQuery는 여전히 유용하며, 크로스 브라우징 문제를 고민하지 않고 간결한 코드를 작성할 수 있도록 해 준다.
이 가이드는 jQuery를 사용할 때 더 효율적이고 유지보수 하기 좋은 코드를 작성하도록 돕기 위해 작성되었다. jQuery API에 대한 상세한 설명은 공식 홈페이지를 참고하기 바란다.
1.X, 2.X, 3.X 버전 모두 파이어폭스, 크롬, 사파리, 오페라 등 다양한 브라우저를 지원한다. jQuery 버전을 선택하여 서비스의 브라우저 지원 범위를 만족하게 할 수 있다.
2.X, 3.X 버전 : IE 6~8버전은 지원하지 않으며, IE9 이상 버전을 지원
1.7.0 이하 버전은 너무 오래된 버전이라 최신 플러그인 및 최신 브라우저와 충돌이 있으므로 사용하지 않는다. 최신 버전은 이전 버전에 비해 더 안정적이기 때문에 되도록 최신 버전을 사용하는 것이 좋다.
npm, bower, CDN으로 설치하거나 저장소에서 직접 내려받을 수 있으며, 프로젝트 성격에 따라 적절히 사용한다.
npm으로 소스코드를 가져올 경우 아래와 같은 커맨드 라인 명령을 사용한다.
$ npm install --save jquery # 최신 버전
$ npm install --save jquery@<version> # 특정 버전
bower로 소스코드를 가져올 경우 아래와 같이 커맨드 라인 명령을 사용한다.
$ bower install jquery # 최신 버전
$ bower install jquery#<tag> # 특정 버전
다음 예시처럼 https://code.jquery.com/에서 원하는 버전을 찾아 CDN을 이용해 바로 사용할 수 있다. jQuery에서 제공하는 CDN뿐만 아니라 Google, Microsoft에서 제공하는 CDN을 사용할 수도 있다.
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
CDN을 사용할 경우 외부 사정에 의해 URL이 변경되거나 장애가 발생했을 때 서비스가 영향을 받을 수 있으므로 사용을 지양한다.
필요한 버전의 jQuery를 공식 사이트에서 다운로드하여 사용한다.
jQuery는 jQuery
라는 하나의 함수로 구성되어 있으며, 이 함수가 네임스페이스의 역할과 더불어 jQuery의 모든 기능을 제공한다. jQuery를 전역으로 로드하면 $
식별자를 jQuery
함수의 별칭으로 지정해 주기 때문에 편의상 $
식별자를 사용한다. 하지만 prototype.js과 같이 $
식별자를 쓰는 자바스크립트 라이브러리를 이미 사용하는 경우 jQuery의 $
식별자가 기존 식별자를 덮어쓸 수 있다. 이 경우 다음과 같이 $.noConflict() 메서드를 호출하면 기존의 $
식별자가 갖고 있던 값을 복원시켜주므로 기존 라이브러리와 병행해서 사용할 수 있다.
<script src="other_lib.js"></script>
<script src="jquery.js"></script>
<script>
$.noConflict();
jQuery( document ).ready(function( $ ) {
// jQuery의 $만 사용할 수 있다.
});
// 다른 라이브러리의 $를 사용할 수 있다.
</script>
jQuery 객체가 할당된 변수에는 $
를 붙여 네이티브 DOM 엘리먼트와 jQuery 객체를 쉽게 구분할 수 있다.
// good
const $myId = $('#myId');
// Bad
const myId = $('#myId');
아이디 선택자를 사용하면 jQuery가 내부에서 document.getElementById()
를 사용하여 엘리먼트를 찾기 때문에 성능상 이점이 있다.
// Good
document.getElementById("myId");
$("#myId");
// Bad
$('.myClass');
아이디는 문서에 고유한 값이기 때문에 다른 조건과 같이 사용하는 것은 의미가 없다.
// Good
$('#inner');
// Bad
$('#outer #inner');
// Good
const $products = $('.products');
// Bad
const $products = $('div.products');
좌측엔 "태그명" 또는 "클래스명"을, 마지막엔 "태그명.클래스명"이 오도록 작성하면 더욱 빠르게 찾을 수 있다. 예를 들어 "li.item"이 온다면 태그이름이 "li"인 모든 엘리먼트를 찾고, 그중에 "item" 클래스명을 가진 모든 엘리먼트를 찾는다. 그 다음에야 뒤에 오는 선택자를 이용해서 다시 DOM 노드를 탐색해 나가기 때문이다.
// Good
$('.wrap li.item');
// Bad
$('ul.wrap .item');
선택자 길이를 최대한 짧게 유지하고 2개가 넘지 않도록 주의한다. 복잡한 선택자는 성능을 느리게 만들며 가독성을 나쁘게 한다.
<div id="container" class="container-class first">
<table id="table-id">
<tr>
<th class="first">Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td class="first">Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td class="first">Eve</td>
<td>Jackson</td>
<td>94</td>
</tr>
</table>
</div>
아래는 위 HTML코드에서 테이블에서 first
클래스를 가진 모든 요소를 선택하는 예시이다.
// Good
$('#table-id .first');
// Bad
$('#container table#table-id tr th.first, td.first')
// Good: first-item 라는 아이디를 가진 요소를 대상으로 확인하기 때문에 더 빠른 결과를 얻을 수 있다.
$('.item','#first-item');
// Bad: item 클래스를 가진 대상을 전부 확인하기 때문에 느린 결과를 얻을 것이다.
$('.item');
Sizzle 선택자 엔진을 거치지 않기 때문에 빠르다.
// Good - #products는 내부적으로 이미 document.getElementById()를 통해 찾은 뒤 div.container 만 Sizzle 선택자 엔진을 거치게 되므로 빠르다.
const $productIds = $('#products').find('div.container');
// Bad - 중첩된 query 모두 Sizzle 선택자 엔진을 통한다.
const $productIds = $('#products div.container');
// Good
$('.buttons').children();
$('.category input:radio');
// Bad
$('.buttons > *');
$('.category *:radio');
가상 선택자를 태그 선택자 없이 사용하면 모든 엘리먼트를 검색하게 되어 성능이 느려진다.
// Good
$('div.someclass input:radio');
// Bad
$('div.someclass :radio');
detach()
를 사용하면 해당 엘리먼트를 실제 DOM에서 분리하여 메모리상에서만 조작할 수 있어, 불필요한 레이아웃 연산이 여러 번 발생하는 것을 방지할 수 있다.
<div id="table_container">
<table>
<tr>
<td>ID</td>
<td>Name</td>
</tr>
</table>
</div>
// Good
<script type="text/javascript">
const parent = $( "#table_container" );
const table = parent.find('table');
table.detach();
table.append('<tr><td>1</td><td>John Smith</td></tr>');
parent.append(table);
</script>
// Bad
<script type="text/javascript">
$('#table_container table').append('<tr><td>1</td><td>Hanjung</td></tr>');
</script>
// Good
const $mySelection = $('#nosuchthing');
if ($mySelection.length) {
$mySelection.slideUp();
...
}
// Bad: 선택자에 아무것도 없다는 것을 여러 과정이 진행 된 이후 알아 차릴 것이다.
$('#nosuchthing').slideUp();
CSS와 자바스크립트는 서로 다른 책임을 갖기 때문에, 각각을 분리해서 관리하는 것이 유지보수 측면에서 유리하다. 되도록이면 CSS에서 클래스를 정의하고, 자바스크립트에서 클래스를 추가, 삭제하여 스타일을 조작한다.
/* Good */
.error { color: red; font-weight: bold; }
// Good
$("#mydiv").addClass("error");
// Bad
$("#mydiv").css({'color':red, 'font-weight':'bold'});
HTML 마크업에 직접 이벤트 핸들러를 설정하면 이벤트를 동적으로 설정하고 제거하는 것이 어려워진다. 또한, 디버깅 할 때 HTML 마크업과 자바스크립트를 모두 확인해야 하는 번거로움이 생긴다.
// Good
$('#myLink').on('click', myEventHandler);
<!-- Bad -->
<a id="myLink" href="#" onclick="myEventHandler();">my link</a>
이벤트 위임(Event delegation)을 사용하면 동적으로 추가 또는 수정되는 자식 엘리먼트의 이벤트도 함께 처리할 수 있는 이점이 있다.
$("#parent-container").on("click", "a", delegatedClickHandler);
익명 함수를 사용하면 디버깅과 관리, 그리고 테스트가 어렵다.
// Good
function myLinkClickHandler(){...}
function myInitHandler(){...}
$('#myLink').on('click', myLinkClickHandler);
$(document).ready(myInitHandler);
$(myInitHandler);
// $(document).ready(myInitHandler)과 동일, 다른 형태로 작성하는 ready 구문은 3.0에서 디프리케이트 되었기 때문에 이렇게 작성할 것을 권장
// Bad
$('#myLink').on('click', function(){...});
$(function(){ ... });
디버깅이 용이하며 동작의 흐름도 추적할 수 있다.
(3.X
버전부터 unbind
메서드가 디프리케이트 되었으므로 대신 off()
메서드 사용을 권장한다.)
const validate = function() {
// 검증 코드
};
$( "form" ).on( "click.validator", "button", validate );
$( "form" ).on( "keypress.validator", "input[type='text']", validate );
// 'validator' namespace 하위의 이벤트 핸들러를 모두 해제 시킨다.
$( "form" ).off( ".validator" );
메서드 체이닝은 변수를 캐싱하거나 선택자를 여러 번 호출하지 않아도 되는 이점이 있다.
// Good
$('#myDiv').addClass('error').show();
// Bad
$('#myDiv').addClass('error');
$('#myDiv').show();
// Good
$('#myLink')
.addClass('bold')
.on('click', myClickHandler)
.on('mouseover', myMouseOverHandler)
.show();
// Bad
$('#myLink').addClass('bold').on('click', myClickHandler).on('mouseover', myMouseOverHandler).show();
// Good
$myLink.attr({
href: '#',
title: 'my link',
rel: 'external'
});
// Bad
$myLink.attr('href', '#').attr('title', 'my link').attr('rel', 'external');
프로토콜을 명시하지 않는 Protocol-relative URL을 사용하여, 브라우저가 현재 페이지의 프로토콜에 따라 자동으로 판단하도록 한다.
// Good
$.ajax({
url: '//www.nhn.com/api/hello',
...
});
// Bad
$.ajax({
url: 'https://www.nhn.com/api/hello',
...
});
가독성이 좋아지고 디버깅이 쉬워진다.
// Good
$.ajax({
url: 'something.php',
data: {
param1: test1,
param2: test2
}
});
// Bad
$.ajax({
url: 'something.php?param1=test1¶m2=test2',
....
});
const jqxhr = $.ajax({
url: url,
...
dataType: 'json',
...
});
$.ajax({ ... }).then(successHandler, failureHandler);
// 위 코드를 다음과 같이 작성할 수 있다.
const jqxhr = $.ajax({ ... });
jqxhr.done(successHandler);
jqxhr.fail(failureHandler);
jQuery 3.0 버전 이후 ES2015와 호환 가능해진 내용을 예시와 함께 간략하게 설명한다 .
ES2015에서 추가된 문법인 for...of
문은 Iterable 프로토콜을 지원하는 모든 객체를 지원한다. jQuery 3.0부터 jQuery 컬렉션도 Iterable 프로토콜을 지원하여 for...of
문에서 사용할 수 있게 되었다.
const $elems = $(".someclass");
// 기존 jQuery 방식
$elems.each((i, elem) => {
// elem 혹은 "this" 을 사용
});
// ES6 방식
for (let elem of $elems) {
// elem을 사용
}
jQuery.ajax()의 결과로 반환되는 jqXHR
객체는 jQuery의 Deferred 객체였다. jQuery 3.0부터 Deferred객체에서 제공되던 success, error, complete 메소드는 제거되었다. 대신 Deferred 객체의 done
, fail
, always
를 사용하게 되고, Promises/A+ 메서드인 then
, catch
를 사용할 수 있게 되었다.
const defer = $.Deferred();
const filtered = defer.then(null, (value) => {
return value * 3;
});
defer.resolve(6);
filtered.done(( value ) => {
alert( "Value is 3*6 =" + value );
});
이는 네이티브 프라미스 객체 뿐만 아니라, Bluebird 와 같은 외부 프라미스 라이브러리도 지원한다는 의미이다. 또한 인자가 여러 개 있는 경우 Promise.all()
처럼 동작하며, 매개변수가 없거나 하나일 때는 Promise.resolve()
처럼 동작한다.
const promise1 = new Promise((resolve, reject) => {
resolve('foo');
});
$.when(promise1).then((value) => {
console.log(value); // "foo"
});
jQuery.when
이나 Promise.resolve()
를 이용하면 jQuery.ready
를 프라미스 형태로 변환하여 사용할 수 있다.
$.when($.ready, $.getScript("optional.js")).then(() => {
// document가 ready 되고 optional.js가 loaded/run 되었을 때
}).catch(() => {
// error가 발생할 경우
});
지금까지 jQuery를 사용할 때 유의할 점과 권장 사항, 3.0 버전에 추가된 ES2015 지원 등을 살펴 보았다. jQuery는 유용한 도구이지만 잘못 사용하면 성능을 오히려 나쁘게 만들거나 코드의 가독성을 떨어뜨린다. 이 가이드를 지침삼아 jQuery가 주는 이점을 최대한 활용할 수 있길 바란다.
이 문서는 NHN Cloud의 FE개발랩에서 작성하고 관리하는 공식 웹 프론트 개발 가이드이다. 가이드 적용 관련 문의나 문서의 오류, 개선 제안은 공식 문의 채널(dl_javascript@nhn.com)을 통해 할 수 있다.
Last Modified |
---|
2019. 03. 29 |