072DATA

Moview - JavaScript 별점 기능 설명(프로젝트 완성) 본문

FrontEnd/HTML, CSS, JavaScript

Moview - JavaScript 별점 기능 설명(프로젝트 완성)

0720 2024. 8. 7. 23:58

안녕


안녕하세요 오늘은 프로젝트 진행중에 별점 기능을 추가하여

해당 로직들을 살펴보고 설명해보는 글을 작성해보도록 하겠습니다.

 

또 자랑도 할 겸 이미지 캡처 올려드리고 깃허브와 배포 링크도 달아두겠습니다.

구경 한 번씩 해보시고  피드백이나 에러사항 있으면 댓글로 부탁드립니다.

 

 

 

Moview - 배포 링크

https://shyunis.github.io/Moview/

 

THISISMOVIE

THISISMOVIE Movie Make Dream

shyunis.github.io

 

 

Moview - 깃허브 링크

https://github.com/sHyunis/Moview

 

GitHub - sHyunis/Moview: 영화 커뮤니티

영화 커뮤니티. Contribute to sHyunis/Moview development by creating an account on GitHub.

github.com

 

이미지

 

 

 

변수 선언

const allStars = document.querySelectorAll('.star');
let lastClickedIndex = null;
let lastSaveScore = null;
let userRating = null;

const params = new URLSearchParams(window.location.search);
const movieId = params.get("id");
const loginId = sessionStorage.getItem("userLoginId");

 

 

 

별점 요소와 관련된 변수들을 초기화합니다.

각각 마지막으로 클릭된 인덱스의 값,

마지막으로 저장된 점수

그리고 데이터베이스에서 가져올 유저의 점수,

현재 페이지의 URL과

영화의 ID, 로그인 ID입니다.

 

 

 

이벤트 리스너 화살표 함수(DomContentLoaded)

document.addEventListener("DOMContentLoaded", async () => {
    userRating = await getUserScore(loginId, movieId);
    showStars(userRating);
    getAverageScoreForMovie(movieId);
    initializeStars(loginId, movieId);
});

 

 

 

페이지가 로드되면 사용자 평점을 불러오고, 이를 화면에 표시하며,

평균 평점을 계산하고, 별점 요소에 이벤트를 추가하는 함수들을 불러옵니다.

 

 

 

점수 저장 함수

async function saveScore(loginId, movieId, score) {
    try {
        const userRef = collection(db, 'userScores');
        const scoreQuery = query(userRef, where('loginId', '==', loginId), where('movieId', '==', movieId));
        const scoreQuerySnapshot = await getDocs(scoreQuery);

        if (!scoreQuerySnapshot.empty) {
            const docId = scoreQuerySnapshot.docs[0].id;
            const docRef = doc(db, 'userScores', docId);
            await updateDoc(docRef, { score });
        } else {
            await addDoc(collection(db, 'userScores'), {
                loginId,
                movieId,
                score
            });
        }
    } catch (e) {
        console.error("데이터 베이스 저장 오류: ", e);
    }
}

 

 

 

사용자 평점을 데이터베이스에 저장하는 함수입니다.

db에서 userScores라는 콜렉션을 가져와서

쿼리문의 where절에 따라서 해당 데이터가 비어있는지 확인하고

이미 존재하는 평점 기반의 데이터가 있으면 업데이트하고, 없으면 새로 추가합니다.

 

(데이터 가져올 때 anync, await 문법을 사용해 getDocs로 가져오는 데이터를 fullfill될 때까지 기다려줍니다)

 

데이터 불러오기

async function getUserScore(loginId, movieId) {
    try {
        if (loginId !== null) {
            const userRef = collection(db, 'userScores');
            const scoreQuery = query(userRef, where('loginId', '==', loginId), where('movieId', '==', movieId));
            const scoreQuerySnapshot = await getDocs(scoreQuery);
            lastSaveScore = scoreQuerySnapshot.docs[0].data().score - 1;
            console.log("실행됨");
        }
    } catch (error) {
        console.error(error);
    }
}

 

 

로그인이 되어있는지 확인하고 db에서 userScores라는

콜렉션을 저장할 때와 동일하게 가져온 뒤

데이터 베이스에 저장된 데이터 값을  쿼리의 where절에

맞게 불러온 뒤 사용자의 평점을 데이터베이스에서 불러오고

불러온 점수를 전역 변수인 lastSaveScore에 넣어줍니다.

 

(데이터 가져올 때 anync, await 문법을 사용해 getDocs로 가져오는 데이터를 fullfilled될 때까지 기다려줍니다)

 

 

영화에 매겨진 모든 평점 가져오고 평균을 뿌려주는 함수들

async function getMovieScores(movieId) {
    const userRef = collection(db, 'userScores');
    const scoreQuery = query(userRef, where('movieId', '==', movieId));
    const scoreQuerySnapshot = await getDocs(scoreQuery);

    if (!scoreQuerySnapshot.empty) {
        return scoreQuerySnapshot.docs.map(doc => doc.data().score);
    }
    return [];
}

 

async function getAverageScoreForMovie(movieId) {
    const scores = await getMovieScores(movieId);
    const moviewAverage = document.querySelector(".moview-average");
    const totalScore = scores.reduce((sum, score) => sum + score, 0);
    const averageScore = (totalScore / scores.length) * 2;
    const resultScore = `<span>Moview</span><p>${parseFloat(averageScore.toFixed(1))}</p>`;
    moviewAverage.innerHTML = resultScore;
}

 

 

getMovieScores함수에서 db에서 점수를 가져오고 쿼리문으로 영화 ID를

확인한 뒤 해당 데이터가 존재하면 return 해줍니다

 

getAverageScoreForMovie() 함수에서 getMovieScores로 받은 

모든 사용자 평점을 이용해 영화의 평균 평점을 계산합니다

쿼리 셀렉트를 사용하여 변수에 담아서 innerHTML로 해당 점수를 덮어 씁니다.

 

 

 

별점 클릭시 호출되는 함수

async function clickStars(loginId, movieId, index) {
    if (loginId !== null) {
        let currentStar = index + 1;
        let score = null;

        if (lastClickedIndex === index) {
            resetStars();
            lastClickedIndex = null;
        } else {
            showStars(currentStar);
            score = index + 1;
            lastClickedIndex = index;
        }
        await saveScore(loginId, movieId, score);
        getUserScore(loginId, movieId);
    } else {
        resetStars();
        alert("로그인 해주세요");
        window.location.href = '/view/member_login.html';
    }
}

 

 

사용자가 별점을 클릭했을 때 해당 별점을 저장하고, 별점 UI를 업데이트합니다.

로그인 되었는지 조건문을 걸고 현재 별점의 값과 점수에 대한 변수를 선언하고

if / else 문으로 별점을 초기화하거나, 별점을 보여주고 해당 점수를 데이터 베이스에

저장할 수 있도록 함수를 호출합니다.

 

만약 로그인 되지 않은 유저라면 로그인 페이지로 이동시킵니다.

 

 

별의 상태를 업데이트하는 함수

function showStars(score) {
    allStars.forEach((star, i) => {
        if (score > i) {
            star.innerHTML = '&#9733';
        } else {
            star.innerHTML = '&#9734';
        }
    });
}

 

 

 

 forEach문으로 주어진 점수와 인덱스를 비교하여

각각의 별에게 빈 별의 유니코드 혹은 채워진 별의 유니코드를 붙여줍니다.

allstars는 맨 처음 전역 변수로 지정하였습니다.

 

 

별에서 마우스 뗐을 때 호출되는 함수

function starMouseOut() {
    showStars(lastSaveScore !== null ? lastSaveScore + 1 : 0);
    getUserScore(loginId, movieId);
}

 

 

 

마우스가 별점에서 벗어났을 때, 이전에 저장된 별점으로 돌아갑니다.

조건문은 lastSaveScore가 값이 없는게 참이면 lastSaveScore +1 을

아니면 0을 반환한 뒤 유저의 점수를 불러오는 함수를 호출합니다.

 

 

 

별의 상태를 초기화 하는 함수

function resetStars() {
    allStars.forEach((star) => {
        star.innerHTML = "&#9734";
    });
}

 

 

 

forEach문으로 모든 별들에 빈 별의 유니코드를 붙여줍니다.

 

 

 

별의 상태를 초기화 해주는 함수

function initializeStars(loginId, movieId) {
    allStars.forEach((star, i) => {
        star.onclick = () => clickStars(loginId, movieId, i);
        star.onmouseover = () => showStars(i + 1);
        star.onmouseout = starMouseOut;
    });
}

 

 

 

해당 함수가 호출되면 각 별의 요소들에게 클릭하거나,

마우스를 올려뒀거나, 마우스를 뗐을 때 필요한 함수들을 호출합니다.

 

 

결론

해당 로직을 사용함으로서 별점 기능을 구현하여 사용자의 경험을 효과적으로 향상시키고

내가 매긴 별점의 데이터를 불러와서 수정하거나 삭제하고 다시 저장할 수 있습니다.

또 전역 변수를 사용하여 변수를 여러 번 선언할 필요 없이 코드의 가독성이 올라갔으며 데이터베이스에서

데이터를 가져올 때 쿼리문을 사용하여 필요한 부분만 가져왔습니다.

 

 

 

 

마무리

오늘 팀프로젝트가 마무리 되었고 가장 어려웠던 부분을 간단하게 정리하였는데

정리하면서 이해가 덜 됐거나 내걸로 만들지 못했던 부분들을 다시 한 번 상기 시켰다

역시 공부는 필기야.. 어쨌든 이제 한 걸음이 시작 됐는데 내 발이 무거워지지 않도록

쭉 나아가 보고 싶다