Google Firebase와 React를 이용한 트위터 클론 만들기 3

Hyuno Choi·2021년 2월 3일
0
post-thumbnail

소스 코드: https://github.com/soonitoon/dwitter
"드위터" 페이지(모바일) : https://soonitoon.github.io/dwitter

이 글은 Nomad Coder"트위터 클론코딩" 강의를 바탕으로 작성되었습니다.


2021년 2월 1일 저녁

지난번에 이은 트위터 클론코딩 3편 입니다.

Home 컴포넌트의 자식인 DwitteFactory에 이어 Dwitte 컴포넌트부터 적겠습니다.

Dwitte

Dwitte 컴포넌트는 홈 화면에서 전체 드윗이 아닌 '각각의 드윗'을 담당합니다. 아래의 코드는 Home 컴포넌트의 JSX 리턴 부분입니다.

여기서 Dwitte 컴포넌트는 Home 컴포넌트의 dwittes 리스트에서 map()을 통해 각각의 prop을 전달받습니다.

isOwner의 경우 드윗 객체의 creatorId와 유저 객체의 uid 값을 비교해서 판단합니다.

기본적으로 자신의 드윗은 닉네임본문, 첨부 이미지와 더불어 삭제 버튼과 수정 버튼이 보입니다.

다른 사람의 드윗일 경우 삭제 버튼과 수정 버튼은 보이지 않습니다.

state

Dwitte 컴포넌트에서 사용하는 state는 두 개입니다.

  1. editing - 수정중인지 아닌지를 true, false로 저장합니다. 만약 수정중이라면 수정 Form을 띄우고, 아니라면 본문을 보여줍니다.

  2. newDwitte 수정 Form에서 글을 수정할 때 수정 내용을 저장합니다.

함수 구현

수정 토글

수정 모드를 켜고 끄는 간단한 토글 함수입니다. 로그인 토글과 마찬가지로 setState로부터 기존값을 인자로 받아 반대값으로 바꿔주는 콜백 함수를 작성했습니다.

드윗 수정

수정 Form input에서 value를 받아와 newDwitte state에 저장하는 함수입니다. 이것 역시 로그인 부분의 onChange 핸들러와 다르지 않습니다.

수정 제출

수정한 본문을 Firestore에 업데이트 하는 함수입니다.

  1. firestore의 doc 메소드의 인자로 콜렉션 이름/문서 이름 문자열을 넘겨서 해당 드윗 문서 레퍼런스를 받아옵니다.

  2. 그리고 레퍼런스의 update 메소드에 해당 드윗의 변경된 text가 담긴 객체를 넘겨줍니다.

  3. setEditing(false)을 통해 수정을 종료합니다.

드윗 삭제

드윗 삭제 과정은 다음과 같습니다.

  1. 삭제하기 전에 window 객체의 confirm 메소드로 대화상자를 통해 사용자에게 확인을 받습니다. 확인 결과는 ok 변수에 저장합니다.

  2. Cloud Firestore의 문서 중 dwitte 콜렉션의 해당 dwitte id로 해당 데이터를 찾고, delete 메소드로 삭제합니다.

  3. Firebase Storage의 refFromURL 메소드를 통해 삭제할 첨부파일의 레퍼런스를 받아옵니다. 매개변수에는 dwitteObjattachmentURL을 넘겨줍니다.

  4. 받아온 레퍼런스의 delete 메소드로 해당 파일을 삭제합니다.

현재 코드에서는 드윗 객체의 attachmentURL이 빈 문자열이던 이미지 다운로드 URL이던 무조건 스토리지 삭제 코드를 실행하고 있습니다.
attachmentURL이 빈 문자열이 아닐 때만 삭제 코드를 실행하도록 조건문을 추가해야겠습니다.

✔️ 완성된 드윗 수정, 삭제 기능입니다!

JSX

Dwitte 컴포넌트가 렌더링하는 JSX 코드의 구조입니다. 긴 부분은 주석으로 잘라냈습니다.

첫 번째로 만약 editing state가 true라면 수정 Form을 렌더링하고, false라면 본문을 렌더링합니다.

그리고 본문에서 Home으로부터 전달받은 isOwner가 true라면 삭제 및 수정 버튼을 추가로 렌더링합니다.

Profile

사용자가 프로필 페이지에서 할 수 있는 것은

  • 로그아웃
  • 닉네임 수정
  • 자신이 작성한 드윗 모아보기

이렇게 세 가지 입니다.

state

Profile 컴포넌트는 두 개의 state를 가집니다.

  1. dwitteArray - 자신이 작성한 드윗 객체들을 가져온 뒤 이 state에 리스트로 저장합니다.

  2. newDisplayName - 사용자가 닉네임 수정 input 태그에 작성한 내용을 저장합니다.

함수 구현

로그아웃

로그아웃 버튼의 onClick 핸들러 함수입니다.

로그아웃 자체는 firebase.authsignOut 메소드를 이용해서 간단히 구현했습니다.

그리고 로그아웃 시 홈페이지로 돌아가게 하기 위하여 react-router-domuseHistory 후크를 사용했습니다.

내 드윗 가져오기

Profile 컴포넌트의 useEffect에서 호출하는 콜백 함수입니다.

firestore의 메소드를 사용해서 현재 로그인 된 유저가 작성한 드윗만 불러오는 과정은 다음과 같습니다.

  1. collection("dwitte")을 통해 dwitte 콜렉션 레퍼런스를 가져옵니다.

  2. where("creatorId", "==", userObj.uid)을 통해 dwitte 콜렉션 중 creatorIduserObj.uid== 같은 쿼리를 가져옵니다.

  3. orderBy("createdAt")를 통해 기존 쿼리를 만들어진 시각에 따라 오름차순으로(기본값) 정렬한 쿼리를 가져옵니다.

  4. get() 대신 onSnapshot()을 사용해서 드윗의 삭제, 수정이 실시간으로 반영될 수 있도록 합니다.

  5. onSnapshot()으로 받아온 QuerySnapshot 객체에서 각각의 docs에 접근하기 위해 snapshot.docs.map()을 사용합니다.

  6. docs를 가지고 각각의 드윗 객체를 만들어줍니다. 첫 번째 속성으로 고유 id를 넣고, ES6 스프레드 연산자로 data()로 가져온 드윗 본문 내용들을 만들고자 하는 객체에 속성으로 할당합니다.

이렇게 만든 드윗 객체 리스트를 dwitteArray state에 저장합니다.

그리고 getMyDwittes 함수를 Profile 컴포넌트의 useEffect() 안에 넣어서 사이드 이펙트로 실행될 수 있도록 합니다.

닉네임 수정 제출

닉네임 수정 form을 제출하는 함수입니다. 수정 내용을 state에 반영하는 함수는 이전에 HomeAuthForm에서 구현했던 onChange 함수와 동일하므로 생략하겠습니다.

사용자의 닉네임 수정 여부를 검사한 후, userObjupdateProfile()을 통해 업데이트 할 내용인 displayNamenewDisplayName state로 하는 객체를 전달해줍니다.

여기서 userObjupdateProfile 메소드는 firebase의 User 객체 안에 있는 메소드로써, App 컴포넌트에서 userObj를 구현할 때 준 것입니다.

닉네임 수정 문제 1

처음에는 위에 설명한 부분까지의 방법으로 닉네임 변경 기능을 구현하였습니다. 그러나 displayName을 업데이트 해도 표시되는 닉네임은 바뀌지 않았습니다.😥

첫 번째 문제는 화면에 렌더링하는 displayName을 firebase가 아니라 App 컴포넌트에서 생성한 userObj에서 받아온다는 것이었습니다.

위의 방법으로 업데이트 되는 건 firebase의 유저 정보 뿐입니다. 따라서 userObj도 함께 업데이트 해주는 코드가 필요합니다.

userObj state 업데이트를 위해서 refreshUserObj 함수를 App 컴포넌트 내에 만들어줍니다. 수정한 이름을 매개변수로 받아서 현재 userObj state를 업데이트하는 간단한 함수입니다.

그리고 App 컴포넌트에서 만든 이 함수를 prop으로 Profile 컴포넌트에 전달해주고, onSubmit 함수에서 호출하면 바뀐 닉네임이 리렌더링되어 보일 것이라고 생각했습니다.

로그인 문제 2

Firebase의 유저 정보를 업데이트하고 App 컴포넌트의 userObj까지 업데이트 했음에도 불구하고 displayName은 새로고침 하기 전까지 업데이트 되지 않았습니다.

2편에서도 설명했듯이, firebase에서 그대로 받아 쓴 userObj의 덩치가 React가 변화를 감지하기에 너무 크기 때문이었습니다.

그래서 세 가지 속성만을 가진 userObj를 새로 만들게 되었고 refreshUserObj 함수도 그에 맞게 수정해주었습니다. (위의 코드는 수정된 버전입니다.)

이 오류를 계기로 중요한 사실을 알게 되었습니다.

지금까지는 setState()를 실행할 때마다 리렌더링이 무조건 일어난다고 생각했으나, 실제로는 React가 훨씬 능동적으로 state의 변화를 감지한다는 것을 알았습니다.

✔️ 이제 닉네임을 수정하면 정상적으로 네비게이션에도 반영됩니다.

지금까지 드위터 화면을 구성하는 컴포넌트에 대해 설명했는데요, 다음에는 CSS를 주제로 포스팅 하겠습니다.😃

profile
프론트엔드 웹 개발자를 목표로 하고 있습니다.

0개의 댓글