콘솔로 이동

iOS에서 데이터 검색

이 문서에서는 데이터 검색의 기초와 함께 Firebase 데이터를 정렬 및 필터링하는 방법을 설명합니다.

Firebase 데이터를 검색하려면 FIRDatabase 참조에 비동기 리스너를 연결합니다. 리스너는 데이터의 초기 상태가 확인될 때 한 번 호출된 후 데이터가 변경될 때마다 다시 호출됩니다.

이벤트 수신 대기

데이터를 검색하는 다음과 같은 유형의 이벤트를 수신 대기할 수 있습니다.

이벤트 유형 일반적인 용도
FIRDataEventTypeValue 경로의 전체 내용에 대한 변경을 읽고 수신 대기합니다.
FIRDataEventTypeChildAdded 항목 목록을 검색하거나 항목 목록에 대한 추가를 수신 대기합니다. FIRDataEventTypeChildChangedFIRDataEventTypeChildRemoved와 함께 사용하여 목록의 변경을 모니터링할 수 있습니다.
FIRDataEventTypeChildChanged 목록의 항목에 대한 변경을 수신 대기합니다. FIRDataEventTypeChildAddedFIRDataEventTypeChildRemoved와 함께 사용하여 목록의 변경을 모니터링할 수 있습니다.
FIRDataEventTypeChildRemoved 목록의 항목 삭제를 수신 대기합니다. FIRDataEventTypeChildAddedFIRDataEventTypeChildChanged와 함께 사용하여 목록의 변경을 모니터링할 수 있습니다.
FIRDataEventTypeChildMoved 순서 있는 목록의 항목 순서 변경을 수신 대기합니다. FIRDataEventTypeChildMoved 이벤트가 현재의 정렬 기준에 따라 항목 순서 변경의 원인이 된 FIRDataEventTypeChildChanged 이벤트를 항상 뒤따릅니다.

이벤트 리스너를 추가하려면 observeEventType 메소드를 사용하여 이벤트 유형 및 콜백 블록을 지정합니다.

값 이벤트

FIRDataEventTypeValue 이벤트를 사용하여 이벤트 발생 시점에 특정 경로에 있던 데이터를 읽을 수 있습니다. 이 메소드는 리스너가 연결될 때 한 번 호출된 후 하위를 포함한 데이터가 변경될 때마다 다시 호출됩니다. 하위 데이터를 포함하여 해당 위치의 모든 데이터를 포함하는 snapshot이 이벤트 콜백에 전달됩니다. 데이터가 없는 경우 반환된 snapshotvaluenil입니다.

다음 예제에서는 데이터베이스에서 게시물 세부정보를 검색하는 소셜 블로깅 애플리케이션을 보여 줍니다.

Objective-C

_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  NSDictionary *postDict = snapshot.value;
  // ...
}];

Swift

refHandle = postRef.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
  let postDict = snapshot.value as! [String : AnyObject]
  // ...
})

리스너는 이벤트 발생 시점에 데이터베이스에서 지정된 위치에 있던 데이터를 value 속성에 포함하는 FIRDataSnapshot을 수신합니다. 이 값을 NSDictionary 등의 적절한 기본 유형에 대입할 수 있습니다. 해당 위치에 데이터가 없는 경우 valuenil입니다.

하위 이벤트

하위 이벤트는 노드의 하위에서 발생하는 특정 작업에 대응하여 발생합니다. 하위 항목이 childByAutoId 메소드를 통해 새로 추가되거나 updateChildValues 메소드를 통해 업데이트되는 경우가 그 예입니다.

FIRDataEventTypeChildAdded 이벤트는 일반적으로 Firebase 데이터베이스의 항목 목록을 검색하는 데 사용됩니다. FIRDataEventTypeChildAdded 이벤트는 기존 하위 항목마다 한 번씩 발생한 후 지정된 경로에 하위 항목이 새로 추가될 때마다 다시 발생합니다. 리스너에는 새 하위 항목의 데이터를 포함하는 스냅샷이 전달됩니다.

FIRDataEventTypeChildChanged 이벤트는 하위 노드가 수정될 때마다 발생합니다. 여기에는 하위 노드의 하위에 대한 수정이 포함됩니다. 이 이벤트는 일반적으로 FIRDataEventTypeChildAddedFIRDataEventTypeChildRemoved 이벤트와 함께 항목 목록의 변경에 대응하는 데 사용됩니다. 이벤트 리스너에 전달되는 스냅샷에는 하위 항목의 업데이트된 데이터가 포함됩니다.

FIRDataEventTypeChildRemoved 이벤트는 바로 아래 하위 항목이 삭제될 때 발생합니다. 이 이벤트는 일반적으로 FIRDataEventTypeChildAddedFIRDataEventTypeChildChanged 이벤트와 함께 사용됩니다. 콜백 블록에 전달되는 스냅샷에는 삭제된 하위 항목의 데이터가 포함됩니다.

하위 항목의 재정렬을 야기하는 업데이트에 의해 FIRDataEventTypeChildChanged 이벤트가 발생할 때마다 FIRDataEventTypeChildMoved 이벤트가 발생합니다. 이 이벤트는 queryOrderedByChild 또는 queryOrderedByValue로 정렬된 데이터에 사용됩니다.

이러한 메소드는 데이터베이스의 특정 노드에 대한 변경을 수신 대기하는 데 유용할 수 있습니다. 예를 들어 소셜 블로깅 앱은 아래와 같이 이러한 메소드를 함께 사용하여 게시물의 댓글 활동을 모니터링할 수 있습니다.

Objective-C

// Listen for new comments in the Firebase database
[_commentsRef
              observeEventType:FIRDataEventTypeChildAdded
              withBlock:^(FIRDataSnapshot *snapshot) {
                [self.comments addObject:snapshot];
                [self.tableView insertRowsAtIndexPaths:@[
                  [NSIndexPath indexPathForRow:[self.comments count] - 1 inSection:kSectionComments]
                ]
                                      withRowAnimation:UITableViewRowAnimationAutomatic];
              }];
// Listen for deleted comments in the Firebase database
[_commentsRef
 observeEventType:FIRDataEventTypeChildRemoved
 withBlock:^(FIRDataSnapshot *snapshot) {
   int index = [self indexOfMessage:snapshot];
   [self.comments removeObjectAtIndex:index];
   [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:kSectionComments]]
                         withRowAnimation:UITableViewRowAnimationAutomatic];
 }];

Swift

// Listen for new comments in the Firebase database
commentsRef.observeEventType(.ChildAdded, withBlock: { (snapshot) -> Void in
  self.comments.append(snapshot)
  self.tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: self.comments.count-1, inSection: self.kSectionComments)], withRowAnimation: UITableViewRowAnimation.Automatic)
})
// Listen for deleted comments in the Firebase database
commentsRef.observeEventType(.ChildRemoved, withBlock: { (snapshot) -> Void in
  let index = self.indexOfMessage(snapshot)
  self.comments.removeAtIndex(index)
  self.tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: self.kSectionComments)], withRowAnimation: UITableViewRowAnimation.Automatic)
})

리스너 분리

관찰자는 ViewController에서 벗어날 때 데이터 동기화를 자동으로 중지하지 않습니다. 관찰자를 적절히 삭제하지 않으면 데이터가 계속 로컬 메모리에 동기화됩니다. 관찰자가 더 이상 필요하지 않은 경우 removeObserverWithHandle 메소드에 해당 FIRDatabaseHandle을 전달하여 삭제하세요.

참조에 콜백 블록을 추가하면 FIRDatabaseHandle이 반환됩니다. 이 핸들을 사용하여 콜백 블록을 삭제할 수 있습니다.

한 데이터베이스 참조에 여러 리스너를 추가하면 이벤트가 발생할 때 각 리스너가 모두 호출됩니다. 해당 위치의 데이터 동기화를 중지하려면 removeAllObservers 메소드를 호출하여 모든 관찰자를 삭제해야 합니다.

리스너에 대해 removeObserverWithHandle 또는 removeAllObservers를 호출해도 하위 노드에 등록된 리스너가 자동으로 삭제되지 않습니다. 이러한 참조 또는 핸들을 별도로 추적하여 삭제해야 합니다.

데이터 한 번 읽기

한 번만 호출되고 즉시 삭제되는 콜백이 필요한 경우가 있습니다. 이후에 변경되지 않는 UI 요소를 초기화할 때가 그 예입니다. 이러한 경우 observeSingleEventOfType 메소드를 사용하면 시나리오가 단순해집니다. 이렇게 추가된 이벤트 콜백은 한 번 호출된 후 다시 호출되지 않습니다.

이 방법은 한 번 로드된 후 자주 변경되지 않거나 능동적으로 수신 대기할 필요가 없는 데이터에 유용합니다. 예를 들어 위 예제의 블로깅 앱에서는 사용자가 새 게시물을 작성하기 시작할 때 이 메소드로 사용자의 프로필을 로드합니다.

Objective-C

NSString *userID = [FIRAuth auth].currentUser.uid;
[[[_ref child:@"users"] child:userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  // Get user value
  User *user = [[User alloc] initWithUsername:snapshot.value[@"username"]];

  // ...
} withCancelBlock:^(NSError * _Nonnull error) {
  NSLog(@"%@", error.localizedDescription);
}];

Swift

let userID = FIRAuth.auth()?.currentUser?.uid
ref.child("users").child(userID!).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
  // Get user value
  let username = snapshot.value!["username"] as! String
  let user = User.init(username: username)

  // ...
  }) { (error) in
    print(error.localizedDescription)
}

데이터 정렬 및 필터링

실시간 데이터베이스의 FIRDatabaseQuery 클래스를 사용하여 키, 값 또는 하위 값으로 정렬된 데이터를 검색할 수 있습니다. 정렬된 결과를 특정 개수로 필터링하거나 키 또는 값 범위에 따라 필터링할 수도 있습니다.

데이터 정렬

정렬된 데이터를 검색하려면 우선 정렬 기준 메소드 중 하나를 지정하여 결과의 순서를 결정합니다.

메소드 용도
queryOrderedByKey 하위 키에 따라 결과를 정렬합니다.
queryOrderedByValue 하위 값에 따라 결과를 정렬합니다.
queryOrderedByChild 지정된 하위 키의 값에 따라 결과를 정렬합니다.

정렬 기준 메소드는 한 번에 하나만 사용할 수 있습니다. 한 쿼리에서 정렬 기준 메소드를 여러 번 호출하면 오류가 발생합니다.

다음 예제에서는 별표 개수를 기준으로 사용자의 최상위 게시물 목록을 검색하는 방법을 보여 줍니다.

Objective-C

// My top posts by number of stars
FIRDatabaseQuery *myTopPostsQuery = [[[self.ref child:@"user-posts"]
                                      child:[super getUid]]
                                     queryOrderedByChild:@"starCount"];

Swift

// My top posts by number of stars
let myTopPostsQuery = (ref.child("user-posts").child(getUid())).queryOrderedByChild("starCount")

이 쿼리는 사용자 ID를 기준으로 데이터베이스 경로에서 사용자의 게시물을 검색하고 각 게시물의 별표 수에 따라 정렬합니다. 이와 같이 ID를 색인 키로 사용하는 기법을 데이터 팬아웃이라고 합니다. 자세한 내용은 데이터베이스 구조화를 참조하세요.

queryOrderedByChild 메소드를 호출할 때는 결과를 정렬할 기준이 될 하위 키를 지정합니다. 여기에서는 각 게시물의 "starCount" 하위 항목 값에 따라 게시물이 정렬됩니다. 다른 데이터 유형이 정렬되는 방법에 대한 자세한 내용은 쿼리 데이터의 순서를 참조하세요.

데이터 필터링

데이터를 필터링하려면 제한 또는 범위 메소드를 정렬 기준 메소드와 조합하여 쿼리를 작성합니다.

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

정렬 기준 메소드와 달리 여러 개의 제한 또는 범위 함수를 결합할 수 있습니다. 예를 들어 queryStartingAtValuequeryEndingAtValue 메소드를 결합하여 결과를 지정된 값 범위로 제한할 수 있습니다.

결과 수 제한

queryLimitedToFirstqueryLimitedToLast 메소드를 사용하여 특정 콜백에서 동기화할 하위 항목의 최대 개수를 설정할 수 있습니다. 예를 들어 queryLimitedToFirst를 사용하여 제한을 100개로 설정하면 처음에 최대 100개의 FIRDataEventTypeChildAdded 콜백만 수신합니다. Firebase 데이터베이스에 저장된 항목이 100개 미만이면 각 항목에 대해 FIRDataEventTypeChildAdded 콜백이 호출됩니다.

항목이 변경됨에 따라 쿼리에 새로 포함되는 항목에 대해 FIRDataEventTypeChildAdded 콜백, 쿼리에서 제외되는 항목에 대해 FIRDataEventTypeChildRemoved 콜백이 수신되며 총 개수는 100개로 유지됩니다.

다음 예제 블로깅 앱에서는 모든 사용자의 게시물 중 최근 100개의 목록을 검색하는 방법을 보여 줍니다.

Objective-C

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
FIRDatabaseQuery *recentPostsQuery = [[self.ref child:@"posts"] queryLimitedToFirst:100];

Swift

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
let recentPostsQuery = (ref?.child("posts").queryLimitedToFirst(100))!

키 또는 값으로 필터링

queryStartingAtValue, queryEndingAtValuequeryEqualToValue를 사용하여 쿼리의 시작, 종료 및 동일 지점을 임의로 선택할 수 있습니다. 이 메소드는 데이터를 페이지화하거나 하위 항목이 특정 값인 항목을 찾는 데 유용합니다.

쿼리 데이터의 순서

이 섹션에서는 FIRDatabaseQuery 클래스의 각 정렬 순서 메소드가 데이터를 정렬하는 방법을 설명합니다.

queryOrderedByKey

queryOrderedByKey를 사용하여 데이터를 정렬하는 경우 데이터가 키에 따라 오름차순으로 반환됩니다.

  1. 키가 32비트 정수로 파싱될 수 있는 하위 항목이 맨 처음에 나오며 오름차순으로 정렬됩니다.
  2. 키가 문자열 값인 하위 항목이 다음에 나오며 사전순, 오름차순으로 정렬됩니다.

queryOrderedByValue

queryOrderedByValue를 사용하면 하위 항목이 값에 따라 정렬됩니다. 정렬 기준은 queryOrderedByChild와 동일하며, 지정된 하위 키의 값 대신 노드의 값이 사용된다는 점이 다릅니다.

queryOrderedByChild

queryOrderedByChild를 사용하면 지정된 하위 키를 포함하는 데이터가 다음과 같이 정렬됩니다.

  1. 지정된 하위 키의 값이 nil인 하위 항목이 맨 처음에 옵니다.
  2. 지정된 하위 키의 값이 false인 하위 항목이 그 다음에 옵니다. 값이 false인 하위 항목이 여러 개이면 키에 따라 사전순으로 정렬됩니다.
  3. 지정된 하위 키의 값이 true인 하위 항목이 그 다음에 옵니다. 값이 true인 하위 항목이 여러 개이면 키에 따라 사전순으로 정렬됩니다.
  4. 숫자 값을 갖는 하위 항목이 다음에 나오며 오름차순으로 정렬됩니다. 지정된 하위 노드의 숫자 값이 동일한 하위 항목이 여러 개이면 키에 따라 정렬됩니다.
  5. 숫자 다음에는 문자열이 나오며 사전순, 오름차순으로 정렬됩니다. 지정된 하위 노드의 값이 동일한 하위 항목이 여러 개이면 키에 따라 사전순으로 정렬됩니다.
  6. 마지막으로 개체가 나오며 키에 따라 사전순, 오름차순으로 정렬됩니다.

다음 단계