nextInt 뒤에 nextLine을 ‘한 번 더 소비’하면 빈 문자열 문제는 사라져도, 같은 줄에 있던 실제 이름을 버릴 수 있습니다. 숫자와 이름이 같은 줄인지, 다음 줄인지 입력 계약부터 확인하세요. nextLine은 의미 없는 개행만 버리는 함수가 아니라 현재 위치부터 줄 끝까지의 내용을 돌려주는 함수입니다.
이미 Java 기본 구조와 Scanner 입력에서 빈 nextLine의 원인을 확인했다면, 이번에는 그 해결책을 무조건 적용했을 때 생기는 데이터 손실을 점검합니다. Temurin OpenJDK 21.0.11에서 같은 줄·다음 줄·CRLF·빈 줄·입력 끝을 실행해 비교했습니다.
1. 소비했다고 버린 문자열에 이름이 들어 있을 수 있습니다
import java.util.Scanner;
public class InputContract {
static void probe(String label, String input) {
try (Scanner sc = new Scanner(input)) {
int age = sc.nextInt();
String rest = sc.hasNextLine() ? sc.nextLine() : "<EOF>";
String next = sc.hasNextLine() ? sc.nextLine() : "<EOF>";
System.out.println(label + ": age=" + age
+ ", rest=[" + rest + "], next=[" + next + "]");
}
}
public static void main(String[] args) {
probe("same-line", "3 Alice\nBob\n");
probe("next-line", "3\nAlice\n");
probe("CRLF", "3\r\nAlice\r\n");
probe("blank-line", "3\n\nAlice\n");
probe("end-of-input", "3");
}
}
same-line: age=3, rest=[ Alice], next=[Bob]
next-line: age=3, rest=[], next=[Alice]
CRLF: age=3, rest=[], next=[Alice]
blank-line: age=3, rest=[], next=[]
end-of-input: age=3, rest=[<EOF>], next=[<EOF>]
같은 줄 입력 3 Alice에서는 rest가 빈 문자열이 아니라 앞 공백을 포함한 ‘ Alice’입니다. 이 값을 버리고 다음 nextLine을 이름으로 저장하면 원래 이름 Alice 대신 다음 줄 Bob을 읽습니다. 숫자 다음에 항상 빈 문자열이 나온다는 설명은 입력 형태를 생략한 것입니다.

2. 다음 줄을 읽는 규칙은 다음 줄 계약에만 적용합니다
입력이 ‘첫 줄 정수 하나, 둘째 줄 이름’으로 고정되어 있다면 nextInt 뒤의 nextLine은 첫 줄의 남은 부분을 소비하는 데 쓸 수 있습니다. 하지만 첫 줄의 나머지 부분을 버려도 되는 계약이어야 합니다. 예제의 CRLF도 다음 줄 이름 Alice를 읽지만, 빈 줄 사례는 둘째 줄 자체가 빈 문자열입니다.
빈 줄을 모두 건너뛰거나 trim 결과가 비었으면 계속 읽는 방식은 빈 문자열이 유효한 입력인 계약을 바꿉니다. ‘같은 줄 나머지가 비었으면 다음 줄로 넘어가라’는 자동 추측도, 한 줄 형식이 고정된 데이터에서 빈 이름을 다음 레코드로 대체할 수 있습니다. 공백 제거 여부도 필드의 규칙에 맞춰 결정하세요.

3. 줄 단위 입력으로 통일하면 경계가 분명해집니다
import java.util.Scanner;
public class WholeLine {
public static void main(String[] args) {
try (Scanner sc = new Scanner("3\nAlice\n")) {
if (!sc.hasNextLine()) {
throw new IllegalArgumentException("missing age line");
}
int age = Integer.parseInt(sc.nextLine().strip());
if (!sc.hasNextLine()) {
throw new IllegalArgumentException("missing name line");
}
String name = sc.nextLine();
System.out.println("age=" + age + ", name=[" + name + "]");
}
}
}
age=3, name=[Alice]
이 코드는 첫 줄 전체를 정수 필드로 읽어 변환하고, 다음 줄 전체를 이름으로 받는 계약입니다. 이름에는 strip을 적용하지 않았으므로 공백을 보존합니다. 같은 줄에 3 Alice를 넣으면 정수 변환에 실패합니다. 오류를 억지로 숨기는 대신, 코드와 입력 계약이 맞지 않는다는 사실을 드러냅니다.

4. 입력 끝과 잘못된 숫자는 별도 오류입니다
첫 실험의 입력 끝 사례에서는 숫자는 읽었지만 이름 줄이 없습니다. hasNextLine이 false이면 이름을 읽으려고 nextLine을 호출하지 않고 입력 부족을 처리해야 합니다. 대화형 콘솔에서는 hasNextLine도 입력을 기다릴 수 있으므로 예제처럼 이미 준비된 문자열을 읽는 상황과 구분하세요.
nextInt는 정수가 아닌 토큰이나 int 범위를 벗어난 값에 InputMismatchException을 낼 수 있고, 더 읽을 입력이 없으면 NoSuchElementException을 낼 수 있습니다. Integer.parseInt는 잘못된 정수 문자열이나 범위 초과에 NumberFormatException을 낼 수 있습니다. 이런 오류는 줄 경계 문제와 원인이 다르므로 ‘nextLine 한 줄 추가’로 해결되지 않습니다.
입력 오류를 좁히는 점검 순서
입력 예시 그대로 재현 → 같은 줄·다음 줄 계약 확인 → 버리는 문자열 출력 → 빈 줄·EOF·숫자 오류 구분 순서로 보세요. 이미 소비한 나머지 줄을 다시 복원할 수는 없습니다. 먼저 읽기 단위를 정하고, 유효한 값을 버리지 않는지 작은 입력으로 확인하는 것이 핵심입니다.
'개발 > 트러블슈팅' 카테고리의 다른 글
| AI가 만든 CSV 매출 보고서 검산하기: 환불·중복 주문을 놓치지 않는 방법 (0) | 2026.10.19 |
|---|---|
| asyncio에서 time.sleep을 쓰면 작업이 멈추는 이유: asyncio.sleep과 to_thread (0) | 2026.10.18 |
| Python dict.fromkeys에서 리스트가 같이 바뀌는 이유: 키별 값 생성하기 (0) | 2026.10.16 |
| Python subprocess.run이 실패를 그냥 지나갈 때: check·returncode·stderr 확인 (0) | 2026.10.15 |
| Python json.dumps에서 NaN이 나올 때: allow_nan=False와 비유한 수 검사 (0) | 2026.10.14 |
댓글