Firebase Data Connect를 사용하면 Google Cloud SQL로 관리되는 PostgreSQL 인스턴스의 커넥터를 만들 수 있습니다. 이러한 커넥터는 스키마의 데이터를 사용하기 위한 쿼리와 변이의 조합입니다.
시작 가이드에서는 PostgreSQL용 영화 리뷰 앱 스키마를 소개했습니다.
이 가이드에서는 쿼리를 비롯한 배포 가능 및 임시 관리 작업도 소개했습니다.
- 배포 가능한 쿼리는 정의한 API 엔드포인트로 클라이언트 앱에서 호출하기 위해 구현하는 쿼리입니다. 서버에 배포된 커넥터에 번들로 묶습니다. Data Connect 도구는 API를 기반으로 클라이언트 SDK를 생성합니다. 배포된 쿼리는 IAM 정책으로 보호되지 않으므로 Data Connect
@auth
지시어를 사용하여 보호해야 합니다. - 임시 관리 쿼리는 권한이 있는 환경에서 실행되어 데이터를 읽습니다. Firebase 콘솔에서 또는 Data Connect VS Code 확장 프로그램을 사용하여 로컬 개발 환경에서 만들고 실행할 수 있습니다.
이 가이드에서는 배포 가능한 쿼리를 자세히 살펴봅니다.
Data Connect 질문의 특징
Data Connect를 사용하면 PostgreSQL 데이터베이스가 제공될 때 예상되는 모든 방식으로 기본 쿼리를 실행할 수 있습니다.
하지만 Data Connect의 GraphQL 확장 프로그램을 사용하면 더 빠르고 효율적인 앱을 위한 고급 쿼리를 구현할 수 있습니다.
- 많은 작업에서 반환된 키 스칼라를 사용하여 레코드에 대한 반복 작업을 단순화합니다.
- 다단계 변이 작업 중에 쿼리를 실행하여 데이터를 조회하면 코드 줄과 서버로의 왕복을 절약할 수 있습니다.
생성된 필드를 사용하여 쿼리 빌드
Data Connect 작업은 스키마의 유형 및 유형 관계에 따라 자동으로 생성된 Data Connect 필드 집합을 확장합니다. 이러한 필드는 스키마를 수정할 때마다 로컬 도구에 의해 생성됩니다.
생성된 필드를 사용하여 단일 테이블에서 개별 레코드 또는 여러 레코드를 가져오는 것부터 관련 테이블에서 여러 레코드를 가져오는 것까지 점점 더 복잡한 쿼리를 구현할 수 있습니다.스키마에 Movie
유형과 연결된 Actor
유형이 포함되어 있다고 가정합니다.
Data Connect는 movie
, movies
, actors_on_movies
필드 등을 생성합니다.
movie
필드를 사용한 쿼리
|
이 필드를 사용하여 키로 단일 영화를 쿼리합니다. query GetMovie($myKey: Movie_Key!) { movie(key: $myKey) { title } } |
movies
필드를 사용한 쿼리
|
이 필드를 사용하여 여러 영화(예: 특정 연도의 모든 영화)를 쿼리합니다. query GetMovies($myYear: Int!) { movies(where: { year: { eq: $myYear } }) { title } } |
actors_on_movies
필드를 사용한 쿼리
|
이 필드를 사용하여 특정 영화와 관련된 모든 배우를 쿼리합니다. query GetActorsOnMovie($myKey: Movie_Key!) { actors_on_movies(where: { movie: { key: { eq: $myKey } } }) { actor { name } } } |
쿼리의 필수 요소
Data Connect 쿼리는 Data Connect 확장 프로그램이 있는 GraphQL 쿼리입니다. 일반 GraphQL 쿼리와 마찬가지로 작업 이름과 GraphQL 변수 목록을 정의할 수 있습니다.
Data Connect는 @auth
와 같은 맞춤 지시어로 GraphQL 쿼리를 확장합니다.
따라서 다음 쿼리에는 다음이 포함됩니다.
query
유형 정의ListMoviesByGenre
작업 (쿼리) 이름- 단일 쿼리 인수(여기서는
String
유형의$genre
변수) - 단일 지시문
@auth
- 단일 필드
movies
query ListMoviesByGenre($genre: String!) @auth(level: PUBLIC) {
movies(where: { genre: { eq: $genre } }) {
id
title
}
}
모든 쿼리 인수에는 유형 선언(예: String
와 같은 내장 유형) 또는 맞춤 스키마 정의 유형(예: Movie
)이 필요합니다.
이 가이드에서는 점점 더 복잡해지는 쿼리의 서명을 살펴봅니다. 배포 가능한 쿼리를 빌드하는 데 사용할 수 있는 강력하고 간결한 관계 표현식을 소개하며 마무리합니다.
쿼리의 주요 스칼라
하지만 먼저 주요 스칼라에 대해 알아보겠습니다.
Data Connect는 {TableType}_Key로 식별되는 각 테이블의 기본 키를 나타내는 특수 키 스칼라를 정의합니다. 기본 키 값의 JSON 객체입니다.
키 스칼라는 대부분의 자동 생성 읽기 필드에서 반환된 응답으로 가져오거나 스칼라 키를 빌드하는 데 필요한 모든 필드를 가져온 쿼리에서 가져옵니다.
실행 중인 예의 movie
와 같은 단수 자동 쿼리는 키 스칼라를 허용하는 키 인수를 지원합니다.
키 스칼라를 리터럴로 전달할 수 있습니다. 하지만 키 스칼라를 입력으로 전달하는 변수를 정의할 수 있습니다.
쿼리
query GetMovie($myKey: Movie_Key!) { movie(key: $myKey) { title } }
응답
{ "data": { "movie": { "title": "Example Movie Title" } } }
다음과 같은 요청 JSON (또는 기타 직렬화 형식)으로 제공할 수 있습니다.
{
# …
"variables": {
"myKey": {"id": "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"}
}
}
맞춤 스칼라 파싱 덕분에 변수를 포함할 수 있는 객체 구문을 사용하여 Movie_Key
를 구성할 수도 있습니다. 이는 어떤 이유로 개별 구성요소를 다른 변수로 나누려는 경우에 주로 유용합니다.
기본 쿼리 작성
쿼리를 작성하여 데이터베이스에서 개별 레코드를 가져오거나 결과를 제한하고 정렬하는 옵션을 사용하여 테이블에서 레코드를 나열할 수 있습니다.
개별 레코드 가져오기
가장 간단한 쿼리는 ID로 단일 레코드를 가져옵니다. 쿼리에서 자동 생성된 movie
필드를 사용합니다.
쿼리
query GetMovieById($id: UUID!) @auth(level: PUBLIC) { movie(id: $id) { id title imageUrl genre } }
응답
{ "data": { "movie": { "id": "some-uuid", "title": "Example Movie Title", "imageUrl": "https://example.com/movie.jpg", "genre": "Action" } } }
표의 모든 레코드 가져오기
Movies
테이블에서 전체 영화 목록의 필드 하위 집합을 가져오려면 쿼리에서 자동 생성된 movies
필드를 사용하며 구현은 다음과 같을 수 있습니다.
쿼리
query ListMovies @auth(level: PUBLIC) { movies { id title imageUrl genre } }
응답
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title", "imageUrl": "https://example.com/movie.jpg", "genre": "Action" }, { "id": "another-uuid", "title": "Another Movie Title", "imageUrl": "https://example.com/another-movie.jpg", "genre": "Comedy" } ] } }
orderBy
, limit
, offset
연산자 사용
당연히 테이블의 모든 레코드를 나열하는 것은 유용성이 제한적입니다.
결과를 정렬하고 페이지로 나눌 수 있습니다. 이러한 인수는 허용되지만 결과로 반환되지는 않습니다.
여기에서 쿼리는 평점별 상위 10개 영화의 제목을 가져옵니다.
쿼리
query MoviesTop10 { movies(orderBy: [{ rating: DESC }], limit: 10) { # graphql: list the fields from the results to return title } }
응답
{ "data": { "movies": [ { "title": "Top Movie 1" }, { "title": "Top Movie 2" }, { "title": "Top Movie 3" } // ... other 7 movies ] } }
등급별로 정렬된 영화 11~20과 같이 오프셋에서 행을 가져오는 사용 사례가 있을 수 있습니다.
쿼리
query Movies11to20 { movies(orderBy: [{ rating: DESC }], limit: 10, offset: 10) { # graphql: list the fields from the results to return title } }
응답
{ "data": { "movies": [ { "title": "Movie 11" }, { "title": "Movie 12" }, { "title": "Movie 13" } // ... other 7 movies ] } }
쿼리에서 별칭 사용
Data Connect는 쿼리에서 GraphQL 별칭을 지원합니다. 별칭을 사용하면 쿼리 결과에서 반환되는 데이터의 이름을 바꿀 수 있습니다. 단일 Data Connect 쿼리는 서버에 대한 하나의 효율적인 요청에서 여러 필터 또는 기타 쿼리 작업을 적용하여 여러 '하위 쿼리'를 한 번에 효과적으로 실행할 수 있습니다. 반환된 데이터 세트에서 이름 충돌을 방지하려면 별칭을 사용하여 하위 쿼리를 구분합니다.
다음은 표현식에서 mostPopular
및 leastPopular
별칭을 사용하는 쿼리입니다.
쿼리
query ReviewPopularitySpread($genre: String) { mostPopular: review( first: { where: {genre: {eq: $genre}}, orderBy: {popularity: DESC} } ), leastPopular: review( last: { where: {genre: {eq: $genre}}, orderBy: {popularity: DESC} } ) }
응답
{ "data": { "mostPopular": [ { "popularity": 9 } ], "leastPopular": [ { "popularity": 1 } ] } }
쿼리 필터 사용
Data Connect 쿼리는 모든 일반적인 SQL 필터 및 순서 작업에 매핑됩니다.
orderBy
연산자를 사용하여 where
로 필터링
표 (및 중첩된 연결)에서 일치하는 모든 행을 반환합니다. 필터와 일치하는 레코드가 없으면 빈 배열을 반환합니다.
쿼리
query MovieByTopRating($genre: String) { mostPopular: movies( where: { genre: { eq: $genre } }, orderBy: { rating: DESC } ) { # graphql: list the fields from the results to return id title genre description } }
응답
{ "data": { "mostPopular": [ { "id": "some-uuid", "title": "Example Movie Title", "genre": "Action", "description": "A great movie" } ] } }
null 값 테스트를 통해 필터링
isNull
연산자를 사용하여 null
값을 테스트할 수 있습니다.
쿼리
query ListMoviesWithoutDescription { movies(where: { description: { isNull: true }}) { id title } }
응답
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title" }, { "id": "another-uuid", "title": "Another Movie Title" } ] } }
더 많은 연산자는 입력 객체 유형 참조 가이드를 참고하세요.
값 비교로 필터링
lt
(미만) 및 ge
(이상)과 같은 연산자를 사용하여 쿼리에서 값을 비교할 수 있습니다.
쿼리
query ListMoviesByRating($minRating: Int!, $maxRating: Int!) { movies(where: { rating: { ge: $minRating, lt: $maxRating }}) { id title } }
응답
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title" }, { "id": "another-uuid", "title": "Another Movie Title" } ] } }
배열 필드에 includes
및 excludes
연산자를 사용하여 필터링
배열 필드에 지정된 항목이 포함되어 있는지 테스트할 수 있습니다.
다음 예는 includes
연산자를 보여줍니다.
Data Connect는 includesAll
, excludes
, excludesAll
등을 지원합니다. 참조 문서의 _ListFilter
제목에서 정수, 문자열, 날짜 및 기타 데이터 유형의 이러한 연산자를 모두 검토하세요.
쿼리
query ListMoviesByTag($tag: String!) { movies(where: { tags: { includes: $tag }}) { # graphql: list the fields from the results to return id title } }
응답
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title" } ] } }
문자열 작업 및 정규 표현식으로 필터링
쿼리에서 정규 표현식을 비롯한 일반적인 문자열 검색 및 비교 작업을 사용할 수 있습니다. 효율성을 위해 여기에서 여러 작업을 번들로 묶고 별칭으로 명확하게 구분합니다.
query MoviesTitleSearch($prefix: String, $suffix: String, $contained: String, $regex: String) {
prefixed: movies(where: {title: {startsWith: $prefix}}) {...}
suffixed: movies(where: {title: {endsWith: $suffix}}) {...}
contained: movies(where: {title: {contains: $contained}}) {...}
}
_or
, _and
, _not
연산자 로직으로 필터링
더 복잡한 로직에는 _or
를 사용합니다. Data Connect는 _and
및 _not
연산자도 지원합니다.
쿼리
query ListMoviesByGenreAndGenre($minRating: Int!, $genre: String) { movies( where: { _or: [{ rating: { ge: $minRating } }, { genre: { eq: $genre } }] } ) { # graphql: list the fields from the results to return title } }
응답
{ "data": { "movies": [ { "title": "Movie Title 1" }, { "title": "Movie Title 2" } ] } }
관계형 쿼리 작성
Data Connect 쿼리는 테이블 간의 관계를 기반으로 데이터에 액세스할 수 있습니다. 스키마에 정의된 객체 (일대일) 또는 배열 (일대다) 관계를 사용하여 중첩된 쿼리를 만들 수 있습니다. 즉, 중첩되거나 관련 유형의 데이터와 함께 한 유형의 데이터를 가져올 수 있습니다.
이러한 쿼리는 생성된 읽기 필드에서 매직 Data Connect, _on_
, _via
구문을 사용합니다.
샘플 스키마를 검토하세요.
다대일
이제 _on_
문법을 보여주는 쿼리를 살펴보겠습니다.
쿼리
query MyReviews @auth(level: USER) { user(key: {id_expr: "auth.uid"}) { reviews: reviews_on_user { movie { name } rating } } }
응답
{ "data": { "user": { "reviews": [ { "movie": { "name": "Movie Title" }, "rating": 5 } ] } } }
일대일
_on_
구문을 사용하여 일대일 쿼리를 작성할 수 있습니다.
쿼리
query GetMovieMetadata($id: UUID!) @auth(level: PUBLIC) { movie(id: $id) { movieMetadatas_on_movie { director } } }
응답
{ "data": { "movie": { "movieMetadatas_on_movie": { "director": "Some Director" } } } }
다대다
다대다 쿼리는 _via_
구문을 사용합니다. 다대다 쿼리는 지정된 영화의 배우를 가져올 수 있습니다.
쿼리
query MoviesActors($id: UUID!) @auth(level: USER) { movie(id: $id) { actors: actors_via_MovieActors { name } } }
응답
{ "data": { "movie": { "actors": [ { "name": "Actor Name" } ] } } }
하지만 별칭을 사용하여 더 복잡한 쿼리를 작성하여 role
를 기준으로 필터링하면 mainActors
및 supportingActors
결과에서 배우와 관련 영화를 가져올 수 있습니다. 다대다이므로 _via_
구문이 사용됩니다.
쿼리
query GetMovieCast($movieId: UUID!, $actorId: UUID!) @auth(level: PUBLIC) { movie(id: $movieId) { mainActors: actors_via_MovieActor(where: { role: { eq: "main" } }) { name } supportingActors: actors_via_MovieActor( where: { role: { eq: "supporting" } } ) { name } } actor(id: $actorId) { mainRoles: movies_via_MovieActor(where: { role: { eq: "main" } }) { title } supportingRoles: movies_via_MovieActor( where: { role: { eq: "supporting" } } ) { title } } }
응답
{ "data": { "movie": { "mainActors": [ { "name": "Main Actor Name" } ], "supportingActors": [ { "name": "Supporting Actor Name" } ] }, "actor": { "mainRoles": [ { "title": "Main Role Movie Title" } ], "supportingRoles": [ { "title": "Supporting Role Movie Title" } ] } } }
집계 쿼리
집계란 무엇이며 왜 사용해야 하나요?
집계 필드를 사용하면 결과 목록에 대한 계산을 실행할 수 있습니다. 집계 필드를 사용하면 다음과 같은 작업을 할 수 있습니다.
- 리뷰의 평균 점수 확인하기
- 장바구니에 있는 상품의 총비용 확인
- 평점이 가장 높거나 낮은 제품 찾기
- 스토어의 제품 수 확인
집계는 서버에서 실행되므로 클라이언트 측에서 집계를 계산하는 것보다 다음과 같은 여러 이점이 있습니다.
- 클라이언트 측 계산을 피하므로 앱 성능이 더 빨라집니다.
- 데이터 이그레스 비용 절감 (모든 입력을 보내는 대신 집계된 결과만 보내므로)
- 보안 개선 (전체 데이터 세트 대신 집계된 데이터에 대한 액세스 권한을 클라이언트에 부여할 수 있으므로)
집계 스키마 예시
이 섹션에서는 집계를 사용하는 방법을 설명하는 데 적합한 스토어 예시 스키마로 전환합니다.
type Product @table {
name: String!
manufacturer: String!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
단순 집계
모든 필드의 _count
가장 간단한 집계 필드는 _count
입니다. 이 필드는 쿼리와 일치하는 행 수를 반환합니다. Data Connect는 유형의 각 필드에 대해 필드 유형에 따라 해당하는 집계 필드를 생성합니다.
쿼리
query CountProducts {
products {
_count
}
}
응답 one
one
예를 들어 데이터베이스에 제품이 5개 있는 경우 결과는 다음과 같습니다.
{
"products": [
{
"_count": 5
}
]
}
모든 필드에는 해당 필드에 null이 아닌 값이 있는 행 수를 계산하는 <field>_count
필드가 있습니다.
쿼리
query CountProductsWithExpirationDate {
products {
expirationDate_count
}
}
응답field_count
field_count
예를 들어 만료일이 있는 제품이 3개인 경우 결과는 다음과 같습니다.
{
"products": [
{
"expirationDate_count": 3
}
]
}
숫자 필드의 경우 _min, _max, _sum, _avg
숫자 필드 (int, float, int64)에도 <field>_min
, <field>_max
, <field>_sum
, <field>_avg
이 있습니다.
쿼리
query NumericAggregates {
products {
quantityInStock_max
price_min
price_avg
quantityInStock_sum
}
}
응답_min _max _sum _avg
_min _max _sum _avg
예를 들어 다음과 같은 제품이 있다고 가정해 보겠습니다.
- 제품 A:
quantityInStock: 10
,price: 2.99
- 제품 B:
quantityInStock: 5
,price: 5.99
- 제품 C:
quantityInStock: 20
,price: 1.99
결과는 다음과 같습니다.
{
"products": [
{
"quantityInStock_max": 20,
"price_min": 1.99,
"price_avg": 3.6566666666666666,
"quantityInStock_sum": 35
}
]
}
날짜 및 타임스탬프의 경우 _min 및 _max
날짜 및 타임스탬프 필드에는 <field>_min
및 <field>_max
이 있습니다.
쿼리
query DateAndTimeAggregates {
products {
expirationDate_max
expirationDate_min
}
}
응답_min _maxdatetime
_min _maxdatetime
예를 들어 다음과 같은 만료일이 있다고 가정해 보겠습니다.
- 제품 A:
2024-01-01
- 제품 B:
2024-03-01
- 제품 C:
2024-02-01
결과는 다음과 같습니다.
{
"products": [
{
"expirationDate_max": "2024-03-01",
"expirationDate_min": "2024-01-01"
}
]
}
Distinct
distinct
인수를 사용하면 필드(또는 필드 조합)의 모든 고유 값을 가져올 수 있습니다. 예를 들면 다음과 같습니다.
쿼리
query ListDistinctManufacturers {
products(distinct: true) {
manufacturer
}
}
응답distinct
distinct
예를 들어 다음과 같은 제조업체가 있다고 가정해 보겠습니다.
- 제품 A:
manufacturer: "Acme"
- 제품 B:
manufacturer: "Beta"
- 제품 C:
manufacturer: "Acme"
결과는 다음과 같습니다.
{
"products": [
{ "manufacturer": "Acme" },
{ "manufacturer": "Beta" }
]
}
집계 필드에서 distinct
인수를 사용하여 고유한 값을 집계할 수도 있습니다. 예를 들면 다음과 같습니다.
쿼리
query CountDistinctManufacturers {
products {
manufacturer_count(distinct: true)
}
}
응답distinctonaggregate
distinctonaggregate
예를 들어 다음과 같은 제조업체가 있다고 가정해 보겠습니다.
- 제품 A:
manufacturer: "Acme"
- 제품 B:
manufacturer: "Beta"
- 제품 C:
manufacturer: "Acme"
결과는 다음과 같습니다.
{
"products": [
{
"manufacturer_count": 2
}
]
}
그룹화된 집계
유형에서 집계 필드와 비집계 필드를 혼합하여 선택하면 그룹화된 집계를 실행할 수 있습니다. 이렇게 하면 집계되지 않은 필드의 값이 동일한 일치하는 행이 모두 그룹화되고 해당 그룹의 집계 필드가 계산됩니다. 예를 들면 다음과 같습니다.
쿼리
query MostExpensiveProductByManufacturer {
products {
manufacturer
price_max
}
}
응답groupedaggregates
groupedaggregates
예를 들어 다음과 같은 제품이 있다고 가정해 보겠습니다.
- 제품 A:
manufacturer: "Acme"
,price: 2.99
- 제품 B:
manufacturer: "Beta"
,price: 5.99
- 제품 C:
manufacturer: "Acme"
,price: 1.99
결과는 다음과 같습니다.
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manufacturer": "Beta", "price_max": 5.99 }
]
}
그룹화된 집계가 있는 having
및 where
having
및 where
인수를 사용하여 제공된 기준을 충족하는 그룹만 반환할 수도 있습니다.
having
를 사용하면 집계 필드를 기준으로 그룹을 필터링할 수 있습니다.where
를 사용하면 집계되지 않은 필드를 기준으로 행을 필터링할 수 있습니다.
쿼리
query FilteredMostExpensiveProductByManufacturer {
products(having: {price_max: {ge: 2.99}}) {
manufacturer
price_max
}
}
응답havingwhere
havingwhere
예를 들어 다음과 같은 제품이 있다고 가정해 보겠습니다.
- 제품 A:
manufacturer: "Acme"
,price: 2.99
- 제품 B:
manufacturer: "Beta"
,price: 5.99
- 제품 C:
manufacturer: "Acme"
,price: 1.99
결과는 다음과 같습니다.
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manufacturer": "Beta", "price_max": 5.99 }
]
}
테이블 간 집계
집계 필드를 생성된 일대다 관계 필드와 함께 사용하여 데이터에 관한 복잡한 질문에 답할 수 있습니다. 다음은 예시에서 사용할 수 있는 별도의 테이블 Manufacturer
가 있는 수정된 스키마입니다.
type Product @table {
name: String!
manufacturer: Manufacturer!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
type Manufacturer @table {
name: String!
headquartersCountry: String!
}
이제 집계 필드를 사용하여 제조업체에서 만드는 제품 수를 찾는 등의 작업을 할 수 있습니다.
쿼리
query GetProductCount($id: UUID) {
manufacturers {
name
products_on_manufacturer {
_count
}
}
}
응답 aggregatesacrosstables
aggregatesacrosstables
예를 들어 다음과 같은 제조업체가 있다고 가정해 보겠습니다.
- 제조업체 A:
name: "Acme"
,products_on_manufacturer: 2
- 제조업체 B:
name: "Beta"
,products_on_manufacturer: 1
결과는 다음과 같습니다.
{
"manufacturers": [
{ "name": "Acme", "products_on_manufacturer": { "_count": 2 } },
{ "name": "Beta", "products_on_manufacturer": { "_count": 1 } }
]
}
고급 쿼리 작성: query
필드를 사용하여 다단계 작업에서 데이터 읽기
삽입이나 업데이트를 실행하기 전에 기존 데이터를 조회하고 확인하기 위해 변이 실행 중에 데이터베이스를 읽어야 하는 상황이 많습니다. 이러한 옵션은 왕복 작업을 절약하므로 비용이 절감됩니다.
Data Connect는 이 기능을 지원합니다. 다단계 작업을 참고하세요.
다음 단계
관심 있을 만한 항목: