본문 바로가기
Java/Java Basic

[Java Basic] 11. 입력값 받기 - System.in과 Scanner 클래스

by Rosmary 2022. 7. 3.
728x90
반응형

 

 

 

지금까지 Java의 기본적인 내용을 포스팅하면서, 변수를 생성하고 출력하는 부분에 대해서만 예시로 많이 보여주었는데, 사용자를 위해 개발되는 프로그램을 떠올려보면 사용자로부터 값을 입력받아 동작해야하는 부분이 꽤 많다. 예를 들면 프로그램 사용을 위해 로그인을 할 때 ID나 비밀번호를 입력하거나, 프로그램 내에서 키워드를 이용하여 저장된 정보를 조회하거나 변경하는 등의 작업 등등등...

 

이번 포스팅에서는 Java 프로그램의 기본 사용 마지막 내용인 "키보드로 값 입력 받기"에 대해 알아보려 한다.

 

 

1. 프로그램에서 키보드로 값 입력 시 컴퓨터에서 일어나는 일들.

 

키보드로 값을 입력받는 것은 어떠한 과정을 통해 이루어질까? 필자가 어떠한 프로그램에서 키보드로 'Hello'를 입력하면 입력한 'Hello'라는 값은 프로그래밍으로 넘어가기 전 컴퓨터의 입력버퍼라는 곳에 저장된다. 이 입력버퍼라는 것은 운영체제(OS)에 의해 제공되는 컴퓨터 메모리 장치의 일부 공간으로, 값을 임시로 보관하는 기능을 한다. 그리고 입력한 값은 입력 스트림(Input Stream)이라고 하는데, 마치 데이터가 흐르는 물과 같아 붙은 이름이다. 또한 키보드는 컴퓨터에 입력값을 입력하는 표준 입력장치로, 표준 입력장치로 입력된 입력 스트림을 표준입력스트림(Standard Input Stream)이라고 별도로 구분한다.

 

 

입력이 완료되어 엔터키(\n)를 입력한다. 입력버퍼의 경우, Enter가 입력되면 입력버퍼에 저장된 글자를 어플리케이션(프로그램)으로 넘기게 된다. 이렇게 넘어간 입력값은 지정된 변수에 저장되거나 연산에 사용되는 등의 작업에 투입된다. 아래의 예시는 단순히 입력값은 input이라는 변수에 저장하는 과정을 나타낸 것이다.

 

 

위의 과정을 살펴보았을 때, Java에서 값을 입력받기 위해 필요한 것은 두 가지이다. 하나는 표준입력스트림을 받을 수 있는 클래스나 메서드, 또는 변수가 존재해야 할 것이고, 다른 하나는 입력버퍼를 조절할 수 있는 클래스나 메서드, 또는 변수다. Java에서는 이들을 각각 System.in과 Scanner 클래스를 사용하여 처리하도록 되어 있다.

 

** 참고로 화면 출력 역시 입력과 별반 다르지 않은데 화면 출력 시, 프로그램에서는 출력할 값을 출력 버퍼라 불리는 메모리 공간에 저장하고, 이 출력 스트림 중 Enter(\n)가 감지되면 출력 버퍼 내용을 화면에 표시하는 것이다.

 

 

2. 표준입력스트림을 저장하는 System.in

 

화면 출력 시 사용하는 매서드인 print(), println(), printf()는 System 클래스 내에 위치한다. System 클래스는 Java 실행 시 기본으로 Import 되는 모듈과 패키지 내에 존재한다(모듈과 패키지도 추후 포스팅 예정이다). 이 시스템 클래스는 여러 기능을 담당하지만, 압도적으로 표준입출력 데이터를 다루기 위해 사용하는 경우가 많다.   

 

[ System 클래스 상위 패키지 및 모듈 ]

 - 기본모듈: java.base

 - 기본 패키지: java.lang

 

 

화면 출력. 즉 표준 출력 스트림을 다룰 때 사용하는 System의 필드는 out이다. 따라서 print 관련 매서드 사용 시, 매서드 앞에 System.out.이 항상 붙어있던 것이다. 여기서 추론이 빠른 분들이라면 표준입력스트림을 담당하는 System 필드는 in이라는 것을 대략적으로 눈치 채셨을 것이다. 

 

 

자세히 보면 System은 .in 뒤에 별도의 매서드(함수)가 없다. 이 말은, 키보드로 입력받는 모든 값은 System.in이라는 변수에 저장된다는 말이다. 여기까지 내용을 이해했다면, 키보드 값을 입력받기 위해 아래와 같이 코드를 작성하면 되지 않을까 하고 생각한 분들도 있을 것이다.

 

 

하지만, 의외로 이 코드를 실행해보면, 값을 입력받는 절차가 나타나는 것이 아니라 이상한 문자열이 결과값으로 툭 튀어나온다.

 

 

필자의 배열 관련 포스팅을 보셨던 분들이라면 대충 눈치채셨겠지만, 이 주소는 표준입력스트림을 저장하는 메모리의 주소다. 따라서, 입력값을 출력하기위해, 이 System.in이라는 메모리 주소를 인자로 버퍼 저장된 값을 출력하는 클래스와 매서드가 존재해야 한다. 따라서 입력버퍼를 다룰 수 있는 별도의 코드가 필요하게 되는데, 이 때 사용하는 Java의 코드가 Scanner라는 클래스에 정의되어 있다.

 

 

3. 입력 버퍼를 통제하는 Scanner 클래스

 

그럼 입력버퍼는 어떻게 통제를 할까? 입력버퍼를 다루는 클래스는 Scanner라는 이름을 가지고 있는 클래스인데, java.util 패키지 내에 존재한다. 

 

 

Java의 Documentation 문서에는 Scanner 클래스에 대한 설명 및 사용 예시가 나와 있는데, 첫 코드를 잘 보면 System.in을 인자로 Scanner 타입의 변수를 하나 생성하는 것이 보인다. 즉, Scanner 클래스는 입력 버퍼를 whitespace(공백, 엔터)로 구분하여 버퍼에 저장된 입력값을 인지하는 기능을 한다.

 

그럼, 위의 내용을 참고하여 코드를 작성해보자. 하지만 여전히 콘솔에서 입력값을 받을 수는 없다.

 

 

이 역시, 입력 버퍼 내에 존재하는 값을 출력한 것이 아니라 scanner 라는 이름의 Scanner 객체를 출력해서 나타나는 결과다. 입력버퍼에 저장된 내용을 출력하기 위해서는 Scanner 클래스 내에 정의된 매서드를 사용해야 한다. 필자가 조금 전에 Java의 Scanner Documentation 문서로 이동하는 링크를 달아놓았는데, 해당 페이지를 아래로 내리다보면 Scanner 클래스에서 사용 가능한 Method 중 next로 시작하는 매서드들이 몇 개 있다. 이 next로 시작하는 매서드들이 입력버퍼에 저장된 값을 호출하는 매서드들이다. next() 매서드를 scanner 뒤에 추가하고 코드를 실행해보자. 코드 실행 후 콘솔에 아무것도 뜨지 않을텐데, 여기에 Hello World라는 단어를 입력하고 Enter를 눌러보자.

 

 

 

 

3. 입력 버퍼 저장값의 화면 출력 관련 Scanner 매서드

 

그럼, 입력버퍼에 저장된 값을 화면으로 출력해주는 매서드에 대해 알아보자. next로 시작하는 매서드가 이 기능을 가지며, Java Documentation에 정의된 종류는 아래와 같다.

 

 

 

next로 시작하는 매서드의 목록을 보면 규칙이 보이는데, next() 및 nextLine() 매서드를 제외한 나머지는 뒤에 Java의 기본형 또는 참조형 변수명이 붙어있다. 

 

-  기본형: Boolean, Byte, Double, Float, Int, Long, Short

-  참조형: BigDecimal, BigInteger   (java.lang 모듈, java.math 패키지에 위치하는 클래스. 큰 수를 다뤄야 할 경우 사용함)

 

 

뒤에 기본형 또는 참조형 변수타입이 붙는 매서드는 입력값을 whitespace로 구분한 뒤, 해당 변수로 변환 가능한 입력값 내용을 출력한다. 변수 타입이 붙는 매서드의 동작을 알기 위해서 먼저 next()와 nextLine() 매서드의 동작 절차와 결과를 알아야한다. 

 

 

(1) Scanner.next() 매서드

 

Scanner 변수로 지정한 scanner 뒤에 .next() 매서드를 추가하여 결과를 확인해보자. 입력값은 "Hello World 123"이며, 이 중 Hello 만 화면에 출력될 것이다.

 

 

next()는 입력값을 공백으로 구분한 뒤, 맨 처음 나타나는 단어를 입력버퍼를 화면에 출력한다. 이 때 화면에 출력한 단어는 입력버퍼에서 사라진다.

 

 

 

그럼, next() 매서드를 여러 번 사용하면 어떻게 될까? 예상하듯이, Hello 다음에는 World가 출력되고, 그 다음에는 123이 출력된다.

 

 

입력버퍼에서는 여전히 남아있는 입력값을 whitespace로 구분한 뒤, 순차적으로 출력을 진행한다.

 

 

계속해서 next() 매서드를 사용하면 어떻게 될까? 당연히 입력 버퍼에 저장된 값이 소진되어 있기 때문에 출력할 것이 없어 아무런 일이 일어나지도 않는다.

 

 

 

참고로 이 next() 매서드는 Scanner의 hasNext() 매서드와도 연관이 꽤 깊다. hasNext는 입력버퍼 내에 값이 남아있는지의 여부를 Boolean 값으로 반환하는 매서드다. 

 

 

hasNext() 매서드와 next() 매서드를 사용하면, 공백을 포함하여 입력한 문자열에 대해서도 전부 출력이 가능하다.

 

 

 

 

 

(2) nextLine() 매서드

 

그런데, 입력한 한 줄을 전부 출력하는데 next()와 hasNext() 매서드만을 사용하는 것이 영 비효율적이다. 늘 그래왔듯이 한 줄을 한 번에 출력하는 매서드도 Scanner 클래스 내에 정의되어 있다. 바로 nextLine() 매서드다. 

 

이 매서드는 입력 버퍼 내에서 Enter(\n) 입력지점까지의 모든 입력값을 버퍼에서 삭제하고 프로그램으로 이동시킨다. 사용 방법은 next() 매서드와 동일하다. 

 

조금 전에 보았던 while 코드와 동일한 결과가 나타난다.

 

굳이 while 문과의 차이를 찾으라고 하면, nextLine()은 버퍼 출력 후 '\n'으로 인해 줄바뀜이 일어난다는 것이다.

 

 

 

(3) next변수형타입()

 

 

이번에는 입력값의 순서를 바꿔 123 Hello World로 입력하려한다. 그리고 앞에 위치한 123만 출력하되, 해당 내용이 정수로 저장되도록 하려한다.

 

 

여기서 nextInt() 매서드를 한 번 더 사용하면 어떻게 될까? 정수형 입력값이 없으니 출력이 안일어나지 않을까 싶지만, 의외로 에러가 발생한다.

 

 

위의 코드 결과를 통해 한 가지 확실히 확인할 수 있는 것이 있는데, 특정 변수형 입력값만을 추출하는 매서드(nextInt, nextFloat() 등)는 먼저 입력버퍼의 값을 불러들인 뒤, 자신들이 변환해야 하는 변수형으로 형 변환을 진행하는 절차를 거친다는 것이다. 따라서 형 변환이 불가능한 값이 감지되면 위와 같이 에러가 나타나는 것이고 말이다.

 

 

 


 

 

날짜의 년, 월, 일과 같이 입력값이 명확한 변수형 타입으로 떨어지지 않는 경우, 보통 nextLine()으로 입력받은 값을 별도의 변수에 저장하고, 이 변수를 정규표현식 등으로 일치 여부를 확인하는 방식으로 입력값 처리를 많이 진행한다. 아직 정규표현식에 대해서 배우지 않았기 때문에, 정규표현식이란 것을 사용하여 입력값을 부분별로 구분할 수 있는 기능을 Java에서 제공한다고만 우선 알고 있자.

 

 

다음 포스팅에서는, Java의 객체지향 특성을 부여하는 클래스에 대해 포스팅하려한다. 클래스의 경우 Java 사용 시 가장 중요한 골격과 같은 부분이기 때문에 배열과 마찬가지로 여러 개의 포스팅을 작성할 예정이다.

 

 

 

Fin. 

 

 

반응형

댓글