✅ 이번에는 Firebase로 실시간 데이터베이스를 구축해보겠습니다. (지난 게시물과 이어집니다.)
먼저 데이터베이스를 테스트 모드로 빠르게 생성해준다.
Firebase > Console > Build > Realtime Database > Create Database 클릭
정상적으로 만들어졌으면 아래와 같이 데이터베이스 url을 얻을 수 있다.
이 URL은 firebaseConfig에 들어갈 정보로, config로 firebase를 initialize 해줌으로써 데이터베이스를 이용할 수 있게 된다.
📍 firebase.js
//src/service/firebase.js
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/database';//🌈추가
const firebaseConfig={
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.REACT_APP_FIREBASE_DB_URL,
projectId: process.env.REACT_APP_PROJECT_ID,
};
const firebaseApp=firebase.initializeApp(firebaseConfig);
export const firebaseAuth=firebaseApp.auth();
export const firebaseDB=firebaseApp.database(); //🌈추가
export const googleProvider=new firebase.auth.GoogleAuthProvider();
export const githubProvider=new firebase.auth.GithubAuthProvider();
이후 데이터를 실시간으로 읽고, 쓰고, 삭제하는 로직을 담을 js파일을 service폴더에 아래와 같이 생성해준다.
데이터 쓰기, 읽기, 삭제 기능은 링크된 공식 사이트에 기재된 방법을 이용해 만들자.
간단히 정리하면 다음과 같다.
set()
메서드에 저장할 데이터를 인자로 전달해 호출remove()
메서드 호출on()
또는 once()
메서드를 사용하여 이벤트(value)를 관찰리스너는 이벤트 발생 시점에 데이터베이스에서 지정된 위치(ref)에 있던 데이터를 포함하는 snapshot
을 수신한다.
어떤 데이터를 가지고 있었는지 알고 싶다면, snapshot
에 val()
메서드를 사용하면 접근 가능하다.
📍 repository.js
//src/service/repository.js
import { firebaseDB } from './firebase';
class Repository {
storeInfo(userId, info) {
firebaseDB.ref(`info/${userId}/${info.id}`).set(info);
}
deleteInfo(userId, info) {
firebaseDB.ref(`info/${userId}/${info.id}`).remove();
}
readInfo(userId, onUpdate) {
const dbRef = firebaseDB.ref(`info/${userId}`);
dbRef.on('value', snapshot => {
const data = snapshot.val();
data && onUpdate(data);
})
return dbRef.off;
}
}
export default Repository;
ref()
안의 주소는 자신이 원하는 경로로 지정하면 된다. 그러면 그 경로에 데이터를 저장하거나 삭제하는 등의 작업이 이루어진다.
이렇게 만든 클래스를 최상위 index.js에서 import해서 만들어 준 뒤 데이터를 저장할 컴포넌트까지 props로 전달해준다. (DI)
📍 index.js
//src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
import Auth from './service/auth';
import Repository from './service/repository';
const auth = new Auth();
const repository = new Repository();
ReactDOM.render(
<React.StrictMode>
<App auth={auth} repository={repository} />
</React.StrictMode>,
document.getElementById('root')
);
📍 app.jsx
//src/app.jsx
import React from 'react';
import Login from './components/login';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Contact from './components/contact';
function App({ auth, repository }) {
return <div>
<BrowserRouter>
<Switch>
<Route exact path="/">
<Login auth={auth} />
</Route>
<Route path="/contact">
<Contact
auth={auth}
repository={repository} //🌈전달해주기
/>
</Route>
</Switch>
</BrowserRouter>
</div>;
}
export default App;
그런 다음 연락처를 실시간으로 저장하고, 수정할 수 있는 컴포넌트를 만들어 주자.
위에서 데이터베이스에 정보를 조작할 수 있는 함수들을 repository 파일에 클래스로 만들어 주었고, index에서 생성해 이 컴포넌트로 전달해 주었다.
그러므로 여기서 해당 함수들을 이용해 다음 기능을 구현해주자.
📍 contact.jsx
// src/components/contact.jsx
import React, { useEffect, useCallback, useState } from 'react';
import { useHistory } from 'react-router';
import Add from './add';
import Edit from './edit';
import Header from './header';
const Contact = ({ auth, repository }) => {
const [infos, setInfos] = useState({});
const history = useHistory();
const state = history.location.state; //🌈추가
const [userId, setUserId] = useState(state && state.id);
const onLogout = useCallback(() => {
auth.logout();
}, [auth]);
//🌈 2,4번 기능 구현
const updateInfo = info => {
setInfos(infos => {
const updated = { ...infos };
updated[info.id] = info;
return updated;
});
repository.storeInfo(userId, info);
};
//🌈 3번 기능 구현
const deleteInfo = info => {//🌈추가된 부분
setInfos(infos => {
const updated = { ...infos };
delete updated[info.id];
return updated;
});
repository.deleteInfo(userId, info);
};
useEffect(() => {//🌞변경된 부분
//onAuthChange는 로그인 상태가 바뀌면 user 정보를 받아오고,
//등록된 콜백함수에 user를 인자로 전달해 실행해준다.
auth.onAuthChange(user => {
if (user) {
setUserId(user.uid);
} else {
history.push('/');
}
});
}, [auth, history]);
//🌈 1번 기능 구현
useEffect(() => {
if (!userId) {
return;
}
//userId가 있다면(즉 로그인된 기록이 있다면) 데이터베이스에서
//해당 id로 저장되었던 데이터들을 불러온다.
const stopRead = repository.readInfo(userId, value => {
setInfos(value);
});
//함수를 호출하고 그 리턴값(리스너를 제거해주는 함수)을
//stopRead에 할당 받아 useEffect의 cleanup 함수로 등록해준다.
return stopRead;
}, [userId, repository]);
return (
<section>
<Header onLogout={onLogout} />
{Object.keys(infos).map(key => {
return <Edit
info={infos[key]}
updateInfo={updateInfo}
deleteInfo={deleteInfo}
/>;
})}
<Add
updateInfo={updateInfo}
/>
</section>
);
};
export default Contact;
마지막으로 Contact의 하위 컴포넌트인 Edit과 Add를 아래와 같이 만들어 주면 끝이다.
📍 edit.jsx
//src/components/edit.jsx
import React from 'react';
const Edit = ({ info, updateInfo, deleteInfo }) => {
const { name, phone } = info;
const onChange = (e) => {
if (e.target == null) {
return;
}
e.preventDefault();
updateInfo({
...info,
[e.target.name]: e.target.value,
});
};
const onSubmit = () => {
deleteInfo(info);
};
return (
<form >
<input
type="text"
name="name"
value={name}
onChange={onChange}
/>
<input
type="text"
name="phone"
value={phone}
onChange={onChange}
/>
<button onClick={onSubmit}>삭제</button>
</form>
);
};
export default Edit;
📍 add.jsx
//src/components/add.jsx
import React, { useRef } from 'react';
const Add = ({ updateInfo }) => {
const formRef = useRef();
const nameRef = useRef();
const phoneRef = useRef();
const onAdd = (e) => {
e.preventDefault();
const info = {
id: Date.now(), //uuid
name: nameRef.current.value || '',
phone: phoneRef.current.value || ''
}
updateInfo(info);
formRef.current.reset();
};
return (
<form ref={formRef}>
<input
ref={nameRef}
type="text"
name="name"
autocomplete="off"
/> //자동완성을 꺼줍니다.
<input
ref={phoneRef}
type="text"
name="phone"
autocomplete="off"
/>
<button onClick={onAdd}>저장</button>
</form>
);
};
export default Add;
Login과 Header 컴포넌트는 이전 포스팅에서 구현된 내용과 변화가 없으므로, 그대로 사용하면 됩니다.
이렇게 만들면, 처음 목표로 했던 기능들이 잘 작동하는 것을 확인할 수 있습니다.
❗ 로그인 팝업에 개인 이메일 정보가 노출되어 팝업창은 옆으로 치워 안보이게 했습니다.
(로그인시 로딩시간이 좀 걸립니다.)
로그인해서 Contact 화면으로 이동한다.
정보 입력후 저장 또는 삭제가 가능하다.
새로고침 시 이전 로그인 정보로 저장했던 데이터 불러온다.
로그아웃하고 다시 들어가면 해당 로그인 uid에 따라 저장된 데이터가 있으면 보여준다.
https://firebase.google.com/docs/database/web/read-and-write?authuser=0