본문으로 건너뛰기

안동민 개발노트

본문 시작

if 조건 분기

if·else·else if의 배타적 경로와 조건 순서를 이용해 회원가입 입력을 저장 또는 거절합니다.

비교식은 참과 거짓을 만들지만 실제 동작을 바꾸지는 않습니다.

if는 그 결과에 따라 실행할 문장 블록을 고릅니다.

입력 범위처럼 서로 겹치는 조건을 나열할 때는 어떤 조건을 먼저 검사했는지가 결과의 일부입니다.

700세이 accepted로 빠지는 잘못된 분기 나무

넓은 양수 조건이 먼저 참이 되면 더 구체적인 최대 시간 검사는 도달하지 못합니다.

  1. age > 0

    true → accepted로 잘못 종료

  2. age > 120

    미 분기가 끝나 검사하지 않음

  3. else

    도달하지 않음


조건 순서의 오류

lab/WrongBranchOrder.java
public final class WrongBranchOrder {
    public static void main(String[] args) {
        int age = 700;
        String result;

        if (age > 0) {
            result = "accepted";
        } else if (age > 120) {
            result = "too-old";
        } else {
            result = "not-positive";
        }

        System.out.println(result);
    }
}
accepted

700은 age > 0을 만족하므로 첫 블록이 실행되고 나머지 else if는 검사조차 하지 않습니다.

age > 120은 양수 범위 안에 포함되므로 앞 조건이 참인 값에서는 도달할 수 없습니다.

컴파일 오류는 아니지만 업무 규칙과 다른 출력입니다.

오류 조건을 먼저 제거하거나 정상 범위를 정확히 표현합니다.

src/AgeValidation.java
public final class AgeValidation {
    public static void main(String[] args) {
        int age = 700;
        String result;

        if (age <= 0) {
            result = "not-positive";
        } else if (age > 120) {
            result = "too-old";
        } else {
            result = "accepted";
        }

        System.out.println(result);
    }
}
too-old

이 사슬은 세 범위를 겹치지 않게 나눕니다.

age <= 0, age > 120, 그리고 두 조건이 모두 거짓인 1–120입니다.

위에서부터 처음 참인 블록 하나만 실행됩니다.


독립 ifelse-if

다음 두 조건은 한 값에서 모두 참일 수 있습니다.

src/IndependentConditions.java
public final class IndependentConditions {
    public static void main(String[] args) {
        int age = 90;

        if (age >= 18) {
            System.out.println("adult");
        }
        if (age >= 65) {
            System.out.println("senior");
        }
    }
}
adult
senior

if는 각각 검사하므로 라벨 두 개가 필요할 때 맞습니다.

반대로 회원을 underage, adult, senior 중 하나로만 분류하려면 else if 사슬을 사용합니다.

if (age < 18) {
    label = "underage";
} else if (age < 65) {
    label = "adult";
} else {
    label = "senior";
}

두 번째 조건은 앞 조건이 거짓이라는 사실, 즉 이미 age >= 18임을 전제로 합니다.

그래서 age >= 18 && age < 65를 반복하지 않아도 됩니다.

조건 사슬은 각 행이 이전 실패 범위를 이어받는 구조입니다.

오류 범위를 먼저 제거하면 정상 구간이 하나로 남는다

1–120세 계약을 겹치지 않는 세 구간으로 펼치면 조건 순서가 분명해집니다.

  1. … 0

    not-positive

  2. 121 …

    too-old


else 결합 규칙

중괄호를 생략하면 한 문장만 조건에 포함되고 else는 아직 짝이 없는 가장 가까운 if에 연결됩니다.

다음 코드는 들여쓰기와 실제 구조가 다르게 보일 수 있습니다.

lab/MisleadingIndentation.java
public final class MisleadingIndentation {
    public static void main(String[] args) {
        boolean emailPresent = false;
        int age = 45;

        if (emailPresent)
            if (age > 0)
                System.out.println("accepted");
            else
                System.out.println("age rejected");

        System.out.println("done");
    }
}
done

else는 바깥 if (emailPresent)가 아니라 안쪽 if (age > 0)의 짝입니다.

이메일이 없을 때는 안쪽 조건 전체가 실행되지 않아 거절 메시지도 없습니다.

중괄호를 항상 쓰면 블록 소속이 들여쓰기와 일치합니다.

if (!emailPresent) {
    System.out.println("email rejected");
} else if (age <= 0) {
    System.out.println("age rejected");
} else {
    System.out.println("accepted");
}

짧은 한 줄이라도 이후 로그 한 줄이 추가되면 중괄호 없는 코드의 실행 범위가 쉽게 어긋납니다.

이 자습서의 조건문은 모두 중괄호를 사용합니다.

독립 if는 여러 결과를, else-if는 하나를 만든다

조건이 겹칠 때 실행 가능한 결과 개수가 두 구조의 가장 큰 차이입니다.

  1. 독립 if

    90세에서 adult와 senior를 모두 출력

  2. else-if 사슬

    90세에서 첫 참인 senior 하나만 출력


저장 가능 상태

조건 분기 자체에 집중하기 위해 이번 예제의 값은 코드에 직접 둡니다.

문자열 숫자 변환과 try/catch를 여기서 미리 사용하지 않고 ch3의 입력 검증에서 처음 설명합니다.

src/SignupDecision.java
public class SignupDecision {
    public static void main(String[] args) {
        String email = "conditions@example.com";
        int age = 45;

        if (email.isBlank()) {
            System.out.println("email is required");
        } else if (age <= 0) {
            System.out.println("age must be positive");
        } else if (age > 120) {
            System.out.println("age must be at most 120");
        } else {
            String label;
            if (age < 18) {
                label = "underage";
            } else if (age < 65) {
                label = "adult";
            } else {
                label = "senior";
            }
            System.out.println("saved=" + email + ":" + age + ":" + label);
        }
    }
}
실행 결과
saved=conditions@example.com:45:adult

age를 0, 18, 65, 121로 바꾸어 한 번씩 실행합니다.

한 실행에서는 위에서부터 처음 참인 블록 하나만 선택됩니다.

실제 사용자 입력도 같은 분기로 들어오지만, 먼저 안전하게 문자열을 숫자로 바꾸는 단계가 필요합니다.

회원가입 입력은 읽기 가능한 순서로 검증한다

각 검사는 다음의 위험한 연산을 실행해도 되는 전제 조건을 만듭니다.

  1. 개수

    args.length == 2

  2. 형식

    age parseInt 성공

  3. 내용

    email이 비어 있지 않음

  4. 범위

    ≤ age ≤ 120

  5. 분류

    saved label 생성


조건 경계표

입력첫 참 조건결과
이메일 없음email.isBlank()이메일 오류
0세age <= 0양수 오류
1세앞 오류 모두 거짓underage 저장
18세age < 65adult 저장
65세마지막 elsesenior 저장
120세마지막 elsesenior 저장
121세age > 120최대값 오류

표의 각 행이 정확히 한 결과에만 도달하는지 확인합니다.

특히 경계와 바로 이웃한 0/1, 17/18, 64/65, 120/121을 실행하면 <<= 차이를 찾을 수 있습니다.

이 검사를 다음 조건 훈련 절에서 자동화합니다.


연습 문제

LoginAttemptClassifier를 작성해 로그인 실패 횟수를 다음처럼 하나의 라벨로 분류하세요.

  • 음수: invalid
  • 0: clean
  • 1–2: allowed
  • 3–5: warning
  • 6 이상: locked

0, 2, 5, 6을 각각 실행해 조건 순서를 확인합니다.

해설 보기
src/LoginAttemptClassifier.java
public final class LoginAttemptClassifier {
    public static void main(String[] args) {
        int failedLoginCount = Integer.parseInt(args[0]);
        String label;

        if (failedLoginCount < 0) {
            label = "invalid";
        } else if (failedLoginCount == 0) {
            label = "clean";
        } else if (failedLoginCount <= 2) {
            label = "allowed";
        } else if (failedLoginCount <= 5) {
            label = "warning";
        } else {
            label = "locked";
        }

        System.out.println(label);
    }
}

<= 조건은 앞 단계보다 큰 값만 받습니다.

failedLoginCount <= 5는 실제로 3–5를 뜻합니다.

넓은 failedLoginCount >= 0을 앞에 놓으면 뒤 라벨에 도달할 수 없으므로 작은 범위부터 위에서 제거합니다.