노코드 앱에서 폼 검증을 위한 데이터베이스 제약조건
폼 검증에 데이터베이스 제약조건을 사용해 잘못된 데이터를 초기에 차단하고, 명확한 오류를 보여주며, 팀 전체에서 노코드 앱의 일관성을 유지하세요.

잘못된 폼 데이터가 빠르게 퍼지는 이유
잘못된 데이터는 한 곳에 머무르지 않습니다. 폼에 잘못된 값 하나가 들어가면 앱의 모든 곳에서 복사·참조·신뢰되며 퍼집니다.
보통은 작은 실수에서 시작합니다. 누군가 이메일 뒤에 공백을 입력하거나, 잘못된 고객을 선택하거나, 필드가 허용하므로 음수 수량을 넣는 식입니다. 폼이 받아들이면 시스템은 그 값을 진실로 취급합니다.
그 이후 파급은 빠르게 일어납니다. 보고서가 잘못된 합계를 보여주고, 자동화가 잘못된 레코드에 작동하며, 고객 메시지는 지저분한 필드를 가져다 전문성이 떨어져 보이게 됩니다. 팀들은 개인 스프레드시트 같은 우회 방법을 만들고, 그로 인해 불일치가 더 생깁니다. 최악의 경우 같은 잘못된 값이 옵션으로 다시 나타나거나 새 레코드에 복사되어 다시 돌아옵니다.
나중에 데이터를 정리하는 건 느리고 위험합니다. 정리는 대개 한 번의 수정으로 끝나지 않습니다. 잘못된 값이 흘러간 모든 곳을 찾아 관련 레코드를 업데이트하고 그에 의존하는 것을 다시 확인해야 합니다. 간단한 수정 하나가 워크플로를 깨뜨리거나 중복 알림을 유발하거나 감사 내역을 흐릴 수 있습니다.
폼 검증을 위한 데이터베이스 제약의 목적은 그 연쇄 반응을 첫 단계에서 차단하는 것입니다. 데이터베이스가 불가능하거나 일관성 없는 데이터를 거부하면, 침묵하는 실패를 막고 UI에서 유용한 피드백을 보여줄 명확한 순간을 얻을 수 있습니다.
예를 들어 AppMaster 같은 노코드 도구로 만든 내부 주문 폼을 상상해보세요. 주문이 고객 링크 없이 저장되거나 중복 주문 번호로 저장되면 인보이스, 배송 작업, 수익 보고서에 문제가 생길 수 있습니다. 제출 시점에 잡아내면 하류가 깨끗하게 유지되고 나중에 고치느라 고통 받지 않아도 됩니다.
데이터베이스 제약, 쉬운 설명
데이터베이스 제약은 데이터베이스 안에 있는 간단한 규칙들입니다. 데이터가 저장될 때마다—웹 폼, 모바일 화면, 임포트, API 호출 등 어디서 왔든—이 규칙들이 실행됩니다. 규칙이 깨지면 데이터베이스가 저장을 거부합니다.
이것이 UI 전용 유효성 검사와의 큰 차이입니다. 폼은 저장 전에 필드를 확인할 수 있지만, 그런 검사들은 무시되거나 우회되기 쉽습니다. 다른 화면이 같은 규칙을 잊을 수도 있고, 자동화가 직접 DB에 쓸 수도 있습니다. 곧 한 곳에서는 괜찮아 보이던 데이터가 다른 곳에서는 보고서를 깨뜨리게 됩니다.
사람들이 폼 검증을 위해 데이터베이스 제약을 언급할 때 의미하는 바는 이렇습니다: 데이터베이스를 최종 심사관으로 두고, UI는 사용자가 거의 그 벽에 부딪히지 않도록 안내하라는 것입니다.
대부분의 실제 앱은 세 가지 기본으로 많은 부분을 커버할 수 있습니다:
- 고유(Unique): “이 값은 유일해야 합니다.” 예: 이메일, 사번, 인보이스 번호.
- 체크(Check): “이 조건이 참이어야 합니다.” 예: quantity > 0, start_date <= end_date.
- 외래 키(Foreign key): “이 값은 다른 테이블의 실제 레코드를 가리켜야 합니다.” 예: 모든 주문은 존재하는 고객을 참조해야 합니다.
노코드 앱에서는 보통 데이터 생성·수정 경로가 여러 가지이기 때문에 제약이 더 중요합니다. 관리자용 웹 앱, 현장 직원용 모바일 앱, 백그라운드에서 레코드를 쓰는 자동화가 있을 수 있습니다. 제약은 그런 모든 경로를 일관되게 유지합니다.
또한 제약을 염두에 두고 설계하면 오류가 더 명확해집니다. 나중에 잘못된 데이터를 흘려보내고 고치는 대신 “해당 인보이스 번호가 이미 존재합니다” 또는 “유효한 고객을 선택해 주세요”처럼 집중된 메시지를 보여주어 데이터베이스를 처음부터 깔끔하게 유지할 수 있습니다.
제약을 사람 친화적 오류 메시지로 옮기기
제약은 나쁜 데이터를 차단하는 데는 훌륭하지만, 원시 데이터베이스 오류는 보통 개발자를 위한 문구로 작성되어 있고 폼을 채우는 사람에게 친절하지 않습니다. 목표는 단순합니다: 규칙은 데이터베이스에 두고, 실패를 사용자에게 무슨 일이었고 어떻게 고치는지 설명하는 메시지로 번역하세요.
각 제약을 두 부분으로 된 작은 "오류 계약"으로 취급하세요: 무엇이 잘못되었는가, 그리고 어떻게 고치나. UI는 친절함을 잃지 않으면서도 데이터 규칙을 약화시키지 않아야 합니다.
잘 작동하는 몇 가지 번역 예시:
-
나쁨: “Unique constraint violation on users_email_key”
-
좋음: “이 이메일은 이미 사용 중입니다. 로그인하거나 다른 이메일을 사용해 보세요.”
-
나쁨: “Check constraint failed: order_total_positive”
-
좋음: “총액은 0보다 커야 합니다. 적어도 한 개의 상품을 추가하거나 수량을 조정하세요.”
-
나쁨: “Foreign key violation on customer_id”
-
좋음: “유효한 고객을 선택하세요. 새 고객이라면 먼저 고객을 생성하세요.”
메시지를 어디에 표시하느냐는 말보다 중요할 수 있습니다. 단일 필드 오류는 해당 필드 옆에 표시하세요. 교차 필드 규칙(예: "종료일은 시작일 이후여야 한다")에는 폼 수준 배너가 더 명확합니다.
메시지 스타일을 작게 유지하세요. 대부분의 문제는 인라인 필드 텍스트, 교차 필드 규칙은 작은 배너, 짧은 확인에는 토스트를 쓰는 정도면 충분합니다.
웹과 모바일 전반에 걸쳐 문구를 일관되게 유지하세요. 웹 폼에 "유효한 고객을 선택하세요"라고 적었다면 모바일 앱에 "Invalid FK" 같은 문구를 쓰지 마세요. 같은 짧은 동사(“선택”, “입력”, “제거”)와 동일한 톤을 사용해 사용자가 무엇을 기대해야 할지 학습하도록 만드세요.
AppMaster에서 빌드하는 경우, 이 매핑은 의도적으로 설계하는 부분입니다: 데이터베이스는 엄격하게 유지하고 UI 로직은 실패를 차분하고 구체적인 안내로 바꿉니다.
단계별: 먼저 규칙을 만들고 그다음 폼을 만들기
폼을 먼저 설계하면 끝없는 모서리 케이스를 쫓아가게 됩니다. 데이터 규칙을 먼저 설계하면 UI는 이미 데이터베이스에 존재하는 규칙을 반영하므로 더 단순해집니다.
실용적인 빌드 순서:
- 정말 중요한 몇몇 필드를 적어두세요. 평범한 말로 “유효”를 정의하세요. 예: “이메일은 고유해야 한다”, “수량은 1 이상이어야 한다”, “모든 주문은 고객에 속해야 한다”.
- 테이블과 관계를 모델링하세요. 화면을 그리기 전에 무엇이 무엇에 속하는지 결정하세요.
- 비협상 규칙에 제약을 추가하세요. 중복에는 고유 제약, 항상 참이어야 하는 규칙에는 체크 제약, 관계에는 외래 키를 사용하세요.
- 제약과 일치하도록 UI를 만드세요. 필수 필드를 표시하고, 적절한 입력 유형을 사용하며 간단한 힌트를 추가하세요. UI는 사람을 안내해야 하고 데이터베이스는 최종 관문이어야 합니다.
- 의도적으로 깨보세요. 지저분한 값을 붙여넣고, 중복을 시도하고, 누락된 관련 레코드를 선택해보세요. 그런 다음 레이블과 오류 문구를 개선해 무엇을 고쳐야 할지 명백하게 하세요.
빠른 예시
내부의 "New Order" 폼을 만든다고 해봅시다. 사용자가 고객 이름으로 검색할 수 있게 할 수 있지만, 데이터베이스는 실제 Customer ID(외래 키)만 받아들여야 합니다. UI에서는 이게 검색 가능한 픽커가 됩니다. 사용자가 고객을 선택하지 않고 제출하면 메시지는 혼란스러운 저장 오류 대신 단순히 “고객을 선택하세요”라고 하면 됩니다.
이렇게 하면 웹과 모바일 폼 전반에 걸쳐 규칙을 반복하지 않고도 일관성을 유지할 수 있습니다.
실제 사람들이 만드는 중복을 막는 고유 제약
고유 제약은 "다른 형태의 같은 것"이 쌓이는 것을 막는 가장 간단한 방법입니다. 폼이 놓치는 중복도 데이터베이스가 거부합니다.
사람들이 실수로 반복하는 값들에 대해 고유 제약을 사용하세요: 이메일, 사용자명, 인보이스 번호, 자산 태그, 사번, 스프레드시트에서 붙여넣은 티켓 번호 등.
첫 번째 결정은 범위입니다. 어떤 값은 시스템 전체에서 고유해야 할 수 있고(사용자명), 어떤 값은 부모 그룹 내에서만 고유하면 됩니다(조직별 인보이스 번호, 창고별 자산 태그). 범위를 의도적으로 선택해 정상적인 데이터를 막지 마세요.
실용적인 사고 방식:
- 전역 고유: 시스템 어디에서나 하나의 값만 허용(사용자명, 공개 핸들)
- 조직별 고유: 회사/팀 내에서만 고유(invoce_number + org_id)
- 위치별 고유: 사이트 내에서만 고유(asset_tag + location_id)
충돌을 처리하는 방식이 규칙만큼 중요합니다. 고유 제약이 실패하면 단순히 “이미 존재함”이라고 하지 마세요. 무엇이 충돌했는지와 사용자가 다음에 무엇을 할 수 있는지 알려주세요. 예: “인보이스 번호 1047이 Acme Co.에 이미 존재합니다. 1047-2를 시도하거나 기존 인보이스를 여세요.” UI가 안전하게 기존 레코드를 참조할 수 있다면 생성일이나 소유자 같은 작은 힌트는 사용자가 노출되지 않는 정보를 보지 않고도 회복하는 데 도움이 됩니다.
수정 작업(edit)에는 특별한 주의가 필요합니다. 업데이트를 새 레코드로 취급해 자기 자신과 충돌을 일으키는 실수가 흔합니다. 저장 로직이 현재 레코드를 인지해 자기 자신과 비교하지 않도록 하세요.
AppMaster에서는 Data Designer에서 먼저 고유 규칙을 정의하고 폼에 친절한 메시지로 반영하세요. 데이터베이스는 최종 관문이고 UI는 실제 규칙을 설명합니다.
항상 참이어야 하는 규칙을 위한 체크 제약
체크 제약은 데이터베이스가 각 행마다 항상 강제하는 규칙입니다. 누군가 규칙을 깨는 값을 입력하면 저장이 실패합니다. 이는 다른 화면, 임포트, 자동화 등 다양한 경로로 데이터가 생성될 때도 절대 위반되어선 안 되는 규칙에 적합합니다.
좋은 체크는 간단하고 예측 가능해야 합니다. 사용자가 규칙을 추측할 수 없다면 계속 오류를 겪게 됩니다. 체크는 사실(fact)에 집중하고 복잡한 정책을 넣지 마세요.
즉시 효과를 보는 일반적인 체크 제약:
- 범위: 수량은 1
1000, 나이는 13120 - 허용 상태: status는 Draft, Submitted, Approved, Rejected 중 하나여야 함
- 양수 숫자: amount > 0, 할인은 0~100 사이
- 날짜 순서: end_date >= start_date
- 단순 논리: status = Approved 이면 approved_at 은 null이 아니어야 함
체크를 친근하게 느끼게 하는 요령은 UI 문구입니다. 제약 이름을 그대로 반복하지 말고 사용자가 무엇을 바꿔야 할지 알려주세요.
좋은 패턴:
- “수량은 1에서 1000 사이여야 합니다.”
- “상태를 선택하세요: Draft, Submitted, Approved, 또는 Rejected.”
- “종료일은 시작일과 같거나 이후여야 합니다.”
- “금액은 0보다 커야 합니다.”
노코드 빌더인 AppMaster에서는 즉시 피드백을 위해 폼에서 같은 체크를 미러링해도 괜찮지만, 데이터베이스의 체크 제약을 최종 가드레일로 유지하세요. 나중에 새로운 화면이 추가되어도 규칙은 유지됩니다.
관계를 실체로 만드는 외래 키
외래 키(FK)는 단순한 약속을 강제합니다: 필드가 다른 레코드를 가리킨다면 그 레코드는 실제로 존재해야 합니다. Order가 CustomerId를 가진다면, 데이터베이스는 Customers 테이블에 없는 고객을 참조하는 주문을 거부합니다.
이것이 중요한 이유는 관계 필드가 '거의 맞음' 데이터가 드러나는 지점이기 때문입니다. 누군가 고객 이름을 약간 잘못 입력하거나 오래된 ID를 붙여넣거나 어제 삭제된 레코드를 선택할 수 있습니다. FK가 없으면 그런 실수는 보고서나 인보이스, 지원 작업에서 문제가 될 때까지 드러나지 않습니다.
UI 패턴은 간단합니다: 자유 텍스트 대신 안전한 선택지를 사용하세요. "Customer"에 텍스트 입력을 두지 말고, 선택(select), 검색, 자동완성 등을 사용해 실제 고객의 ID를 뒤에서 저장하세요. 노코드 빌더(예: AppMaster UI 컴포넌트를 모델에 바인딩)에서는 드롭다운이나 검색 리스트를 Customers 테이블에 바인딩하고 선택된 레코드 참조를 저장하는 식입니다.
참조된 레코드가 없거나 삭제된 경우의 동작을 미리 결정하세요. 대부분 팀은 다음 중 하나를 택합니다:
- 관련 레코드가 있을 때 삭제를 금지(고객, 제품, 부서에 흔함)
- 삭제 대신 보관(아카이브)해 기록을 보존하면서 관계가 깨지지 않게 함
- 정말 안전한 경우에만 cascade delete 적용(비즈니스 데이터에서는 드묾)
- 관계가 선택사항이면 참조를 빈 값으로 설정
또한 "관련 레코드 생성" 흐름을 계획하세요. 사용자가 다른 곳으로 나가서 고객을 만든 다음 돌아와서 다시 입력하게 해선 안 됩니다. 실용적인 방법은 먼저 고객을 생성해 새 ID를 반환하고 자동으로 선택하는 "새 고객" 액션을 제공하는 것입니다.
FK가 실패하면 원시 데이터베이스 메시지를 보여주지 마세요. 무슨 일이 있었는지 평범한 문장으로 알려주세요: “선택한 고객이 더 이상 존재하지 않습니다. 유효한 고객을 선택하세요.” 이 한 문장이 깨진 관계가 퍼지는 것을 막습니다.
UI 흐름에서 제약 실패 처리하기
좋은 폼은 실수를 조기에 잡지만 자체가 최종 심사관인 것처럼 굴지는 않습니다. UI는 사용자가 더 빠르게 작업하도록 도와주고, 데이터베이스는 저장되지 않도록 보장합니다.
클라이언트 측 검사는 명백한 것들을 잡습니다: 필수 필드가 비어 있거나 이메일에 @가 없거나 숫자가 범위를 크게 벗어나는 경우. 즉시 보여주면 폼이 반응성이 좋아 보이고 실패 제출을 줄입니다.
서버 측 검사는 제약이 실제로 작동하는 곳입니다. UI가 무언가를 놓치거나(또는 두 사람이 동시에 제출하는 경우) 데이터베이스가 중복, 잘못된 값, 깨진 관계를 차단합니다.
제약 오류가 서버에서 돌아올 때는 응답을 예측 가능하게 유지하세요:
- 사용자의 모든 입력을 폼에 유지하세요. 페이지를 리셋하지 마세요.
- 문제를 일으킨 필드를 강조하고 근처에 짧은 메시지를 추가하세요.
- 이슈가 여러 필드를 포함하면 상단 메시지를 보여주고 가장 관련 있는 필드를 여전히 표시하세요.
- 안전한 다음 행동을 제안하세요: 값을 수정하거나 상황에 맞다면 기존 레코드를 열기.
마지막으로 무슨 일이 있었는지 로깅해 폼을 개선하세요. 제약 이름, 테이블/필드, 오류를 일으킨 사용자 동작을 캡처하세요. 어떤 제약이 자주 실패하면 UI에 작은 힌트를 추가하거나 클라이언트 측 검사를 강화하세요. 갑작스런 실패 증가도 혼란스러운 화면이나 깨진 통합을 신호할 수 있습니다.
시간이 지나도 깨끗한 내부 주문 폼 예시
영업과 지원이 사용하는 간단한 내부 도구인 "Create Order" 폼을 생각해보세요. 겉보기엔 무해하지만 데이터베이스의 중요한 테이블을 건드립니다. 폼이 한 번이라도 잘못된 입력을 받으면 그 실수는 인보이스, 배송, 환불, 보고서로 퍼집니다.
깨끗하게 만드는 방법은 데이터베이스 규칙에 UI를 따르게 하는 것입니다. 폼은 규칙들을 친절하게 안내하는 프런트엔드가 되고, 누군가 데이터를 임포트하거나 다른 곳에서 편집하더라도 규칙은 계속 유지됩니다.
Order 테이블에서 강제할 항목 예:
- 고유 주문 번호: 각
order_number는 달라야 합니다. - 항상 참인 규칙을 위한 체크:
quantity > 0,unit_price >= 0, 필요하면unit_price <= 100000같은 제약. - Customer로의 외래 키: 모든 주문은 실제 고객 레코드를 가리켜야 합니다.
실사용에서 무슨 일이 일어나는지 보세요.
영업 사원이 외워둔 주문 번호를 입력하다 실수로 이미 사용된 번호를 다시 입력하면 저장이 고유 제약에서 실패합니다. “저장 실패” 같은 모호한 메시지 대신 UI는 “주문 번호가 이미 존재합니다. 다음 사용 가능한 번호를 사용하거나 기존 주문을 검색하세요.”라고 보여줄 수 있습니다.
나중에 누군가 고객 레코드를 병합하거나 삭제했는데 다른 사람이 폼을 열어둔 채 저장을 누르면 외래 키가 이를 막습니다. 좋은 UI 응답은 “선택한 고객을 더 이상 사용할 수 없습니다. 고객 목록을 새로고침하고 다른 고객을 선택하세요.” 그런 다음 Customer 드롭다운을 다시 로드하고 폼의 나머지 내용을 유지해 사용자가 작업을 잃지 않게 합니다.
이 패턴은 시간이 지나도 사람들에게 매일 조심하라고 의존하지 않고 주문을 일관되게 유지합니다.
혼란스러운 오류와 더러운 데이터를 만드는 흔한 실수
가장 빠르게 데이터를 엉망으로 만드는 방법은 UI 전용 규칙에만 의존하는 것입니다. 폼의 필수 필드는 도움이 되지만 임포트, 통합, 관리자 편집, 또는 같은 테이블을 쓰는 다른 화면에는 보호가 되지 않습니다. 데이터베이스가 잘못된 값을 허용하면 나중에 어디서든 보입니다.
또 다른 흔한 실수는 현실 생활에 비해 제약을 너무 엄격하게 만드는 것입니다. 처음엔 맞아 보이던 체크가 일주일 뒤 정상적인 사용 사례(환불, 부분 배송, 해외 전화번호 등)를 막을 수 있습니다. 좋은 규칙은 항상 참이어야 하는 것만 제약하는 것입니다.
업데이트는 자주 간과됩니다. 편집 시 자기 자신과의 중복 충돌은 고전적 오류입니다: 사용자가 레코드를 열어 관련 없는 필드를 바꾸었는데 저장이 실패하는 경우입니다. 상태 전환도 함정입니다. 레코드가 Draft → Approved → Cancelled로 이동할 수 있다면 체크가 전체 경로를 허용하는지 확인하세요.
외래 키 실패는 가장 피할 수 있는 방식으로 발생합니다: 사람들이 ID를 직접 타이핑하도록 허용하는 것. 관계 필드를 자유 텍스트로 두면 깨진 관계가 생깁니다. 기존 레코드에 바인딩된 선택기를 선호하고 데이터베이스 제약을 백스탑으로 유지하세요.
마지막으로 원시 데이터베이스 오류는 패닉과 지원 티켓을 만듭니다. 엄격한 제약을 유지하면서도 사람 친화적 메시지를 보여줄 수 있습니다.
간단한 수정 목록:
- 제약을 단순한 폼 규칙이 아닌 진실의 출처로 유지하세요
- 실제 워크플로(예외 포함)를 고려해 체크를 설계하세요
- 생성뿐 아니라 수정과 상태 전환도 처리하세요
- 관계에는 식별자 입력 대신 픽커를 사용하세요
- 제약 실패를 필드 수준의 친절한 메시지로 매핑하세요
노코드 팀을 위한 빠른 체크리스트와 다음 단계
폼을 배포하기 전에 급하게, 기분 나쁜 날, 복사된 데이터로 사용될 것을 가정하세요. 가장 안전한 접근은 폼 검증을 위한 데이터베이스 제약을 두어 UI가 놓쳐도 데이터베이스가 진실을 강제하게 하는 것입니다.
출시 전 빠른 점검
데이터베이스에 쓰는 모든 폼에서 다음을 확인하세요:
- 중복: 고유해야 하는 것을 식별(이메일, 주문 번호, 외부 ID)하고 고유 규칙이 있는지 확인
- 누락된 관계: 필요한 모든 관계가 강제되는지 확인(예: 주문은 고객을 가져야 함)
- 잘못된 범위: 반드시 범위 내에 있어야 하는 값에 체크 추가(quantity > 0, discount 0~100 등)
- 필수 필드: 필수 데이터는 UI 필수 표시 뿐 아니라 DB 레벨에서도 강제
- 안전한 기본값: 자동 채울 값을 결정(status = "Draft" 등)하여 사용자가 추측하지 않게 함
그런 다음 빌더가 아닌 사용자인 척 테스트하세요: 먼저 한 번 정상 제출을 끝까지 해 보고, 그다음 중복, 누락된 관계, 범위를 벗어난 수치, 빈 필수 필드, 잘못된 타입 입력 등으로 깨보세요.
AppMaster에서의 다음 단계
AppMaster에서 빌드 중이라면(Data Designer에서) 먼저 규칙을 모델링(unique, check, foreign keys)한 뒤 웹 또는 모바일 UI 빌더에서 폼을 만들고 Business Process Editor에 저장 로직을 연결하세요. 제약이 실패하면 오류를 잡아 한 가지 명확한 행동(무엇을 바꿀지, 어디에서 바꿀지)을 제안하도록 매핑하세요.
오류 문구는 차분하고 일관되게 유지하세요. 비난하는 어조를 피하세요. “이메일이 유효하지 않습니다”보다는 “고유한 이메일을 사용하세요” 같은 문구를 선호하세요. 가능하면 충돌한 값이나 요구 범위를 보여줘서 고치기 쉽게 만드세요.
하나의 실제 폼(예: "Create Customer" 또는 "New Order")을 선택해 엔드투엔드로 만들고 팀의 실제 그날그날 데이터를 사용해 엉망으로 만들어 보며 검증하세요.
자주 묻는 질문
데이터베이스 제약조건으로 시작하세요. 데이터베이스는 웹 폼, 모바일 화면, 임포트, API 호출 등 모든 쓰기 경로를 보호합니다. 클라이언트 측 유효성 검사는 빠른 피드백을 위해 유용하지만, 데이터베이스가 최종 관문이 되어야 다른 화면이나 자동화로 잘못된 값이 들어오는 것을 막을 수 있습니다.
실제 비즈니스 데이터를 망가뜨리는 대부분의 문제를 막는 기본에 집중하세요: 고유(Unique) 는 중복을 막고, 체크(Check) 는 항상 참이어야 하는 규칙을 보장하며, 외래 키(Foreign keys) 는 관계의 실체성을 지킵니다. 임포트나 예외 상황까지 고려해 절대 위반되면 안 되는 규칙만 추가하세요.
값이 특정 범위에서 한 레코드를 식별해야 할 때 고유 제약을 사용하세요. 이메일, 인보이스 번호, 사번처럼 기록을 고유하게 식별해야 하는 경우입니다. 먼저 범위(scope)를 정하세요(전역 vs 조직별/위치별) — 그래야 정상적 반복을 불필요하게 막지 않습니다.
체크 제약은 간단하고 예측 가능해야 합니다. 예: 범위, 양수, 날짜 순서 같은 규칙입니다. 사용자가 레이블만 보고 규칙을 추측할 수 없다면 반복해서 오류를 겪게 됩니다. UI에서 안내를 분명히 해주고 복잡한 정책을 체크로 집어넣지 마세요.
외래 키는 ‘거의 맞는’ 참조를 막아줍니다. 주문이 존재하지 않는 고객을 가리키면 안 됩니다. UI에서는 자유 입력 대신 픽커, 검색, 자동완성 같은 안전한 선택지를 사용해 레이블이 아닌 고객의 ID를 저장하세요.
각 제약을 ‘오류 계약’처럼 다루세요: 기술적 실패를 무슨 일이었고 어떻게 고치는지 알려주는 문장으로 바꾸면 됩니다. 예를 들어 원시 고유 위반을 “이 이메일은 이미 사용 중입니다. 다른 이메일을 사용하거나 로그인하세요.” 처럼 바꿔 보여주세요.
단일 필드 관련 오류는 해당 필드 옆에 바로 보여주고, 사용자의 입력은 그대로 유지하세요. 교차 필드 규칙(예: 시작/종료일)은 폼 상단의 배너로 간단히 알리고 가장 관련 있는 필드를 여전히 강조하면 고치기 쉬워집니다.
클라이언트 측 검사는 빈 필수 필드, 기본 포맷 오류처럼 명백한 문제를 조기에 잡아 전송 실패를 줄이는 용도입니다. 하지만 레이스 컨디션이나 다른 쓰기 경로를 막기 위해선 데이터베이스 제약이 여전히 필요합니다.
UI 규칙만 믿지 마세요. 현업의 변화나 통합, 관리자가 직접 수정하는 경우 등 다양한 경로로 데이터가 들어옵니다. 제약을 너무 엄격하게 만들면 정상적인 사용까지 막을 수 있고, 업데이트 시 자기 자신과 충돌하는 중복 오류가 발생하기도 합니다. 관계 필드를 자유 입력으로 두지 말고 선택기를 사용하세요. 그리고 원시 DB 오류는 사용자 패닉을 만듭니다—항상 친절한 메시지로 바꾸세요.
먼저 Data Designer에서 데이터와 제약을 모델링한 뒤 폼을 만들고, 제약 실패를 UI에서 알아보기 쉬운 메시지로 매핑하세요. AppMaster에서는 모델에 unique/check/FK를 정의하고 Business Process Editor에서 저장 로직을 연결한 다음, 실패 시 사용자가 무엇을 해야 할지 한 가지 명확한 행동을 제안하는 흐름을 만드는 것이 일반적입니다.


