본문으로 건너뛰기
안동민 개발노트 아이콘

안동민 개발노트

본문 시작
2장 : 값·식·분기·반복

if 조건 분기

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

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

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

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


조건 순서의 오류

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를 반복하지 않아도 됩니다.

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


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");
}

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

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


저장 가능 상태

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

문자열 숫자 변환과 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로 바꾸어 한 번씩 실행합니다.

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

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


조건 경계표

입력첫 참 조건결과
이메일 없음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을 앞에 놓으면 뒤 라벨에 도달할 수 없으므로 작은 범위부터 위에서 제거합니다.