27. tabSlide

탭(Tab) 메뉴 UI 내부에서 슬릭(Slick) 라이브러리를 활용한 캐러셀(Carousel, 슬라이드)과 매그니픽 팝업(Magnific Popup) 라이브러리를 활용한 라이트박스(Lightbox, 이미지 확대 모달)를 결합하는 구현 방법을 다룬다. 탭 전환 시 보이지 않는 영역의 슬라이더가 겪는 너비 계산 오류를 해결하고, 각 탭에 독립된 슬라이더와 팝업 기능을 안정적으로 연동하는 과정을 설명한다. HTML, CSS, 자바스크립트(JavaScript)로 나누어 마크업 구조부터 플러그인 재초기화 로직까지 단계별로 살펴본다.

탭 슬라이더 및 팝업 중첩 구조 — 탭 메뉴 구조 내부에 캐러셀 슬라이더와 라이트박스 팝업을 연동한다.

예제

미리보기

제공된 링크를 통해 탭 버튼 클릭에 따른 슬라이드 뷰 전환과 이미지 클릭 시 실행되는 라이트박스 팝업 동작을 미리 확인할 수 있다.

HTML

탭 메뉴와 슬라이더, 팝업 링크를 구성하는 마크업이다. 탭 전환 버튼과 탭별 콘텐츠 영역을 각각 독립된 요소로 정의하고 외부 라이브러리 스타일과 스크립트를 연결한다.

<!DOCTYPE html>
<html>
  <head>
    <title>Carousel with Tabs and Lightbox</title>
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.css" />
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/magnific-popup.js/1.1.0/magnific-popup.min.css" />
    <link rel="stylesheet" href="css.css" />
  </head>

  <body>
    <div class="tabs">
      <div class="tab-button-outer">
        <div id="tab1" class="tab-button active">Tab 1</div>
        <div id="tab2" class="tab-button">Tab 2</div>
        <div id="tab3" class="tab-button">Tab 3</div>
      </div>
      <div id="content1" class="tab-content active carousel">
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/1.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/1.jpg" /></div>
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/2.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/2.jpg" /></div>
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/3.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/3.jpg" /></div>
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/4.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/4.jpg" /></div>
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/5.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/5.jpg" /></div>
      </div>
      <div id="content2" class="tab-content carousel">
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/5.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/5.jpg" /></div>
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/6.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/6.jpg" /></div>
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/7.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/7.jpg" /></div>
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/8.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/8.jpg" /></div>
      </div>
      <div id="content3" class="tab-content carousel">
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/1.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/1.jpg" /></div>
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/2.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/2.jpg" /></div>
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/3.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/3.jpg" /></div>
        <div class="popup-outer"><a href="http://qwerew.cafe24.com/images/4.jpg" class="popup">클릭</a><img src="http://qwerew.cafe24.com/images/4.jpg" /></div>
      </div>
    </div>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/magnific-popup.js/1.1.0/jquery.magnific-popup.min.js"></script>
    <script src="js.js"></script>
  </body>
</html>

HTML 구조의 주요 구성 요소는 다음과 같다.

  • 외부 플러그인 로드: <head> 영역에서 슬릭 캐러셀과 매그니픽 팝업에 필요한 CSS 스타일시트를 CDN(콘텐츠 전송 네트워크) 방식으로 연결한다. <body> 하단에서는 제이쿼리 라이브러리와 각 플러그인의 자바스크립트 파일을 순서대로 로드한다.
  • 탭 버튼 구조: .tab-button-outer 내부에 #tab1, #tab2, #tab3 ID를 가진 탭 버튼 요소를 배치한다. 첫 번째 버튼에는 기본적으로 활성화 상태를 나타내는 active 클래스를 부여한다.
  • 탭 콘텐츠 및 슬라이더 구조: #content1, #content2, #content3 컨테이너는 각 탭의 내용을 담는다. carousel 클래스를 함께 지정하여 슬라이더로 동작하도록 구성하고, 내부에는 이미지와 .popup 클래스를 가진 <a> 링크를 배치하여 라이트박스 팝업이 연결되도록 설계한다.

CSS

탭 버튼의 정렬 및 활성화 상태 스타일, 탭 콘텐츠의 표시와 숨김 여부, 그리고 슬라이더와 팝업 버튼의 레이아웃을 지정하는 스타일시트다.

.tabs {
  margin: 50px;
}

.tab-button-outer {
  display: flex;
  justify-content: center;
}

.tab-button {
  background-color: #ccc;
  padding: 10px 20px;
  cursor: pointer;
}

.tab-button:hover {
  background-color: #ddd;
}

.tab-button.active {
  background-color: #bbb;
}

.tab-content {
  display: none;
}

.tab-content.active {
  display: block;
}

.carousel {
  width: 300px;
  margin: 50px auto;
}

.carousel img {
  width: 100%;
  margin: 0 auto;
}
.popup{position:absolute; display:block; background:white;}

CSS 스타일의 핵심 속성은 다음과 같다.

  • 탭 버튼 플렉스 정렬: .tab-button-outer에 display: flex;와 justify-content: center;를 적용하여 탭 버튼들을 가로 방향 중앙에 배치한다.
  • 탭 활성화 상태: .tab-button.active 선택자로 활성화된 탭 버튼의 배경색을 #bbb로 변경하여 비활성 버튼과 시각적으로 구분한다.
  • 탭 콘텐츠 전환 제어: .tab-content는 기본적으로 display: none;을 지정하여 숨기고, .active 클래스가 추가된 요소만 display: block;으로 변경하여 화면에 표시한다.
  • 슬라이더 레이아웃: .carousel 요소의 너비를 300px로 고정하고 margin: 50px auto;를 통해 화면 중앙에 정렬한다. 내부 이미지에는 width: 100%;를 지정하여 슬라이더 너비에 맞춘다.
  • 팝업 트리거 버튼: .popup 링크에 position: absolute;를 적용하여 이미지 위에 겹쳐서 표시되도록 위치를 지정한다.

JS

초기 탭의 슬라이더를 구동하고, 탭 버튼 클릭 시 해당 탭의 슬라이더 인스턴스를 정상적으로 재생성하며, 이미지 클릭 시 매그니픽 팝업을 실행하는 자바스크립트 코드다.

$('#content1.carousel').slick({ slidesToShow: 2 });

$('.tab-button').on('click', function () {
  const tabButtonId = $(this).attr('id');
  $('.tab-button').removeClass('active');
  $('.tab-content').removeClass('active');

  $(this).addClass('active');
  //변수tabButtonId 의 값을 이용하여 content1 로 편집
  const targetContent = $('#' + 'content' + tabButtonId.slice(-1));
  targetContent.addClass('active');

  if (targetContent.hasClass('slick-initialized')) {
    targetContent.slick('unslick');
  }
  targetContent.slick({ slidesToShow: 2 });
});

$('.popup').magnificPopup({ type: 'image' });

slice() 는 문자열의 일부를 추출하여 새로운 문자를 반환하는 메서드
MDN

자바스크립트 로직의 동작 과정과 주의해야 할 지점은 다음과 같다.

  • 첫 번째 탭 슬라이더 초기화: 페이지 로드 시 화면에 기본 표시되어 있는 #content1.carousel 요소에 slick({ slidesToShow: 2 })를 호출하여 슬라이더를 초기화한다. slidesToShow: 2 옵션은 한 화면에 동시에 2장의 슬라이드가 보이도록 설정하는 옵션이다.
  • 탭 클릭 이벤트 및 식별자 추출: .tab-button 클릭 시 $(this).attr('id')로 클릭된 버튼의 ID(예: tab1, tab2)를 가져온 뒤, tabButtonId.slice(-1)를 실행하여 맨 끝 숫자만 추출한다. 이를 통해 연결할 콘텐츠 ID(#content1 등)를 동적으로 조합한다.
  • 숨김 상태 탭과 슬라이더 너비 계산 문제: 슬릭과 같은 캐러셀 라이브러리는 초기화 시점에 브라우저 렌더링 엔진으로부터 부모 요소의 실제 너비를 측정하여 슬라이드 너비를 계산한다. display: none; 상태인 탭 내부에서는 요소의 크기가 0으로 인식되므로, 슬라이더가 찌그러지거나 레이아웃이 깨지는 문제가 발생한다.
  • 슬라이더 파기 및 재초기화: 대상 탭에 active 클래스를 먼저 부여하여 display: block; 상태로 만든 후, 해당 요소가 이미 슬릭에 의해 초기화된 적이 있는지 hasClass('slick-initialized')로 판별한다. 이미 초기화된 상태라면 slick('unslick') 메서드로 기존 인스턴스를 완전히 제거한 뒤 다시 slick({ slidesToShow: 2 })를 실행하여 정상적인 크기로 슬라이더를 다시 생성한다.
  • 라이트박스 팝업 바인딩: $('.popup').magnificPopup({ type: 'image' })를 실행하여 .popup 클래스를 가진 <a> 태그 클릭 시 href 속성에 지정된 원본 이미지가 모달 창으로 확대 표시되도록 설정한다.

댓글 남기기