Firebase Hosting으로 실제 웹 서비스 오픈까지 진행합니다.
서비스 오픈까지 개발을 진행하면서 계속 수정, 변경될 것입니다.
회원의 업체 관리
회원은 여러 업체를 등록할 수 있습니다.
회원이 등록한 업체는 등록한 회원이 관리자가 됩니다.
회원은 여러 업체를 등록할 수 있습니다.
회원이 등록한 업체는 등록한 회원이 관리자가 됩니다.
등록한 업체의 정보는 수정, 삭제할 수 있습니다.
companies collection
업체는 firestore의 companies 컬렉션에 저장을 합니다.
companies collection 구조는 다음과 같습니다.
{
name: '업체명',
description: '소개글',
category: '배달음식',
ownerUid: 'userUid',
createdAt: Timestamp,
}
src/router/index.js
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import Register from '../views/Register.vue'
import Profile from '../views/Profile.vue'
import Login from '../views/Login.vue'
const routes = [
{ path: '/', name: 'home', component: Home },
{ path: '/register', name: 'register', component: Register },
{ path: '/profile', name: 'profile', component: Profile },
{ path: '/login', component: Login },
{
path: '/register-company',
name: 'RegisterCompany',
component: () => import('@/views/RegisterCompany.vue'),
meta: { requiresAuth: true }
},
{
path: '/my-companies',
name: 'MyCompanies',
component: () => import('@/views/MyCompanies.vue'),
meta: { requiresAuth: true }
},
{
path: '/edit-company/:id',
name: 'EditCompany',
component: () => import('@/views/EditCompany.vue'),
meta: { requiresAuth: true }
},
{
path: '/company/:id',
component: () => import('@/views/CompanyDetail.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes,
})
export default router
src/stores/companyStore.js
🔹 업체 등록 : addCompany
🔹 회원별 등록 업체 조회 : fetchMyCompanies
🔹 업체 수정 : updateCompany
🔹 업체 목록 : fetchAllCompanies
🔹 업체 삭제 : deleteCompany
// src/stores/companyStore.js
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { db } from '@/firebase'
import {
collection,
doc,
addDoc,
updateDoc,
deleteDoc,
query,
where,
getDocs,
serverTimestamp
} from 'firebase/firestore'
import { useAuthStore } from './authStore'
export const useCompanyStore = defineStore('company', () => {
const companies = ref([])
// 🔹 업체 등록
const addCompany = async (name, description, category) => {
const authStore = useAuthStore()
const user = authStore.user
if (!user?.uid) throw new Error('로그인이 필요합니다.')
const company = {
ownerId: user.uid,
name,
description,
category,
createdAt: serverTimestamp(),
}
await addDoc(collection(db, 'companies'), company)
}
// 🔹 회원별 등록 업체 조회
const fetchMyCompanies = async () => {
const authStore = useAuthStore()
const user = authStore.user
if (!user?.uid) return
const q = query(
collection(db, 'companies'),
where('ownerId', '==', user.uid)
)
const snapshot = await getDocs(q)
companies.value = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data()
}))
}
// 🔹 업체 수정
const updateCompany = async (id, updatedData) => {
const ref = doc(db, 'companies', id)
await updateDoc(ref, {
...updatedData,
updatedAt: serverTimestamp()
})
}
const fetchAllCompanies = async () => {
const snapshot = await getDocs(collection(db, 'companies'))
companies.value = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data()
}))
}
const deleteCompany = async (id) => {
await deleteDoc(doc(db, 'companies', id))
if (Array.isArray(companies.value)) {
companies.value = companies.value.filter(c => c.id !== id)
} else {
companies.value = []
}
}
return {
companies,
addCompany,
fetchMyCompanies,
updateCompany,
fetchAllCompanies,
deleteCompany
}
})
src/views/RegisterCompany.vue - 업체 등록
<!-- src/views/RegisterCompany.vue-->
<template>
<v-container>
<v-card class="pa-4" max-width="500" mx-auto>
<v-card-title>업체 등록</v-card-title>
<v-text-field v-model="name" label="업체명" required />
<!-- 업종 태그 선택 -->
<div class="my-3">
<div class="mb-1">업종 선택</div>
<v-chip-group v-model="category" column mandatory>
<v-chip
v-for="item in categories"
:key="item"
:value="item"
class="ma-1"
color="primary"
variant="outlined"
filter
>
{{ item }}
</v-chip>
</v-chip-group>
</div>
<v-textarea v-model="description" label="소개글" />
<v-btn color="primary" @click="submit">등록</v-btn>
</v-card>
</v-container>
</template>
<script setup>
import { ref } from 'vue'
import { useCompanyStore } from '@/stores/companyStore'
import { useRouter } from 'vue-router'
const name = ref('')
const description = ref('')
const category = ref('')
const categories = ['배달음식', '카페', '소매업', '서비스업', '교육', '병원', '기타']
const companyStore = useCompanyStore()
const router = useRouter()
const submit = async () => {
if (!name.value || !category.value) {
alert('업체명과 업종을 입력해주세요.')
return
}
try {
await companyStore.addCompany(name.value, description.value, category.value)
alert('업체가 등록되었습니다.')
router.push('/my-companies')
} catch (e) {
alert('등록에 실패했습니다.')
}
}
</script>
src/views/EditCompany.vue - 업체 등록, 삭제
<!-- src/views/EditCompany.vue-->
<template>
<v-container>
<v-card class="pa-4" max-width="500" mx-auto>
<v-card-title>업체 수정</v-card-title>
<v-text-field v-model="name" label="업체명" required />
<div class="my-3">
<div class="mb-1">업종 선택</div>
<v-chip-group v-model="category" column mandatory>
<v-chip
v-for="item in categories"
:key="item"
:value="item"
class="ma-1"
color="primary"
variant="outlined"
filter
>
{{ item }}
</v-chip>
</v-chip-group>
</div>
<v-textarea v-model="description" label="소개글" />
<v-card-actions class="justify-space-between mt-4">
<v-btn color="red" @click="confirmDialog = true">삭제</v-btn>
<div>
<v-btn color="primary" @click="submit">수정</v-btn>
<v-btn color="grey" @click="cancel">취소</v-btn>
</div>
</v-card-actions>
<!-- 삭제 확인 다이얼로그 -->
<v-dialog v-model="confirmDialog" max-width="400">
<v-card>
<v-card-title class="text-h6">정말 삭제하시겠습니까?</v-card-title>
<v-card-actions class="justify-end">
<v-btn color="grey" text @click="confirmDialog = false">취소</v-btn>
<v-btn color="red" text @click="deleteCompany">삭제</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-card>
</v-container>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useCompanyStore } from '@/stores/companyStore'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const companyStore = useCompanyStore()
const id = route.params.id
const name = ref('')
const description = ref('')
const category = ref('')
const categories = ['배달음식', '카페', '소매업', '서비스업', '교육', '병원', '기타']
const confirmDialog = ref(false)
onMounted(() => {
const company = companyStore.companies.find(c => c.id === id)
if (company) {
name.value = company.name
description.value = company.description
category.value = company.category
}
})
const cancel = () => {
router.push('/my-companies')
}
const submit = async () => {
if (!name.value || !category.value) {
alert('업체명과 업종을 입력해주세요.')
return
}
try {
await companyStore.updateCompany(id, {
name: name.value,
description: description.value,
category: category.value
})
alert('수정 완료되었습니다.')
router.push('/my-companies')
} catch (e) {
alert('수정 실패')
}
}
const deleteCompany = async () => {
try {
await companyStore.deleteCompany(id)
alert('삭제 하였습니다.')
router.push('/my-companies')
} catch (e) {
alert('삭제 실패: ' + (e?.message || '알 수 없는 오류'))
console.error(e)
}
}
</script>
src/views/MyCompanies.vue - 회원별 등록 업체
<!-- src/views/MyCompanies.vue -->
<template>
<v-container>
<v-card class="pa-4" max-width="700" mx-auto>
<v-card-title>내 업체 목록</v-card-title>
<v-list>
<v-list-item
v-for="company in companyStore.companies"
:key="company.id"
class="border mb-2"
@click="goToEdit(company.id)"
clickable
>
<v-list-item-title class="font-weight-bold">
{{ company.name }}
</v-list-item-title>
<v-list-item-subtitle>
업종: {{ company.category }}
</v-list-item-subtitle>
<v-list-item-subtitle>
소개: {{ company.description || '없음' }}
</v-list-item-subtitle>
</v-list-item>
</v-list>
</v-card>
</v-container>
</template>
<script setup>
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useCompanyStore } from '@/stores/companyStore'
const companyStore = useCompanyStore()
const router = useRouter()
const goToEdit = (id) => {
router.push(`/edit-company/${id}`)
}
onMounted(() => {
companyStore.fetchMyCompanies()
})
</script>
.multiline-subtitle {
white-space: pre-line;
}
'예약 포털 (Vue3 + Firebase) - 서비스 오픈까지' 카테고리의 다른 글
8. 동네 (예약) 포털 (Vue 3 + Firebase) - 예약하기 (0) | 2025.06.02 |
---|---|
7. 동네 (예약) 포털 (Vue 3 + Firebase) - 등록 업체 보기(홈) (0) | 2025.06.01 |
5. 동네 (예약) 포털 (Vue 3 + Firebase) - 구글 계정으로 로그인 (0) | 2025.05.30 |
4. 동네 (예약) 포털 (Vue 3 + Firebase) - 비밀번호 변경, 비밀번호 리셋 (0) | 2025.05.30 |
3. 동네 (예약) 포털 (Vue 3 + Firebase) - 회원 등록, 로그인 (0) | 2025.05.29 |