[Python] input() 의 동작

Humpback Whale·2024년 2월 17일
0

python

목록 보기
1/2

파이썬에서 input() 메서드는 모두가 익숙할 것입니다.

그저 터미널에서 사용자의 입력이 필요할 때 사용하죠. 단순히 '키보드 입력 받을 때 쓰는 함수'라고만 이해해선 안된다고 생각합니다.
어떻게 동작하는지 이해해야 input() 을 사용하는 프로그램을 다른 방식으로도 다룰 수 있습니다.

아마 처음 언어를 배우고 이것을 활용하기 위해 간단한 CLI 기반 게임을 많이들 만들 겁니다. 오목게임이라든가, 숫자 야구게임 등등...

이런 프로그램을 만들기는 쉽습니다. 사용자의 입력을 받고, 로직을 검사하고, 출력을 만들어내죠.
사용자의 입력은 input() 메서드를 통해 만들겁니다.

하지만 문제에 봉착하는 순간은, 이렇게 만든 프로그램에 대한 테스트 코드를 작성할 때 나타날겁니다.

프로그램 실행 시, 어떻게 터미널에 입력할 내용을 자동으로 주입할까??

input() 메서드를 통해 사용자 입력을 만드는건 아주 쉽습니다. 생각할 필요도 없죠. 하지만 테스트를 해야 한다면, 사람이 직접 터미널에 입력을 하는 것이 아니라, 시스템에서 자동으로 주어진 입력을 하도록 만들어야 합니다.

컴퓨팅 시스템을 왜 이해하는 것이 중요한 대목입니다. 사용자의 입력을 받는 함수가 어떻게 동작을 하는지, 어떤 컴퓨팅 자원과 시스템을 활용하는지 알아야 해당 문제에 접근하고 해결할 수 있을 것입니다.


컴퓨터 관점에서 사용자 입력은 어떤 동작으로 구성될까? 제 가정은 다음과 같았습니다.

  • input() 메서드는 사용자의 키보드 입력(표준입력)을 기다리는 프로세스를 만든다.
  • 사용자가 키보드를 통해 입력을 만들어 내면, 입력한 내용을 임시적인 버퍼에 담는다.
  • 입력 프로세스는 입력 버퍼를 계속 감시하며, 버퍼에 엔터(\n)가 입력되는 순간 \n 전까지 입력된 내용들을 소비한다.

이 가정을 기반으로 input() 메서드에 사용자가 직접 입력하는 것이 아닌, 시스템상으로 자동으로 주입할 수 있을지 가정을 해봅시다.

  • input() 프로세스가 표준 입력을 감시하며, 감시는 표준 입력 버퍼를 통해 이루어 진다면,
  • 표준 입력 버퍼에 특정 텍스트를 강제로 주입.
  • 알아서 input() 메서드는 해당 버퍼를 읽고 이것은 사용자가 입력한 텍스트다 라고 인식할 것이다.

한 번 이 가정을 토대로 코드를 만들어 봅시다.

먼저, 사용자의 입력을 받는 함수입니다.

def create_input():
    text = input("여기에 입력해 주세요 : ")
    return text
    

input_text = create_input()
print(input_text)

실행 시,
여기에 입력해 주세요 : 가 나타나며, hello를 입력 시, hello를 출력합니다.

이 함수를 자동으로 테스트 하기 위해서는 사용자가 직접 입력하는 것이 아닌, 코드상으로 자동으로 주입될 수 있어야 합니다.
제 생각엔 아마 표준입력 버퍼에 텍스트를 강제로 밀어넣은 상태라면, 알아서 읽지 않을까 합니다.

sys.stdin 모듈이 적합하겠군요. 파이썬의 표준입력에 대한 모듈입니다.
이걸 한번 이용해 봅시다.

import sys


def create_input():
    text = input("여기에 입력해 주세요 : ")
    return text


sys.stdin = "hello"


input_text = create_input()
print(input_text)

실행결과, AttributeError: 'str' object has no attribute 'readline' 이 발생합니다.

이럴수가, 실패했네요.

sys.stdin의 타입은 TextIO 입니다. 아마 버퍼를 읽는 과정에서 내부적으로 readline이 구현된 객체를 통해 입력을 처리하는가 봅니다.

그럼 텍스트가 아닌, TextIO와 호환되는 io.StringIO 객체에 텍스트를 넣은 상태로 sys.stdin에 주입해 봅시다.

import sys
from io import StringIO


def create_input():
    text = input("여기에 입력해 주세요 : ")
    return text


sys.stdin = StringIO("hello")


input_text = create_input()
print(input_text)

실행 시, 출력은 다음과 같습니다.

여기에 입력해 주세요 : hello

create_input() 메서드 호출하기 앞서서 stdin이 있어야 합니다. stdin이 뒤에 올 경우, input 함수는 그저 사용자의 입력을 받기 위해 무한히 대기하며 다음줄로 넘어가지 않기 때문입니다.

input() 을 통해 출력한 메시지인 여기에 입력해 주세요 :String.IO를 통해 우리가 주입한 hello 라는 텍스트가 결합되어 출력된 것입니다.
예외가 발생하지 않고 잘 처리되었습니다.
사용자가 입력할 필요도 없이 알아서 주입되었습니다.


그렇다면 다중 입력이 필요한 경우는 어떨까요?
이제 create_input() 함수는 내부에 사용자 입력을 3번 받아야 하는 함수로 만들어 봅시다.

그리고 함수를 호출할때 자동으로 입력을 주입하기 위해서, 제 생각엔 String.IO 객체에 입력 텍스트를 \n으로 구분하여 "hello\nworld\n!" 로 주입하면,
sys.stdin 버퍼에서 해당 텍스트를 \n 을 기준으로 소비하며, 소비한 내용은 버퍼에서 없애주지 않을까 합니다.
첫번째 소비는 hello 일 것이며, 두번째 소비는 world, 세번째 소비는 ! 하게 되지 않을까 합니다.

from io import StringIO


def create_input():
    text1 = input("여기에 입력해 주세요1 : ")
    text2 = input("여기에 입력해 주세요2 : ")
    text3 = input("여기에 입력해 주세요3 : ")
    return text1 + text2 + text3


sys.stdin = StringIO("hello\nworld\n!")


input_text = create_input()
print(input_text)

실행 시, 출력은 다음과 같습니다.

여기에 입력해 주세요1 : 여기에 입력해 주세요2 : 여기에 입력해 주세요3 : helloworld!

제가 직접 입력하지 않아도 자동으로 입력되었으며, 예상대로 잘 동작했습니다.

input 입력 문구들이 나란히 출력되는 이유는 아마 readline()함수가 텍스트를 읽음에 있어서 \n까지 읽고 \n은 없애버렸기 때문이지 않을까 싶습니다.


이제 터미널 기반 프로그램을 작성한다면, 이를 이용해 테스팅 모듈을 작성할 수 있을 것입니다.
사용자 입력을 받아야 한다면, 시스템상에서 자동으로 입력될 수 있도록 만들 수 있을 것입니다.

0개의 댓글