๐Ÿ“‘ firestore database ์ ‘๊ทผ ๋ฐ ์‚ญ์ œ code, firestore Queries ๐ŸŽ

2022. 8. 28. 17:19ใ†Programming/Swift

 

 

 

 

 

https://hello-developer.tistory.com/52

https://hello-developer.tistory.com/53?category=951601 

https://velog.io/@leesangsu200/Firestore์—์„œ-๋ฐ์ดํ„ฐ-๊ฐ€์ ธ์˜ค๊ธฐ 

https://github.com/DeveloperAcademy-POSTECH/MC3-Team3-Puhaha

 

GitHub - DeveloperAcademy-POSTECH/MC3-Team3-Puhaha: ๋ฐฅ ๋จน์–ธ? : ๋ถ€๋ชจ์™€ ์ž๋…€ ๊ฐ„ ์‹์‚ฌ์‚ฌ์ง„์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋Š” ์•ฑ

๋ฐฅ ๋จน์–ธ? : ๋ถ€๋ชจ์™€ ์ž๋…€ ๊ฐ„ ์‹์‚ฌ์‚ฌ์ง„์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋Š” ์•ฑ . Contribute to DeveloperAcademy-POSTECH/MC3-Team3-Puhaha development by creating an account on GitHub.

github.com

https://firebase.google.com/docs/firestore/query-data/queries

 

 

 

 

 

 

- error handling

   db.collection("users").addDocument(data: [:]) { error in
       // this code is run after the operation is complete
       if error == nil {
           // operation success
       } else {
           // error, do something
       }
   }

 

 

 

 

 

 

 

- db์— data๋ฅผ ๋„ฃ๋Š” ํ•จ์ˆ˜ (write)

    func setUserName(userIdentifier: String, userName: String) {
        db.collection("Users").document(userIdentifier).updateData(["name": userName])
    }
    
    func setDefaultUserData(userIdentifier: String) {
        db.collection("Users").document(userIdentifier).setData(["familyCode": "",
                                                            "name": "",
                                                            "pokeState": ["pokedBy": "",
                                                                          "pokedtime": ""],
                                                            "pokingTool": ["color": "",
                                                                           "tool": ""]])
    }

 

.setData() : ์ดˆ๊ธฐ์— data๋ฅผ set ํ•  ๋•Œ ์‚ฌ์šฉ (๋ฎ์–ด์“ฐ๊ธฐ ์ฃผ์˜)

.updateData() : ๊ธฐ์กด ํ•„๋“œ์˜ ๊ฐ’์„ update ํ•  ๋•Œ ์‚ฌ์šฉ

 

db.collection("users").document("documentPath").delete()

-> ํ•ด๋‹น path์˜ ๋ฌธ์„œ๊ฐ€ ํ†ต์œผ๋กœ ์ง€์›Œ์ง

db.collection("users").document("documentPath").updateData(["name": FieldValue.delete()])

-> ํŠน์ • ํ•„๋“œ์˜ ๊ฐ’๋งŒ ์ง€์›Œ์ง

 

 

 

 

 

 

- db์—์„œ data๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜ (read)

    func getSignInUser(userIdentifier: String, completion: @escaping () -> Void) {
        loginedUser = User(accountId: userIdentifier)
        
        db.collection("Users").document(userIdentifier).addSnapshotListener { [self] (document, error) in
            if let document = document {
                let name = document.data()?["name"] as? String ?? ""
                let familyCodes = document.data()?["familyCode"] as? [String] ?? []
                let pokingTool = document.data()?["pokingTool"] as? [String: String] ?? [:]
                let pokeStateValue = document.data()?["pokeState"] as? [String: String] ?? [:]
                
                let pokingToolColor: String = pokingTool["color"]?.lowercased() ?? ""
                let pokingToolType: String = pokingTool["tool"]?.lowercased() ?? ""
                let toolImage: UIImage = UIImage(named: "\(pokingToolColor)_\(pokingToolType)") ?? UIImage()
                let pokedBy: String = pokeStateValue["pokedBy"] ?? ""
                let pokedTime: String = pokeStateValue["pokedTime"] ?? ""
                
                loginedUser.setName(name: name)
                loginedUser.setToolImage(toolImage: toolImage)
                loginedUser.setToolType(with: pokingToolType)
                loginedUser.setToolColor(with: pokingToolColor)
                loginedUser.setFamilyCodes(code: familyCodes)
                loginedUser.setPoke(poke: Poke(pokedBy: pokedBy, pokedTime: pokedTime))
                
                completion()
                
            } else {
                print(error ?? "")
            }
        }
    }

- escaping closure๋กœ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒฝ์šฐ

: ๋‹จ์ˆœํžˆ ์„œ๋ฒ„์˜ data๋งŒ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒฝ์šฐ์—๋Š” getDocument๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋œ๋‹ค.

-> but, UI๋ฅผ ๊ทธ๋ฆฌ๋Š” ๊ณผ์ •์—์„œ data๊ฐ€ ๋ถˆ๋Ÿฌ์˜จ ๋’ค, ํ™”๋ฉด์„ ๊ทธ๋ ค์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š”

     completion handler๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌํ•œ๋‹ค. 

 

 

 

 

 

 

1) ํŠน์ • document ๋ฅผ ์กฐํšŒํ•˜๊ธฐ (pull) 

 

db.collection("users").document("documentPath").getDocument { (snapshot, error) in
    if error == nil && snapshot != nil && snapshot!.data() != nil {
        print(snapshot!.data()
    }
}

: collection ๋‚ด๋ถ€์˜ ํŠน์ • document ์—์„œ getDocument

 

 

 

 

2) collection ๋‚ด๋ถ€์˜ ๋ชจ๋“  document ์กฐํšŒํ•˜๊ธฐ (pull) 

db.collection("users").getDocuments { (snapshot, error) in
    if error == nil && snapshot != nil {
        for document in snapshot!.documents {
            print(document.documentID)
        }
    } else {
    // error. do something
    }
}

: collection์—์„œ getDocuments 

 

 

 

 

3) ํŠน์ • document ๋ฅผ ์กฐํšŒํ•˜๊ธฐ (push)

pull: ์›๋ž˜๋Š” db์˜ data๋ฅผ ๊ทธ๋ƒฅ ๋ฐ›์•„์˜ด -> push: database ์ƒ์˜ ๋ณ€ํ™”๊ฐ€ ์ƒ๊ฒผ์„ ๋•Œ, data๋ฅผ ๋ณด๋‚ด์ฃผ๋Š” ๊ฒƒ

let listener = db.collection("users").document("documentPath").addSnapshotListener { (snapshot, error) in
    if error == nil && snapshot != nil && snapshot!.data() != nil {
        print(snapshot!.data())
    }
}

: ์„œ๋ฒ„์—์„œ ๋ณ€ํ™”ํ•˜๋Š” data๋ฅผ ์•Œ์•„์•ผํ•  ๋•Œ ์‚ฌ์šฉ (addSnapshotListener ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ, ์—…๋ฐ์ดํŠธ ์‚ฌํ•ญ์„ ์ˆ˜์‹ )

 

 

 

 

4) collection ๋‚ด๋ถ€์˜ ๋ชจ๋“  documents ์กฐํšŒ (push)

let listener = db.collection("users").addSnapshotListener { (snapshot, error) in
    if error == nil && snapshot != nil {
        // ๋ณ€ํ™”๊ฐ€ ์žˆ๋Š”๊ฒƒ๋งŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
        for change in snapshot!.documentChanges {
            print(change.document.documentID)
        }
    } else {
        // error. do something
    }
}

์ถœ์ฒ˜: https://hello-developer.tistory.com/53?category=951601 

: ๋ชจ๋“  ๋ฌธ์„œ๋ฅผ ๋Œ์ง€ ์•Š๊ณ , ๋ณ€ํ™”๊ฐ€ ์ƒ๊ธด ๊ฐ’๋งŒ ๊ฐ€์ ธ์˜ฌ ๋•Œ change ํ™œ์šฉ

 

 

 

 

 

 

 

 

- Firestore Queries

: whereField๋ฅผ ํ†ตํ•ด ์›ํ•˜๋Š” ์กฐ๊ฑด์˜ data๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค!

: google firebase ๊ณต์‹๋ฌธ์„œ ์ฐธ์กฐ https://firebase.google.com/docs/firestore/query-data/queries#compound_queries

let query = db.collection("users").whereField("age", isEqualTo: 30)

query.getDocuments { (snapshot, error) in
    
    let docs = snapshot!.documents

    for doc in docs {
        print(doc.documentID)
    }
}

 

 

 

// Create a reference to the cities collection
let citiesRef = db.collection("cities")

// Create a query against the collection.
let query = citiesRef.whereField("state", isEqualTo: "CA")
let capitalCities = db.collection("cities").whereField("capital", isEqualTo: true)

- 1) ์ฃผ๊ฐ€ "CA"์ธ ๋„์‹œ๋“ค์„ ๋ฐ˜ํ™˜, 2) ์ˆ˜๋„์ธ ๋„์‹œ(capital์ด true์ธ)๋“ค์„ ๋ฐ˜ํ™˜

 

 

 

 

db.collection("cities").whereField("capital", isEqualTo: true).getDocuments() { (querySnapshot, err) in
    if let err = err {
        print("Error getting documents: \(err)")
    } else {
        for document in querySnapshot!.documents {
            print("\(document.documentID) => \(document.data())")
        }
    }
}

-> whereField ๊นŒ์ง€๊ฐ€ ์ฟผ๋ฆฌ ๊ฐ์ฒด ์ƒ์„ฑ -> get ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ

 

 

 

 

 

์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ฟผ๋ฆฌ ์—ฐ์‚ฐ์ž๋“ค (์กฐ๊ฑด ๊ฒ€์ƒ‰ ์‹œ) : ๊ณต์‹ ๋ฌธ์„œ์— ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ์ž˜ ์ •๋ฆฌ๋˜์–ด ์žˆ๋‹ค.

 

 

 

 

 

- array ์ฟผ๋ฆฌ

1) in

let citiesRef = db.collection("cities")

citiesRef.whereField("country", in: ["USA", "Japan"])

-> country ํ•„๋“œ ๊ฐ’์ด USA ์ด๊ฑฐ๋‚˜ Japan์ธ ๋ชจ๋“  ๋ฌธ์„œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. (ex) ์ƒŒํ”„๋ž€์‹œ์Šค์ฝ”, ๋กœ์Šค์—”์ ค๋ ˆ์Šค, ํ† ๊ต, 

 

 

 

 

2) not-in

citiesRef.whereField("country", notIn: ["USA", "Japan"])

-> country ํ•„๋“œ๊ฐ€ ์กด์žฌํ•˜๋˜, USA or Japan์€ ์•„๋‹ˆ๋ฉฐ, ๋˜ํ•œ ๋นˆ ๊ฐ’๋„ ์•„๋‹ˆ์–ด์•ผ ํ•จ!

: ๊ทธ๋Ÿฐ ๋ชจ๋“  cities collection์˜ ๋ฌธ์„œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

: ์ฃผ์˜์‚ฌํ•ญ ์žˆ์œผ๋‹ˆ ๊ณต์‹๋ฌธ์„œ์—์„œ ๋” ์ฝ์–ด๋ณด์ž.

not-in ์ฟผ๋ฆฌ๋Š” ์ง€์ •๋œ ํ•„๋“œ๊ฐ€ ์—†๋Š” ๋ฌธ์„œ๋ฅผ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค. ๋นˆ ๋ฌธ์ž์—ด(""), null, NaN(์ˆซ์ž ์•„๋‹˜)์„ ํฌํ•จํ•˜๋Š” ์–ด๋–ค ๊ฐ’์œผ๋กœ๋“  ์„ค์ •๋˜๋ฉด ํ•„๋“œ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. x != null์€ undefined๋กœ ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค. null์„ ๋น„๊ต ๊ฐ’ ์ค‘ ํ•˜๋‚˜๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” not-in ์ฟผ๋ฆฌ๋Š” ์–ด๋–ค ๋ฌธ์„œ์™€๋„ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

 

 

 

3) array-contains-any

let citiesRef = db.collection("cities")
citiesRef.whereField("regions", arrayContainsAny: ["west_coast", "east_coast"])

- regions ํ•„๋“œ๋Š” west_coast ๋˜๋Š” east_coast๊ฐ€ ํฌํ•จ๋œ ๋ฐฐ์—ด

- ์ค‘๋ณต๋˜๋Š” array-contains-any์˜ ๊ฒฐ๊ณผ๊ฐ€ ์‚ญ์ œ

- array-contains-any: ํ•ญ์ƒ ๋ฐฐ์—ด ๋ฐ์ดํ„ฐ ์œ ํ˜• ๊ธฐ์ค€์œผ๋กœ ํ•„ํ„ฐ๋ง: ๋ฐฐ์—ด ๋Œ€์‹  regions ํ•„๋“œ๊ฐ€ west_coast ๋ฌธ์ž์—ด์ธ city ๋ฌธ์„œ๋Š” X

: ๋‹จ์ˆœํžˆ ๊ฐ’ ๋งŒ์˜ ๋น„๊ต๋Š” In์„ ์‚ฌ์šฉํ•˜๊ณ , ๋ฐฐ์—ด ๋‚ด๋ถ€์˜ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์ ‘๊ทผ์€ array-contains-any ์‚ฌ์šฉ!

 

 

 

 

 

 

 

- ๋ณตํ•ฉ ์ฟผ๋ฆฌ

: ์—ฌ๋Ÿฌ ๋“ฑํ˜ธ ์—ฐ์‚ฐ์ž๋ฅผ ํ†ตํ•ด, ๊ตฌ์ฒด์ ์ธ ์กฐ๊ฑด์„ ๊ฑธ ์ˆ˜ ์žˆ๋‹ค. -> ๋ณตํ•ฉ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ

citiesRef
    .whereField("state", isEqualTo: "CO")
    .whereField("name", isEqualTo: "Denver")
    
citiesRef
    .whereField("state", isEqualTo: "CA")
    .whereField("population", isLessThan: 1000000)

 

ํ•„๋“œ 1๊ฐœ์—์„œ๋งŒ ๋ฒ”์œ„ ํ•„ํ„ฐ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ! (๋ฒ”์œ„ ํ•„ํ„ฐ๋ž€ -> isLessThan ์ด๋Ÿฐ ์—ฐ์‚ฐ! ๋ถ€๋“ฑํ˜ธ ์—ฐ์‚ฐ๋“ค!)

 

 

 

 

citiesRef
    .whereField("state", isGreaterThanOrEqualTo: "CA")
    .whereField("population", isGreaterThan: 1000000)

์ž˜๋ชป๋œ ์‚ฌ์šฉ! ์—ฌ๋Ÿฌ ํ•„๋“œ์— ๋ฒ”์œ„ ํ•„ํ„ฐ ์‚ฌ์šฉ (ํ•„ํ„ฐ: , ๋’ค์— ๋ถ™๋Š” ๊ฒƒ์œผ๋กœ ์ดํ•ด)

 

 

 

 

 

 

 

 

 

[ MC2 project์—์„œ ํ™œ์šฉํ•œ ์˜ˆ์‹œ ]

 

//  User.swift

import UIKit

class User {
    private let accountId: String?
    private var name: String?
    private var loginForm: Int?

    private var toolImage: UIImage?
    private var toolType: String?
    private var toolColor: String?
    private var familyCodes: [String?]
    private var pokeState: Poke?
    
    init() {
        self.accountId = nil
        self.name = nil
        self.loginForm = 0

        self.toolImage = nil
        self.toolType = nil
        self.toolColor = nil
        self.familyCodes = []
        self.pokeState = nil
    }
    
    init(accountId: String) {
        self.accountId = accountId
        self.name = nil
        self.loginForm = 0

        self.toolImage = nil
        self.toolType = nil
        self.toolColor = nil
        self.familyCodes = []
        self.pokeState = nil
    }

    init(accountId: String, name: String) {
        self.accountId = accountId
        self.name = name
        self.loginForm = 0

        self.toolImage = nil
        self.toolType = nil
        self.toolColor = nil
        self.familyCodes = []
        self.pokeState = nil
    }

    init(accountId: String, name: String, toolImage: UIImage, toolType: String, toolColor: String, familyCodes: [String], pokeState: Poke) {
        
        self.accountId = accountId
        self.name = name
        self.loginForm = 0

        self.toolImage = toolImage
        self.toolType = toolType
        self.toolColor = toolColor
        self.familyCodes = familyCodes
        self.pokeState = pokeState
    }
    
    public func setName(name: String) {
        self.name = name
    }
    public func getName() -> String {
        return name ?? ""
    }

    public func setToolImage(toolImage: UIImage) {
        self.toolImage = toolImage
    }
    public func getToolImage() -> UIImage {
        return toolImage ?? UIImage()
    }
    
    public func setToolType(with tool: String) {
        self.toolType = tool
    }
    public func getToolType() -> String {
        return toolType ?? ""
    }
    
    public func setToolColor(with color: String) {
        self.toolColor = color
    }
    public func getToolColor() -> String {
        return toolColor ?? ""
    }
    
    public func setPoke(poke: Poke) {
        self.pokeState = poke
    }
}

 

 

 

//  FirestoreManager.swift

import UIKit

import FirebaseFirestore
import FirebaseFirestoreSwift

class FirestoreManager: ObservableObject {
    private let tagColor: [UIColor] = [.customPurple, .customBlue, .customGreen]
    
    private var db = Firestore.firestore()
    
    @Published var meals: [Meal]
    @Published var user: User
    @Published var loginedUser: User
    @Published var userIdentifiers: [String]
    @Published var families: [Family]
    @Published var documentsCount = 0
    @Published var isExistFamily: Bool
    
    init() {
        self.meals = []
        self.user = User()
        self.loginedUser = User()
        self.userIdentifiers = []
        
        self.families = [Family(user: User(accountId: "",
                                           name: "๋ชจ๋‘",
                                           toolImage: UIImage(named: "IconEveryoneFilter")!,
                                           toolType: "",
                                           toolColor: "",
                                           familyCodes: [String()],
                                           pokeState: Poke()),
                                isSelected: true)]
        
        self.isExistFamily = false
    }
    
    func fetchMeals(familyCode: String, date: Date?, completion: @escaping () -> Void) {
        var documentPath: Query = db.collection("Families").document(familyCode).collection("Meals")
        
        if date != nil {
            documentPath = documentPath.whereField("uploadDate", isEqualTo: date!.dateText)
        }
        
        documentPath.order(by: "uploadTime", descending: true).addSnapshotListener { [self] (querySnapshot, _) in
            guard let documents = querySnapshot?.documents else {
                print("No Documents")
                return
            }
            
            meals = documents.map { (queryDocumentSnapshot) -> Meal in
                let data = queryDocumentSnapshot.data()
                
                let mealImageIndex = data["mealImageIndex"] as? String ?? ""
                let tagsString = data["tags"] as? [String: String] ?? [:]
                var tags: [Tag] = []
                for key in tagsString.keys {
                    tags.append(Tag(content: tagsString[key] ?? "", backgroundColor: self.tagColor[Int(key) ?? 0]))
                }
                
                let userIdentifier = data["uploadUser"] as? String ?? ""
                let uploadedTime = data["uploadTime"] as? String ?? ""
                let uploadedDate = data["uploadDate"] as? String ?? ""
                let reactionsValue = data["reactions"] as? [[String: String]] ?? []
                var reactions: [Reaction?] = [nil]
                for i in 0..<reactionsValue.count {
                    for key in reactionsValue[i].keys {
                        reactions.append(Reaction(reactionEmojiString: reactionsValue[i][key]!, reactedUserName: key))
                    }
                }
                
                let meal = Meal(mealImage: UIImage(), mealImageName: mealImageIndex, uploadUser: "", userIdentifier: userIdentifier, userIcon: UIImage(), tags: tags, uploadedDate: uploadedDate, uploadedTime: uploadedTime, reactions: reactions)
                
                DispatchQueue.main.async {
                    completion()
                }
                
                return meal
            }
        }
    }
    
    func getUploadUser(userIdentifier: String, completion: @escaping () -> Void) {
        user = User(accountId: userIdentifier)
        
        db.collection("Users").document(userIdentifier).addSnapshotListener { [self] (document, error) in
            if let document = document {
                let name = document.data()?["name"] as? String ?? ""
                let familyCodes = document.data()?["familyCode"] as? [String] ?? []
                let pokingTool = document.data()?["pokingTool"] as? [String: String] ?? [:]
                let pokeStateValue = document.data()?["pokeState"] as? [String: String] ?? [:]
                
                let pokingToolColor: String = pokingTool["color"]?.lowercased() ?? ""
                let pokingToolTool: String = pokingTool["tool"]?.lowercased() ?? ""
                let toolImage: UIImage = UIImage(named: "\(pokingToolColor.lowercased())_\(pokingToolTool)") ?? UIImage()
                
                /* Tool enum ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ
                let toolType = convertStringToToolType(string: pokingTool["tool"] ?? "")
                let toolColor = convertStringToToolColor(string: pokingTool["color"] ?? "")
                let toolImage: UIImage = UIImage(named: "\(toolColor)_\(toolType.imageFileName)") ?? UIImage()
                */
                
                let pokedBy: String = pokeStateValue["pokedBy"] ?? ""
                let pokedTime: String = pokeStateValue["pokedTime"] ?? ""
                
                user.setName(name: name)
                user.setToolImage(toolImage: toolImage)
                user.setToolType(with: pokingToolTool)
                user.setToolColor(with: pokingToolColor)
                user.setFamilyCodes(code: familyCodes)
                user.setPoke(poke: Poke(pokedBy: pokedBy, pokedTime: pokedTime))
                completion()
            } else {
                print(error ?? "")
            }
        }
    }
    
    func getSignInUser(userIdentifier: String, completion: @escaping () -> Void) {
        loginedUser = User(accountId: userIdentifier)
        
        db.collection("Users").document(userIdentifier).addSnapshotListener { [self] (document, error) in
            if let document = document {
                let name = document.data()?["name"] as? String ?? ""
                let familyCodes = document.data()?["familyCode"] as? [String] ?? []
                let pokingTool = document.data()?["pokingTool"] as? [String: String] ?? [:]
                let pokeStateValue = document.data()?["pokeState"] as? [String: String] ?? [:]
                
                let pokingToolColor: String = pokingTool["color"]?.lowercased() ?? ""
                let pokingToolType: String = pokingTool["tool"]?.lowercased() ?? ""
                let toolImage: UIImage = UIImage(named: "\(pokingToolColor)_\(pokingToolType)") ?? UIImage()
                let pokedBy: String = pokeStateValue["pokedBy"] ?? ""
                let pokedTime: String = pokeStateValue["pokedTime"] ?? ""
                
                loginedUser.setName(name: name)
                loginedUser.setToolImage(toolImage: toolImage)
                loginedUser.setToolType(with: pokingToolType)
                loginedUser.setToolColor(with: pokingToolColor)
                loginedUser.setFamilyCodes(code: familyCodes)
                loginedUser.setPoke(poke: Poke(pokedBy: pokedBy, pokedTime: pokedTime))
                
                completion()
                
            } else {
                print(error ?? "")
            }
        }
    }
    
    func getFamilyMember(familyCode: String, completion: @escaping () -> Void) {
        db.collection("Families").document(familyCode).addSnapshotListener { [self] (document, error) in
            if let document = document {
                let userIdentifier: [String] = document.data()?["users"] as? [String] ?? []
                userIdentifiers = userIdentifier
                db.collection("Users").whereField("familyCode", isEqualTo: familyCode).addSnapshotListener { [self] (querySnapshot, _) in/*.getDocuments(source: .default) { [self] (querySnapshot, error) in*/
                    if let error = error {
                        print("Error getting documents: \(error)")
                    } else {
                        families = [Family(user: User(accountId: "",
                                                      name: "๋ชจ๋‘",
                                                      toolImage: UIImage(named: "IconEveryoneFilter")!,
                                                      toolType: "",
                                                      toolColor: "",
                                                      familyCodes: [String()],
                                                      pokeState: Poke()),
                                           isSelected: true)]
                        for document in querySnapshot!.documents {
                            let data = document.data()
                            
                            let accountId = data["accountId"] as? String ?? ""
                            let name = data["name"] as? String ?? ""
                            /* ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ์ด ๋˜๋Š” ๊ฒฝ์šฐ 0: ์• ํ”Œ ๊ณ„์ • ๋กœ๊ทธ์ธ, 1: ๊ตฌ๊ธ€ ๊ณ„์ • ๋กœ๊ทธ์ธ์œผ๋กœ ์‚ฌ์šฉ ์˜ˆ์ •
                            let loginForm = data["loginForm"] as? Int ?? 0
                            */
                            let familyCodes = data["familyCode"] as? [String] ?? []
                            let pokingTool = data["pokingTool"] as? [String: String] ?? [:]
                            let pokeStateValue = data["pokeState"] as? [String: String] ?? [:]
                            
                            let pokingToolColor: String = pokingTool["color"]?.lowercased() ?? ""
                            let pokingToolTool: String = pokingTool["tool"]?.lowercased() ?? ""
                            let toolImage: UIImage = UIImage(named: "\(pokingToolColor)_\(pokingToolTool)") ?? UIImage()
                            let pokedBy: String = pokeStateValue["pokedBy"] ?? ""
                            let pokedTime: String = pokeStateValue["pokedTime"] ?? ""
                            
                            let user = User(accountId: accountId,
                                            name: name,
                                            toolImage: toolImage,
                                            toolType: pokingToolTool,
                                            toolColor: pokingToolColor,
                                            familyCodes: familyCodes,
                                            pokeState: Poke(pokedBy: pokedBy, pokedTime: pokedTime))
                            
                            families.append(Family(user: user, isSelected: false))
                        }
                        completion()
                    }
                }
            } else {
                print(error ?? "")
            }
        }
    }
    
    func setPokingToolData(userIdentifier: String, tool passedTool: PokeTool) {
        
        let tool = passedTool.tool.imageFileName
        let color = convertUIColorToString(color: passedTool.color)
        
        db.collection("Users").document(userIdentifier).updateData([
            "pokingTool.tool": tool,
            "pokingTool.color": color
        ])
    }
    
    func addReaction(familyCode: String, meal: Meal, newReaction: [String: String]) {
        db.collection("Families").document(familyCode).collection("Meals").whereField("uploadUser", isEqualTo: meal.userIdentifier).whereField("uploadDate", isEqualTo: meal.uploadedDate).whereField("uploadTime", isEqualTo: meal.uploadedTime).getDocuments { querySnapshot, error in
            if let error = error {
                print("Error getting documents: \(error)")
            } else {
                for document in querySnapshot!.documents {
                    let data = document.data()
                    
                    var reactions = data["reactions"] as? [[String: String]] ?? []
                    reactions.append(newReaction)
                    document.reference.updateData(["reactions": reactions])
                }
            }
        }
    }
    
    func setFamilyCode(userIdentifier: String, code: String) {
        db.collection("Users").document(userIdentifier).updateData(["familyCodes": [code]])
    }
    
    // TODO: ํŠน์ • ์ฝ”๋“œ๋ฅผ ์ง€์šฐ๊ธฐ ์œ„ํ•ด parameter๋กœ code๋ฅผ ๋ฐ›์•„์•ผ ํ•˜๋‚˜?
    func deleteFamilyCode(userIdentifier: String) {
        db.collection("Users").document(userIdentifier).updateData(["familyCodes": ""])
    }
    
    func setUserName(userIdentifier: String, userName: String) {
        db.collection("Users").document(userIdentifier).updateData(["name": userName])
    }
    
    func setDefaultUserData(userIdentifier: String) {
        db.collection("Users").document(userIdentifier).setData(["familyCode": "",
                                                            "name": "",
                                                            "pokeState": ["pokedBy": "",
                                                                          "pokedtime": ""],
                                                            "pokingTool": ["color": "",
                                                                           "tool": ""]])
    }
    
    func isExistFamily(roomCode: String, completion: @escaping () -> Void) {
        db.collection("Families").document(roomCode).getDocument { document, error in
            let isExist = document?.exists ?? false
            self.isExistFamily = isExist
            completion()
        }
    }
    
    func setUpMeals(image: UIImage,
                    userIdentifier: String,
                    familyCode: String,
                    tags: [String]) {
        let today = Date.now
        
        let documentRef = db.collection("Families").document(familyCode).collection("Meals")
        let storageManager = StorageManager()
        let imageName: String = "img_\(today.dateText)_\(today.timeNumberText)\(today.secondsText()).jpeg"
        
        storageManager.uploadMealImage(image: image, familyCode: familyCode, imageName: imageName) {
            documentRef.addDocument(data: [
                "mealImageIndex": imageName,
                "uploadUser": userIdentifier,
                "uploadDate": Date().dateText,
                "uploadTime": Date().timeNumberText,
                "tags": ["0": tags[0],
                         "1": tags[1],
                         "2": tags[2]
                        ],
                "reactions": []])
        }
        print("์ด๋ฏธ์ง€์ด๋ฆ„: \(imageName)")
    }