본문 바로가기

모바일 개발/iOS 앱 개발

[Swift] Firebase 실시간 데이터베이스에서 (한 번에 모든) 데이터 읽어오기

iOS 앱 개발 중 Firebase 실시간 데이터베이스(Firebase Realtime Database)에서 데이터를 읽어오는 방법에 대해서 알아보겠습니다.

 

단순 데이터 읽기

 

먼저, 일부 데이터를 읽어올 때는 공식문서에서 소개하는 대로 사용하면 쉽습니다. 다만 문서 내용이 업데이트되지 않았는지 메소드명이나 변수명이 약간 다릅니다.

 

{
    "user" : [
        {
            "key1" : "value1",
            "key2" : 1,
            "key3" : false
        },
        {
            "key1" : "value2",
            "key2" : 2,
            "key3" : true
        },
        {
            "key1" : "value3",
            "key2" : 3,
            "key3" : false
        }
    ]
}

 

위 데이터를 읽어오는 소스는 아래와 같습니다.

여기서 한 가지 유의할 점이 있다면, 아래 소스는 차례차례 진행되지 않습니다. DB를 읽어오는 부분인 ref.child...로 시작하는 소스는 진입하는 동시에 기존 흐름과는 별개로 진행되며, 이 DB 데이터 읽기가 끝날 때까지 getData1 메소드가 기다려주지 않습니다. 즉, print(vS)는 nil을 출력합니다.

 

//
//  Copyright © 2020 DINOPIA. All rights reserved.
//

func getData1(of userIndex: Int) {
    var vS: String
    var vI: Int
    var vB: Bool
    
    let ref: DatabaseReference! = Database.database().reference()
    ref.child("user").child(String(userIndex)).observeSingleEvent(of: .value, with: { snapshot in
        let value = snapshot.value as? NSDictionary
        vS = value?["key1"] as? String ?? "No string"
        vI = value?["key2"] as? Int ?? -1
        vB = value?["key3"] as? Bool ?? false
    })
    
    print(vS)
}

 

만약 DB에서 얻어온 데이터 값을 다른 곳에서 사용하려면, 해당 값의 변화를 감지하도록 소스 코드를 작성해야 합니다.

 

값이 일치하는 데이터 읽기

 

그러면 아래와 같은 경우에는 어떻게 할까요?

 

{
    "collection2" : [
        {
            "userIndex" : 2,
            "key1" : "value1",
            "key2" : 1
        },
        {
            "userIndex" : 0,
            "key1" : "value2",
            "key2" : 2
        },
        {
            "userIndex" : 1,
            "key1" : "value3",
            "key2" : 3
        },
        {
            "userIndex" : 0,
            "key1" : "value4",
            "key2" : 4
        }
    ]
}

 

아래는 위와 같이 collection2에 user index별로 데이터가 있을 때 해당 데이터를 읽어오는 소스입니다. 이 경우에는 데이터별로 값을 비교하여 찾아야 합니다.

 

//
//  Copyright © 2020 DINOPIA. All rights reserved.
//

func getData2(of userIndex: Int) {
    var collectionIndex: Int
    var v1: String
    var v2: Int
    
    let ref: DatabaseReference! = Database.database().reference()
    ref.child("collection2").observeSingleEvent(of: .value, with: { snapshot in
        for child in snapshot.children {
            let dataSnapshot = child as? DataSnapshot
            let item = dataSnapshot?.value as? NSDictionary
            
            if(item?["userIndex"] as! Int == userIndex) {
                collectionIndex = Int(dataSnapshot?.key ?? "-1")!
                v1 = item?["key1"] as? String ?? "No string"
                v2 = item?["key2"] as? Int ?? -1
                break;
            }
        }
    })
}

 

모든 데이터 읽기

 

{
    "user" : [
        {
            "key1" : "value1",
            "key2" : 1,
            "key3" : false
        },
        {
            "key1" : "value2",
            "key2" : 2,
            "key3" : true
        },
        {
            "key1" : "value3",
            "key2" : 3,
            "key3" : false
        }
    ],
    "collection1" : [
        {
            "key1" : true
        },
        {
            "key1" : false
        }
    ],
    "collection2" : [
        {
            "userIndex" : 2,
            "key1" : "value1",
            "key2" : 1
        },
        {
            "userIndex" : 0,
            "key1" : "value2",
            "key2" : 2
        },
        {
            "userIndex" : 1,
            "key1" : "value3",
            "key2" : 3
        },
        {
            "userIndex" : 0,
            "key1" : "value4",
            "key2" : 4
        }
    ]
}

 

위 데이터에서 특정 사용자를 위한 데이터를 모두 읽어오는 클래스를 작성하면 다음과 같습니다.

 

//
//  Copyright © 2020 DINOPIA. All rights reserved.
//

import Firebase

class ReadData {
    private let ref: DatabaseReference! = Database.database().reference()
    
    var user: User
    var arr1: [DO1]
    var arr2: [DO2]
    
    init(userIndex: Int) {
        user = User(pS: "", pI: 0, pB: false)
        arr1 = [DO1]()
        arr2 = [DO2]()
        
        self.getAllData(userIndex)
    }
    
    private func getAllData(_ userIndex: Int) {
        ref.queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
            // user
            let userDataSnapshot = snapshot.childSnapshot(forPath: "user").childSnapshot(forPath: String(userIndex))
            let userItem = userDataSnapshot.value as? NSDictionary
            self.user.pS = userItem?["key1"] as? String ?? "No string"
            self.user.pI = userItem?["key2"] as? Int ?? -1
            self.user.pB = userItem?["key3"] as? Bool ?? false
            
            // arr1
            for child in snapshot.childSnapshot(forPath: "collection1").children {
                let dataSnapshot = child as? DataSnapshot
                let item = dataSnapshot?.value as? NSDictionary
                self.arr1.append(DO1(
                    p: item?["key1"] as? Bool ?? false
                ))
            }
            
            // arr2
            for child in snapshot.childSnapshot(forPath: "collection2").children {
                let dataSnapshot = child as? DataSnapshot
                let item = dataSnapshot?.value as? NSDictionary
                if(item?["userIndex"] as! Int == userIndex) {
                    self.arr2.append(DO2(
                        collectionIndex: Int(dataSnapshot?.key ?? "-1")!,
                        p1: item?["key1"] as? String ?? "No string",
                        p2: item?["key2"] as? Int ?? -1
                    ))
                }
            }
            
            self.printData()
        })
    }
    
    public func printData() {
        print("* user")
        let pSStr = "pS: " + self.user.pS
        let pIStr = ", pI: " + String(self.user.pI)
        let pBStr = ", pB: " + String(self.user.pB)
        print(pSStr + pIStr + pBStr)
        print("* arr1")
        for element in self.arr1 {
            print("p: " + String(element.p))
        }
        print("* arr2")
        for element in self.arr2 {
            let collectionIndexStr = "collectionIndex: " + String(element.collectionIndex)
            let p1Str = ", p1: " + element.p1
            let p2Str = ", p2: " + String(element.p2)
            print(collectionIndexStr + p1Str + p2Str)
        }
    }
}

 

user와 arr2는 userIndex를 이용하여 해당 데이터만 읽어오고, arr1은 모든 데이터를 읽어옵니다.

 

userIndex가 0일 때, 콘솔에 다음과 같이 출력됩니다.

 

* user
pS: value1, pI: 1, pB: false
* arr1
p: true
p: false
* arr2
collectionIndex: 1, p1: value2, p2: 2
collectionIndex: 3, p1: value4, p2: 4

 

주의할 점은 두 가지입니다. getAllData 메소드에서 클래스의 속성(properties)인 user, arr1, arr2를 사용하기 위해선, 먼저 해당 속성들이 초기화되어야 합니다. 그리고 init에서 getAllData를 호출했더라도, user, arr1, arr2에 DB 데이터의 값이 들어가는 것은 초기화 이후입니다.

이 두 가지를 유념하여 Firebase DB에서 값을 읽어와 다루는 코드를 작성하길 바랍니다.

 

대빵디노는 Firebase를 사용하지 않고, 서버를 구현하기로 했습니다. 대빵디노가 현재 개발 중인 앱에는 NoSQL 데이터베이스보다 관계형 데이터베이스가 훨씬 적합하기 때문입니다 ☺️