오늘의 공부

1. 프로젝트 분리 및 연결

  • 오늘 아침 결과물들을 어떻게 이어붙여서 디렉토리 구조를 분리/관리 할까에 대해 고민을 하고있었다..

  • 그 때 팀원분들 중 한 분이 디렉토리 구조와 스타일을 정리해서 와주셨다!
    (협업 유경험자 감사합니다 ㅠㅠ)

만세! 빨리 뜯어서 적용 시켜야지

프로젝트회생

뜬금 디렉토리 연결용 함수 정리

HTML

<!-- 디렉토리 구조 내의 css파일을 연결-->
<link rel="stylesheet" href="./css/style.css" />
<!-- 디렉토리 구조 내의 js파일을 연결-->
<script src="./js/editMode.js" type="module"></script>

JavaScript

//가져갈 값 지정해주기 (함수도 된다)
export {};

//값 가져오기
import {} from './firebase.js'
  • 어제는 GitHub Page 로 빌드하는 것에 고집해서 Jekyll로 이어지게 하려고 했는데..
    그냥 index.html에 합쳐져 나오게 만들면 되는 것 이었다..

2. 멤버 소개페이지 프로젝트 방명록 삭제 기능 구현 +@

  • 삭제 기능은 그냥 html <body> 쪽 들어가 remove()만 하면 되는거 아니야?
    => 라는 생각을 했던 때가 있엇죠..

  • 사실 페이지만 본다면 틀린말은 아니지만.. 페이지에 댓글들을 저장하기위해 API연동을 시켰기에..
    => 댓글들을 삭제해도 API 서버에 남아있으면 다시 불러와 진다는 것이다!

  • 그렇게 찾아본 Firebase 함수 deleteDoc()..

    deleteDoc(doc(db, "comments", doc.id))
  • 어라..? doc의 id를 알아야 삭제가 되네..?
    => 그럼 doc id를 지정할 수 있게 adddoc()을 setdoc()으로 바꾸자!
    // doc.id를 특정하기 위한 정보로 "날짜" 와 "작성자 이름" 을 썻다..
    await setDoc(doc(db, "comments", name + today), innerDoc)
  • 하지만 그렇게 만든 코드는 “같은 날짜”의 “같은 이름”을 쓰면 기록이 덮어진다는 오류를 범하게 된다는 걸 알게되어 팀원분들과 상의를 했다..
    => 그 때 팀원 중 한 분이 “작성된 시간”과 “이름”으로 삭제할 doc를 구별하는건 어떠냐고 조언을 해주셨다..
// comments 라이브러리의 값 가져오기 + orderBy로 정렬
let docs = await getDocs(
    query(collection(db, 'comments'), orderBy('createdAt'))
);
// 값 읽어와서 객체에 데이터 저장하기
docs.forEach((docRef) => {
    let row = docRef.data();
    let name = row['nickName'];
    let text = row['comment'];
    let Date = row['date'];
    let when = row['createdAt'];
    //객체 생성
    const li = document.createElement('li');
    const div = document.createElement('div');
    const h3 = document.createElement('h3');
    const btn_del = document.createElement('button');
    //버튼 name으로 style + 기능 묶기
    btn_del.name = 'delete';
    btn_del.type = 'button';
    //댓글 구분용 데이터 "작성된 시간" 저장
    h3.dataset.date = when.toDate();
    //들어갈 값 정해주기
    btn_del.textContent = '삭제';
    h3.textContent = name;
    //구조 묶어주기 
    div.append(h3);
    li.append(div);
    li.append(btn_del);
    //Comment는 붙일 곳을 querySelector로 불러온 것이다.
    Comment.prepend(li);
});
// "작성" 버튼 클릭 함수
$("#btn").click(async function () {
    // 넣을 값 읽어오기 + 작성된 날짜 기록하기
    let name = document.getElementById("ipName").value;
    let text = document.getElementById("ipText").value;
    let today = new Date().toLocaleDateString();
    let now = new Date();
    //올릴 값들을 정리하는 곳
    const innerDoc = {
            nickName: name,
            comment: text,
            date: today,
            createdAt: now,
            password: password,
        };
    //값 올리기
    await addDoc(collection(db, "comments"), innerDoc)
});
// 삭제 버튼 클릭 함수
$(document).on("click", "button[name='delete']", async function () {
    // 체크용 comments 라이브러리 값 새로 불러오기
    let checkDocs = await getDocs(query(collection(db, "comments"), orderBy("createdAt")));
    // 삭제 버튼을 누른 댓글에서 비교할 값 가져오기
    let nameCheck = $(this).prev().find("h3").text();
    // floor 및 0.001 은 날짜 값을 올렸을 때 뒷자리 3자리가 "내림" 당한 값과 비교하기 위함
    let dateCheck = Math.floor(new Date($(this).parent().prev().find("h3").data('date')).getTime() * 0.001);
    // 라이브러리 속 doc들 순회
    checkDocs.forEach((docRef) => {
        // 데이터 조리해서 가져오기
        let row = docRef.data();
        let name = row['nickName'];
        let date = Math.floor(row['createdAt'].toDate() * 0.001);
        // 이름과 작성된 시간을 비교하여 삭제할 댓글 찾기
        if (name === nameCheck && date === dateCheck) {
            //댓글을 firebase에서 삭제
            deleteDoc(doc(db, "comments", docRef.id))
        }
    });
});

참고로 위 코드는 최대한 간략화 해서 구조만 보여주도록 리빌딩했다.

  • 시행착오들을 간략히 설명하자면
  1. 데이터 값을 어떻게 요소에 저장시키냐
    => 요소에 dataset 을 이용해 data를 저장시킬 수 있었다!

  2. 데이터 값인 new Date()가 타임스탬프 유형으로 들어가 어떻게 비교하냐
    => 이게 좀 오래걸렸지만 toDate()나 getTime()을 쓰고 뒷자리 3자리를 지웠다
    => 파일이 Firebase에 올라갔다 내려올 때 3자리까지 값이 “내림” 당하는 것 같다.

  3. Firebase에 저장된 데이터들에 작성시간이 포함되어있는데 이를 이용해 정렬할 순 없나..?
    => Firebase 내장함수 query 와 orderBy를 이용했다.
  4. 비밀번호를 입력해서 데이터를 삭제할 수 있는 권한이 부여되면 좋을 것 같다.
    => 값을 받아서 Firebase에 저장하고 위에 2번처럼 비교를 하였다.
    (data는 직접 입력하는 값(prompt)과 Firebase에서 직접 가져오게(요소에 저장하면 해킹위험이 있을 것 같았다))

결과다! 뿌듯하다
(디자인은 팀원분이 만져주셧다 + 갓누리님이 디렉토리/스타일 구조 정리 해주신 분)

placeholder

학습 과정 중 특이사항 / 삽질

  • 오늘은 다행히 뻘짓보단 결과가 나오는 일들이 많았다!

관리자 모드 상호작용

발단

  • 관리자 모드를 병합시킬 때 세션 스토리지를 사용해서 관리자인 상태를 저장해서 사용한다는 걸 알게 되었다.
    => 관리자 모드가 되면 댓글 삭제는 그냥 가능해야되는거 아냐?
    (비밀번호 건너뛰기 기능)

전개

  • 세션에서 값을 읽어오는 구문을 분석해서 사용했다.
// 세션에 로그인 상태 저장
sessionStorage.setItem('editMode', 'true');
// 세션 스토리지의 관리자 유무 가져오기
const editMode = sessionStorage.getItem('editMode')

checkDocs.forEach((docRef) => {
    // 데이터 조리해서 가져오기
    let row = docRef.data();
    let name = row['nickName'];
    let pswdCheck = row['password'];
    // 위 설명대로 뒷자리 3자리 배제
    let date = Math.floor(row['createdAt'].toDate() * 0.001);

    // 이름과 작성된 날짜를 비교하여 삭제할 댓글 찾기
    if (name === nameCheck && date === dateCheck) {

        // 비밀번호 비어있는지 + 관리자 모드  확인
        if (pswdCheck !== "" && pswdCheck !== undefined && editMode !== 'true') {
            // 비밀번호 입력
            let key = prompt('비밀번호를 입력해주세요.', '')

            if (pswdCheck === key) {
                alert('비밀번호 일치')
                //댓글을 firebase에서 삭제
                deleteDoc(doc(db, "comments", docRef.id))
                //댓글을 화면에서 삭제
                $(this).parent().parent().remove();
                //확인
                alert('성공적으로 삭제되었습니다!');
            } else {
                alert('잘못된 접근입니다.');
            }
        } else {
            //댓글을 firebase에서 삭제
            deleteDoc(doc(db, "comments", docRef.id))
            //댓글을 화면에서 삭제
            $(this).parent().parent().remove();
            //확인
            alert('성공적으로 삭제되었습니다!');
        };
    };
});

결말

  • 의외로 금방 해냈다.. (비밀번호 설정하는게 더 귀찮았기에..)

멤버 소개페이지 프로젝트 회의

  • 내일 있을 발표를 위해 발표자를 선정했다
    (방법은 절대 사다리타기가 아닙니다..(아마도))

  • 발표자료 예시를 보며 Canva 라는 사이트를 알게되어 거기서 자료를 만들게 되었다.

  • 아직 병합되지 않은 기능들이 조금 남아있으나 거의 정리가 다 되서 다행이다!