앱 조건문을 안전하게 만드는 불리언 대수

불리언 대수는 조건을 짧게 쓰는 공식보다 “제품 요구사항을 모든 입력 조합에서 같은 뜻으로 구현했는가”를 확인하는 도구에 가깝습니다. &&와 ||가 문법상 맞아도 괄호 위치 하나가 관리자 권한을 막거나 정지 계정을 허용할 수 있습니다.

2026년 10월 3일 Windows 10 x64와 Dart 3.13.4에서 게시물 수정 권한 정책의 16개 입력을 전부 실행했습니다. 의도한 정책과 결정 사유 enum이 같은 결과인지 확인하고, 실무에서 나올 법한 잘못된 식 세 개를 주입해 어느 입력에서 차이가 나는지 셌습니다. 37개 검증 조건은 모두 통과했고 잘못된 식은 각각 6개, 2개, 1개 입력에서 검출됐습니다.

연산자보다 먼저 정책 문장을 고정한다

관리자는 다른 상태와 무관하게 수정할 수 있다. 일반 사용자는 로그인했고, 글 소유자이며, 정지 상태가 아닐 때만 수정할 수 있다.

이 문장을 Dart 식으로 옮기면 다음과 같습니다.

final mayEdit = isAdmin ||
    (isAuthenticated && isOwner && !isSuspended);

여기서 괄호는 단순 장식이 아닙니다. 관리자 우회와 일반 사용자 조건을 두 묶음으로 보여 줍니다. Dart의 우선순위상 &&가 ||보다 먼저 평가되더라도, 요구사항 경계를 괄호로 드러내면 리뷰할 때 “정지 규칙이 관리자에게도 적용되는가” 같은 정책 질문을 코드에서 바로 찾을 수 있습니다.

16개 입력을 전부 돌려 얻은 결정 분포

결정 입력 수 의미
adminOverride 8 관리자 우회로 허용
allowed 1 로그인한 일반 소유자이며 정지 아님
loginRequired 4 비로그인 일반 사용자
suspended 2 로그인했지만 정지된 일반 사용자
ownerRequired 1 로그인했지만 소유자가 아님

허용은 9행, 거부는 7행이었습니다. 관리자 입력 8개가 모두 허용되고, 비로그인 일반 사용자와 정지된 일반 사용자가 모두 거부되는지도 별도 불변 조건으로 검사했습니다. 이 수치는 특정 서비스의 정답이 아니라 위에서 선언한 정책의 결과입니다. 서비스가 “정지된 관리자는 차단”을 요구한다면 정책 문장과 식 모두 바꿔야 합니다.

Dart 불리언 권한 정책 16개 조합과 잘못된 조건 3개를 직접 검증한 결과
2026년 10월 3일 Dart 3.13.4에서 16개 권한 조합, 세 가지 잘못된 식과 단일 선택 XOR를 실행한 결과입니다.

잘못된 식을 넣어야 테스트의 힘을 알 수 있다

정상 식만 통과시키면 테스트가 중요한 차이를 실제로 감지하는지 알기 어렵습니다. 그래서 다음 세 변이를 만들고 16행에서 원래 정책과 비교했습니다.

주입한 오류 잘못된 식의 핵심 불일치 행 실제 영향
관리자도 소유권 필요 (admin || login) && owner && !suspended 6 관리자 우회가 일부 거부됨
소유자 또는 활성 계정 login && (owner || !suspended) 2 소유자가 아닌 활성 계정이 허용됨
정지 검사 누락 admin || (login && owner) 1 정지된 일반 소유자가 허용됨

마지막 오류는 단 1행에서만 드러납니다. 대표 사례 몇 개만 손으로 고르면 놓치기 쉬운 이유입니다. 입력 bool이 네 개라면 16행이라 전수 검사가 저렴합니다. 입력이 늘어 조합이 폭발하면 정책의 경계값과 불변 조건을 먼저 뽑고, 속성 기반 테스트나 생성 테스트로 확장할 수 있습니다.

true·false만 반환하면 거부 이유가 사라진다

화면 버튼을 활성화할 때는 최종 bool 하나로 충분할 수 있지만, 로그인 안내와 정지 안내를 다르게 보여 줘야 한다면 결정 이유를 보존해야 합니다.

enum EditDecision {
  adminOverride,
  loginRequired,
  suspended,
  ownerRequired,
  allowed,
}

EditDecision decideEdit(UserState user) {
  if (user.isAdmin) return EditDecision.adminOverride;
  if (!user.isAuthenticated) return EditDecision.loginRequired;
  if (user.isSuspended) return EditDecision.suspended;
  if (!user.isOwner) return EditDecision.ownerRequired;
  return EditDecision.allowed;
}

이번 실행에서는 enum 결정이 허용인지 판정한 값과 한 줄 불리언 정책을 16행 모두 대조했습니다. 두 구현이 같아야 하고, 화면은 enum으로 안내 문구를 선택할 수 있습니다. 이때 분기 순서는 제품 정책입니다. 예를 들어 정지 안내를 소유권 안내보다 먼저 보여 줄지 여부는 개인정보 노출 위험까지 검토해 정해야 합니다.

“둘 중 정확히 하나”는 OR가 아니라 XOR 조건이다

카드와 계좌이체 중 하나만 선택해야 한다면 cardSelected || bankSelected는 부족합니다. 둘 다 선택된 경우도 true이기 때문입니다. bool 두 개에서는 서로 다름을 뜻하는 !=로 정확히 하나를 표현할 수 있습니다.

카드 계좌이체 card != bank 판정
false false false 선택 필요
false true true 유효
true false true 유효
true true false 중복 선택

실행 결과 OR와 XOR는 “둘 다 선택” 한 행에서 달랐습니다. 다만 선택지가 셋 이상이면 bool 여러 개보다 하나의 enum 값으로 선택 상태를 모델링하는 편이 불가능한 조합 자체를 만들지 않아 더 안전합니다.

클라이언트 조건은 권한 검사의 최종선이 아니다

이 예제는 앱 화면과 도메인 로직을 검증한 것입니다. 버튼을 숨기거나 앱에서 mayEdit를 false로 만드는 것만으로 서버 데이터가 보호되지는 않습니다. 보호된 수정 요청은 서버가 인증 정보와 대상 소유권을 다시 확인해야 합니다. OWASP도 보호된 모든 요청에서 권한을 검증하도록 안내합니다.

앱과 서버가 같은 정책을 공유하더라도 역할은 다릅니다. 앱의 판정은 빠른 안내와 잘못된 요청 방지에 쓰고, 서버 판정은 실제 허용·거부를 결정합니다. 둘의 정책표를 같은 테스트 사례로 관리하면 “앱은 허용하지만 서버는 거부”하는 불일치도 찾기 쉬워집니다.

조건문 리뷰에 바로 쓰는 순서

  1. 자연어로 허용 규칙과 예외를 한 문장씩 적습니다.
  2. 각 bool의 true 의미를 긍정형으로 통일합니다. notDisabled와 isSuspended처럼 부정 기준을 섞지 않습니다.
  3. 입력 수가 작으면 전체 조합표를 생성합니다.
  4. 관리자 우회, 비로그인 거부처럼 절대 지켜야 할 불변 조건을 따로 검사합니다.
  5. 괄호 이동, 항 누락, AND·OR 교환 변이를 넣어 테스트가 실패하는지 확인합니다.
  6. 거부 후 행동이 다르면 bool 대신 enum으로 이유를 보존합니다.
  7. 서버의 권한 검사와 같은 사례를 공유하되 클라이언트를 보안 경계로 보지 않습니다.

이번 검증에서 하지 않은 것

  • Dart 3.13.4 Windows VM에서 순수 함수만 실행했으며 실제 로그인 서버나 데이터베이스는 연결하지 않았습니다.
  • 관리자 우회가 올바른 정책이라고 일반화하지 않습니다. 이 글의 명시된 요구사항을 테스트한 결과입니다.
  • 16행 전수 검사는 네 bool 예제에 적합합니다. 입력이 많거나 연속값이 섞이면 경계값·속성 기반 테스트를 함께 설계해야 합니다.
  • 연산자 축약에 따른 성능 차이는 측정하지 않았고 성능 우위를 주장하지 않습니다.

인접한 글과 검색 의도 구분

이 글은 요구사항을 AND·OR·XOR 정책표로 만들고 잘못된 조합을 검출하는 방법에 집중합니다. 괄호 전체의 부정을 동치식으로 바꾸는 법은 드모르간 법칙 검증에서, if·switch·반복문의 입력 흐름은 Dart 조건문과 반복문 예제에서 다룹니다.

실행 결과와 대조한 1차 자료