
AI로 웹앱을 만들다 보면 가장 먼저 붙는 기능 중 하나가 문의폼, 예약폼, 회원가입이다.
이 기능들은 얼핏 보면 단순 입력창처럼 보인다.
이름, 이메일, 전화번호, 날짜, 요청사항 정도만 받으니까 가볍게 느껴질 수도 있다.
하지만 실제로는 그렇지 않다.
문의폼이나 회원가입이 붙는 순간부터, 내 서비스는 개인정보를 다루는 서비스가 된다.
이번 글에서는 AI로 만든 웹앱에 문의폼, 예약폼, 회원가입 기능이 있다면 배포 전에 무엇을 먼저 확인해야 하는지 정리해보려고 한다.
가장 먼저 해야 할 일은 기술 설정이 아니라 수집 항목 정리다.
예를 들어 문의폼이라면 흔히 이런 항목이 들어간다.
예약폼은 여기에 더해질 수 있다.
회원가입은 보통 이렇다.
여기서 먼저 물어야 할 질문은 간단하다.
이 정보가 정말 필요한가?
예를 들어:
많이 받는다고 좋은 것이 아니다.
불필요한 정보가 많을수록 저장, 접근권한, 삭제, 노출 리스크가 같이 커진다.
폼을 만들 때 프론트엔드에서 흔히 이런 검증을 한다.
이건 UX에는 좋다.
하지만 보안 검증으로는 부족하다.
OWASP도 입력값 검증은 반드시 서버 측에서 수행되어야 한다고 설명한다. 브라우저 측 JavaScript 검증은 우회될 수 있기 때문이다.
즉 이런 구조면 부족하다.
프론트엔드에서만 required, minLength, pattern 체크
서버는 그대로 저장
최소한 서버에서도 확인해야 한다.
예를 들어 예약폼이라면 이런 식의 의미 검증도 필요하다.
예약 날짜가 과거 날짜는 아닌가?
인원 수가 0명 또는 10000명은 아닌가?
선택 가능한 시간대만 허용하는가?
기술적으로 동작하는 것과, 이상한 입력을 막는 것은 다르다.
문의폼이나 예약폼을 만들 때 실제 저장 위치는 생각보다 다양하다.
문제는 UI를 만든 사람과 데이터를 저장하는 사람이 다를 수 있다는 점이다.
특히 AI로 빠르게 만든 프로젝트에서는 더 자주 그렇다.
최소한 이 질문은 답할 수 있어야 한다.
이 폼 데이터는 어디에 저장되는가?
누가 그 데이터를 볼 수 있는가?
운영자 외 다른 사용자가 접근할 수 있는가?
Supabase나 Firebase를 쓴다면 이전 글에서 다룬 RLS, Security Rules가 다시 중요해진다. [지난글-Supabase/Firebase를 쓴다면 꼭 봐야 할 권한 설정 문제]
문의 데이터나 예약 데이터는 보통 공개 데이터가 아니다.
다음 상황은 다시 봐야 한다.
폼 데이터는 DB에만 저장되지 않는 경우가 많다.
실무에서는 보통 이런 흐름이 붙는다.
사용자 제출
→ 이메일 발송
→ Slack 알림
→ Notion 기록
→ Google Sheets 저장
문제는 이 흐름이 편한 만큼 개인정보가 넓게 퍼질 수 있다는 점이다.
예를 들어:
특히 이메일은 전달, 포워딩, 검색, 아카이빙이 쉽기 때문에 더 조심해야 한다.
질문은 이것이다.
이 알림을 꼭 이렇게 상세하게 보내야 하는가?
모든 운영자가 이 전체 정보를 봐야 하는가?
문의폼과 예약폼은 기능상 공개되는 경우가 많다.
그래서 자동화 공격에 매우 취약할 수 있다.
대표적인 문제는 이런 것들이다.
회원가입이 있다면 OWASP Authentication Cheat Sheet에서 말하는 자동화 공격 방어도 같이 봐야 한다.
최소한 아래 중 일부는 있어야 한다.
문의폼은 “로그인 없이 누구나 써야 하니까” 더 위험할 수 있다.
예약폼은 “자리가 한정되어 있으니까” 비즈니스 영향이 더 클 수 있다.
회원가입은 “계정이 생기니까” 이후 보안 문제가 더 커질 수 있다.
모든 폼에 CSRF가 필요한 것은 아니다.
하지만 로그인된 상태에서 서버 데이터를 바꾸는 요청이라면 확인해야 한다.
예를 들어 이런 경우다.
OWASP는 CSRF 방어에 대해 synchronizer token pattern 같은 서버 측 토큰 기반 방식을 권장한다.
핵심은 이거다.
사용자가 로그인된 상태일 때,
다른 사이트가 사용자의 브라우저를 악용해서
내 서비스에 원치 않는 요청을 보내지 못하게 해야 한다.
즉 “폼이 있으니 제출된다”가 아니라,
“이 요청이 정말 우리 서비스에서 의도된 사용자 행동인가”를 확인해야 한다.
폼 기능을 붙이면 디버깅을 위해 로그를 남기고 싶어진다.
예를 들어 이런 코드가 쉽게 들어간다.
console.log("signup payload", req.body)
console.log("inquiry submitted", formData)
개발 중에는 편하다.
운영에서는 위험할 수 있다.
OWASP Logging Cheat Sheet는 로그에 남기면 안 되는 정보로 인증 비밀번호, 토큰, DB 연결 문자열, 일부 민감한 개인정보 등을 명시한다. 이메일 주소나 전화번호 같은 비민감 개인정보도 필요 이상 남기지 말고, 필요 시 비식별화나 축약을 고려하라고 안내한다.
다음은 피하는 게 좋다.
특히 회원가입 기능에서는 더 민감하다.
운영 환경 로그는 디버깅 도구이지, 개인정보 보관소가 아니다.
문의폼, 예약폼보다 회원가입은 한 단계 더 무겁다.
이유는 단순하다.
사용자 계정과 인증 정보가 생기기 때문이다.
최소한 이것들은 봐야 한다.
직접 인증을 구현한다면 평문 저장은 물론 안 되고, 적절한 해시 방식이 필요하다.
가능하면 Supabase Auth, Firebase Authentication 같은 검증된 제품을 우선 검토하는 편이 낫다.
가입 직후 아무 검증 없이 바로 중요한 기능을 다 열어두는 구조라면 악용 가능성이 커질 수 있다.
예를 들어 “이 이메일은 이미 가입되어 있습니다” 같은 메시지는 UX에는 좋지만, 경우에 따라 계정 존재 여부를 추측하는 데 쓰일 수 있다. 서비스 성격에 따라 균형을 봐야 한다.
토큰이 짧거나, 너무 오래 유효하거나, URL이 로그에 남는 구조면 다시 봐야 한다.
이건 초반 MVP에서 자주 놓치는 부분이다.
폼을 만들고 저장은 했는데, 그다음이 없다.
기능이 잘 되는 것만으로는 끝나지 않는다.
특히 문의폼과 예약폼은 생각보다 빨리 데이터가 쌓인다.
최소한 이런 기준은 필요하다.
무엇을 저장하는가
왜 저장하는가
누가 보는가
얼마나 보관하는가
언제 삭제하는가
문의폼, 예약폼, 회원가입 기능이 있다면 배포 전에 아래 항목은 한 번씩 체크해보는 것이 좋다.
문의폼, 예약폼, 회원가입은 기능적으로는 단순해 보인다.
하지만 보안 관점에서는 이런 문제들이 한 번에 붙는다.
즉 이런 기능을 붙이는 순간부터 내 서비스는 그냥 “화면 몇 개 있는 웹앱”이 아니라, 사용자 데이터를 책임지는 서비스가 된다.
개인적으로는 AI로 만든 웹앱일수록 이 지점을 더 의식해야 한다고 생각한다.
기능은 빠르게 붙일 수 있지만, 데이터 책임은 자동으로 해결되지 않기 때문이다.
다음 글에서는 관리자 페이지를 만들었다면 배포 전에 꼭 확인해야 할 보안 포인트를 정리해보려고 한다.