Theme:

프로그래밍에서 "흐름 제어"는 가장 기본적인 도구다. 조건에 따라 다른 코드를 실행하고, 같은 작업을 반복하고, 특정 조건에서 빠져나오고 — 이 세 가지를 할 수 있으면 어떤 로직이든 만들 수 있다. Java의 제어문과 반복문을 한 번에 정리해보자.

조건문 — if/else

기본 구조

JAVA
int score = 85;

if (score >= 90) {
    System.out.println("A");
} else if (score >= 80) {
    System.out.println("B");
} else if (score >= 70) {
    System.out.println("C");
} else {
    System.out.println("F");
}
// 출력: B
  • 위에서 아래로 조건을 검사하고, 처음 true가 되는 블록만 실행한다.
  • else는 선택이다. 모든 조건에 해당하지 않을 때 실행된다.

중괄호를 생략하면?

JAVA
if (score >= 90)
    System.out.println("A");
    System.out.println("축하합니다"); // 이건 항상 실행됨!

중괄호 없이 쓰면 바로 다음 한 줄만 if에 속한다. 두 번째 줄은 if와 무관하게 항상 실행된다. 실수하기 딱 좋은 구조이므로, 중괄호를 항상 쓰는 게 좋다.

삼항 연산자

간단한 조건은 삼항 연산자로 한 줄에 쓸 수 있다.

JAVA
int score = 85;
String grade = (score >= 90) ? "A" : "B 이하";
// grade = "B 이하"

조건 ? 참일 때 값 : 거짓일 때 값 형태다. 간결하지만 중첩하면 가독성이 급격히 떨어지므로 한 단계만 쓰는 게 좋다.

조건문 — switch

여러 값 중 하나를 선택할 때는 switchif-else if 체인보다 깔끔할 수 있다.

전통적인 switch

JAVA
int day = 3;

switch (day) {
    case 1:
        System.out.println("월요일");
        break;
    case 2:
        System.out.println("화요일");
        break;
    case 3:
        System.out.println("수요일");
        break;
    default:
        System.out.println("기타");
        break;
}
// 출력: 수요일

break를 빼먹으면?

JAVA
switch (day) {
    case 1:
        System.out.println("월요일");
        // break 없음 → 아래로 쭉 실행 (fall-through)
    case 2:
        System.out.println("화요일");
    case 3:
        System.out.println("수요일");
}
// day가 1이면: 월요일, 화요일, 수요일 모두 출력!

break를 빼먹으면 다음 case로 쭉 떨어진다. 이걸 fall-through라고 한다. 의도적으로 쓰는 경우도 있지만 대부분은 실수다.

Enhanced switch (Java 14+)

Java 14부터는 화살표() 문법으로 fall-through 없는 switch를 쓸 수 있다.

JAVA
int day = 3;

switch (day) {
    case 1 -> System.out.println("월요일");
    case 2 -> System.out.println("화요일");
    case 3 -> System.out.println("수요일");
    case 4 -> System.out.println("목요일");
    case 5 -> System.out.println("금요일");
    case 6, 7 -> System.out.println("주말");
    default -> System.out.println("잘못된 입력");
}
  • break가 필요 없다 — 화살표 문법은 자동으로 해당 case만 실행
  • 여러 값을 콤마로 묶을 수 있다 (case 6, 7)

switch 표현식

값을 반환하는 switch도 쓸 수 있다.

JAVA
String dayName = switch (day) {
    case 1 -> "월요일";
    case 2 -> "화요일";
    case 3 -> "수요일";
    case 4 -> "목요일";
    case 5 -> "금요일";
    case 6, 7 -> "주말";
    default -> "잘못된 입력";
};

System.out.println(dayName); // 수요일

여러 줄이 필요하면 yield를 쓴다.

JAVA
String description = switch (day) {
    case 1, 2, 3, 4, 5 -> {
        String name = getDayName(day);
        yield name + " (평일)";
    }
    case 6, 7 -> "주말";
    default -> "잘못된 입력";
};

switch에 쓸 수 있는 타입

JAVA
// Java 7 이전: byte, short, char, int
// Java 7+: String 추가
// Java 14+: 화살표 문법
// Java 21+: 패턴 매칭 (record, sealed class 등)

String command = "start";
switch (command) {
    case "start" -> System.out.println("시작");
    case "stop" -> System.out.println("정지");
    case "pause" -> System.out.println("일시정지");
    default -> System.out.println("알 수 없는 명령");
}

반복문 — for

기본 for문

JAVA
for (int i = 0; i < 5; i++) {
    System.out.println(i);
}
// 0, 1, 2, 3, 4

구조: for (초기화; 조건; 증감)

  • 초기화: 반복 변수 선언 (int i = 0)
  • 조건: true인 동안 반복 (i < 5)
  • 증감: 매 반복 후 실행 (i++)

향상된 for문 (for-each)

배열이나 컬렉션을 순회할 때는 for-each가 깔끔하다.

JAVA
int[] numbers = {10, 20, 30, 40, 50};

for (int num : numbers) {
    System.out.println(num);
}

for (타입 변수 : 배열또는컬렉션) 형태다. 인덱스가 필요 없을 때 쓰면 좋다.

JAVA
List<String> names = List.of("Alice", "Bob", "Charlie");

for (String name : names) {
    System.out.println(name);
}

인덱스가 필요하면?

for-each에서는 인덱스에 접근할 수 없다. 인덱스가 필요하면 기본 for문을 쓴다.

JAVA
String[] names = {"Alice", "Bob", "Charlie"};

for (int i = 0; i < names.length; i++) {
    System.out.println(i + ": " + names[i]);
}
// 0: Alice
// 1: Bob
// 2: Charlie

반복문 — while / do-while

while

조건이 true인 동안 반복한다.

JAVA
int count = 0;

while (count < 5) {
    System.out.println(count);
    count++;
}
// 0, 1, 2, 3, 4

반복 횟수가 정해져 있으면 for, 조건 기반이면 while이 자연스럽다.

JAVA
// 사용자 입력을 "quit"가 나올 때까지 받기
Scanner scanner = new Scanner(System.in);
String input = "";

while (!input.equals("quit")) {
    System.out.print("입력: ");
    input = scanner.nextLine();
    System.out.println(">> " + input);
}

do-while

do-while최소 한 번은 실행한다는 점이 while과 다르다.

JAVA
int count = 10;

// while: 조건 먼저 검사 → 한 번도 실행 안 됨
while (count < 5) {
    System.out.println("while: " + count);
    count++;
}

// do-while: 일단 한 번 실행 → 그 다음 조건 검사
count = 10;
do {
    System.out.println("do-while: " + count);
    count++;
} while (count < 5);
// 출력: do-while: 10

실무에서 do-while은 상대적으로 드물게 쓰이지만, "최소 한 번은 실행해야 하는" 로직에서 유용하다.

무한 루프

의도적으로 무한 반복을 만들 때가 있다. 서버가 요청을 계속 대기하거나, 게임 루프 같은 경우다.

JAVA
// 방법 1: while(true)
while (true) {
    // 작업 수행
    if (종료조건) break;
}

// 방법 2: for(;;)
for (;;) {
    // 작업 수행
    if (종료조건) break;
}

둘 다 똑같이 동작하지만, while (true)가 더 흔하고 의도가 명확하다.

break와 continue

break — 반복문 탈출

JAVA
for (int i = 0; i < 10; i++) {
    if (i == 5) break; // i가 5면 반복 중단
    System.out.println(i);
}
// 0, 1, 2, 3, 4

continue — 다음 반복으로 건너뛰기

JAVA
for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) continue; // 짝수면 건너뜀
    System.out.println(i);
}
// 1, 3, 5, 7, 9

레이블(Label) — 중첩 반복문 탈출

중첩 반복문에서 바깥 반복문을 탈출하고 싶을 때 레이블을 쓴다.

JAVA
outer:
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (i == 1 && j == 1) {
            break outer; // 바깥 for문까지 탈출
        }
        System.out.println(i + ", " + j);
    }
}
// 0, 0
// 0, 1
// 0, 2
// 1, 0

레이블 없이 break만 쓰면 안쪽 for문만 탈출한다. 레이블은 복잡한 중첩 구조에서 유용하지만, 너무 많이 쓰면 코드가 읽기 어려워진다. 가능하면 메서드를 분리하는 게 낫다.

실전 패턴

패턴 1: 배열에서 특정 값 찾기

JAVA
int[] numbers = {3, 7, 2, 9, 5};
int target = 9;
int index = -1;

for (int i = 0; i < numbers.length; i++) {
    if (numbers[i] == target) {
        index = i;
        break; // 찾으면 즉시 탈출
    }
}

System.out.println(index); // 3

패턴 2: 중첩 루프로 구구단

JAVA
for (int i = 2; i <= 9; i++) {
    System.out.println("--- " + i + "단 ---");
    for (int j = 1; j <= 9; j++) {
        System.out.printf("%d x %d = %d%n", i, j, i * j);
    }
}

패턴 3: 입력 검증 루프

JAVA
Scanner scanner = new Scanner(System.in);
int age;

while (true) {
    System.out.print("나이를 입력하세요 (1-150): ");
    age = scanner.nextInt();
    if (age >= 1 && age <= 150) break;
    System.out.println("잘못된 입력입니다. 다시 입력해주세요.");
}

System.out.println("입력한 나이: " + age);

패턴 4: 이중 루프에서 조건 필터링

JAVA
int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// 5보다 큰 값만 출력
for (int[] row : matrix) {
    for (int value : row) {
        if (value <= 5) continue;
        System.out.println(value);
    }
}
// 6, 7, 8, 9

자주 하는 실수

1. 무한 루프에 빠지기

JAVA
int i = 0;
while (i < 10) {
    System.out.println(i);
    // i++ 을 빼먹으면 영원히 0만 출력
}

2. off-by-one 에러

JAVA
int[] arr = {10, 20, 30};

// 잘못된 코드 — arr[3]은 ArrayIndexOutOfBoundsException
for (int i = 0; i <= arr.length; i++) {
    System.out.println(arr[i]);
}

// 올바른 코드 — < 사용
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

배열 인덱스는 0부터 시작하고, length는 1부터 센 개수다. 그래서 i < arr.length가 맞다 (<=가 아니라).

3. switch에서 break 빠뜨리기

JAVA
// 화살표 문법을 쓰면 이 실수를 원천 차단할 수 있다
switch (value) {
    case 1 -> doFirst();
    case 2 -> doSecond();
    default -> doDefault();
}

TIP 이 글의 코드 예제를 직접 실행해보고 싶다면 Java 기본기 핸드북을 확인해보세요.

정리

  • if/else: 조건 분기의 기본. 중괄호를 항상 쓰자
  • switch: 여러 값 비교 시 유용. Java 14+에서는 화살표 문법으로 fall-through 방지
  • for: 정해진 횟수 반복. 배열/컬렉션 순회 시 for-each 활용
  • while: 조건 기반 반복. do-while은 최소 한 번 실행 보장
  • break/continue: 반복 흐름 제어. 레이블은 중첩 탈출 시에만
  • Enhanced switch: 가능하면 화살표 문법을 쓰자 — break 실수를 방지하고 코드도 깔끔하다

다음 글에서는 메서드와 배열을 다룬다. 코드를 나누는 방법(메서드)과 데이터를 묶는 방법(배열)은 어떤 프로그램이든 기본이 되는 주제다.

댓글 로딩 중...