본문으로 건너뛰기

Study

localStorage

로그인을 하고 새로고침을 하면 로그인했던 정보가 날라가서 정삭적으로 렌더링이 안된다. localStorage를 사용해 로그인을 하면 유지되도록 만들었다.

//Login.js
const submitHandler = (event) => {
event.preventDefault();
setId("");
setPw("");
if (idReady === true && pwReady === true && tryNum > 4) {
acceptLogin.forEach((item) => {
console.log(item);
if (item.id === id && item.pw === pw) {
idChange(item.name);
localStorage.setItem("loggedIn", item.name);
navigate("/Home");
} else {
setIsModalOpen(true);
}
});
}
};

정상적으로 로그인이 동작하면 key 값으로 loggedIn, value 값으로 로그인된 이름을 넘기고

//Layout.js
const loggedInId = localStorage.getItem("loggedIn");

<div>{loggedInId}, 안녕하세요!</div>;

layout 컴포넌트에서 가져와 사용했다.

const logoutHandler = () => {
localStorage.removeItem("loggedIn");
navigate("/");
};

로그아웃 버튼도 만들어 handler를 동작하게 했다.

firebase

backend와 연결

어제 firebase 동작을 연습했으니 프로젝트에 적용시켜 봤다.

Create

const createMemo = async () => {
try {
const collectionRef = collection(db, "Memo");
const docRef = await addDoc(collectionRef, {
title: title,
content: content,
});
} catch (error) {
console.error("Error happend : ", error);
}
};

어제는 setDoc으로 바꾸어 문서값을 지정하려고 했으나, 문서값을 지정해서 딱히 할게 없었다.(그냥 키 값일 뿐이라)
addDoc으로 수정해 만들었다.

Read

const readMemo = async () => {
try {
const memoSnapShot = await getDocs(collection(db, "Memo"));
setContents(
memoSnapShot.docs.map((doc) => {
return { ...doc.data(), id: doc.id };
})
);
// setContents(prev=>memoSnapShot.docs);
} catch (error) {
console.error("Error happened:", error);
}
};

처음에 getDocs로 받아온 값 자체에 .map을 적용시켰더니 적용이 되지 않았다.
반환값은 querySnapshot이였는데, .forEach가 적용되어서 .map은 왜 안되지하고 봤더니 .forEach함수가 내장되어있어서 사용이 가능한 거였다.
.docs를 붙이면 배열을 반환하고, .map을 적용할 수 있었다. 각 배열값들에 .data()를 적용하면 필드값들을 가져온다.


setContents(
memoSnapShot.docs.map((doc) => {
return { ...doc.data(), id: doc.id };
})
);

이 코드를 짜는게 생각보다 어려웠다. 각 문서값과 id에 접근하여 내가 사용할 수 있는 state에 배열로 집어넣어야 하는데, .map의 반환값이 배열이란 사실을 헷갈려서 고민을 많이 했다.


추후 delete, update에 사용하기 위해 doc.id (문서값)도 state에 저장했다. 이렇게 만든 state은

{
contents?.map((item) => {
return (
<MemoList
key={item.id}
title={item.title}
content={item.content}
id={item.id}
onDelete={deleteHandler}
contentChange={contentChangeHandler}
></MemoList>
);
});
}

map을 통해 뿌려줬다.
코드를 짜면서 궁금한 점이 생겼다. readMemo에서 코드를 받아오는 동안, 코드가 실행되면?
즉 비동기 처리가 완료되기 전에 비동기 반환값을 렌더링 시켜버리면?


error 발생 혹은 버퍼링 발생. 버퍼링은 ssr이냐 csr이냐에 따라 조금 다르긴 한데, 암튼 컴포넌트가 나오는데 까지 시간이 걸린다. 이를 해결하는 방법으로 suspense, fallback, react-query등을 사용할 수 있다. suspense를 걸어 아직 요청한 값이 도착하지 않았으면 fallback함수를 실행시켜 loading 중이라는 것을 표시한다. react-query로 이를 단순화 시킬 수 있다.

Delete

const deleteMemo = async (id) => {
try {
const docRef = doc(db, "Memo", id);
await deleteDoc(docRef);
} catch (error) {
console.error("Error happened", error);
}
};

delete는 비교적 간단하게 구현했다. 다만 한가지 주의할 점은, Create을 하든 delete을 하든 그 뒤에 read을 해줘야 했다.
서버에서 값을 만들거나 지우기만 할 뿐, 새로 읽어들이진 않았다.

실시간 반영

이를 반영하기 위해 처음 생각했던 방법은 useEffect를 사용하려했지만, 이는 무한순환이 야기 되었다.
또 다른 방법으로 실행이 될 때마다 setContents를 변경하는 방법이였는데, 이는 서버의 값과 이질적인 느낌이 든다고 생각했다.
그래서 각 함수들이 실행되고 readMemo()역시 실행되도록 구현했다.

Update

그냥 state배열을 사용할 땐 props로 editContent, content를 들고 다녔는데 벡엔드와 연동하여 하려고 하니 content값(필드의 값)에 직접 접근하는게 쉽지 않았다.
content가 아닌 id값을 전달하니 한결 수월해졌다.

const updateMemo = async (editContent, id) => {
try {
const docRef = doc(db, "Memo", id);
await updateDoc(docRef, { content: editContent });
} catch (error) {
console.error("Error happened:", error);
}
};

Optimistic Update

edit 버튼을 누르면, 조금의 시간이 흐르고 바뀐다. 서버의 값을 변경하는 과정에서 생기는 delay인데, 사용자가 보기에 좋지 않아보였다.
이를 해결하는 방법으로 낙관적인 업데이트가 있다. 브라우저에는 이미 동작한 것처럼 보여주고, 서버에 값 반영은 추후에 하는 것이다.

git add

저번에 git add .을 하면 숨김파일은 업로드 되지 않는다고 했는데, 다시해보니 업로드가 되었다. 그래서 어쩔수 없이 파일명을 그대로 쓰는 수밖에 없었다.
근데 내 md파일들의 경로는 유니코드로 만들어져 있어 그냥 경로만 치면 올릴 수 없었고 git add $''를 통해 올릴 수 있었다. ""는 안되고 ''만 된다.

I

좋아함의 정도