Syw.Frontend

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

1๋‹จ๊ณ„. Vue 2 + Express + Axios ์ธ์ฆ ๊ธฐ๋ฐ˜ ํˆฌ๋‘์•ฑ ๋งŒ๋“ค๊ธฐ

1-11. ๋กœ๊ทธ์ธ ์—†์ด ์‚ฌ์šฉ์ž ๊ตฌ๋ถ„ ์œ ์ง€ํ•˜๊ธฐ

๐Ÿ“Œ localStorage ๊ธฐ๋ฐ˜ ์‚ฌ์šฉ์ž ์‹๋ณ„์ž ์ž๋™ ๋ฐœ๊ธ‰ ๋ฐ ์œ ์ง€

๐ŸŽฏ ํ•™์Šต ๋ชฉํ‘œ

  • ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์•„๋„ ๊ณ ์œ  userId๋ฅผ ๋ถ€์—ฌํ•˜๊ณ  ์œ ์ง€ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ ๋‹ค.
  • API ์š”์ฒญ ์‹œ ํ•ญ์ƒ ํ•ด๋‹น userId๋ฅผ ํฌํ•จ์‹œ์ผœ ์‚ฌ์šฉ์ž๋ณ„ ๋ฐ์ดํ„ฐ ๊ฒฉ๋ฆฌ๋ฅผ ์œ ์ง€ํ•œ๋‹ค.

โœ… 1. ์™œ userId๋ฅผ ์ €์žฅํ•ด์•ผ ํ•˜๋‚˜์š”?

์‚ฌ์šฉ์ž A๊ฐ€ ํ•  ์ผ์„ ์ž…๋ ฅํ–ˆ๋Š”๋ฐ,

ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๊ฑฐ๋‚˜ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๊ป๋‹ค ์ผœ๋ฉด userId๊ฐ€ ์‚ฌ๋ผ์ ธ์„œ

์ƒˆ ์‚ฌ์šฉ์ž๊ฐ€ ๋œ ๊ฒƒ์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

โ†’ ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๋ ค๋ฉด ๋ธŒ๋ผ์šฐ์ €์— userId๋ฅผ ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


โœ… 2. ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: localStorage์— userId ์ €์žฅ

1. userId๋ฅผ localStorage์—์„œ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

2. ์—†์œผ๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑํ•˜์—ฌ ์ €์žฅ

function getOrCreateUserId() {
  const KEY = 'todo-user-id'
  let userId = localStorage.getItem(KEY)

  if (!userId) {
    userId = 'user-' + Date.now()
    localStorage.setItem(KEY, userId)
  }

  return userId
}

โœ… 3. App.vue์—์„œ ์ ์šฉํ•˜๊ธฐ

๐Ÿ“„ src/App.vue

<template>
  <div class="todo-container">
    <h2>๐Ÿงพ ํ•  ์ผ ๊ด€๋ฆฌ (์ˆ˜์ • ํฌํ•จ)</h2>
    <div class="form">
      <input v-model="newTodo" placeholder="ํ•  ์ผ์„ ์ž…๋ ฅํ•˜์„ธ์š”" />
      <button @click="addTodo">์ถ”๊ฐ€</button>
    </div>
    <ul class="todo-list">
      <TodoItem
        v-for="todo in todos"
        :key="todo.id"
        :content="todo.title"
        :editing="editingId === todo.id"
        @delete="removeTodo(todo.id)"
        @edit="startEdit(todo.id)"
        @update="updateTodo(todo.id, $event)"
      />
    </ul>
  </div>
</template>

<script>
import axios from 'axios';
import TodoItem from './components/TodoItem.vue';

const API_URL = '/api/todos';

export default {
  components: { TodoItem },
  data() {
    return {
      newTodo: '',
      todos: [],
      editingId: null,
      userId: null, // userId๋ฅผ ์ƒํƒœ๋กœ ๊ด€๋ฆฌ
    };
  },
  mounted() {
    this.userId = this.getOrCreateUserId();
    this.fetchTodos();
  },
  methods: {
    getOrCreateUserId() {
      const KEY = 'todo-user-id';
      let id = localStorage.getItem(KEY);
      if (!id) {
        id = 'user-' + Date.now();
        localStorage.setItem(KEY, id);
      }
      return id;
    },
    async fetchTodos() {
      const res = await axios.get(`${API_URL}?user=${this.userId}`);
      this.todos = res.data;
    },
    async addTodo() {
      const title = this.newTodo.trim();
      if (!title) return;
      const res = await axios.post(API_URL, {
        title,
        completed: false,
        userId: this.userId,
      });
      this.todos.unshift(res.data);
      this.newTodo = '';
    },
    async removeTodo(id) {
      await axios.delete(`${API_URL}/${id}?user=${this.userId}`);
      this.todos = this.todos.filter((todo) => todo.id !== id);
    },
    startEdit(id) {
      this.editingId = id;
    },
    async updateTodo(id, newTitle) {
      if (newTitle === null) {
        this.editingId = null;
        return;
      }
      const title = newTitle.trim();
      if (!title) return;

      await axios.put(`${API_URL}/${id}`, { title, userId: this.userId });
      this.todos = this.todos.map((todo) =>
        todo.id === id ? { ...todo, title } : todo
      );
      this.editingId = null;
    },
  },
};
</script>

<style scoped>
.todo-container {
  max-width: 480px;
  margin: 40px auto;
  padding: 24px;
  border: 1px solid #ccc;
  border-radius: 12px;
  background: #f9f9f9;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
}

.form {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
}

input {
  flex: 1;
  padding: 8px;
  border: 1px solid #aaa;
  border-radius: 6px;
  font-size: 14px;
}

button {
  padding: 8px 16px;
  background-color: #42b983;
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
}

button:hover {
  background-color: #36956d;
}

ul {
  list-style: none;
  padding: 0;
}
</style>

โœ… ๋ฐ˜์˜๋œ ํ•ต์‹ฌ ๋ณ€๊ฒฝ์  ์š”์•ฝ

ํ•ญ๋ชฉ ๋ณ€๊ฒฝ ๋‚ด์šฉ
userId ์ƒ์„ฑ localStorage์— ์ €์žฅ (์—†์œผ๋ฉด ์ƒ์„ฑ)
userId ์‚ฌ์šฉ ๋ชจ๋“  Axios ์š”์ฒญ์— ํฌํ•จ
๊ธฐ์กด ๊ตฌ์กฐ ๋ฐ˜์˜ TodoItem.vue์™€ ์ˆ˜์ • ๊ธฐ๋Šฅ ์œ ์ง€

โœ… 4. ์ž‘๋™ ํ๋ฆ„ ์š”์•ฝ

๋™์ž‘ ์„ค๋ช…
์•ฑ ์‹œ์ž‘ ์‹œ localStorage์— userId๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ
์—†์œผ๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑ (ex. user-1721437827613) ํ›„ ์ €์žฅ
API ์š”์ฒญ ํ•ญ์ƒ ํ•ด๋‹น userId๋ฅผ ํฌํ•จ์‹œ์ผœ ์„œ๋ฒ„์— ์š”์ฒญ
์„œ๋ฒ„ todosByUser[userId] ๊ธฐ์ค€์œผ๋กœ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ

๐Ÿง  ์š”์•ฝ

ํ•ญ๋ชฉ ์„ค๋ช…
์‚ฌ์šฉ์ž ์‹๋ณ„ localStorage์— userId ์ €์žฅ
์œ ์ง€ ๋ฐฉ์‹ ์ƒˆ๋กœ๊ณ ์นจ/์žฌ๋ฐฉ๋ฌธ ์‹œ์—๋„ ๊ฐ™์€ userId ์‚ฌ์šฉ
์„œ๋ฒ„ ์—ฐ๋™ ์š”์ฒญ ์‹œ ํ•ญ์ƒ userId ํฌํ•จํ•˜์—ฌ ๋ฐ์ดํ„ฐ ๋ถ„๋ฆฌ ์œ ์ง€

FE ์ €์žฅ์†Œ

GitHub - heroyooi/vue2-app at ch11

๐Ÿ’ฌ ๋Œ“๊ธ€

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