ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [2일차] 미니프로젝트 진행 (Firebase)
    내일배움캠프/미니프로젝트 2025. 2. 20. 11:20

    ✅ 오늘의 목표

    - 4주차 Firebase 활용한 강의 수강 후 멤버카드 CRUD 구현하기.

    ✅ Firebase 란?

    Firebase는 Google에서 제공하는 Backend-as-a-Service(BaaS) 플랫폼으로, 모바일 및 웹 애플리케이션 개발을 더욱 쉽고 빠르게 할 수 있도록 지원하는 클라우드 기반 솔루션. 서버 구축 없이 강력한 기능을 활용할 수 있다.

    ✅ Firebase의 주요 기능

    🔹 1) 실시간 데이터베이스 및 저장소

    1. Cloud Firestore: 실시간 동기화 및 강력한 쿼리 기능을 제공하는 NoSQL 클라우드 데이터베이스
    2. Realtime Database: 변경 사항을 실시간으로 동기화하는 NoSQL 데이터베이스
    3. Cloud Storage: 이미지, 동영상, 파일 등을 안전하게 저장하고 공유하는 클라우드 스토리지

    🔹 2) 인증 및 보안

    1. Firebase Authentication: Google, Facebook, Apple 로그인과 같은 소셜 로그인 및 이메일/비밀번호 기반 인증 지원
    2. Firebase Security Rules: 데이터베이스와 스토리지 보안을 설정하여 접근 제어 가능

    🔹 3) 클라우드 함수 및 호스팅

    1. Cloud Functions: 서버리스 환경에서 특정 이벤트 발생 시 자동으로 실행되는 백엔드 함수
    2. Firebase Hosting: 정적 웹사이트 및 SPA(Single Page Application) 배포를 위한 초고속 호스팅 서비스

    🔹 4) 분석 및 마케팅 도구

    1. Google Analytics: 사용자 행동 분석 및 맞춤형 마케팅 지원
    2. Firebase Cloud Messaging(FCM): 무료 푸시 알림 서비스
    3. Remote Config: 앱 업데이트 없이 설정 변경 가능 (예: A/B 테스트)

    https://firebase.google.com/?hl=ko

     

    위 주소에서 가입 후 사용이 가능하다.

    ✅ Firebase 간단 사용법

    <script type="module">
            // Firebase SDK 라이브러리 가져오기
            import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
            import { getFirestore } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
            import { collection, addDoc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
            import { getDocs } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
    
            // Firebase 구성 정보 설정
            const firebaseConfig = {
                    ... 
            };
    
    
            // Firebase 인스턴스 초기화
            const app = initializeApp(firebaseConfig);
            const db = getFirestore(app);
    
            $("#btn").click(async function () {
                // json 형식
                let doc = {
                    'title': title,
                    'content': content,
                    'date': date
                };
                // "board" 컬렉션에 data 저장
                await addDoc(collection(db, "board"), doc); // 저장
            })
    
            // "board" 컬렉션에서 data list 조회
            let docs = await getDocs(collection(db, "board"));
            docs.forEach((doc) => {
                let row = doc.data();
            }); 
    
        </script>

    addDoc 으로 data를 저장할 수 있고 getDocs 로 data 목록을 조회할 수 있다.

    ✅ 멤버 카드 구현

    나는 Firebase 의 firestore를 활용하여 데이터를 간단하게 주고 받기로 했다.

    1. 멤버 저장

    와이어 프레임을 토대로 위와 같이 퍼블리싱을 진행하였다.

    자기소개 부분은 Quill Editor 를 활용하였고, 다른 image-upload, input 박스들은 기존에 사용했던 script를 기반으로 작성하였다.

    스킬 부분을 검색하여 추가하고 싶어서 어떻게 구현할지 고민하였다.

    스킬에 관련된 데이터를 json 으로 먼저 만들어놓고 json 데이터를 fetch로 불러와서 해당 데이터를 기반으로 작성하도록 결정했다.

    완성된 결과물이다.

    // 스킬 검색 및 선택 기능 추가
          const skillSearch = document.getElementById('skillSearch')
          const skillList = document.getElementById('skillList')
          const selectedSkillList = document.getElementById('selectedSkillList')
    
          let skills = []
          let selectedSkills = []
    
          // 스킬 목록 불러오기
          fetch('/assets/json/language.json')
            .then(response => response.json())
            .then(data => {
              skills = data
              updateSkillList()
            })
            .catch(error => console.error('Error loading skills:', error))
    
          // 검색 결과 업데이트 함수
          function updateSkillList() {
            const searchText = skillSearch.value.toLowerCase().trim()
            // 검색어가 없으면 목록 초기화 후 함수 종료
            if (!searchText) {
              skillList.innerHTML = ''
              return
            }
            skillList.innerHTML = ''
            skills.forEach(skill => {
              if (skill.name.toLowerCase().includes(searchText)) {
                const li = document.createElement('li')
                li.textContent = skill.name
                li.style.cursor = 'pointer'
                // 클릭 시 선택된 스킬 목록에 추가
                li.addEventListener('click', () => {
                  addSkill(skill.name)
                })
                skillList.appendChild(li)
              }
            })
          }
    
          // 선택된 스킬 목록 업데이트 함수
          // selectedSkills 배열을 순회하며 li 생성
          function updateSelectedSkillList() {
            selectedSkillList.innerHTML = ''
    
            selectedSkills.forEach((skill, index) => {
              const li = document.createElement('li')
              li.classList.add('chip') // 칩 스타일용 클래스
    
              // 스킬 이름 영역
              const skillNameSpan = document.createElement('span')
              skillNameSpan.textContent = skill
              li.appendChild(skillNameSpan)
    
              // 삭제 아이콘
              const removeIcon = document.createElement('i')
              removeIcon.classList.add('material-icons', 'remove-icon')
              removeIcon.textContent = 'close'
              // 클릭 시 스킬 제거
              removeIcon.addEventListener('click', () => {
                removeSkill(index)
              })
              li.appendChild(removeIcon)
    
              selectedSkillList.appendChild(li)
            })
          }
    
    
          // 스킬 추가 함수(중복 체크)
          function addSkill(skillName) {
            if (!selectedSkills.includes(skillName)) {
              selectedSkills.push(skillName)
              updateSelectedSkillList()
            }
          }
    
          // 스킬 제거 함수
          function removeSkill(index) {
            selectedSkills.splice(index, 1)
            updateSelectedSkillList()
          }

    위와 같이 데이터를 가져온 후 검색어를 모두 소문자로 바꾸고 검색 결과가 있을 경우 ul 태그 안에 li 태그를 추가하였다.

    그 후 스킬을 선택할 시 selectedSkills 변수에 선택한 스킬을 넣고 중복 되지 않게 선택된 스킬을 관리하였다.

    그 외 CSS를 적용하여 위와 같이 만들었다.

    try {
        await addDoc(collection(db, "member"), data)
        checkUnload = false
        alert("등록에 성공하였습니다.")
        location.href="list.html"
      } catch (error) {
        alert("등록 실패:", error)
      }

    기존에 배웠던 addDoc 함수를 활용하여 member 컬렉션에 data를 저장하였다.

    2. 멤버 목록 조회

    멤버 목록 조회는

    <script type="module">
        import { app, db, collection, getDocs, query, orderBy } from '../../../assets/js/firebase-config.js'
    
        (async () => {
          const q = query(collection(db, "member"), orderBy("reg_dt", "desc"));
          const docs = await getDocs(q);
        })()
    
      </script>

    위와 같이 getDocs 를 활용해 멤버 목록을 조회하였다.

    저장할 때 저장 시점의 reg_dt를 같이 저장해서 order_by를 활용하여 저장된 역순으로 목록을 조회하였다.

    3. 멤버 조회 및 수정

    조회 및 수정을 어떻게 할까 하다가 그냥 등록 페이지를 활용하여, 기존 데이터를 입력 시켜주고 데이터를 수정하도록 하였다.

    멤버의 id 를 query string 으로 받아서 해당 id로 조회를 하였다. (?id=xxxx)

      async function loadDetail() {
          const params = new URLSearchParams(window.location.search)
          const id = params.get('id')
          if (!id) {
            alert("문서 id가 제공되지 않았습니다.")
            location.href = 'list.html'
            return
          }
          const docRef = doc(db, "member", id)
          const docSnap = await getDoc(docRef)
          if (docSnap.exists()) {
            setData(docSnap.data())
          } else {
            alert("해당 문서가 존재하지 않습니다.")
            location.href = 'list.html'
          }
        }

    getDoc() 함수를 사용하면 member 컬렉션에서 해당 id에 맞는 data를 조회할 수 있다.

    const params = new URLSearchParams(window.location.search)
    const id = params.get('id')
    const docRef = doc(db, "member", id)
    await updateDoc(docRef, updateData)

    updateDoc 함수를 사용하면 해당 member 컬렉션에서 해당 id에 맞는 data에 덮어씌울 수 있다.

    이렇게 수정기능을 구현하였다.

    4. 멤버 삭제

    const params = new URLSearchParams(window.location.search)
    const id = params.get('id')
    const docRef = doc(db, "member", id)
    await deleteDoc(docRef)

    삭제도 멤버 id를 활용하 member 컬렉션에서 id로 조회 하여 deleteDoc() 함수를 사용하면 된다.

    오늘은 멤버 관리 CRUD를 완성하였다.

    내일은 다른 조원들의 기능이 완성되면, 디자인을 통일화 하도록 하고 피드백이 있으면 해당 피드백은 반영하도록 해야겠다.

Designed by Tistory.