(Firebase) 실시간 데이터베이스 기본 기능

호두파파·2021년 5월 7일
0

Firebase

목록 보기
2/2

1. Firebase 실시간데이터베이스 읽기

Firebase는 Documenbt형태이고 트리구조라고 되어 있다. 그렇기 때문에 미리 선언된 ref 변수를 사용한다고 해도 전체 데이터를 가져오게 된다. 그래서 child([명칭])을 사용해서 특정 노드에 들어가 데이터를 읽고 축적할 수 있다.
그래서 아래와 같이 선언을 해주고 시작해야 한다.

var boardRef = ref.child("Board")

읽기는 기본적으로 2개의 함수를 제공해주고 있다.

//호출
func observe(_eventType: FIRDateEventType, with block: @escaping(FIRDataSnapshot) -> Swift.Void) -> Unit

//한번호출 
func observeSingleEvent(of eventType: FIRDataEventType, with block: @escaping (FIRDataSnapshot) -> Swift.Void)

기본적으로 파이어베이스는 비동기를 지원하고 있다. 그리고 FIRDataEventType에 따라 호출될수 있도록 선언하고 있다. 또한 계속 호출할 것인지 최초 한번만 호출을 할 것인지 제공해주고 있다.

FIRDateSnapshot(이하 스냅샵)은 데이터가 가져오는 형태라고 생각하면 된다. 기존의 데이터베이스는 JSON으로 되어 있다. 스냅샵을 사용하면 Swift에 맞도록 SDK에서 제공해준다. Dictionary 타입으로 리턴이 된다.

let value = snapshot.value as![string.Any]

하지만 이 역시 주의해서 사용해야 한다. 계속 추가만해서 사용한다면 통신비가 증가할 수 있다.

이벤트 타입

  • value : 데이터 전체를 가지고 온다.
  • childAdded : 노드에서부터 추가되는 데이터를 감지한다.
  • childRemoved: 노드에서부터 삭제되는 데이터를 감지한다.
  • childChanged: 노드에서부터 변경되는 데이터를 감지한다.
  • childMoved: 노드에서부터 이동하는 데이터를 감지한다.

Firebase 실시간데이터베이스 쓰기

기존의 데이터를 저장하기 위해서는 3가지 정도의 방법이 있다. 그리고 고유값을 사용하기 위해서 사용하는 메소드가 4종류가 있다.

  • childByAutold : 고유값 생성해주는 메소드이다. 간단하게 말하자면 key를 생성해주는 녀석이다. 데이터베이스를 사용한다면 특정 값을 찾기 위해서 혹은 비교하기 위해서 등의 이유로 고유키를 사용하게 된다. (게시판으로 치면 게시번호라고 보면 좋다)
    그렇기 때문에 글이 중복되지 않게 하거나, 혹은 특정값을 찾기 혹은 증명하는 용도로 많이 사용한다.

    Document 타입인 파이어베이스 실시간 데이터 베이스는 트리 형태의 구조를 가지고 있고, JSON 형태를 사용하고 있다. 그렇기 때문에 수많은 값을 증명하고 고유값을 부여해야 한다.

let ref : FIRDatabaseReference! =
FIRDatavase.database().reference().child("board")
let key = ref.childByAutoId().key // string
// 반드시 하나의 노드를 생성해서 사용해야 한다. child({노드이름})
let ref : FIRDatabaseReference! = 
FIRDatabase.database().reference().child("board")

let data : [string : Any] = [
  "key" : ref.childByAutoId().key,
  "title" : "test".
  "text" : "테스트로 일단 넣는 글입니다.",
  "recordTime" : FIRServerValue.timestamp()
]
ref.setValue(data, withCompletionBlock: {(error, ref) in 
  if let err = error {
    print(err.localizeDescription)
  }
  ref.observe(.value, with: {(snapshot) in
    guard snapshot.exists() else {
      return 
    }
	// 코드 작성
  }
})
  • setValue : 데이터를 저장할때
  • updateChildValues : 데이터를 수정 혹은 저장할때
    함수명에서도 나와있듯이 업데이트를 할 경우에 많이 사용이 되는 구문이다. 다만 차이가 조금 있다면
let userID = FIRAuth.auth().currentUser.uid
let key = ref.child("board").childByAutoId().key
let post = ["uid": useID,
            "author": username, 
            "title": title,
            "body": body]
let childUpdates = ["/board/\(key)":post, 
                    "/user-posts/\(userID)/\(key)/":post]
ref.updateChildValues(childUpdates)

위의 코드와 같이 여러개의 노드를 동시에 해야 할 경우에는 위와 같이 사용하면 된다.
여기서 중요한 부분이 있는데, 실시간 데이터 베이스다 보니 여러명의 사용자가 특정 글을 수정하게 될 경우에는 (예시 댓글 혹은 좋아요 표현) 문제가 있을 수 있다. (신시간의 특성의 문제)
그렇기 때문에 runTransactionBlock을 사용하면된다.

  • runTransactionBlock : 데이터를 순차적으로 저장 수정해야 할때(예시: 페이스북의 좋아요 수치를 수정)
    이 함수는 순차적으로 데이터를 덮어 씌워준다. 그렇기 때문에 여러명이 한글에 대해서 댓글을 달거나 좋아요 표현을 할 경우 순서대로 처리해준다.
ref.runTransactionBlock({ (currentData: FIRMutalbeData) -> FIRTransactionResult in
  if var post = currentData.value as? [String : AnyObject], let uid = FIRAuth.auth()?.currentUser?.uid {
  	var start : Dictionaty<string, Bool>
    stars = post["stars"] as? [String : Bool] ?? [:]
	var starCount = post["statCount"] as? Int ?? 0
    if let_= stars[uid] {
      // unstar the post and remove self from stats
      statCount -=1
      stars.removeValueForKey(uid)
    } else {
      // star the post and add self to stars
      starCount += 1 
      stars[uid] = true
    }
	post["starCount"] = starCount
	post["stars"] = stars

	// Set value and report transaction success
	currentData.value = post
	
	return FIRTransactionResult.successWithValue(currentData)
  }
  return FIRTransactionResult.successWithValue(currentData)
}) { (error, committed, snapshot) in
  if let error = error {
    print(error.localizedDescription)
  }
}

중요한 부분은 마지막 리턴을 해야 하는 부분을
FIRTransactionResult.successWithValue(currentData)사용해서 넣어주면 된다.

Firebase 실시간데이터베이스 삭제

여기서 삭제란 '완전삭제'를 의미한다. 그렇기 때문에 간으하면 유저에게 알림을 통해서 완전삭제의 메시지를 통해서 복구가 안됨을 고지해야 한다.

ref.child(key!).removeValue(completionBlock: { (err,ref) in 
  if err !== nil {
    	debugPrint("failed to remove reply")
    	return 
  }
})

위의 코드와 같이 삭제할때는 키값만 있으면 삭제가 된다. 그리고 확실하게 노드로 구분을 해줘야 한다. 보통은 고유키 값이라서 중복된 부분이 있을리가 만무하지만, 혹 모르는 일이니 노드까지 다 붙여서 사용하는 것이 좋다.

Firebase 실시간데이터베이스 기타

파이어 베이스의 경우에는 조건에 대한 부분이 참 까다롭다. 특정 값에 대한 필터가 사실상 어렵다.
기본적으로 파베에서는 정렬은 제공을 해준다.
키, 값, 자식(child) 3가지를 제공해준다.
ref를 선언하고 나서 아래 표의 3가지 중 하나를 사용하면 된다.

  • queryOrderedByKey : 하위 키에 따라 결과를 정렬한다.
  • queryOrderedByValue : 하위 값에 따라 결과를 정렬한다.
  • queryOrderedByChild : 지정된 하위 키의 값에 따라 결과를 정렬한다.
var ref = FIRDatabaseReference! = 
FIRDatabase.database().reference().child().child("board").queryOrderedByChild("createTime")

만약 게시판을 기준으로 하게 된다면 createTime(생성시간) 기준으로 정렬을 하면 된다. 기본적으로 사전순, 오름차순 순으로 정렬이 된다.
(내림차순은 클로저를 이용해서 따로 처리를 해주어야 한다.)

데이터 필터링을 하기 위한 옵션

  • queryLimitedToFirtst : 정렬된 결과 목록에서 맨 처음부터 반환할 최대 항목 개수를 설정한다.
  • queryLimitedTOLast : 정렬된 결과 목록에서 맨 끝부터 반환할 최대 항목 개수를 설정한다.
  • queryStartingAtValue : 선택한 정렬 기준 메소드에 따라 지정된 키 또는 값보다 크거나 같은 항목을 반환한다.
  • queryEndingAtValue : 선택한 정렬 기준 메소드에 따라 지정된 키 또는 값보다 작거나 같은 항목을 반환한다.

페이징

사용하기에 따라 다르겠지만 다중 값에 대한 필터링은 힘들다. 제목과 내용에 특정 값을 찾기라던가, 내가 원하는 특정의 뭔가를 임의적으로 하기에 많이 부족하다.

var ref = FIRDatabaseReference! = 
FIRDatabase.database().reference().child("board")
let rangeOfPosts : Unit = 10 // 페이지당 표현 갯수 
var pageOfPosts : Unit = 1 // 현재 페이지 갯수 
var lastestKey : String? // 불러온 가장 마지막 값 
  
// 전체 갯수를 가져온다 (Reload 혹은 최초로 당시)
ref.queryLimited(toFirst: rangeOfPosts *
pageOfPosts).observeSingleEvent(of: .value, with: {(snapshot) in 
    guard snapshot.exists() else {
      return 
    }
// 검색된 값중 가장 마지막 키갑
	selt.lastestKey = (snapshot.children.allObject.last as! FIRDataSnapshot).key
// 코드 생략
}
// 마지막 키값 기준의 10개씩 가지고 있다. 
ref.queryStarting(atValue : lastestKey).queryLimited(toFirt: rangeOfPosts).observeSingleEvent(of: .value, with: {(snapshot) in 
  guard snapshot.exists() else {
    return 
  }
	// 검석된 값중 가장 마지막 키 값 
	self.lastestKey = (snapshot.children.allObjcet.last as! FIRDataSnapshot).key
	// 코드 생략
}

swift var lastedKey : string? 부분은 검색된 부분의 가장 마지막 값이다.
아쉽게도 파이어베이스는 원하는 것을 정확하게 뽑아내기가 힘들다. 예를 들자면 이곳 저곳의 데이터를 동시에 뽑아낼 수 없고, 특정 범위 중에서 만족하는 값(=교집합)을 뽑아낼 수 없다. 단순히 범위까지는 뽑아낸다. (toLast toFirst)
NoSQL 문서타입의 특성때문에 상황에 따른 적절한 검색이 좀 힘들다. 그냥 값ㅇ르 가져와서 자체적으로 검색할 수 있게 만드는 것이 더 편하게 만드는 코딩일 것이다.


출처

[공부해서남주기] Firebase 실시간 데이터 베이스

        
profile
안녕하세요 주니어 프론트엔드 개발자 양윤성입니다.

0개의 댓글