본문 바로가기

개발 일지/TIL

[ #4 ] TIL

 ✏️ 0418      


방명록 JS => 모듈 가능한 코드로 변경

Firebase 연동

2차 병합 및 회의

깃허브 충돌


JS 코드 전체 수정
더보기
  • 기존 스크립트 코드
// 새 댓글 등록 함수
function addComment(name, text) {
  // 랜덤배경저장
  const backColor = randomColor();
  // 댓글 뻐대
  const commentHtml = `<div class="box" data-comment-id=${doc.id} style="background-color: ${backColor};">
  <div class="nickname-btn-container">
    <div class="nickname">${name}</div>
    <div class="btns">
    <button type="button" class="btn UDbtn del" style="background-color: rgb(221, 221, 221);">삭제</button>
    <button type="button" class="btn UDbtn ins" style="background-color: rgb(221, 221, 221);">수정</button>
    </div>
  </div>
  <div class="content">${text}</div>
  <div hidden>${today}</div>
</div>`;
  // 새로 만든 div에 댓글 뼈대 넣기
  newComment.innerHTML = commentHtml;
  // 댓글 컨테이너에서 자식이 있으면 그 위에 올리기
  const textContainer = document.getElementById("text_container");
  const firstChild = textContainer.firstChild;
  if (firstChild) {
    textContainer.insertBefore(newComment.firstChild, firstChild);
  } else {
    textContainer.appendChild(newComment.firstChild);
  }
}
// 삭제 버튼 기능
function delComment() {
  const dels = document.querySelectorAll(".del");
  dels.forEach(function (del) {
    del.addEventListener("click", () => {
      let del_check = confirm("정말 삭제하시겠습니까?");
      if (del_check === true) {
        // 가장 가까운 상위 요소 찾기
        const commetBox = del.closest(".box");
        commetBox.remove();
      } else {
        return;
      }
      // const commetBox = del.closest('.box')
      // commetBox.remove()
    });
  });
}
// 등록 버튼 클릭 시 실행
document.addEventListener("DOMContentLoaded", function () {
  document.getElementById("sav").addEventListener("click", () => {
    // 입력 값 가져오기
    const name = document.getElementById("inp_name").value;
    const text = document.getElementById("inp_txt").value;
    // 내용 입력 확인
    if (name == "" && text == "") {
      alert("닉네임과 내용은 필수입니다");
    } else if (name == "") {
      alert("닉네임을 입력해주세요");
    } else if (text == "") {
      alert("내용을 입력해주세요");
    } else {
      let check = confirm("등록하시겠습니까?");
      if (check === true) {
        // 댓글을 컨테이너에 추가
        addComment(name, text);
        // 입력 필드 지우기
        document.getElementById("inp_name").value = "";
        document.getElementById("inp_txt").value = "";
        // 삭제 버튼 기능 호출
        delComment();
      } else {
        return;
      }
    }
  });
});
// 입력 취소 버튼
document.getElementById("cancell").addEventListener("click", () => {
  document.getElementById("inp_name").value = "";
  document.getElementById("inp_txt").value = "";
  $("#commentgbox").hide();
});

방명록 페이지에서 입력한 댓글 저장을 위해 Firebase 를 연동하여 데이터를 저장하려고 했다

 

강의 내용 중 Firebase 를 이용하기 위해 type="modlue" 을 사용하면 onclick() 함수가 사용이 안된다는 것을 기억하고

onclick()을 사용하지 않은 JS 코드를 열심히 구글링하며 찾아봤다

 

강의에서 사용한 형식을 참고하고 이전에 배웠던 내용과 병합해서 코드를 작성해보았다

실행도 잘 되었고 입력, 삭제 둘 다 잘 된 것을 확인하고 Firebase와 연동하기 위해 초기 세팅 코드를 넣었더니

이게 무슨일이지 JS 코드는 무슨 Firebase 또한 연동이 되지 않아서 한참 해맸다

 

처음에는 js 형식이 아닌 node 형식으로 짜야했던가?!  ...라는 생각도 해봤다

강의에서는 node.js 로 작성해서 그런 줄...

 

당일 하루 밖에 남지 않은 상황이었기에 멘붕이 왔고 원인을 계속 찾았지만

방법이 잘 나오지 않아 팀원분들에게 헬프를 외쳐 도움을 요청했다

결국 저 코드는 주석 처리로 안녕~ 하였고 파이어베이스 연동을 먼저 한 후 다시 코드를 짰다

 

더보기
  • 다시 새로 작성한 스크립트 코드
//취소 누르면 다시 comment 입력창 숨기기
    $("#cancell").click(async function () {
      $('.mycommentgbox').hide();
    });

    //날짜 정보 가져오기
    let today = new Date();
    let year = today.getFullYear();
    let month = today.getMonth() + 1;
    let date = today.getDate();
    let hour = today.getHours();
    let minutes = today.getMinutes();
    let seconds = today.getSeconds();

    // id 날짜 정보
    let date_id = `${year}${("00" + month.toString()).slice(-2)}${(
      "00" + date.toString()
    ).slice(-2)}${hour}${minutes}${seconds}`;

    //저장
    $("#sav").click(async function () {
      const color_list = [
        "#8ABDD1",
        "#8AD1CF",
        "#8AA7D1",
        "#FFD5C2",
        "#FFCAC2",
      ];

      backColor = color_list[Math.floor(Math.random() * color_list.length)];
      let name = $("#inp_name").val();
      let text = $("#inp_txt").val();

      // 내용 입력 확인
      if (name == "" && text == "") {
        alert("닉네임과 내용은 필수입니다");
        return;
      } else if (name == "") {
        alert("닉네임을 입력해주세요");
        return;
      } else if (text == "") {
        alert("내용을 입력해주세요");
        return;
      }

      let check = confirm("등록하시겠습니까?");
      if (check === false) {
        return;
      }

      let password = prompt("비밀번호를 적어주세요.");

      //취소 버튼 눌렀을 때
      if(password == null) {
        return;
      }
      
      //비밀번호 안 입력 했을 때
      if(password == '') {
        alert("비밀번호를 입력해주세요 !")
      }else { //비밀번호 입력 되었을 때 저장
        await setDoc(doc(db, "comments", "id" + date_id), {
        name: name,
        text: text,
        backColor: backColor,
        today: today,
        date_id: date_id,
        password: password
      });
      location.reload();
      }
    });

    let docs = await getDocs(collection(db, "comments"));
    docs.forEach((doc) => {
      let row = doc.data();
      let name = row["name"];
      let text = row["text"];
      let backColor = row["backColor"];
      let today = row["today"];
      let date_id = row["date_id"];
      let temp_html = `<div class="box" data-comment-id=${doc.id} style="background-color: ${backColor};">
        <div class="nickname-btn-container">
          <div class="nickname">${name}</div>
          <div class="btns">
          <button type="button" class="btn UDbtn del" style="background-color: rgb(221, 221, 221);">삭제</button>
          <button type="button" class="btn UDbtn ins" style="background-color: rgb(221, 221, 221);">수정</button>
          </div>
        </div>
        <div class="content">${text}</div>
        <div hidden>${today}</div>
      </div>`;

      $("#text_container").append(temp_html);
    });

    //코멘트 삭제버튼 클릭시
    $(document).on("click", ".del", async function () {
      //id 가져오기
      let commentID = $(this).closest(".box").attr("data-comment-id");
      let password = prompt("비밀번호를 적어주세요.");
      if(password == null) {
        return;
      }

      let truepassword;

      //DB에서 id 값에 맞는 특정 데이터 가져오기
      const q = query(
        collection(db, "comments"),
        where('date_id', '==', commentID.substring(2))
      );
      const querySnapshot = await getDocs(q);

      querySnapshot.forEach((el) => {
        truepassword= el.data().password;
      });

      if(password != truepassword) {
        alert("비밀번호가 다릅니다 !");
        return;
      }

      let check = confirm("삭제하시겠습니까?");
      if (check === true) {
        try {
          await deleteDoc(doc(db, "comments", commentID));
          $(this).closest(".box").remove();
        } catch (error) {
          console.error("Error deleting document: ", error);
        }
      }
    });


    //코멘트 수정버튼 클릭시
    $(".ins").click(async function () {
      //id 가져오기
      let commentID = $(this).closest(".box").attr("data-comment-id");
      let password = prompt("비밀번호를 적어주세요.");
      if(password == null) {
        return;
      }
      
      let truepassword;

      //DB에서 id 값에 맞는 특정 데이터 가져오기
      const q1 = query(
        collection(db, "comments"),
        where('date_id', '==', commentID.substring(2))
      );
      const querySnapshot1 = await getDocs(q1);

      querySnapshot1.forEach((el) => {
        truepassword= el.data().password;
      });

      if(password != truepassword) {
        alert("비밀번호가 다릅니다 !");
        return;
      }

      //DB에서 id 값에 맞는 특정 데이터 가져오기
      const q2 = query(
        collection(db, "comments"),
        where('date_id', '==', commentID.substring(2))
      );
      const querySnapshot2 = await getDocs(q2);

      let old_text;
      let old_name;

      querySnapshot2.forEach((el) => {
        old_name = el.data().name;
        old_text = el.data().text;
      });

      let new_name = prompt("수정 할 닉네임을 적어주세요.", old_name);
      let new_text = prompt("수정 할 텍스트를 적어주세요.", old_text);

      if (new_name !== null && new_text) {
        //값 업데이트
        const docRef = doc(db, "comments", commentID);

        // Set the "capital" field of the city 'DC'
        await updateDoc(docRef, {
          name: new_name,
          text: new_text
        });
      } else if (new_name !== null) {
        const docRef = doc(db, "comments", commentID);
        await updateDoc(docRef, {
          name: new_name,
          text: old_text
        });
      } else if (new_text !== null) {
        const docRef = doc(db, "comments", commentID);
        await updateDoc(docRef, {
          name: old_name,
          text: new_text
        });
      }
      location.reload();
    });

팀원들이 다같이 안 되는 기능을 검색해서 찾아보고 코드 짜고 하다보니 겨우 어떻게든 기능을 완성했다

꼼꼼하고 세세한 팀원의 도움으로 if 을 상세하게 나눌 수 있었다

 

모듈 (modlue) 에 대해

 

 

모듈 ( module )
여러 기능들에 관한 코드가 모여있는 하나의 파일

 

  • 사용되는 이유
    • 유지보수성 : 의존성을 줄일 수 있기 때문에 기능을 개선하거나 수정할 때 훨씬 편하게 할 수 있다
    • 네임스페이스화 : 자바스크립트에서 전역변수는 전역공간을 가지기 때문에 코드의 양이 많을수록 겹치는 네임스페이스가 많아질 수 있다 모듈로 분리하면 모듈만의 네임스페이스를 갖기 때문에 문제가 해결된다
    • 재사용성 : 똑같은 코드를 반복하지 않고 모듈로 분리시켜서 필요할 때마다 사용할 수 있다

 

<script type="module" src="index.js"></script>
// 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, setDoc, doc, updateDoc, query, where } 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";
    import { deleteDoc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";

 

이런 식으로 사용할 수 있다고 한다

 

그런데 이번 경우 HTML 파일에 포함되어 있을 때는 잘 작동하지만,

코드 정리를 위해 별도의 js 파일로 분리하고 외부 파일로 불러오려고 할 때 문제가 발생한다

에러코드 : Uncaught SyntaxError: Cannot use import statement outside a module

 

구글링 해봤지만 type="module" 을 적으라는 이야기와 .mjs 확장자로 변경하라는 이야기가 많았다

둘 다 시도해보았지만 잘 되지 않았다 

 

원인은 서버 측의 CORS 설정이 가능성이 높다고 한다

Firebase SDK를 제공하는 서버에서 CORS를 허용하는 설정이 필요하다고 한다

 

다음 기회에 한번 더 도전해보는 것으로

 

CORS ( Cross-Origin Resource Sharing )
교차 출처 리소스 공유, 도메인이 다른 서버끼리 리소스를 주고 받을 때 보안을 위해 설정된 정책

 

 

Git pull & push 충돌

 

다같이 같은 파일을 수정하고 수정하다 보니 충돌이 발생했다

충돌을 해결하기 위해 팀원들과 작업 내용을 화면 공유를 통해 비교하며 코드를 지웠다

 

당일 충돌이 여러번 일어나다 보니 팀원들 모두 힘들어 하였고

충돌을 최소화 하기 위해 코드를 다른 곳에 백업해두거나 push 하기 전에 미리 말해주는 방향으로 개선해나갔다

 

이러한 문제는 팀이 바뀌어도 자주 혹은 한번쯤은 일어날 것 같다 

최대한 줄이기 위해 회의할 때나 push 할 때 서로의 작업 내용을 공유하며

충돌이 일어날 상황을 줄이는 것이 최선일 것 같다.

'개발 일지 > TIL' 카테고리의 다른 글

[ #6 ] TIL  (0) 2024.04.22
[ #5 ] TIL  (0) 2024.04.20
[ #3 ] TIL  (0) 2024.04.20
[ #2 ] TIL  (0) 2024.04.20
[ #1 ] TIL  (0) 2024.04.19