Dart Programmer 되기 [23]

< Dart 기초문법 – Console I/O (Input & Output) >

아마 프로그래밍 언어를 하나 즈음 배워본 사람이라면, 지금 즈음 의아해 할 부분이 있습니다. “왜 사용자로부터 키보드를 통해서 입력을 받는 부분이 전혀 안 나오지?”. 이제 Dart 언어를 통해서 사용자로부터 키보드를 통해서 입력을 받아, 이를 처리하는 프로그램 기법에 대해서 이야기를 하도록 하겠습니다. 참고로 콘솔(Console) 이라는 단어는 MS 윈도우의 도스창 혹은 터미날, MacOS/Linux의 터미날이라는 까만 창에서 글자들만 입력/출력되는 모드를 이야기 합니다. 그리고 바로 다음 절에서 설명할 CLI는 Command Line Interface의 약어로서, 콘솔(혹은 터미날)에서 사용자와 키보드와 화면만으로 (마우스 등 그래픽 인터페이스 없이) 동작하는 프로그램을 의미합니다.

Command-Line Interface vs Web Interface

왜 이제서야 키보드 입력을 설명하는지 이유를 알아봅니다. Dart 언어는 최근에 만들어진 언어로서, 스마트폰의 모바일앱(MobileApp)과 웹인터페이스(Web)을 지원하면서, 서버(Server) 쪽의 프로그래밍도 동시에 가능한 언어입니다. 두가지 경우에 대해서 별도의 프로그램을 추가로 설치하지 않아도 Dart 언어의 기본 기능으로 개발이 가능합니다. 만약 JavaScript 였다면 이야기가 달라집니다. JavaScript 언어는 웹인터페이스를 기본으로 제공하기에 별도의 추가 도구가 필요 없지만, 모바일앱을 만들고자 한다면 추가로 Cordova/PhoneGap 등의 프레임워크를 설치해야 합니다. 서버 쪽의 프로그램을 만든다면 다시 추가로 Node.js 등의 프레임워크를 설치해야 합니다. 하지만 모든 경우에 대해서 Dart는 언어의 기본 기능으로 일단 시작이 가능 합니다. 이 중 지금 설명할 콘솔 I/O 즉, 컴퓨터 운영체제에서 바로 프로그램을 실행해서, 화면에 출력을 하고 키보드로 부터 입력을 받는 Dart의 기법을 설명하고자 합니다.

Standard Input & Output (stdin & stdout)

대부분의 사용자는 iOS, MacOS, Android, Ubuntu, Windows와 같은 GUI(Graphic User Interface) 기반의 인터페이스, 즉 마우스와 그림으로 되어 있는 사용자 인터페이스에 익숙합니다. 하지만 MS DOS, Linux, Unix와 같은 전통적인 운영체제는 GUI 이전에 키보드 타이핑과 글자 화면 출력을 기본으로 했습니다. 아래의 그림을 보면, 애플2 컴퓨터의 CLI 기반 사용자 인터페이스를 볼 수 있습니다. 까만 화면에 글자 출력과 키보드 입력이며, 마우스와 그림은 없는 것을 볼 수 있습니다.

[그림] Apple II (출처: https://nationalmaglab.org/)

이렇게 과거의 GUI 이전 프로그래밍 언어에서는 가장 기본이 되는 글자 기반의 입출력을 표준 입력과 출력으로 지정하였고, 영문으로 standard input과 standard output이며, 프로그래밍 언어에서 실제 사용하기 위한 용어로 줄여서 stdinstdout으로 사용하고 있습니다. 유명한 C/C++를 비롯한 매우 많은 언어 들에서, 동일한 단어를 사용합니다.

Stdin & Stdout in Dart Language

Dart 언어에서 stdin과 stdout을 다루고자 한다면, 프로그램의 시작 부분에서 다음의 구문이 있어야 합니다. 아래 구문의 의미를 설명하면, “dart 언어가 제공하는 (standard console 기반의) input과 output 기능을 사용 하겠으니, 이에 대해 미리 만들어 놓은 소프트웨어를 포함(import) 하도록 하라”는 의미 입니다. 이런 프로그램을 라이브러리(library)라고 하며, 이에 대한 자세한 설명은 다음에 하겠습니다. 일단 아래의 구문이 필요하다는 정도로만 이해하면 되겠습니다.

import 'dart:io';

Stdout에 대해서 알아 보겠습니다. Dart 언어가 stdout을 위해서 제공하는 많은 기능 중에서 가장 기본적인 함수(메소스) 들은 다음과 같습니다 [참조].

  • stdout.write(object): 하나의 입력 파라메타(객체, object)를 받아서 화면에 출력하는 가장 간단한 기능 [출처]
  • stdout.writeln(object): 하나의 입력 파라메타(객체, object)를 받아서 화면에 출력하는 기능에 추가해서, obejct 출력후 줄바꿈 기능(‘\n’)을 수행함 [출처]
  • stdout.writeAll(object[s]): 리스트와 같이 복수의 값을 갖는 입력 파라메타를 받아서, 복수의 값들을 하나 하나 출력하는 기능을 수행함. 추가적으로 (필수가 아닌 optional 한 값을 주면) 값들 사이에 개발자가 희망하는 기호 출력이 가능함 [출처]

Stdin에 대해서 알아 보겠습니다. Dart 언어가 stdin을 위해서 제공하는 많은 기능 중에서 여기서는 하나의 함수(메소드) 만을 설명하도록 하겠습니다.

  • stdin.readLineSync(): 사용자가 엔터키를 입력하기 전까지의 내용을 키보드로 부터 받아서, 문자열 형태로 return 함 [출처]

위의 함수들을 보면, stdout과 stdin이 클래스이며, 이들 안의 메소드를 실행하는 것을 볼 수 있습니다. Dart 언어의 공식 홈페이지에서 두 클래스의 내용에 대해서 읽어 보기 바랍니다.

Sample Program using Stdin & Stdout

아래의 darttutorial-23-01.dart 프로그램은 앞서 설명한 4개의 메소드를 활용한 아주 단순한 예제 프로그램 입니다. #0은 stdin과 stdout을 활용하기 위하여 import를 한 부분입니다. #1은 stdout의 write 함수(메소드)를 보여 줍니다. 입력 파라메타를 화면에 출력할 뿐 별도의 줄 바꾸기 같은 기능은 없음에 유의 합니다. #2는 stdout의 writeln 함수이며, 입력 파라메타를 화면에 출력한 후, 줄바꾸기를 하는 부분이 차이점 입니다. #3은 stdout의 writeAll 함수에게 리스트를 전달하여, 리스트 내부 값들을 출력합니다. 마지막으로 #4는 사용자로부터 키보드를 통해서 입력을 받자 마자, 그래도 화면에 출력하는 것을 보여 줍니다. 단, 사용자가 ‘exit’ 외의 글자를 입력하면 무한 반복하며, ‘exit’ 글자를 입력하면 프로그램의 수행을 종료 합니다.

// darttutorial-23-01.dart

// #0 Includes console I/O functionality into this program 
import 'dart:io';

void main() {
  // #1 write method in stdout class
  // Reference: https://api.flutter.dev/flutter/dart-io/Stdout/write.html
  stdout.write("A");
  stdout.write("B");
  stdout.write("\n");
  
  // #2 writeln method in stdout class
  // Reference: https://api.flutter.dev/flutter/dart-io/Stdout/writeln.html
  stdout.writeln("A");
  stdout.writeln("B");  
  stdout.write("\n");

  // #3 writeln method in stdout class
  // Reference: https://api.flutter.dev/flutter/dart-io/Stdout/writeAll.html
  List myList = ["A", "B"];
  stdout.writeAll(myList, ":");
  stdout.write("\n");

  // #4 readLineSync method in stdin class
  // Reference: https://api.flutter.dev/flutter/dart-io/Stdin/readLineSync.html
  for(String myInput = "";myInput != "exit";) {
    stdout.write('type\$ ');
    myInput = stdin.readLineSync();
    stdout.writeln('----> $myInput');
  }
}

4가지의 기본 메소드만 이해하면 크게 어렵지 않은 프로그램이며, 수행 결과를 아래와 같이 표시 하였으니, 참조하기 바랍니다.

AB
A
B

A:B
type$ My name is Dr.Sungwon.
----> My name is Dr.Sungwon.
type$ exit
----> exit

How to execute a sample program?

위의 프로그램을 지금까지 우리가 사용하는 개발 도구인 MS Visual Code에서 수행한다면, 의아한 결과가 나올 것입니다. 현재 MS Visual Code에서는 Dart 언어의 Debug 방식에서는 stdin과 stdout 기술을 제대로 지원하지 못하고 있습니다. 따라서, 다른 방식으로 수행을 해야 합니다.

MS Visual Code를 이용하여, 작업할 폴더를 만들고, darttutorial-23-01.dart 프로그램을 에디터 창에 입력하는 부분까지 동일하게 합니다. 그리고 왼쪽 tab에서 디버그 아이콘을 클릭하는 부분까지 진행합니다. 이제 달라지는 부분입니다. darttutorial-23-01.dart 프로그램 에디터 화면 하단을 보면, 아마도 “디버그 콘솔” 이라는 탭이 디촐트로 열려 있을 겁니다. print() 함수의 출력이 여기에 나타나고 있는 것이지요. 그러면, 이젠 오른쪽의 “터미널”을 눌러 봅니다. MS Visual Code 자체적인 콘솔 화면이 열린 것이라고 보면 됩니다. 따라서 입출력이 모두 가능하겠지요? “터미널” 화면 안에서 pwd 라고 입력을 하고 엔터를 쳐보면, 작업을 위하여 만든 폴더 이름이 쓰여져 있는데, 현재 터미널 화면이 해당 폴더 안에서 작업을 할 수 있도록 들어가 있다는 의미입니다. 아래는 제 컴퓨터의 예제로서, darttutorial-23 이라는 디렉토리에서 작업을 하고 있다는 의미입니다. 여기서 ls 라고 입력하고 엔터를 칩니다. 아래의 예제처럼, 본 글에서 다룰 4개의 dart 소스 화일의 이름들을 볼 수 있습니다. 여기서 pwd와 ls 같은 명령은 과거 Unix 운영체제 부터 있어온 오래된 명령들로 Linux 운영체제 및 서버 프로그래밍에서 많이 사용하는 명령 들 입니다.

(base) MacMini-ServerFarm:darttutorial-23 drsungwon$ pwd
/Users/drsungwon/OneDrive/Programming/hellodart/darttutorial-23
(base) MacMini-ServerFarm:darttutorial-23 drsungwon$ ls
darttutorial-23-01.dart darttutorial-23-03.dart
darttutorial-23-02.dart darttutorial-23-04.dart
(base) MacMini-ServerFarm:darttutorial-23 drsungwon$ 

이제 Dart 언어를 사용해서 darttutorial-23-01.dart 프로그램을 실행하기 위해서는, 터미널 창 안에서 다음 처럼 입력하고 엔터키를 치면 됩니다. 의미는 손으로 dart 프로그래밍 언어를 실행하여, darttutorial-23-01.dart 프로그램을 있는 내용을 동작 시키라는 의미입니다.

dart darttutorial-23-01.dart

실행하는 화면과 결과 화면은 다음과 같이 나타났을 겁니다.

(base) MacMini-ServerFarm:darttutorial-23 drsungwon$ dart darttutorial-23-01.dart
AB
A
B

A:B
type$ My name is Dr.Sungwon.
----> My name is Dr.Sungwon.
type$ exit
----> exit
(base) MacMini-ServerFarm:darttutorial-23 drsungwon$ 

방금 전의 프로그램 실행 화면과 결과 화면을 포함한, Visual Code 전체 화면의 스크린 샷이 아래와 같으니, 혹시 오류가 있는 사람은 천천히 수행해 봅니다.

[그림] darttutorial-23-01.dart 프로그램의 실행 및 결과 화면

“구구단 프로그램” ver 1.0

단지 입력을 받아서 출력만 하면 별 의미가 없으니, 간단하지만 나름 의미있는 구구단 프로그램을 만들어 봅니다. 기능은 매우 단순합니다. 사용자로부터 구구단 계산을 원하는 ‘단’에 해당하는 숫자를 받아서, 결과 값을 화면에 출력해 줍니다. 정확하게 목적에 필요한 내용 만으로 만든 프로그램이 darttutorial-23-02.dart에 나타나 있습니다.

// darttutorial-23-02.dart

import 'dart:io';

void main() {
  stdout.write('Enter number between 1 to 9> ');
  var myNum = int.parse(stdin.readLineSync());
  for(var tmp = 1;tmp <= 9; tmp++) {
    stdout.writeln("$myNum x $tmp = ${myNum * tmp}");
  } 
}

main()의 첫줄에서 "1~9 사이 정수를 입력하라"고 했습니다. 두번째 줄에서는 stdin.readLineSync()를 통해서 입력을 받고, int.parse()로 입력 값을 정수로 바꾼후, myNum 변수에 '단' 수로 쓰일 정수를 저장한 것을 볼 수 있습니다. 그리곤 for 반복 구문으로 곱하기 1부터 9까지의 구구단을 계산하여 출력합니다.

"구구단 프로그램" ver 2.0

Ver 1.0의 문제는 뭔가요? 일단 동작하는 것과 상관 없지만, 나중에 이 프로그램을 다시 보거나 다른 사람이 보면 이해를 위해서 소스 코드를 매우 꼼꼼하게 읽어 봐야지, 뭘 하는 프로그램 인지 이해를 할 수 있다는 점 입니다. 당연하겠죠? 소스코드는 컴퓨터에게 일을 시키는 것이지, 사람을 위한 것은 아니니까요. 따라서, 추가해야 할 부분은 뭔가요? 바로 주석문 입니다. 앞서에서 배운 주석 구문 문법을 사용하여, 간단하게 어떤 일을 하는지를 추가로 설명합니다. 이렇게 개선한 프로그램이 darttutorial-23-03.dart에 나타나 있습니다.

// darttutorial-23-03.dart

// #0 Includes console I/O functionality into this program 
import 'dart:io';

void main() {
  // #1 Print usage for user 
  stdout.write('Enter number between 1 to 9> ');

  // #2 Get user input and convert string to integer
  var myNum = int.parse(stdin.readLineSync());

  // #3 Calculates result
  for(var tmp = 1;tmp <= 9; tmp++) {
    stdout.writeln("$myNum x $tmp = ${myNum * tmp}");
  } 
}

"구구단 프로그램" ver 3.0

Ver 2.0의 문제점 혹은 개선이 필요한 부분은 무엇 일까요? 가장 흔한 사례는, 개발자와 다르게 사용하는 사용자들에 대한 대처 입니다. 이 프로그램은 일단 1~9 사이 정수를 입력하라고 했으니, 0이하 혹은 10이상의 숫자를 넣는 사람들이 걱정이며, 추가로 숫자가 아닌 문자열이나 실수를 넣는 사람들도 문제입니다. 이를 위해서 추가적인 에러 방지 코드가 추가되어야 합니다. 이렇게 개선한 프로그램이 아래의 darttutorial-23-04.dart 입니다.

// darttutorial-23-04.dart

// #0 Includes console I/O functionality into this program 
import 'dart:io';

void main() {
  // #1 Local variable for user input
  var myNum = 0;

  // #2 Print usage for user 
  stdout.write('Enter number between 1 to 9> ');

  // #3 Check input wether integer or not
  try {
    // #3.1 Convert string to integer
    myNum = int.parse(stdin.readLineSync());
  } catch (e) {
    // #3.2 Print error message and exit the program
    stdout.writeln("error> enter integer number between 1 to 9.");
    return;
  }

  // #4 Check input wether in right range
  if((myNum >= 1) && (myNum <= 9)) {
    // #4.1 Calculates result
    for(var tmp = 1;tmp <= 9; tmp++) {
      stdout.writeln("$myNum x $tmp = ${myNum * tmp}");
    }
  } else {
    // #4.2 Print error message
    stdout.writeln("error> enter integer number between 1 to 9.");
  }
}

마무리

이 글에서는 (다른 언어들의 경우는 다소 일찍 등장하는) 키보드로 부터 입력을 받는 내용과 이를 화면에 출력하는 가장 기본적인 프로그래밍 형태인, stdin과 stdout을 어떻게 Dart 언어에서 수행하는지 알아보았습니다. 또한, 구구단 프로그램에서 꼭 필요한 부분부터 제대로된 형태로 진화하는 구조도 살펴보았습니다. 이를 통해서 기본적인 기능만 만든 프로그램이 11줄이였으나, 마지막에 총33줄로 늘어나는 모습을 보았습니다. 이렇게 프로그램을 개발한다는 것은 원래하고 싶은 동작과, 예상되는 오류 등을 미리 논리적으로 생각하는 노력이 필요합니다.

Creative Commons License (CC BY-NC-ND)

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다