많은 주니어 개발자가 하는 가장 큰 오해 중 하나는 "프론트엔드의 .env 파일에 적어둔 환경변수는 비밀이다"라는 생각입니다. 하지만 Next.js에서 NEXT_PUBLIC_ 접두사를 붙이는 순간, 그 값은 빌드 타임에 자바스크립트 코드 안에 평문으로 박혀버립니다.
사용자가 우리 사이트에 접속해 F12(개발자 도구)를 누르고 네트워크 탭만 봐도, Supabase와 통신할 때 사용하는 anon key는 아주 선명하게 노출됩니다. 즉, 전 세계 누구나 여러분의 DB API 주소와 키를 알 수 있다는 뜻입니다. 만약 아무런 조치도 하지 않았다면, 해커는 이 키를 이용해 여러분의 DB 데이터를 싹 지워버리거나 남의 비공개 글을 훔쳐볼 수 있습니다.
우리는 두 가지 선택지에 직면합니다.
Supabase의 철학을 100% 활용하면서도 보안을 챙기는 정석적인 방법은 바로 RLS를 활성화하는 것입니다.
RLS는 행 수준 보안(Row Level Security)의 약자로, 테이블 전체에 대한 권한이 아니라 데이터 한 줄(Row)마다 개별적인 보안 규칙을 적용하는 기술입니다.
auth.uid())를 기준으로 필터링을 수행합니다.가장 먼저 해당 테이블의 RLS를 켜야 합니다. (기본적으로 꺼져 있으면 무법지대입니다.)
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
블로그 포스트나 댓글 시스템에서 가장 흔히 쓰이는 방식입니다.
-- 1. 모두에게 읽기 권한 허용 CREATE POLICY "전체 읽기 허용" ON posts FOR SELECT USING (true); -- 2. 로그인한 작성자에게만 수정 권한 허용 CREATE POLICY "작성자만 수정 가능" ON posts FOR UPDATE USING (auth.uid() = author_id);
남의 이름으로 글을 쓰는 것을 방지합니다.
CREATE POLICY "본인 글만 작성 가능" ON posts FOR INSERT WITH CHECK (auth.uid() = author_id);
RLS를 켜고 나서 데이터를 조회했는데 빈 배열([])이 온다면, 해당 유저에게 허용된 Policy가 하나도 없기 때문입니다. RLS는 "허용되지 않은 모든 것은 거부한다"는 원칙으로 동작하므로, 반드시 SELECT 정책을 명시적으로 추가해줘야 합니다.
"API 키는 공개되어 있다. 그러니 보안은 DB 내부에서 챙긴다." 이것이 Supabase 환경에서의 올바른 보안 마인드셋입니다.
환경변수에 anon key를 넣는 것은 보안을 위해서가 아니라 관리를 위해서라는 점을 꼭 기억하세요. 진짜 보안은 여러분이 정성스럽게 작성한 RLS Policy 한 줄에서 시작됩니다. 지금 바로 여러분의 Supabase 대시보드에 들어가 RLS가 켜져 있는지 확인해 보세요!