어제에 이어 프로젝트를 진행했습니다.
오늘은 총 세가지의 일을 진행했습니다.
첫번째는 메인 화면의 정보를 상세 페이지로 넘겨주는 것이고, 두번째는 기존에 작업했던 파이어베이스에서 데이터를 읽는 방식을 수정, 세번째는 팀원이 했던 작업을 이어서, 저장된 데이터를 사용자가 직접 수정할 수 있게 하는 것입니다.
1. 페이지 간 데이터 전달
먼저 메인 화면에 DB에 적재되어 있는 만큼 멤버카드를 만들 수 있게 합니다.
여기서 변수 uniqueID가 사용됐는데, 이는 각 멤버카드마다 고유 ID를 주기 위함입니다. 왜냐하면 .getElementById()가 태그에 적재된 id를 읽어오는데, 동일한 id가 있으면 가장 첫번째 id만 읽어오기 때문입니다.
이렇게 각 멤버카드마다 고유 ID를 생성하고, 이를 클릭하면 이벤트가 발생합니다.
아래 코드는 지정된 url 쪽에 "log"와 연결된 곳애 변수 name에 저장된 값을 보낸다는 뜻입니다. 이를 통해 메인 페이지에서 상세 페이지로 넘어갈 때, 상세 페이지가 무엇을 보여주면 좋을지 알려주는 역할을 하게 됩니다.
url.searchParams.append("log", name);
아래는 전체 코드입니다.
여기서 url은 page.html 입니다.
왜냐하면 this.이 현재 실행한 코드에서 자신이 속한 객체를 가리키기 때문입니다. 현재 uniqueId가 가리키는 href는 page.html이기 때문에, url은 page.html이 되었습니다.
다음은 index.html에서 넘어온 값을 page.html이 받는 코드입니다.
코드는 다음과 같습니다.
1. 데이터 읽는 방식 수정
아래 코드는 기존에 사용하던 방법입니다. forEach()를 사용했고, logValue와 데이터베이스에 있는 name이 동일하면 값을 가져오는 방식입니다.
let docs = await getDocs(collection(db, "albums"));
docs.forEach((doc) => {
let row = doc.data();
// 원하는 값을 가져오는 건 이름으로 판별
if(row['name'] == logValue) { // logValue는 해당 페이지 주인의 이름
let birth = row['birth']
let blog = row['blog']
let etc = row['etc']
let git = row['git']
let img = row['img']
let mbti = row['mbti']
let name = row['name']
let pros = row['pros']
}
});
하지만 이 방법은 비효율적입니다.
왜냐하면 forEach()는 모든 데이터를 순회하며 조건에 맞는 것을 찾기 때문입니다. 데이터가 많다면 그것들을 전부 순회하느라 네트워크 비용이 많이 들고, 클라이언트 측에서도 불필요한 연산이 발생합니다.
그래서 Firestore 쿼리를 사용하기로 했습니다.
쿼리는 조건에 맞는 문서를 직접 검색해서 찾아줍니다. 이는 네트워크 비용과 클라이언트 측의 연산 비용을 줄여주기 때문에, forEach() 보다 효율적입니다.
쿼리의 장점은 다음과 같습니다.
- 코드의 명확성: 쿼리는 조건에 맞는 문서만 가져오기 때문에, 쿼리 자체가 조건을 명확하게 보여준다 = 코드의 가독성과 유지보수성을 향상한다.
- 유연성과 재사용성: 필요에 따라 조건을 쉽게 변경하거나 추가할 수 있다. 예를 들어 where절에 다른 조건을 추가하거나, limit 값을 조정하여 반환되는 문서의 수를 제어할 수 있다.
다음은 사용한 쿼리문입니다.
// 특정 정보를 가져오는 함수
async function getCelebrityInfo(logValue) {
if (!logValue) // 찾는 값이 없을 경우
return;
// 쿼리 설정: "albums" 컬렉션에서 name 필드가 logValue와 일치하는 데이터
// limit(1)은 만약 동일한 필드가 있다면, 첫번째 데이터만 가져올 수 있도록...
const q = query(collection(db, "albums"), where("name", "==", logValue), limit(1));
const querySnapshot = await getDocs(q); // 쿼리 실행
if (!querySnapshot.empty) { // 일치하는 데이터가 있는지 확인하고,
const doc = querySnapshot.docs[0]; // 첫 번째 데이터를 반환
const row = doc.data();
return row; // 원하는 데이터를 반환
}
}
// 함수 호출
getCelebrityInfo(logValue).then(row => {
if (row) {
let birth = row['birth']
let blog = row['blog']
let etc = row['etc']
let git = row['git']
let img = row['img']
let mbti = row['mbti']
let name = row['name']
let pros = row['pros']
}
});
2. 사용자 데이터 변경
두번째는 팀원이 했던 파트를 이어 받아 파이어베이스 내의 데이터를 사용자가 변경할 수 있게 하는 일입니다.
그러기 위해선 일단 변경할 데이터의 ID를 찾고, setDoc()을 통해 데이터의 값을 수정해야 합니다.
async function getCelebrityInfo(logValue) {
if (!logValue) // 찾는 값이 없을 경우
return;
const q = query(collection(db, "albums"), where("name", "==", logValue), limit(1));
const querySnapshot = await getDocs(q); // 쿼리 실행
if (!querySnapshot.empty) { // 일치하는 데이터가 있는지 확인하고,
const doc = querySnapshot.docs[0]; // 첫 번째 데이터를 반환
const row = doc.data();
const docId = doc.id; // 문서의 ID를 가져옴
return { id: docId, data: row }; // 데이터와 ID를 반환
}
}
다만 주의할 점이 있는데, 해당 수정 페이지는 수정 마무리 버튼을 누르면, 데이터 수정과 동시에 메인 페이지로 이동하게 됩니다.
하지만 메인 페이지로 이동하는 과정에서 데이터가 저장되지 않는 사태가 발생했습니다.
메인 페이지로 이동하지 않을 때는 데이터 변경이 잘 됐는데, 이동만 하면 문제가 발생했습니다. 사용했던 코드는 다음과 같습니다.
async function getCelebrityInfo(logValue) {
const q = query(collection(db, "albums"), where("name", "==", logValue), limit(1));
const querySnapshot = await getDocs(q); // 쿼리 실행
if (!querySnapshot.empty) { // 일치하는 데이터가 있는지 확인하고,
const doc = querySnapshot.docs[0]; // 첫 번째 데이터를 반환
const row = doc.data();
const docId = doc.id; // 문서의 ID를 가져옴
return { id: docId, data: row };
}
}
function goToHomePage(docId, newData) {
// 페이지 이동
window.location.href = 'index.html';
}
let docs = await getDocs(collection(db, "albums"));
docs.forEach((doc) => {
let row = doc.data();
if (row['name'] == logValue) {
console.log(row); //가져온데이터 콘솔창에 표시
// 중략
}
});
$("#writebtn").click(async function () {
// 중략
let DDoc = {
// 중략
};
getCelebrityInfo(logValue).then(result => {
if (result) {
// 반환된 데이터를 기반으로 추가 작업 수행
setDoc(doc(db, "albums", result.id), DDoc);
}
});
alert('수정되었습니다!');
goToHomePage(); // 이 코드만 실행되면 데이터 변경이 안된다....
})
찾아보니, setDoc()은 파이어베이스에 데이터를 저장하는 비동기 작업입니다. 비동기 작업은 즉시 완료되지 않으며, 네트워크 지연이나 서버 처리 시간 등의 이유로 시간이 걸릴 수 있습니다.
그에 반면, goToHomePage()는 페이지를 즉시 리디렉션합니다. 비동기 작업이 완료되기 전에 페이지가 리디렉션되면 데이터가 저장되기 전에 페이지가 변경되고, 이 때문에 제대로 저장되지 않을 수 있습니다.
즉, 변경된 데이터를 저장하기 위해선 비동기 작업이 완료된 후 페이지르 리디렉션 할 수 있도록 코드를 짜야합니다.
이를 위해 await 키워드를 사용하여 비동기 작업이 완료될 때까지 기다립니다.
아래는 수정된 코드입니다.
$("#writebtn").click(async function () {
// 중략
let DDoc = {
// 중략
};
// 수정된 곳
const result = await getCelebrityInfo(logValue);
if (result) {
console.log("문서 ID:", result.id);
// 파이어베이스 데이터 업데이트
await setDoc(doc(db, "albums", result.id), DDoc);
alert('수정되었습니다!');
goToHomePage(); // 업데이트가 완료된 후 페이지 리디렉션
}
});
await는 총 두 곳에서 사용됐는데, 하나는 getCelebrityInfo() 이고, 다른 하나는 setDoc()입니다. 사용한 이유는 다음과 같습니다.
- getCelebrityInfo(): 함수가 반환하는 Promise 가 완료될 때까지 기다린다. 이 함수는 정보를 가져오는 비동기 작업을 수행한다.
- setDoc(): 파이어베이스에 문서를 업데이트하는 비동기 작업이 완료될 때까지 기다린다. 작업이 완료된 후에 goToHomePage()를 호출하여 페이지를 리디렉션한다.
Promise 는 JavaScript에서 비동기 작업의 완료 또는 실패를 나타내는 객체입니다. 비동기 작업이 완료되거나 실패했을 때 콜백 함수를 실행하며, async / await 를 사용하면 좀 더 직관적이고 동기적으로 처리할 수 있습니다.
- Promise는 비동기 작업의 완료 또는 실패를 나타내는 객체.
- async 함수는 항상 Promise를 반환한다.
- await 키워드는 Promise가 완료될 때까지 기다리며, Promise의 결과 값을 반환한다.
이러한 비동기 작업 관리는 데이터가 확실히 저장된 후에 다음 작업이 이루어지도록 보장합니다.
따라서 await 키워드를 사용하여 비동기 작업의 완료를 보장하고, 그 후에 페이지 리디렉션을 수행하면 데이터가 올바르게 업데이트됩니다.
'프로젝트' 카테고리의 다른 글
24/08/09 - 팀프로젝트 최종 회고 (0) | 2024.08.09 |
---|---|
24/08/08 - 팀프로젝트 일일 회고 4 (0) | 2024.08.08 |
24/08/06 - 팀프로젝트 일일 회고 2 (0) | 2024.08.06 |
24/08/05 - 팀프로젝트 일일 회고 1 (0) | 2024.08.05 |
마리오 게임 모작 (0) | 2024.04.22 |