Argparse 자습서
***************

저자:
   Tshepang Lekhonkhobe

This tutorial is intended to be a gentle introduction to "argparse",
the recommended command-line parsing module in the Python standard
library. This was written for argparse in Python 3. A few details are
different in 2.x, especially some exception messages, which were
improved in 3.x.

주석: 같은 작업을 수행하는 다른 두 모듈이 있습니다, "getopt" (C 언어
  에서 "getopt()" 와 동등합니다) 와 폐지된 "optparse". "argparse" 는
  "optparse" 에 기반을 두고 있어서 사용법 면에서 매우 비슷합니다.


개념
====

**ls** 명령을 사용하여 이 입문서에서 다룰 기능들을 살펴봅시다:

   $ ls
   cpython  devguide  prog.py  pypy  rm-unused-function.patch
   $ ls pypy
   ctypes_configure  demo  dotviewer  include  lib_pypy  lib-python ...
   $ ls -l
   total 20
   drwxr-xr-x 19 wena wena 4096 Feb 18 18:51 cpython
   drwxr-xr-x  4 wena wena 4096 Feb  8 12:04 devguide
   -rwxr-xr-x  1 wena wena  535 Feb 19 00:05 prog.py
   drwxr-xr-x 14 wena wena 4096 Feb  7 00:59 pypy
   -rw-r--r--  1 wena wena  741 Feb 18 01:01 rm-unused-function.patch
   $ ls --help
   Usage: ls [OPTION]... [FILE]...
   List information about the FILEs (the current directory by default).
   Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.
   ...

네 가지 명령에서 배울 수 있는 몇 가지 개념들입니다:

* **ls** 명령은 옵션 없이 실행될 때도 유용합니다. 기본적으로 현재 디
  렉 터리의 내용을 표시합니다.

* 기본적으로 제공하는 것 이상으로 원한다면, 조금 더 말합니다. 이 경
  우 에는 다른 디렉터리인 "pypy" 를 표시하기를 원합니다. 우리가 한 것
  은 위치 인자라고 알려진 것을 지정하는 것입니다. 프로그램이 명령행에
  표 시되는 위치를 기준으로 값을 어떻게 처리해야 하는지를 알아야 하므
  로 이런 이름이 사용됩니다. 이 개념은 **cp** 와 같은 명령에 더 적절합
  니 다. 가장 기본적인 사용법은 "cp SRC DEST" 입니다. 첫 번째 위치는 *
  복 사하고자 하는 것* 이고 두 번째 위치는 *사본을 저장할 곳* 입니다.

* 자, 프로그램의 행동을 바꾸고 싶다고 합시다. 이 예에서는 파일 이름
  만 표시하는 대신 각 파일에 대한 정보를 더 많이 표시합니다. 이 경우
  "-l" 은 옵션 인자로 알려져 있습니다.

* 이것이 도움말 텍스트입니다. 이전에 사용해보지 않은 프로그램을 접했
  을 때 도움말 텍스트를 읽는 것만으로 작동하는 방식을 이해할 수 있다는
  점 에서 매우 유용합니다.


기본
====

(거의) 아무것도 하지 않는 아주 간단한 예제로 시작합시다:

   import argparse
   parser = argparse.ArgumentParser()
   parser.parse_args()

다음은 코드를 실행한 결과입니다:

   $ python prog.py
   $ python prog.py --help
   usage: prog.py [-h]

   optional arguments:
     -h, --help  show this help message and exit
   $ python prog.py --verbose
   usage: prog.py [-h]
   prog.py: error: unrecognized arguments: --verbose
   $ python prog.py foo
   usage: prog.py [-h]
   prog.py: error: unrecognized arguments: foo

일어난 일은 이렇습니다:

* 옵션 없이 스크립트를 실행하면 아무것도 표준 출력에 표시되지 않습니
  다 . 별로 유용하지 않습니다.

* 두 번째는 "argparse" 모듈의 쓸모를 보여주기 시작합니다. 거의 아무
  것 도 하지 않았지만 이미 도움말을 얻었습니다.

* "--help" 옵션은, "-h" 로 단축할 수도 있습니다, 무료로 얻을 수 있는
  유일한 옵션입니다 (즉, 지정할 필요가 없습니다). 다른 값을 지정하면
  오류가 발생합니다. 그러나 그때조차도 우리는 사용 안내를 얻습니다, 여
  전히 공짜입니다.


위치 인자 소개
==============

예:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("echo")
   args = parser.parse_args()
   print args.echo

코드를 실행합니다:

   $ python prog.py
   usage: prog.py [-h] echo
   prog.py: error: the following arguments are required: echo
   $ python prog.py --help
   usage: prog.py [-h] echo

   positional arguments:
     echo

   optional arguments:
     -h, --help  show this help message and exit
   $ python prog.py foo
   foo

이런 일이 일어났습니다:

* "add_argument()" 메서드를 추가했습니다. 이 메서드는 프로그램이 받
  고 싶은 명령행 옵션을 지정하기 위해 사용합니다. 이 경우 기능과 일치
  하도 록 "echo" 라고 이름 붙였습니다.

* 이제 프로그램을 호출하려면 옵션을 지정해야 합니다.

* "parse_args()" 메서드는 실제로 지정된 옵션으로부터 온 데이터를 돌
  려 줍니다. 이 경우에는 "echo" 입니다.

* 변수는 "argparse" 이 공짜로 수행하는 일종의 '마법' 입니다 (즉, 값
  이 저장되는 변수를 지정할 필요가 없습니다). 또한, 그 이름이 메서드에
  주 어진 문자열 인자 "echo" 와 일치함을 알 수 있습니다.

그러나 도움말이 멋지게 보이지만, 현재로서는 가능한 최선이 아닙니다. 예
를 들어 "echo" 가 위치 인자임을 볼 수 있지만, 추측하거나 소스 코드를
읽는 것 외에는 그것이 무엇을 하는지 모릅니다. 그럼 좀 더 유용하게 만들
어 봅시다:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("echo", help="echo the string you use here")
   args = parser.parse_args()
   print args.echo

그러면 이렇게 됩니다:

   $ python prog.py -h
   usage: prog.py [-h] echo

   positional arguments:
     echo        echo the string you use here

   optional arguments:
     -h, --help  show this help message and exit

이제, 뭔가 더 쓸모있는 일을 하는 것은 어떻습니까:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("square", help="display a square of a given number")
   args = parser.parse_args()
   print args.square**2

다음은 코드를 실행한 결과입니다:

   $ python prog.py 4
   Traceback (most recent call last):
     File "prog.py", line 5, in <module>
       print args.square**2
   TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'

잘 안됐습니다. 우리가 달리 지시하지 않는다면, "argparse" 는 우리가 준
옵션들을 문자열로 취급하기 때문입니다. 그럼, "argparse" 에게 그 입력을
정수로 취급하라고 알려줍시다:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("square", help="display a square of a given number",
                       type=int)
   args = parser.parse_args()
   print args.square**2

다음은 코드를 실행한 결과입니다:

   $ python prog.py 4
   16
   $ python prog.py four
   usage: prog.py [-h] square
   prog.py: error: argument square: invalid int value: 'four'

잘 됩니다. 이제 이 프로그램은 잘못된 입력이 올 때 더 진행하지 않고 종
료하기조차 합니다.


옵션 인자 소개
==============

지금까지 우리는 위치 인자를 다뤘습니다. 옵션 인자를 추가하는 방법에 대
해 살펴봅시다:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("--verbosity", help="increase output verbosity")
   args = parser.parse_args()
   if args.verbosity:
       print "verbosity turned on"

출력은 이렇습니다:

   $ python prog.py --verbosity 1
   verbosity turned on
   $ python prog.py
   $ python prog.py --help
   usage: prog.py [-h] [--verbosity VERBOSITY]

   optional arguments:
     -h, --help            show this help message and exit
     --verbosity VERBOSITY
                           increase output verbosity
   $ python prog.py --verbosity
   usage: prog.py [-h] [--verbosity VERBOSITY]
   prog.py: error: argument --verbosity: expected one argument

일어난 일은 이렇습니다:

* 이 프로그램은 "--verbosity" 가 지정되었을 때 어떤 것을 표시하고 그
  렇 지 않을 때는 아무것도 표시하지 않도록 작성되었습니다.

* 옵션이 실제로 선택 사항임을 확인하기 위해, 이 옵션을 사용하지 않고
  프로그램을 실행할 때 오류가 없습니다. 기본적으로 옵션 인자가 사용되
  지 않는다면 관련 변수 (이 경우 "args.verbosity")는 값으로 "None" 이
  주어집니다. 이 때문에 "if" 문의 논리값 검사가 실패합니다.

* 도움말 메시지가 약간 달라졌습니다.

* "--verbosity" 옵션을 사용할 때, 어떤 값을 지정해야 합니다. 어떤 값
  이 건 상관없습니다.

위의 예제는 "--verbosity" 에 임의의 정숫값을 허용하지만, 우리의 간단한
프로그램에서는 실제로 "True" 또는 "False" 두 값만 쓸모 있습니다. 그것
에 맞게 코드를 수정합시다:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("--verbose", help="increase output verbosity",
                       action="store_true")
   args = parser.parse_args()
   if args.verbose:
      print "verbosity turned on"

출력은 이렇습니다:

   $ python prog.py --verbose
   verbosity turned on
   $ python prog.py --verbose 1
   usage: prog.py [-h] [--verbose]
   prog.py: error: unrecognized arguments: 1
   $ python prog.py --help
   usage: prog.py [-h] [--verbose]

   optional arguments:
     -h, --help  show this help message and exit
     --verbose   increase output verbosity

일어난 일은 이렇습니다:

* 이 옵션은 이제 값을 요구하는 것이 아니라 플래그입니다. 이 개념과
  일 치하도록 옵션의 이름을 변경하기까지 했습니다. 새로운 키워드
  "action" 을 지정하고, ""store_true"" 값을 지정했습니다. 이것은, 옵션
  이 지정되 면 "args.verbose" 에 값 "True" 를 대입하라는 뜻입니다. 지
  정하지 않으 면 묵시적으로 "False" 입니다.

* 값을 지정하면 불평하는데, 플래그의 정의를 따르고 있습니다.

* 도움말 텍스트가 바뀐 것을 확인하십시오.


짧은 옵션
---------

명령행 사용법에 익숙하다면 짧은 옵션 버전에 관한 내용을 아직 다루지 않
았음을 알 수 있을 겁니다. 아주 간단합니다:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("-v", "--verbose", help="increase output verbosity",
                       action="store_true")
   args = parser.parse_args()
   if args.verbose:
       print "verbosity turned on"

그러면 이렇게 됩니다:

   $ python prog.py -v
   verbosity turned on
   $ python prog.py --help
   usage: prog.py [-h] [-v]

   optional arguments:
     -h, --help     show this help message and exit
     -v, --verbose  increase output verbosity

새로운 기능은 도움말 텍스트에도 반영됩니다.


위치 및 옵션 인자 결합하기
==========================

프로그램이 점점 복잡해지고 있습니다:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("square", type=int,
                       help="display a square of a given number")
   parser.add_argument("-v", "--verbose", action="store_true",
                       help="increase output verbosity")
   args = parser.parse_args()
   answer = args.square**2
   if args.verbose:
       print "the square of {} equals {}".format(args.square, answer)
   else:
       print answer

이제 출력은 이렇게 됩니다:

   $ python prog.py
   usage: prog.py [-h] [-v] square
   prog.py: error: the following arguments are required: square
   $ python prog.py 4
   16
   $ python prog.py 4 --verbose
   the square of 4 equals 16
   $ python prog.py --verbose 4
   the square of 4 equals 16

* 위치 인자를 다시 도입했기 때문에, 불평합니다.

* 순서는 중요하지 않습니다.

이 프로그램에 여러 상세도를 지정할 수 있도록 하는 능력을 다시 부여하고
, 실제로 그것을 사용하는 것은 어떨까요:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("square", type=int,
                       help="display a square of a given number")
   parser.add_argument("-v", "--verbosity", type=int,
                       help="increase output verbosity")
   args = parser.parse_args()
   answer = args.square**2
   if args.verbosity == 2:
       print "the square of {} equals {}".format(args.square, answer)
   elif args.verbosity == 1:
       print "{}^2 == {}".format(args.square, answer)
   else:
       print answer

출력은 이렇습니다:

   $ python prog.py 4
   16
   $ python prog.py 4 -v
   usage: prog.py [-h] [-v VERBOSITY] square
   prog.py: error: argument -v/--verbosity: expected one argument
   $ python prog.py 4 -v 1
   4^2 == 16
   $ python prog.py 4 -v 2
   the square of 4 equals 16
   $ python prog.py 4 -v 3
   16

우리 프로그램의 버그를 드러내는 마지막 것을 제외하고는 그럴듯해 보입니
다. "--verbosity" 옵션이 받아들일 수 있는 값을 제한해서 고쳐봅시다:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("square", type=int,
                       help="display a square of a given number")
   parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
                       help="increase output verbosity")
   args = parser.parse_args()
   answer = args.square**2
   if args.verbosity == 2:
       print "the square of {} equals {}".format(args.square, answer)
   elif args.verbosity == 1:
       print "{}^2 == {}".format(args.square, answer)
   else:
       print answer

출력은 이렇습니다:

   $ python prog.py 4 -v 3
   usage: prog.py [-h] [-v {0,1,2}] square
   prog.py: error: argument -v/--verbosity: invalid choice: 3 (choose from 0, 1, 2)
   $ python prog.py 4 -h
   usage: prog.py [-h] [-v {0,1,2}] square

   positional arguments:
     square                display a square of a given number

   optional arguments:
     -h, --help            show this help message and exit
     -v {0,1,2}, --verbosity {0,1,2}
                           increase output verbosity

변경 내용은 오류 메시지와 도움말 문자열에도 반영됩니다.

이제 상세도를 다루는 다른 접근법을 사용해 봅시다, 이 방법은 꽤 널리 사
용됩니다. 또한, 이 방법은 CPython 실행 파일이 자신의 상세도를 처리하는
방식과도 일치합니다 ("python --help" 의 결과를 확인하십시오):

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("square", type=int,
                       help="display the square of a given number")
   parser.add_argument("-v", "--verbosity", action="count",
                       help="increase output verbosity")
   args = parser.parse_args()
   answer = args.square**2
   if args.verbosity == 2:
       print "the square of {} equals {}".format(args.square, answer)
   elif args.verbosity == 1:
       print "{}^2 == {}".format(args.square, answer)
   else:
       print answer

특정 옵션 인자를 지정한 횟수를 계산하기 위해 "count" 라는 또 다른 액션
을 도입했습니다:

   $ python prog.py 4
   16
   $ python prog.py 4 -v
   4^2 == 16
   $ python prog.py 4 -vv
   the square of 4 equals 16
   $ python prog.py 4 --verbosity --verbosity
   the square of 4 equals 16
   $ python prog.py 4 -v 1
   usage: prog.py [-h] [-v] square
   prog.py: error: unrecognized arguments: 1
   $ python prog.py 4 -h
   usage: prog.py [-h] [-v] square

   positional arguments:
     square           display a square of a given number

   optional arguments:
     -h, --help       show this help message and exit
     -v, --verbosity  increase output verbosity
   $ python prog.py 4 -vvv
   16

* 예, 이제 이전 버전의 스크립트처럼 ("action="store_true"" 와 유사하
  게 ) 플래그가 되었습니다. 출력되는 불평이 설명됩니다.

* 또한 "store_true" 액션과 비슷하게 작동하기도 합니다.

* 이제 여기에서 "count" 액션이 제공하는 것을 보여줍니다. 이런 종류의
  사용법을 전에도 보았을 것입니다.

* And, just like the "store_true" action, if you don't specify the
  "-v" flag, that flag is considered to have "None" value.

* 예측하듯이, 플래그의 긴 형식을 지정하면, 같은 출력이 얻어져야 합니
  다 .

* 안타깝게도 스크립트가 얻은 새로운 기능에 대한 도움말 출력은 그다지
  유익하지 않지만, 스크립트의 문서를 개선하면 항상 해결할 수 있습니다
  (예, "help" 키워드 인자를 사용해서).

* 마지막 출력은 우리 프로그램의 버그를 노출합니다.

고칩시다:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("square", type=int,
                       help="display a square of a given number")
   parser.add_argument("-v", "--verbosity", action="count",
                       help="increase output verbosity")
   args = parser.parse_args()
   answer = args.square**2

   # bugfix: replace == with >=
   if args.verbosity >= 2:
       print "the square of {} equals {}".format(args.square, answer)
   elif args.verbosity >= 1:
       print "{}^2 == {}".format(args.square, answer)
   else:
       print answer

그러면 이렇게 됩니다:

   $ python prog.py 4 -vvv
   the square of 4 equals 16
   $ python prog.py 4 -vvvv
   the square of 4 equals 16
   $ python prog.py 4
   Traceback (most recent call last):
     File "prog.py", line 11, in <module>
       if args.verbosity >= 2:
   TypeError: unorderable types: NoneType() >= int()

* 첫 번째 출력은 잘 동작하고, 앞에서 나온 버그를 고칩니다. 즉, 2보다
  크거나 같은 (>=) 모든 값을 최대의 상세도로 취급하고 싶습니다.

* 세 번째 결과가 좋지 않습니다.

이 버그를 고쳐 봅시다:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("square", type=int,
                       help="display a square of a given number")
   parser.add_argument("-v", "--verbosity", action="count", default=0,
                       help="increase output verbosity")
   args = parser.parse_args()
   answer = args.square**2
   if args.verbosity >= 2:
       print "the square of {} equals {}".format(args.square, answer)
   elif args.verbosity >= 1:
       print "{}^2 == {}".format(args.square, answer)
   else:
       print answer

또 다른 키워드 "default" 를 소개했습니다. 다른 int 값과 비교하기 위해
"0" 으로 설정했습니다. 기본적으로, 옵션 인자가 지정되지 않으면 "None"
값을 갖게 되고, 그것은 int 값과 비교될 수 없음을 (그래서 "TypeError"
예외를 일으킵니다) 기억하십시오.

그리고:

   $ python prog.py 4
   16

여러분은 지금까지 배운 것만으로도 아주 멀리 갈 수 있으며, 우리는 단지
표면을 긁었을 뿐입니다. "argparse" 모듈은 매우 강력합니다. 이 자습서를
끝내기 전에 좀 더 탐색해 보겠습니다.


조금 더 발전시키기
==================

우리의 작은 프로그램을 확장하여 제곱만이 아닌 다른 거듭제곱을 수행하기
를 원하면 어떻게 될까요:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("x", type=int, help="the base")
   parser.add_argument("y", type=int, help="the exponent")
   parser.add_argument("-v", "--verbosity", action="count", default=0)
   args = parser.parse_args()
   answer = args.x**args.y
   if args.verbosity >= 2:
       print "{} to the power {} equals {}".format(args.x, args.y, answer)
   elif args.verbosity >= 1:
       print "{}^{} == {}".format(args.x, args.y, answer)
   else:
       print answer

출력:

   $ python prog.py
   usage: prog.py [-h] [-v] x y
   prog.py: error: the following arguments are required: x, y
   $ python prog.py -h
   usage: prog.py [-h] [-v] x y

   positional arguments:
     x                the base
     y                the exponent

   optional arguments:
     -h, --help       show this help message and exit
     -v, --verbosity
   $ python prog.py 4 2 -v
   4^2 == 16

지금까지는 표시되는 텍스트를 *변경* 하기 위해 상세도를 사용했습니다.
다음 예제는 대신 *더 많은* 텍스트를 표시하기 위해 상세도를 사용합니다:

   import argparse
   parser = argparse.ArgumentParser()
   parser.add_argument("x", type=int, help="the base")
   parser.add_argument("y", type=int, help="the exponent")
   parser.add_argument("-v", "--verbosity", action="count", default=0)
   args = parser.parse_args()
   answer = args.x**args.y
   if args.verbosity >= 2:
       print "Running '{}'".format(__file__)
   if args.verbosity >= 1:
       print "{}^{} ==".format(args.x, args.y),
   print answer

출력:

   $ python prog.py 4 2
   16
   $ python prog.py 4 2 -v
   4^2 == 16
   $ python prog.py 4 2 -vv
   Running 'prog.py'
   4^2 == 16


충돌하는 옵션들
---------------

지금까지 우리는 "argparse.ArgumentParser" 인스턴스의 두 가지 메서드로
작업 해왔습니다. 세 번째를 소개합시다,
"add_mutually_exclusive_group()". 이것은 서로 배타적인 옵션을 지정할
수 있도록 합니다. 새로운 기능을 더 잘 이해할 수 있도록 프로그램의 나머
지 부분을 변경해 보겠습니다: "--quiet" 옵션을 도입하는데 "--verbose"
의 반대입니다:

   import argparse

   parser = argparse.ArgumentParser()
   group = parser.add_mutually_exclusive_group()
   group.add_argument("-v", "--verbose", action="store_true")
   group.add_argument("-q", "--quiet", action="store_true")
   parser.add_argument("x", type=int, help="the base")
   parser.add_argument("y", type=int, help="the exponent")
   args = parser.parse_args()
   answer = args.x**args.y

   if args.quiet:
       print answer
   elif args.verbose:
       print "{} to the power {} equals {}".format(args.x, args.y, answer)
   else:
       print "{}^{} == {}".format(args.x, args.y, answer)

프로그램은 이제 더 간단 해졌으며, 데모를 위해 일부 기능을 잃어버렸습니
다. 어쨌든, 출력은 이렇습니다:

   $ python prog.py 4 2
   4^2 == 16
   $ python prog.py 4 2 -q
   16
   $ python prog.py 4 2 -v
   4 to the power 2 equals 16
   $ python prog.py 4 2 -vq
   usage: prog.py [-h] [-v | -q] x y
   prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
   $ python prog.py 4 2 -v --quiet
   usage: prog.py [-h] [-v | -q] x y
   prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose

따라가기 쉽습니다. 여러분이 얻는 유연성을 볼 수 있도록 마지막 출력을
추가했습니다. 즉, 긴 형식 옵션을 짧은 형식 옵션과 섞어 쓸 수 있습니다.

결론을 내리기 전에, 여러분은 아마도 사용자들이 모를 경우를 대비해서 프
로그램의 주요 목적을 알려주기를 원할 것입니다:

   import argparse

   parser = argparse.ArgumentParser(description="calculate X to the power of Y")
   group = parser.add_mutually_exclusive_group()
   group.add_argument("-v", "--verbose", action="store_true")
   group.add_argument("-q", "--quiet", action="store_true")
   parser.add_argument("x", type=int, help="the base")
   parser.add_argument("y", type=int, help="the exponent")
   args = parser.parse_args()
   answer = args.x**args.y

   if args.quiet:
       print answer
   elif args.verbose:
       print "{} to the power {} equals {}".format(args.x, args.y, answer)
   else:
       print "{}^{} == {}".format(args.x, args.y, answer)

사용법 텍스트의 약간의 차이점에 유의하십시오. "[-v | -q]" 에 주목해야
하는데, "-v" 나 "-q" 를 사용할 수 있지만 동시에 둘 다를 사용할 수는 없
다는 뜻입니다:

   $ python prog.py --help
   usage: prog.py [-h] [-v | -q] x y

   calculate X to the power of Y

   positional arguments:
     x              the base
     y              the exponent

   optional arguments:
     -h, --help     show this help message and exit
     -v, --verbose
     -q, --quiet


맺음말
======

"argparse" 모듈은 여기에 제시된 것보다 훨씬 많은 것을 제공합니다. 문서
는 아주 상세하고 철저하며 예제가 풍부합니다. 이 자습서를 끝내면 압도감
없이 쉽게 소화할 수 있을 겁니다.
