<aside> 💡

Scanner에 들어가는 System.in 도대체 무엇일까?

System.in의 의미

1. 기본 데이터 타입 읽기

2. 문자열 읽기

3. 파일에서 입력 읽기

Scanner는 파일에서 데이터를 읽는 용도로도 사용할 수 있습니다. 파일을 읽을 때도 next(), nextInt() 등 일반적인 메서드를 그대로 사용할 수 있으며, 파일 끝을 체크하는 메서드도 제공합니다.

4. 입력 유무 체크

입력이 더 남아 있는지 여부를 확인하기 위한 메서드들로, 반복문을 사용할 때 유용합니다.

Scanner는 말그대로 무언가 입력 받도록 변환해주는 클래스이고 System.in으로 입력 스트림을 받아야 한다는 것이 핵심이다.

</aside>

<aside> 💡

Console과 scanner은 그래서 어떤 관계인데? 근데 이거 여기서만 쓰는거네…ㅅㅂ…

</aside>

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package camp.nextstep.edu.missionutils;

import java.util.Scanner;

public class Console {
    private static Scanner scanner;

    private Console() {
    }

    public static String readLine() {
        return getInstance().nextLine();
    }

    public static void close() {
        if (scanner != null) {
            scanner.close();
            scanner = null;
        }

    }

    private static Scanner getInstance() {
        if (scanner == null) {
            scanner = new Scanner(System.in);
        }

        return scanner;
    }
}

<aside>

콜솔이라는 객체는 Scanner를 getInstance를 통해서 싱글톤으로 관리하고 있다. 따라서 Console을 한번 만들고 나면 Console을 닫기 전까지는 같은 Scanner와 같은 입력 스트림을 사용하게 된다. 또 close()를 하면 문제인게 Scanner 같은 객체는 만들어도 그만이지만 System.in 이 생기는 것이 아니기 때문에 문제가 될수도 있다고 한다.

</aside>

<aside> 💡

Test 트러블 슈팅 해결

</aside>

package racingcar.view;

import camp.nextstep.edu.missionutils.Console;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.io.ByteArrayInputStream;
import java.util.List;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

class InputViewTest {

    private InputView inputView = new InputView();

    @AfterEach
    void set() {
        Console.close();;
    }

    @Test
    @DisplayName("inputCarNames 테스트")
    void inputCarNames() {
        //given
        String readLine = "Car1,Car2,Car3";
        System.setIn(new ByteArrayInputStream(readLine.getBytes()));

        //when
        List<String> carNames = inputView.inputCarnames();

        //then
        assertThat(carNames)
                .isEqualTo(List.of("Car1", "Car2", "Car3"));
    }

    @Test
    @DisplayName("inputGameTokens 테스트")
    void inputGameTokens() {
        //given
        String readLine = "5";
        System.setIn(new ByteArrayInputStream(readLine.getBytes()));

        //when
        int gameTokens = inputView.inputGameTokens();

        //then
        assertThat(gameTokens)
                .isEqualTo(5);
    }

<aside>

다음 코드에서 보게 되면 Test는 현재 Console 이 없는 상태이다 System.In은 존재하고 있다.이에 따라서 System.setOn을 Console 생성 전에 바꿔주고 Console.readLine을 호출하면 자동차 리스트가 들어간Console이 생기게 된다. Console은 싱글톤이기 때문에 한번 주입된 System.in을 계속 사용할 것이다. 아래 테스트에도 같은 것을 사용하는 것이 문제다. 이에 따라서 Console을 없애버림으로써 다른 System.in을 주입할 수 있는 Console을 만들고자 했고. Console.close()를 하는 과정에서 내부의 인풋 스트림을 날려버렸기 때문에 다시 만들어 준것이다.

</aside>

<aside> 💡

Scanner에 주입될 수 있는 것들

다만! Console은 강제로 scanner에 System.in을 주입해버리기 때문에…

</aside>

<aside> 💡

동적 입력 스트림과 정적 입력 스트림!!! (중요)

1. System.in

2. ByteArrayInputStream

요약

이러한 차이로 인해 System.in은 콘솔로부터 동적인 입력을 받을 수 있는 반면, ByteArrayInputStream은 고정된 데이터를 다루는 데 유용합니다.

System.in 은 동적으로 입력을 계속 받을 수 있다.

</aside>