Syw.Frontend

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

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

2-9-๊ณผ์ œ. StickyPanel.vue - Express API + Axios + Vuex ๊ตฌ์กฐ ๋ฆฌํŒฉํ† ๋ง

โœ… 1. ๋ฐฑ์—”๋“œ (vue2-megagong-api)

๐Ÿ“„ data/stickyPanel.json

[
  {
    "img": "https://img.megagong.net/m/2025/0801_sticky/sticky1.png",
    "link": "https://megagong.net/event/sticky1"
  },
  {
    "img": "https://img.megagong.net/m/2025/0801_sticky/sticky2.png",
    "link": "https://megagong.net/event/sticky2"
  }
]

๐Ÿ“„ routes/stickyPanel.js

const express = require("express");
const router = express.Router();
const fs = require("fs");
const path = require("path");

router.get("/", (req, res) => {
  const filePath = path.join(__dirname, "../data/stickyPanel.json");
  fs.readFile(filePath, "utf8", (err, data) => {
    if (err) return res.status(500).send("Error loading sticky panel");
    res.json(JSON.parse(data));
  });
});

module.exports = router;

๐Ÿ“„ server.js ๋“ฑ๋ก

const stickyPanelRoutes = require("./routes/stickyPanel");
app.use("/api/sticky-panel", stickyPanelRoutes);

โœ… 2. ํ”„๋ก ํŠธ์—”๋“œ (vue2-megagong)

๐Ÿ“ store/modules/stickyPanel.js

import axios from "@/libs/axios";

export default {
  namespaced: true,
  state: () => ({
    items: [],
  }),
  mutations: {
    SET_ITEMS(state, payload) {
      state.items = payload;
    },
  },
  actions: {
    async fetchStickyPanel({ commit }) {
      try {
        const res = await axios.get("/api/sticky-panel");
        commit("SET_ITEMS", res.data);
      } catch (err) {
        console.error("์Šคํ‹ฐํ‚ค ํŒจ๋„ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์‹คํŒจ", err);
      }
    },
  },
};

๐Ÿ“„ store/index.js์— ๋“ฑ๋ก

import Vue from "vue";
import Vuex from "vuex";
import stickyPanel from "./modules/stickyPanel";

Vue.use(Vuex);

export default new Vuex.Store({
  modules: {
    stickyPanel,
  },
});

โœ… 3. ์ปดํฌ๋„ŒํŠธ StickyPanel.vue ๋ฆฌํŒฉํ† ๋ง

<template>
  <div class="sticky-panel">
    <a
      v-for="(item, i) in stickyItems"
      :key="i"
      :href="item.link"
      target="_blank"
      rel="noopener noreferrer"
    >
      <img :src="item.img" :alt="`์Šคํ‹ฐํ‚ค ๋ฐฐ๋„ˆ ${i + 1}`" />
    </a>
  </div>
</template>

<script>
import { mapState } from "vuex";

export default {
  name: "StickyPanel",
  computed: {
    ...mapState("stickyPanel", ["items"]),
    stickyItems() {
      return this.items;
    },
  },
  created() {
    this.$store.dispatch("stickyPanel/fetchStickyPanel");
  },
};
</script>

<style scoped>
.sticky-panel {
  position: sticky;
  top: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.sticky-panel img {
  width: 100%;
  border-radius: 8px;
}
</style>

FE ์ €์žฅ์†Œ

GitHub - heroyooi/vue2-megagong at ch9_resolve

BE ์ €์žฅ์†Œ

GitHub - heroyooi/vue2-megagong at ch9_resolve

๐Ÿ’ฌ ๋Œ“๊ธ€

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