From ee14507b59db20457ccb0c38afa32d5040e8d18b Mon Sep 17 00:00:00 2001 From: treasure-sky Date: Wed, 11 Sep 2024 03:45:09 +0900 Subject: [PATCH 1/5] =?UTF-8?q?chore:=20firebase=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/firebase/.gitkeep | 0 src/shared/firebase/firebaseConfig.ts | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+) delete mode 100644 src/shared/firebase/.gitkeep create mode 100644 src/shared/firebase/firebaseConfig.ts diff --git a/src/shared/firebase/.gitkeep b/src/shared/firebase/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/shared/firebase/firebaseConfig.ts b/src/shared/firebase/firebaseConfig.ts new file mode 100644 index 0000000..7a2cce1 --- /dev/null +++ b/src/shared/firebase/firebaseConfig.ts @@ -0,0 +1,18 @@ +import { initializeApp } from "firebase/app"; +import { getAnalytics } from "firebase/analytics"; +import { getFirestore } from "firebase/firestore/lite"; + +const firebaseConfig = { + apiKey: import.meta.env.VITE_apiKey, + authDomain: import.meta.env.VITE_authDomain, + projectId: import.meta.env.VITE_projectId, + storageBucket: import.meta.env.VITE_storageBucket, + messagingSenderId: import.meta.env.VITE_messagingSenderId, + appId: import.meta.env.VITE_appId, + measurementId: import.meta.env.VITE_measurementId, +}; + +const app = initializeApp(firebaseConfig); + +export const db = getFirestore(app); +export const analytics = getAnalytics(app); From 71b9405f8a27a396d4f9ddab67b9bed95d27703c Mon Sep 17 00:00:00 2001 From: treasure-sky Date: Wed, 11 Sep 2024 20:55:50 +0900 Subject: [PATCH 2/5] =?UTF-8?q?chore:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BB=A8=EB=B2=84=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/firebase/noticeConverter.ts | 45 ++++++++++++++++++++++++++ src/shared/types/notice.ts | 13 ++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/shared/firebase/noticeConverter.ts create mode 100644 src/shared/types/notice.ts diff --git a/src/shared/firebase/noticeConverter.ts b/src/shared/firebase/noticeConverter.ts new file mode 100644 index 0000000..85d6429 --- /dev/null +++ b/src/shared/firebase/noticeConverter.ts @@ -0,0 +1,45 @@ +import { + DocumentData, + FirestoreDataConverter, + QueryDocumentSnapshot, + Timestamp, +} from "firebase/firestore/lite"; +import { Notice } from "../types/notice"; + +// 유효성 검사 함수 +function validation(notice: Notice) { + if (!(notice.createdAt instanceof Date)) throw new Error("Invalid or missing createdAt."); + if (typeof notice.order !== "number" || notice.order < 0) + throw new Error("Invalid or missing order."); + if (typeof notice.renewal !== "boolean") throw new Error("Invalid or missing renewal."); + if (typeof notice.title !== "string" || !notice.title.trim()) + throw new Error("Invalid or missing title."); + if (typeof notice.contents !== "string" || !notice.contents.trim()) + throw new Error("Invalid or missing contents."); +} + +// Firestore data converter +export const noticeConverter: FirestoreDataConverter = { + toFirestore(notice: Notice): DocumentData { + validation(notice); + return { + createdAt: Timestamp.fromDate(notice.createdAt), // Date 객체를 Firestore Timestamp로 변환 + order: notice.order, + renewal: notice.renewal, + title: notice.title, + contents: notice.contents, + }; + }, + fromFirestore(snapshot: QueryDocumentSnapshot): Notice { + const data = snapshot.data(); + const notice: Notice = { + createdAt: data.createdAt.toDate(), // Firestore Timestamp를 Date 객체로 변환 + order: data.order, + renewal: data.renewal, + title: data.title, + contents: data.contents, + }; + validation(notice); + return notice; + }, +}; diff --git a/src/shared/types/notice.ts b/src/shared/types/notice.ts new file mode 100644 index 0000000..74fddf4 --- /dev/null +++ b/src/shared/types/notice.ts @@ -0,0 +1,13 @@ +// 공지사항 타입 +export interface Notice { + createdAt: Date; + order: number; + renewal: boolean; + title: string; + contents: string; +} + +// 공지사항 반환 타입 +export interface NoticeDto extends Notice { + id: string; +} From 7369c5fc1469eefb806af035cb840eb7bd1c001e Mon Sep 17 00:00:00 2001 From: treasure-sky Date: Wed, 11 Sep 2024 21:00:41 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20CRUD=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/firebase/noticeService.ts | 71 ++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/shared/firebase/noticeService.ts diff --git a/src/shared/firebase/noticeService.ts b/src/shared/firebase/noticeService.ts new file mode 100644 index 0000000..0b71a38 --- /dev/null +++ b/src/shared/firebase/noticeService.ts @@ -0,0 +1,71 @@ +import { + collection, + addDoc, + updateDoc, + getDocs, + getDoc, + deleteDoc, + doc, +} from "firebase/firestore/lite"; +import { Notice, NoticeDto } from "../types/notice"; +import { db } from "./firebaseConfig"; +import { noticeConverter } from "./noticeConverter"; + +// 공지사항 생성 +export async function createNotice(newNotice: Notice) { + try { + const docRef = await addDoc(collection(db, "notice").withConverter(noticeConverter), newNotice); + return docRef.id; + } catch (e) { + throw new Error("Error adding document: " + e); + } +} + +// 전체 공지사항 조회 +export async function getNoticeList(): Promise { + try { + const querySnapshot = await getDocs(collection(db, "notice").withConverter(noticeConverter)); + const result: NoticeDto[] = []; + querySnapshot.forEach((doc) => { + result.push({ id: doc.id, ...doc.data() }); + }); + return result; + } catch (e) { + throw new Error("Error getting documents: " + e); + } +} + +// 공지사항 조회 +export async function getNoticeById(noticeId: string): Promise { + try { + const docRef = doc(db, "notice", noticeId).withConverter(noticeConverter); + const docSnapshot = await getDoc(docRef); + + if (!docSnapshot.exists()) { + throw new Error("No document found with id " + noticeId); // 문서가 없는 경우 예외 처리 + } + + return { id: docSnapshot.id, ...docSnapshot.data() }; + } catch (e) { + throw new Error("Error getting document: " + e); + } +} + +// 공지사항 수정 +export async function updateNotice(noticeId: string, newNotice: Notice) { + try { + const docRef = doc(db, "notice", noticeId).withConverter(noticeConverter); + await updateDoc(docRef, { ...newNotice }); + } catch (error) { + throw new Error("Error updating document: " + error); + } +} + +// 공지사항 삭제 +export async function deleteNotice(noticeId: string) { + try { + await deleteDoc(doc(db, "notice", noticeId)); + } catch (error) { + throw new Error("Error removing document: " + error); + } +} From aae4f37e94b1574cb6a2f51959ea8a5493fbbfaf Mon Sep 17 00:00:00 2001 From: treasure-sky Date: Wed, 11 Sep 2024 21:00:50 +0900 Subject: [PATCH 4/5] =?UTF-8?q?chore:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/firebase/FirebaseTest.tsx | 132 +++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 src/shared/firebase/FirebaseTest.tsx diff --git a/src/shared/firebase/FirebaseTest.tsx b/src/shared/firebase/FirebaseTest.tsx new file mode 100644 index 0000000..8d5d7aa --- /dev/null +++ b/src/shared/firebase/FirebaseTest.tsx @@ -0,0 +1,132 @@ +import { useState } from "react"; +import { + createNotice, + getNoticeList, + getNoticeById, + updateNotice, + deleteNotice, +} from "./noticeService"; +import { NoticeDto } from "../types/notice"; + +// 테스트용 컴포넌트 추후 삭제 예정 +function FirebaseTest() { + const [order, setOrder] = useState(0); + const [renewal, setRenewal] = useState(false); + const [title, setTitle] = useState(""); + const [contents, setContents] = useState(""); + const [createdAt, setCreatedAt] = useState(new Date()); + const [notices, setNotices] = useState([]); + const [noticeId, setNoticeId] = useState(""); + return ( + <> +
+

공지사항 생성

+ + + + + +
+
+

전체 공지사항 조회

+ +
+ {notices.map((notice, index) => ( +
+ {`ID: ${notice.id}, title: ${notice.title}, contents: ${notice.contents}, order: ${notice.order}, renewal: ${notice.renewal ? "O" : "X"}, createdAt: ${notice.createdAt}`} +
+ ))} +
+
+
+

공지사항 조회 / 수정

+ + + + + + + + +
+ + ); +} + +export default FirebaseTest; From 22b066d9ddb5e239648b179f20ffa601fc720252 Mon Sep 17 00:00:00 2001 From: treasure-sky Date: Thu, 12 Sep 2024 02:24:36 +0900 Subject: [PATCH 5/5] =?UTF-8?q?chore:=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/firebase/firebaseConfig.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/shared/firebase/firebaseConfig.ts b/src/shared/firebase/firebaseConfig.ts index 7a2cce1..7061877 100644 --- a/src/shared/firebase/firebaseConfig.ts +++ b/src/shared/firebase/firebaseConfig.ts @@ -3,13 +3,13 @@ import { getAnalytics } from "firebase/analytics"; import { getFirestore } from "firebase/firestore/lite"; const firebaseConfig = { - apiKey: import.meta.env.VITE_apiKey, - authDomain: import.meta.env.VITE_authDomain, - projectId: import.meta.env.VITE_projectId, - storageBucket: import.meta.env.VITE_storageBucket, - messagingSenderId: import.meta.env.VITE_messagingSenderId, - appId: import.meta.env.VITE_appId, - measurementId: import.meta.env.VITE_measurementId, + apiKey: import.meta.env.VITE_FIREBASE_API_KEY, + authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, + projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, + storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, + messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, + appId: import.meta.env.VITE_FIREBASE_APP_ID, + measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID, }; const app = initializeApp(firebaseConfig);