데이터 저장

데이터 저장 방법

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

PUT으로 데이터 쓰기

REST API를 통한 기본 쓰기 작업은 PUT입니다. 데이터 저장을 시연하기 위해 글과 사용자로 구성된 블로깅 애플리케이션을 만들어 보겠습니다. 모든 애플리케이션 데이터는 Firebase 데이터베이스 URL https://docs-examples.firebaseio.com/rest/saving-data/fireblog에 저장됩니다.

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

PUT을 사용하여 문자열, 숫자, 부울, 배열 또는 임의의 JSON 개체를 Firebase 데이터베이스에 저장할 수 있습니다. 여기에서는 개체를 전달합니다.

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

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

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

위의 두 예제, 즉 개체 하나로 값을 한 번에 쓰는 예제와 하위 위치에 별도로 값을 쓰는 예제가 Firebase 데이터베이스에 저장하는 데이터는 결과적으로 동일합니다.

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

200 OK HTTP 상태 코드는 요청 성공을 나타내고, 응답에는 데이터베이스에 기록한 데이터가 포함됩니다. 첫 번째 예제에서는 데이터를 감시하는 클라이언트에서 이벤트가 한 번 발생하는 반면 두 번째 예제에서는 이벤트가 두 번 발생합니다. 주의할 점은 users 경로에 이미 데이터가 있는 경우 첫 번째 방식에서는 데이터를 덮어쓰지만 두 번째 방식에서는 하위 노드 각각의 값만 수정되며 다른 하위 노드는 바뀌지 않는다는 점입니다. PUT은 자바스크립트 SDK의 set()에 해당합니다.

PATCH로 데이터 업데이트

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

curl -X PATCH -d '{
  "nickname": "Alan The Machine"
}' \
  'https://docs-examples.firebaseio.com/rest/saving-data/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/rest/saving-data/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/rest/saving-data/users.json'

이렇게 하면 동작이 달라져서 전체 /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. 요청을 재시도하려면 새 정보를 사용합니다. 실시간 데이터베이스는 실패한 조건부 요청을 자동으로 재시도하지 않습니다. 그러나 실패 응답에서 반환한 새 값과 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/rest/saving-data/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/rest/saving-data/users/alanisawesome.json'

200 OK HTTP 상태 코드는 DELETE 요청 성공을 나타내고, 응답에는 JSON null이 포함됩니다.

URI 매개변수

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

auth

auth 요청 매개변수는 모든 요청 유형에서 지원되며 Firebase 실시간 데이터베이스 규칙으로 보호되는 데이터로의 액세스를 허용합니다. 인수는 사용자 인증 섹션에서 설명하는 Firebase 앱 비밀번호나 인증 토큰입니다. 다음 예제에서는 auth 매개변수와 함께 POST 요청을 전송하며, 여기에서 CREDENTIAL은 Firebase 앱 비밀번호 또는 인증 토큰입니다.

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

print

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

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

서버 값 쓰기

자리표시자 값, 즉 ".sv" 키 하나가 포함된 개체를 사용하여 특정 위치에 서버 값을 쓸 수 있습니다. 이 키의 값은 설정할 서버 값의 유형입니다. 예를 들어 사용자 생성 시점의 타임스탬프를 설정하는 방법은 다음과 같습니다.

curl -X PUT -d '{".sv": "timestamp"}' \
  'https://docs-examples.firebaseio.com/rest/saving-data/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 실시간 데이터베이스 규칙에 정의되어 있지 않습니다.
  • 요청에서 지정된 쿼리 매개변수 중 하나를 지원하지 않습니다.
  • 요청에 쿼리 매개변수와 얕은 GET 요청이 혼용되어 있습니다.
401 승인되지 않음

다음 오류 조건 중 하나입니다.

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

데이터 보안

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

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