Firebase Realtime Database 란?
개요
Firebase Realtime Database는 구글이 제공하는 클라우드 호스팅 데이터베이스입니다.
키-값 형태로 데이터를 저장하고(NoSQL Database), 클라이언트와 서버가 실시간으로 데이터를 동기화할 수 있습니다.
웹과 모바일 앱 개발자들에게 실시간 데이터 처리를 간단하게 만들어주며, 멀티 유저 애플리케이션 구축에 이상적인 환경을 제공합니다.
특징
실시간 데이터 동기화:
데이터가 변경되면, 연결된 모든 클라이언트에게 자동으로 이 변경사항이 실시간으로 전송됩니다.
오프라인 지원
앱이 오프라인일 때도 데이터베이스 작업이 가능하며, 온라인으로 돌아오면 자동으로 동기화됩니다.
유연한 데이터베이스 규칙
Firebase의 보안 규칙을 사용하여 데이터에 대한 접근을 제어할 수 있습니다.
확장성
Firebase Realtime Database는 다양한 크기의 애플리케이션에 맞춰 확장 가능합니다.
서버리스 아키텍처
서버 관리의 필요 없이 데이터베이스 관리를 할 수 있어, 개발 시간과 비용을 절감합니다.
Firebase Realtime Database vs Firestore Database
특징 | Firestore Database | Firebase Realtime Database |
---|---|---|
데이터 모델 | JSON | 키-값 |
쿼리 | 고급 쿼리 지원 | 기본 쿼리 지원 |
동기화 | 비동기 | 실시간 |
용도 | 다양한 앱 개발에 적합 | 실시간 데이터 동기화에 적합 |
Firestore Database
Firestore Database는 클라우드 기반의 NoSQL 데이터베이스 서비스입니다.
JSON 형태로 데이터를 저장하고, 다양한 쿼리 기능을 제공합니다. Firestore Database는 다음과 같은 특징을 가지고 있습니다.
- JSON 데이터 모델:
- JSON 형태로 데이터를 저장하여, 다양한 데이터 구조를 지원합니다.
- 고급 쿼리 지원:
- 다양한 조건을 사용하여 데이터를 검색할 수 있습니다.
- 비동기 동기화:
- 클라이언트와 서버가 비동기 방식으로 데이터를 동기화합니다.
Firebase Realtime Database
Firebase Realtime Database는 클라우드 기반의 실시간 데이터베이스 서비스입니다.
키-값 형태로 데이터를 저장하고, 클라이언트와 서버가 실시간으로 데이터를 동기화할 수 있습니다.
Firebase Realtime Database는 다음과 같은 특징을 가지고 있습니다.
- 키-값 데이터 모델:
- 키-값 형태로 데이터를 저장하여, 데이터를 쉽게 접근하고 관리할 수 있습니다.
- 기본 쿼리 지원:
- 기본적인 조건을 사용하여 데이터를 검색할 수 있습니다.
- 실시간 동기화:
- 클라이언트와 서버가 실시간으로 데이터를 동기화하여, 데이터가 변경되면 즉시 모든 클라이언트에 반영됩니다.
Database 선택
Firestore Database를 사용하는 경우
- 다양한 데이터 구조를 지원해야 하는 경우
- 고급 쿼리 기능이 필요한 경우
Firebase Realtime Database를 사용하는 경우
- 실시간으로 데이터를 동기화해야 하는 경우
- 키-값 형태로 데이터를 저장해야 하는 경우
사용 방법
1. Firebase 프로젝트 생성 및 Flutter SDK 연결
기본적인 Firebase 프로젝트를 생성하고, Flutter SDK를 연결하는 것은 전 포스팅인 아래 글을 참조해주세요.
(Flutter-기초 강의) 18. Flutter에서 Firebase 사용하기 (소개 및 프로젝트 설정)
2. Firebase Realtime Database 생성
Firebase console에 접속합니다.
위에서 생성한 프로젝트를 선택합니다.
(본인은 앞선 포스팅에서 생성한 devitworld-tutorial
프로젝트를 사용합니다.)
[Build] -> [Realtime Database] -> [Create Database] 를 선택하고
DB 위치(Location) -> 규칙(Rules) 을 선택하여 Database를 생성합니다.
- 규칙은 Test 모드로 하지 않으면 기본이 false라 read와 write가 모두 불가합니다.


3. Flutter App 생성 및 연결
Flutter project(테스트 프로젝트 이름은 _7_firebase
) 를 생성하고
firebase_core
, firebase_database
모듈을 추가했습니다.
$ flutter pub add firebase_core firebase_database
메인 페이지에 App 생성전에 Firebase.initializeApp()
을 통해 Firebase를 초기화 합니다.
void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); runApp(const MyApp()); }
4. Model 생성
Database에 사용할 모델을 정의합니다.
DataSnapshot 객체(snapshot
)를 입력받아 fromSnapshot()
을 통해 데이터베이스에서 가져와 정의한 Memo
클래스에 데이터를 로드합니다.
DataSnapshot 객체는 Firebase 데이터베이스의 특정 경로에 있는 Data의 스냅샷으로 데이터를 객체(Memo class)로 로드할 수 있게 합니다.
다시 Memo
클래스의 데이터를 전송할 때는 toJson()
을 이용하여 JSON 형태로 변경하여 전송합니다.
/lib/models/memo.dart
import 'package:firebase_database/firebase_database.dart'; class Memo { String? key; String title; String content; String createTime; Memo(this.title, this.content, this.createTime); Memo.fromSnapshot(DataSnapshot snapshot) : key = snapshot.key, title = (snapshot.value as Map)["title"] ?? '', content = (snapshot.value as Map)["content"] ?? '', createTime = (snapshot.value as Map)['createTime'] ?? ''; toJson() { return { 'title': title, 'content': content, 'createTime': createTime, }; } }
5. Database 내용 표시 View
먼저 Database 접근 경로(URL)를 Firebase Console에서 확인합니다.

Database의 데이터를 표시할 Page(MemoPage
)를 생성합니다.
initState()
에서 Firebase.app()
를 통해 Firebase 앱의 인스턴스 (FirebaseApp)를 가져오고
Firebase Database Instance로 사용할 FirebaseDatabase(_database
)를 Firebase.instanceFor()
과 앞서 확인한 URL(_databaseURL
)을 이용하여 앞서 만든 데이터 베이스에 접근하기 위해 초기화 합니다.
DatabaseReference(reference
)를 통하여 Database의 memo
에 해당하는 경로를 참조합니다.
이후 DatabaseReference의 onChildAdded.listen()
을 통해 memo
경로에 있는 데이터를 실시간으로 감시하게 됩니다.
그렇게 감지된 데이터를 List<Memo>(memos
) 리스트에 메모를 추가하여 관리합니다.
/lib/views/memo_page.dart
class _MemoPage extends State<MemoPage> { FirebaseDatabase? _database; DatabaseReference? reference; final String _databaseURL = 'Database URL'; // URL List<Memo> memos = List.empty(growable: true); @override void initState() { super.initState(); final firebaseApp = Firebase.app(); _database = FirebaseDatabase.instanceFor( app: firebaseApp, databaseURL: _databaseURL); reference = _database!.ref().child('memo'); reference!.onChildAdded.listen( (event) { print(event.snapshot.value.toString()); setState( () { memos.add(Memo.fromSnapshot(event.snapshot)); }, ); }, ); }
6. 데이터 표시
GridView 를 이용하여, memo
데이터를 표시 합니다.
/lib/views/memo_page.dart
return Scaffold( appBar: AppBar( title: const Text('Memo App'), ), body: Container( child: Center( child: memos.isEmpty ? const CircularProgressIndicator() : GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, ), itemBuilder: (context, index) { return Card( child: GridTile( header: Text(memos[index].title), footer: Text( memos[index].createTime.substring(0, 10), ), child: Container( padding: const EdgeInsets.only(top: 20, bottom: 20), child: SizedBox( child: GestureDetector( onTap: () {}, onLongPress: () {}, child: Text(memos[index].content), ), ), ), ), ); }, itemCount: memos.length, ), ), ),
7. 데이터 추가
TextField와 TextEditingController를 통해 데이터를 제목(title
)과 내용(content
)를 입력받아서
Store 버튼을 누르면 Memo 모델을 만들고 이를 JSON 형태로 변환하여 reference.push().set()
을 통해 데이터 베이스에 저장하는 페이지를 생성합니다.
/lib/views/memo_add_page.dart
class _MemoAddPage extends State<MemoAddPage> { TextEditingController? titleController; TextEditingController? contentController; @override void initState() { super.initState(); titleController = TextEditingController(); contentController = TextEditingController(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Add Memo'), ), body: Container( padding: const EdgeInsets.all(20), child: Center( child: Column( children: <Widget>[ TextField( controller: titleController, decoration: const InputDecoration( labelText: 'Title', fillColor: Colors.blueAccent), ), Expanded( child: TextField( controller: contentController, keyboardType: TextInputType.multiline, maxLines: 100, decoration: const InputDecoration(labelText: 'Contents'), )), MaterialButton( onPressed: () { widget.reference .push() .set(Memo( titleController!.value.text, contentController!.value.text, DateTime.now().toIso8601String()) .toJson()) .then((_) { Navigator.of(context).pop(); }); }, shape: OutlineInputBorder(borderRadius: BorderRadius.circular(1)), child: const Text('Store'), ) ], ), ), ), ); } }
진입은 MemoPage
에서 floatingActionButton
을 눌러 이동시킵니다.
이동시키면서 DatabaseReference를 파라미터로 함께 전달합니다.
floatingActionButton: Padding( padding: const EdgeInsets.only(bottom: 40), child: FloatingActionButton( onPressed: () { Navigator.of(context).push(MaterialPageRoute( builder: (context) => MemoAddPage(reference!))); }, child: const Icon(Icons.add), ),
결과
