THE DEVLOG

scribbly.

Next JS 치트 시트

2025.05.16 12:41:43

View란?

정의:

  • PostgreSQL의 View는 하나 이상의 테이블을 조합한 가상의 읽기 전용 테이블이다.
  • 복잡한 SELECT 쿼리를 미리 정의해서 재사용할 수 있음.
  • Supabase에서는 이 View도 .from('view_name')으로 조회 가능.

예시

CREATE VIEW post_summaries AS
SELECT
  posts.id,
  posts.title,
  users.name AS author_name,
  COUNT(comments.*) AS comment_count
FROM posts
JOIN users ON posts.author_id = users.id
LEFT JOIN comments ON comments.post_id = posts.id
GROUP BY posts.id, users.name;

→ Supabase에서 사용:

const { data } = await supabase
  .from('post_summaries')
  .select('*');

View 특징 요약

항목설명
읽기 전용기본 View는 INSERT, UPDATE, DELETE 불가
쿼리 단순화복잡한 JOIN, GROUP BY 등을 미리 정의 가능
권한View에도 RLS 정책 적용 가능
Supabase 사용법.from('뷰_이름').select(...)

 

Supabase에서 View 등록 방법 (PostgreSQL View 생성)

A. SQL Editor에서 View 생성

  1. Supabase 프로젝트 접속
  2. 왼쪽 메뉴 → SQL Editor 클릭
  3. New Query → 다음과 같은 쿼리 입력
create or replace view public.post_summaries as
select
  posts.id,
  posts.title,
  users.name as author_name,
  count(comments.*) as comment_count
from posts
join users on posts.author_id = users.id
left join comments on comments.post_id = posts.id
group by posts.id, users.name;

  1. 상단의 Run 버튼 클릭 → 성공하면 뷰가 생성됨

B. 뷰 권한 부여 (읽기 허용)

grant select on public.post_summaries to anon, authenticated;

그래야 Supabase API로 접근할 수 있음

C. Supabase에서 사용

const { data, error } = await supabase
  .from('post_summaries')
  .select('*');

정리 요약

항목ViewRPC(Function)
생성CREATE VIEWCREATE FUNCTION
SQL EditorSupabase UI > SQL Editor동일
권한 부여GRANT SELECT ON viewGRANT EXECUTE ON FUNCTION ...
호출.from('view').rpc('function_name', { params })

참고: materialized view가 필요한 경우?

  • 조회 속도가 느리거나 정적인 요약 데이터를 빠르게 제공해야 한다면 materialized view를 고려
  • 하지만 Supabase에서 바로 refresh materialized view는 사용자가 직접 트리거하거나 job으로 설정해야 함 

RPC (Remote Procedure Call = SQL Function 호출)

정의:

  • **PostgreSQL의 저장 함수(CREATE FUNCTION)**를 Supabase가 HTTP API처럼 실행할 수 있게 래핑한 것
  • 복잡한 서버 로직(예: 트랜잭션, 커스텀 계산 등)을 캡슐화

예시

CREATE FUNCTION get_posts_by_tag(tag_input text)
RETURNS SETOF posts
LANGUAGE sql
AS $$
  SELECT p.*
  FROM posts p
  JOIN post_tags pt ON pt.post_id = p.id
  JOIN tags t ON t.id = pt.tag_id
  WHERE t.name = tag_input;
$$;

→ Supabase에서 사용:

const { data } = await supabase
  .rpc('get_posts_by_tag', { tag_input: 'nextjs' });

RPC 특징 요약

항목설명
완전 커스텀 쿼리SELECT뿐만 아니라 INSERT/UPDATE 등도 가능
매개변수 전달.rpc('함수이름', { 파라미터 })
리턴 타입SETOF, RECORD, BOOLEAN, INT 등 PostgreSQL에서 정의
권한 관리함수에도 RLS 및 권한 설정 가능 (권한 없으면 실행 안 됨)
Supabase JS 사용법.rpc('함수명', { 매개변수 })

View vs RPC 비교

항목ViewRPC
목적복잡한 SELECT 쿼리 정리복잡한 로직/계산 실행
호출 방식.from('view').rpc('function', { params })
읽기/쓰기읽기 전용 (기본)읽기/쓰기 모두 가능
RLS 적용가능가능 (explicit grant 필요)
트랜잭션불가가능 (BEGIN ... COMMIT)
예시가상의 게시글 요약 테이블특정 태그에 해당하는 글 반환

 

Supabase에서 RPC 등록 방법 (PostgreSQL Function 생성)

A. SQL Editor에서 함수 생성

create or replace function public.get_posts_by_tag(tag_input text)
returns setof posts
language sql
as $$
  select p.*
  from posts p
  join post_tags pt on pt.post_id = p.id
  join tags t on t.id = pt.tag_id
  where t.name = tag_input;
$$;

B. 함수 실행 권한 부여

grant execute on function public.get_posts_by_tag(text) to anon, authenticated;

Supabase 클라이언트에서 .rpc() 호출하려면 필수

C. Supabase에서 사용

const { data, error } = await supabase
  .rpc('get_posts_by_tag', { tag_input: 'nextjs' });

 

Supabase에서 사용할 때 주의할 점

주의할 점설명
View는 읽기 전용수정하려면 INSTEAD OF 트리거 혹은 materialized view 고려
RPC는 GRANT 필요GRANT EXECUTE ON FUNCTION your_func TO anon, authenticated 반드시 명시
View/RPC 모두 보안은 RLS뷰에서 참조하는 테이블의 RLS 정책을 따름 (주의!)
  • View는 조회 대상 테이블들의 RLS 정책을 따름
  • RPC는 함수 안에서 사용하는 테이블의 RLS 정책을 따름
  • 따라서 테스트할 때는 auth.uid()를 쓰는 정책이 있다면 로그인된 JWT 필요