Syw.Frontend

๐Ÿงฉ Vue 2๋กœ ๋ฐฐ์šฐ๋Š” ํ”„๋ก ํŠธ์—”๋“œ ๊ธฐ์ดˆ

2๋‹จ๊ณ„. ๋„ฅ์ŠคํŠธ๊ณต๋ฌด์› ๋ฉ”์ธ Vue + Express๋กœ ๋กœ ํด๋ก ํ•˜๊ธฐ

2-3-๊ณผ์ œ. ๋ฉ”์ธ ํƒญ + ์Šฌ๋ผ์ด๋“œ ์—ฐ๋™ UI ๊ตฌํ˜„

โœ… ๊ณผ์ œ 1 ๊ตฌํ˜„ โ€“ MainTabs.vue, abSlide.vue ์ฝ”๋“œ ์ˆ˜์ •

๐Ÿ” MainTabs.vue ๋‚ด๋ถ€ ๋ณ€๊ฒฝ:

<script>
import TabSlide from "./TabSlide.vue";

export default {
  components: { TabSlide },
  data() {
    return {
      currentTab: 0,
      renderKey: 0,
      mounted: true,
      tabs: [
        {
          label: "๊ตญ์–ด",
          slides: [
            {
              img: "https://img.megagong.net/profphoto/gong/gilltoraebi/main_intro.png",
              alt: "์ด์œค์ฃผ",
              subject: "๊ตญ์–ด",
            },
            {
              img: "https://img.megagong.net/profphoto/gong/jhssam1003/main_intro.png",
              alt: "์ž„์ง€ํ˜œ",
              subject: "๊ตญ์–ด",
            },
          ],
        },
        {
          label: "์˜์–ด",
          slides: [
            {
              img: "https://img.megagong.net/profphoto/gong/tjenglish08/main_intro.png",
              alt: "์กฐํƒœ์ •",
              subject: "์˜์–ด",
            },
            {
              img: "https://img.megagong.net/profphoto/gong/megamega2/main_intro.png",
              alt: "์„ฑ์ •ํ˜œ",
              subject: "์˜์–ด",
            },
            {
              img: "https://img.megagong.net/profphoto/gong/incredvoc/main_intro.png",
              alt: "๊ฒฝ์„ ์‹",
              subject: "์˜์–ด",
            },
            {
              img: "https://img.megagong.net/profphoto/gong/nojoony1/main_intro.png",
              alt: "๋ฐ•๋…ธ์ค€",
              subject: "์˜์–ด",
            },
          ],
        },
        {
          label: "ํ•œ๊ตญ์‚ฌ",
          slides: [
            {
              img: "https://img.megagong.net/profphoto/gong/gosabu88/main_intro.png",
              alt: "๊ณ ์ข…ํ›ˆ",
              subject: "ํ•œ๊ตญ์‚ฌ",
            },
            {
              img: "https://img.megagong.net/profphoto/gong/vision0911/main_intro.png",
              alt: "๋ผ์˜ํ™˜",
              subject: "ํ•œ๊ตญ์‚ฌ",
            },
            {
              img: "https://img.megagong.net/profphoto/gong/duwo20405/main_intro.png",
              alt: "์ตœ์˜์žฌ",
              subject: "ํ•œ๊ตญ์‚ฌ",
            },
            {
              img: "https://img.megagong.net/profphoto/gong/jeonhangil/main_intro.png",
              alt: "์ „ํ•œ๊ธธ",
              subject: "ํ•œ๊ตญ์‚ฌ",
            },
          ],
        },
        {
          label: "ํ–‰์ •ํ•™",
          slides: [
            {
              img: "https://img.megagong.net/profphoto/gong/plower3362/main_intro.png",
              alt: "ํ™ฉ์ฒ ๊ณค",
              subject: "ํ–‰์ •ํ•™",
            },
            {
              img: "https://img.megagong.net/profphoto/gong/happy0308/main_intro.png",
              alt: "์ด์ƒํ—Œ",
              subject: "ํ–‰์ •ํ•™",
            },
            {
              img: "https://img.megagong.net/profphoto/gong/shin242/main_intro.png",
              alt: "์‹ ์šฉํ•œ",
              subject: "ํ–‰์ •ํ•™",
            },
          ],
        },
        {
          label: "ํ–‰์ •๋ฒ•",
          slides: [
            {
              img: "https://img.megagong.net/profphoto/gong/allawyer/main_intro.png",
              alt: "์œ ํœ˜์šด",
              subject: "ํ–‰์ •๋ฒ•",
            },
            {
              img: "https://img.megagong.net/profphoto/gong/wjsgywls1/main_intro.png",
              alt: "์ „ํšจ์ง„",
              subject: "ํ–‰์ •๋ฒ•",
            },
            {
              img: "https://img.megagong.net/profphoto/gong/geek2991/main_intro.png",
              alt: "์ •์ธ๊ตญ",
              subject: "ํ–‰์ •๋ฒ•",
            },
            {
              img: "https://img.megagong.net/profphoto/gong/ysw7/main_intro.png",
              alt: "์–‘์Šน์šฐ",
              subject: "ํ–‰์ •๋ฒ•",
            },
          ],
        },
      ],
    };
  },
  methods: {
    changeTab(index) {
      this.currentTab = index;
      this.mounted = false;
      this.$nextTick(() => {
        this.renderKey++;
        this.mounted = true;
      });
    },
  },
};
</script>

๐Ÿ” TabSlide.vue <swiper-slide> ๋‚ด๋ถ€ ๋ณ€๊ฒฝ:

<swiper-slide v-for="(item, i) in slides" :key="i" class="teacher-slide">
  <div class="teacher-box">
    <img :src="item.img" :alt="item.name" />
    <p class="teacher-name">{{ item.name }}</p>
    <p class="teacher-subject">{{ item.subject }}</p>
  </div>
</swiper-slide>

โœ… ์Šคํƒ€์ผ ์ถ”๊ฐ€

.teacher-subject {
  font-size: 13px;
  color: #666;
  margin-top: 4px;
}

โœ… ๊ณผ์ œ 2. ๋ฐ˜์‘ํ˜• ์ฒ˜๋ฆฌ (768px ์ดํ•˜์—์„œ ์Šฌ๋ผ์ด๋“œ ์ˆ˜ ์ค„์ด๊ธฐ)

์ด๋ฏธ swiperOption์— ๋ฐ˜์‘ํ˜•์„ ๋„ฃ์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค:

swiperOption: {
  slidesPerView: 4,
  spaceBetween: 10,
  pagination: {
    el: ".swiper-pagination",
  },
  loop: false,
  breakpoints: {
    1200: { slidesPerView: 4 },
    1024: { slidesPerView: 3 },
    768: { slidesPerView: 2 },
    480: { slidesPerView: 1 },
  },
},

โœ… ์ตœ์ข… ์ ์šฉ ์ •๋ฆฌ

๐Ÿ” TabSlide.vue ์™„์„ฑ ์˜ˆ์‹œ

<template>
  <swiper ref="mySwiper" :options="swiperOption" class="swiper-container">
    <swiper-slide v-for="(item, i) in slides" :key="i" class="teacher-slide">
      <div class="teacher-box">
        <img :src="item.img" :alt="item.alt" />
        <p class="teacher-name">{{ item.alt }}</p>
        <p class="teacher-subject">{{ item.subject }}</p>
      </div>
    </swiper-slide>
    <div class="swiper-pagination" slot="pagination"></div>
  </swiper>
</template>

<script>
export default {
  props: {
    slides: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      swiperOption: {
        slidesPerView: 4,
        spaceBetween: 10,
        pagination: {
          el: ".swiper-pagination",
        },
        loop: false,
        breakpoints: {
          1200: { slidesPerView: 4 },
          1024: { slidesPerView: 3 },
          768: { slidesPerView: 2 },
          480: { slidesPerView: 1 },
        },
      },
    };
  },
  watch: {
    slides() {
      this.$nextTick(() => {
        const swiper = this.$refs.mySwiper?.swiper;
        if (swiper) {
          swiper.update();
          swiper.slideTo(0);
        }
      });
    },
  },
};
</script>

<style scoped>
.swiper-container {
  width: 100%;
  overflow: hidden;
}

.swiper-slide {
  /* width: auto !important; */
  flex-shrink: 0;
  display: flex;
  justify-content: center;
}

.teacher-box {
  width: 100%;
  max-width: 200px;
  background: #fff;
  border: 1px solid #eee;
  border-radius: 8px;
  padding: 12px;
  text-align: center;
}

.teacher-box:hover {
  transform: translateY(-4px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.teacher-box img {
  width: 100%;
  height: auto;
  border-radius: 4px;
  margin-bottom: 8px;
}

.teacher-name {
  font-size: 14px;
  font-weight: 600;
  color: #333;
}

.teacher-subject {
  font-size: 13px;
  color: #666;
  margin-top: 4px;
}
</style>

โœ… ์ด์ œ ์ด๋ฆ„๊ณผ ๊ณผ๋ชฉ์ด ์Šฌ๋ผ์ด๋“œ์— ์ž˜ ํ‘œ์‹œ๋˜๊ณ ,

โœ… 768px ์ดํ•˜์—์„œ ์Šฌ๋ผ์ด๋“œ ์ˆ˜๊ฐ€ ์ž๋™์œผ๋กœ ์ค„์–ด๋“ค๋ฉฐ ๋ฐ˜์‘ํ˜•์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

GitHub - heroyooi/vue2-megagong at ch3_resolve

๐Ÿ’ฌ ๋Œ“๊ธ€

    โ€ป ๋กœ๊ทธ์ธ ํ›„ ๋Œ“๊ธ€์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.