2025. 1. 30. 19:46ㆍDevelopers 공간 [Basic]/Frontend
데이터베이스(Database)란 정보를 수집하고 보관하기 위한 시스템 혹은 그 데이터의 묶음을 의미하며, 다양한 종류가 있습니다.
이 중 서비스에 최근 많이 활용되는 NoSQL 데이터베이스를 포함해 다양한 기능을 제공하는 Google의 플랫폼인 Firebase는 백엔드 기능까지 클라우드 서비스 형태로 제공하기 때문에 모바일 앱/웹을 개발하기 위해 활용되기도 합니다.
이번 글에서는 데이터베이스에 대한 기본적인 내용을 살펴보고, Flutter에 Firebase를 적용하는 과정을 살펴보려고 합니다.
<구성>
1. Database and Firebase
a. SQL vs NoSQL
b. Firesbase DB
2. Flutter with Firebase
a. Firebase에 Firestore 셋팅하기
b. Flutter에 Firebase 셋팅하기
c. Flutter에 Firestore 구현하기
d. Flutter Firestore - Advanced
글효과 분류1 : 코드
글효과 분류2 : 폴더/파일
글효과 분류3 : 용어설명
글효과 분류4 : 글 내 참조
글효과 분류5 : 글 내 참조2
글효과 분류6 : 글 내 참조3
1. Database and Firebase
데이터베이스는 크게 아래와 같이 4가지 종류로 나눠볼 수 있습니다.
- 계층적(Hierarchical) DB : 데이터의 관계를 트리구조로 정의하며, 빠르지만 데이터 중복 이슈가 생깁니다.
ex) 하드디스크, DVD파일시스템 - 네트워크(Network)형 DB : 데이터의 관계를 그물처럼 정의하며, 구조 변경이 어렵습니다.
- 관계형(Relational) DB (RDBMS) : 행&열로 구성된 Table형태를 가지며, SQL(Structured Query Language)를 사용해 데이터를 관리하고 접근합니다. 완전성(Integrity)를 가지지만, 유연성(Flexibility)은 낮습니다.
- ex) Oracle(Oracle), MS-SQL Server(Microsoft), MySQL(Oracle)
- ex) DB2(IBM), Infomix(IBM), Sybase(Sybase), Derby(APache)
- ex) SQLite(Opensource)
- NoSQL(Not Only SQL) DB : key-value를 가지는 json과 유사한 형태를 가지며, key를 활용해 데이터를 관리하고 접근합니다. 완전성(Integrity)은 낮지만, 유연성(Flexibility)이 높습니다.
- Key-Value : ex) Memchached, Tokyo Tyrant, Flare, Roma, Redis
- Document : ex) MongoDB, CouchDB
- Graph : ex) Neo4j, ArangoDB, Amazon Neptune
- Big Table(Column 형) DB : ex) Hbase, Casandara, Hypertable
이 중 가장 많이 사용하는 3. RDBMS(SQL)과 4. NoSQL을 비교하고 살펴보겠습니다.
a. SQL vs NoSQL
다른 글에 따르면 아직 80% 가량의 개발자가 SQL을 사용중이지만, NoSQL의 "유연성"이라는 장점으로 인해 이 글에서 설명할 Firebase의 데이터베이스에서는 주로 NoSQL을 활용합니다.
먼저 SQL과 NoSQL의 추상적인 차이점은 아래와 같습니다.
SQL | NoSQL | |
활용 DB | 관계형 RDBMS의 시스템 활용 | 비관계형 DB의 시스템 활용 |
대상 데이터 | 미리 정의된 Schema가 있는 정규화된 데이터 | 비정형 & 반정형의 비정규화된 데이터 |
저장 형태 | 행과 열로 되어있는 테이블을 활용하며, 이런 고정된 데이터 모델이 존재 | 컬렉션 또는 문서를 활용하며, 다양한 데이터 모델 존재 ** 아래 추가로 설명하겠습니다. |
유연성 | 수평 확장이 사실상 불가능하며, 실제 확장하는 경우 다른 모든 데이터의 변경 및 초기화가 필요 | 수평확장을 통해 대량 데이터를 처리 가능 |
쿼리 | JOIN 등 복잡한 쿼리를 지원하는 SQL을 활용하며, 신뢰 있는 transaction을 위해 ACID를 준수 ** ACID : Atomicity(원자성), Consistency(일관성), Isolation(격리성), Durability(지속성) |
JOIN 등 복잡한 쿼리를 지원하지 않으며, ACID를 꼭 준수하지는 않음 |
위 내용 중 NoSQL의 다양한 데이터 모델은 아래와 같습니다.
- 키-값 저장소(Key-Value Store) : 간단한 Key-Value 방식을 활용해 데이터를 저장하는 방식입니다.
- 문서 저장소(Document Store) : SON, BSON 또는 XML과 같은 형식으로 저장하는 방식입니다. 컬렉션 내에서 문서마다 다를 수 있는 유연한 스키마를 가집니다.
- 그래프 데이터베이스 : Graph형태를 활용해 데이터를 표현하고 저장하는 방식으로, 관계를 탐색하는데 적합합니다.
- 칼럼 저장소 : 일반적인 것과 다르게 Row가 아닌 Column으로 데이터를 저장하는 방식으로, 요약이 필요한 쿼리 Workload에 유리합니다.
참고로 이 글에서 핵심적으로 다룰 Firebase의 Cloud Firestore는 NoSQL의 다양한 데이터 모델 중 두번째에 해당하는 Document-Oriented 데이터베이스입니다.
이제 SQL과 NoSQL의 차이를 구체적으로 살펴보기 위해 예시를 하나 들어보겠습니다.
어떤 이벤트(Event)의 참가자 인원(Attendees)들 각각의 정보(Users)에 대해 얻고 싶은 경우
이때 RDBMS의 관계모델 형태와 결과를 얻기 위한 SQL 쿼리는 아래 그림과 같으며, 아래와 같은 특징을 같습니다.
- Column(필드) 데이터의 데이터 타입이 무엇인지 명시되어있습니다.
- Column(필드) 데이터는 제한(Constraints)를 가지고 있습니다. 즉, 어떤 값을 가져야만하는 지를 제한해야 합니다.
- 모든 Schema는 기본 키를 위한 ID가 필요합니다.
- 두 데이터를 연결하기 위해서는 외부키를 가지는 중간자(ex. Attendees)를 둘 수 있으며, JOIN과 같은 SQL쿼리를 통해 위 목적을 달성할 수 있습니다.
-----------------------------------------------------------
<SQL 문법 기초>
SQL 문법은 두가지로 나눠볼 수 있으며 특히 JOIN과 같은 함수에서 다릅니다. 이 중 ANSI 표준 문법 위주로 살펴보겠습니다.
- Oracle 문법 : 오라클 프로그램에서 사용 가능한 문법
- ANSI 표준 문법 : 다른 SQL 프로그램에서도 공통적으로 사용 가능한 표준 문법
모든 문법을 살펴볼 수는 없어 기본적인 것만 살펴보겠습니다. 아래 예시를 보겠습니다.
USE 스키마;
SELECT 열_이름
FROM 테이블_이름
WHERE 조건식
GROUP BY 열_이름
HAVING 조건식
ORDER BY 열_이름
LIMIT 숫자
-- 텍스트는 ''로 표시해줘야함.
;
- USE : 원하는 데이터베이스(Schema)를 선택하여 사용합니다.
- SELECT : 최종적으로 원하는 Column 혹은 값을 선택할때 사용합니다.
- ex) SELECT * FROM 테이블명 : 특정 테이블명에서 모든 컬럼을 가져올 때 사용합니다.
- ex) SELECT genre, AVG(price) AS "평균" : genre라는 column과 price라는 column의 평균을 사용합니다.
- 집계 함수 (Aggregate Function)
SUM() : 컬럼의 합계를 반환
AVG() : 컬럼의 평균을 반환
MIN() : 컬럼의 최소값을 반환
MAX() : 컬럼의 최대값을 반환
COUNT() : 행의 개수를 셈
COUNT(DISTINCT) : 행의 개수를 셈
- FROM : 어떤 테이블을 사용할지 선택할 때 사용합니다.
- WHERE : 특정 조건을 만족하는 데이터만 조회할 때 사용합니다.
- GROUP BY : 여러개의 Row 데이터를 하나의 Row로 그룹하는데 사용합니다.
- HAVING : 그룹화된 데이터에 대한 조건을 제한합니다.
- ORDER BY : 최종 조회된 데이터를 정렬할 때 사용합니다.
- LIMIT : 최종 조회된 데이터의 개수를 제한할 때 사용합니다.
- -- 주석 : 주석을 표현할 때 사용 가능합니다.
다음으로 복잡한 쿼리를 만들어내기 위해 SQL에서 활용하는 JOIN함수에 살펴보겠습니다. 먼저 간단한 요약은 아래와 같습니다.
- OUTER JOIN(외부 조인) : 두 테이블을 조인할 때, 1개의 테이블에만 데이터가 있어도 결과가 나온다.
- INNER JOIN(내부 조인) : 두 테이블을 조인할 때, 두 테이블에 모두 지정한 열의 데이터가 있어야 한다.
- CROSS JOIN(상호 조인) : 한쪽 테이블의 모든 행과 다른 쪽 테이블의 모든 행을 조인하는 기능이다.
- SELF JOIN(자체 조인) : 자신이 자신과 조인한다는 의미로, 1개의 테이블을 사용한다.

가장 많이 활용하는 INNER JOIN 예시를 보겠습니다.
SELECT [조회할 열 이름]
FROM [1번 테이블]
INNER JOIN [2번째 테이블]
ON [조인 될 조건]
WHERE [조회 조건]
- SELECT : 최종적으로 원하는 Column 혹은 값을 선택할때 사용합니다.
- FROM : 어떤 테이블을 사용할지 선택할 때 사용합니다.
- INNER JOIN : 어떤 2번째 테이블을 사용할지 선택할 때 사용합니다.
- ON : 양쪽 테이블 데이터가 어떤 조건을 만족하는 경우에만 출력할지 선택합니다.
- WHERE : 특정 조건을 만족하는 데이터만 조회할 때 사용합니다.
추가적으로 3개 이상의 테이블을 JOIN하는 하는 경우의 코드 예시를 하나 보이겠습니다.
SELECT e.first_name||' '||e.last_name as 직원성명,
d.department_name as 부서명,
l.city as 도시,
c.country_name as 국가
FROM employees e JOIN departments d
ON e.department_id=d.department_id
JOIN locations l
ON d.location_id=l.location_id
JOIN countries c
ON l.country_id=c.country_id;
-----------------------------------------------------------
이와 반대로, 위와 같은 예시를 NoSQL을 활용하는 경우는 아래 그림과 같으며, 아래와 같은 특징을 같습니다.
- 필드 데이터의 데이터 타입이 무엇인지 명시할 필요가 없습니다.
- 필드 데이터는 제한(Constraints)이 없습니다.
하지만 본 글에서 설명할 Firebase에는 "Security Rule"이라는 보안 규칙을 만들어 해당 데이터를 제한할 수 있습니다.
- .read : user에게 읽힐 수 있는 데이터인지
- .write : user에게 쓰일 수 있는 데이터인지
- .validate : 포맷이나 타입이 정확한 데이터인지
- .indexOn : child를 명시함으로써 뒤 설명할 order함수와 query함수가 가능하도록 합니다.
- 모든 Schema는 기본 키를 위한 ID가 필요없으며, key가 기본키로 동작할 수 있습니다.
하지만 이는 필수적이지는 않습니다. - 두 데이터를 연결하기 위해서는 위와 같이 외부키를 가진 중간자를 두거나, "기본키를 동일하게 사용"하는 컬렉션을 추가함으로써 구현할 수 있습니다. (아래 예시는 후자)
아래 예에서 "기본키를 동일하게 사용"하는 이유는 굳이 이벤트 정보를 조회하기 위해 모든 참가자의 정보를 조회하게 되는 문제를 막기 위함입니다.
위 그림 중 쿼리는 Firebase의 API 함수를 예시로 넣었으며, 이 때 활용하는 orderFunction과 queryFunction은 아래와 같은 예시들이 있습니다.
- orderFunction() : 결과물을 정리하는 역할을 하는 함수입니다.
- orderByKey() : 기본키를 기준으로 정렬하는 방법입니다.
- orderByChild('?') : 특정 속성을 기준으로 정렬하는 방법이며, SQL의 WHERE과 비슷하게 특정 값에 대해 요청하기 위해 사용하기도합니다.
ex) orderByChild("name").equalTo("TOM") - orderByValue() : 숫자 값 등을 기준으로 정렬하는 방법입니다.
- orderByPriority() : 우선순위를 기준으로 정렬하는 방식입니다. 잘 사용하지 않습니다.
- queryFunction() : 선택적으로 결과물을 제한하기 위한 함수입니다.
- startAt('?') : 어떤 값으로 시작하는 값을 얻습니다.
- endAt('?') : 어떤 값으로 끝나는 값을 얻습니다.
- equalTo('?') : SQL의 WHERE과 비슷하며, 원하는 값을 얻을 때 사용합니다.
- limitToFirst(10) : 상위 10개를 얻을 때 사용합니다.
- limitToLast(10) : 하위 10개를 얻을 때 사용합니다.
b. Firebase DB
위에서 살펴본 DB를 구축한다 해도 백엔드 서버가 없다면 DB의 정보를 받아볼 수 없습니다.
따라서 따로 백엔드 서버를 구축하지 않아도 DB기능과 백엔드 서버 기능(CRUD)이 가능한 Serverless 서비스를 제공하는 대표적인 플랫폼인 Google의 Firebase를 활용하려고 합니다.
** CRUD : C(Create), R(Read), U(Update), D(Delete)의 데이터를 처리하는 기본적인 기능
Firebase에는 데이터베이스, 인증, 스토리지, 호스팅, 애널리틱스 등 아래와 같은 다양한 종류의 서비스 기능이 있으며, 빠른 개발 & 빠른 배포 & 통합된 백엔드 서비스 등의 장점으로 인해 많이 활용됩니다.
- Database : Realtime Database, Cloud Firestore, Cloud Storage
- Authentication
- Hosting
- Cloud Messaging
하지만 가격이나 SQL 대비해서는 낮은 쿼리의 성능과 제한적인 기능으로 인해 잘 선택할 필요도 있습니다.
Firebase에는 다양한 DB가 존재합니다.
- Realtime Database : Cloud Firestore 이전에 만들어졌지만 쿼리 수준이 낮은데다가 용량이 적기 때문에 현재는 구형 DB로 취급받습니다.
- Cloud Firestore : 상용 DB인 MongoDB보다는 지원되는 쿼리가 적지만, 그래도 현재 Firebase의 대표적인 데이터베이스입니다.
- Firebase Storage : 이미지, 동영상과 같은 큰 용량의 컨텐츠를 저장하는 데이터베이스입니다.
이번 글에서는 Cloud Firestore, 즉 Firestore를 중심으로 설명하려고 합니다.
Firestore만의 다양한 특징이 있겠지만 본 글에서는 한가지 Firestore의 특징에 대해서만 언급하고 넘어가고자 합니다.
기존의 데이터베이스는 수정될 경우 UI가 이를 어떻게 반영하는지에 대한 방식을 두가지로 분류 가능합니다.
- Pull-Driven Mechanism : 데이터베이스의 내용이 수정되어도 앱에서 따로 DB를 읽어 Rebuild하지 않는 이상 UI가 변하지 않습니다. 보통의 데이터베이스는 이와 같이 동작합니다.
- Push-Driven Mechanism : 데이터베이스의 내용이 수정되면 앱에서 이를 반영해 데이터를 받아 UI가 변합니다.
Firebase의 데이터베이스는 다른 데이터베이스들과 다르게 기본적으로 두가지 방식을 모두 지원합니다.
2. Flutter with Firebase
이번 챕터에서는 Firebase와 Flutter를 활용해 셋팅하고 구현하는 과정을 살펴보겠습니다.
a. Firebase에 Firestore 셋팅하기
먼저, Firebase 콘솔(https://console.firebase.google.com)에 들어가 Firestore를 셋팅하기 위한 프로젝트를 만들어줍니다. Analytics를 사용하려면 선택적으로 추가할 수 있습니다.
이제 프로젝트에 들어가 Firestore 데이터베이스를 만들어보겠습니다.
DB를 생성할 때는 아래와 같은 옵션을 선택해주었습니다.
- 위치 : Region을 선택할 때 아래 기재된 gcping을 참고해 선택합니다.
** https://gcping.com/ - 모드 : Read/Write권한을 인증되지 않은 사용자도 가능하도록 하기 위해 이 글에서는 테스트 모드를 활용하겠습니다.
자 이제 생성된 DB를 보니 아래와 같이 되었습니다.
b. Flutter에 Firebase 셋팅하기
이번에는 Flutter 측에서 Firebase를 활용하기 위한 셋팅을 진행하겠습니다.
작업중인 프로젝트의 콘솔에 들어가 Firebase를 셋팅해 줄 것입니다. 먼저 Firebase CLI를 설치해줍니다. 아래는 Mac이나 Linux에서 설치하는 명령어입니다.
curl -sL https://firebase.tools | bash
다음으로 Firebase에 로그인을 해줍니다.
firebase login
flutter는 이미 설치되었다고 가정하고, flutter에 FlutterFire CLI를 설치해줍니다.
dart pub global activate flutterfire_cli
이제 위 콘솔에서 만들었던 프로젝트와 연결하겠습니다. 아래 configure 뒤에 위에서 적었던 프로젝트의 이름을 명시해줍니다.
혹시 코드 아래 메시지와 같이 에러가 난다면 아래 코드 주석처럼 재인증을 진행하고 다시 진행합니다.
# IF ERROR
# firebase login --reauth
flutterfire configure --project test-project
COMMAND: firebase projects:list --json
ERROR: Failed to list Firebase projects. See firebase-debug.log for more info.
이제 firebase를 사용하기 위해 꼭 필요한 코어 플러그인을 설치해줍니다.
flutter pub add firebase_core
flutterfire configure
마지막으로, flutter 프로젝트의 main.dart로 돌아가 main() 함수 내에서 앱을 실행하기 전에 아래와 같이 firebase를 초기화해줍니다.
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
Future<void> main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
flutter에서는 다양한 firebase의 서비스를 활용할 준비가 끝났습니다.
c. Flutter에 Firestore 구현하기
기본적인 Firebase를 활용하기 위한 셋팅이 마무리되었으니, Firestore를 활용하기 위해 구현을 시작하겠습니다.
먼저, 아래와 같이 firestore를 설치해줍니다.
flutter pub add cloud_firestore
flutterfire configure
이제 코드 내에서 Firestore를 활용하기 위해 instance를 아래와 같이 선언해줍니다.
db = FirebaseFirestore.instance;
다음으로 users 컬렉션에 아래와 같은 문서를 추가 하겠습니다.
// Create a new user with a first and last name
final user = <String, dynamic>{
"first": "Ada",
"last": "Lovelace",
"born": 1815
};
// Add a new document with a generated ID
db.collection("users").add(user).then((DocumentReference doc) =>
print('DocumentSnapshot added with ID: ${doc.id}'));
DocumentSnapshot added with ID: bTSk1XXXXXXXXXXXXXXX
users 컬렉션에 또 다른 문서를 추가 하겠습니다. 기존의 key-value쌍에 나타나지 않은 값을 넣어주었습니다.
// Create a new user with a first and last name
final user = <String, dynamic>{
"first": "Alan",
"middle": "Mathison",
"last": "Turing",
"born": 1912
};
// Add a new document with a generated ID
db.collection("users").add(user).then((DocumentReference doc) =>
print('DocumentSnapshot added with ID: ${doc.id}'));
DocumentSnapshot added with ID: YgOQ3XXXXXXXXXXXX
이제 Firebase 콘솔로 돌아가 추가되었는지 확인해보니 아래와 같이 잘 추가되었네요.
이번엔 추가된 데이터들을 읽어보겠습니다.
await db.collection("users").get().then((event) {
for (var doc in event.docs) {
print("${doc.id} => ${doc.data()}");
}
});
YgOQ3XXXXXXXXXXXX => {first: Alan, middle: Mathison, last: Turing, born: 1912}
bTSk1XXXXXXXXXXXXX => {first: Ada, last: Lovelace, born: 1815}
d. Flutter Firestore - Advanced
작성중
설치 : https://firebase.google.com/docs/flutter/setup?hl=ko&platform=ios
SQL: https://blog.naver.com/regenesis90/222188241512
SQL JOIN :
https://velog.io/@estell/SQL-%EA%B3%A0%EA%B8%89-%EB%AC%B8%EB%B2%95-JOIN
https://m.blog.naver.com/regenesis90/222190687396
SQL vs NoSQL
https://www.integrate.io/ko/blog/the-sql-vs-nosql-difference-ko/
https://maily.so/devpill/posts/wjzded2yo3p
NoSQL 스키마 디자인
Firebase
firestore 예시
'Developers 공간 [Basic] > Frontend' 카테고리의 다른 글
[Flutter] 프로젝트 시작하기 (0) | 2023.02.18 |
---|---|
[Web] React와 Typescript 시작하기 (0) | 2023.01.06 |
[Web] Javascript 기초 문법 정리 (0) | 2022.12.21 |
[Web] HTML/CSS 기초 문법 정리 (0) | 2022.12.21 |