3 분 소요

supabase연결과 403에러(RLS)

저는 현재 mysql과 연결하여 프로젝트 데이터베이스를 관리하고 있습니다. 그런데, mysql연결 에러가 프로젝트를 새로 가동시킬 때마다 발생해서 supabase로 바꿔서 연결했습니다.

⚡supabase로 전환

  1. 필요한 패키지 설치

    npm install @supabase/supabase-js

    위 명령어로 필요한 패키지를 설치해줬습니다.

  2. supabse 클라이언트 설정

    db.jssupabase와 연결을 위해 설정을 해줬습니다.

    import { createClient } from '@supabase/supabase-js'
       
    const supabaseUrl = process.env.SUPABASE_URL
    const supabaseKey = process.env.SUPABASE_ANON_KEY
       
    export const supabase = createClient(supabaseUrl, supabaseKey)
    
  3. MySQL 쿼리를 Supabase 쿼리로 변경

    • MySQL의 SELECT → Supabase의 select()

    • MySQL의 INSERT → Supabase의 insert()

    • MySQL의 UPDATE → Supabase의 update()

    • MySQL의 DELETE → Supabase의 delete()

  4. 환경변수 변경

    SUPABASE_URL=your_supabase_project_url
    SUPABASE_ANON_KEY=your_supabase_anon_key
    

이미지 업로드 기능

기존에 생성해놨던 이미지 업로드 기능을 구체적으로 구현하고 연결할 차례입니다.

이미지 업로드 요청은 처음에 프론트엔드에서 post요청을 보내면, 요청 데이터를 검증합니다. 이때 필수 필드가 누락이 됐는지 확인합니다. 요청 데이터 검증이 완료되면 파일 정보를 설정합니다. 파일 경로, 파일 이름등을 설정하고 완료된다면 storage에 업로드합니다.

업로드가 완료되면

     const { data: urlData } = supabase.storage
            .from('images')
            .getPublicUrl(fileName);

        const publicUrl = urlData.publicUrl;

와 같이 public URL을 생성합니다.

이후에 DB에 images테이블에도 해당 정보를 저장해줍니다.

프론트엔드에서는 token을 확인하고, api 요청을 합니다.

image-20250416103527476

또한 업로드 될 이미지에 대한 types.ts 파일과 이미지 업로드에 관련된 endpoint를 추가 해줬습니다.

image-20250416103657802

image-20250416103715908

⚠️문제 발생

image-20250416103801194

 error: {
    statusCode: '403',
    error: 'Unauthorized',
    message: 'new row violates row-level security policy'
  },

403에러가 발생했습니다. 브라우저 측에서는

image-20250416103931662

image-20250416104007611

500에러 상태가 발생했습니다.

여기서 에러 발생 이유를 찾아보니 크게 3가지가 존재했습니다.

  1. Storage 권한 문제
    • 403 에러는 주로 권한 문제에서 발생합니다.
    • Supabase의 버킷 정책이 적절히 설정되어 있지 않은 것 같습니다.
  2. 파일 경로 문제
    • 파일 경로에 사용자 ID를 포함시키고 있는데, URL에서 경로 추출 시 문제가 있을 수 있습니다.
  3. JWT 인증 문제
    • 토큰이 올바르게 생성되었는지, 클라이언트에서 올바르게 전송되고 있는지 확인이 필요합니다.

✅문제 해결

  1. Supabase Storage 버킷 정책 설정

    Supabase 대시보드에서 Storage > images 버킷 > Policies로 이동해 적절한 정책을 설정합니다.

    image-20250416104352008

    image-20250416104327384

  2. getPublicUrl에서 경로 문제 해결

    Public URL 생성 부분에서 문제가 있을 수 있습니다.

    //수정 전 코드
    const { data: { publicUrl } } = supabase.storage
        .from('images')
        .getPublicUrl(fileName);
       
    // 수정된 코드
    const { data: urlData } = supabase.storage
        .from('images')
        .getPublicUrl(fileName);
       
    const publicUrl = urlData.publicUrl;
    console.log('4.1 Public URL 생성 완료:', { fileName, publicUrl });
    
  3. Supabase 클라이언트 설정 확인

    • config/db.js 파일에서 Supabase 클라이언트가 올바르게 설정되어 있는지 확인하세요.

    • 서비스 키(service role key)를 사용하고 있는지 확인합니다.

      image-20250416104750253

  4. multer 설정 검토

    • 파일 크기 제한을 추가

      const upload = multer({ 
        storage: storage,
        limits: { fileSize: 5 * 1024 * 1024 } // 5MB 제한
      });
      

이렇게 설정을 해줬는데도 문제가 발생해서 다시 해결방법을 찾아봤는데, 문제가 있었습니다. -

  1. 인증 키 문제

    현재 프로젝트에서 Supabase클라이언트 설정을 확인할 때, 사용하는 키는 두 가지인데, 첫 번째는 SUPABASE_URL두 번째는 SUPABASE_ANON_KEY입니다.

    문제는 여기서 발생합니다.

    SUPABASE_ANON_KEY말고 SUPABASE_SERVICE_ROLE_KEY 사용해야하기 때문입니다.

    image-20250416105236069

    SUPABASE_ANON_KEY 대신 SUPABASE_SERVICE_ROLE_KEY를 서버 측에서 사용해야 하는 이유는 권한 범위의 차이 때문입니다.

    ANON_KEY의 제한사항

    1. 제한된 권한: ANON_KEY는 공개적으로 노출되어도 안전하도록 설계되어 있어 매우 제한된 권한만 가집니다.
    2. RLS(Row Level Security): ANON_KEY로는 Supabase의 Row Level Security 정책을 우회할 수 없습니다.
    3. Storage 제한: Storage 작업에서도 세부적인 정책 설정이 필요하며, 정책이 없으면 거의 모든 작업이 차단됩니다.

    SERVICE_ROLE_KEY의 장점

    1. 관리자 권한: SERVICE_ROLE_KEY는 모든 RLS 정책을 우회할 수 있는 관리자 수준 권한을 가집니다.
    2. 서버 측 전용: 이 키는 절대 클라이언트 측에 노출되어서는 안 되며, 서버 측 코드에서만 사용해야 합니다.
    3. 스토리지 작업: 복잡한 정책 설정 없이도 스토리지 작업을 수행할 수 있습니다.

    실제 작동 방식

    • 클라이언트 측: 사용자가 이미지를 업로드하려고 하면, 브라우저에서 서버(Express)로 요청을 보내고
    • 서버 측: Express 서버가 SERVICE_ROLE_KEY를 사용해 Supabase Storage에 접근합니다.
    • 권한 우회: 이렇게 하면 복잡한 정책 설정 없이도 이미지를 업로드할 수 있습니다.

    SUPABASE_ANON_KEY를 사용하고 있어서, 스토리지에서 작업할 권한이 없기 때문에 이미지 업로드 시 403 Forbidden 에러가 발생했던 것입니다.

    그래서 SUPABASE_SERVICE_ROLE_KEY를 환경변수에 추가하고 이를 supabaseKey로 사용했습니다.

    image-20250416105634315

수정 후에 에러 발생 없이 잘 업로드 되는 것을 확인 할 수 있습니다. 또한 Database 및 Storage에도 정상적으로 잘 업로드 되었습니다.

image-20250416105853815

image-20250416105939869

image-20250416110005620

배운 점

image-20250416110219684

DataAPI 탭에서 살펴보니 RLS에 대해서 아래 설명으로 명시하고 있습니다. 빠르게 연결하려고 대충 봤기 때문에 더 시간이 소요된 경험이었습니다.

왜 사용하는지, 지금 사용하고 있는게 뭔지에 대한 질문을 끝없이 하면서 공부해야겠습니다.!

카테고리:

업데이트:

댓글남기기