๐ 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 ์ ์ฅ์
๐ฌ ๋๊ธ
โป ๋ก๊ทธ์ธ ํ ๋๊ธ์ ์์ฑํ ์ ์์ต๋๋ค.