읽기·쓰기

서버 컴포넌트에서 목록을 가져오고, 폼으로 한 행을 넣습니다.

서버에서 읽기

목록은 서버 컴포넌트에서 읽는다. 브라우저에 쿼리를 숨기는 것이 아니라, HTML을 만들 때 이미 행을 넣는 것이다. publishable 키와 정책은 그대로 적용된다.

import { createClient } from '@/lib/supabase/server'

export default async function Page() {
  const supabase = await createClient()
  const { data, error } = await supabase
    .from('posts')
    .select('id, title')

  if (error) return <p>불러오지 못했습니다.</p>

  return (
    <ul>
      {data.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

data가 []이면 오류가 아니다. 행이 없거나, RLS가 이 요청에 행을 안 보여 주는 것이다. select와 insert가 어떤 SQL인지는 PostgreSQL · 조회·변경에 있다.

읽기 정책

SQL 편집기에서 테이블에 RLS를 켜고, 누구나 읽어도 되는 정책 하나를 둔다. 회원만 보게 하려면 to anon, authenticated와 using (true)를 회원 조건으로 바꾼다.

alter table posts enable row level security;

create policy "posts are public to read"
on posts
for select
to anon, authenticated
using (true);

서버 함수로 추가

폼 저장은 Next.js 서버 함수에서 한다. insert도 정책이 있어야 성공한다. 정책이 없으면 키를 알아도 거절된다.

'use server'

import { createClient } from '@/lib/supabase/server'

export async function addPost(formData) {
  const supabase = await createClient()
  const { error } = await supabase.from('posts').insert({
    title: formData.get('title')
  })
  return { error: error ? error.message : null }
}
create policy "signed in users insert posts"
on posts
for insert
to authenticated
with check (true);

authenticated는 로그인한 요청이다. 로그인 전이면 이 정책에 걸리지 않는다. 로그인 방법은 인증·키에 있다.