문제 : https://www.acmicpc.net/problem/10718 (백준 온라인 저지)

정답 코드

2줄 출력이므로 설명은 생략하겠습니다.

'Programing > Baekjoon 문제 알고리즘(node.js)' 카테고리의 다른 글

10869번 사칙연산  (0) 2022.02.24
10998 AxB  (0) 2022.02.24
1001번 A-B  (0) 2022.02.24
1000번 A+B  (0) 2022.02.23
2557번 Hello World  (0) 2022.02.22
Posted by englishmath
,

문제 : https://www.acmicpc.net/problem/1001 (백준 온라인 저지)

정답 코드

1000번문제의 A+B를 A-B로 수정하였습니다. 나머지 코드는 1000번 문제에서 설명드렸으므로 따로 언급하지 않겠습니다.

'Programing > Baekjoon 문제 알고리즘(node.js)' 카테고리의 다른 글

10869번 사칙연산  (0) 2022.02.24
10998 AxB  (0) 2022.02.24
10718번 We love kriii  (0) 2022.02.24
1000번 A+B  (0) 2022.02.23
2557번 Hello World  (0) 2022.02.22
Posted by englishmath
,

문제 : https://www.acmicpc.net/problem/1000 (백준온라인저지)

 

두 값을 입력받아 합을 출력하는 문제입니다. 다른 언어로는 쉽게 풀 수 있으나 node.js는 일반적으로 C의 scanf나 python의 input 함수처럼 표준 입력 스트림(standard input stream)에 접근하는 내장함수를 지원하지 않으므로 직접 표준 입력 스트림에 접근해야 합니다. 먼저 아래의 실패한 코드를 살펴보겠습니다.

실패한 코드

let process = require("process");
let stdin = process.stdin;
 
process 객체를 가져온 후 process 객체에 포함된 stdin 객체를 변수에 저장합니다. 여기서 process 객체는 Node.js가 실행될 때 생성되는 Node.js 프로세스에 대한 정보를 담고 있는 객체이며 stdin 객체는 Node.js 프로세스가 사용하는 표준 입력 스트림에 대한 정보를 담고 있는 객체입니다. 일반적으로 process 전역변수는 명시를 따로 하지 않아도 process 객체를 담고 있지만 공식 Node.js 문서에서는 가급적 명시하라고 하여 코드를 명시하였습니다.
 
stdin.on("readable", function scanf(){
    let stdinBuffer = stdin.read();
    let stdinNumbers = stdinBuffer.toString().split(" ");
    let A = parseInt(stdinNumbers[0]);
    let B = parseInt(stdinNumbers[1]);
    console.log(A+B);
});
 
stdin 객체에서 "readable" 이벤트가 발생했을 때 수행할 함수(이벤트 리스너)를 등록합니다. readable 이벤트는 스트림에 읽을 수 있는 데이터가 존재할 때 발생되는 이벤트이며 scanf는 데이터를 읽어오기 위해 임의로 작성한 함수입니다.  즉 표준 입력 스트림에서 읽을 수 있는 데이터가 생길 경우 scanf함수를 호출한다는 의미이며 scanf 코드는 다음과 같습니다.
 
let stdinBuffer = stdin.read();
let stdinNumbers = stdinBuffer.toString().split(" ");
 
표준 입력 스트림에서 데이터를 읽어 버퍼에 저장합니다. 일반적으로 stdin객체의 read 메소드는 데이터를 읽을 때 공백, CR, LF 또한 데이터로 인식하여 읽어들이므로 실질적으로 사용할 입력값을 추출하기 위해 읽어들인 데이터가 담긴 버퍼에 적절한 처리를 해주어야 합니다.
 
읽어들인 버퍼를 문자열로 변환한 후 변환한 문자열을 공백을 기준으로 나눕니다. 나누어진 문자열을 요소로 가지는 배열로 저장하면 배열에는 실질적으로 사용할 입력값만 남게 되며 쉽게 정리하면 다음과 같습니다.
 
표준 입력 스트림 -> 1 2
 
버퍼 -> [0x31, 0x20, 0x32, 0x0d, 0x0a]
 
숫자버퍼 -> 버퍼.toString().split(" ")
 
숫자버퍼 -> ["1","2"]
 
let A = parseInt(stdinNumbers[0]);
let B = parseInt(stdinNumbers[1]);
console.log(A+B);
 
숫자버퍼의 각 요소를 정수로 변환한 후 합을 출력합니다.
 
 
위의 코드를 내 PC의 node.js로 실행하니 정상적으로 수행이 되었습니다. 그래서 백준 사이트에서 코드를 입력 하였는데 백준에서는 놀랍게도 다음의 결과가 나왔습니다.

 

TypeError 발생

javascript에서 TypeError는 함수에 잘못된 타입의 인수를 주었거나 객체에서 존재하지 않는 메소드를 호출한 경우에 발생합니다.(물론 TypeError가 발생하는 이유는 이것보다 더 다양합니다.) 내 PC에서는 제대로 동작하였는데 왜 백준에서는 에러가 발생했을까요? 몇 시간을 삽질한 결과 다음과 같은 가설을 세워보았습니다.

1.  Node.js 버전이 달라 구문해석을 잘못한 경우

    -> 다만 이경우에는 SyntaxError가 발생했을 것이므로 아니라고 판단하였습니다.

2.  Node.js에 모듈이 존재하지 않아 실패한 경우

    -> process 모듈이 존재하지 않는 경우는 확률이 너무 낮으므로 일단 패스하였습니다.

3.  이벤트 리스너가 중복 추가되어서 한 번 이벤트가 발생했을 때 이벤트 리스너가 중복 호출되는 경우

    -> 내 PC에서는 입력을 받은 자바스크립트가 이벤트 리스너를 호출하고 자연스럽게 종료합니다. node.js 프로세스가 종료되면 사용했던 자원은 다 반납되므로 추가했던 이벤트 리스너는 사라집니다. 즉 하나의 node.js 프로세스가 하나의 스크립트를 실행한 후 종료하므로 node.js를 100번 실행한다해도 이벤트 리스너가 중복 추가될 일은 없습니다. 하지만 백준 사이트에서는 하나의 node.js 프로세스가 여러개의 자바스크립트를 처리하여 각각의 자바스크립트들이 자원을 서로 공유할수도 있겠다라는 생각이 들었습니다. 자원을 공유한다는 것은 process 등의 객체를 공유한다는 것을 의미하므로 이미 이벤트 리스너가 등록된 process객체에 이벤트 리스너가 또 추가될 수 있는 상황인 것입니다.

이벤트 리스너가 중복 추가된 경우 한 번의 이벤트가 발생했을 때 이벤트 리스너가 여러 개 호출될 수 있습니다. 즉 stdin 객체에서 readable 이벤트에 대해 2개의 scanf 이벤트 리스너가 중복 추가된 경우 readable 이벤트가 발생하면 scanf가 2번 호출될 수 있다는 것입니다. 이렇게 되면 첫 번째 scanf에서 데이터를 이미 다 처리했기 때문에 2번 째 호출되는 scanf에서는 값을 읽어오지 못해 null 버퍼가 될 것이며  null 버퍼에서 toString 메소드를 호출할 수는 없으므로 TypeError 에러가 발생할 수 있겠다라는 생각이 들었습니다.

3번 가설이 맞는지 확인하기 위해 코드를 수정해보겠습니다. 리스너가 중복 추가되서 scanf가 여러번 호출되는 것이 문제이므로 scanf 함수를 호출할 때마다 등록된 리스너를 제거해주면 될 것 같습니다. 수정된 코드는 다음과 같습니다.

수정한 코드
stdin.removeListener("readable", scanf)
 
scanf 함수 처리가 끝나면 removeListener 메소드를 호출하여 stdin 객체의 readable 이벤트에 대한 scanf 이벤트 리스너를 제거합니다. 이렇게 코드를 수행하면 scanf 함수가 호출될 때마다 등록된 scanf 이벤트 리스너가 제거될 것이므로 이벤트 리스너가 중복 호출될 일은 없을 것입니다.
 
내 PC에서는 문제없이 동작합니다. 백준에서 코드를 수행해보겠습니다.
 
성공
 

PS. stdin.removeListener 메소드 대신 process.exit() 메소드를 호출해도 성공합니다. 아마 node.js 프로세스가 종료되면서 자원을 전부 반납하기 때문에 이벤트 리스너가 중복추가가 안되는 것으로 보입니다. 다만 처리시간이 기존코드보다 조금 오래 걸리는데 이 이유는 node.js 프로세스가 종료되면 node.js를 다시 재실행하는 과정을 거쳐서 시간이 조금 오래 걸리는 것이라고 추측해볼 수 있겠습니다.(아닐 수도 있습니다.) 수정된 코드는 다음과 같습니다.

process.exit() 메소드를 호출하는 코드

 

'Programing > Baekjoon 문제 알고리즘(node.js)' 카테고리의 다른 글

10869번 사칙연산  (0) 2022.02.24
10998 AxB  (0) 2022.02.24
10718번 We love kriii  (0) 2022.02.24
1001번 A-B  (0) 2022.02.24
2557번 Hello World  (0) 2022.02.22
Posted by englishmath
,