if 조건 분기
if·else·else if의 배타적 경로와 조건 순서를 이용해 회원가입 입력을 저장 또는 거절합니다.
비교식은 참과 거짓을 만들지만 실제 동작을 바꾸지는 않습니다.
if는 그 결과에 따라 실행할 문장 블록을 고릅니다.
입력 범위처럼 서로 겹치는 조건을 나열할 때는 어떤 조건을 먼저 검사했는지가 결과의 일부입니다.
700은 첫 조건에서 이미 다른 결과로 갈린다
age 700에서 첫 true로 종료하는 잘못된 조건 순서와 오류 우선의 다른 검사 경로를 대비합니다. 검사 도달·미도달을 단계로 보여 줍니다.
FLOWCHART · BRANCH ORDER
700은 첫 조건에서 이미 다른 결과로 갈린다
else-if는 위에서 처음 참인 블록에서 멈춘다. 같은 700도 첫 guard를 어떻게 정의했는지에 따라 accepted 또는 too-old가 된다.
잘못된 순서
age > 0— PASSaccepted에서 사슬 종료age > 120— 검사하지 않음
오류 우선 순서
age <= 0— FAILage > 120— PASStoo-old로 종료
두 trace의 첫 차이는 결과 노드가 아니라 첫 guard의 범위다.
이 예의 조건
- 두 분기 코드는 동일 입력 age=700에서 비교합니다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 2 | 같은 age 700이 잘못된 순서에서는 age가 0보다 큰 첫 조건을 통과해 accepted가 되고 최대 나이 검사에는 도달하지 않지만, 오류 우선 순서에서는 age 120 초과 조건을 통과해 too-old가 되는 두 실행 trace다. |
| 구분 | 내용 |
|---|---|
| 설명 | 같은 age 700이 잘못된 순서에서는 age가 0보다 큰 첫 조건을 통과해 accepted가 되고 최대 나이 검사에는 도달하지 않지만, 오류 우선 순서에서는 age 120 초과 조건을 통과해 too-old가 되는 두 실행 trace다. |
| 전체 읽기 정보 | 700은 첫 조건에서 이미 다른 결과로 갈린다 700세 입력의 잘못된 조건 순서와 올바른 조건 순서 비교 FLOWCHART · BRANCH ORDER 700은 첫 조건에서 이미 다른 결과로 갈린다 else-if는 위에서 처음 참인 블록에서 멈춘다. 같은 700도 첫 guard를 어떻게 정의했는지에 따라 accepted 또는 too-old가 된다. 잘못된 순서 age > 0 — PASS accepted 에서 사슬 종료 age > 120 — 검사하지 않음 오류 우선 순서 age <= 0 — FAIL age > 120 — PASS too-old 로 종료 두 trace의 첫 차이는 결과 노드가 아니라 첫 guard의 범위다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 첫 조건 잘못된 순서는 양수 여부를 먼저 검사합니다. |
|
|
| 도달 여부 잘못된 순서는 accepted를 선택하고 상한 검사에 도달하지 않습니다. |
|
|
| 결과 구별 오류 우선 순서는 too-old를 선택합니다. |
|
|
700은 첫 조건에서 이미 다른 결과로 갈린다
age 700에서 첫 true로 종료하는 잘못된 조건 순서와 오류 우선의 다른 검사 경로를 대비합니다. 검사 도달·미도달을 단계로 보여 줍니다.
FLOWCHART · BRANCH ORDER
700은 첫 조건에서 이미 다른 결과로 갈린다
else-if는 위에서 처음 참인 블록에서 멈춘다. 같은 700도 첫 guard를 어떻게 정의했는지에 따라 accepted 또는 too-old가 된다.
잘못된 순서
age > 0— PASSaccepted에서 사슬 종료age > 120— 검사하지 않음
오류 우선 순서
age <= 0— FAILage > 120— PASStoo-old로 종료
두 trace의 첫 차이는 결과 노드가 아니라 첫 guard의 범위다.
이 예의 조건
- 두 분기 코드는 동일 입력 age=700에서 비교합니다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 2 | 같은 age 700이 잘못된 순서에서는 age가 0보다 큰 첫 조건을 통과해 accepted가 되고 최대 나이 검사에는 도달하지 않지만, 오류 우선 순서에서는 age 120 초과 조건을 통과해 too-old가 되는 두 실행 trace다. |
| 구분 | 내용 |
|---|---|
| 설명 | 같은 age 700이 잘못된 순서에서는 age가 0보다 큰 첫 조건을 통과해 accepted가 되고 최대 나이 검사에는 도달하지 않지만, 오류 우선 순서에서는 age 120 초과 조건을 통과해 too-old가 되는 두 실행 trace다. |
| 전체 읽기 정보 | 700은 첫 조건에서 이미 다른 결과로 갈린다 700세 입력의 잘못된 조건 순서와 올바른 조건 순서 비교 FLOWCHART · BRANCH ORDER 700은 첫 조건에서 이미 다른 결과로 갈린다 else-if는 위에서 처음 참인 블록에서 멈춘다. 같은 700도 첫 guard를 어떻게 정의했는지에 따라 accepted 또는 too-old가 된다. 잘못된 순서 age > 0 — PASS accepted 에서 사슬 종료 age > 120 — 검사하지 않음 오류 우선 순서 age <= 0 — FAIL age > 120 — PASS too-old 로 종료 두 trace의 첫 차이는 결과 노드가 아니라 첫 guard의 범위다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 첫 조건 잘못된 순서는 양수 여부를 먼저 검사합니다. |
|
|
| 도달 여부 잘못된 순서는 accepted를 선택하고 상한 검사에 도달하지 않습니다. |
|
|
| 결과 구별 오류 우선 순서는 too-old를 선택합니다. |
|
|
조건 순서의 오류
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);
}
}accepted700은 age > 0을 만족하므로 첫 블록이 실행되고 나머지 else if는 검사조차 하지 않습니다.
age > 120은 양수 범위 안에 포함되므로 앞 조건이 참인 값에서는 도달할 수 없습니다.
컴파일 오류는 아니지만 업무 규칙과 다른 출력입니다.
오류 조건을 먼저 제거하거나 정상 범위를 정확히 표현합니다.
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입니다.
위에서부터 처음 참인 블록 하나만 실행됩니다.
독립 if와 else-if
다음 두 조건은 한 값에서 모두 참일 수 있습니다.
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에 연결됩니다.
다음 코드는 들여쓰기와 실제 구조가 다르게 보일 수 있습니다.
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");
}
}doneelse는 바깥 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의 입력 검증에서 처음 설명합니다.
public class SignupDecision {
public static void main(String[] args) {
String email = "[email protected]";
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);
}
}
}[email protected]:45:adultage를 0, 18, 65, 121로 바꾸어 한 번씩 실행합니다.
한 실행에서는 위에서부터 처음 참인 블록 하나만 선택됩니다.
실제 사용자 입력도 같은 분기로 들어오지만, 먼저 안전하게 문자열을 숫자로 바꾸는 단계가 필요합니다.
오류 가드를 통과한 나이만 하나의 라벨로 출력된다
이메일/나이 가드 후 남은 범위에서 underage/adult/senior를 선택하는 실제 분기입니다. 현재 판정과 이후 미도달 조건을 단계로 구분할 수 있습니다.
FLOWCHART · GUARD CLAUSES
오류 가드를 통과한 나이만 하나의 라벨로 출력된다
이메일과 나이 오류를 먼저 종료하면 1–120만 남는다. 정상 범위는 겹치지 않는 세 구간 중 하나로 분류하여 saved 문자열 한 줄을 출력합니다.
1 · 위에서부터 오류를 검사합니다
- 이메일이 비어 있는가?
email.isBlank()true →
email is required출력 후 종료false → 다음 나이 검사
- 나이가 0 이하인가?
age <= 0true →
age must be positive출력 후 종료false → 상한 검사
- 나이가 120을 초과하는가?
age > 120true →
age must be at most 120출력 후 종료false → 정상 범위 1–120으로 진행
2 · 남은 범위에서 라벨 하나를 고릅니다
underage
adult
senior
else-if는 처음 true인 구간 하나만 실행합니다. 정상 범위에는 빈틈과 중복이 없습니다.
3 · saved 결과 한 줄을 출력합니다
saved=email:age:label
본문 코드는 System.out.println으로 결과를 보여줍니다. 실제 저장소에 회원을 기록하는 기능은 아직 없습니다.
세 오류 guard가 모두 false라는 사실이 정상 범위 1–120의 전제가 된다.
이 예의 조건
- 정수 변환 성공을 전제합니다. saved는 콘솔 문자열이며 저장소 기록이 아닙니다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 2 | 이메일/나이 세 오류 guard는 참이면 출력 후 종료, 거짓만 다음 검사; 정상 1–120은 underage/adult/senior 중 하나→saved 출력(저장 아님) |
| 구분 | 내용 |
|---|---|
| 설명 | 이메일/나이 세 오류 guard는 참이면 출력 후 종료, 거짓만 다음 검사; 정상 1–120은 underage/adult/senior 중 하나→saved 출력(저장 아님) |
| 전체 읽기 정보 | 오류 가드를 통과한 나이만 하나의 라벨로 출력된다 회원가입 오류 guard와 배타적 나이 분류 흐름 FLOWCHART · GUARD CLAUSES 오류 가드를 통과한 나이만 하나의 라벨로 출력된다 이메일과 나이 오류를 먼저 종료하면 1–120만 남는다. 정상 범위는 겹치지 않는 세 구간 중 하나로 분류하여 saved 문자열 한 줄을 출력합니다. 1 · 위에서부터 오류를 검사합니다 이메일이 비어 있는가? email.isBlank() true → email is required 출력 후 종료 false → 다음 나이 검사 나이가 0 이하인가? age <= 0 true → age must be positive 출력 후 종료 false → 상한 검사 나이가 120을 초과하는가? age > 120 true → age must be at most 120 출력 후 종료 false → 정상 범위 1–120으로 진행 2 · 남은 범위에서 라벨 하나를 고릅니다 1–17 underage 18–64 adult 65–120 senior else-if는 처음 true인 구간 하나만 실행합니다. 정상 범위에는 빈틈과 중복이 없습니다. 3 · saved 결과 한 줄을 출력합니다 saved=email:age:label 본문 코드는 System.out.println으로 결과를 보여줍니다. 실제 저장소에 회원을 기록하는 기능은 아직 없습니다. 세 오류 guard가 모두 false라는 사실이 정상 범위 1–120 의 전제가 된다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 오류 가드 공백 email, age≤0, age>120을 차례로 검사합니다. |
|
|
| 라벨 선택 남은 age 1–120에서 두 경계를 순서대로 검사합니다. |
|
|
| saved 출력 email:age:label 형태를 출력합니다. |
|
|
오류 가드를 통과한 나이만 하나의 라벨로 출력된다
이메일/나이 가드 후 남은 범위에서 underage/adult/senior를 선택하는 실제 분기입니다. 현재 판정과 이후 미도달 조건을 단계로 구분할 수 있습니다.
FLOWCHART · GUARD CLAUSES
오류 가드를 통과한 나이만 하나의 라벨로 출력된다
이메일과 나이 오류를 먼저 종료하면 1–120만 남는다. 정상 범위는 겹치지 않는 세 구간 중 하나로 분류하여 saved 문자열 한 줄을 출력합니다.
1 · 위에서부터 오류를 검사합니다
- 이메일이 비어 있는가?
email.isBlank()true →
email is required출력 후 종료false → 다음 나이 검사
- 나이가 0 이하인가?
age <= 0true →
age must be positive출력 후 종료false → 상한 검사
- 나이가 120을 초과하는가?
age > 120true →
age must be at most 120출력 후 종료false → 정상 범위 1–120으로 진행
2 · 남은 범위에서 라벨 하나를 고릅니다
underage
adult
senior
else-if는 처음 true인 구간 하나만 실행합니다. 정상 범위에는 빈틈과 중복이 없습니다.
3 · saved 결과 한 줄을 출력합니다
saved=email:age:label
본문 코드는 System.out.println으로 결과를 보여줍니다. 실제 저장소에 회원을 기록하는 기능은 아직 없습니다.
세 오류 guard가 모두 false라는 사실이 정상 범위 1–120의 전제가 된다.
이 예의 조건
- 정수 변환 성공을 전제합니다. saved는 콘솔 문자열이며 저장소 기록이 아닙니다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 2 | 이메일/나이 세 오류 guard는 참이면 출력 후 종료, 거짓만 다음 검사; 정상 1–120은 underage/adult/senior 중 하나→saved 출력(저장 아님) |
| 구분 | 내용 |
|---|---|
| 설명 | 이메일/나이 세 오류 guard는 참이면 출력 후 종료, 거짓만 다음 검사; 정상 1–120은 underage/adult/senior 중 하나→saved 출력(저장 아님) |
| 전체 읽기 정보 | 오류 가드를 통과한 나이만 하나의 라벨로 출력된다 회원가입 오류 guard와 배타적 나이 분류 흐름 FLOWCHART · GUARD CLAUSES 오류 가드를 통과한 나이만 하나의 라벨로 출력된다 이메일과 나이 오류를 먼저 종료하면 1–120만 남는다. 정상 범위는 겹치지 않는 세 구간 중 하나로 분류하여 saved 문자열 한 줄을 출력합니다. 1 · 위에서부터 오류를 검사합니다 이메일이 비어 있는가? email.isBlank() true → email is required 출력 후 종료 false → 다음 나이 검사 나이가 0 이하인가? age <= 0 true → age must be positive 출력 후 종료 false → 상한 검사 나이가 120을 초과하는가? age > 120 true → age must be at most 120 출력 후 종료 false → 정상 범위 1–120으로 진행 2 · 남은 범위에서 라벨 하나를 고릅니다 1–17 underage 18–64 adult 65–120 senior else-if는 처음 true인 구간 하나만 실행합니다. 정상 범위에는 빈틈과 중복이 없습니다. 3 · saved 결과 한 줄을 출력합니다 saved=email:age:label 본문 코드는 System.out.println으로 결과를 보여줍니다. 실제 저장소에 회원을 기록하는 기능은 아직 없습니다. 세 오류 guard가 모두 false라는 사실이 정상 범위 1–120 의 전제가 된다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 오류 가드 공백 email, age≤0, age>120을 차례로 검사합니다. |
|
|
| 라벨 선택 남은 age 1–120에서 두 경계를 순서대로 검사합니다. |
|
|
| saved 출력 email:age:label 형태를 출력합니다. |
|
|
조건 경계표
| 입력 | 첫 참 조건 | 결과 |
|---|---|---|
| 이메일 없음 | email.isBlank() | 이메일 오류 |
| 0세 | age <= 0 | 양수 오류 |
| 1세 | 앞 오류 모두 거짓 | underage 저장 |
| 18세 | age < 65 | adult 저장 |
| 65세 | 마지막 else | senior 저장 |
| 120세 | 마지막 else | senior 저장 |
| 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을 각각 실행해 조건 순서를 확인합니다.
해설 보기
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을 앞에 놓으면 뒤 라벨에 도달할 수 없으므로 작은 범위부터 위에서 제거합니다.