← 전체 글
보안과 거버넌스 개발자 게시됨 · · 작성자 ObjectStack Team

Lovable는 프로덕션에 안전한가: 접근 제어 리뷰의 문제

Lovable는 빠른 프로토타입에 강하다. 하지만 프로덕션 시스템에서 물어야 할 것은 작동하는지가 아니라, 누가 접근 제어를 검토할 수 있는지다. RLS, 프런트엔드 필터, 보안 스캔의 경계를 구분해야 한다.

Lovable는 프로덕션에 안전한가: 접근 제어 리뷰의 문제
  • Lovable
  • Vibe Coding
  • 보안
  • 접근 제어
  • AI-Native

TL;DR: Lovable는 비개발자가 진짜로 수익화 가능한 풀스택 앱을 출시하는 가장 빠른 방법 중 하나다. 위험은 구체적으로 설명할 수 있다. 누가 당신 사용자의 데이터를 읽을 수 있는지 결정하는 접근 제어가 Supabase 행 수준 보안(RLS) SQL로 생성된다 — AI에 의해, 정의상 SQL을 읽을 수 없는 사람을 위해 — 그리고 using (true) 같은 패턴은 보안 정책처럼 보이면서도 아무것도 보호하지 않는다. 데모를 올바르게 동작하게 만드는 프런트엔드 필터 .eq('owner', user.id)요청의 편의일 뿐, 보안 경계가 아니다. “작동한다”와 “안전하다”는 서로 다른 주장이며, 출시하는 사람은 그 둘을 구별하지 못한다.

실패의 정확한 형태를 보여주겠다. “vibe coding은 위험하다”는 말은 쓸모가 없고, 구체가 전부이기 때문이다.

한 창업자가 사용자 계정이 있는 앱을 설명한다. Lovable가 그것을 만들고, Supabase를 연결하고, 출시한다. 모든 사용자는 자기 레코드만 본다. 창업자가 계정 두 개로 테스트한다. 완벽하게 동작한다. 데모에서는, 옳다.

이제 브라우저의 네트워크 탭 — 비개발자라면 결코 열지 않는 그것 — 을 열고, 앱이 실제로 보내는 요청을 보라.

// 프런트엔드가 쿼리를 현재 사용자로 "한정"한다:
const { data } = await supabase
  .from('customers')
  .select('*')
  .eq('owner', user.id)   // 보안 경계처럼 보인다. 아니다.

.eq('owner', user.id)필터다 — 요청에 붙은 매개변수일 뿐이다. 누구든 네트워크 탭을 열어 엔드포인트를 복사하고, 필터 없이 다시 보낼 수 있다. 그들이 모두의 행을 가져가지 못하게 막아야 하는 것은 테이블에 걸린 행 수준 보안 정책이다. Lovable가 흔히 생성하는 것은 다음과 같다.

-- 경계여야 하는 RLS 정책:
create policy "Enable read access for all users"
  on public.customers for select
  using (true);          -- ← 모든 행, 모든 호출자에 대해 true로 평가된다

using (true)가 의미하는 바는 이렇다. 인증된 모든 호출자가 모든 행을 읽을 수 있다. 정책은 존재한다. 켜져 있다. 그리고 아무것도 보호하지 않는다. 앱이 안전한 것은 정확히 한 가지 구성에서뿐이다 — 프런트엔드가 마침 요청하는 그 구성 — 그리고 누군가 다르게 요청하는 순간 활짝 열린다. CVE-2025-48757 같은 사례가 보여주는 것은, 이 실패가 추상적 우려가 아니라 생성 앱의 접근 제어에서 실제로 일어날 수 있다는 점이다.

필터는 경계가 아니다

이것이 개념의 전부이며, 이름을 붙일 가치가 있다. 한 번 보고 나면 다시 안 본 것으로 할 수 없기 때문이다. 필터는 요청의 편의이고, 경계는 요청과 무관하게 서버에서 강제된다. 데모가 동작하는 것은 필터 덕분이다. 데이터가 안전한 것은 경계가 있을 때뿐이다. 둘은 바깥에서는 구별되지 않는다 — 둘 다 두 테스트 계정에 올바른 것을 보여준다 — 바로 그렇기에 비개발자는 앱을 써보는 것만으로는 안전한 앱과 노출된 앱을 가려낼 수 없다.

Lovable가 앱을 만들 권한을 주는 사람은, 제품 자체의 설계상, using (true)와 진짜 using (auth.uid() = owner)의 차이를 읽을 준비가 가장 안 된 사람이다. 능력과 이해가 떼어졌다. 그 분리가 곧 제품의 마법이자, 같은 붓질로 그 책임이기도 하다.

뻔한 반론을 해체하기: “Lovable는 보안 스캐너를 추가했다”

Lovable의 공으로 인정하자. 이 비판은 가닿았고 그들은 응답했다 — 보안 스캐너, SSO와 감사 로그를 갖춘 엔터프라이즈 등급이다. 그러니 공정한 질문은 이것이다. 그것으로 막히지 않는가?

스캐너가 무엇을 확인할 수 있고 무엇을 확인할 수 없는지 보라. 보고된 동작에 따르면, 그것은 테이블에 RLS 정책이 존재한다는 것을 검증한다 — 그리고 using (true) 존재하는 정책이다. 아무것도 강제하지 않으면서 “정책이 존재함”을 통과시킨다. 존재를 확인하는 것은 기계적이다. 유효성을 확인하는 것 — 이 정책이 실제로 누가 무엇을 읽는지 제약하는가 — 은 데이터 모델을 이해하고 모든 접근 경로를 추론하는 것을 요구한다. 그것은 바로, 도구가 사용자에게 건너뛰게 해주려고 존재하는 그 전문가 판단이다. 스캐너는 제품이 제거한 이해를 공급할 수 없다. 이해가 안전해 보이게 제거됐다는 것을 확인해줄 수 있을 뿐이다.

그리고 엔터프라이즈 통제 — SSO, 조직 감사 로그 — 가 다스리는 것은 사람들이 Lovable에 어떻게 로그인하는가이지, AI가 당신 앱에 어떤 RLS를 써넣었는가가 아니다. 그것들은 진짜이고 좋고 직교한다. 빈틈은 Lovable의 엔터프라이즈 태세가 아니다. AI가 생성하는 앱별 접근 로직이, 그것에 책임을 지는 단 한 사람에게 읽히지 않는다는 점이다.

유출을 설명하는 숫자: 검증 비대칭

이 패턴을 불운이 아니라 불가피로 만드는 수치가 여기 있다. 두 비용을 비교하라.

  • 만드는 비용(앱을 짓는): 한 문장. 설계상 사실상 0 — 그게 곧 세일즈 포인트 전부다.
  • 검증하는 비용(안전한지 확인하는): 테이블마다 생성된 RLS를 읽고, API 경로마다 추론하고, 필터와 경계를 구별한다. 전문가의 시간, 그리고 만든 이가 갖지 못한 기술.

만드는 비용이 0으로 붕괴하는데 검증 비용은 전문가의 시간에 머무르면, 비전문가의 합리적 행동은 검증을 건너뛰는 것이다 — 할 수 없고, 앱은 완성된 것처럼 보이기 때문이다. 이것을 모집단 전체에 곱하면 유출은 우연한 몇 건이 아니라 반복되는 고장 모드가 된다. 읽을 수 없는 접근 제어를 비전문가가 생성하게 두는, 그 어떤 도구에서든 검증 비대칭은 규모에서 작동한다.

Lovable가 정확히 옳은 지점 — 그리고 정직한 단서

프로토타입, 랜딩 페이지, 해커톤 결과물, 사내 일회용, 투자 유치 데모 — 실존 인물의 데이터가 앱 뒤에 놓여 있지 않은 어디서든, Lovable는 탁월하고, 거의 확실히 더 절제된 그 무엇(우리를 포함해)보다 빠르다. 노출할 것이 없을 때 검증 비대칭은 아무 비용도 들지 않는다. 쓰면 되고, 즐기면 된다.

그리고 이것을 비방 기사가 되지 않게 하는 정직한 단서. 이것은 구조적이지, Lovable의 결함이 아니다. 비전문가의 만드는 비용을 붕괴시키면서 검증은 전문가 비용으로 남겨두는 도구라면 어느 것이든 같은 비대칭을 물려받는다 — 그리고 Lovable는 그것을 좁히려 적극적으로 노력하고 있다. 요지는 “Lovable가 나쁘다”가 아니다. 프로덕션 데이터에 관해서는, “만든 이가 접근 제어를 읽지 못한다”는 것이, 빌더에 누구의 로고가 붙어 있든 실격 사유라는 것이다.

선은 선명하다. 실존 인물의 데이터가 그 뒤에서 살아가는 순간 — 고객, 환자, 직원 — “작동한다”는 그 무엇의 증거도 아니게 되고, “경계가 보이지 않는다”는 편의가 아니게 되어 위험이 된다.

30초 만에 해볼 수 있는 테스트

기능 목록은 잊어라. 출시한 사람에게 이렇게 물어라.

“네트워크 탭을 열고, 데이터 요청을 가져와서, 필터를 떼고 보내봐. 돌아오는 게 네 행만이야 — 아니면 전부야?”

그 테스트를 실행하지 못하거나, 이해하지 못하거나, 답이 “전부”라면, 그 앱은 아름다운 프로토타입이지 프로덕션 시스템이 아니다 — 아무리 다듬어져 보이든, 스캐너가 체크 표시를 줬든.

ObjectStack의 입장

ObjectStack는 유출을 일으키는 그 분리를 뒤집는다. 접근 제어는 만든 이가 읽지 못하는 생성 SQL이 아니다. 선언된, 읽을 수 있는 메타데이터다 — 객체의 속성으로서의 read: owner == current_user — 그리고 런타임이 서버 측에서 강제하는 경계다. 그래서 프런트엔드 필터를 떼어도 아무것도 바뀌지 않는다. AI가 누가-무엇을-읽을-수-있는지에 대한 변경을 제안하면, 그것은 비전문가가 실제로 승인할 수 있는 diff로 드러난다 (“이렇게 하면 customers가 모두에게 읽힙니다 — 확인할까요?”). SQL을 읽어야만 잡아낼 수 있었던, 조용한 using (true)로는 결코 나타나지 않는다. 그리고 셀프 호스팅 가능하고 당신이 이미 운영 중인 시스템 위에 얹히기 때문에, 민감한 데이터가 당신이 점검할 수 없는 어딘가로 조용히 흘러들지 않는다.

우리는 첫 데모까지의 시간에서 Lovable를 이기지 못할 것이고, 프로토타입이라면 시도조차 하지 않을 것이다. 진짜 데이터가 걸리는 순간 중요해지는 주장은 이것이다. AI가 만든 앱의 보안은 그것에 책임지는 사람에게 보여야 한다 — 경계를 볼 수 없는 앱은 안전한 게 아니라 아직 혹독한 방식으로 시험받지 않았을 뿐이기 때문이다.