RealTime Database를 API처럼 사용해보기
RealTime Database에 JSON 형식의 데이터를 만들고 수정해가면서 API를 사용하는 것처럼 활용해봅니다.
_Firebase RealTime Database_는 클라우드기반의 호스팅 데이터베이스입니다.
데이터는 JSON으로 저장되며 연결된 모든 클라이언트에 실시간으로 동기화되고, Apple, Android 등 크로스 플랫폼 앱을 개발하면 모든 클라이언트가 하나의 실시간 데이터베이스 인스턴스를 공유하고 자동 업데이트로 최신 데이터를 수신한다고 하네요.
말그대로 실시간 데이터베이스니 채팅기능 같은 것들도 구현할 수 있을 거에요.
자세한 내용은 공식 문서 링크에 가면 보실 수 있어요.
아래 시리즈인 Storage
와 이 글의 내용은 유사합니다.
물론 용도가 더욱 다양하겠지만, 적어도 제가 사용하는 용도에 한해서 RealTime Database와 Storage의 가장 큰 차이는 이겁니다.
RealTime Database는 실제 백엔드서버처럼 데이터를 수정해도 클라이언트 코드를 바꾸지 않아도 되고, Storage는 말그대로 저장소이기 때문에 json 파일을 수정하면 재업로드하고 공유링크도 새로 받아와야 합니다.
이렇게 보면 당연히 RealTime Database가 더 편해보이죠?
하지만 마냥 그렇지 많은 않아요. 상황에 따라 선택하시면 될 텐데, Storage 같은 경우는 json을 업로드하고 공유링크만 받아오면 땡입니다. 데이터가 계속 변하지 않는다고 가정하면 굉장히 간편하죠.
하지만, RealTime Database는 상대적으로 초기 설정을 해야 하는 부분이 제법 있습니다. 하나씩 해볼까요 ?
1. 앱을 추가하여 시작하기
둘 다 아래 조건을 진행했다고 치고 진행할게요.
Firebase SDK를 설치합니다.
Firebase Console에서 Firebase 프로젝트에 앱을 추가합니다.
그러면 RealTime Database는 iOS 앱을 추가해야 해요. 아래 앱을 추가하여 시작하기 윗 부분에 iOS 버튼을 눌러줄게요.

2. 번들 ID 넣어주기
그리고 시키는 대로 하면 되는데 번들 ID
를 넣어줘야 합니다. 어디있냐면, 내 프로젝트로 가서 좌측에 프로젝트 워크스페이스를 클릭하고 General-Target을 보면 상단에 Bundle Identifier가 보이죠? 저것을 그대로 복사해와서 붙여넣어 줍니다.
Bundle Identifier는 RealTime Database에서 내 앱을 구별하는 고유 값이 될 거에요. 나머지 2개는 선택사항이니 다음으로 넘어갑니다.

3. 구성파일 추가하기
글에 나와있는대로 구성파일을 다운로드하고 프로젝트에 드래그앤드랍으로 추가해줍니다.


4. 패키지 추가하기
그리고 또 시키는 대로 Firebase SDK를 추가해줄게요. (시간 제법 걸립니다.)


패키지 추가 팝업이 열리면 밑에 Github을 클릭하고 문서에 나와있는 링크를 복사붙여넣기해서 검색창에 검색합니다. 그러면 아래처럼 sdk를 찾을 수 있죠? 이것을 Add Package 할게요.

같이 선택할 패키지가 나오는데 저희는 RealTime Database만 사용해볼거니까 이것만 설치하겠습니다. 다른 것도 같이하셔도 무방합니다.

5. 초기화 코드 추가하기
패키지 추가가 완료되면 초기화 코드를 추가해주라고 하죠 ?
그런데 iOS 13부터는 SceneDelegate 라는 것이 생겼어요. 그래서 문서에 나와있는대로 하려면 SceneDelegate를 삭제해주어야 합니다.
SceneDelegate 삭제

Application Scene Manifest 삭제(리스트 우측 - 버튼)

Appdelegate에서 아래 함수 제거
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
Appdelegate에서 window를 추가하고 파이어베이스 설정
import UIKit
import Firebase
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
return true
}
여기까지 했으면 이제 설정은 끝난겁니다. 이것도 어렵진 않지만, 아까 말했다시피 Storage 보다는 복잡하죠? 그래서 상황에 맞게 적절히 사용하시면 될 것 같아요.
이제 사용해볼건데, 공식문서 샘플도 있지만 처음에 보기엔 다소 어려울 수 있기도 하고 저희는 그냥 서버처럼 써보기만 할거라서 따로 설명하지는 않겠습니다. 공식문서는 여기에 있으니 보셔도 될 것 같아요.
6. JSON 문서 만들기
자, Storage때처럼 json을 업로해주진 않아요. 다만 json 형식으로 문서를 만들어 주면 되겠죠. 콘솔로 오면 친절히 데이터베이스 만들기라는 버튼이 있습니다.

그리고 차례대로 변경없이 다음-사용설정을 눌러주면 데이터베이스가 생성됩니다.

익숙한 화면이죠? 똑같이 규칙을 볼까요? 잠금모드 이지만 읽기, 쓰기 권한이 모두 false로 되어있네요. 둘 다 true로 바꿔줍니다. 바꿔주지 않으면 접근할 때 권한이 없다고 나올거에요.
{
"rules": {
".read": true,
".write": true
}
}
자, 다시 데이터로 돌아와서 + 버튼을 누르면 아래와 같이 입력창이 나오고 데이터를 생성할 수 있습니다.

기본적으로 같은 키값을 가지고 있다면 데이터는 덮어쓰기 됩니다.

이런식으로 데이터를 생성할 수 있다는 것만 알아둘게요. 왜냐면 저희는 이미 json 파일을 가지고 있기 때문이죠. 데이터베이스 우측 설정버튼을 누르고 json 가져오기를 클릭합니다.
파일을 선택하고 Storage에서 사용했던 json을 추가하면 아래와 같이 데이터가 생성되는 것을 볼 수 있지요.

7. 저장된 데이터베이스 불러오기
자, 이제 이걸 불러와야 할 겁니다. 코드를 볼까요 ? 자세한 방법은 여기에 있으니 참고하시면 됩니다.
일단, json 데이터 타입을 다시 보겠습니다. 아래와 같은 식이죠? 데이터의 키값이 menus
이고, 데이터는 Array
값으로 지정되어 있습니다.
{
"menus": [
{
"order": 1,
"name": "인바디 Q&A",
"url": "https://m.cafe.naver.com/ca-fe/web/cafes/25016228/menus/1957"
},
{
"order": 2,
"name": "다이어트 톡",
"url": "https://m.cafe.naver.com/ca-fe/web/cafes/25016228/menus/1648"
},
{
"order": 3,
"name": "제품랭킹",
"url": "https://m.cafe.naver.com/ca-fe/web/cafes/25016228/menus/2089"
},
{
"order": 4,
"name": "오늘의 식단",
"url": "https://m.cafe.naver.com/ca-fe/web/cafes/25016228/menus/333"
}....
이전에 했던 Storage는 URLSesstion을 이용해 데이터 통신을 했지만, 지금은 Firebase를 이용하고 있으므로 URLSesstion은 필요 없어요. 아래는 전체 코드입니다.
import UIKit
import Firebase
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
var ref: DatabaseReference!
var menus = [Menus]()
override func viewDidLoad() {
super.viewDidLoad()
request()
}
private func request() {
ref = Database.database().reference()
ref.child("menus").observeSingleEvent(of: .value, with: { [weak self] snapshot in
for child in snapshot.children {
let dataSnapshot = child as? DataSnapshot
if let item = dataSnapshot?.value as? NSDictionary {
let menu = Menus(name: item["name"] as? String ?? "",
url: item["url"] as? String ?? "")
self?.menus.append(menu)
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}
}
})
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menus.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = menus[indexPath.row].name
cell.detailTextLabel?.text = menus[indexPath.row].url
return cell
}
}
struct Menus: Decodable {
let name: String
let url: String
}
여기서 중요한 점은 ref 값을 생성하면서 초기화하지 말아야 한다는 점, 무슨말이냐면 아래처럼 꼭 두 줄로 나눠줘야 한다는 겁니다. 한 줄로 하면 크래시가 발생해요.
var ref: DatabaseReference!
ref = Database.database().reference()
8. 데이터베이스 변경하고 다시 실행해보기
자, 그럼 이번엔 데이터를 바꿔볼까요? 그러려고 RealTime Database를 사용하는 거니까요.
현재 데이터는 아래와 같습니다.

그리고 이걸 음 파이어베이스 데이터베이스를 열고 원하는 인덱스에 마우스를 올리면 나오는 x를 눌러 데이터를 삭제해볼게요.


몇 개의 데이터를 지웠고 현재 데이터는 이렇습니다.

클라이언트의 코드는 바꾸지 않았죠? 다시 프로그램을 실행해보겠습니다.

출력이 제대로 되는 것을 볼 수 있죠.
Storage와는 다르게 실시간으로 데이터베이스가 반영되는 것을 볼 수 있습니다. 실시간 데이터베이스는 읽기뿐만 아니라 수정도 얼마든지 가능합니다. 따라서 채팅같은 것도 구현이 가능하지요. 그건 나중에 해볼게요. 이상입니다.
Last updated