TIL. Firebase를 이용한 실시간 데이터 베이스 구현

ichbinmin2·2021년 3월 24일
0

React

목록 보기
25/25
post-thumbnail

👩🏻‍💻 Toy Project(Teta Card Maker)의 진행 기록 : firebase의 realtime database를 활용한 데이터 저장 기능 구현

카드의 정보와 프로필 사진을 업로드 했으니, 마지막으로 소셜 로그인한 사용자가 작성한 데이터를 실시간으로 저장하는 기능을 구현해보려고 한다. firebase의 realtime database를 활용하여 사용자의 데이터를 저장해볼 예정이다.

먼저, firebase 실시간 데이터베이스를 이용하기 위해 초기 설정을 진행해보자.

firebase 콘솔로 돌아와, 서비스를 이용할 해당 프로젝트로 이동한 뒤 Realtime database 메뉴로 이동한다. 그리고 Create Database를 클릭하여 새로운 Database를 생성한다.

이번에는 프라이빗 모드로 Database를 만들지 않을 예정이기 때문에, 빠르게 테스트 할 수 있는 Test 모드로 선택하여 생성하였다.

생성 후엔 이렇게 생성된 Realtime Data를 확인할 수 있다.

로그인한 사용자에 따라 cards를 저장할 예정이므로, 로그인한 사용자의 ID에 맞게 해당하는 cards를 저장하는 형태가 될 것이다.

이제 Database를 사용할 것이기 때문에, 이전에 MainBoard Component에서 Mock Data로 관리하던 테스팅용 Data를 삭제해주었다. 그리고 이전의 API를 관리하던 것처럼, service 폴더에 저장 서비스만 담당할 CardRepository Component 파일을 생성해주었다. 이제 이 파일 안에서 card를 저장하고 card의 정보를 받아오는 실시간 데이터베이스 서비스를 받아오는 역할만 수행할 것이다.

** card_repository.js **

import firebase from "./firebase";

class CardRepository {
  
}

export default CardRepository;

사용자의 card가 업데이트 될 때마다 card_repository를 이용해야 하는데 이때 사용자의 id를 이용해서 사용자가 id 별로 저장할 수 있도록 만들어야 한다. id가 있어야 id를 이용해서 해당하는 card를 불러올 수 있기 때문이다.

여기서 나는 MainBoard Component에서 userLogin(업데이트) 할 때마다 콜백 함수의 인자를 통해서 useruid를 담아주는 방식을 취하고 있기 때문에 간단하게 미리 생성해주었던 state인 id 를 CardMaker Component의 props로 전달해주었다. 그리고 userLogin(업데이트)가 될때만 id 값을 받아 CardMaker Component를 렌더하고 아니면(null), 그대로 Login Component를 렌더하도록 설정해주었다.

mainBoard.jsx

const MainBoard = ({ FileInput, authService, cardRepository }) => {

const [id, setId] = useState(null);

const handlerMaker = (user) => {
    setId(user);
  };

useEffect(() => {
    authService //
      .userLogin((user) => {
        if (user) {
          handlerMaker(user.uid);
        } else {
          handlerMaker(null);
        }
      });
  });

return (
				.
				.
				.
				{id === null ? (
            <Login onLogin={onLogin} />
          ) : (
            <CardMaker
              FileInput={FileInput}
              id={id}
            />
          )}
				.
				.
				.

	);
};

export default MainBoard;

MainBoard Component에서 CardRepository 를 사용하기 위해서는 index.js 에서도 new 키워드로 생성하여 App Component에 props로 전달하고

index.js

import CardRepository from "./service/card_repository";

const authService = new AuthService();
const cardRepository = new CardRepository();
const imageUploader = new ImageUploader();
const FileInput = (props) => (
  <ImageInput {...props} imageUploader={imageUploader} />
);

ReactDOM.render(
  <React.StrictMode>
    <App
      authService={authService}
      FileInput={FileInput}
      cardRepository={cardRepository}
    />
  </React.StrictMode>,
  document.getElementById("root")
);

MainBoard Component 의 부모인 App Comoponent 에서도 CardRepository를 전달해주었다.

app.jsx



function App({ FileInput, authService, cardRepository }) {
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/">
          <Main />
        </Route>
        <Route path="/board">
          <MainBoard
            authService={authService}
            FileInput={FileInput}
            cardRepository={cardRepository}
          />
        </Route>
      </Switch>
    </BrowserRouter>
  );
}

그리고 마지막으로 MainBoard 에서 사용자가 card를 만들거나 Add하는 등의 업데이트가 발생하고 있는 자식 component인 CardMaker 까지 props로 전달해준 후에, 이전에 전달해준 id 와 함께 card가 업데이트하는 함수에서 사용해줄 생각이다.

card_maker.jsx

const CardMaker = ({ FileInput, id, cardRepository }) => {
  const [cards, setCards] = useState({});

 // 1. card가 추가가 되거나 실시간으로 update 되는 함수
  const onAddOrUpdateCard = (card) => {
    setCards((cards) => {
      const updated = { ...cards };
      updated[card.id] = card;
      return updated;
    });
  };

	// 2. card를 삭제해주는 함수 
  const onDeleteCard = (card) => {
    setCards((cards) => {
      const updated = { ...cards };
      delete updated[card.id];
      return updated;
    });
  };

.
.
.

공식문서의 실시간 데이터베이스-웹-데이터 읽기 및 쓰기 챕터를 확인해보면, 해당 기본 쓰기 작업에 대해서 form을 제공해주고 있는데 이 부분을 참조하여 작성했다.

공식문서 form

function writeUserData(userId, name, email, imageUrl) {
  firebase.database().ref('users/' + userId).set({
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}

saveCard 안에서 database를 저장하는 역할을 할 생각이다. 앞서 공식문서를 참조해보면 .ref는 설정하고자 하기 나름인데, 나는 일단 사용자(id) 별로 데이터를 관리할 생각이므로, 처음엔 ${userId} / cards / ${card.id} 의 경로에 set() 코드를 이용해서 card 를 저장하도록 설정하였다.

card_repository.js


import firebase from "./firebase";

class CardRepository {
  saveCard(userId, card) {
    firebase.database().ref(`${userId}/cards/${card.id}`).set(card);
  }
}

export default CardRepository;

이제 카드가 업데이트 될 때마다 database에 업데이트를 해주는 함수를 실행한다. 이때 업데이트 된 idcardsaveCard에 인자로 전달해주면 된다.

card_maker.jsx


const CardMaker = ({ FileInput, id, cardRepository }) => {
  const [cards, setCards] = useState({});

  const onAddOrUpdateCard = (card) => {
    setCards((cards) => {
      const updated = { ...cards };
      updated[card.id] = card;
      return updated;
    });
    cardRepository.saveCard(id, card);
  };
.
.
.

이와 반대로 card가 지워질 때마다 database에서도 삭제되도록 설정하려면 어떻게 설정해야할까? 일단 공식문서에서는 remove() 를 호출하라고 되어있다.

앞서 saveCard와 동일하게 업데이트 된 상태(idcard)를 전달받아야 하므로 해당 역할만 수행하도록 removeCard 함수를 만들고, 또 다시 동일한 userIdcard를 인자로 받은 뒤에, 레퍼런스 경로 뒤에 바로 remove() 코드를 통해서 간단하게 database에서 삭제해주도록 설정하였다.

card_repository.js

import firebase from "./firebase";

class CardRepository {
  saveCard(userId, card) {
    firebase.database().ref(`${userId}/cards/${card.id}`).set(card);
  }

	removeCard(userId, card) {
    firebase.database().ref(`${userId}/cards/${card.id}`).remove();
  }
}

export default CardRepository;

그리고 이 역시 CardMaker Component에서 card를 삭제 해주는 함수가 실행되어 업데이트 될 때마다 똑같이 removeCard 안에서 idcard를 인자로 넘겨받도록 설정하였다.

card_maker.jsx


const CardMaker = ({ FileInput, id, cardRepository }) => {
  const [cards, setCards] = useState({});

  const onAddOrUpdateCard = (card) => {
    setCards((cards) => {
      const updated = { ...cards };
      updated[card.id] = card;
      return updated;
    });
    cardRepository.saveCard(id, card);
  };

  const onDeleteCard = (card) => {
    setCards((cards) => {
      const updated = { ...cards };
      delete updated[card.id];
      return updated;
    });
    cardRepository.removeCard(id, card);
  };

.
.
.

실행 결과!

입력한 결과가 card에 업데이트가 되면,

내가 설정한 레퍼런스 경로 대로 Database에 저장되고 있는 것을 확인할 수 있다.

card를 delete 함수를 통해서 지워주면 database 안에 있는 data도 지워지는 것을 확인할 수 있다.

최종 실행 화면


참조
공식문서 : firebase, realtime database
공식문서 : firebase, realtime database-웹-데이터 읽기 및 쓰기

profile
N개월차 프론트엔드 개발자, Teta Min

0개의 댓글