본문 바로가기
스파르타(부트캠프)/TIL (Today I Learned)

[내일배움캠프] 스파르타 5일차_24.10.04

by Kimuky 2024. 10. 4.

공부 내용 및 활동 내용

 -  미니 프로젝트 개발

1. 이미지 미리보기
2. 버그 수정 (Update)
3. Firestore Delete (Javascript)
4. 미니프로젝트 끝?

 

1. 이미지 미리보기 (FileReader)

프로필을 수정할 때, 이미지 미리보기가 있으면 좋겠다는 생각을 해서 찾아보았다.

// js
$('#cur_img').change(function () {
    const file = this.files[0];
    if (file) {
        const reader = new FileReader();
        reader.onload = function (e) {
            $('#circle_img').attr('src', e.target.result).show();
        }
        reader.readAsDataURL(file);
    }

});
 
 
 // html
 <div class="input-group mb-3">
    <input type="file" class="form-control" id="cur_img" accept="image/*">
</div>

 

input file에 있는 것이 chage 될때마다 리스너가 인식하여 파일을 읽고 이미지를 바로 볼 수 있게 해주는 것이다.


2. 버그 수정 (Update)

 프로젝트를 진행 중, CRUD 중 Update를 구현 중 문제점들을 발견했다.

 

그래서 일단은 문제점들을 분석해보았다.

1) Update가 되지 않는다.

2) Update를 하지않았을 때, 갱신 오류

 

-> 음? 도대체 뭘 수정한 걸까... 이래서 계획한 거보다 크게 확장하면 어렵다... 일단 고쳐보자

1)Update가 되지 않는다.

이건 정말 심각한 문제이다.. 음 왜 갱신이 안됫던건지 천천히 분석해보자. 전혀 문제 없어보인다.. 역시 막코딩은 무섭다 문제를 발견하기 정말 어렵기 때문이다. 그래서 일단은 천천히 로직을 다시 분석했다. 

순서는

카드(동적으로 생성된 요소) -> 프로필 확인 -> 수정

 

카드에서 프로필 확인할 때, 카드 안에 있는 data-id를 통해 각 프로필의 주인을 식별한다.

예)

음 데이터가 잘 들어가있고, 

// 각 카드를 클릭했을 때,
$('#card').on('click', '#member_card', function () {
    const profile_id = $(this).data('id')
    handleClick(profile_id)
});

 

각 카드를 눌렀을 때, profile id가 handleClick에 들어간다.

//프로필 등록한 사람 전부 들고오기
const q_select_from_joinUser = query(collection(db, "users"), where("confirm", "==", '1'),);

async function handleClick(profile_id) {

    // 해당 프로필의 아이디로 유저 데이터 받아옴
    let docs = await getDocs(q_select_from_joinUser);
    let name, mbti, style, link, img = ''
    docs.forEach((doc) => {
        let row = doc.data();
        id = row['id']
        name = row['name']
        mbti = row['mbti']
        style = row['style']
        link = row['link']
        img = row['url']
    })


    $('#box_name').text(name)

    if (sessionStorage.getItem('user_id') == profile_id) {
        //셋팅
        $('#mbti_me').attr('value', mbti)
        $('#style_me').attr('value', style)
        $('#url_me').attr('value', link)
        $('#circle_img').attr('src', img)

        // 내 카드일 시, 실행될 곳
        $('#card_modal_container').css('display', 'flex');
        $('#card_modal_content').css('display', 'flex');
        $('#box_mbti_style_url_me').css('display', 'flex');
        $('#card').css('visibility', 'hidden');
        $('#box_img_file').css('display', 'block');

        $('#cur_img').change(function () {
            const file = this.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = function (e) {
                    $('#circle_img').attr('src', e.target.result).show();
                }
                reader.readAsDataURL(file);
            }

        });


    } else {
        // 셋팅
        $('#other_grid_mbti').text(mbti)
        $('#other_grid_style').text(style)
        $('#other_grid_url').attr('href', link)
        $('#circle_img').attr('src', img)

        // 남의 카드일 시, 실행될 곳
        $('#card_modal_container').css('display', 'flex');
        $('#card_modal_content').css('display', 'flex');
        $('#box_mbti_style_url_other').css('display', 'grid');
        $('#card').css('visibility', 'hidden');
    }

}

 

음? 잘 작동된다. 문제가 없다.........?

 

원래 가장 무서운것은 코드가 작동되는데 원하는 기능이 구현이 안될 때이다.

 

코드가 작동이 된다라... 음 믿을 수가 없다.. 

 

아.. 아 찾았다.

const q_select_from_joinUser = query(collection(db, "users"), where("confirm", "==", '1'),);
let docs = await getDocs(q_select_from_joinUser);
docs.forEach((doc) => {
        let row = doc.data();
        let id = row['id']
        name = row['name']
        mbti = row['mbti']
        style = row['style']
        link = row['link']
        img = row['url']
    })

여기서 doc을 프로필을 등록한 사람 전부 조회한다. 그럼 저렇게 넣어버리면

제일 마지막에 조회가 된 프로필 정보값이 들어가 버린다.

업데이트할 때, 10.3날 마지막 프로필에 갱신하다보니 문제가 없는 것인줄 알았는데 정말 이상하게 짠 것이다.... 쓰읍

그래서 코드를 조금 수정해주었다. 이러면 id == profile_id가 같을 때 그정보만을 변수에 담아주기에 카드프로필의 정보만 빼올 수 있다.

$('#card').on('click', '#member_card', function () {
    const profile_id = $(this).data('id')
    handleClick(profile_id)
});

async function handleClick(profile_id) {

    // 해당 프로필의 아이디로 유저 데이터 받아옴
    let docs = await getDocs(q_select_from_joinUser);
    let name, mbti, style, link, img = ''
    docs.forEach((doc) => {
        let row = doc.data();
        let id = row['id']

        if (id == profile_id) {
            name = row['name']
            mbti = row['mbti']
            style = row['style']
            link = row['link']
            img = row['url']
        }
    })


    $('#box_name').text(name)

    if (sessionStorage.getItem('user_id') == profile_id) {
        //셋팅
        $('#mbti_me').attr('value', mbti)
        $('#style_me').attr('value', style)
        $('#url_me').attr('value', link)
        $('#circle_img').attr('src', img)

        // 내 카드일 시, 실행될 곳
        $('#card_modal_container').css('display', 'flex');
        $('#card_modal_content').css('display', 'flex');
        $('#box_mbti_style_url_me').css('display', 'flex');
        $('#card').css('visibility', 'hidden');
        $('#box_img_file').css('display', 'block');

        $('#cur_img').change(function () {
            const file = this.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = function (e) {
                    $('#circle_img').attr('src', e.target.result).show();
                }
                reader.readAsDataURL(file);
            }

        });


    } else {
        // 셋팅
        $('#other_grid_mbti').text(mbti)
        $('#other_grid_style').text(style)
        $('#other_grid_url').attr('href', link)
        $('#circle_img').attr('src', img)

        // 남의 카드일 시, 실행될 곳
        $('#card_modal_container').css('display', 'flex');
        $('#card_modal_content').css('display', 'flex');
        $('#box_mbti_style_url_other').css('display', 'grid');
        $('#card').css('visibility', 'hidden');
    }

}

 

2) Update를 하지않았을 때, 갱신 오류

내 프로필카드를 열었을 때,

저렇게 프로필 카드를 열면 자신의 프로필이면 입력창이 나온다. 여기서 문제점이 많이 발생하였다. 

1) Input 데이터 수정하고 Cancel 버튼을 누를 시, 입력데이터가 Firestore로 저장되진 않지만 페이지에 남아 있는 오류

2) Input 파일데이터를 넣거나 넣지않을 때 처리 오류

 

1)의 문제는 그냥

    window.location.reload()

해서 해당페이지를 리로드해서 초기화시켰다.

 

절대 좋은 방식은 아니며 사용자가 Cancel 할 때마다 페이지가 갱신되어 불편하다. 

 

내가 생각하기에 좋은 방식은 해당 프로필을 누를 때, 기존에 입력된 데이터를 리스트 혹은 딕셔너리 형태로 저장 후,

input에 change 리스너를 붙혀 값이 달라지면 비교해서 바뀐값을 식별할 수있게 해줘야한다.

 

 또, Update 버튼을 없애는게 맞다. 굳이 저런 형식으로 줄거면 Update 버튼이 있을 필요가 없다. 설계를 제대로 안하고 구현부터 한 결과가 이렇게 스노우볼이 굴러왔다.

 

정리하자면가장 좋은 것은

- 프로필사진에만 update 버튼을 주기

- 나머지 input은 change 리스너를 넣기 (값이 바뀐 것을 파악)

- 프로필 들어갈 시, 기존 데이터(이전 데이터)는 따로 리스트 혹은 딕셔너리로 저장

인 것 같다. 아직은 뭐가 맞는지 잘 모르겠다.

 

2)번의 문제는 단순히 input에 데이터가 다 들어가야 한다고 생각해 input file에도 빈값이 잇으면 안된다는 로직을 넣었는데 그렇게 할 것이 아니라 input text에만 빈값이 없는지 검사한 후, input file에 파일이 추가되었는지 안됬는지 해야한다. 거기에 따라 수정로직도 바뀔 것이다.


3. Firestore Delete (Javascript)

당연히 프로필을 등록하고 삭제도 가능해야한다. 그렇기에 삭제버튼을 구현했다.

프로필을 등록할려면 로그인 등록해야한다 (회원가입 - 로그인 - 프로필 등록)

그렇기에 굳이 유저데이터를 전부 지우긴 싫어서 삭제하는 방법을 찾아보았다.

문서에 있는 필드만 지우고 싶은 방법을 찾아보니 deleteDoc 이런걸 쓰는게 아닌

update 방식으로 그 필드에 

date: deleteField()
await updateDoc(doc(db, "users", find_uid), {
        confirm: '0',
        date: deleteField(),
        link: deleteField(),
        mbti:deleteField(),
        shor: deleteField(),
        style: deleteField(),
        url: deleteField()
    });

 

해주는 것이다. 정말 간단했다.

 

//알아서 쓰시길
import { doc, updateDoc, deleteField, deleteDoc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";

// 내 프로필 삭제할 때,
$('#modal_delete').on('click', function () {
    handleDeleteClick()
});

async function handleDeleteClick() {

    let my_id = sessionStorage.getItem('user_id')
    let find_uid
    
    // uid 찾기
    const querySnapshot = await getDocs(q_find_me);
    querySnapshot.forEach((doc) => {
        let row = doc.data();
        let id = row['id']
        if (my_id == id) {
            find_uid = row['uid']
        }
    })

    console.log(find_uid)
    // 멤버 등록 정보만 삭제
    await updateDoc(doc(db, "users", find_uid), {
        confirm: '0',
        date: deleteField(),
        link: deleteField(),
        mbti:deleteField(),
        shor: deleteField(),
        style: deleteField(),
        url: deleteField()
    });

    $('#card_modal_container').css('display', 'none')
    $('#card').css('visibility', 'visible')
    $('#box_mbti_style_url_me').css('display', 'none')
    $('#box_img_file').css('display', 'none');
    $("input[type='file']").val(null)
    alert('프로필이 정상적으로 삭제되었습니다.')
    window.location.reload()
}

4. 미니프로젝트 끝?