이렇게 누구나 쉽게 바이브 코딩으로 웹 서버를 배포할 수 있어졌는데, 막상 코드를 까대기 해보면 보안은 전혀 고려되어 있지 않은 경우가 훨씬 많다.
가끔 친구들이 "xub2야 나 ~~~ 웹 개발했는데 한번 봐줘 ~~" 라고 링크를 보내줄 때가 있다.
나는 어느정도 웹 개발을 하다보니, static 하게끔만 해도 충분한 서비스인지 아닌지 구분점이 서는데, 선수 지식 없이 바이브 코딩으로만 작성된 코드를 보면 몇 가지 반복되는 패턴이 보였다.
가장 먼저, 웹 서버(Web Server)와 WAS(Web Application Server)의 차이를 모른다는 점이다(당연히 모르는게 맞다..!).
정적 파일만 던져주면 되는 웹 서버 선에서 끝날 일인데, 이 구분이 없다 보니 요청 하나하나를 다 서버 로직으로 처리하려고 한다.
결과적으로 굳이 필요 없는 백엔드 서버를 띄워야 하고, 상태 관리와 API를 다 만들어버리는 오버엔지니어링이 자연스럽게 들어간다. 정적 페이지 하나면 될 걸 WAS 구조로 짜놓으니 배포도 복잡해지고, 관리 포인트도 같이 늘어난다.
여기서 한 발 더 나가면 프로젝트 규모가 커지면서 DB 관리 문제가 따라온다. 이게 내가 생각했을 때의 가장 큰 문제점이다.
사실 사이드 프로젝트나 이런 소규모 랜딩페이지 수준에서는 데이터를 영속적으로 저장해야 할 이유가 거의 없는데도, 일단 DB부터 붙이고 보는 경우가 많다.
특히 요새는 비전공자도 쉽게 쓸 수 있는 Supabase 같은 BaaS(Backend as a Service)가 많다 보니, 실제로 이런 식의 코드를 자주 볼 수 있다.
const supabase = createClient('(DB 주소)', '(API 키)');
const { data } = await supabase.from('users').select('*');
문제는 이 코드가 브라우저에서 그대로 실행된다는 점이다.
개발자 도구 네트워크 탭만 열어봐도 위 주소와 키 값이 그대로 노출되는 건 물론이고, 어줍잖게 이 코드를 그대로 깃에 푸시해서 공개 레포로 올려버리면 코드만 봐도 바로 주소와 키가 드러난다.
anon key 자체는 원래 클라이언트 노출을 전제로 설계된 키라 이것만으로는 문제가 아니지만, RLS(Row Level Security) 정책이 비어있으면 이 select('*') 한 줄로 테이블 전체가 그대로 털린다.
그래서 결론은, 나는 바이브 코딩을 꽤나 긍정적으로 생각하고 있는 사람으로써 실제 프로덕트를 만들어보고 싶은 비개발 직군이라면 AI 프롬프트에서 '보안', '오버엔지니어링' 두가지 키워드를 프롬프트에 자주 섞어가며 구현 해 나가면 좋을 것 같다.