[개발일기] 파이어베이스로 만든 일기장에 추가기능 구현하기 - 달력에서 사진 보여주기, 날짜 클릭하면 해당 날짜의 일기 보여주기

박소정·2022년 10월 20일
0
post-thumbnail

이제 마지막 단계만 남았다!

바로 저장된 일기들을 불러와서 해당 일기가 쓰여진 날짜의 백그라운드로 저장된 사진을 보여주는 것과 백그라운드에 저장된 사진이 보여지는 날짜를 클릭했을 때 해당 일기를 보여주는 작업을 하면 일단 끝이난다..!

이 부분이 가장 어려울거라는 생각이 있긴 했는데, 시작하자마자 에러가 터져나왔다 🤯

Error log

우선 첫번째 에러!

파이어베이스에서 불러온 데이터들이 null값으로 뜨는 문제

달력이 이미 불러와져있는 상태에서 데이터를 가공하면 원하는대로 돌아가던 로직이 새로고침을 하면서 새로 데이터를 불러오면 에러가 나서 달력 전체가 불러와지지 않는 에러였다.
로직을 지우고 하나하나 콘솔을 찍어보니 콘솔 사랑해 처음 불러올때 null값이 찍히는 것이 확인 되었다.
그래서 이 부분은 documents&&을 추가해주어 documents가 falsy한 값이 아닐때만 동작하게 하였다!

다음 에러는 두구두구두구두구두구

Uncaught RangeError: Invalid time value

아무래도 날짜함수 관련 라이브러리인 date-fns에서 발생하는 에러로 보여졌는데, 찾아보니 유효하지 않은 날짜형식이라 나는 에러로 보여졌다...! 띄용! 나 분명 같은 함수로 format을 바꾼거였는데 말이쥐!
찾다보니 Date 생성자 함수에 넣어서 toISOString()함수로 하니 해결되었다는 블로깅을 찾아 해보았는데도 여전히 에러가 떴고, stackoverflow의 한 글에서 Date생성자 함수 안에 undefined가 들어간거 아니냐는 말에 콘솔에 찍어봤더니 두둥! undefined가 떴다! 아니 근데 대체 와이?!
흠 근데 undefined가 뜨지않게 Date 생성자 함수를 만들어도 여전히 에러가 떠버렸다 쥬륵

다른 방법을 생각해보다가 비교하는 날짜의 포맷을 저장하는 형식과 맞추는 걸로 시도해 보기로 했다.

doc.date === format(day, "yyyyMMMdd")

이런식으로 현재 비교되는 날짜의 포맷을 반복문을 도는 중에만 맞춰주니 이 문제는 해결이 되었다

그러나 또 다른 에러가


파이어베이스에서 받아온 데이터는 배열형태여서 map을 돌며 일기가 쓰여진 날짜와 현재 달력의 날짜가 같은지를 확인하고, 만약 날짜가 같다면 인라인 스타일로 해당 배열에서 첨부된 사진파일을 background-image로 넣을 것을 생각했다.
이 코드에서 한가지 걸렸던 점은 달력을 그릴때에도 반복문을 돌고, 그 안에서 매 날짜마다 일기장 배열 map을 돌려 Big O notation관점에서 좋지 않다고 생각되어 더 좋은 방법이 있을까 계속해서 고민하고 있지만 마땅한 방법이 떠오르지않아 일단 완성을 시켜두고 개선해나가는 방향으로 계획하였다.

style={{
            backgroundColor: `${
              documents &&
              documents.map((doc) => {
                if (doc.date === format(day, "yyyyMMMdd")) {
                  return "red";
                }
              })
            }`,
          }}

처음에는 이렇게 로직을 짜보았는데 한개의 데이터만 있을때에는 동작을 했지만, 여러개가 쌓이니 동작을 하지 않았고,

map안의 if문에서 return을 해주면 안된다는 경고가 떠버렸다! 흠!

그래서 삼항연산자로 return 문을 바꾸었더니 에러도 뜨지않고 아무런 변화도 일어나지 않았다...!
하나하나 찍어보니 여전히 새로고침을 해도 null이 뜨고 있었다!
엥!
이정도 되면 데이터를 받아오는 부분부터 문제일것같다 코드를 하나하나 까보기 시작했다.
(사실 데이터를 받아오는 부분은 강의로 시작했기 때문에 공부를 미루고 있었는데 이렇게 공부하게 되었다 ㅎㅅㅎ)
데이터는 onSnapShot이라는 파이어베이스 함수를 통해서 받아오고 있었는데 이것의 파라미터부터 찍어봤다!

const unsubscribe = onSnapshot(
      myQuery ? q : collection(appFireStore, transaction),
      (snapshot) => {
        let result = [];
        snapshot.docs.forEach((doc) => {
          result.push({ ...doc.data(), id: doc.id });
        });
        setDocuments(result);
        setError(null);
      },
      (error) => {
        setError(error.message);
      }
    );

기존의 코드는 snapshot의 docs에 접근에 반복문을 돌리는것이었는데... 콘솔에 찍힌 docs에는 배열로 보이는 무언가가 없었다...! 소오름! 물론 공식문서에서도 docs에 관련한 부분을 찾아볼 수 없었다...!
오히려 docChanges에 데이터들이 들어있었다...! 물론 엄청 객체에 감싸져있긴하지만!

공식문서를 읽어보니 docChanges함수를 이용해 데이터를 가져오면 기존문서에 대한 정보도 가져올 수 있고, 수정사항, 삭제된 정보도 받아올 수 있다는 설명이 있었고, 공식문서를 따라가보았다.

snapshot.docChanges().forEach((change) => {
          if (change.type === "added") {
            result.push({ ...change.doc.data(), id: change.doc.id });
          }
           if (change.type === "modified") {
             result.push({ ...change.doc.data(), id: change.doc.id });
           }

그런데 이렇게 고쳐도 컴포넌트를 넘어가니 또다시 null 이 찍히고 있었다!!!!

흠! 뭔가 타이밍이 맞지 않는것 같지만 그렇다고 hook 으로 분리해둔 함수를 다 들고오면 컴포넌트가 정리가 되지 않을것 같아 (이 방법은 너무 쉽게 구현하는 방법인 것 같아 왠지 쓰고싶지 않았는데......)props로 넘겨주는 부분에서
documents={documents !== null && documents}이렇게 처리해 주면서 null이 찍히는 문제는 해결하였는데 새로운 에러를 발견했다.

새로운 일기를 추가해보니 새로 추가된 일기는 바로 반영이 되는데, 이전에 있던 일기들은 새로고침을 해주어야만 반영이 되었다. 찾아보니 "added"는 새로 생성되는거고 "modified"는 수정될 경우인데, 새로고침을 했을 경우에는 기존에 있던 정보들이 "added"상태로 넘어오지만 새로운 데이터가 추가되게 되면 새로운 데이터만 "added"상태로 넘어오기 때문에 기존의 데이터가 보이지 않는것 같았다..........

처음 코드에서는 그래도 실시간 데이터가 모두 반영이 되었던것 같아 원래 코드로 고쳐보았더니 새로 데이터가 추가되어도 바로바로 반영이 되었다!

나 이틀동안 뭐한거야?!ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

forEach로 각각의 .data()로 찍어봤더니

각각의 데이터가 찍히고 있었다!(이거 먼저 찍어볼껄.... 이제 함부로 다른걸로 넘어가지 않을것....)

그리고 다시 스타일로 넘어가서 삽질을 하다가 내가 너무 좁게 생각하고 있음을 깨달았다!
단순히 이미지를 backgroundImage로 줘야하니까 style props안에서만 생각하고 있었는데 생각해보니 클릭이 일어나면 해당 일기도 보여줘야하니까 전체 다이어리를 보여주기도 했어야하는데 그부분까지는 생각도 못하고 style props 내에서만 삽질을 하고 있었다!

각 날짜를 그리는 li태그를 만드는 함수 윗부분에서 데이터가 저장되어있는 날짜를 변수에 저장시키고 그 변수가 undefined인지 아닌지를 기준으로 background를 시험삼아 넣어보았더니

드디어 성공~!

그런데 또 다시 문제는 가져와진 사진이 보여지지 않는다는거다~~~ ( 나중에 보고 안거지만 url로 감싸는걸 까먹은 거였음 ㅎㅅㅎ url로 감싸니...
성공... 사이즈가 안맞아서 이렇긴 하지만....)

사이즈 조절까지 해주어서 우선은
이렇게 성공!!

그런데 또 다른 문제는 삽질중에 이미지를 콘솔에 찍어보니 엄청난 문자열로 나왔는데 이 문자를 보는 순간 한 기억이 떠올랐다... 바로 프로젝트를 하던 당시에 사진 데이터를 저장하고 불러오는 과정에서 이렇게 떠서 용량 문제로 s3를 사용했던 기억이 났다... 파이어베이스 쓰는데 s3도 붙여서 써야하나? 하는 생각이 들어서 다시 검색 ㄱㄱ...

검색하다보니 생각보다 공사가 많이 남은걸 깨달았다!ㅋㅋㅋㅋㅋㅋ 우선 파이어베이스에서도 s3와 같은 서비스로 스토리지라는 개념이 존재했고, 당장 사진은 백그라운드로 불러와져서 상관은 없지만 용량을 생각한다면 스토리지를 사용하는 것이 좋아보였다!

파이어 스토리지를 바로 연결을 해볼까 하다가 일단 완성하고 연결해 보는걸로..!


사진이 있는 날짜를 클릭하면 일기 보이게 하기

사진이 있는 날짜 셀을 클릭하면 작성된 일기를 보이게 하기 위해서는 우선 현재 어떻게 동작하고 있는지를 짚어보자!
1. 일기를 작성하고 싶은 날짜를 클릭
2. +버튼을 클릭해서 일기 작성 폼이 열린다.
이때, context api로 모달을 여는 boolean값과 일기가 기록되는 date값을 넘겨주고 있었는데, 이 부분을 활용해서 모달을 띄워볼까 싶었다.


diary context부분에 데이터 값을 넘겨주고, 만약 일기가 쓰여진 곳이라면 data를, 아니라면 null 값을 넘겨주어 모달을 띄울 계획이다.


모달이 띄워질 부분을 해당 데이터 값이 null일 경우에만 diary를 작성하는 폼을 불러오게 하였고, null이 아닐 경우 일기를 보여줄 폼을 설정하였다.

그리고 <Diary />안에서는 context api에 접근해서 data 부분을 빼왔고, 이 데이터를 펼쳐내었더니

두둥!


일기가 길게 써질때 스크롤도 만들어서 디자인까지 완! 저거 테스트로 작성한 내용 웃기긴한데... 모자이크하기 귀찮아...


그리고 파비콘도 만들었다 헤헿

놓치고 있었던 부분을 잡자!

그리고 마지막으로 섬세하지 못했던 부분을 보충해주었다.

이미지 파일만 받기!

form 부분의 input에서 이미지 파일만 업로드 할 수 있도록
accept="image/*"코드를 추가해 주었다.

폼에 사진이 빈값이라면 업로드되지 않게 하기!


사진이 들어가는 변수가 빈값이라면 alert를 띄우게 구현하였다!조건문을 걸때 덜 발생할 확률을 else문으로 넣는 것이 효율에 좋다던데 사진을 기본적으로 다 넣겠지...?사진일기장인걸...

선택되어진 날짜에 이미 일기가 존재한다면 일기 보여주기(alert였던것)

이 부분을 어떻게 구현할 수 있을지 고민하다가 context api의 data 값을 활용해 보기로 했다. 현재까지는 모달을 닫는 순간 data값을 null로 설정하는데 그렇게 되어버리면 이미 일기가 기록된 날짜에도 일기를 쓸 수 있는 모달이 열리게 된다.

그래서 우선 모달이 닫히는 순간에도 data를 기억할 수 있게 해주었는데............

그렇게 되어버리면... 다른 날짜 셀들을 클릭해도

이렇게 data가 남아있단 말이쥐?!

기록된 일기를 받아오는 것을 각 날짜 셀에 집어넣어야하기 때문에 반복문을 계속해서 돌려야하는데 이 작업을 여러번 해버리면 좋지 않을것 같아 일단 날짜 셀을 만드는 부분으로 가서 data가 없다면 클릭할때마다 이 데이터를 지워주는 방법을 생각하였다.


이렇게 클릭할 때마다 데이터가 없으면 null이 되는것은 구현해 주었고, 일기가 있으면 alert를 띄우는것을 구현하려고 +버튼을 클릭하는 부분에도 우선 context api를 통해 데이터를 받아오는 부분을 구현하려고 코드를 추가했는데

이렇게 추가를 시켰더니 이미 데이터가 있다면 모달 부분의 조건문에 의해 +버튼을 누르면 바로 일기장이 보여지게 구현되었다...!얻어걸렸다...!

사실 alert보다는 이 방향이 더 맞는 방향인것 같아 이대로 구현하기로 했다!
버그가 아닌 기능입니다..... 사실 기능 맞다구요.... 이걸 원했어요...... 손이 머리보다 빨랐을뿐....

삭제 기능 구현하기!

이 부분은 firebase를 이용하는 커스텀 훅으로 미리 만들어 두었어서 delete부분을 클릭하면 해당 함수가 작동하도록 추가해주었다!

정말 삭제하시겠습니까? confirm 넣어주기!


이렇게 confirm을 넣어주어 확인 버튼을 눌렀을 경우에만 삭제가 진행되고, 취소버튼을 클릭하면 다시 일기장을 보여주게 된다.


이렇게 우선 기본 기능은 구현이 끝났다...!

0개의 댓글