ios Firebase에서 json 데이터를 완벽하게 받아내는법

 

firebase를 이용해 백을 구축했다가, json 데이터를 Dictionary형태로 받아 풀고, Array로 바꾸고 하는 등의 삽질을 한 결과

이전 포스트 에서 데이터를 받아오긴 했습니다.

그러나!

문제는 아무리 json을 정렬해서 받아오도록 하여도 순서를 자기 마음대로 뒤죽박죽 섞어버린다는 문제점이 있었습니다.

또한 MVC를 적용하기도 힘들구요.

 

그래서, 모델을 작성하고 해당 모델에 맞게 데이터를 (너무나) 가져오고 싶어서

검색에 검색을 더한 결과!!

찾아낼 수 있었습니다.

심지어 이러한 방법은 2021년부터 지원을 한다는 소식을 듣고

'아.. 이래서 레퍼런스를 찾기 힘들었구나' 싶었습니다.

아무튼! 이제부터 그 방법을 공개하겠습니다.

 

여기서 사용된 기술은 FirebaseDecoder() 이며,

Podfile에 CodableFirebase 가 설치되어 있어야 합니다.

또한 VC 에서 import CodableFirebase 까지 하셔야 사용 가능합니다.

 

먼저 다음의 데이터 구조가 있다고 가정합니다.

우리의 DB 밑에 있는 places/autoid2/user_comments/test 라는 key-value 입니다.

 

"test" : {
          "comment" : "comment_test",
          "comment_image_address" : "image2021_03_11_21_59_06_2490",
          "comment_rating" : 4.2,
          "user_id" : "user_id 테스트"
        }

 

위 데이터 구조에 맞춰 model도 한번 만들어봅시다.

struct UserComment:Codable {
    let commentRating: Double
    let commentImageAddress: String //comment_image_address
    let userId: String //user_id
    let comment: String

    enum CodingKeys: String, CodingKey {
        case commentRating = "comment_rating"
        case commentImageAddress = "comment_image_address"
        case userId = "user_id"
        case comment
    }
}

 

위 데이터 구조를 작성한 model 한번 받아봅시다.

먼저 Database.database().reference()로 ref를 선언해야하는 건 알고 있다고 생각하겠습니다.

(이에 대해 궁금하신 분들은 firebase ios에서 데이터 읽기 및 쓰기 공식문서 를 참고하세요)

 

        // getting one comment
        self.ref.child("places").child("autoid2").child("user_comments").child("test").getData { 
          (error, snapshot) in
            if let error = error {
                print("Error getting data \(error)")
            }
            else if snapshot.exists() {
                guard let value = snapshot.value else {return}
                do {
                    let userComment = try FirebaseDecoder().decode(UserComment.self, from: value)
                    print(userComment)
                } catch let err {
                    print (err)
                }

            }
            else {

                print("No data available")
            }
        }

 

받아온 userComment 를 출력한 결과는 다음과 같습니다.

UserComment(commentRating: 4.2, commentImageAddress: "image2021_03_11_21_59_06_2490", userId: "user_id 테스트", comment: "comment_test")

 

위 데이터에서 commentImageAddress 같은 String을 얻고 싶다면 그냥 userComment.commentImageAddress로 접근하면 됩니다.

 

이제 한 차원 더 깊게 들어가봅시다.

만약 모든 데이터가 key-value 하나로만 돼 있으면 좋겠지만,

대개의 경우 key-[key-value] 와 같이 어떤 key의 value가 또다시 배열인 구조를 가지고 있는 경우가 많습니다.

예를 들어 다음처럼요.

 

"user_comments" : {
        "-MVWMcO-jvY32tBLMd3a" : {
          "comment" : "사보루 ~~ 최고인 ~~~",
          "comment_image_address" : "image2021_03_11_21_59_06_2490",
          "comment_rating" : 4.2,
          "user_id" : "사보r 최고!"
        },
        "-MVZcaLaVum2zU1seXUj" : {
          "comment" : "Good",
          "comment_image_address" : "image2021_03_12_13_12_06_0040",
          "comment_rating" : 2.8,
          "user_id" : "polarbear"
        },
        "-MVZcmG7mhkBJMPFJ4Ps" : {
          "comment" : "Bad",
          "comment_image_address" : "default_image.png",
          "comment_rating" : 0,
          "user_id" : "holly"
        },
        ...
      }

 

위와 같은 데이터 구조는 살짝 다르게 받아주어야 합니다.

일단 user_comments 속 comment 하나하나는 UserComment로 위에서 모델을 정의했습니다.

여기서 제 아이디어는 user_comments 정보를 dictionary<String, UserComment>로 받자는 것입니다.

한번 코드로 확인해볼까요?

 

self.ref.child("places").child("autoid2").child("user_comments").getData { 
  (error, snapshot) in
            if let error = error {
                print("Error getting data \(error)")
            }
            else if snapshot.exists() {
                print("Got data \(snapshot.value!)")
                print("type: \(type(of: snapshot.value!))")

                guard let value = snapshot.value else {return}
                do {
                    let userComments 
                  = try FirebaseDecoder().decode(Dictionary<String, UserComment>.self, from: value)
                    for comment in userComments.values {
                        print("comment: \(comment)")
                    }
                } catch let err {
                    print (err)
                }

            }
            else {

                print("No data available")
            }
        }

 

for-in 문을 이용해 dictionary로 가져온 모든 아이템 하나하나를 출력해보았습니다.

그 결과, 로그창에 다음과 같이 정보가 원하는 대로 잘 print 되었습니다.

 

comment: UserComment(commentRating: 3.4, commentImageAddress: "image2021_03_14_19_45_40_0040", userId: "42kcho***", comment: "Good")

comment: UserComment(commentRating: 0.0, commentImageAddress: "default_image.png", userId: "bad", comment: "Bad")

comment: UserComment(commentRating: 4.2, commentImageAddress: "image2021_03_11_21_59_06_2490", userId: "user_id 테스트", comment: "comment_test")

comment: UserComment(commentRating: 5.0, commentImageAddress: "image2021_03_17_13_13_46_7950", userId: "42kcho***", comment: "나킴")
...

 

refs

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기
// custom