본문으로 건너뛰기

Ch 2.6 화면 입출력 (Console I/O)

지금까지 우리는 변수에 직접 값을 대입하여 화면에 정보를 출력해보았습니다. 이번 챕터에서는 원하는 형식으로 문장을 출력 하는 방법과 반대로 사용자가 키보드로 입력한 값을 읽어다가 변수에 저장 하는 방법을 배웁니다.

1. 출력 메서드 세 가지 비교

자바에서 콘솔에 출력하는 방법은 세 가지입니다.

메서드줄바꿈사용처
System.out.println()자동 줄바꿈일반 출력
System.out.print()줄바꿈 없음같은 줄에 이어서 출력
System.out.printf()없음 (\n 직접 추가)형식 지정 출력
public class OutputMethods {
public static void main(String[] args) {
// println: 출력 후 자동 줄바꿈
System.out.println("첫 번째 줄");
System.out.println("두 번째 줄");
System.out.println(); // 빈 줄 출력

// print: 줄바꿈 없이 이어서 출력
System.out.print("A");
System.out.print("B");
System.out.print("C");
System.out.println(); // 줄바꿈 추가

// printf: 형식 지정 출력
System.out.printf("이름: %s, 나이: %d세%n", "Alice", 20);
// %n은 플랫폼에 맞는 줄바꿈 (\n과 동일하지만 이식성 높음)
}
}

출력:

첫 번째 줄
두 번째 줄

ABC
이름: Alice, 나이: 20세

2. printf와 형식 지정자 (Format Specifier) 완전 정리

printf는 C언어에서 유래한 형식 지정 출력 메서드입니다. % 기호 뒤에 타입을 나타내는 문자를 붙여 사용합니다.

기본 형식 지정자

지정자타입설명예시
%d정수10진수 정수%d42
%o정수8진수 정수%o52
%x정수16진수 (소문자)%x2a
%X정수16진수 (대문자)%X2A
%f실수고정 소수점%f3.140000
%e실수지수 표기법 (소문자)%e3.140000e+00
%E실수지수 표기법 (대문자)%E3.140000E+00
%g실수더 짧은 방식 자동 선택-
%s문자열String%s"hello"
%S문자열대문자 String%S"HELLO"
%c문자char%cA
%b논리boolean%btrue
%n-줄바꿈 (플랫폼 독립)-
%%-% 문자 자체%%%
public class PrintfBasic {
public static void main(String[] args) {
int intVal = 42;
double dblVal = 3.14;
String strVal = "Java";
char charVal = 'J';
boolean boolVal = true;

System.out.printf("정수: %d%n", intVal);
System.out.printf("8진수: %o%n", intVal);
System.out.printf("16진수(소): %x%n", intVal);
System.out.printf("16진수(대): %X%n", intVal);
System.out.printf("실수: %f%n", dblVal);
System.out.printf("지수: %e%n", dblVal);
System.out.printf("문자열: %s%n", strVal);
System.out.printf("대문자: %S%n", strVal);
System.out.printf("문자: %c%n", charVal);
System.out.printf("논리: %b%n", boolVal);
System.out.printf("퍼센트: 50%%%n");
}
}

출력:

정수: 42
8진수: 52
16진수(소): 2a
16진수(대): 2A
실수: 3.140000
지수: 3.140000e+00
문자열: Java
대문자: JAVA
문자: J
논리: true
퍼센트: 50%

3. printf 플래그와 폭/정밀도 지정

형식 지정자에 추가 옵션을 붙여 출력 형식을 세밀하게 조정할 수 있습니다.

문법: %[플래그][폭][.정밀도]타입

플래그/옵션의미예시
숫자출력 폭(너비) 지정%10d → 10자리 오른쪽 정렬
-왼쪽 정렬%-10d → 10자리 왼쪽 정렬
0빈 자리를 0으로 채움%010d0000000042
+부호 항상 출력%+d+42 또는 -42
양수 앞에 공백% d 42
,천 단위 구분자%,d1,000,000
.숫자소수점 이하 자리수%.2f3.14
public class PrintfFlags {
public static void main(String[] args) {
int num = 42;
double d = 3.14159;
long big = 1_000_000;

// 폭 지정 (오른쪽 정렬 기본)
System.out.printf("[%10d]%n", num); // [ 42]
System.out.printf("[%-10d]%n", num); // [42 ]

// 0 패딩
System.out.printf("[%010d]%n", num); // [0000000042]

// 부호 표시
System.out.printf("[%+d]%n", num); // [+42]
System.out.printf("[%+d]%n", -num); // [-42]

// 천 단위 구분자
System.out.printf("[%,d]%n", big); // [1,000,000]

// 소수점 자리수
System.out.printf("[%.2f]%n", d); // [3.14]
System.out.printf("[%.5f]%n", d); // [3.14159]
System.out.printf("[%10.2f]%n", d); // [ 3.14]
System.out.printf("[%-10.2f]%n", d); // [3.14 ]

// 표 형식 출력 예제
System.out.printf("%-15s %5s %8s%n", "이름", "나이", "점수");
System.out.printf("%-15s %5d %8.2f%n", "김자바", 20, 95.5);
System.out.printf("%-15s %5d %8.2f%n", "이파이선", 22, 88.3);
System.out.printf("%-15s %5d %8.2f%n", "박씨샵", 21, 92.1);
}
}

출력:

[        42]
[42 ]
[0000000042]
[+42]
[-42]
[1,000,000]
[3.14]
[3.14159]
[ 3.14]
[3.14 ]
이름 나이 점수
김자바 20 95.50
이파이선 22 88.30
박씨샵 21 92.10

4. Scanner 클래스 상세

Scanner는 키보드 입력을 읽는 가장 기본적인 방법입니다. 사용 전에 import java.util.Scanner;를 선언해야 합니다.

Scanner 주요 메서드

메서드반환 타입설명
next()String공백 이전까지 한 단어 읽기
nextLine()String한 줄 전체 읽기 (Enter까지)
nextInt()int정수 읽기
nextLong()longlong 정수 읽기
nextDouble()double실수 읽기
nextFloat()floatfloat 실수 읽기
nextBoolean()booleantrue/false 읽기
hasNext()boolean다음 토큰 있는지 확인
hasNextInt()boolean다음 토큰이 int인지 확인
import java.util.Scanner;

public class ScannerMethods {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

// 다양한 타입 입력받기
System.out.print("이름 입력: ");
String name = scanner.nextLine(); // "홍길동 ABC" 입력 시 전체 읽음

System.out.print("나이 입력: ");
int age = scanner.nextInt(); // 정수만 읽음

System.out.print("키 입력: ");
double height = scanner.nextDouble(); // 실수 읽음

System.out.printf("이름: %s, 나이: %d, 키: %.1f cm%n",
name, age, height);

scanner.close();
}
}

5. Scanner 사용 시 주의사항 (nextLine 공백 문제)

nextInt(), nextDouble() 등은 숫자만 읽고 줄바꿈 문자(\n)를 버퍼에 남겨둡니다. 이후 nextLine()을 호출하면 버퍼에 남은 \n을 읽어 빈 문자열이 반환됩니다.

import java.util.Scanner;

public class ScannerPitfall {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

System.out.print("나이 입력: ");
int age = scanner.nextInt(); // "25\n" 입력 → 25 읽음, \n은 버퍼에 남음

// 문제 발생!
// System.out.print("이름 입력: ");
// String name = scanner.nextLine(); // \n을 읽어서 빈 문자열 반환!

// 해결 방법 1: nextInt() 후에 빈 nextLine()으로 버퍼 비우기
scanner.nextLine(); // 버퍼에 남은 \n 소비

System.out.print("이름 입력: ");
String name = scanner.nextLine(); // 이제 정상 동작

System.out.printf("이름: %s, 나이: %d%n", name, age);

scanner.close();
}
}
경고

Scanner 주의사항:

  1. nextInt()nextLine() 사용 시 버퍼 비우기 필수
  2. nextLine() 버퍼 비우기: scanner.nextLine(); 한 번 더 호출
  3. 또는 nextLine()만 사용하고 Integer.parseInt()로 변환하는 방법도 있음

더 안전한 방법: nextLine + parseInt 조합

import java.util.Scanner;

public class ScannerSafeMethod {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

// 모든 입력을 nextLine()으로 받고, 필요시 형변환
System.out.print("이름 입력: ");
String name = scanner.nextLine();

System.out.print("나이 입력: ");
int age = Integer.parseInt(scanner.nextLine().trim());

System.out.print("키 입력: ");
double height = Double.parseDouble(scanner.nextLine().trim());

System.out.printf("이름: %s, 나이: %d, 키: %.1f cm%n",
name, age, height);

scanner.close();
}
}

6. hasNext를 활용한 반복 입력

import java.util.Scanner;

public class ScannerLoop {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int sum = 0;
int count = 0;

System.out.println("숫자를 입력하세요 (종료: 빈 줄):");

// hasNextInt(): 다음 입력이 정수인지 확인
while (scanner.hasNextInt()) {
int num = scanner.nextInt();
sum += num;
count++;
System.out.println(" 입력됨: " + num + " (합계: " + sum + ")");
}

if (count > 0) {
System.out.printf("총 %d개, 합계: %d, 평균: %.2f%n",
count, sum, (double) sum / count);
}

scanner.close();
}
}

7. BufferedReader를 활용한 빠른 입력

경쟁 프로그래밍이나 대량 데이터 처리 시 BufferedReaderScanner보다 훨씬 빠릅니다.

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class BufferedReaderExample {
public static void main(String[] args) throws IOException {
// BufferedReader는 IOException을 던질 수 있으므로 예외 처리 필요
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

System.out.print("이름 입력: ");
String name = br.readLine(); // 한 줄 읽기

System.out.print("나이 입력: ");
int age = Integer.parseInt(br.readLine().trim()); // 정수 변환

System.out.print("키 입력: ");
double height = Double.parseDouble(br.readLine().trim());

System.out.printf("이름: %s, 나이: %d, 키: %.1f cm%n",
name, age, height);

br.close();
}
}

Scanner vs BufferedReader 비교

항목ScannerBufferedReader
속도느림빠름
사용 편의성편리 (타입별 메서드)번거로움 (모두 String)
예외 처리불필요IOException 처리 필요
사용 적합일반 입력대량 데이터, 알고리즘

8. System.err와 System.in

public class SystemStreams {
public static void main(String[] args) {
// System.out: 표준 출력 스트림 (콘솔 화면)
System.out.println("일반 출력");

// System.err: 표준 에러 스트림 (보통 콘솔에 빨간색으로 표시됨)
System.err.println("에러 메시지!"); // 에러나 경고 출력에 사용

// System.in: 표준 입력 스트림 (키보드)
// Scanner와 BufferedReader에서 System.in을 사용함
System.out.println("System.in 타입: " + System.in.getClass().getName());

// 에러 출력 활용 예
int divisor = 0;
if (divisor == 0) {
System.err.println("[ERROR] 나누는 수가 0입니다!");
} else {
System.out.println("결과: " + (10 / divisor));
}
}
}

9. try-with-resources로 Scanner 닫기

자원(Scanner, BufferedReader 등)은 사용 후 반드시 닫아야 합니다. try-with-resources 구문을 사용하면 자동으로 닫힙니다.

import java.util.Scanner;

public class TryWithResources {
public static void main(String[] args) {
// try-with-resources: 블록 종료 시 자동으로 scanner.close() 호출
try (Scanner scanner = new Scanner(System.in)) {
System.out.print("이름 입력: ");
String name = scanner.nextLine();

System.out.print("나이 입력: ");
int age = Integer.parseInt(scanner.nextLine().trim());

System.out.printf("안녕하세요, %s님! (%d세)%n", name, age);

} catch (NumberFormatException e) {
System.err.println("나이는 숫자로 입력해주세요: " + e.getMessage());
}
// 이 시점에서 scanner는 자동으로 닫혀있음
}
}

10. 실전 예제: 학생 정보 입력 프로그램

import java.util.Scanner;

public class StudentInfoInput {
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
System.out.println("╔════════════════════════════╗");
System.out.println("║ 학생 정보 입력 시스템 ║");
System.out.println("╚════════════════════════════╝");

// 기본 정보 입력
System.out.print("이름: ");
String name = scanner.nextLine().trim();

System.out.print("학번: ");
String studentId = scanner.nextLine().trim();

System.out.print("나이: ");
int age = Integer.parseInt(scanner.nextLine().trim());

System.out.print("학과: ");
String major = scanner.nextLine().trim();

// 과목 점수 입력
System.out.println("\n과목 점수를 입력하세요 (0~100):");
System.out.print("국어: ");
int korean = Integer.parseInt(scanner.nextLine().trim());

System.out.print("수학: ");
int math = Integer.parseInt(scanner.nextLine().trim());

System.out.print("영어: ");
int english = Integer.parseInt(scanner.nextLine().trim());

// 계산
int total = korean + math + english;
double average = (double) total / 3;

// 학점 계산
char grade;
if (average >= 90) grade = 'A';
else if (average >= 80) grade = 'B';
else if (average >= 70) grade = 'C';
else if (average >= 60) grade = 'D';
else grade = 'F';

// 결과 출력
System.out.println();
System.out.println("╔═══════════════════════════════════╗");
System.out.printf("║ %-33s║%n", "성적표");
System.out.println("╠═══════════════════════════════════╣");
System.out.printf("║ 이름: %-27s║%n", name);
System.out.printf("║ 학번: %-27s║%n", studentId);
System.out.printf("║ 나이: %-27s║%n", age + "세");
System.out.printf("║ 학과: %-27s║%n", major);
System.out.println("╠═══════════════════════════════════╣");
System.out.printf("║ 국어: %3d점%24s║%n", korean, "");
System.out.printf("║ 수학: %3d점%24s║%n", math, "");
System.out.printf("║ 영어: %3d점%24s║%n", english, "");
System.out.println("╠═══════════════════════════════════╣");
System.out.printf("║ 총점: %3d점 평균: %5.2f점 학점: %c ║%n",
total, average, grade);
System.out.println("╚═══════════════════════════════════╝");

} catch (NumberFormatException e) {
System.err.println("점수는 숫자로 입력해주세요!");
}
}
}

입력/출력 예시:

╔════════════════════════════╗
║ 학생 정보 입력 시스템 ║
╚════════════════════════════╝
이름: 김자바
학번: 20240001
나이: 20
학과: 컴퓨터공학과

과목 점수를 입력하세요 (0~100):
국어: 92
수학: 85
영어: 88

╔═══════════════════════════════════╗
║ 성적표 ║
╠═══════════════════════════════════╣
║ 이름: 김자바 ║
║ 학번: 20240001 ║
║ 나이: 20세 ║
║ 학과: 컴퓨터공학과 ║
╠═══════════════════════════════════╣
║ 국어: 92점 ║
║ 수학: 85점 ║
║ 영어: 88점 ║
╠═══════════════════════════════════╣
║ 총점: 265점 평균: 88.33점 학점: B ║
╚═══════════════════════════════════╝

11. 입출력 형식 정리 치트시트

public class CheatSheet {
public static void main(String[] args) {
// 자주 쓰는 printf 패턴
int n = 42;
double d = 3.14159;
String s = "Java";

// 정수 출력
System.out.printf("%d%n", n); // 42
System.out.printf("%5d%n", n); // [ 42] 오른쪽 정렬
System.out.printf("%-5d%n", n); // [42 ] 왼쪽 정렬
System.out.printf("%05d%n", n); // [00042] 0 패딩
System.out.printf("%,d%n", 1000000); // 1,000,000 천단위 구분

// 실수 출력
System.out.printf("%.2f%n", d); // 3.14
System.out.printf("%8.2f%n", d); // [ 3.14]
System.out.printf("%-8.2f%n", d); // [3.14 ]
System.out.printf("%e%n", d); // 3.141590e+00

// 문자열 출력
System.out.printf("%s%n", s); // Java
System.out.printf("%10s%n", s); // [ Java]
System.out.printf("%-10s%n", s); // [Java ]
System.out.printf("%.3s%n", s); // Jav (최대 3자리)
}
}

정리

  • println(): 출력 + 자동 줄바꿈
  • print(): 출력 (줄바꿈 없음)
  • printf(): 형식 지정 출력 (%d, %s, %f, %.2f, %10s 등)
  • Scanner: 키보드 입력 읽기, next(), nextLine(), nextInt(), nextDouble()
  • nextInt() 후 nextLine() 주의: 버퍼에 남은 \n 소비 필요
  • BufferedReader: 빠른 입력, readLine() + parseInt() 조합
  • try-with-resources: Scanner/BufferedReader 자동 close
  • System.err: 에러 메시지 출력용 스트림