본문 바로가기

Frontend Dev/Supabase

[supabase] supabase에서 관계 테이블 가져오기 (과정을 기록해보자!)

반응형

✏️ 이 글은 사이드프로젝트 "스무디"를 개발하며 알게된 점은 정리하여 기록한 글입니다. 

 

 

 

이 글은 supabase를 사용하여 관계 테이블을 가져오기까지의 과정을 정리하였습니다.

결론만 궁금하시다면, 글 가장 하단에 있습니다 😊

 

 

supabase에서 관계 테이블을 가져오기까지 과정

서버에서 데이터를 가져올 때 관계된 테이블에서 데이터를 함께 가져와야 하는 경우가 몇차례 있었다.

프로필을 예로 들자면, 아래와 같은 항목 중 참여한 프로젝트의 항목은 project 테이블에서 해당 유저의 프로젝트를 가지고 오는 방식으로 하고 싶었다. 관계를 형성해야 하는 것이었다.

"스무디"의 마이페이지 Profile 탭

 

projects 테이블과 profiles 테이블 구성은 아래와 같았다.

# projects 테이블 
create table
  public.projects (
    id bigint generated by default as identity,
    title text not null,
    description text not null,
    repo_url text not null,
    image_url text null,
    profile_id uuid not null,
    inside_project boolean not null,
    created_at timestamp with time zone not null default now(),
    modified_at timestamp with time zone not null default now(),
    constraint projects_pkey primary key (id),
    constraint public_projects_profile_id_fkey foreign key (profile_id) references profiles (id)
  ) tablespace pg_default;
  
 # profiles 테이블
 create table
  public.profiles (
    id uuid not null,
    email text not null,
    user_name text not null,
    avatar_url text null,
    created_at timestamp with time zone not null default now(),
    modified_at timestamp with time zone not null default now(),
    cover_letter text null,
    tech_tags text[] null,
    hard_skills text[] null,
    soft_skills text[] null,
    position text null,
    constraint profiles_pkey primary key (id),
    constraint profiles_id_fkey foreign key (id) references auth.users (id) on delete cascade
  ) tablespace pg_default;

 

 

1. 처음에는 해당 유저의 프로필과 프로젝트를 각각 조회하는 방식 사용

profiles 테이블에서 해당 유저를 가져오고, projects 테이블에서 해당 유저의 프로젝트를 가져오는 방식이었다.

const { data, error } = await supabase
    .from("profiles") // 프로필 가져오기
    .select()
    .eq("id", targetId);
  
const { data, error } = await supabase
    .from("projects") // 프로젝트 가져오기
    .select()
    .eq("id", targetId);

 

하지만 이는 유저의 프로필 정보를 조회할 때마다 2번의 요청을 보내야했다. 이를 좀 더 효율적으로 가져오는 방법이 있을 것 같아서 다른 방법을 찾아보기 시작했다.

 

 

2. 더 효율적인 방법을 위해 공식문서 탐색

테이블간의 관계를 활용해 관계가 있는 테이블을 함께 가져오는 방법이 있을 것 같아서 공식문서를 찾아보다 아래와 같은 부분을 발견했다.

Fetch data의 여러 옵션들 중 내가 원했던 참조 테이블에서 데이터를 가져오는 방법이었다.

🔗 출처 https://supabase.com/docs/reference/javascript/select

 

supabase - javascript fetch data

const { data, error } = await supabase
	.from('cities')
	.select('name, countries(*)') 
	.eq('countries.name', 'Estonia')
# Data source
create table
  countries (id int8 primary key, name text);
create table
  cities (
    id int8 primary key,
    country_id int8 not null references countries,
    name text
  );

insert into
  countries (id, name)
values
  (1, 'Germany'),
  (2, 'Indonesia');
insert into
  cities (id, country_id, name)
values
  (1, 2, 'Bali'),
  (2, 1, 'Munich');
// Response
{
  "data": [
    {
      "name": "Bali",
      "countries": null
    },
    {
      "name": "Munich",
      "countries": null
    }
  ],
  "status": 200,
  "statusText": "OK"
}

 

 

DB를 다룰 줄 모르고, SQL문도 잘 몰라서 처음에는 해석하는 것도 어려웠는데 내가 만든 테이블에 대입해 생각해보니 무슨 뜻인지 파악할 수 있었다.

const { data, error } = await supabase
  .from("projects") // 데이터를 검색할 테이블
  .select("profile_id, profiles(*)") // 데이터로 보여줄 부분
  // profile_id는 각 프로젝트와 연결된 프로필 id
  // profiles(*)는 profiles 테이블의 모든 열이 포함되어야 함
  // 쉽게 생각하면, data 객체 안에 profile_id와 profiles가 출력
  .eq("profiles.id", targetId); // 필터 적용
  // profiles 테이블의 id 열이 targetId와 동일한 행만 검색

 

위와 같이 작성하면 아래와 같이 데이터를 가져온다. profiles.id가 targetId와 같은 걸 찾아서 profile_id와 project의 모든 열을 가져온다.

 

내가 가져오고 싶었던 데이터의 형태는 유저 프로필과 해당 유저의 project 열 모두였기 때문에 위는 내가 생각했던 데이터의 형태가 아니었다.

 

 

3. [결론] 전체 선택자를 사용하여 2개의 테이블 모두 한번에 가져오기

내가 원했던건 데이터의 일부가 아니라 profiles 테이블의 모든 열과 projects 테이블의 모든 열이었다. 전체 선택자를 사용하면 내가 원하는 형태로 데이터를 가져올 수 있었다.

const { data, error } = await supabase
  .from("profiles")
  .select("*, projects(*)")
  .eq("id", targetId);

 

위와같이 projects 테이블의 전체 열이 profiles 테이블 안에 들어간 것을 확인할 수 있다.

이렇게 하면 한 번의 네트워크 요청으로 필요한 데이터를 모두 가져올 수 있으니 이전보다는 더 효율적으로 코드를 작성할 수 있을 것 같다.

 

 

💬

백엔드와 DB에 대해 거의 무지한 상태였기 때문에 테이블을 이런식으로 가져올 수 있을거라는 생각 자체를 처음에는 하지 못했다. 그런데 이전 팀 프로젝트에서 백엔드 팀원분들이 "테이블 간의 관계를 형성한다"라는 이야기를 몇차례 했던 걸 들은 적이 있었고, 아무것도 모르지만 테이블 관계도(?)도 본 적이 있었다. 그 때의 기억을 되살려보니 내가 원했던 걸 어떠한 형식으로 구현할 수 있을지 감을 잡았던 것 같고, supabase에서도 분명 이와 같은 기능을 제공할 것이라고 생각이 들었다. supabase를 사용하면서 프론트, 백을 선을 그어버리듯 나누어 학습하기보다는 좀 더 폭넓게 이해할 수 있도록 공부해야겠다는 생각이 들었다.
반응형