JavaScript/문법 공부

Node JS 표준 입력 받기

한땀코딩 2020. 7. 27. 23:31

https://nodejs.org/api/readline.html

 

Readline | Node.js v14.6.0 Documentation

Readline# Source Code: lib/readline.js The readline module provides an interface for reading data from a Readable stream (such as process.stdin) one line at a time. It can be accessed using: const readline = require('readline'); The following simple exampl

nodejs.org

로컬 환경에서의 테스트나 백준 알고리즘 문제를 풀 때는 표준 입력을 구현해야 하는 경우가 종종 있다. 개인적으로 Java나 Python의 경우 비교적 명확한 표준 입력 방식이 있고, 보통 한두 줄 정도로 끝나기 때문에 깊게 생각해보지 않았는데, 자바스크립트의 경우, 정확히는 Node JS에서는 비동기라는 변수가 표준 입력을 까다롭게 만들고 있었다.

우선 내가 발견한 정답부터 적자면 아래와 같다. prompt 함수와 question 함수 두 가지로 구현했는데, 만약 검색해서 들어오신 분이라면 필요에 맞게 사용하면 될 것 같다. 조심할 부분은, 아래에서도 설명하겠지만 비동기 특성 때문에 다른 언어들처럼 표준 입력을 통해 들어온 값을 어떤 변수에 저장해두고 사용하는 것이 아니라 바로 콜백 함수에 넘겨주는 방식으로 활용하는 것이 편리하다 (정신 건강에 이로울지도...).

solution함수는 그냥 임의의 함수라고 생각하자. 표준 입력으로 받은 내용으로 하고 싶은 내용이 담긴 함수 정도로 이해하면 된다.

question 함수 활용

const readline = require("readline");

const r1 = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

r1.question('', (input) => {
  solution(input);
  r1.close();
});

 

prompt, on('line') 활용

const readline = require("readline");

const r1 = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

r1.prompt();

r1.on("line", (input) => {
  solution(input);
  r1.close();
}).on("close", function () {
  process.exit();
});

 

무엇이 문제인가

위에서도 설명했지만, 기존에 사용하던 다른 언어들처럼 표준 입력으로 받아온 내용을 변수에 담아두고 그 변수를 함수에 호출하는 방식으로 문제를 해결하려고 했다. 문제는 Node JS는 비동기고, readline 함수들도 마찬가지라는 점.

단적인 예시는 아래와 같다.

const readline = require("readline");

const r1 = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

let val = null;

r1.question('', (input) => {
	val = input;
	r1.close();
});

console.log(val);

이렇게 작성했을 때, 동기적인 코드였다면 표준 출력은 input에 담긴 값이었을 것이다. 그러나 실제로 출력해보면 null이 나온다. 즉 값을 담아오는 걸 실행하고 있는 중에 이미 console.log 함수가 비동기적으로 실행되면서 의도한 대로 동작하지 않는다는 것이다.

위와 관련하여 해결 방법이 담긴 내용을 찾기가 어려웠던 것은 단순히 입력한 값을 받아오는 건 간단하기 때문이다. 대부분의 예시는 값을 받아와서 콜백 함수 선언 안에서 console.log로 출력만 해보는 것으로 끝이었기 때문에, 이 값을 가지고 다른 작업을 하고 싶을 때는 어떤 접근이 적절한지 알기가 힘들었다.

이 외에도 좋은 방법이 있을 수도 있겠지만, 우선 공식 docs를 참고해보면 이 둘이 가장 대표적으로 소개되고 있는 듯하다.

연속으로 입력 받기

개인적으로 작성하던 코드에선 입력된 값이 올바르지 않으면 다시 받아오는 걸 구현할 필요가 있었다. 다만, 보다시피 표준 입력을 구현하는 코드가 길어서 이걸 어떻게 루프문 안에 구현해야 하나 고민했었는데, 이건 루프 문으로 해결하는 게 아니라 prompt 함수를 알면 해결될 수 있는 부분이다. 다만 기존의 다른 언어들과는 워낙 작성 방식이 달라서 처음엔 직관적이지 않다는 문제가 있다.

방법은 입력된 값을 검증하는 함수를 만들어놓고 입력 값을 그 함수에게 넘겨서 false가 반환되면 다시 prompt함수를 호출하는 것이다. 이렇게 하면 다시 커서를 프롬프트 창, 즉 표준 입력으로 가게끔 한다고 이해하면 쉽다. 그러면 아직 close함수가 호출되지 않아 프롬프트 창은 살아있기 때문에 다시 문장을 입력할 수 있게 깜빡거리고 있을 것이다. 이때 무언가 또 입력하면 다시 r1.on('line', callback) 함수로 진입한다. 코드 예시는 아래와 같다.

단, prompt함수는 의도적으로 닫아주지 않으면 계속 열려있어서 다음 동작이 진행되지 않는다. 닫았을 때는 process.exit()을 실행하여 완전히 종료시켜주지 않으면 오류가 생길 가능성이 높으니 참고하자.

const readline = require("readline");

const r1 = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

r1.prompt();

r1.on("line", (input) => {
  let is_valid_input = check_validity(input);
  if (is_valid_input) {
    solution(input);
    r1.close();
  } else {
    console.log("Wrong input. Please write valid input.");
    r1.prompt();
  }
}).on("close", function () {
  process.exit();
});