데이터 저장

데이터 저장 방법

PUT 정의된 경로(예: fireblog/users/user1/<data>)에 데이터를 쓰거나 대체합니다.
PATCH 정의된 경로에서 모든 데이터를 대체할 필요 없이 일부 키를 업데이트합니다.
POST Firebase 데이터베이스의 데이터 목록에 추가합니다. POST 요청을 전송할 때마다 Firebase 클라이언트가 fireblog/users/<unique-id>/<data>와 같은 고유 키를 생성합니다.
삭제 지정된 Firebase 데이터베이스 참조에서 데이터를 삭제합니다.

PUT으로 데이터 쓰기

REST API를 통한 기본 쓰기 작업은 PUT입니다. 데이터 저장을 시연하기 위해 게시물과 사용자가 포함된 블로그 애플리케이션을 만들어 보겠습니다. 애플리케이션의 모든 데이터는 Firebase 데이터베이스 URL `https://docs-examples.firebaseio.com/fireblog`의 `fireblog` 경로에 저장됩니다.

우선 Firebase 데이터베이스에 사용자 데이터를 저장해 보겠습니다. 고유한 사용자 이름을 기준으로 각 사용자를 저장하고 성명과 생일을 함께 저장하려고 합니다. 각 사용자가 고유한 사용자 이름을 가지므로 POST 대신 PUT을 사용하는 것이 합리적입니다. 이미 키가 있어 별도의 키를 만들 필요가 없기 때문입니다.

PUT을 사용하여 문자열, 숫자, 불리언, 배열 또는 임의의 JSON 객체를 Firebase 데이터베이스에 기록할 수 있습니다. 여기에서는 객체를 전달합니다.

curl -X PUT -d '{
  "alanisawesome": {
    "name": "Alan Turing",
    "birthday": "June 23, 1912"
  }
}' 'https://docs-examples.firebaseio.com/fireblog/users.json'

JSON 객체가 데이터베이스에 저장될 때 객체 속성은 중첩된 형태로 하위 위치에 자동으로 매핑됩니다. 새로 생성된 노드를 탐색하면 'Alan Turing'이라는 값이 표시됩니다. 하위 위치에 데이터를 직접 저장할 수도 있습니다.

curl -X PUT -d '"Alan Turing"' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome/name.json'
curl -X PUT -d '"June 23, 1912"' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome/birthday.json'

위의 두 예시, 즉 값을 객체에 한꺼번에 기록하는 경우와 하위 위치에 각각 기록하는 경우에 Firebase 데이터베이스에 저장되는 데이터는 결과적으로 동일합니다.

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing"
    }
  }
}

성공한 요청은 200 OK HTTP 상태 코드로 표시되고, 응답은 데이터베이스에 기록한 데이터를 포함합니다. 첫 번째 예시에서는 데이터를 감시하는 클라이언트에서 이벤트가 1개만 트리거되지만 두 번째 예시에서는 2개의 이벤트가 트리거됩니다. 사용자 경로에 이미 데이터가 있는 경우, 첫 번째 방식은 데이터를 덮어쓰는 반면 두 번째 방식은 각각 분리된 하위 노드 값만 수정되고 다른 하위 노드는 바뀌지 않는다는 점에 유의해야 합니다. PUT는 자바스크립트 SDK의 set()에 해당합니다.

PATCH로 데이터 업데이트

PATCH 요청을 사용하면 기존 데이터를 덮어쓰지 않고 특정 하위 항목을 업데이트할 수 있습니다. PATCH 요청으로 Turing의 사용자 데이터에 닉네임을 추가해 보겠습니다.

curl -X PATCH -d '{
  "nickname": "Alan The Machine"
}' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome.json'

위 요청에서는 name 또는 birthday 하위 항목을 삭제하지 않고 alanisawesome 객체에 nickname을 씁니다. 이 대신 PUT 요청을 실행했다면 namebirthday가 요청에 포함되지 않아 삭제되었을 것입니다. 이제 Firebase 데이터베이스의 데이터가 다음과 같이 표시됩니다.

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    }
  }
}

성공한 요청은 200 OK HTTP 상태 코드로 표시되고, 응답은 데이터베이스에 기록된 업데이트 데이터를 포함합니다.

Firebase는 다중 경로 업데이트도 지원합니다. 다시 말해 이제 PATCH로 Firebase 데이터베이스의 여러 위치에서 동시에 값을 업데이트할 수 있으며, 이 강력한 기능을 통해 데이터를 비정규화할 수 있습니다. 다중 경로 업데이트를 사용하여 Alan과 Grace의 닉네임을 동시에 추가할 수 있습니다.

curl -X PATCH -d '{
  "alanisawesome/nickname": "Alan The Machine",
  "gracehopper/nickname": "Amazing Grace"
}' \
  'https://docs-examples.firebaseio.com/fireblog/users.json'

이 업데이트에 따라 Alan과 Grace의 닉네임이 추가되었습니다.

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper",
      "nickname": "Amazing Grace"
    }
  }
}

경로가 포함된 객체를 기록하여 객체를 업데이트하려고 하면 동작이 달라집니다. Grace와 Alan을 다음과 같은 방법으로 업데이트하면 어떻게 되는지 살펴보겠습니다.

curl -X PATCH -d '{
  "alanisawesome": {"nickname": "Alan The Machine"},
  "gracehopper": {"nickname": "Amazing Grace"}
}' \
  'https://docs-examples.firebaseio.com/fireblog/users.json'

이렇게 하면 동작이 달라져서 전체 /fireblog/users 노드를 덮어쓰게 됩니다.

{
  "users": {
    "alanisawesome": {
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "nickname": "Amazing Grace"
    }
  }
}

조건부 요청으로 데이터 업데이트

트랜잭션에 해당하는 REST 기능인 조건부 요청을 사용하면 기존 상태에 따라 데이터를 업데이트할 수 있습니다. 예를 들어 투표 카운터를 늘리면서 동시에 이루어지는 여러 투표를 정확히 반영하려면 조건부 요청을 사용하여 카운터에 새 값을 기록합니다. 2개의 쓰기 요청이 동시에 발생하면 카운터가 동일한 숫자로 바뀌는 대신 쓰기 요청 중 하나가 실패하여 새로운 값으로 요청을 재시도할 수 있습니다.
  1. 특정 위치에서 조건부 요청을 수행하려면 해당 위치의 현재 데이터에 대한 고유 식별자, 즉 ETag를 가져옵니다. 해당 위치에서 데이터가 변경되면 ETag도 변경됩니다. PATCH 이외의 메서드로 ETag를 요청할 수 있습니다. 다음 예시에서는 GET 요청을 사용합니다.
    curl -i 'https://test.example.com/posts/12345/upvotes.json' -H 'X-Firebase-ETag: true'
    
    헤더에서 명시적으로 ETag를 호출하면 HTTP 응답에서 지정된 위치의 ETag가 반환됩니다.
    HTTP/1.1 200 OK
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    ETag: [ETAG_VALUE]
    Cache-Control: no-cache
    
    10 // Current value of the data at the specified location
    
  2. 반환된 ETag를 다음번 PUT 또는 DELETE 요청에 포함하여 해당 ETag 값과 일치하는 데이터를 업데이트합니다. 예시에 따라 카운터를 최초에 가져온 값인 10보다 1만큼 큰 11로 업데이트하고, 값이 더 이상 일치하지 않는 경우 요청을 실패로 처리하려면 다음 코드를 사용합니다.
    curl -iX PUT -d '11' 'https://[PROJECT_ID].firebaseio.com/posts/12345/upvotes.json' -H 'if-match:[ETAG_VALUE]'
    
    지정된 위치의 데이터 값이 여전히 10이고 PUT 요청의 ETag가 일치하면 요청이 성공하여 데이터베이스에 11이 기록됩니다.
    HTTP/1.1 200 OK
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    Cache-Control: no-cache
    
    11 // New value of the data at the specified location, written by the conditional request
    
    예를 들어 다른 사용자가 데이터베이스에 새 값을 기록한 경우와 같이 위치가 ETag와 더 이상 일치하지 않으면 요청이 실패하고 해당 위치에 값이 기록되지 않습니다. 반환 응답에는 새 값과 ETag가 포함됩니다.
    HTTP/1.1 412 Precondition Failed
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    ETag: [ETAG_VALUE]
    Cache-Control: no-cache
    
    12 // New value of the data at the specified location
    
  3. 요청을 재시도하려면 새 정보를 사용하세요. Realtime Database는 실패한 조건부 요청을 자동으로 재시도하지 않습니다. 그러나 실패 응답에서 반환한 새 값과 ETag를 사용하여 새로운 조건부 요청을 작성할 수 있습니다.

REST 기반 조건부 요청은 HTTP if-match 표준을 구현합니다. 그러나 표준과 다음과 같은 차이점이 있습니다.

  • 각 if-match 요청에 대해 여러 개가 아닌 하나의 ETag 값만 제공할 수 있습니다.
  • 표준에서는 모든 요청에서 ETag를 반환하도록 권장하지만, 실시간 데이터베이스는 X-Firebase-ETag 헤더를 포함하는 요청에서만 ETag를 반환합니다. 따라서 표준 요청에 대한 청구 비용이 감소합니다.

또한 조건부 요청은 일반적인 REST 요청보다 느릴 수 있습니다.

데이터 목록 저장

Firebase 데이터베이스 참조에 추가된 모든 하위 항목에 대해 고유한 타임스탬프 기반 키를 생성하려면 POST 요청을 전송합니다. users 경로에서는 사용자마다 고유한 사용자 이름이 있으므로 키를 직접 정의하는 것이 좋습니다. 그러나 사용자가 앱에 블로그 게시물을 추가하는 경우에는 POST 요청을 사용하여 각 블로그 게시물의 키를 자동으로 생성합니다.

curl -X POST -d '{
  "author": "alanisawesome",
  "title": "The Turing Machine"
}' 'https://docs-examples.firebaseio.com/fireblog/posts.json'

이제 posts 경로의 데이터는 다음과 같습니다.

{
  "posts": {
    "-JSOpn9ZC54A4P4RoqVa": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

POST 요청을 사용했으므로 -JSOpn9ZC54A4P4RoqVa 키가 자동으로 생성되었습니다. 성공한 요청은 200 OK HTTP 상태 코드로 표시되고, 응답은 새로 추가된 데이터의 키를 포함합니다.

{"name":"-JSOpn9ZC54A4P4RoqVa"}

데이터 삭제

데이터베이스에서 데이터를 삭제하려면 데이터를 삭제할 경로의 URL과 함께 DELETE 요청을 전송합니다. 다음은 users 경로에서 Alan을 삭제합니다.

curl -X DELETE \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome.json'

성공한 DELETE 요청은 JSON null을 포함한 응답과 함께 200 OK HTTP 상태 코드로 표시됩니다.

URI 매개변수

데이터베이스에 데이터를 쓸 때 REST API는 다음과 같은 URI 매개변수를 사용합니다.

auth

auth 요청 파라미터는 Firebase Realtime Database Security Rules으로 보호되는 데이터에 대한 액세스를 허용하며 모든 요청 유형에서 지원됩니다. 인수는 사용자 승인 섹션에서 설명하는 Firebase 앱 보안 비밀 또는 인증 토큰입니다. 다음 예시에서는 auth 파라미터와 함께 POST 요청을 전송합니다. 여기서 CREDENTIAL은 Firebase 앱 보안 비밀 또는 인증 토큰입니다.

curl -X POST -d '{"Authenticated POST request"}' \
  'https://docs-examples.firebaseio.com/auth-example.json?auth=CREDENTIAL'

인쇄

print 매개변수를 사용하면 데이터베이스의 응답 형식을 지정할 수 있습니다. 요청에 print=pretty를 추가하면 사람이 읽을 수 있는 형식으로 데이터가 반환됩니다. print=prettyGET, PUT, POST, PATCH, DELETE 요청에서 지원됩니다.

데이터를 쓸 때 서버의 출력을 표시하지 않으려면 요청에 print=silent를 추가합니다. 요청이 성공하면 결과 응답이 비어 있고 204 No Content HTTP 상태 코드로 표시됩니다. print=silentGET, PUT, POST, PATCH 요청에서 지원됩니다.

서버 값 쓰기

서버 값은 단일 ".sv" 키가 있는 객체인 자리표시자 값을 사용하여 위치에 기록될 수 있습니다. 이 키의 값은 설정할 서버 값의 유형입니다. 예를 들어 사용자가 생성되는 시점의 타임스탬프를 설정하는 방법은 다음과 같습니다.

curl -X PUT -d '{".sv": "timestamp"}' \
  'https://docs-examples.firebaseio.com/alanisawesome/createdAt.json'

"timestamp"는 유일하게 지원되는 서버 값이며, UNIX 에포크 이후의 시간(밀리초)입니다.

쓰기 성능 향상

데이터베이스에 대량의 데이터를 쓰는 경우 print=silent 매개변수를 사용하여 쓰기 성능을 개선하고 대역폭 사용량을 줄일 수 있습니다. 일반적인 쓰기 동작에서는, 서버가 기록된 JSON 데이터를 사용하여 응답합니다. print=silent를 지정하면 데이터가 수신된 후 서버가 즉시 연결을 종료하므로 대역폭 사용량이 감소합니다.

데이터베이스에 여러 요청을 전송하는 경우 HTTP 헤더에 Keep-Alive 요청을 전송하여 HTTPS 연결을 재사용할 수 있습니다.

오류 조건

REST API는 다음과 같은 상황에서 오류 코드를 반환합니다.

HTTP 상태 코드
400 잘못된 요청

다음의 오류 조건 중 하나에 해당됩니다.

  • PUT 또는 POST 데이터를 파싱할 수 없습니다.
  • PUT 또는 POST 데이터가 누락되었습니다.
  • 요청에서 PUT 또는 POST를 실행하려는 데이터가 너무 큽니다.
  • REST API 호출의 경로에 잘못된 하위 이름이 포함되었습니다.
  • REST API 호출 경로가 너무 깁니다.
  • 요청에 인식할 수 없는 서버 값이 포함되어 있습니다.
  • 쿼리에 대한 색인이 Firebase Realtime Database Security Rules에 정의되어 있지 않습니다.
  • 지정된 쿼리 매개변수 중 하나를 요청에서 지원하지 않습니다.
  • 요청에 쿼리 매개변수와 얕은 GET 요청이 혼용되어 있습니다.
401 승인되지 않음

다음 오류 조건 중 하나에 해당됩니다.

  • 인증 토큰이 만료되었습니다.
  • 요청에 사용된 인증 토큰이 잘못되었습니다.
  • access_token으로 인증하지 못했습니다.
  • 요청이 Firebase Realtime Database Security Rules을 위반합니다.
404 찾을 수 없음 지정한 Firebase 데이터베이스를 찾을 수 없습니다.
500 내부 서버 오류 서버에서 오류를 반환했습니다. 자세한 내용은 오류 메시지를 참조하세요.
503 서비스를 사용할 수 없음 지정한 Firebase 실시간 데이터베이스를 일시적으로 사용할 수 없어 요청을 시도하지 않았습니다.

데이터 보안

Firebase는 데이터의 서로 다른 노드에 사용자의 읽기 및 쓰기 권한을 정의하는 보안 언어를 제공합니다. 자세한 내용은 Realtime Database Security Rules을 참조하세요.

데이터 저장에 대해 알아보았으므로 다음 섹션에서는 REST API를 통해 Firebase 데이터베이스에서 데이터를 검색하는 방법을 살펴보겠습니다.