개요[편집 / 원본 편집]

jQuery 로고

jQuery(제이쿼리)는 존 레식(John Resig)이 2006년에 개발한 경량화된 자바스크립트 라이브러리이다. "Write Less, Do More"라는 모토로 시작된 이 라이브러리는 HTML 문서의 탐색과 조작, 이벤트 핸들링, 애니메이션, Ajax 등을 훨씬 간단하게 처리할 수 있게 해주는 크로스 브라우저 자바스크립트 라이브러리이다.

jQuery는 웹 개발 역사상 가장 널리 사용된 자바스크립트 라이브러리 중 하나로, 한때 전체 웹사이트의 80% 이상에서 사용될 정도로 압도적인 점유율을 자랑했다. 그 이유는 당시 각각 다른 브라우저들(Internet Explorer, Firefox, Chrome, Safari 등)이 자바스크립트를 서로 다르게 구현했기 때문에, 개발자들이 크로스 브라우저 호환성을 위해 복잡한 코드를 작성해야 했는데, jQuery가 이 문제를 한 줄의 코드로 해결해주었기 때문이다.

2005년 당시의 웹 개발 환경은 말 그대로 지옥이었다. Internet Explorer 6가 브라우저 시장을 독점하고 있었지만, Firefox, Safari 등 다른 브라우저들도 각자의 방식으로 자바스크립트를 구현하고 있었다. 개발자들은 간단한 DOM 조작 하나를 위해서도 수십 줄의 브라우저 감지 코드를 작성해야 했다.

// 2005년 당시 크로스브라우저 이벤트 리스너 추가 방법
function addEventListener(element, event, handler) {
    if (element.addEventListener) {
        element.addEventListener(event, handler, false);
    } else if (element.attachEvent) {
        element.attachEvent('on' + event, handler);
    } else {
        element['on' + event] = handler;
    }
}

이런 상황에서 존 레식BarCamp NYC에서 jQuery의 초기 버전을 공개했다. 단 한 줄로 크로스브라우저 호환성을 제공하겠다는 아이디어였다.

역사[편집 / 원본 편집]

탄생 배경[편집 / 원본 편집]

jQuery 1.0이 2007년에 정식 출시되면서, 웹 개발 패러다임이 완전히 바뀌었다. 2008년에는 jQuery 1.2가 출시되면서 CSS 셀렉터 엔진 Sizzle이 도입되었고, 2010년에는 jQuery 1.4가 출시되어 성능이 대폭 개선되었다. 2011년에는 jQuery Mobile이 출시되어 모바일 시장에 진출했으며, 2013년에는 jQuery 2.0이 출시되면서 IE 6-8 지원 중단이 발표되었다. 2014년에는 전체 웹사이트의 65%가 jQuery를 사용할 정도로 절정에 달했다.

쇠퇴와 현재[편집 / 원본 편집]

2015년 ES6(ECMAScript 2015)의 등장과 함께 네이티브 자바스크립트가 강력해지기 시작했고, React, Vue.js, Angular 같은 프레임워크들이 등장하면서 jQuery의 입지가 줄어들기 시작했다. 2016년에는 jQuery 3.0이 출시되었지만 사용률 감소가 시작되었고, 2020년에는 GitHub에서 jQuery 제거를 발표했다. 2024년 현재는 여전히 많은 레거시 사이트에서 사용되고 있지만 신규 프로젝트에서는 거의 사용되지 않는다.

특징[편집 / 원본 편집]

핵심 철학[편집 / 원본 편집]

jQuery의 핵심 철학은 간결함직관성이다. 복잡한 자바스크립트 코드를 단 몇 줄로 줄여주는 것이 가장 큰 특징이다.

// 순수 자바스크립트로 모든 div 요소를 숨기기
var divElements = document.getElementsByTagName('div');
for (var i = 0; i < divElements.length; i++) {
    divElements[i].style.display = 'none';
}

// jQuery로 모든 div 요소를 숨기기
$('div').hide();

CSS 스타일 셀렉터[편집 / 원본 편집]

jQuery는 CSS 셀렉터 문법을 그대로 사용한다. CSS를 알고 있다면 jQuery 셀렉터도 자연스럽게 사용할 수 있다. $('#myId')는 CSS의 #myId와 동일하고, $('.myClass')는 CSS의 .myClass와 동일하다. 심지어 $('input[type="text"]') 같은 속성 셀렉터도 그대로 사용할 수 있다.

메소드 체이닝[편집 / 원본 편집]

jQuery의 메소드 체이닝은 코드를 더욱 간결하고 읽기 쉽게 만들어준다.

$('#myDiv')
    .addClass('highlight')
    .fadeIn(500)
    .delay(2000)
    .fadeOut(500);

크로스 브라우저 호환성[편집 / 원본 편집]

jQuery의 가장 큰 장점 중 하나는 완벽한 크로스 브라우저 호환성이다. Internet Explorer 6부터 최신 브라우저까지 동일한 코드로 동작한다. 당시에는 이것이 혁명적이었다.

플러그인 생태계[편집 / 원본 편집]

jQuery는 풍부한 플러그인 생태계를 갖고 있다. jQuery UI, jQuery Mobile을 비롯해 수천 개의 써드파티 플러그인들이 존재한다.

설치 및 사용법[편집 / 원본 편집]

CDN을 통한 설치[편집 / 원본 편집]

가장 간단한 방법은 CDN(Content Delivery Network)을 이용하는 것이다.

<!DOCTYPE html>
<html>
<head>
    <title>jQuery 예제</title>
    <!-- jQuery 3.x 최신 버전 -->
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    
    <!-- 또는 Google CDN -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    
    <!-- 또는 Microsoft CDN -->
    <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.7.1.min.js"></script>
</head>
<body>
    <h1>jQuery 테스트</h1>
    <script>
        $(document).ready(function() {
            console.log('jQuery 로드 완료!');
        });
    </script>
</body>
</html>

파일 다운로드[편집 / 원본 편집]

jQuery 공식 사이트(jquery.com)에서 jquery-3.x.x.min.js 파일을 다운로드하여 사용할 수도 있다.

<script src="js/jquery-3.7.1.min.js"></script>

npm을 통한 설치[편집 / 원본 편집]

Node.js 환경에서는 npm을 통해 설치할 수 있다.

npm install jquery
// CommonJS
const $ = require('jquery');

// ES6 모듈
import $ from 'jquery';

Document Ready[편집 / 원본 편집]

jQuery 코드는 반드시 DOM이 완전히 로드된 후에 실행되어야 한다. 이를 위해 $(document).ready()를 사용한다.

// 방법 1: 전체 문법
$(document).ready(function() {
    // jQuery 코드
});

// 방법 2: 축약 문법 (가장 많이 사용)
$(function() {
    // jQuery 코드
});

// 방법 3: arrow function 사용
$(() => {
    // jQuery 코드
});

기본 문법[편집 / 원본 편집]

jQuery 기본 구조[편집 / 원본 편집]

jQuery의 기본 구조는 $(selector).action() 형태이다. 여기서 $는 jQuery를 의미하는 별칭(alias)이고, selector는 HTML 요소를 선택하는 셀렉터이며, action()은 선택된 요소에 수행할 동작이다.

$('#myButton').click(function() {
    alert('버튼이 클릭되었습니다!');
});

$ 충돌 방지[편집 / 원본 편집]

다른 라이브러리와 $ 기호 충돌을 방지하려면 jQuery.noConflict()를 사용한다.

// $ 기호 충돌 방지
var jq = jQuery.noConflict();

jq(document).ready(function() {
    jq('#myDiv').hide();
});

// 또는 즉시실행함수 사용
jQuery.noConflict();
(function($) {
    $(document).ready(function() {
        $('#myDiv').hide();
    });
})(jQuery);

jQuery 객체와 DOM 객체[편집 / 원본 편집]

jQuery 셀렉터로 선택한 요소는 jQuery 객체가 되며, 일반 DOM 객체와는 다르다.

// jQuery 객체
var $div = $('#myDiv');
console.log($div); // jQuery 객체

// DOM 객체로 변환
var domDiv = $div[0]; // 또는 $div.get(0)
console.log(domDiv); // HTMLDivElement

// DOM 객체를 jQuery 객체로 변환
var $domDiv = $(domDiv);

셀렉터[편집 / 원본 편집]

jQuery의 셀렉터CSS 셀렉터 문법을 기반으로 하며, 추가적인 고유 셀렉터들도 제공한다.

기본 셀렉터[편집 / 원본 편집]

// 전체 셀렉터
$('*')              // 모든 요소 선택

// 태그 셀렉터
$('div')            // 모든 div 요소
$('p')              // 모든 p 요소
$('h1, h2, h3')     // 여러 태그 동시 선택

// ID 셀렉터
$('#myId')          // id="myId"인 요소

// 클래스 셀렉터
$('.myClass')       // class="myClass"인 모든 요소
$('.class1.class2') // 두 클래스를 모두 가진 요소

// 속성 셀렉터
$('[name]')         // name 속성을 가진 요소
$('[name="email"]') // name="email"인 요소
$('[href^="http"]') // href가 "http"로 시작하는 요소
$('[src$=".jpg"]')  // src가 ".jpg"로 끝나는 요소
$('[title*="test"]') // title에 "test"가 포함된 요소

계층 셀렉터[편집 / 원본 편집]

// 후손 셀렉터
$('div p')          // div 내부의 모든 p 요소

// 자식 셀렉터
$('div > p')        // div의 직접 자식인 p 요소만

// 인접 형제 셀렉터
$('h1 + p')         // h1 바로 다음의 p 요소

// 일반 형제 셀렉터
$('h1 ~ p')         // h1 이후의 모든 형제 p 요소

jQuery 고유 셀렉터[편집 / 원본 편집]

// 순서 기반 셀렉터
$('li:first')       // 첫 번째 li 요소
$('li:last')        // 마지막 li 요소
$('li:eq(2)')       // 인덱스 2번째 li 요소 (0부터 시작)
$('li:lt(3)')       // 인덱스 3보다 작은 li 요소들
$('li:gt(1)')       // 인덱스 1보다 큰 li 요소들
$('li:odd')         // 홀수 번째 li 요소들 (1, 3, 5...)
$('li:even')        // 짝수 번째 li 요소들 (0, 2, 4...)

// 내용 기반 셀렉터
$(':contains("Hello")')    // "Hello" 텍스트를 포함한 요소
$(':empty')               // 내용이 비어있는 요소
$(':parent')              // 자식 요소나 텍스트를 가진 요소

// 가시성 기반 셀렉터
$(':visible')             // 보이는 요소들
$(':hidden')              // 숨겨진 요소들

// 폼 관련 셀렉터
$(':input')               // 모든 input, textarea, select, button 요소
$(':text')                // type="text"인 input 요소
$(':password')            // type="password"인 input 요소
$(':radio')               // type="radio"인 input 요소
$(':checkbox')            // type="checkbox"인 input 요소
$(':submit')              // type="submit"인 input 요소
$(':file')                // type="file"인 input 요소
$(':enabled')             // 활성화된 폼 요소
$(':disabled')            // 비활성화된 폼 요소
$(':checked')             // 체크된 radio나 checkbox
$(':selected')            // 선택된 option 요소

필터링 메소드[편집 / 원본 편집]

$('li').filter('.important')    // li 중에서 .important 클래스를 가진 것만
$('li').not('.ignore')         // li 중에서 .ignore 클래스를 가지지 않은 것만
$('li').has('span')            // li 중에서 span 자식 요소를 가진 것만
$('li').eq(2)                  // li 중에서 인덱스 2번째 요소만
$('li').first()                // li 중에서 첫 번째 요소
$('li').last()                 // li 중에서 마지막 요소
$('li').slice(1, 4)            // li 중에서 인덱스 1부터 3까지의 요소들

DOM 조작[편집 / 원본 편집]

jQuery는 DOM(Document Object Model) 조작을 매우 간단하게 만들어준다.

내용 조작[편집 / 원본 편집]

// 텍스트 내용 조작
$('#myDiv').text()                    // 텍스트 내용 가져오기
$('#myDiv').text('새로운 텍스트')        // 텍스트 내용 설정하기

// HTML 내용 조작
$('#myDiv').html()                    // HTML 내용 가져오기
$('#myDiv').html('<p>새로운 HTML</p>')  // HTML 내용 설정하기

// 값 조작 (폼 요소)
$('#myInput').val()                   // 입력값 가져오기
$('#myInput').val('새로운 값')          // 입력값 설정하기
$('#mySelect').val(['option1', 'option2']) // select 다중 선택

속성 조작[편집 / 원본 편집]

// 속성 조작
$('#myImg').attr('src')                    // src 속성값 가져오기
$('#myImg').attr('src', 'image.jpg')       // src 속성값 설정하기
$('#myImg').attr({                         // 여러 속성 동시 설정
    'src': 'image.jpg',
    'alt': '이미지 설명',
    'title': '이미지 제목'
});
$('#myImg').removeAttr('title')            // title 속성 제거

// 프로퍼티 조작 (속성과 비슷하지만 다름)
$('#myCheckbox').prop('checked')           // checked 상태 확인
$('#myCheckbox').prop('checked', true)     // checked 상태로 만들기
$('#myInput').prop('disabled', true)       // disabled 상태로 만들기

// 데이터 속성 조작
$('#myDiv').data('user-id')                // data-user-id 속성값 가져오기
$('#myDiv').data('user-id', '123')         // data-user-id 속성값 설정하기
$('#myDiv').removeData('user-id')          // data-user-id 속성 제거

CSS 조작[편집 / 원본 편집]

// CSS 스타일 조작
$('#myDiv').css('color')                   // color 스타일값 가져오기
$('#myDiv').css('color', 'red')            // color 스타일 설정하기
$('#myDiv').css({                          // 여러 스타일 동시 설정
    'color': 'red',
    'font-size': '20px',
    'background-color': '#f0f0f0'
});

// 클래스 조작
$('#myDiv').addClass('highlight')          // 클래스 추가
$('#myDiv').removeClass('highlight')       // 클래스 제거
$('#myDiv').toggleClass('highlight')       // 클래스 토글 (있으면 제거, 없으면 추가)
$('#myDiv').hasClass('highlight')          // 클래스 존재 여부 확인

// 크기와 위치
$('#myDiv').width()                        // 너비 가져오기
$('#myDiv').width(300)                     // 너비 설정하기
$('#myDiv').height(200)                    // 높이 설정하기
$('#myDiv').innerWidth()                   // padding 포함 너비
$('#myDiv').outerWidth()                   // margin 포함 너비
$('#myDiv').offset()                       // 문서 기준 위치 {top: 100, left: 50}
$('#myDiv').position()                     // 부모 기준 위치

요소 생성과 삽입[편집 / 원본 편집]

// 요소 생성
var $newDiv = $('<div>새로운 div</div>');
var $newP = $('<p>', {
    text: '새로운 문단',
    class: 'highlight',
    id: 'newParagraph'
});

// 내부에 삽입
$('#container').append($newDiv)            // container 마지막에 추가
$('#container').prepend($newDiv)           // container 첫 번째에 추가
$newDiv.appendTo('#container')             // newDiv를 container 마지막에 추가
$newDiv.prependTo('#container')            // newDiv를 container 첫 번째에 추가

// 외부에 삽입
$('#target').after($newDiv)                // target 뒤에 추가
$('#target').before($newDiv)               // target 앞에 추가
$newDiv.insertAfter('#target')             // newDiv를 target 뒤에 추가
$newDiv.insertBefore('#target')            // newDiv를 target 앞에 추가

요소 복사와 제거[편집 / 원본 편집]

// 요소 복사
var $cloned = $('#original').clone()       // 단순 복사
var $clonedWithEvents = $('#original').clone(true) // 이벤트까지 복사

// 요소 제거
$('#target').remove()                      // 요소와 이벤트 모두 제거
$('#target').detach()                      // 요소 제거하지만 이벤트는 유지
$('#target').empty()                       // 자식 요소들만 제거

// 요소 교체
$('#old').replaceWith('<div>새로운 내용</div>')
$('<div>새로운 내용</div>').replaceAll('#old')

// 요소 감싸기
$('#target').wrap('<div class="wrapper"></div>')      // 각각 감싸기
$('.targets').wrapAll('<div class="container"></div>') // 모두 함께 감싸기
$('#target').wrapInner('<span></span>')               // 내용물 감싸기
$('#target').unwrap()                                 // 감싸는 요소 제거

이벤트 처리[편집 / 원본 편집]

jQuery는 이벤트 처리를 매우 직관적이고 강력하게 만들어준다.

기본 이벤트 처리[편집 / 원본 편집]

// 클릭 이벤트
$('#myButton').click(function() {
    alert('버튼이 클릭되었습니다!');
});

// 더블클릭 이벤트
$('#myButton').dblclick(function() {
    alert('버튼이 더블클릭되었습니다!');
});

// 마우스 이벤트
$('#myDiv').mouseenter(function() {
    $(this).css('background-color', 'yellow');
});

$('#myDiv').mouseleave(function() {
    $(this).css('background-color', 'white');
});

// hover 이벤트 (mouseenter + mouseleave)
$('#myDiv').hover(
    function() { // mouseenter
        $(this).addClass('highlight');
    },
    function() { // mouseleave
        $(this).removeClass('highlight');
    }
);

// 키보드 이벤트
$('#myInput').keydown(function(event) {
    console.log('키가 눌렸습니다:', event.which);
});

$('#myInput').keyup(function(event) {
    console.log('키가 떼어졌습니다:', event.which);
});

// 폼 이벤트
$('#myForm').submit(function(event) {
    event.preventDefault(); // 기본 제출 동작 방지
    console.log('폼이 제출되었습니다');
});

$('#myInput').focus(function() {
    $(this).css('border', '2px solid blue');
});

$('#myInput').blur(function() {
    $(this).css('border', '1px solid gray');
});

on() 메소드[편집 / 원본 편집]

on() 메소드는 jQuery의 통합 이벤트 핸들러로, 모든 이벤트를 처리할 수 있다.

// 기본 사용법
$('#myButton').on('click', function() {
    alert('클릭되었습니다!');
});

// 여러 이벤트 동시 처리
$('#myInput').on('focus blur', function(event) {
    console.log('이벤트 타입:', event.type);
});

// 이벤트 객체와 데이터 전달
$('#myButton').on('click', {name: 'John', age: 30}, function(event) {
    console.log('이름:', event.data.name);
    console.log('나이:', event.data.age);
});

// 네임스페이스 사용
$('#myButton').on('click.myNamespace', function() {
    console.log('네임스페이스 클릭');
});

// 조건부 이벤트 처리
$('#myDiv').on('click', 'button', function() {
    // myDiv 내부의 button 요소가 클릭되었을 때만 실행
    console.log('내부 버튼 클릭');
});

이벤트 위임[편집 / 원본 편집]

이벤트 위임(Event Delegation)은 동적으로 생성되는 요소에 이벤트를 처리할 때 매우 유용하다.

// 문제가 있는 코드 - 나중에 추가된 버튼은 이벤트가 작동하지 않음
$('.dynamic-button').click(function() {
    alert('클릭!');
});

// 올바른 코드 - 이벤트 위임 사용
$(document).on('click', '.dynamic-button', function() {
    alert('클릭!');
});

// 더 구체적인 이벤트 위임
$('#container').on('click', '.item', function() {
    console.log('아이템 클릭:', $(this).text());
});

// 새로운 요소 추가 (이벤트가 자동으로 적용됨)
$('#container').append('<div class="item">새 아이템</div>');

이벤트 제거[편집 / 원본 편집]

// 모든 이벤트 제거
$('#myButton').off();

// 특정 이벤트 타입 제거
$('#myButton').off('click');

// 네임스페이스로 제거
$('#myButton').off('click.myNamespace');

// 특정 핸들러 제거
function myHandler() {
    alert('클릭!');
}

$('#myButton').on('click', myHandler);
$('#myButton').off('click', myHandler);

// 한 번만 실행되는 이벤트
$('#myButton').one('click', function() {
    alert('첫 번째 클릭에만 실행됩니다');
});

커스텀 이벤트[편집 / 원본 편집]

// 커스텀 이벤트 정의
$('#myDiv').on('myCustomEvent', function(event, data1, data2) {
    console.log('커스텀 이벤트 발생!');
    console.log('데이터 1:', data1);
    console.log('데이터 2:', data2);
});

// 커스텀 이벤트 발생시키기
$('#myDiv').trigger('myCustomEvent', ['Hello', 'World']);

// triggerHandler - 기본 동작 방지하고 첫 번째 요소에만 적용
$('#myDiv').triggerHandler('myCustomEvent', ['Data']);

AJAX[편집 / 원본 편집]

jQuery의 AJAX 기능은 서버와의 비동기 통신을 매우 간단하게 만들어준다.

기본 AJAX 메소드[편집 / 원본 편집]

// $.ajax() - 가장 기본적이고 강력한 메소드
$.ajax({
    url: 'https://api.example.com/data',
    type: 'GET', // 또는 method: 'GET'
    dataType: 'json',
    data: {
        userId: 123,
        category: 'electronics'
    },
    success: function(response) {
        console.log('성공:', response);
    },
    error: function(xhr, status, error) {
        console.log('오류:', error);
    },
    complete: function() {
        console.log('완료 (성공/실패 관계없이)');
    }
});

// Promise 스타일로도 사용 가능
$.ajax({
    url: 'api/data.json',
    type: 'GET',
    dataType: 'json'
}).done(function(data) {
    console.log('성공:', data);
}).fail(function(xhr, status, error) {
    console.log('실패:', error);
}).always(function() {
    console.log('항상 실행');
});

간편 AJAX 메소드[편집 / 원본 편집]

// GET 요청
$.get('api/users.json', function(data) {
    console.log('사용자 데이터:', data);
});

// POST 요청
$.post('api/users', {
    name: 'John Doe',
    email: '[email protected]'
}, function(response) {
    console.log('사용자 생성:', response);
});

// JSON 데이터 가져오기
$.getJSON('api/config.json', function(config) {
    console.log('설정:', config);
});

// 스크립트 동적 로딩
$.getScript('js/dynamic-script.js', function() {
    console.log('스크립트 로드 완료');
});

폼 데이터 전송[편집 / 원본 편집]

// 폼 데이터 직렬화
var formData = $('#myForm').serialize();
// 결과: "name=John&[email protected]&age=30"

var formArray = $('#myForm').serializeArray();
// 결과: [{name: "name", value: "John"}, {name: "email", value: "[email protected]"}]

// 폼 AJAX 제출
$('#myForm').submit(function(event) {
    event.preventDefault();
    
    $.ajax({
        url: $(this).attr('action'),
        type: $(this).attr('method'),
        data: $(this).serialize(),
        success: function(response) {
            alert('폼 제출 성공!');
        },
        error: function() {
            alert('폼 제출 실패!');
        }
    });
});

파일 업로드[편집 / 원본 편집]

// FormData를 사용한 파일 업로드
$('#fileForm').submit(function(event) {
    event.preventDefault();
    
    var formData = new FormData(this);
    
    $.ajax({
        url: 'upload.php',
        type: 'POST',
        data: formData,
        processData: false, // jQuery가 데이터를 처리하지 않도록
        contentType: false, // content-type 헤더를 설정하지 않도록
        success: function(response) {
            console.log('업로드 성공:', response);
        },
        error: function() {
            console.log('업로드 실패');
        }
    });
});

AJAX 전역 설정[편집 / 원본 편집]

// 전역 AJAX 설정
$.ajaxSetup({
    timeout: 10000, // 10초 타임아웃
    cache: false,   // 캐시 비활성화
    headers: {
        'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
    }
});

// 전역 AJAX 이벤트
$(document).ajaxStart(function() {
    console.log('AJAX 요청 시작');
    $('#loading').show();
});

$(document).ajaxStop(function() {
    console.log('모든 AJAX 요청 완료');
    $('#loading').hide();
});

$(document).ajaxSuccess(function(event, xhr, settings) {
    console.log('AJAX 요청 성공:', settings.url);
});

$(document).ajaxError(function(event, xhr, settings, error) {
    console.log('AJAX 요청 실패:', settings.url, error);
});

애니메이션과 효과[편집 / 원본 편집]

jQuery의 애니메이션 기능은 웹사이트에 생동감을 불어넣어준다.

기본 효과[편집 / 원본 편집]

// 보이기/숨기기
$('#myDiv').show()              // 즉시 보이기
$('#myDiv').hide()              // 즉시 숨기기
$('#myDiv').toggle()            // 토글 (보이면 숨기고, 숨겨있으면 보이기)

// 지속시간 지정
$('#myDiv').show(1000)          // 1초 동안 서서히 보이기
$('#myDiv').hide('slow')        // 느리게 숨기기 (600ms)
$('#myDiv').toggle('fast')      // 빠르게 토글 (200ms)

// 콜백 함수
$('#myDiv').hide(500, function() {
    console.log('숨김 애니메이션 완료');
    $(this).remove();
});

페이드 효과[편집 / 원본 편집]

// 페이드 인/아웃
$('#myDiv').fadeIn()            // 서서히 나타나기
$('#myDiv').fadeOut()           // 서서히 사라지기
$('#myDiv').fadeToggle()        // 페이드 토글

// 투명도 조절
$('#myDiv').fadeTo(1000, 0.5)   // 1초 동안 50% 투명도로

// 체이닝과 콜백
$('#myDiv')
    .fadeOut(500)
    .delay(1000)
    .fadeIn(500, function() {
        $(this).css('color', 'red');
    });

슬라이드 효과[편집 / 원본 편집]

// 슬라이드 업/다운
$('#myDiv').slideUp()           // 위로 접기
$('#myDiv').slideDown()         // 아래로 펼치기
$('#myDiv').slideToggle()       // 슬라이드 토글

// 지속시간과 이징
$('#myDiv').slideUp(800, 'swing', function() {
    console.log('슬라이드 업 완료');
});

// 아코디언 메뉴 구현
$('.accordion-header').click(function() {
    $(this).next('.accordion-content').slideToggle();
    $(this).siblings('.accordion-header').next('.accordion-content').slideUp();
});

커스텀 애니메이션[편집 / 원본 편집]

// animate() 메소드
$('#myDiv').animate({
    left: '250px',
    opacity: 0.5,
    height: '150px',
    width: '150px'
}, 1000);

// 상대적 값 사용
$('#myDiv').animate({
    left: '+=50px',     // 현재 위치에서 50px 오른쪽으로
    top: '-=30px'       // 현재 위치에서 30px 위로
}, 'slow');

// 여러 단계 애니메이션
$('#myDiv')
    .animate({left: '250px'}, 1000)
    .animate({top: '100px'}, 500)
    .animate({opacity: 0.3}, 300);

// 커스텀 이징과 콜백
$('#myDiv').animate({
    width: '300px',
    height: '300px'
}, {
    duration: 2000,
    easing: 'swing', // 'linear' 또는 'swing'
    step: function(now, fx) {
        // 각 스텝에서 실행
        console.log(fx.prop + ': ' + now);
    },
    complete: function() {
        // 애니메이션 완료 시 실행
        console.log('애니메이션 완료!');
    }
});

애니메이션 제어[편집 / 원본 편집]

// 애니메이션 일시정지/재개
$('#pauseBtn').click(function() {
    $('#myDiv').stop();         // 현재 애니메이션 정지
});

$('#resumeBtn').click(function() {
    $('#myDiv').animate({left: '500px'}, 2000); // 새 애니메이션 시작
});

// 애니메이션 큐 제어
$('#myDiv').stop(true, true);   // 큐 비우고 현재 애니메이션 완료
$('#myDiv').finish();           // 모든 애니메이션 즉시 완료

// 지연(delay)
$('#myDiv')
    .fadeOut(500)
    .delay(2000)                // 2초 대기
    .fadeIn(500);

// 큐 확인
$('#myDiv').queue(function(next) {
    console.log('큐에서 실행');
    // 다음 애니메이션으로 진행
    next();
});

플러그인[편집 / 원본 편집]

jQuery의 플러그인 시스템은 기능을 확장하고 재사용 가능한 컴포넌트를 만들 수 있게 해준다.

유명한 jQuery 플러그인들[편집 / 원본 편집]

// jQuery UI - 공식 UI 라이브러리
$('#datepicker').datepicker();
$('#dialog').dialog();
$('#sortable').sortable();
$('#draggable').draggable();

// Owl Carousel - 이미지 슬라이더
$('.owl-carousel').owlCarousel({
    loop: true,
    margin: 10,
    nav: true,
    responsive: {
        0: { items: 1 },
        600: { items: 3 },
        1000: { items: 5 }
    }
});

// Select2 - 강화된 셀렉트 박스
$('#mySelect').select2({
    placeholder: "옵션을 선택하세요",
    allowClear: true
});

// DataTables - 테이블 기능 강화
$('#myTable').DataTable({
    paging: true,
    searching: true,
    ordering: true,
    language: {
        url: '//cdn.datatables.net/plug-ins/1.11.5/i18n/Korean.json'
    }
});

// Validation - 폼 검증
$('#myForm').validate({
    rules: {
        email: {
            required: true,
            email: true
        },
        password: {
            required: true,
            minlength: 8
        }
    },
    messages: {
        email: "유효한 이메일을 입력하세요",
        password: "비밀번호는 8자 이상이어야 합니다"
    }
});

플러그인 작성법[편집 / 원본 편집]

// 기본 플러그인 구조
(function($) {
    $.fn.myPlugin = function(options) {
        // 기본 설정
        var settings = $.extend({
            color: 'red',
            fontSize: '14px'
        }, options);
        
        // 체이닝을 위해 this 반환
        return this.each(function() {
            var $this = $(this);
            
            // 플러그인 로직
            $this.css({
                'color': settings.color,
                'font-size': settings.fontSize
            });
        });
    };
})(jQuery);

// 사용법
$('.my-class').myPlugin({
    color: 'blue',
    fontSize: '16px'
});

고급 플러그인 패턴[편집 / 원본 편집]

// 객체 지향 플러그인 패턴
(function($) {
    function MyWidget(element, options) {
        this.element = $(element);
        this.options = $.extend({}, MyWidget.DEFAULTS, options);
        this.init();
    }
    
    MyWidget.DEFAULTS = {
        color: 'black',
        speed: 300
    };
    
    MyWidget.prototype = {
        init: function() {
            this.bindEvents();
            this.render();
        },
        
        bindEvents: function() {
            var self = this;
            this.element.on('click.mywidget', function() {
                self.toggle();
            });
        },
        
        render: function() {
            this.element.css('color', this.options.color);
        },
        
        toggle: function() {
            this.element.fadeToggle(this.options.speed);
        },
        
        destroy: function() {
            this.element.off('.mywidget');
            this.element.removeData('mywidget');
        }
    };
    
    // jQuery 플러그인으로 등록
    $.fn.myWidget = function(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data('mywidget');
            var options = typeof option == 'object' && option;
            
            if (!data) {
                $this.data('mywidget', (data = new MyWidget(this, options)));
            }
            if (typeof option == 'string') {
                data[option]();
            }
        });
    };
    
})(jQuery);

// 사용법
$('#myElement').myWidget({color: 'red'});
$('#myElement').myWidget('toggle');
$('#myElement').myWidget('destroy');

메소드 체이닝[편집 / 원본 편집]

jQuery의 메소드 체이닝은 코드를 간결하고 읽기 쉽게 만드는 핵심 기능이다.

체이닝의 기본 원리[편집 / 원본 편집]

// 체이닝 없이 작성한 코드
var $div = $('#myDiv');
$div.addClass('highlight');
$div.css('color', 'red');
$div.fadeIn(500);

// 메소드 체이닝으로 작성한 코드
$('#myDiv')
    .addClass('highlight')
    .css('color', 'red')
    .fadeIn(500);

// 더 복잡한 체이닝 예제
$('#myButton')
    .addClass('btn-primary')
    .attr('disabled', true)
    .text('Processing...')
    .animate({opacity: 0.7}, 300)
    .delay(2000)
    .animate({opacity: 1}, 300)
    .attr('disabled', false)
    .text('Complete!')
    .removeClass('btn-primary')
    .addClass('btn-success');

end()를 이용한 체이닝 제어[편집 / 원본 편집]

// end()로 이전 선택으로 돌아가기
$('#container')
    .find('p')              // container 내의 p 요소들 선택
        .addClass('highlight')
        .css('font-weight', 'bold')
    .end()                  // 다시 #container 선택
    .find('span')           // container 내의 span 요소들 선택
        .css('color', 'red')
    .end()                  // 다시 #container 선택
    .addClass('processed');

// addBack()으로 이전 선택과 현재 선택 합치기
$('#container')
    .find('p')
    .addBack()              // #container와 p 요소들 모두 선택
    .addClass('styled');

조건부 체이닝[편집 / 원본 편집]

// 조건부 메소드 체이닝
$('#myElement')
    .addClass('base-class')
    .css('display', 'block')
    [someCondition ? 'addClass' : 'removeClass']('conditional-class')
    .attr('title', someCondition ? 'Active' : 'Inactive');

// 함수를 이용한 조건부 체이닝
$.fn.conditionalMethod = function(condition, trueMethod, falseMethod) {
    return this[condition ? trueMethod : falseMethod]();
};

$('#myElement')
    .addClass('base')
    .conditionalMethod(isVisible, 'show', 'hide')
    .css('color', 'blue');

버전별 차이점[편집 / 원본 편집]

jQuery 1.x[편집 / 원본 편집]

jQuery 1.x는 2006년부터 2016년까지 개발된 버전으로, Internet Explorer 6-8 지원이 특징이다. live() 메소드를 사용했으나 현재는 deprecated되었고, $.browser를 통한 브라우저 감지 기능이 있었다가 jQuery 1.9에서 제거되었다.

// jQuery 1.x의 특징
// - IE 6-8 지원
// - live() 메소드 사용 (현재는 deprecated)
$('.button').live('click', function() {
    // IE 6-8에서도 동작
});

// 브라우저 감지 (jQuery 1.9에서 제거됨)
if ($.browser.msie && $.browser.version < 9) {
    // IE 8 이하에서만 실행
}

jQuery 2.x[편집 / 원본 편집]

jQuery 2.x는 2013년부터 2016년까지 개발된 버전으로, IE 6-8 지원을 중단했다. 이로 인해 성능이 향상되고 파일 크기가 약 12% 감소했다. API는 1.x와 동일하지만 더 빠른 성능을 제공한다.

jQuery 3.x[편집 / 원본 편집]

jQuery 3.x는 2016년부터 현재까지 개발되고 있는 최신 버전이다. Promise 호환성이 추가되어 $.get() 등의 AJAX 메소드가 .then().catch()를 지원한다. for...of 루프를 지원하고, requestAnimationFrame을 사용하며, CSS 커스텀 프로퍼티도 지원한다.

// jQuery 3.x의 새로운 기능들

// Promise 호환성
$.get('data.json')
    .then(function(data) {
        console.log('성공:', data);
    })
    .catch(function(error) {
        console.log('실패:', error);
    });

// for...of 루프 지원
var $elements = $('.items');
for (let element of $elements) {
    console.log(element); // DOM 요소
}

// requestAnimationFrame 사용
$('#element').show(); // 이제 requestAnimationFrame 사용

// CSS 커스텀 프로퍼티 지원
$('#element').css('--main-color', 'red');

// 더 엄격한 HTML 파싱
$('<div class="test"/>'); // 올바른 방식
// $('<div class=test>'); // 3.x에서는 오류 발생 가능

마이그레이션 가이드[편집 / 원본 편집]

1.x에서 3.x로 마이그레이션할 때 주의해야 할 사항들이 있다. size() 메소드가 제거되어 .length 속성을 사용해야 하고, andSelf() 메소드가 addBack()으로 변경되었으며, load() 이벤트 핸들러가 제거되어 on('load')를 사용해야 한다.

// 1.x에서 3.x로 마이그레이션 시 주의사항

// 1. size() 메소드 제거됨
// 잘못된 방법 (1.x)
var count = $('.items').size();

// 올바른 방법 (3.x)
var count = $('.items').length;

// 2. andSelf() 메소드 -> addBack()로 변경
// 잘못된 방법 (1.x)
$('#container').find('p').andSelf().addClass('highlight');

// 올바른 방법 (3.x)
$('#container').find('p').addBack().addClass('highlight');

// 3. load() 이벤트 핸들러 제거됨
// 잘못된 방법 (1.x)
$('#image').load(function() {
    console.log('이미지 로드됨');
});

// 올바른 방법 (3.x)
$('#image').on('load', function() {
    console.log('이미지 로드됨');
});

장점과 단점[편집 / 원본 편집]

jQuery의 장점[편집 / 원본 편집]

jQuery의 가장 큰 장점은 크로스 브라우저 호환성이다. 2006년 당시에는 각 브라우저마다 자바스크립트를 다르게 구현했기 때문에, 개발자들이 복잡한 브라우저 감지 코드를 작성해야 했다. jQuery는 이 문제를 한 줄의 코드로 해결해주었다.

간결한 문법도 큰 장점이다. 순수 자바스크립트로는 수십 줄이 필요한 작업을 jQuery로는 몇 줄로 처리할 수 있다. 풍부한 플러그인 생태계도 jQuery의 강점이다. jQuery UI를 비롯해 수천 개의 써드파티 플러그인들이 존재한다. 체인 방식의 직관적 API는 코드를 읽기 쉽고 작성하기 쉽게 만들어준다.

// jQuery 없이 (2006년 당시)
function addEvent(element, event, handler) {
    if (element.addEventListener) {
        element.addEventListener(event, handler, false);
    } else if (element.attachEvent) {
        element.attachEvent('on' + event, handler);
    } else {
        element['on' + event] = handler;
    }
}

// jQuery 사용
$('#element').on('click', handler); // 모든 브라우저에서 동작

jQuery의 단점[편집 / 원본 편집]

jQuery의 가장 큰 단점은 성능 오버헤드이다. jQuery를 거쳐서 DOM을 조작하는 것은 순수 자바스크립트보다 느리다. 파일 크기도 문제가 될 수 있다. jQuery 3.x는 약 85KB(압축 시 30KB)로, 모바일 환경에서는 부담이 될 수 있다. 사용하지 않는 기능도 포함되어 있어 비효율적이다.

DOM 조작 중심의 접근도 현재로서는 단점이다. jQuery는 명령형 프로그래밍 방식을 사용하는데, 현대적인 프레임워크들은 선언적 프로그래밍 방식을 사용한다. 전역 네임스페이스 오염도 문제가 될 수 있다. $ 기호가 전역 변수로 사용되어 다른 라이브러리와 충돌할 가능성이 있다.

// jQuery (느림)
$('#myDiv').hide();

// 순수 자바스크립트 (빠름)
document.getElementById('myDiv').style.display = 'none';

현재 상황과 대안[편집 / 원본 편집]

jQuery의 현재 상황[편집 / 원본 편집]

jQuery는 여전히 많은 웹사이트에서 사용되고 있지만, 신규 프로젝트에서는 사용률이 급격히 감소하고 있다. 2015년에는 전체 웹사이트의 78%가 사용했지만, 2024년 현재는 약 77%가 사용하고 있다. 수치상으로는 비슷해 보이지만 대부분이 레거시 사이트이다.

주요 변화로는 GitHub이 2018년부터 jQuery 제거를 시작했고, Bootstrap 5에서 jQuery 의존성을 완전히 제거했다. 대부분의 신규 프로젝트에서는 React, Vue.js, Angular 등을 사용한다.

jQuery가 불필요해진 이유[편집 / 원본 편집]

모던 브라우저의 표준화가 가장 큰 이유이다. 예전에는 브라우저마다 다른 API를 사용했지만, 현재는 document.querySelector(), element.classList, fetch() 등이 모든 모던 브라우저에서 지원된다.

ES6+ 기능들도 jQuery의 필요성을 줄였다. document.querySelectorAll().forEach()fetch().then().catch(), async/await 등으로 jQuery 없이도 간편하게 작업할 수 있다.

// jQuery 스타일
$('.items').each(function(index, element) {
    $(element).addClass('processed');
});

// 모던 자바스크립트
document.querySelectorAll('.items')
    .forEach(element => element.classList.add('processed'));

// jQuery AJAX
$.get('/api/users')
    .done(data => console.log(data))
    .fail(error => console.log(error));

// 모던 자바스크립트
fetch('/api/users')
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.log(error));

// 또는 async/await
async function getUsers() {
    try {
        const response = await fetch('/api/users');
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.log(error);
    }
}

jQuery 대안들[편집 / 원본 편집]

Vanilla JavaScript(순수 자바스크립트)가 가장 일반적인 대안이다. 모던 브라우저에서는 jQuery 없이도 충분히 간편하게 DOM을 조작할 수 있다.

경량 라이브러리들도 있다. Zepto.js는 jQuery API와 호환되면서 더 가볍고, Cash는 32KB를 6KB로 줄인 라이브러리이며, Umbrella JS는 4KB 크기의 초경량 라이브러리이다.

현대적 프레임워크들이 가장 인기 있는 대안이다. React는 컴포넌트 기반의 선언적 UI를 제공하고, Vue.js는 템플릿 기반의 직관적인 문법을 사용하며, Angular는 완전한 프레임워크 솔루션을 제공한다.

// React
function Button({ label, onClick }) {
    const [active, setActive] = useState(false);
    
    const handleClick = () => {
        setActive(!active);
        onClick();
    };
    
    return (
        <button 
            className={active ? 'active' : ''} 
            onClick={handleClick}
        >
            {label}
        </button>
    );
}

jQuery를 사용해야 하는 경우[편집 / 원본 편집]

jQuery가 여전히 유용한 경우들도 있다. 레거시 프로젝트 유지보수에서는 필수적이고, 빠른 프로토타이핑에는 여전히 유용하다. 간단한 웹사이트(블로그, 랜딩 페이지)에서는 jQuery가 오버헤드 없이 사용될 수 있고, jQuery 플러그인이 필수인 경우팀이 jQuery에 익숙한 경우에도 사용할 수 있다.

실용 예제[편집 / 원본 편집]

이미지 갤러리[편집 / 원본 편집]

<div class="gallery">
    <img src="thumb1.jpg" data-large="large1.jpg" alt="이미지 1">
    <img src="thumb2.jpg" data-large="large2.jpg" alt="이미지 2">
    <img src="thumb3.jpg" data-large="large3.jpg" alt="이미지 3">
</div>
<div id="modal" style="display: none;">
    <img id="modal-image" src="" alt="">
    <button id="close-modal">닫기</button>
</div>
$(document).ready(function() {
    // 썸네일 클릭 시 모달 열기
    $('.gallery img').click(function() {
        var largeSrc = $(this).data('large');
        var alt = $(this).attr('alt');
        
        $('#modal-image').attr('src', largeSrc).attr('alt', alt);
        $('#modal').fadeIn(300);
    });
    
    // 모달 닫기
    $('#close-modal, #modal').click(function(e) {
        if (e.target === this) {
            $('#modal').fadeOut(300);
        }
    });
    
    // ESC 키로 모달 닫기
    $(document).keyup(function(e) {
        if (e.keyCode === 27) { // ESC
            $('#modal').fadeOut(300);
        }
    });
});

동적 폼 필드 추가/제거[편집 / 원본 편집]

<form id="dynamic-form">
    <div class="field-group">
        <input type="text" name="items[]" placeholder="항목을 입력하세요">
        <button type="button" class="remove-field">제거</button>
    </div>
    <button type="button" id="add-field">필드 추가</button>
    <button type="submit">제출</button>
</form>
$(document).ready(function() {
    var fieldIndex = 1;
    
    // 필드 추가
    $('#add-field').click(function() {
        var newField = $(`
            <div class="field-group">
                <input type="text" name="items[]" placeholder="항목을 입력하세요">
                <button type="button" class="remove-field">제거</button>
            </div>
        `);
        
        $(this).before(newField);
        newField.find('input').focus();
        fieldIndex++;
    });
    
    // 필드 제거 (이벤트 위임 사용)
    $(document).on('click', '.remove-field', function() {
        var $fieldGroup = $(this).closest('.field-group');
        
        if ($('.field-group').length > 1) {
            $fieldGroup.fadeOut(300, function() {
                $(this).remove();
            });
        } else {
            alert('최소 하나의 필드는 있어야 합니다.');
        }
    });
    
    // 폼 제출
    $('#dynamic-form').submit(function(e) {
        e.preventDefault();
        
        var items = [];
        $('input[name="items[]"]').each(function() {
            var value = $(this).val().trim();
            if (value) {
                items.push(value);
            }
        });
        
        console.log('제출된 항목들:', items);
        
        if (items.length === 0) {
            alert('최소 하나의 항목을 입력하세요.');
            return;
        }
        
        // 실제 서버 전송
        $.post('/submit', { items: items })
            .done(function(response) {
                alert('성공적으로 제출되었습니다!');
            })
            .fail(function() {
                alert('제출 중 오류가 발생했습니다.');
            });
    });
});

무한 스크롤[편집 / 원본 편집]

<div id="content">
    <div class="item">항목 1</div>
    <div class="item">항목 2</div>
</div>
<div id="loading" style="display: none;">로딩 중...</div>
$(document).ready(function() {
    var page = 1;
    var loading = false;
    var hasMore = true;
    
    function loadMore() {
        if (loading || !hasMore) return;
        
        loading = true;
        $('#loading').show();
        
        $.ajax({
            url: '/api/items',
            data: { page: page },
            method: 'GET',
            dataType: 'json'
        })
        .done(function(response) {
            if (response.items.length === 0) {
                hasMore = false;
                $('#loading').text('더 이상 항목이 없습니다.');
                return;
            }
            
            response.items.forEach(function(item) {
                var $item = $('<div class="item">' + item.title + '</div>');
                $('#content').append($item);
            });
            
            page++;
        })
        .fail(function() {
            alert('데이터를 불러오는 중 오류가 발생했습니다.');
        })
        .always(function() {
            loading = false;
            $('#loading').hide();
        });
    }
    
    // 스크롤 이벤트
    $(window).scroll(function() {
        if ($(window).scrollTop() + $(window).height() >= 
            $(document).height() - 100) {
            loadMore();
        }
    });
    
    // 초기 로드
    loadMore();
});

실시간 검색[편집 / 원본 편집]

<input type="text" id="search-input" placeholder="검색어를 입력하세요">
<div id="search-results"></div>
$(document).ready(function() {
    var searchTimer;
    var currentXHR;
    
    $('#search-input').on('input', function() {
        var query = $(this).val().trim();
        
        // 이전 타이머 클리어
        clearTimeout(searchTimer);
        
        // 이전 요청 취소
        if (currentXHR) {
            currentXHR.abort();
        }
        
        if (query.length < 2) {
            $('#search-results').empty();
            return;
        }
        
        // 500ms 후에 검색 실행 (디바운싱)
        searchTimer = setTimeout(function() {
            search(query);
        }, 500);
    });
    
    function search(query) {
        $('#search-results').html('<div>검색 중...</div>');
        
        currentXHR = $.ajax({
            url: '/api/search',
            data: { q: query },
            method: 'GET',
            dataType: 'json'
        })
        .done(function(response) {
            displayResults(response.results);
        })
        .fail(function(xhr) {
            if (xhr.statusText !== 'abort') {
                $('#search-results').html('<div>검색 중 오류가 발생했습니다.</div>');
            }
        });
    }
    
    function displayResults(results) {
        var $container = $('#search-results');
        $container.empty();
        
        if (results.length === 0) {
            $container.html('<div>검색 결과가 없습니다.</div>');
            return;
        }
        
        results.forEach(function(result) {
            var $item = $(`
                <div class="search-item">
                    <h3>${result.title}</h3>
                    <p>${result.description}</p>
                </div>
            `);
            
            $item.click(function() {
                window.location.href = result.url;
            });
            
            $container.append($item);
        });
    }
    
    // 검색 결과 외부 클릭 시 숨기기
    $(document).click(function(e) {
        if (!$(e.target).closest('#search-input, #search-results').length) {
            $('#search-results').empty();
        }
    });
});

탭 인터페이스[편집 / 원본 편집]

<div class="tab-container">
    <ul class="tab-menu">
        <li class="tab-item active" data-tab="tab1">탭 1</li>
        <li class="tab-item" data-tab="tab2">탭 2</li>
        <li class="tab-item" data-tab="tab3">탭 3</li>
    </ul>
    <div class="tab-content">
        <div id="tab1" class="tab-panel active">탭 1 내용</div>
        <div id="tab2" class="tab-panel">탭 2 내용</div>
        <div id="tab3" class="tab-panel">탭 3 내용</div>
    </div>
</div>
$(document).ready(function() {
    $('.tab-item').click(function() {
        var tabId = $(this).data('tab');
        
        // 모든 탭 비활성화
        $('.tab-item').removeClass('active');
        $('.tab-panel').removeClass('active').hide();
        
        // 클릭된 탭 활성화
        $(this).addClass('active');
        $('#' + tabId).addClass('active').fadeIn(300);
        
        // URL 업데이트 (선택사항)
        history.pushState(null, null, '#' + tabId);
    });
    
    // 페이지 로드 시 URL 해시에 따라 탭 선택
    function initializeTab() {
        var hash = window.location.hash.substring(1);
        if (hash && $('#' + hash).length) {
            $('.tab-item[data-tab="' + hash + '"]').click();
        }
    }
    
    // 뒤로가기/앞으로가기 지원
    $(window).on('hashchange', function() {
        initializeTab();
    });
    
    initializeTab();
});

같이 보기[편집 / 원본 편집]

외부 링크[편집 / 원본 편집]