├── .gitignore
├── LICENSE
├── README.md
├── doc
├── dart-tutorial-01.md
├── dart-tutorial-02.md
├── dart-tutorial-03.md
├── dart-tutorial-04.md
├── dart-tutorial-05.md
├── dart-tutorial-06.md
├── dart-tutorial-07.md
├── dart-tutorial-08.md
├── dart-tutorial-09.md
├── dart-tutorial-10.md
├── dart-tutorial-11.md
├── dart-tutorial-12.md
├── dart-tutorial-13.md
├── dart-tutorial-14.md
├── dart-tutorial-15.md
├── dart-tutorial-16.md
├── dart-tutorial-17.md
├── dart-tutorial-18.md
├── dart-tutorial-19.md
├── dart-tutorial-20.md
├── dart-tutorial-21.md
└── dart-tutorial-22.md
└── source
├── .DS_Store
├── darttutorial-06
└── darttutorial-06-01.dart
├── darttutorial-07
├── darttutorial-07-01.dart
├── darttutorial-07-02.dart
├── darttutorial-07-03.dart
├── darttutorial-07-04.dart
└── darttutorial-07-05.dart
├── darttutorial-08
├── darttutorial-08-01.dart
├── darttutorial-08-02.dart
└── darttutorial-08-03.dart
├── darttutorial-09
├── darttutorial-09-01.dart
├── darttutorial-09-02.dart
└── darttutorial-09-03.dart
├── darttutorial-10
├── darttutorial-10-01.dart
├── darttutorial-10-02.dart
├── darttutorial-10-03.dart
├── darttutorial-10-04.dart
├── darttutorial-10-05.dart
├── darttutorial-10-06.dart
├── darttutorial-10-07.dart
└── darttutorial-10-08.dart
├── darttutorial-11
├── darttutorial-11-01.dart
├── darttutorial-11-02.dart
├── darttutorial-11-03.dart
├── darttutorial-11-04.dart
├── darttutorial-11-05.dart
├── darttutorial-11-06.dart
└── darttutorial-11-07.dart
├── darttutorial-12
├── darttutorial-12-01.dart
├── darttutorial-12-02.dart
├── darttutorial-12-03.dart
├── darttutorial-12-04.dart
├── darttutorial-12-05.dart
└── darttutorial-12-06.dart
├── darttutorial-13
└── darttutorial-13-01.dart
├── darttutorial-14
├── darttutorial-14-01.dart
├── darttutorial-14-02.dart
├── darttutorial-14-03.dart
├── darttutorial-14-04.dart
├── darttutorial-14-05.dart
└── darttutorial-14-06.dart
├── darttutorial-15
├── darttutorial-15-01.dart
└── darttutorial-15-02.dart
├── darttutorial-16
└── darttutorial-16-01.dart
└── darttutorial-17
└── darttutorial-17-01.dart
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://www.dartlang.org/guides/libraries/private-files
2 |
3 | # Files and directories created by pub
4 | .dart_tool/
5 | .packages
6 | build/
7 | # If you're building an application, you may want to check-in your pubspec.lock
8 | pubspec.lock
9 |
10 | # Directory created by dartdoc
11 | # If you don't generate documentation locally you can remove this line.
12 | doc/api/
13 |
14 | # Avoid committing generated Javascript files:
15 | *.dart.js
16 | *.info.json # Produced by the --dump-info flag.
17 | *.js # When generated by dart2js. Don't specify *.js if your
18 | # project includes source files written in JavaScript.
19 | *.js_
20 | *.js.deps
21 | *.js.map
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
122 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dart Language Tutorial
2 |
3 | Dart 언어를 통해서 Full Stack 프로그래머가 되도록 하는 Tutorial 입니다.
4 | 원본은 경희대학교 이성원 교수 웹사이트에서도 볼 수 있으며, 주1회 정도로 내용 업데이트시 목차도 함께 업데이트 됩니다.
5 |
6 | 1. Tutorial 개요
7 | 2. 왜 Dart 언어를 선택했는가?
8 | 3. 개발 환경 구축하기
9 | 4. C++ 프로그래머를 위한 Quick Summary Part.1
10 | 5. C++ 프로그래머를 위한 Quick Summary Part.2
11 | 6. Dart 기초문법 - Hello World!
12 | 7. Dart 기초문법 - Variables & Constants
13 | 8. Dart 기초문법 - Number Types
14 | 9. Dart 기초문법 - String Types
15 | 10. Dart 기초문법 - Boolean & Conditional Statement
16 | 11. Dart 기초문법 - Repetition (Loop)
17 | 12. Dart 기초문법 - Functions
18 | 13. MS Visual Code를 통한 Debug
19 | 14. Dart 기초문법 - Class 사용하기
20 | 15. Dart 기초문법 - List
21 | 16. Dart 기초문법 - Set
22 | 17. Dart 기초문법 - Map
23 | 18. Dart 기초문법 - Useful Dart Features
24 | 19. Dart 기초문법 - Exception Handling
25 | 20. Dart 기초문법 - Making Class, Part.1
26 | 21. Dart 기초문법 - Making Class, Part.2
27 | 22. Dart 기초문법 - Making Class, Part.3
28 |
29 | 본 게시글은 다음의 사이트를 통해서 연재를 이어 갑니다.
30 | [ Dart & Flutter 게시글 보기 (2023.10 기준) ]
31 |
Creative Commons License (CC BY-NC-ND)
37 | 38 | 39 | -------------------------------------------------------------------------------- /doc/dart-tutorial-01.md: -------------------------------------------------------------------------------- 1 | # 글을 시작하며 2 | 3 | 4 |2020년을 맞이하여, 소프트웨어 개발자 되기 프로젝트(Be a Programmer)를 시작해 봅니다. 여기서 '개발자'는 2020년도의 소프트웨어 기술 개발 추이를 고려한, 저(이성원 교수)의 지극히 개인적인 취향입니다. 그리고 수업에서 전달하기 어려웠던 이야기들을 여기서 풀어볼까 합니다. 가능하면 2월 부터 1주일에 하나의 글을 올리는 것을 목표로 합니다. 지금 현재 생각하는 내용은 다음과 같습니다.
5 | 6 | 7 | 8 |내용과 순서는 작성하는 시점의 개인적 흥미에 따라서 그때 그때 달라질 겁니다.
13 | 14 | 15 | 16 |Creative Commons License (CC BY-NC-ND)
21 | 22 | -------------------------------------------------------------------------------- /doc/dart-tutorial-02.md: -------------------------------------------------------------------------------- 1 | # 왜 Dart 언어를 선택했는가? 2 | 3 | 4 |Dart 언어를 선택한 이유는 2020년 현재 제가 가장 관심 갖는 프로그래밍 언어 이기도 하지만, 제 전공인 컴퓨터 네트워킹과 취미인 프로그래밍을 함께 할 수 있는 개인적 취향, 그리고 앞으로의 발전 가능성을 고려한 선택으로 이해하면 좋겠습니다. 개인적인 취향 이라고 하면 다소 황당하니, 객관적 근거를 아래의 그림을 토대로 설명해 봅니다.
5 | 6 | 7 | 8 |소프트웨어 개발자들이 사용하는 프로그래밍 언어는 몇개나 될까요? 오픈소스 커뮤니티의 성지인 GitHub에 따르면, 2019년에 업로드 된 프로그램들에 사용된 프로그래밍 언어의 갯수는 총370개 라고 합니다 [출처]. 그림에는 대표적으로 많이 사용 되는 언어들의 최초 Release 년도, 활용 분야(모바일 앱, 서버, 데스크탑, 웹), 그리고 활용 분야별로 사용해야 하는 Framework를 나열하였습니다. Dart를 선택한 이유를 하나 하나 짚어 보면 다음과 같습니다.
13 | 14 | 15 | 16 |첫째로, Full-Stack 프로그래머가 될 수 있습니다. Dart는 유행하는 말로 Full Stack, 다시 말해서 사용자가 사용하는 모바일 앱(안드로이드, iOS)과 서버 프로그램의 개발이 가능한 언어입니다. 특히 Flutter라는 User Interface (UI) Framework를 사용하여, 다양한 모바일 운영체제(안드로이드, iOS)의 UI/UX(User Experience)를 하나의 소스 코드로 개발하는 것이 가능합니다. 통상 Cross-Platform이라고 불리는 개발 방법 입니다. 이렇게 사용자 쪽에 가까운 어플리케이션(줄여서 앱)을 Front-End라고 할때, Front-End가 정보를 가져오는 반대 방향에 서버(Server)라고 불리우는 대형 컴퓨터들이 있으며, 이를 통상 Back-End라고 부르는데, Dart는 이런 서버 프로그램의 개발도 가능합니다. 이외에 MS윈도우/MacOS/Linux와 같은 데스크탑 컴퓨터의 프로그램과 웹의 개발도 가능합니다. 특히 다른 언어들은 활용 분야별로 서로 다른 Framework를 사용하기에, 알아야 하는 사항들이 다소 많고, 활용해야 하는 기술이 많으나, Dart는 최근의 기술로서 (비록 많은 부분에서 아직 미진한 부분이 많지만) 다양한 분야를 Flutter 하나의 기술로 일관된 UI/UX를 제공하도록 하고 있습니다.
17 | 18 | 19 | 20 |사실 이 부분에서는 개인적으로 고민이 있는 부분이기도 합니다. 최근에 취미 삼아 손댄 기술은 JavaScript 입니다. JavaScript는 Dart를 제외하면, Full-Stack 프로그래머라는 단어에 가장 부합하는 언어입니다. 소프트웨어를 전공으로 하지 않는 학생들과 개발자들이 많이 사용하는 언어이기도 합니다. ES6(ECMA Script 6) 버전 이후로는 언어적인 능력도 왠만한 프로그래밍 언어 수준에 육박하는 좋은 언어가 되었습니다. 성능면에서도 (아래 네번째 문장의 참조 자료를 보면 알겠지만) 계속적으로 개선이 이루어 지는 언어입니다. 서버 프로그램 개발시에도 Node.js를 사용하는 경우는, (C/C++과 비교했을 경우는 상대적으로 부하가 큰 편이지만,) Python 등의 언어와 비교하면 탁월한 수준의 성능을 나타내고 있습니다. 하지만 활용 분야별로 파편화된(fragmented) 프레임워크들이 너무 많아 학습이 많이 필요한 부분과, Script 언어에서 시작한 연유로 디버그에 애로가 많다는 점을 고려한다면, Dart의 첫인상은 저 같이 C/C++을 뿌리로 두는 프로그래머 들에겐 확실하게 매력적인 언어라고 생각합니다. 그렇다고 JavaScript가 구축한 영역을 당장 Dart가 대체 하기는 어려울 겁니다. 적은 자료/커뮤니티를 고려하면, Dart는 좀 더 지켜볼 언어로 사료됩니다. 그래도 재미삼아 시작하는 프로젝트인데, Dart를 써보는 것도 나쁘진 않겠죠?
21 | 22 | 23 | 24 |둘째로, Google(구글)이 만든 언어 입니다. 사실 구글은 Dart를 2011년에 공개했습니다. 하지만 지금까지 Dart를 아는 사람은 많지 않은 것이 사실입니다. Dart를 다룬 해외/국내 서적과 인터넷의 정보가 매우 적은 편입니다. 따라서 구글이 만들었다는 이유만으로 언어를 배우는 것은 사실 큰 의미는 없습니다. (2018년 초반에는 배울 필요가 없는 언어에서도 최상위권에 속한 경험이 있는 언어입니다.) 그런데, 이렇게 인기 없던 Dart는 2018년 대비 2019년에 관심도가 무려 532% 증가합니다 [출처]. Python이 151%인 것을 감안하면 엄청난 증가율 입니다. 이유는 첫번째 이유에서 언급한 Flutter의 출현입니다. 2017년 Flutter의 알파 버전이 나온 후, 긴가 민가 했던 분위기는, 2018년 12월 4일에 구글이 Flutter 1.0의 최초 안정된 버전을 공개 하면서 급 반전에 성공합니다. 이후 안드로이드와 iOS에 동일 어플리케이션을 개발해야 하는 회사들의 성공사례가 이어지면서, 구글이 이 기술을 계속 가져 갈 것이라는 기대감과 이제 제대로 써 볼만 하다는 신뢰감이 늘게 됩니다.
25 | 26 | 27 | 28 |셋째로, Adobe가 관심을 갖는 언어 입니다. 모바일 앱을 개발할 때 매우 중요한 분야가 UI/UX 분야입니다. 통상 앱이나 웹서비스는 사용자가 접하는 UI/UX를 먼저 설계하며, 이를 전문 디자이너 들이 작업을 하는 경우가 대부분 입니다. 이렇게 정리한 UI/UX를 개발자가 실현해야 하는데, 문제는 둘을 이어주는 기술이 많이 불편합니다. UI/UX 디자이너들은 사용자가 접하는 화면을 채울 그림/메뉴 들과 이들 간의 상호 작용을 만들어 냅니다. 이때에 작업한 자원(그림, 아이콘, 폰트, 내용, 메뉴 체계, 프로토타입 등)을 개발자가 효과적으로 재활용 한다면, 개발 결과물과 당초의 디자인 간의 차이도 줄고, 개발도 용이합니다. 이런 분야에 많이 활용되는 도구들은 그림에서 나타난 것과 같이, Sketch, Zeplin, InVision 등 입니다. 세개의 소프트웨어를 써야 하는 번거러움도 문제지만, 디자이너의 결과물을 개발자가 받아서 사용하기도 어려운 형태입니다. 이 와중에 Adobe가 모바일 앱 분야의 UI/UX 도구로 만들어서 점차 사용자가 늘어나는 소프트웨어인 Adobe XD가 있는데, Adobe와 구글은 함께 협력하여, Adobe XD의 결과물을 Dart 언어의 Flutter Framework로 변환해 주는 Plugin을 함께 만들었고, 오픈소스로 공개하였습니다 [출처]. 이런 이유로 잘만 된다면, 미래에는 UI/UX 부터 모바일/데스크탑/서버/웹을 관통하는 개발체계를 갖춘 유일 무이한 언어로서 Dart가 자리 매김할 가능성도 있습니다.
29 | 30 | 31 | 32 |넷째로, 비교적 최근에 만들어진 언어 입니다. 90년대 중반까지 FORTRAN, COBOL, BASIC, PASCAL 등 목적에 맞는 언어들을 사용하다가, C언어가 인기를 얻으면서 모든 분야를 통일하는 듯 했습니다. 하지만, 이후로 C++이 나오기는 했지만, 웹기술 등에 대한 대응이 어려웠고, 언어자체의 난이도도 높기에, 프로그래밍 언어는 다시 춘추전국시대로 들어갑니다. 이후 프로그래밍 언어는 목적에 맞도록 설계되고, 특정 분야가 정해지면 특정 언어를 사용하는 분위기로 되었습니다. 이런 입장에서 운영체제나 커널을 다루는 분야에서는 C/ C++/ Assembly 외의 대안은 없어 보이지만, 그외 분야라면 (거의 모든 분야를 다루는) 범용 언어로서 Dart를 권해 볼만 합니다. 특히 언어적으로 C/C++과 유사하면서, Python 스러운 부분도 섞여 있는 등 나름 가장 최신의 언어에 속하기에, 기존 언어들의 장단점을 잘 모아놓은 언어로 보입니다. 애플도 Swift를 오픈소스로 헌정했지만, 애플 장비 들을 제외하면 이렇다 할 도구들이 미비한 상황에서, Dart는 빠르게 지원 도구들을 늘리고 있고, Android Studio와 Xcode에도 통합이 되는 등 기존 개발환경을 용이하게 활용 가능합니다.
33 | 34 | 35 | 36 |다소 정성적인 이유로 Dart를 선택한 감이 있습니다. 정량적인 부분을 함께 고려해야 하는데, 아직까지 Dart를 정량적인 부분에서 성능 평가한 사례는 찾기 어렵습니다. 이 부분은 개인적으로 2020년에 연구 주제로 삼아 심심할때 성능평가와 분석을 해 볼 예정이니, 결과가 나오면 업로드 하도록 하겠습니다. 참고로 다른 프로그래밍 언어별 성능 평가들은 아래의 사이트 들에서 확인 가능합니다.
37 | 38 | 39 | 40 |Dart에 대한 소개는 간단히 마치고, 이제 Dart를 실제 사용해 보도록 합시다.
45 | 46 | 47 | 48 |Creative Commons License (CC BY-NC-ND)
53 | 54 | -------------------------------------------------------------------------------- /doc/dart-tutorial-03.md: -------------------------------------------------------------------------------- 1 | # 개발 환경 구축하기 2 | 3 | 4 |Dart 언어는 오픈소스로 공개 되어 있습니다. 공식 웹사이트인 dart.dev에서 Dart 언어에 대한 다양한 자료를 구할 수 있고, 커뮤니티 정보도 얻을 수 있으며, Dart 언어를 실행할 소프트웨어를 운영체제에 맞춰서 다운로드 받아 설치 할 수 있습니다. 현재 Dart 언어는 MS 윈도우, 애플 MacOS, 리눅스 운영체제에 설치가 가능합니다. Dart 언어를 설치하기 위하여 다음의 작업을 수행합니다.
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |설치의 편의성을 위하여, MS 윈도우에 설치하는 경우는 "Install using a setup wizard"를 권장합니다. 별다른 작업 없이 쉽게 설치가 될 것 입니다. 리눅스의 경우는 apt-get 혹은 Debian package manager를 활용 할 수 있으니, 선호도에 따라서 선택합니다. MacOS는 Homebrew를 활용 합니다.
13 | 14 | 15 | 16 |Dart 언어의 설치와 운영체제 별로 필요한 환경 설정을 마치면 Dart를 사용할 수 있습니다. 콘솔창(MS 윈도우의 DOS창, MacOS/리눅스의 Terminal)을 열어서 dart를 실행하면, 아래의 그림과 같이 command line interface (CLI)로 Dart를 실행한 결과를 확인할 수 있습니다. 참고로 아래의 실행 결과 그림은 MacOS 상에서 실행한 결과이며, 추가로 dart 언어 프로그램의 버전(dart --version)을 확인한 결과도 같이 나타나 있습니다.
17 | 18 | 19 | 20 |
설치한 CLI 환경 만으로도 Dart 언어를 사용하여 소프트웨어 개발이 가능하지만, 생산성 향상을 위하여 좋은 도구를 사용하는 것도 매우 중요합니다. 여기서는 개발용 컴퓨터에 개발환경을 설치해서, 개발용 컴퓨터가 인터넷에 연결되지 않아도 소프트웨어 개발이 가능하지만, 설치를 해야해서 다소 불편한 오프라인 개발 환경을 먼저 이야기 합니다. 그리고 컴퓨터에 소프트웨어 설치과정 없이 웹브라우저만 있으면 Dart 언어를 경험할 수 있으나, 인터넷에 개발용 컴퓨터가 반드시 연결되어야 하는 온라인 개발 환경을 다음으로 설명합니다.
Dart 공식 사이트에는 Dart 언어를 지원하는 오프라인/온라인 개발 도구를 권장하고 있습니다 [출처]. 우리가 사용할 오프라인 개발 환경용 소프트웨어는 MS사가 오픈소스로 공개한 Visual Code 입니다 [출처]. MS Visual Code는 오픈소스 소프트웨어이며, 별도의 비용을 지불하지 않습니다. 지원하는 운영체제가 MS 윈도우, MacOS, 리눅스로 다양하며, 운영체제가 다르더라도 사용자 인터페이스와 기능이 동일하여, 다양한 플랫폼에서 개발하는 개발자에게 일관된 경험을 제공합니다. 특히 외부 Plugin (Extension)을 추가하여 기능을 확장할 수 있습니다. 특히 In-line Trace 기능을 제공하는데, 이는 소스 코드를 한줄 한줄 실행하는 기능으로서, 디버그를 할 때에 매우 요긴한 기능입니다. 그러면 Dart 언어를 위한 오프라인 개발 환경을 구축해 봅니다.
33 | 34 | 35 | 36 |첫째로, MS Visual Code는 공식 사이트에 접속하여, 본인의 운영체제에 맞는 프로그램을 설치하면 됩니다 [출처]. 별다른 어려움 없이 설치가 이루어 지며, 설치할 때 별다른 이유가 없다면, "stable" 버전을 선택 합니다.
37 | 38 | 39 | 40 |둘째로, MS Visual Code에서 Dart를 실행하기 위한 Extension을 설치 합니다. 먼저 MS Visual Code를 실행합니다. 실행한 화면에서, 왼쪽에 위치한 아이콘 중 Extension을 의미하는 다음의 아이콘을 클릭 합니다.
41 | 42 | 43 | 44 |
Extension 아이콘을 클릭하면, Visual Code 왼쪽 상단에 "마켓플레이스에서 확장 검색"이라는 글씨가 나타나며, 여기에 직접 dart를 타이핑 한 후, 엔터를 칩니다. 그러면, 아래의 그림에서 숫자로 된 버전은 다르겠지만, Dart 언어 Extension 기능을 찾을 수 있습니다.
Visual Code가 설치되어 있지 않으면, 우측 하단의 톱니바퀴 아이콘 대신, "Install"이라는 글자가 있는 버튼이 나타납니다. 이 단추를 클릭하면 Visual Code에 Dart Extension을 설치하게 됩니다. 혹은 위의 그림을 누르면 오른쪽 화면에 해당 Extension에 대한 자세한 설명이 아래와 같이 나타납니다.
위의 그림은 Visual Code에 Dart Extension이 설치된 이후의 화면이며, Visual Code에 Dart Extension이 설치되어 있지 않으면, "사용안함"이라는 글자 대신 "사용함" 혹은 "Install"이라는 글자가 있습니다. 설치를 위하여 이를 누르면 Dart Extension이 Visual Code에 설치됩니다.
이제 Visual Code에서 Dart 언어로 소프트웨어를 개발하고 디버그 할 수 있게 되었습니다. 이제 Visual Code는 Dart 언어 Extension이 업데이트 되면, 개발자에게 알려줘서 업데이트를 유도할 것 입니다. Visual Code의 사용 방법은 추후 다루기로 하고, 이번 글에서는 설치에 만 집중하도록 합니다.
69 | 70 | 71 | 72 |Dart 공식 사이트에 명시된 Dart 언어를 지원하는 온라인 개발 도구인 DartPad는 별도의 오프라인 소프트웨어 도구를 설치하지 않아도 됩니다 [출처]. 웹브라우저만 있으면, 언제 어디서나 Dart 언어를 통한 소프트웨어 개발을 해 볼 수 있고, 굳이 컴퓨터가 아니더라도 테블릿 컴퓨터와 같은 도구를 사용해서도 개발이 가능합니다. 사용법은 매우 단순하며, 그냥 DartPad 웹사이트에 접속하면 됩니다 [출처]. 그러면 아래와 같은 화면을 볼수 있습니다.
77 | 78 | 79 | 80 |
준비된 예제 프로그램을 실행 할 수 도 있습니다. 오른쪽 위의 "Samples" 메뉴를 누르면, 이미 제공되는 예제 중 하나를 선택 할 수 있습니다. 여기서 "Hello World"를 선택하면, 왼쪽 하단의 화면에 Dart로 만들어진 "Hello World" 프로그램의 소스 코드가 나타나는 것을 볼 수 있습니다. 이제 실행을 하기 위하여, 중간 가운데에 있는 "RUN" 버튼을 누릅니다. 그러면, 아래 그림과 같이, 오른쪽 화면에 수행 결과가 나타난 것을 볼 수 있습니다.
온라인 개발환경은 오프라인 개발환경 대비 기능적 제약이 있으나, Dart 언어 자체를 학습하기 위한 단계에서는 편리하게 사용할 수 있는 방법 입니다. 앞으로 Dart 언어의 기본 문법을 익히면서 쉽고 편하게 실행해 보고 싶다면, 위의 그림에서 왼쪽의 Dart 언어로 쓰여져 있는 부분을 지우고, 본인이 실행하고 싶은 내용으로 채운 후 실행하면 됩니다.
개발 환경도 구축 했으니, 이제 Dart 언어 속으로 들어가 보도록 합니다.
97 | 98 | 99 |Creative Commons License (CC BY-NC-ND)
107 | 108 | -------------------------------------------------------------------------------- /doc/dart-tutorial-04.md: -------------------------------------------------------------------------------- 1 | # C++ 프로그래머를 위한 Quick Summary Part.1 2 | 3 | 4 |개인적으로 초등학교때 BASIC과 PASCAL에 손을 대면서 프로그래밍을 접했습니다. 그리고 대학교에서 C, C++, Think-C, Assembly, PROLOG, LISP, FORTRAN, COBOL, Java 등의 다양한 언어를 다룬 것 같습니다. 회사에서는 여유가 없으니, C++만 구사했었는데, 교수라는 직업을 가지면서, 다시 취미 생활을 시작하여 JavaScript, HTML, CSS, Go, C#, Python 등을 조금씩 건드려 본 것 같습니다. 그러다 보니 느낀 점은, 최근의 언어들은 기존의 언어들을 충분히 겪어본 사람들이 만들다 보니, 서로 다른 언어 간에도, 유사한 문법들이 언어간에 나타난 다는 점입니다. 이런 경험을 토대로, 이미 C++과 Python 정도를 아는 사람을 위한 Dart 언어 기본 문법의 속성 요약을 2회에 걸쳐서 수행해 봅니다. 언어가 지원하는 문법이 방대한데, 하나 하나 모두 알고 프로그래밍 하기 보다는, 속성으로 살펴본 후 바로 프로그램 개발을 시작하는 것도 나쁘지 않다는 생각에서 입니다. 먼저, Part.1에서는 기초 문법에 대해서 다루며, 다음의 Part.2에서는 클래스와 Dart 언어의 특징 중 하나인 비동기 작업(async)에 대해서 다루겠습니다.
5 | 6 | 7 | 8 |Dart를 처음 접하면서, C++과 Python에서 볼 수 있는 문법적 특성을 느꼈는데, 이는 아마도 Dart 언어를 만든 사람인 Gilad Bracha가 Java 프로그래밍 언어를 만드는데 참여한 사람이여서 그렇지 않을까 싶습니다. 이제는 Oracle Database과 오픈소스 Database인 MySQL을 소유하고 Oracle사는, Sun이라는 과거 UNIX 계열 운영체제인 Solaris와 자체 CPU인 Spark를 사용한 Workstation으로 유명했던 회사입니다. Gilad Bracha는, 현재 Toitware 라는 IoT 전용 운영체제 회사에서 새로운 운영체제와 언어를 만들고 있지만, Sun에서 Java 언어와 이를 위한 Virtual Machine을 개발했던 사람입니다. 따라서 C++과 Python을 익힌 사람이라면, Dart 언어를 배우면서, C++의 사촌인 Java를 만든 Gilad Bracha에 의해서, 저와 비슷한 문법적 경험을 할 것 같습니다. 이에 여기서는 C++과 대비되는 Dart 언어의 차이점을 빠르게 리뷰할까 합니다. 이 글에서는 문법적인 차이점만 빠르게 설명하므로, 개발 도구를 통한 수행과 결과는 별도로 설명하지 않습니다.
9 | 10 | 11 | 12 |Dart도 C++과 같이 main()를 반드시 가져야 하며, 역할은 동일합니다. main()의 return 값이 없는 경우에 대해서, C++과 동일하게 void를 return 값의 타입으로 declare하며, 입력 파라메타가 없는 경우는, 입력 파라메타 부분을 비워두면 됩니다. 아래는 Dart 언어의 가장 간단한 Hello World 프로그램 입니다.
17 | 18 | 19 | 20 |void main() { 21 | print('Hello, World!'); 22 | } 23 |24 | 25 | 26 | 27 |
main()의 declare를 수행하는 첫 줄은 C++과 동일한 것을 알 수 있다. 함수의 본문을 define 할 때, 중괄호인 {}로 열고 닫는 것도 C++과 같습니다. 그리고 statement가 끝나는 지점에 semi-colon를 타이핑 하는 것도 같습니다. 터미날에 글자를 출력하는 함수는 "cout <<" 구문을 사용하지 않고, print 함수를 사용하는 것은 차이가 있습니다. 또한, 문자열을 열고 닫을 때에는, 인용구문 기호(" " 혹은 ' ')을 사용하는 것은 C++과 동일합니다.
28 | 29 | 30 | 31 |Dart의 주석(Comment) 처리는 C++과 동일하다. 즉, 한 줄을 주석으로 만드는 경우는, 줄의 시작을 //으로 하면 되고, 복수의 줄을 주석 처리 할때는, /*로 시작하고 */로 마치면 된다.
36 | 37 | 38 | 39 |Dart도 C++과 같이 내장 숫자형 데이타 타입이 있습니다. C++과 유사하게 Dart 언어에서는 정수(integer), 실수(double), 이진(boolean) 타입을 지원 합니다. 먼저 main() 안에 정수, 실수, 이진 타입의 변수를 만들어서 임의의 값을 치환한 후, 값을 터미날에 출력하는 다음의 소스 프로그램을 참조 합니다. 각 print()의 결과 값은, 해당 라인의 오른쪽에 주석으로 적어 두었으니, 이해에 도움이 되기를 바랍니다.
44 | 45 | 46 | 47 |void main() { 48 | int i = 1; 49 | double d = 1.0; 50 | bool b = true; 51 | 52 | print(i); // 1 53 | print(d); // 1.0 54 | print(b); // true 55 | } 56 |57 | 58 | 59 | 60 |
Dart의 integer와 C++의 integer를 비교할때, 첫번째 차이점은 C++의 숫자형 데이타 타입은 C에서 유래 했기 때문에, 객체지향 클래스 기반의 객체가 아닌, 단순히 N개의 바이트로 구성된 컴퓨터 메모리 지만, Dart에서는 클래스 기반의 객체로 만들어 진다는 점 입니다. 둘째로 C++에서 숫자형 데이타 타입이 갖는 최대값과 최소값은 CPU와 운영체제가 몇 Bit 형 인가에 따라 가변적 이지만, Dart 언어의 경우는 현재 64 bit 이상으로 지원하지 않도록 제한되어 있으며, Dart VM(Virtual Machine)을 사용하는 경우는 최소 -2의 63승에서 최대 2의 63승 빼기 1의 값을 갖습니다. Dart 언어가 가지는 특징 중의 하나로서, Dart 프로그램을 JavaScript 프로그램으로 변환하는 기능을 사용하는 경우는, 최소 -2의 53승에서 최대 2의 53승 빼기 1의 값을 갖습니다.
61 | 62 | 63 | 64 |Dart의 double은 64 bit의 실수형 데이타 타입으로서, IEEE 754 표준을 따릅니다 [출처]. C++에서와 같이, 실수형 값을 다루는데 사용하는 점에서는 유사하나, 앞서의 integer 타입과 마찬가지로 클래스 기반의 객체입니다.
65 | 66 | 67 | 68 |Boolean 데이타 타입은, C++에서와 같이, 참(true)과 거짓(false) 값을 다루는데 사용하는 점에서는 유사하나, 앞서의 integer 및 double 데이타 타입과 마찬가지로, Dart에서는 클래스 기반의 객체로 동작합니다.
69 | 70 | 71 | 72 |Dart에서는 위처럼 Built-in Data Type을 직접적으로 언급하기 보다는, Dart 언어가 알아서 타입을 유추하는 type inference 기능을 활용하는 것이 일반적 입니다. 앞서의 Built-int Number Types에서 설명한 프로그램을 Dart의 동적인 type inference 형태로 바꾼 소스 프로그램이 아래에 나타나 있습니다. 이 경우, integer, double 그리고 boolean의 구체적인 데이타 타입 대신 variable을 의미하는 var로 표현이 바뀐 것을 볼수 있습니다. 이 경우 var 타입의 변수는 오른쪽의 객체에 대한 레퍼런스(reference)를 왼쪽의 변수에 저장합니다.
77 | 78 | 79 | 80 |void main() { 81 | var i = 1; 82 | var d = 1.0; 83 | var b = true; 84 | 85 | print(i); // 1 86 | print(d); // 1.0 87 | print(b); // true 88 | } 89 |90 | 91 | 92 | 93 |
주의할 점은, 이미 값을 저장하고 있는 var 타입 변수에 다른 타입의 값을 저장하는 경우입니다. 만약 실수를 저장하는 var 변수의 값을 정수 변수로 바꾼다면, 새롭게 주어진 정수는 실수로 바뀌어 에러 없이 저장됩니다. 하지만 정수형 var 변수에 실수 타입의 값을 저장하려 한다면, Dart는 에러를 나타내고 진행을 중지합니다. 만약, 변수가 프로그램의 실행 중에 타입과 상관없이 값을 저장하기를 원한다면, var 대신 dynamic 타입으로 변수를 생성하면 됩니다. 아래는 var 타입이 아닌 dynamic을 사용하여 변수를 만든 후, 다른 타입의 값으로 치환하는 예제를 보여주며, 문제 없이 동작하는 것을 볼 수 있습니다.
94 | 95 | 96 | 97 |void main() { 98 | dynamic i = 1; 99 | dynamic d = 1.0; 100 | i = 2.0; 101 | d = 2; 102 | 103 | print(i); // 2.0 104 | print(d); // 2 105 | } 106 |107 | 108 | 109 | 110 |
프로그램을 수행하면서 값이 수시로 바뀌는 변수는 var (혹은 dynamic) 타입으로 선언합니다. 프로그램의 수행 중 변하지 않는 상수 값을 저장하는 경우에, Dart는 C++과 유사하게 const 데이타 타입을 사용합니다. const 데이타 타입의 상수는 compile시에 결정 되므로, 프로그램의 실행 중에 값의 변경은 불가능 합니다. 프로그램이 실행되면서 최초 한번은 값을 저장하지만, 이후로는 해당 값을 상수 값처럼 유지하는 경우를 위해서는 final 타입을 사용합니다.
111 | 112 | 113 | 114 |Dart는 C++의 배열(array)과 STL(Standard Template Library)에 상응하는 데이타 저장 공간으로 List, Set 그리고 Map을 지원합니다. C++의 배열은 단순 컴퓨터 메모리(들)이고 STL은 클래스 타입으로 구현된 점과 비교하여, Dart는 모든 타입을 클래스 타입으로 구현하고 있습니다. 세가지 데이타 타입을 간단하게 구현한 예제는 다음과 같으며, 각각에 대한 결과는 print() 구문의 오른쪽에 주석으로 설명하였습니다.
119 | 120 | 121 | 122 |void main() { 123 | var mylist = [1, 2, 3]; // List 124 | var myset = {'C', 'C++', 'Go', 'Dart', 'Go'}; // Set 125 | var mymap = {1:'apple', 2:'banana'}; // Map 126 | 127 | print(mylist); // [1, 2, 3] 128 | print(myset); // {C, C++, Go, Dart} 129 | print(mymap); // {1: apple, 2: banana} 130 | } 131 |132 | 133 | 134 | 135 |
첫째로 Dart의 List는 C++의 배열과 유사하게, 동일한 타입의 데이타를 복수 개 저장하는 것이 가능합니다. List에 저장한 데이타는 개발자가 저장한 순서를 그대로 유지합니다. Dart에서 List는 대괄호 기호인 [ ]를 사용하여 표현 합니다.
136 | 137 | 138 | 139 |둘째로 Dart의 Set은 C++의 Set STL에 상응하여, 수학의 집합에 해당하는 개념을 지원합니다. 수학의 집합 개념을 지원하기에, 저장하는 데이타에 중복된 값이 없도록 하고, 저장한 데이타의 순서는 의미가 없습니다. Dart에서 Set은 중괄호 기호인 { }를 사용하여 표현 합니다.
140 | 141 | 142 | 143 |셋째로 Dart의 Map은 C++의 Map STL에 상응하여, 'Key' 값과 이에 해당 하는 'Value'의 쌍을 저장하는 것을 지원합니다. Key 값은 중복될 수 없으며, Value 는 중복이 가능합니다. 그리고 데이타가 저장된 순서는 의미가 없습니다. Dart에서 Map은 중괄호 기호인 { }를 사용하여 표현 하는 점에서 Set과 중복되지만, 저장하는 값이 "Key : Value"의 형태이기에, Dart 언어가 괄호의 형태에 상관없이 구분할 수 있습니다.
144 | 145 | 146 | 147 |Dart의 Built-in Collection Type인 List, Set 그리고 Map에 대해서는 추후 기본 문법의 자세한 설명을 하는 부분에서 다시 설명할 것이며, 지금은 단순히 C++ 대비 차이점만 언급 하면서 지나가도록 하겠습니다.
148 | 149 | 150 | 151 |Dart의 조건문(conditional statement)는 C++과 동일합니다. if 구문, else if 구문, 그리고 else, break, continue 구문으로 이루어 지며, 사용하는 괄호도 동일합니다. Dart는 C++과 같이 switch 구문도 지원합니다. 기본적으로 거의 같지만 일부 다른 점이 있습니다. C++에서는 각각의 case 구문을 break로 구분할 수도 있고, 그렇게 하지 않을수도 있습니다. 중첩된 case 구문이 break 구문 없이 작성되면, 위의 case 구문에서 아래의 case 구문으로 프로그램의 수행이 이어지는데 이를 fall-through라고 부르며, 이는 break 구문을 만날때 까지 이어집니다. 만약 중첩된 case 구문들이 하는 일이 없이 중첩만 되다가 마지막에 있는 case 구문에서만 작업을 한다면, Dart와 C++의 차이점은 없습니다. 즉, 다음의 예제는 Dart와 C++에서 차이가 없는 형태입니다.
156 | 157 | 158 | 159 |void main() { 160 | var i = 0; 161 | 162 | switch(i) { 163 | case 0: 164 | case 1: 165 | print("No error"); 166 | } 167 | } 168 |169 | 170 | 171 | 172 |
하지만, 중첩된 case 구문 중 마지막 case 구문이 아닌 경우에도 작업을 하도록 하고 있는데, break 구문이 없어서 fall-through를 수행하고자 했다면, 이는 C++에서는 문제가 없지만, Dart에서는 에러를 만들게 됩니다.
173 | 174 | 175 | 176 |void main() { 177 | var i = 0; 178 | 179 | switch(i) { 180 | case 0: // Error at here 181 | print("0"); 182 | case 1: 183 | print("1"); 184 | } 185 | } 186 |187 | 188 | 189 | 190 |
이런 경우는 continue 구문과 print("0") 구문의 수행후 이동하고자 하는 시점에 Label을 지정하는 방법으로 C++과 유사한 스타일을 구현할 수 있지만, 개인적으로 프로그램의 특정 위치에 Label을 정하는 방법은, 과거 언어의 Go To 라는 문법이 현대 언어에서 지원은 되지만 사용하지 않도록 하는 입장과 같은 입장에서 사용하지 않도록 권장합니다.
191 | 192 | 193 | 194 |기본적으로 Dart의 반복 구문은 C++과 조금의 차이를 제외하면 거의 동일합니다. for, while, do-while, break, continue 구문이 있다는 점에서는 동일 합니다. 작은 차이점으로는 List와 같이 복수의 데이타가 들어 있는 Data Collection Type의 경우 'in' 구문을 사용하여, 복수의 데이타에서 하나 씩 데이타를 추출하는 문법을 쓸수 있는 점 입니다. 아래의 예제는 List 안의 데이타를 하나씩 꺼내는 in 문법과, 전통적인 C++에서 처럼, 반복 횟수를 카운트 하는 변수를 통한 반복을 보여 줍니다. Modern C++에서도 유사한 문법을 제공하여, Data collection type의 경우에 range를 벗어나지 않도록 도움을 주고 있습니다.
199 | 200 | 201 | 202 |void main() { 203 | var mylist = [1, 2, 3]; // List 204 | 205 | for(var item in mylist) { 206 | print(item); 207 | } 208 | 209 | for(var i=0; i<3; i++) { 210 | print(mylist[i]); 211 | } 212 | } 213 |214 | 215 | 216 | 217 |
Dart의 함수는 두 가지 형태로 이루어 집니다. 첫째로 함수의 이름이 있는 일반적인 형태의 함수로서, C++와 Dart 언어의 차이점은 없다고 볼 수 있습니다. 함수의 이름과, 입력 파라메타, 그리고 출력 파라메타의 타입으로 이루어진 형태를 의미합니다. 둘째로 (C++의 function pointer를 사용하는 경우와 유사하게) 함수의 이름이 없는 경우가 있습니다. 주로 짧은 문장을 구현하는 경우이면서, 함수를 입력 파라메타로 전달하는 경우에서 많이 볼 수 있는데, 짧은 함수를 임시로 만들어서 전달하는 목적으로 사용하는 경우입니다. 통상 anonymous 함수라고 부르는 경우인데, 다음 예제를 보면 이해가 바로 될 것으로 봅니다.
222 | 223 | 224 | 225 |void main() { 226 | var a = ((i) => print(i)); 227 | a(1); // 1 228 | } 229 |230 | 231 | 232 | 233 |
위의 예제에서. 변수 a는 이름이 없는 anonymous 함수를 레퍼런스 하게 됩니다. 이 함수의 이름은 없지만, 입력 파라메타를 하나 받도록 되어 있으며, 문장에서 i에 해당합니다. 함수가 해야하는 작업은 화살표인 =>의 오른쪽에 있습니다. 즉, 입력 받은 값을 i로 치환한 후, 그 값을 터미날에 출력하는 단순한 기능입니다. 함수의 실행은 C++의 fucntion pointer 처럼, 해당 함수를 레퍼런스하는 변수인 a의 이름을 활용하여 실행합니다. a를 함수 이름으로 하고, i에 해당 하는 입력 파라메타에 1을 전달한 것으로, 터미날에는 1을 출력하게 됩니다.
234 | 235 | 236 | 237 |Dart에서도 C++처럼 매우 많은 기능을 다양한 방법으로 제공합니다. 지금까지 보여준 예제들은 별도의 라이브러리를 사용하지 않아도 되는 기본 기능으로만 이루어 졌지만, Dart도 다른 언어들과 마찬가지로 Dart가 지원하는 기본 라이브러리 들이 있으며, 개발자 혹은 제3자가 만든 라이브러리를 사용하는 것도 가능합니다. Dart가 기본적으로 제공하는 라이브러리는 다음과 같으며, dart:core는 별도의 문법 없이도, 기본적으로 프로그램에 포함됩니다.
242 | 243 | 244 | 245 |C++의 경우는 소스 코드에 #include 구문을 통해서 헤더 화일을 포함하는 작업이 필요한데, 이와 비교하여 Dart에서는 import 구문을 사용합니다. 위의 built-in 라이브러리를 사용하는 경우와, 개발자 혹은 제3자가 만든 화일을 포함하는 경우에 대한 예제가 아래에 있습니다.
250 | 251 | 252 | 253 |// Importing core libraries 254 | import 'dart:math'; 255 | 256 | // Importing files 257 | import 'directory/subdirectory/filename.dart'; 258 |259 | 260 | 261 | 262 |
Part.1에서 우리는 main() 함수, 각종 데이타 타입, 반복 및 조건 문장, 함수, 라이브러리 등의 기초적인 부분에 대해서 다뤘습니다. 다음 Part.2에서는 클래스, 비동기(async) 동작, 그리고 예외처리를 다룹니다.
267 | 268 | 269 | 270 |Creative Commons License (CC BY-NC-ND)
275 | 276 | -------------------------------------------------------------------------------- /doc/dart-tutorial-05.md: -------------------------------------------------------------------------------- 1 | # C++ 프로그래머를 위한 Quick Summary Part.2 2 | 3 | 4 |지난 Part.1에서는 어떤 언어든 가지고 있는 기초적인 문법에 대해서, Dart와 C++간에 유사한 부분이 많다는 것을 확인했습니다. 이번 글에서는 클래스, 예외처리와 함께 비동기적 동작에 대해서 설명합니다. 이를 위해서 Dart 공식 홈페이지에 있는 간단한 예제 들을 살펴보겠습니다 [출처: Dart 공식 홈페이지의 Language samples].
5 | 6 | 7 | 8 |이 글에서 다룰 메인 주제는 Dart의 클래스 입니다. Part.1의 기초 문법은 Dart와 C++이 크게 다르지 않았지만, 클래스에는 두 언어가 다소 차이가 있는 편입니다. Dart의 클래스에 대해서는 향후 자세하게 다룰 예정이므로, 여기서는 Dart의 클래스의 주요 기능을 짧게 살펴보기로 합니다.
13 | 14 | 15 | 16 |클래스를 시작과 끝인 1번과 28번 라인은 C++과 동일합니다. Class aggregation을 위한 2와 3번 라인도 동일합니다. Dart의 클래스도 C++과 동일하게 constructor를 가지며, C++과 유사한 방식의 constructor가 6번~8번 라인에 나타나 있습니다. 여기서 6번 라인을 보면, C++처럼 this 문법이 있는 것을 볼 수 있으며, 의미는 C++과 유사하다. 입력 파라메타 부분에서 this에 '.' 연산자를 사용함으로써, 입력 받은 두개의 파라메타를 객체 자신의 멤버 변수에 각각 대응하는 것으로 동작합니다. 31번 라인에서, 이 방법을 사용한 constructor 활용 예제가 있습니다. 첫번째 문자열은 Spacecraft 클래스 기반 voyager 객체의 멤버 변수인 name에 매핑되며, 두번째 DateTime 객체는 멤버 변수인 launchDate에 매핑됩니다.
21 | 22 | 23 | 24 |Dart는 다른 방법의 contructor로써, named constructor를 제공합니다. 그림의 11번 라인에 있는 것으로서, 클래스의 이름에 '.' 연사자를 선언하는 부분에서 사용한 후, constructor로 사용할 함수 이름을 사용합니다. 현재 이 constructor는 unlaunched 이며, 입력 파라메타를 문자열로 하나 받습니다. 이렇게 받은 이름을 가지고 멤버 변수인 name을 채우고, 멤버 변수인 launchDate는 null로 채우는 것을 ": this(name, null)" 문법을 통해서 수행하고 있습니다. 이 방법을 사용한 constructor 활용 예제가 35번 라인에 있습니다.
25 | 26 | 27 | 28 |클래스 내부의 멤버 함수는 C++과 유사한 방법으로 만들며, 18번 ~ 27번 라인에서 return 값은 없고 (void), 입력 값도 없는 describe()를 볼 수 있습니다. 이 함수의 기능을 설명 하면, 다음과 같습니다. C++ 개발자는 각 라인을 유추할 수 있을 텐데, Dart가 C++과 다른 부분 중심으로 설명합니다. 18번 라인에서는 클래스 이름과 객체 이름을 출력하는데, print 구문의 문자열 안에 $ 기호를 주면, 해당 기호 뒤의 변수 이름을 출력합니다. 22번 라인에서의 ~/ 연산자는 21번 라인의 주석문을 읽어 보기 바랍니다.
29 | 30 | 31 | 32 |마지막으로 13번과 14번 라인의 설명입니다. => 화살표 문법을 통해서 일종의 함수인 것으로 유추할 수 있습니다. 여기서 핵심은 get 문법으로서, 이는 launchYear 라는 이름의 getter 함수를 선언하는 것을 의미합니다. 33번과 37번 라인을 보면, 함수를 의미하는 () 기호는 없지만, 객체 내부의 값을 꺼내 오는 것을 볼 수 있습니다. 14번 라인에서 launchDate?.year의 의미는, launchDate가 null이 아닌 경우, launchDate의 멤버인 year 값을 추출하는 것을 의미합니다. 아래는 위의 프로그램을 수행한 결과 입니다. 이해한 결과와 동일한지 확인해 보기 바랍니다.
33 | 34 | 35 | 36 |Spacecraft: Voyager I 37 | Launched: 1977 (42 years ago) 38 | 1977 39 | Spacecraft: Voyager III 40 | Unlaunched 41 | null42 | 43 | 44 | 45 |
Dart의 유전의 법칙은 single inheritance이다. 다수의 base class로 부터 derived class를 만들 수 있는 C++과 차이가 있습니다. C++에서 inheritance를 의미하는 ':'에 해당하는 Dart의 문법은 'extends' 입니다. 앞서 [그림 1]의 Spacecraft 클래스를 base class로 하여 Orbiter 클래스를 만드는 예제가 아래에 있습니다.
50 | 51 | 52 | 53 |class Orbiter extends Spacecraft { 54 | num altitude; 55 | Orbiter(String name, DateTime launchDate, this.altitude) 56 | : super(name, launchDate); 57 | }58 | 59 | 60 | 61 |
extends 문법을 제외하면, 다른 부분은 C++과 유사한 형태로 충분히 유추 가능하리라 생각합니다. 일단 derived class에는 새로운 멤버 변수인 altitude 라는 이름의 num 타입 객체임을 알 수 있습니다. 그리고 클래스 이름과 같은 이름의 constructor가 있으며, 3개의 입력 파라메타를 받으면, name과 launchDate는 base class 내부의 멤버 변수로 전달하게 되며, 새롭게 만들어진 altitude 변수를 새번째 입력 파라메타 값으로 저장하게 됩니다.
62 | 63 | 64 | 65 |Dart는 base class가 하나만 존재해야 하지만, C++에서 base class가 다수인 효과를 유사하게 만들수 있는 문법인 mixins 기법이 있습니다. 이때 사용하는 문법은 'with' 이며, 통상 inheritance를 하는 'extends' 구문과 함께 사용합니다. 아래는 앞서 만든 Spacecraft 클래스를 base class로 해서, derived class인 PilotedCraft를 만드는 예제를 보여줍니다. 이때 Piloted 클래스를 'with' 문법으로 엮어줌으로써, PilotedCraft 클래스는 base class의 데이타와 메소드외에, Piloted 클래스의 멤버 변수인 astronauts와 멤버 메소드인 describeCrew()를 추가로 갖게 됩니다.
70 | 71 | 72 | 73 |그림 2의 프로그램을 실행하면서, PilotedCraft 클래스로 만든 pCraft 객체 안에, Spacecraft 클래스와 Piloted 클래스의 데이타 및 메소드가 함께 포함되어 있는 것을 확인 할 수 있습니다. 이의 실행 결과는 다음과 같습니다.
78 | 79 | 80 | 81 |Spacecraft: Voyager I 82 | Launched: 1977 (42 years ago) 83 | 1977 84 | Number of astronauts: 185 | 86 | 87 | 88 |
Dart에서도 C++의 abstract class를 base class로 갖는 derived class의 개념을 지원합니다. 그리고 base class의 method를 derived class에서 overloading 하거나 override 하는 것도 가능합니다. 이에 대한 사항은 향후 Dart의 클래스 문법을 설명하면서 자세하게 설명하도록 하겠습니다. 결론적으로 Dart의 클래스는 C++의 클래스와 같은 부분도 있지만, 차이가 나는 부분도 존재합니다. C++은 제외한 대부분의 객체지향 언어에서 복수의 base class를 갖지 못하도록 하거나, 가능하더라도 사용하지 않도록 권고하는 것을 본다면, Dart의 클래스는 최근 언어들의 특징에 부합한다고 볼수 있습니다.
93 | 94 | 95 | 96 |Dart는 C++과 거의 동일한 exception handling 문법을 제공합니다. 즉, try, catch, throw, rethrow의 문법을 제공합니다.
101 | 102 | 103 | 104 |통상 동기식으로 동작한다는 것은, A작업이 실행되는 동안에 B작업이 작업을 못하고, A작업의 결과가 종료 되기만을 기다리는 기다리는 문제점이 발생할 수 있습니다. 비동기식 방식은 A작업이 진행되고 있는 동안에, 일단 B작업을 병렬로 처리하다가, A작업이 마쳐지면 이에 대한 후속 조치를 진행함으로, 프로그램이 중간에 멈추는 현상을 완화할 수 있습니다. 이를 위해서 Dart는 async와 await 문법을 제공합니다.
109 | 110 | 111 | 112 |Future116 | 117 | 118 | 119 |printWithDelay(String message) async { 113 | await Future.delayed(oneSecond); 114 | print(message); 115 | }
위의 프로그램에서 printWithDelay() 함수가 비동기식으로 동작한다는 의미에서 async 문법이 사용됩니다. 그리고 함수 안을 보면, Future.delayed() 함수 앞에 await 문법을 사용함으로써, delayed() 함수가 실행되는 동안에 다른 작업들이 동시에 이루어 질 수 있도록 합니다. 이후 delayed() 함수의 작업이 종료되면, 이어서 print(message) 구문을 수행한 후, 함수를 리턴하게 됩니다. 따라서, 함수가 비동기식으로 동작함으로 결과가 나중에 return 된다는 의미에서 결과 값의 타입을 기술하는 부분에서 Future 문법을 사용합니다. 이렇게 함으로써, Dart는 여러 작업이 동시에 비동기적으로 진행되는 것을 지원합니다.
120 | 121 | 122 | 123 |참고로 C++는 이런 비동기식 작업을 지원하는 언어적인 기능이 약한 편이였지만, C++11(2011년 표준 버전)에서 처음으로 async 동작을 위한 STL이 추가 되었으며, C++17과 C++20에서 지속적으로 개선 작업이 이루어 지고 있습니다 [출처].
124 | 125 | 126 | 127 |이것으로 C++ 개발자를 위해서 Dart 언어와 C++의 유사점과 차이점을 빠르게 알아보았습니다. 이제부터 Dart 언어의 기본 문법에 대해서 자세하게 알아보도록 하겠습니다.
128 | 129 | 130 | 131 |Creative Commons License (CC BY-NC-ND)
136 | 137 | -------------------------------------------------------------------------------- /doc/dart-tutorial-06.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - Hello World! 2 | 3 | 4 |프로그래밍 언어의 추세를 보면, 언어 자체의 기초 문법은 쉬워지고, 언어가 제공하는 기능은 많아지며 강력해 지는 추세 입니다. 따라서 프로그래밍 언어 자체의 문법을 습득하는 시간과 노력은 과거 대비 상대적으로 줄어 들고 있습니다. 그러나 소프트웨어의 소스코드(source code)를 공개하여, 누구나 소프트웨어가 어떻게 만들어 졌는지 공부할 수 있고, 수정할 수 있고, 확장할 수 있는 오픈소스 소프트웨어(open source software) 생태계가 일반화 되면서, 소프트웨어 개발 환경을 구축하고 자료를 얻는 작업은 점점 더 초보자에게는 어려워 지고 있는 것이 추세입니다. 즉, 예전에는 상용 소프트웨어를 구매하여 안정적으로 사용하고, 잘 정리된 책을 사서 읽는 식으로 학습을 하지만, 점점 오픈소스 소프트웨어 프로그램의 사용이 증가하면셔, 상용 소프트웨어 대비 버그와 사용의 불편함이 늘어 나는 것은 사실이므로, 이로 인한 시행착오에 괴로워하게 되고, 잦은 업그레이드와 빠른 기술 개발 주기로 대부분 잘 정리된 책이 없기에 Googling(구글을 통해서 궁금한 것을 학습하는 작업을 의미)이라는 단어에 익숙하게 된다. 또한 한글화된 자료가 많지 않기에, 영어 공부를 좀 더 열심히 할 걸 하는 후회를 갖게 합니다. 하지만, 한 번 정도, 오픈소스 소프트웨어를 활용한 소프트웨어 개발의 처음과 끝을 경험해 보면, 점차 익숙해 지게 되며, 누구나 겪는 통과 의례이니 묵묵하게 지나가면 될 일 입니다. 따라서, 본 글에서는 쉽고 편리한 웹 기반 DartPad가 아닌, 다소 불편하지만 결국에는 사용할 MS Visual Code (이후 MSVC로 명칭함) 오픈소스 소프트웨어를 기반으로 실습과 설명을 하도록 합니다. 그리고, 강조하지만 Dart언어 자체도 오픈소스 소프트웨어 입니다.
5 | 6 | 7 | 8 |본 연재 글은, 소프트웨어 개발을 처음 시작하는 수준을 가정하고 있습니다. 그러면, 이제 Dart 언어를 배우는 여행을 시작해 보도록 하겠습니다. 이번 글부터는 실제 Dart 언어로 프로그램을 타이핑하고 실행할 테니, 앞쪽에서 설명한 MSVC 프로그램의 설치 과정을 통해서, 개발용 컴퓨터에 MSVC와 Dart Extension이 설치가 되어 있어야 합니다.
9 | 10 | 11 | 12 |이제 Dart 언어로 프로그램을 시작해 봅시다. Dart 언어로 작성할 화일들을 컴퓨터의 어느 곳에 저장할지 생각한 후, 저장을 원하는 디렉토리를 생성합니다. 여기서는 "darttutorial-06"이라는 이름으로 디렉토리를 만들고, 앞으로 작성할 Hello World! 프로그램을 이 곳에 저장하겠습니다. 일반적으로 프로그램을 개발할 때에는 하나의 프로그램에 많은 소스코드 화일과 아이콘, 이미지 등 여러 화일이 필요합니다. 따라서 MSVC는 프로그램 개발을 위한 화일들을 디렉토리(폴더)에 묶어서 관리하여, 하나의 프로그램이 필요한 여러 화일들을 쉽게 관리 하도록 합니다.
17 | 18 | 19 | 20 |이제 MSVC를 실행합니다. MSVC는 프로그램이 수행되면, "시작"이라는 탭(tab)이 나타나서, 최근에 수행한 작업에 대한 기록을 보여 줍니다. 그리고 MSVC에 대한 도움말, 추가 기능에 대한 설정 방법, 그리고 주요 기능에 대한 설명을 보여 줍니다.
21 | 22 | 23 | 24 |앞서 만든 작업용 폴더를 사용할 차례로써, MSVC의 메뉴에서 "작업 영역에 폴더 추가..."를 선택합니다. 그리고 앞서 만든 폴더를 선택합니다. 메뉴에서 "파일"-"새파일"을 실행하면, MSVC에 "Untitled-1"이라는 이름의 탭이 열립니다. 아무것도 타이핑하지 않은 상태에서, "파일"-"저장"을 실행 합니다. 앞서 만든 작업용 폴더안에 화일을 저장하도록 되어 있는 것을 확인할 수 있다. 이는 타이핑하는 Dart 언어로 만든 소프트웨어 소스코드의 이름을 묻는 것으로, 여기서는 글의 구성상 "darttutorial-06-01.dart"로 하겠습니다. 본인이 편한대로 "HelloWorld.dart" 등으로 변경해도 됩니다. 다만, 화일의 확장자를 Dart 언어로 만들었다는 의미로, "dart"로 해야 합니다. 이 작업을 마치면, 탭의 이름이 방금 입력한 화일 이름으로 바뀌었으며, 화일이름 앞에 Dart를 의미하는 파란색 로고가 나타난 것을 볼 수 있습니다. 이렇게 새로운 화일을 만들고 난 이후의 화면은 다음과 같습니다. 왼쪽에 작업하는 폴더 이름 밑에 방금 만든 dart 화일이 포함되어 있는 것을 볼 수 있습니다.
25 | 26 | 27 | 28 |참고로 MSVC와 같이 소프트웨어 개발시 소스코드를 타이핑할 때 사용하는 프로그램을 에디터(editor)라고 합니다. 대부분의 에디터는 타이핑을 편하게 하기 위한 기능을 제공하는 수준이지만, MSVC는 소스코드를 작성할때, 프로그래밍 언어에 맞는 다양하고 편리한 기능을 제공하여, 프로그램의 오류를 조기에 제거하는 용도가 많이 들어 있습니다. 에디터가 단순하게 소스코드의 타이핑에 도움을 주는 도구라면, IDE(Integrated Development Environment)라고 불리는 우는 소프트웨어들이 있습니다. 대표적으로 MS의 Visual Studio, Apple의 Xcode, 오픈소스인 Eclipse 등이 있습니다. IDE는 에디터의 기능을 포함하면서, 프로그램을 실행하고, 오류를 잡는 디버그에 대한 기능을 제공하고, 프로그램의 성능을 개선하는 등의 기능을 제공을 하는 도구들입니다. MSVC는 에디터의 영역이지만, 각종 Extension을 추가함으로써, IDE처럼 사용할 수 있는 좋은 도구 입니다. MSVC은 Dart, Python, Go 등 최신 언어를 지원하면서도, C++과 같이 전통적인 언어들을 지원하므로, 친숙해 지면 프로그래밍에 많은 도움을 받을 수 있습니다. 특히, 발표 이후 개발자들이 선호하는 개발환경에서 Top 3, Top 5 혹은 1등에 오르는 등, 개발자들 사이에서 인기가 매우 높은 오픈소스 소프트웨어 도구입니다 [참조].
33 | 34 | 35 | 36 |MSVC에 새롭게 만든 탭에 다음의 프로그램을 타이핑 합니다. 프로그램을 모두 타이핑 했다면, "파일"-"저장"을 선택해서 화일을 저장합니다. 컴퓨터의 디렉토리를 따라가면, 화일이 만들어져 있는 것을 확인 할 수 있습니다.
41 | 42 | 43 | 44 |// darttutorial-06-01.dart 45 | 46 | void main() { 47 | print("Hello World!"); 48 | }49 | 50 | 51 | 52 |
먼저 // 기호는 Dart 언어를 수행하는 경우에는 전혀 상관이 없는 구문입니다. 주석(comment)이라고 불리는 기술로서, // 기호 이후의 문장은 프로그램의 수행과 상관 없이, 개발자가 작성한 내용에 대한 설명을 작성하고 싶을때 활용합니다. 딱 한줄만 작성 가능하며, 위의 프로그램에서의 의미는, 제가 이 프로그램을 지칭하기 위한 용도로, "이 프로그램은 darttutorial-06-01.dart"라고 불러여 겠다는 의미로 작성한 겁니다.
53 | 54 | 55 | 56 |그 다음 중인 void main()에 대해서는, 향후 함수(function)를 배울 때 까지는, 다음처럼 단순하게 이해하면 됩니다. "Dart 언어로 만드는 프로그램은 main() 이라고 불리우는 영역에 속한 내용을 제일 먼저 수행한다" 정도 입니다. 당분간 우리가 배우고 실습하는 내용은 main() 이라 불리우는 { 기호와 } 기호 사이에 타이핑 하면서 작업을 해 나갈 예정입니다.
57 | 58 | 59 | 60 |참고로, Hello World! 라는 글자만 출력하는 이 단순한 프로그램의 시작은 C 언어입니다. 당시 "C - The Programming Language" 라는 책에서, C언어를 만든 저자가 첫번째로 예를 들어 설명한 프로그램이 다음과 같았습니다.
65 | 66 | 67 | 68 |main( ) { 69 | printf("Hello, world!"); 70 | }71 | 72 | 73 | 74 |
이 후로, 새롭게 나오는 대부분의 언어들은 최초의 프로그램 예제를 보여줄때, 이 Hello World!를 출력하는 것으로 시작하는 전통이 생겼습니다. 아울러, Dart의 print가 C에서 printf로 바뀐 것을 제외하면 같은 형태인 것을 볼 수 있습니다.
75 | 76 | 77 | 78 |darttutorial-06-01.dart 프로그램을 MSVC 안에서 실행해 봅시다. MSVC의 왼쪽 아이콘 중 다음과 같이 생긴 아이콘을 클릭 합니다. 아이콘이 벌레 처럼 생겼는데, 실제 프로그램을 개발하면서 발생하는 오류들을 벌레를 의미하는 bug로 부릅니다. 그리고, 오류를 제거하는 과정을 디버그(de-bug, debug)라고 합니다.
83 | 84 | 85 | 86 |MSVC는 dart 언어로 만든 프로그램을 실행하면서 디버그 하는 작업을 시작하면서, 다음과 같은 메뉴를 새롭게 보여줄 겁니다. 해당 아이콘을 누르면 실제 디버그를 시작합니다.
91 | 92 | 93 | 94 |대부분의 경우, MSVC에서의 Dart 프로그램의 첫 실행에서는 오류가 발생합니다. MSVC는 Dart 언어 전용의 도구가 아니고, Dart 언어를 지원할 수 있도록 확장한 겁니다. 따라서, 부족한 설정을 개발자가 직접 입력해 줘야할 필요가 있습니다. MacOS에서 MSVC를 사용하여 darttutorial-06-01.dart 프로그램을 실행하면, 다음과 같은 내용이 담긴 탭이 열리면서, 화면 하단에 "program" 필드를 제대로 수정하다는 메시지가 나올 겁니다.
99 | 100 | 101 | 102 |이 화일은 MSVC가 자동으로 만들어 주는 화일로써, Dart 언어로 인식한 이 프로그램의 수행시에 참조 하려는 의도의 화일 입니다. 여기서 손을 댈 부분은 "program"으로 되어 있는 필드입니다. 현재 자동으로 생성된 내용은, 폴더의 bin 디렉토리 밑의 main.dart 라는 프로그램을 수행한다는 의미입니다. 우리는 단순하게 폴더에 바로 darttutorial-06-01.dart라는 이름의 화일을 저장했으니, "program": "bin/main.dart"를 "program": "darttutorial-06-01.dart"로 바꾼 후, 화일을 저장합니다.
107 | 108 | 109 | 110 |이제 Hello World!를 출력한 준비를 마쳤습니다. darttutorial-06-01.dart를 타이핑하던 탭으로 이동해서, 다시 디버그 아이콘을 누릅니다. 이제 MSVC의 상단에 다음의 그림이 나타나는 것을 확인 할 수 있습니다. 중간의 초록색 화살표를 누르면 MSVC는 우리가 타이핑한 프로그램을 실행합니다.
111 | 112 | 113 | 114 |darttutorial-06-01.dart를 타이핑한 탭 하단을 보면, 다음과 같이 바뀌어 있는 것을 볼수 있습니다. 디버그 콘솔 탭으로 이동하면, print() 구문안에 있던 Hello World! 라는 문장을 화면에 출력 한 것을 볼 수 있습니다. Exited는 프로그램의 수행을 성공적으로 마쳤다는 의미입니다.
119 | 120 | 121 | 122 |이제까지 작업을 성공적으로 마쳤다면, 아래의 아이콘을 클릭해서 작업한 내용들의 결과물로 어떤 화일들이 만들어 졌는지 확인하는 것으로 마무리 합니다.
127 | 128 | 129 | 130 |성공적인 작업 결과를 다시 한번 요약하면 다음과 같습니다. darttutorial-06 폴더를 만들었으며, darttutorial-06-01-.dart 소스코드를 포함 하였습니다. 그리고 MSVC가 자동으로 실행환경 화일인 launch.json 화일을 만들었습니다. 실제 darttutorial-06 폴더에는 darttutorial-06-01-.dart 소스코드만 저장이 되며, launch.json 화일은 MSVC가 별도로 관리 합니다.
135 | 136 | 137 | 138 |이에 작업 내용을 닫아 봅니다. "파일"-"폴더 닫기"를 선택하면, MSVC는 작업한 내용들을 모두 제거하고, 처음 MSVC를 실행했을 때와 같이 "시작" 탭만 있는 화일로 만들어 줍니다. 여기서 '최근 항목'이라고 써있는 부분을 보면, 작업한 "darttutorial-06 폴더"가 보입니다. 이를 클릭하면, 다시 MSVC가 개발에 관련한 내용들을 읽어 들여서 업데이트 되어 있는 것을 확인 할 수 있습니다. 이렇게 함으로써, 필요할 때 폴더를 열고 작업을 수행하면 됩니다.
143 | 144 | 145 | 146 |이번 글에서는 기본 문법을 시작하는 입장에서, 프로그램을 타이핑하고 수행할 MS Visual Code의 기초적인 사용방법을 알아보고, 대표적인 첫번째 프로그램인 Hello World! 프로그램을 만들고 실행해 보았습니다.
151 | 152 | 153 | 154 |Creative Commons License (CC BY-NC-ND)
159 | 160 | -------------------------------------------------------------------------------- /doc/dart-tutorial-07.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - Variables & Constants 2 | 3 | 4 |최초의 전자식 디지털 컴퓨터는 Compute/r가 의미 하듯이 계산을 하는 것이 주목적 입니다. 따라서, "X = 1, Y = 2, Z = X + 1"과 같이 산수/수학을 지원하는 것을 기초로 합니다. 그리고 우리는 이미 X, Y, Z를 변수라고 하는데, 이는 변하는 값이라는 의미입니다. 상수는 수학의 원주율인 pi 처럼 3.141592...의 값이 이미 고정되어서, 변경되지 않는 것으로 이미 알고 있습니다. 모든 프로그래밍 언어는 이러한 기능을 그대로 변수(variables)와 상수(constants)라 부릅니다. Dart에서 어떻게 사용하는지 알아봅니다.
5 | 6 | 7 | 8 |바로 코드로 시작해 봅니다. Be.Programmer [6] 글을 통해서 알게된 작업용 폴더 만들기 후, 다음 darttutorial-07-01.dart의 소스 프로그램을 타이핑 하고, 실행합니다. 반드시 1) 폴더를 만들고, 2) 폴더에 dart 화일을 저장하고, 3) main()이 있는 dart 화일명이 제대로 launch.json 화일의 program 필드에 적혀 있는지 확인하기 바랍니다.
13 | 14 | 15 | 16 |// darttutorial-07-01.dart 17 | 18 | void main() { 19 | int myInt = 1; 20 | double myDouble = 1.0; 21 | String myString = "Dr.Sungwon"; 22 | 23 | print("$myInt $myDouble $myString"); 24 | }25 | 26 | 27 | 28 |
Be.Programmer [6]에서 언급한 것 처럼, void main() {과 }는 향후 설명하면, 그 사이에 있는 내용을 다루는 것이 이번 글의 범주 입니다. 그러면 각각에 대해서 한줄씩 설명 합니다. 참고로 darttutorial-07-01.dart 프로그램의 출력 값은 다음과 같습니다.
29 | 30 | 31 | 32 |1 1.0 Dr.Sungwon33 | 34 | 35 | 36 |
int myInt = 1; 먼저, Dart 언어에서 하나의 의미 있는 구문의 끝은 ';'로 반드시 마쳐야 합니다. 이러한 이유로 이후의 모든 라인들도 ';'로 끝나는 것을 볼 수 있습니다. Dart 언어는 다양한 타입의 데이타를 처리할 수 있는데, 여기서 int는 integer의 약자로서 정수를 처리할 수 있음을 의미합니다. 정수에 대한 설명은 추후 데이타 타입에 대한 구체적인 설명에서 수행할 예정이며, 당장은 수학의 정수를 다룬다고 보면 됩니다. Dart 언어에서 정수를 다루고 싶다면, 정수에 이름을 붙여야 하는데, 지금은 myInt 라는 이름으로 변수를 하나 만든 겁니다. 이렇게 Dart 에서는 변수를 사용하고 싶으면, 먼저 타입 이름을 앞에 적고, 그 다음에 사용하고자 하는 변수의 이름을 적습니다. 만약, 변수를 만들면서 특정 값으로 초기화를 원한다면, 예제처럼 1의 값을 저장한다는 의미로 "=" 기호를 사용합니다. 이는 수학과 같습니다. 결론적으로 이 줄은 정수를 저장하는 용도로 myInt 변수를 만든후, 1의 값을 초기화 값으로 저장한다는 의미입니다. 여기서 꼭 int와 같이, 저장할 값을 미리 예측해서 일일이 적어야 하냐고 묻는다면, 꼭 그렇지는 않습니다. Dart 언어는 타입을 지정할 수도 있고, 밑에 설명하듯이 Dart가 타입에 맞춰서 알아서 처리하도록 할 수도 있습니다.
37 | 38 | 39 | 40 |double myDouble = 1.0; 앞서와 유사한 의미인데, double은 double 혹은 floating-point 라고 해석하여 실수형 변구를 지칭합니다. 따라서, 이 줄은 myDouble 이라는 이름으로 실수를 저장할 변수를 만들고, 1.0의 값으로 초기화를 한다는 의미입니다.
41 | 42 | 43 | 44 |String myString = "Dr.Sungwon"; 컴퓨터가 다루는 데이타가 숫자에 국한되지는 않습니다. String 타입은 영어 단어가 의미 하듯이, 문자열(string)을 다루는 Dart의 데이타 타입을 의미합니다. 지금은, 글을 작성할때 말을 하는 것을 의미하는 인용 구문인 " " 기호를 사용하여, "Dr.Sungwon"이라는 글자를 만든후, myString 변수에 저장하는 것을 의미합니다.
45 | 46 | 47 | 48 |지금처럼 integer, double, String과 같은 구체적인 타입을 사용하여 변수를 만드는 방식은 다루고자 하는 값의 타입이 바뀌지 않는 다는 전제에서 사용합니다. 만약 서로 다른 타입의 변수들 간에 값을 주고 받도록 하려 하면, 다음과 같이 에러가 나타남을 볼 수 있습니다. 그림에서 myDouble = myInt;와 myString = myInt;처럼 하여 서로 다른 타입으로 값을 전달하게 하였는데, 이미 실행전에 MSVC에서 붉은 색으로 에러가 있음을 보여주고 있으며, 이를 무시하고 실행을 하면 에러가 날 것이라는 경고를 보여주는 것을 볼 수 있습니다.
49 | 50 | 51 | 52 |변수를 만들때, 굳이 저장되는 값을 생각하지 않고 만들고 싶다면, var 문법을 사용할 수 있습니다. darttutorial-07-01.dart에서 구체적으로 타입을 정의한 것을 var 문법으로 바꾼 예제 프로그램은 다음과 같습니다.
57 | 58 | 59 | 60 |// darttutorial-07-02.dart 61 | 62 | void main() { 63 | var myInt = 1; 64 | var myDouble = 1.0; 65 | var myString = "Dr.Sungwon"; 66 | 67 | print("$myInt $myDouble $myString"); 68 | }69 | 70 | 71 | 72 |
프로그램의 수행결과는 1 1.0 Dr.Sungwon 로 제대로 출력하는 것을 볼 수 있습니다. 하지만 var 문법을 사용하는 경우도, 앞서 설명한 것처럼, 서로 다른 타입의 값을 주고 받는 방식은 에러를 만듭니다. 이런 방식을 static type 이라고 부르며, 불편할지 모르겠지만 서로 다른 타입의 값들을 주고 받으면서 에러가 발생할 수 있는 프로그램에서 에러를 억제하는 효과를 제공합니다. C++의 경우도 이렇게 static한 type checking을 지원하는 언어입니다. 하지만, 최근의 프로그래밍 언어들은 변수를 만들때 타입을 지정하지도 않으며, 서로 다른 타입의 값들을 서로 주고 받는 것이 원활합니다. Dart는 이런 dynamic type 방식도 지원합니다. 서로 다른 타입의 변수들 간에 자유롭게 데이타를 주고 받는 소스 코드는 아래의 darttutorial-07-03.dart과 darttutorial-07-04.dart 처럼 var 문법 대신 Object 문법을 사용하면 됩니다.
73 | 74 | 75 | 76 |// darttutorial-07-03.dart 77 | 78 | void main() { 79 | Object myInt = 1; 80 | Object myDouble = 1.0; 81 | Object myString = "Dr.Sungwon"; 82 | Object temp; 83 | 84 | temp = myInt; 85 | myInt = myDouble; 86 | myDouble = myString; 87 | myString = temp; 88 | 89 | print("$myInt $myDouble $myString"); 90 | }91 | 92 | 93 | 94 |
Object 대신 아래처럼 dynamic 문법을 사용하여도 동일한 동작을 합니다.
95 | 96 | 97 | 98 |// darttutorial-07-04.dart 99 | 100 | void main() { 101 | dynamic myInt = 1; 102 | dynamic myDouble = 1.0; 103 | dynamic myString = "Dr.Sungwon"; 104 | dynamic temp; 105 | 106 | temp = myInt; 107 | myInt = myDouble; 108 | myDouble = myString; 109 | myString = temp; 110 | 111 | print("$myInt $myDouble $myString"); 112 | }113 | 114 | 115 | 116 |
두 프로그램의 수행 결과는 모두 1.0 Dr.Sungwon 1 입니다. 서로 다른 타입의 변수들끼리 값을 주고 받아도 문제가 없으므로, 개발자는 저장할 정보에만 집중하고, 이들의 타입에 대해서는 신경을 쓰지 않아도 됩니다. 이렇게 서로 다른 타입간에 정보를 주고 받는 dynamic 타입과 static 타입은 서로 다른 용도이지, 어느쪽이 좋고 나쁘다라고 하는 문제는 아닙니다. 따라서, 본인이 작성하는 프로그램에서 엄격한 타입 checking이 필요한지 아닌지에 따라서 선택하면 됩니다.
117 | 118 | 119 | 120 |참고로 darttutorial-07-03.dart과 darttutorial-07-04.dart 프로그램에서는 변수 temp를 만드는 순간에 초기화 값을 주지는 않았습니다. 이런 경우 Dart는 해당 temp 변수의 값을 자동으로 null로 초기화 합니다. 프로그래밍 언어에 따라서 의미없는 값이 들어가는 경우가 있는, 이를 통상 garbage 값이라고 합니다. Dart는 초기화 되지 않은 변수가 null 값을 갖도록 함으로서, 이후 변수에 값이 있는지 없는지 점검을 용이하게 합니다.
121 | 122 | 123 | 124 |Hello World! 프로그램을 출력할 때는, print("Hello World!");를 통해서, "Hello World!" 문자열을 화면에 출력하였습니다. 그런데 darttutorial-07-01.dart에서는 print("$myInt $myDouble $myString");로 인용구문을 사용한 문자열의 출력처럼 보이지만, 단순하게 문자열을 출력하는 것과 다른 역할을 합니다.
129 | 130 | 131 | 132 |그러면, 무슨 의미인지 위에서 미리 보여준 출력 값으로 동작을 유추해 봅니다. darttutorial-07-01.dar의 출력 값을 "1 1.0 Dr.Sungwon"로 하였는데요, print 구문의 출력 내용의 문자열에 '$' 기호가 있으면, 해당 기호 뒤의 이름에 해당하는 변수(혹은 상응하는 타입)의 값을 출력하게 됩니다. 따라서, print 구문은 순서대로 myInt 변수 값인 1, myDouble 변수 값인 1.0 그리고 myString 변수 값인 Dr.Sungwon을 출력하게 된 것입니다.
133 | 134 | 135 | 136 |이후 복수의 값을 출력하기 위한 용도로 요기하게 사용하기 바랍니다. print 구문은 매우 다양한 기능을 제공하므로, 이에 대한 부분은 필요할 때마다 하나 하나 알아 갈 것 입니다.
137 | 138 | 139 | 140 |수학의 상수의 개념처럼, 변하지 않는 값을 의미하며, Dart는 두가지 방법으로 상수를 지원합니다. 첫째는 final 문법이며, 둘째는 const 문법입니다. 아래의 darttutorial-07-05.dart는 둘을 사용한 예제 프로그램이며, 수행결과는 Sungwon Lee 3.141592 1.3 입니다.
145 | 146 | 147 | 148 |// darttutorial-07-05.dart 149 | 150 | void main() { 151 | final String firstName = "Sungwon"; 152 | final secondName = "Lee"; 153 | 154 | const double myPi = 3.141592; 155 | const changeRate = 1.3; 156 | 157 | print("$firstName $secondName $myPi $changeRate"); 158 | }159 | 160 | 161 | 162 |
darttutorial-07-05.dart 프로그램에서 사용하는 firstName, secondName, myPi, changeRate는 프로그램에서 값을 바꿀수 없습니다. 따라서 수학의 상수와 같이 고정된 값을 저장하도록 합니다. 차이점은 개발자 입장에서 미묘한데, const는 말 그대로 constant로서, Dart로 작성한 프로그램을 컴퓨터에서 수행할 수 있는 형태로 바꿔주는 compile 시점에서 상수로 만드는 것을 의미합니다. 이에 반하여, final 문법은 정확하게는 "프로그램 전체에서 한번만 값을 저장할 수 있는 변수"를 의미합니다. 하지만, 개발자 입장에서는 상수 값을 저장하는데 사용함에 큰 차이를 느끼기는 어렵습니다. final과 const 문법을 사용할때는 darttutorial-07-05.dart 처럼 구체적인 타입을 적어줄 수도 있고, 그렇지 않으면 final과 const 문법만 사용하여도 무방합니다.
163 | 164 | 165 | 166 |이 글에서는 컴퓨터 프로그램에서 가장 기초적인 내용인 변수와 상수에 대해서 다뤘습니다. 컴퓨터에서의 변수와 상수는 수학과 같은 숫자를 다룰수도 있지만, 글자 등의 다양한 데이타를 다룰 수 있는 개념입니다.
171 | 172 | 173 | 174 |Creative Commons License (CC BY-NC-ND)
179 | 180 | -------------------------------------------------------------------------------- /doc/dart-tutorial-08.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - Number Types 2 | 3 | 4 |Dart는 정수과 실수를 다루기 위하여, 각각 int(integer) 타입과 double 타입을 지원합니다. 이 두 타입의 값을 저장하는 변수와 상수를 어떻게 만드는지에 대해서는, 이미 Be.Programmer [07]에서 다뤘습니다. 변수의 타입(int, double)을 지정 하기도 했고, var/dynamic/Object 타입을 사용하여, 정수와 실수 타입의 데이타를 저장할 수 도 있습니다. 여기서는 이 들을 어떻게 사용하는 지에 대해서 다루도록 합니다.
5 | 6 | 7 | 8 |Dart에서 int 데이타는 64bit 보다 작도록 제한됩니다. 그리고, 다양한 플랫폼을 지원할 수 있는 Dart의 특성상, Dart VM을 통하여 실행하는 경우의 int는 -2^63 ~ 2^63 -1 의 값을 표현할 수 있고, Dart 프로그램을 JavaScript로 변환하여 실행하는 환경에서는 -2^53 ~ 2^53 -1의 값을 표현할 수 있습니다.
13 | 14 | 15 | 16 |Dart에서 실수 데이타는 double 데이타 타입으로 나타냅니다. 원래 컴퓨터에서 실수형 데이타를 다루는 것은 매우 복잡한 작업 중의 하나입니다. Dart는 컴퓨터에서 실수 데이타를 다루는 표준 방법인 IEEE 754 방식을 지원하며, 64bit의 처리를 합니다. IEEE 754의 64bit Double 처리 방식에 대해서는 [참조] 문서를 통해서 더 상세한 내용을 확인 합니다.
21 | 22 | 23 | 24 |정수와 실수는 수학에서 다양한 연산을 위해서 사용합니다. Dart에서도 수학의 개념을 구현할 수 있도록 연산자를 제공하고 있으며, 대부분 수학과 유사한 개념을 옮겨 놓고 있습니다. 대표적인 연산자들을 하나씩 알아 보도록 합니다.
29 | 30 | 31 | 32 |Add 더하기는 수학과 같은 '+' 기호를 사용합니다. 별도의 설명이 필요 없겠죠.
33 | 34 | 35 | 36 |Subtract 빼기도 수학과 같은 '-' 기호를 사용합니다. 별도의 설명은 하지 않습니다.
37 | 38 | 39 | 40 |Minus Sign 마이너스 부호는 수학과 같이 '-' 기호를 숫자 앞에 붙이면 됩니다.
41 | 42 | 43 | 44 |Multiply 곱하기는 개념은 수학과 같지만, 알파벳의 'x (엑스)'와 헷갈리지 않도록 하기 위하여, '*' 기호를 사용합니다.
45 | 46 | 47 | 48 |Divide 나누기는 수학과 좀 많이 다릅니다. 정수/실수 구분없이 '/' 기호를 사용하면, 결과는 소숫점을 갖는 하나의 숫자를 나타냅니다. 특이하게 생긴 '~/' 기호는 정수 결과를 나타내는데, 몫을 나타낸다고 생각하면 이해가 빠르겠지요. 마지막을 세번째 나누기 기호는 '%'이며, 정수로 몫을 구한 후에 나머지를 정수 형태로 나타냅니다.
49 | 50 | 51 | 52 |동적 타입 변수들을 만들어서 정수와 실수에 대해서 위의 연산자들을 적용한 예제 프로그램이 아래에 나타나 있습니다. 첫번째 부분은 정수들에 대한 연산이고, 두번째 부분은 실수들에 대한 연산이고, 세번째 부분은 실수와 정수를 섞어서 계산한다는 예제를 보여줍니다.
53 | 54 | 55 | 56 |// darttutorial-08-01.dart 57 | 58 | void main() { 59 | dynamic num1 = 9; 60 | dynamic num2 = 4; 61 | dynamic res1 = num1 + num2; 62 | dynamic res2 = num1 - num2; 63 | dynamic res3 = num1 * num2; 64 | dynamic res4 = num1 / num2; 65 | dynamic res5 = num1 ~/ num2; 66 | dynamic res6 = num1 % num2; 67 | 68 | // print 13 5 36 2.25 2 1 69 | print("$res1 $res2 $res3 $res4 $res5 $res6"); 70 | 71 | num1 = 9.0; 72 | num2 = 4.0; 73 | res1 = num1 + num2; 74 | res2 = num1 - num2; 75 | res3 = num1 * num2; 76 | res4 = num1 / num2; 77 | res5 = num1 ~/ num2; 78 | res6 = num1 % num2; 79 | 80 | // print 13.0 5.0 36.0 2.25 2 1.0 81 | print("$res1 $res2 $res3 $res4 $res5 $res6"); 82 | 83 | num1 = 9.0; 84 | num2 = 4; 85 | res1 = num1 + num2; 86 | res2 = num1 - num2; 87 | res3 = num1 * num2; 88 | res4 = num1 / num2; 89 | res5 = num1 ~/ num2; 90 | res6 = num1 % num2; 91 | 92 | // print 13.0 5.0 36.0 2.25 2 1.0 93 | print("$res1 $res2 $res3 $res4 $res5 $res6"); 94 | }95 | 96 | 97 | 98 |
수학에는 없는 기호가 있는데, 바로 '++' 기호(연산자)입니다. 사용 문법은 단순한데, 변수 a가 있을때, ++a 혹은 a++로 사용하는데, 만약 다른 문장과 섞어서 사용하지 않고, 한줄에 ++a 혹은 a++만 있다면, 둘 모두 a = a + 1과 같은 의미입니다. 하지만 다른 문장과 섞여서 사용하면, 둘은 서로 다른 의미를 나타냅니다. 아래의 darttutorial-08-01.dart 예제를 동작시키면, 두번째와 세번째 print 구문에서 서로 다른 결과가 나오는 것을 알 수 있습니다.
99 | 100 | 101 | 102 |// darttutorial-08-01.dart 103 | 104 | void main() { 105 | dynamic var1 = 1; 106 | print("$var1"); // Print 1 107 | 108 | var1 = ++var1 + 10; 109 | print("$var1"); // Print 12 110 | 111 | var1 = var1++ + 10; 112 | print("$var1"); // Print 22 113 | }114 | 115 | 116 | 117 |
첫번째 print 구문에서는 var1 변수를 만들고 1로 초기화 한 값을 그대로 출력합니다.
118 | 119 | 120 | 121 |두번째 print 구문에서는 12의 결과가 나왔는데, 이유는 다음과 같다. var1 = ++var1 + 10;을 해석하면, ++ 연산자가 변수 앞에 있는 경우에 있어서, ++ 연산자가 다른 연산자인 + 및 = 기호화 같은 라인에 있는 것을 볼 수 있습니다. 이런 경우 ++ 연산자가 변수 앞에 있는 경우는, 다른 연산자 보다 먼저 작업을 합니다. 따라서 위의 줄은 1) var1의 값을 1만큼 증가 시키고, 2) 앞서 1)의 결과에 10을 더한 후, 3) 더한 결과를 var1에 다시 저장합니다. 따라서 var1이 1로 초기화된 상태에서, 1)을 수행하면 2가 되고, 2)를 수행하면 다시 12로 증가하며, 이를 3)에서 var1에 저장하는 것을 확인할 수 있습니다.
122 | 123 | 124 | 125 |세번째 print 구문에서는 ++ 연산자가 var1의 뒤에 놓인 것을 볼수 있습니다. ++ 연산자가 변수 뒤에 있는 경우는, 앞서 두번째 경우와 반대로 동작합니다. 즉, 같은 라인의 다른 연산자들을 먼저 수행하고, 나중에 ++ 연산을 합니다. 따라서 1) var1의 값에 10을 더한후, 2) 앞서 1)의 결과를 var1에 저장한 후, 3) var1의 값을 1만큼 증가 시킵니다.
126 | 127 | 128 | 129 |'--' 연산자도 있으며, '++' 연산자와 마찬가지로 --a 혹은 a-- 처럼 사용합니다. 의미는 a = a - 1로 해석합니다. 다른 연산자와 함께 사용하는 경우의 우선순위는 '++' 연산자와 동일합니다.
130 | 131 | 132 | 133 |나중에 클래스(Class)와 함수(Function)을 배우면, 더 많은 내용을 배우겠지만, Dart 언어는 숫자 외에도 매우 많은 기능을 제공합니다. 다음의 darttutorial-08-03.dart 프로그램은 몇가지 유용한 기능을 보여줍니다.
138 | 139 | 140 | 141 |// darttutorial-08-03.dart 142 | 143 | void main() { 144 | // integer in hexadecimal format 145 | var num1 = 0x0F; 146 | // print 15 147 | print("$num1"); 148 | 149 | // exponential number format 150 | var num2 = 1.1e2; 151 | // print 110.0 152 | print("$num2"); 153 | 154 | // String -> int 155 | var num3 = int.parse('1'); 156 | // print 1 157 | print("$num3"); 158 | 159 | // String -> double 160 | var num4 = double.parse('1.1'); 161 | // print 1.1 162 | print("$num4"); 163 | }164 | 165 | 166 | 167 |
num1은 10진법으로 값을 표현하는 일반적인 방식 외에, 16진법으로 숫자를 표현하는 것을 보여줍니다. 0x로 시작하는 숫자는 16진법으로써, 16진법은 1자리에 0~15까지의 10진법에 해당하는 의미를 표현 가능한데, 10진법의 10/11/12/13/14/15가 각각 A/B/C/D/E/F로 표현 됩니다. 따라서 num1을 출력하면 16진법 F에 해당하는 10진법 15가 출력됩니다.
168 | 169 | 170 | 171 |num2는 지수 로그 값을 표현하는 방법을 보여줍니다. 1.1에 지수로그 2를 계산하여, 두번째 print 구문에서 110.0이 출력되는 것을 볼 수 있습니다.
172 | 173 | 174 | 175 |num3와 num4는 숫자로만 만들어진 문자열이 주어 졌을때, 문자열을 숫자로 변환하는 것을 보여줍니다. num3는 정수 1의 문자열이므로, 1의 정수값이 전달되며, num4는 실수 1.1의 문자열이 저장되어 있으므로, 이를 실수로 변환한 값을 num4에 저장합니다.
176 | 177 | 178 | 179 |이번 글에서는 수학 계산을 Dart 언어에서 수행하기 위한 방법을 알아 보았습니다. Dart 언어의 정수와 실수 데이타 타입과 연산자 들에서 학습 하였으며, 부가적인 기능 등에 대해서 설명 하였습니다.
184 | 185 | 186 | 187 |Creative Commons License (CC BY-NC-ND)
192 | 193 | -------------------------------------------------------------------------------- /doc/dart-tutorial-09.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - String Types 2 | 3 | 4 |Be.Programmer [07]에서 String이라는 데이타 타입을 미리 살펴보았습니다. 글자들로 이루어진 문자열을 저장하는 용도였습니다. String은 매우 많은 용도에서 활용이 가능합니다. 수학 계산은 정수와 실수 타입을 주로 작업을 하지만, 웹 크롤링, 자연어 처리 등 다양한 분야에서 컴퓨터가 처리하는 데이타 타입은 String을 사용하여 처리합니다.
5 | 6 | 7 | 8 |Be.Programmer [07]에서 간단히 살펴본 String은 다음의 다양한 형태로 문자열을 저장할 수 있습니다. 아래는 dart.dev 사이트의 예제를 간단히 변환한 형태입니다.
13 | 14 | 15 | 16 |// darttutorial-09-01.dart 17 | 18 | void main() { 19 | var s1 = 'Single quotes work well for string literals.'; 20 | var s2 = "Double quotes work just as well."; 21 | var s3 = 'It\'s easy to escape the string delimiter.'; 22 | var s4 = "It's even easier to use the other delimiter."; 23 | 24 | print(" $s1 \n $s2 \n $s3 \n $s4 "); 25 | }26 | 27 | 28 | 29 |
프로그램의 수행결과는 다음과 같습니다.
30 | 31 | 32 | 33 |Single quotes work well for string literals. 34 | Double quotes work just as well. 35 | It's easy to escape the string delimiter. 36 | It's even easier to use the other delimiter.37 | 38 | 39 | 40 |
문자열은 ' ' 혹은 " " 기호를 사용하여 표현 합니다. 둘 간의 차이점은 없습니다. s1은 ' ' 기호를 사용하였고, s2는 " " 기호를 사용한 경우입니다. s4 처럼 it's 같은 표현을 하고 싶다면, Double quotation mark 기호로 문자열을 감싸 주면 됩니다. 그렇지 않은 경우는 문자열 안에서 single quotation mark 앞에 \ 기호를 사용하면 됩니다. 이렇게 하면, 문자열안에 \ 기호 뒤의 글자를 넣기 위한 특수 기호로 \ 기호를 인식함으로, 결과적으로 s3의 결과 값처럼, 기호 \은 사라지고 ' 기호만 출력이 되는 것을 볼 수 있습니다. 특수 기호인 \을 사용한 예가 마지막 print 구문 안에서도 있는 것을 볼 수 있다. \ 기호 뒤에 n이 있으면, 줄 바꾸기를 의미합니다. 따라서 결과처럼 각각의 문자열이 다른 줄로 출력된 것을 볼 수 있습니다.
41 | 42 | 43 | 44 |Be.Programmer [8]에서 숫자만으로 이루어진 문자열을 정수 혹은 실수 데이타 타입으로 변환하는 예제를 살펴 보았습니다. 당연하게 유추하겠지만, 반대의 경우도 가능합니다. 아래의 darttutorial-09-02.dart 프로그램을 살펴봅시다.
49 | 50 | 51 | 52 |// darttutorial-09-02.dart 53 | 54 | void main() { 55 | String s1 = 1.toString(); 56 | String s2 = 3.141592.toStringAsFixed(4); 57 | 58 | print("$s1 $s2"); 59 | }60 | 61 | 62 | 63 |
위의 darttutorial-09-02.dart 프로그램을 보면, s1은 정수 1의 숫자 뒤에 '.' 기호를 사용한 후, toString()이라는 문법을 사용하는 것을 볼 수 있습니다. 이런 문법은 이후 클래스를 배우면 원리를 이해할 수 있습니다. 지금은 이런 형태의 변환이 필요할때를 위하여 조기에 설명을 하고 있습니다. s2의 경우는 실수 데이타를 문자열로 바꾸는 것을 보여 줍니다. 실수의 경우도 마찬가지로, 실수의 마지막 끝에 '.' 기호를 사용한 후, toString을 사용하는데, 이 경우는 소숫점 n자리 까지만 짤라서 문자열로 바꾸는 문법인 toStringAsFixed를 사용하였습니다. 지금은 n이 4이므로, 실수인 3.142592의 소수점 밑 4번째 자리인 5까지를 포함하여 문자열을 만듭니다. 따라서 print 구문은 1 3.1416을 결과로 나타냅니다.
64 | 65 | 66 | 67 |다루고자 하는 문자열을 만들기 위해서 복수의 문자열을 연결해서 하나로 만들어야 하거나, 혹은 여러 줄의 문장을 연결해야 하는 경우가 있습니다. 이를 위한 Dart 언어의 예제가 아래의 darttutorial-09-03.dart에 3가지 방법으로 나타나 있습니다.
72 | 73 | 74 | 75 |// darttutorial-09-03.dart 76 | 77 | void main() { 78 | String s1 = "My name is "; 79 | String s2 = "Dr.Sungwon"; 80 | String s3 = s1 + s2; 81 | 82 | var s4 = 'My ' 83 | 'name ' 84 | 'is ' 85 | 'Dr.Sungwon'; 86 | 87 | dynamic s5 = ''' 88 | My name is Dr.Sungwon. 89 | Dart is lovely. 90 | '''; 91 | 92 | print("$s3\n$s4\n$s5"); 93 | }94 | 95 | 96 | 97 |
s3는 두 개의 문자열을 합치는 방법입니다. 낯설지만 숫자 데이타를 다룰때 설명했던 + 연산자는 문자열에서도 사용 가능합니다. 의미는 수학 처럼 두개의 문자열을 하나로 합치는 효과 입니다. 이렇게 함으로써 s3는 "My name is Dr.Sungwon"가 됩니다.
98 | 99 | 100 | 101 |s4는 특이하지만, 복수의 문자열을 그냥 연이어 나열한 경우입니다. 이 경우는 'My ' + 'name' + 'is' + 'Dr.Sungwon'의 의미라고 보면 됩니다. 따라서 s4를 출력하면 s3와 동일한 결과를 출력 합니다.
102 | 103 | 104 | 105 |s5는 시작 부분에 ' 기호를 3번 연결해서 '''와 같이 나타냈고, 마지막 줄에 다시 '''을 사용하여 닫습니다. 이 문법이 의미하는 것은 s5가 두개의 줄이 하나로 합쳐진 복수의 줄을 갖는 문자열이라는 의미입니다. 따라서 s5를 출력하면 다음과 같이 나타납니다.
106 | 107 | 108 | 109 |My name is Dr.Sungwon. 110 | Dart is lovely.111 | 112 | 113 | 114 |
컴퓨터는 수학을 주로 다루기 위하여 만들어 졌지만, 최근의 컴퓨터 활용 분야는 인간이 만들어 놓은 데이타를 다루는 것이 주 목적이 되었기에, 다양한 문자와 책들을 다루고 있습니다. Dart 언어의 String은 이런 문자열(들)을 다루는 기능을 제공합니다. 이후 클래스와 함수를 배우고 나면 Dart의 String이 처리할 수 있는 보다 다양한 기능을 다시 알아 보겠습니다.
119 | 120 | 121 | 122 |Creative Commons License (CC BY-NC-ND)
127 | 128 | -------------------------------------------------------------------------------- /doc/dart-tutorial-10.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - Boolean & Conditional Statement 2 | 3 | 4 |지금까지 살펴본 정수, 실수 및 문자열은 수학과 웹/책 등의 일상적인 환경에서 다룰 수 있는 데이타 타입들 입니다. 이 글에서 살펴볼 Boolean은 접해 본 사람도 있지만 다소 낯선 사람도 있을 수 있는 분야 입니다. 이산수학(discrete mathematics) 등 수학에서는 두가지 값으로 만 이루어진 문제를 다루는 분야가 있습니다. 그리고 Boolean 타입을 가장 많이 사용하는 프로그램에서의 역할은, 프로그램의 수행중에 왼쪽 혹은 오른쪽을 선택하는 경우의 문법입니다.
5 | 6 | 7 | 8 |Boolean 데이타 타입의 의미는 두 가지의 값으로 이루어 졌다는 의미 입니다. 통상 논리 연산이라고도 하는데, 두가지 값이란 true 혹은 false를 의미한다. Boolean 데이타는 static한 타입으로는 bool로 나타내며, dynamic 타입인 var, dynamic 등도 동일하게 사용할 수 있습니다. darttutorial-10-01.dart에는 static 타입의 logic1을 bool 타입으로 만든 후, true 값을 저장하는 예제를 보여 줍니다. 그리고 logic2를 dynamic 타입으로 만든후, false 값을 저장하는 예제를 보여 줍니다. print 구문을 사용하여 두 값을 출력하면 true false 처럼 결과가 출력됩니다.
13 | 14 | 15 | 16 |// darttutorial-10-01.dart 17 | 18 | void main() { 19 | bool logic1 = true; 20 | dynamic logic2 = false; 21 | 22 | print("$logic1 $logic2"); 23 | }24 | 25 | 26 | 27 |
두 가지 값을 갖는 Boolean 데이타는 전용의 연산자가 있으며, 통상 논리 연산자(logic operator)라고 부릅니다. 아마도 수학 기호에서 이미 봐 왔을 익숙한 연산자들 일 겁니다.
32 | 33 | 34 | 35 |Equal (==) 연산자는 두 값이 같은 지를 검사합니다. 수학에서는 등호 기호 =를 하나만 사용해도 비교를 하는 용도로 사용할 수 있지만, 프로그래밍 언어들에서는 같은 기호를 두가지 이상의 의미로 사용할 수 없습니다. 따라서 등호는 오른쪽의 값을 왼쪽으로 전달하는 assignment 용도로만 사용하며, 두 값을 비교하고자 한다면, 등호를 두번 연속적으로 써야 합니다. Equal 연산자는 두 값이 같다면 true, 그렇지 않고 다르면 false로 처리합니다.
36 | 37 | 38 | 39 |Not-equal (!=) 연산자는 두 값이 다른 지를 검사합니다. 위의 Equal에 반대되는 개념으로, 비교하는 두 값이 다르면 true, 같으면 false로 처리합니다. 수학에서는 등호 위에 사선을 긋는 연산자를 사용하지만, 프로그래밍에서는 키보드에 존재할 수 있는 버튼 갯수의 제약으로 인하여, 두 개의 글자를 연이어서 연결함으로써 사용합니다.
40 | 41 | 42 | 43 |다음은 두 값 중 어느쪽이 큰지 혹은 작은지를 비교하는 연산자들이 있습니다. 이들은 Greater than (>), Greater than or equal to (>=), Less than (<), Less than or equal to (<=) 연산자들 입니다. 순서대로 왼쪽의 값이 오른쪽 보다 큰 경우, 왼쪽의 값이 오른쪽 보다 크거나 같을때, 오른쪽 값이 왼쪽 보다 큰 경우, 오른쪽 값이 왼쪽 보다 크거나 같을때 true를 나타내며, 그렇지 않은 경우는 false로 처리합니다.
44 | 45 | 46 | 47 |Not (!) 연산자는 하나의 값에 대해서, 원래 가지고 있는 값을 반대로 뒤집는 역할을 합니다. 만약 true 값에 대해서 Not 연산자를 사용하면 false로 처리합니다. 반대로 false 값에 대해서 Not 연산자를 사용하면 true로 처리합니다.
48 | 49 | 50 | 51 |AND (&&) 연산자도 수학에서 가져온 연산자로써, 두 값이 모두 true 인경우는 true이며, 그렇지 않은 경우는 false 입니다. 문장으로 풀면 "A 조건과 B 조건이 모두 true 이면 true"로 해석합니다.
52 | 53 | 54 | 55 |OR (||) 연산자도 수학에서 가져온 연산자로써, 두 값중 하나라도 true 이면, true로 처리합니다. 문장으로 풀면 "A 조건과 B 조건 중 하나라도 true 이면 true"로 해석합니다.
56 | 57 | 58 | 59 |지금까지 설명한 논리 연산자들을 아래의 darttutorial-10-02.dart 프로그램의 예제를 통해서 이해하도록 합니다. 복잡하지 않은 연산자들이므로 별도의 설명은 하지 않습니다.
60 | 61 | 62 | 63 |// darttutorial-10-02.dart 64 | 65 | void main() { 66 | var cond1 = true; 67 | var cond2 = false; 68 | var num1 = 1; 69 | var num2 = 9; 70 | 71 | var res1 = (cond1 == cond2); 72 | var res2 = (cond1 != cond2); 73 | var res3 = (num1 > num2); 74 | var res4 = (num1 < num2); 75 | var res5 = (num1 >= num2); 76 | var res6 = (num1 <= num2); 77 | var res7 = (!cond1); 78 | var res8 = (!cond2); 79 | var res9 = (cond1 && cond2); 80 | var res10 = (cond1 || cond2); 81 | 82 | // print false true 83 | print("$res1 $res2"); 84 | 85 | // print false true false true 86 | print("$res3 $res4 $res5 $res6"); 87 | 88 | // print false true false true 89 | print("$res7 $res8 $res9 $res10"); 90 | }91 | 92 | 93 | 94 |
Dart에서 if 구문은 영어 의미 그대로 "만약 ~ 하다면"으로 해석합니다. 프로그램의 수행을 특정 조건에 맞춰서 서로 다른 방향으로 바꿔 주는 역할을 합니다. if 구문은 예제 프로그램을 먼저 이해한 후 설명을 하도록 합니다. 다음의 darttutorial-10-02.dart를 수행해 봅니다.
99 | 100 | 101 | 102 |// darttutorial-10-02.dart 103 | 104 | void main() { 105 | var num1 = 1; 106 | var num2 = 2; 107 | 108 | if(num1 > num2) { 109 | print("num1 is greater than num2"); 110 | } else if(num1 == num2) { 111 | print("num1 equal to num2"); 112 | } else { 113 | print("num1 is less than num2"); 114 | } 115 | }116 | 117 | 118 | 119 |
이 프로그램은 결과로 num1 is less than num2를 출력합니다. if 문법은 "if(a) { ... }"와 같습니다. a 조건이 true 이면 { } 안의 문장을 수행합니다. 만약 a 조건이 false 라면, { }안으로 들어가지 않고, if 구문에 상응하는 else if 혹은 else 구문이 있는지 확인 합니다. darttutorial-10-02.dart에서, if 구문에서 검사하는 조건이 num1 > num2인데, num1은 1이고 num2는 2 이므로, 이 문장의 결과는 1 > 2 이므로 false 입니다. 즉, if 구문 뒤의 { }에는 들어가지 않으므로, 다음 조건인 else if ((num1 == num2) { ... }로 이동합니다. else if는 앞의 if 구문이 false인 경우, 후속으로 조건 검사를 하기 위하여 사용합니다. 1 == 2도 true가 아니므로, 자연스럽게 다음 조건인 else로 이동하게 됩니다. else 구문은 별도로 조건을 검사하지 않고 뒤의 { } 안으로 들어가게 됩니다. 앞에서 수행한 조건 검사가 모두 false가 되어서, 이에 대한 후속 작업을 해야 하는 경우에 사용합니다. 따라서 세번째 print 구문만 수행되는 것니다. 만약 num2의 값을 1로 바꿔서 실행한다면, num1과 num2가 같을때 수행하는 두번째 print 구문만 출력됩니다. 만약 num2를 0으로 바꿔서 실행한다면, 첫번째 if() 구문이 true 이므로, 첫번째 print 구문만 출력하고 프로그램을 종료합니다.
120 | 121 | 122 | 123 |if, else if, else 구문을 사용하여도 문제가 없지만, 조건 검사의 종류가 많고 복잡한 경우, Dart 언어는 보다 직관적인 문법으로 switch, case, break, default 구문을 제공합니다. 앞서의 if 구문들과 마찬가지로 예제 프로그램인 아래의 darttutorial-10-04.dart를 먼저 실행해 보고, 이에 대한 설명을 수행하도록 합니다.
128 | 129 | 130 | 131 |// darttutorial-10-04.dart 132 | 133 | void main() { 134 | var command = 'OPEN'; 135 | switch (command) { 136 | case 'CLOSED': 137 | print("CLOSED"); 138 | break; 139 | case 'PENDING': 140 | print("PENDING"); 141 | break; 142 | case 'APPROVED': 143 | print("APPROVED"); 144 | break; 145 | case 'DENIED': 146 | print("DENIED"); 147 | break; 148 | case 'OPEN': 149 | print("OPEN"); 150 | break; 151 | default: 152 | print("default"); 153 | } 154 | }155 | 156 | 157 | 158 |
switch 구문은 switch( command ) { ... } 처럼 사용하며, command 변수가 갖는 값에 부합하는 경우에 대한 동작을 { ... } 안에서 찾는 작업을 합니다. { ... } 안에는 case 구문이 있으며, 여기에 command 조건이 부합하기를 희망하는 조건을 작성합니다. 예를 들면, case 'OPEN':으로 작성을 하면, command 변수가 문자열 "OPEN"과 같은 값일 경우에 수행하는 문장들이 기술되어 있다는 의미입니다. 예제 프로그램에서 command 변수는 'OPEN' 문자열을 가지고 있으므로, 다른 case 구문은 무시하게 되며, case 'OPEN': 구문을 수행하게 됩니다. 이 경우, case 'OPEN': 구문에 있는 break; 구문까지의 작업을 수행하는데, 이 경우는 한줄 뿐으로 단순하게 화면에 "OPEN"이라는 글자만 출력하는 작업인 print("OPEN");를 수행합니다. 만약 command 변수가 'DENIED' 값을 가지고 있다면, 이 프로그램은 print("DENIED");을 수행합니다. 만약 command 변수가, case 구문 중 어디에도 속하지 않는 "OK" 값을 같는 다고 가정하면, 어떠한 case 구문에도 부합하지 않으므로, 이러한 경우는 default: 구문에서 정의한 동작을 합니다.
159 | 160 | 161 | 162 |darttutorial-10-04.dart는 command 값에 따라 서로 다른 동작을 하도록 만든 경우입니다. 하지만, 실제 상황에서는 복수의 조건에서 같은 동작을 하는 경우가 있습니다. 예를 들어, 대소문자를 구분하지 않는 경우 'close'와 'CLOSE'가 같은 동작을 수행하는 경우입니다. darttutorial-10-05.dart 프로그램은 이런 경우에 대한 예제 프로그램입니다. 두개의 case 구문이 중간에 어떠한 문장도 포함되지 않은 상태에서, 연속적으로 연결되어 있는 것을 볼 수 있습니다.
163 | 164 | 165 | 166 |// darttutorial-10-05.dart 167 | 168 | void main() { 169 | var command = 'close'; 170 | switch (command) { 171 | case 'close': 172 | case 'CLOSE': 173 | print("CLOSE"); 174 | break; 175 | case 'open': 176 | case 'OPEN': 177 | print("OPEN"); 178 | break; 179 | default: 180 | print("default"); 181 | } 182 | }183 | 184 | 185 | 186 |
혹시 C/C++과 같은 언어에 경험이 있다면, darttutorial-10-06.dart 처럼 소문자인 경우에 대해서, 대문자로 입력하라는 경고 문구를 출력하되, 대문사/소문자 상관없이 동작하는 구문인 print("CLOSE"); 혹은 print("OPEN");를 출력하고 싶을 수 있습니다. Dart는 이런 구문을 지원하지 않습니다. 따라서 print("Use uppercase."); 구문이 들어간 case 'close':와 case 'open':는 에러를 유발하게 됩니다.
187 | 188 | 189 | 190 |// darttutorial-10-06.dart 191 | 192 | void main() { 193 | var command = 'close'; 194 | switch (command) { 195 | case 'close': // Error 196 | print("Use uppercase."); 197 | case 'CLOSE': 198 | print("CLOSE"); 199 | break; 200 | case 'open': // Error 201 | print("Use uppercase."); 202 | case 'OPEN': 203 | print("OPEN"); 204 | break; 205 | default: 206 | print("default"); 207 | } 208 | }209 | 210 | 211 | 212 |
Dart 언어에서는 디버그를 지원하기 위한 용도의 문법들이 있으며, assert( condition ); 구문이 대표적인 용도입니다. assert( ... ) 구문은 프로그램을 debug 모드로 실행 시켰을때에만 동작하는데, condition이 true 조건이면 아무런 일이 없지만, condition이 false가 되면 프로그램의 수행을 중지 시킵니다.
217 | 218 | 219 | 220 |아래의 darttutorial-10-07.dart를 수행하면, bool1과 bool2가 동일하게 true이므로, 마지막의 print 구문이 실행됩니다.
221 | 222 | 223 | 224 |// darttutorial-10-07.dart 225 | 226 | void main() { 227 | var bool1 = true; 228 | var bool2 = true; 229 | 230 | assert(bool1 == bool2); 231 | //assert(bool1 != bool2); 232 | 233 | print("Completed!"); 234 | }235 | 236 | 237 | 238 |
하지만, 아래의 darttutorial-10-08.dart를 수행하면, bool1과 bool2가 동일하기에 assert 구문안은 false가 됩니다. 따라서, 마지막의 print 구문이 실행되지 못하고, 해당 assert 구문에서 프로그램의 에러가 발생하고 멈추는 것을 볼 수 있습니다.
239 | 240 | 241 | 242 |// darttutorial-10-08.dart 243 | 244 | void main() { 245 | var bool1 = true; 246 | var bool2 = true; 247 | 248 | //assert(bool1 == bool2); 249 | assert(bool1 != bool2); 250 | 251 | print("Completed!"); 252 | }253 | 254 | 255 | 256 |
프로그램의 동작이 멈춘 화면을 참조로 아래와 같이 캡쳐하여 본문에 포함 시켰습니다.
257 | 258 | 259 | 260 |이번 글에서는 프로그램의 흐름이 단순히 위에서 아래로 폭포수처럼 흐르지 않고, 중간에서 왼쪽 혹은 오른쪽과 같이 분기가 이루어 지는 문법을 배웠습니다. 이를 위한 중요한 데이타 타입인 true/false 값을 갖는 Boolean 데이타 타입에 대해서도 배웠습니다. 분기를 문법으로는 if, else if, else 및 switch 구문에 대해서도 다루었습니다. 마지막으로 디버그시에 프로그램이 특정 조건에 부합(혹은 부합하지 않으면) 프로그램의 수행을 중단 시키는 assert 구문에 대해서도 배웠습니다.
269 | 270 | 271 | 272 |Creative Commons License (CC BY-NC-ND)
277 | 278 | -------------------------------------------------------------------------------- /doc/dart-tutorial-11.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - Repetition (Loop) > 2 | 3 | 4 |반복문(repetition, loop)는 같은 동작을 반복적으로 수행하는 문법을 의미합니다. Dart에서는 for, while, do-while의 세가지 반복문 문법을 제공합니다. 지금까지는 반복문을 사용하는 예제를 보이지 않았지만, 구구단을 계산하거나 대량의 데이타를 다루는 List, Set, Map 등의 문법을 익히면 반드시 반복 구문을 사용할 수 밖엔 없으니 잘 이해 바랍니다.
5 | 6 | 7 | 8 |For 반복문의 문법은 "for ( 초기값 설정 ; 조건 검사 ; 초기값 업데이트 ) { ... }" 형태입니다. 간단한 예제를 보는 것이 더 빠른 이해를 돕는 방법이기에, darttutorial-11-01.dart를 통해서 자세한 설명을 하도록 합니다.
13 | 14 | 15 | 16 |// darttutorial-11-01.dart 17 | 18 | void main() { 19 | var sum = 0; 20 | for(var num = 1; num <= 10; num++) { 21 | sum = sum + num; 22 | print("sum is $sum and num is $num."); 23 | } 24 | print("Finally, sum is $sum."); 25 | }26 | 27 | 28 | 29 |
darttutorial-11-01.dart안의 for 문을 보면 초기값 설정 부분에 var num = 1;로 되어 있습니다. 이 부분은 for 구문을 들어가는 처음에 한번 수행됩니다. 그리고 중간의 조건 검사는 for 구문을 통한 반복을 수행할 것인지, 아니면 반복을 중간할 것인지를 검사하는 구문입니다. 여기에서 true 값이 나오면 for 구문의 { ... }로 진입을 하지만, false가 나오면 { ... }로 진입하지 않고, for 구문의 다음 문장으로 바로 이동합니다. 마지막으로 마지막의 num++는 { .. } 안의 작업을 마친후 수행하는 문장으로, 이 프로그램에서는 sum = sum + num;을 수행한 후 num 값을 1 증가시킨다는 의미입니다. 이해가 안되는 부분은 아래의 수행 결과를 보면서 차분하게 생각해 보도록 합니다.
30 | 31 | 32 | 33 |sum is 1 and num is 1. 34 | sum is 3 and num is 2. 35 | sum is 6 and num is 3. 36 | sum is 10 and num is 4. 37 | sum is 15 and num is 5. 38 | sum is 21 and num is 6. 39 | sum is 28 and num is 7. 40 | sum is 36 and num is 8. 41 | sum is 45 and num is 9. 42 | sum is 55 and num is 10. 43 | Finally, sum is 55.44 | 45 | 46 | 47 |
반복문 안에 반복문을 넣는 동작이 필요할 수 있습니다. 예를 들어 구구단을 계산한다면, N * M의 결과를 계산해야 합니다. 이 경우 N이 1~9까지 증가하면서 M도 1~9까지 증가하는 반복 구문의 반복이 필요하게 됩니다. 이렇게 구구단을 계산하는 프로그램을 darttutorial-11-02.dart에서 확인할 수 있습니다.
48 | 49 | 50 | 51 |// darttutorial-11-02.dart 52 | 53 | void main() { 54 | var temp = 0; 55 | 56 | for(var num1 = 1; num1 <= 9; num1++) { 57 | for(var num2 = 1; num2 <= 9; num2++) { 58 | temp = num1 * num2; 59 | print("$num1 * $num2 = $temp"); 60 | } 61 | } 62 | }63 | 64 | 65 | 66 |
수행결과는 익히 알고 있는 구구단의 결과를 보여줍니다. darttutorial-11-01.dart 프로그램 처럼 반복구문 안에 반복구문을 넣는 것은 가능하며, 이를 nested-for 혹은 nested-loop 라고 부릅니다. 아래는 수행결과의 처음과 끝부분만 부분적으로 표시하였습니다.
67 | 68 | 69 | 70 |1 * 1 = 1 71 | 1 * 2 = 2 72 | 1 * 3 = 3 73 | ... 74 | 9 * 7 = 63 75 | 9 * 8 = 72 76 | 9 * 9 = 8177 | 78 | 79 | 80 |
반복구문을 사용할때 한가지 주의사항이 있습니다. 반복구문 안에서 만든 데이타는 반복구문 밖에서는 사용할 수 가 없습니다. 반대로 반복구문 밖에서 만든 데이타는 반복구문 안에서 사용이 가능합니다. 이를 간단한 예제인 darttutorial-11-03.dart을 통해서 이해해 봅니다.
81 | 82 | 83 | 84 |// darttutorial-11-03.dart 85 | 86 | void main() { 87 | var oNum = 1; 88 | for(var iNum=1; iNum <3; iNum++){ 89 | print("[OUT] oNum is $oNum and iNum is $iNum."); 90 | } 91 | print("[IN ] oNum is $oNum and iNum is $iNum."); 92 | }93 | 94 | 95 | 96 |
darttutorial-11-03.dart를 MSVC에서 타이핑하면, 이미 for 구문의 밖에 있는 두번째 print 구문안의 $iNum 밑에 붉은 색 라인이 나타나며, 마우스를 그 위에 올리는 순간 iNum이 undefined 되어 있다고 나옵니다. 변수들은 유효한 범위가 있는데, 반복구문안에서 만들어진 변수들은 반복구문 밖에서는 의미가 없기에, Dart 언어가 못찾겠다고 나오는 겁니다. 하지만 반복구문 밖에서 만든 oNum의 경우는 for 구문의 위, 속, 밖에 있어도 문제없이 쓸수 있는 것을 볼 수 있습니다.
97 | 98 | 99 | 100 |while 반복문의 문법은 "while (조건 검사) { ... }" 형태의 단순한 구조입니다. 조건 검사 구문이 true 이면 { ... }의 작업을 수행한다는 의미입니다. 반복 구문의 문법이 세가지 이지만, 어떤 문법을 사용하든, 유사한 효과를 만들 수 있습니다. 아래는 darttutorial-11-01.dart을 while 구문을 사용해서 변경한 darttutorial-11-04.dart 프로그램으로 동작은 동일합니다.
105 | 106 | 107 | 108 |// darttutorial-11-04.dart 109 | 110 | void main() { 111 | var sum = 0; 112 | var num = 1; 113 | while(num <= 10) { 114 | sum = sum + num; 115 | print("sum is $sum and num is $num."); 116 | num++; 117 | } 118 | print("Finally, sum is $sum."); 119 | }120 | 121 | 122 | 123 |
개발자들에게 선호하는 반복구문을 물어보면, 가장 많은 개발자들이 for 구문을 선택합니다. 이유를 위에서 살펴보면, for 구문의 경우는 반복을 해야 하는 3가지 조건이 for 구문의 첫줄에 명확하게 명시가 되므로 실수할 가능성이 낮은데 반하여, while 구문은 while 문장의 위 혹은 안에 추가적인 작업을 해야 한다는 의미로서, 깜빡하고 잊는 경우가 발생하면, 반복구문이 오동작할 확률이 증가하는 요인입니다. 하지만, 반복구문은 어떤 구문을 사용하든 거의 동일한 작업을 할 수 있으므로, 어떤 구문을 사용하든 큰 문제는 없습니다.
124 | 125 | 126 | 127 |Do-While 문법은 do { ... } while(조건 검사); 형태로 작성합니다. Do-While은 앞서의 for, while 구문과 한가지 차이점이 있습니다. 반드시 한번은 { ... }을 수행한다는 점입니다. for, while의 경우는 조건에 부합하지 않으면, { ... }을 한번도 수행하지 않을 수 있습니다. 아래는 darttutorial-11-01.dart을 while 구문을 사용해서 변경한 darttutorial-11-05.dart 프로그램으로 동작은 동일합니다. 한가지 명심할 부분은 do-while 구문의 경우는 while (...) 구문 뒤에 반드시 semi-colon을 적어야 합니다.
132 | 133 | 134 | 135 |// darttutorial-11-05.dart 136 | 137 | void main() { 138 | var sum = 0; 139 | var num = 1; 140 | do { 141 | sum = sum + num; 142 | print("sum is $sum and num is $num."); 143 | num++; 144 | } while(num <= 10); 145 | print("Finally, sum is $sum."); 146 | }147 | 148 | 149 | 150 |
반복 구문안에서 break와 continue 구문을 사용하면, 반복의 흐름을 조정하는데 사용합니다. 먼저 continue 구문을 사용하여 darttutorial-11-01.dart을 수정한 다음의 darttutorial-11-06.dart를 이해해 봅시다.
155 | 156 | 157 | 158 |// darttutorial-11-06.dart 159 | 160 | void main() { 161 | var sum = 0; 162 | for(var num = 1; num <= 10; num++) { 163 | if(num % 2 == 0){ 164 | sum = sum + num; 165 | } else { 166 | continue; 167 | } 168 | print("sum is $sum and num is $num."); 169 | } 170 | print("Finally, sum is $sum."); 171 | }172 | 173 | 174 | 175 |
for 구문 안을 보면, if-else의 조건 구문이 있는 것을 볼 수 있습니다. if 구문의 경우는 num을 2로 나눠서 정수 나머지가 0인 경우, 즉 짝수인 경우에만 sum의 값에 num을 추가해서 업데이트 하는 내용으로 기존의 학습 내용을 통해서 이해 가능합니다. else 구문 안을 보면, 딱 한줄인 continue;만 있는데, 이렇게 반복 구문 안에 continue가 있으면, 현재 수행중인 지점 아래의 내용을 수행하지 않고(skip하고), 반복을 바로 이어 가도록 합니다. darttutorial-11-06.dart의 경우는, for 구문의 num++를 수행하도록 하고, 이어서 num < 10의 조건 검사로 넘어가는 것 입니다. 다음의 수행 결과를 통해서 자세하게 이해하기 바랍니다.
176 | 177 | 178 | 179 |sum is 2 and num is 2. 180 | sum is 6 and num is 4. 181 | sum is 12 and num is 6. 182 | sum is 20 and num is 8. 183 | sum is 30 and num is 10. 184 | Finally, sum is 30.185 | 186 | 187 | 188 |
반복문 안에 break 구문이 있으면, 현재 수행중인 반복구문을 중단합니다. 그렇게 되면, 프로그램의 흐름은 반복구문 바로 뒤의 작업으로 넘어갑니다. darttutorial-11-06.dart 프로그램을 수정해서, 반복작업을 하다가 sum의 값이 20과 같거나 크면 반복구문을 중단하도록 수정해 봅니다. 이렇게 수정한 darttutorial-11-07.dart을 이해해 봅니다.
189 | 190 | 191 | 192 |// darttutorial-11-07.dart 193 | 194 | void main() { 195 | var sum = 0; 196 | for(var num = 1; num <= 10; num++) { 197 | if(num % 2 == 0){ 198 | sum = sum + num; 199 | } else { 200 | continue; 201 | } 202 | print("sum is $sum and num is $num."); 203 | 204 | if(sum >= 20) { 205 | break; 206 | } 207 | } 208 | print("Finally, sum is $sum."); 209 | }210 | 211 | 212 | 213 |
수행결과는 일부 바뀌어서 sum의 값이 20이 된 순간 종료하게 되며, 이의 수행 결과가 아래와 같습니다. 이렇게 반복구문 안에서의 프로그램의 흐름은 조건문을 사용하거나, break 및 continue 구문을 사용하여 제어할 수 있습니다.
214 | 215 | 216 | 217 |sum is 2 and num is 2. 218 | sum is 6 and num is 4. 219 | sum is 12 and num is 6. 220 | sum is 20 and num is 8. 221 | Finally, sum is 20.222 | 223 | 224 | 225 |
컴퓨터 프로그램을 작성하는 이유 중 하나가 반복되는 작업을 사람보다 빠르게 진행하는 용도입니다. 이를 위하여 Dart 언어는 for, while, do-while 구문을 제공합니다. 어느 문법을 사용하든 대부분의 경우 동일한 결과를 얻을 수 있습니다. 반복구문 안에서 프로그램의 흐름의 바꾸기 위한 용도로 break와 continue가 있으며, break는 반복을 중단하는 용도로 사용하고, continue는 일부 작업을 skip 하고 반복을 재개하는 역할로 사용합니다.
230 | 231 | 232 | 233 |Creative Commons License (CC BY-NC-ND)
238 | 239 | -------------------------------------------------------------------------------- /doc/dart-tutorial-12.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - Functions 2 | 3 | 4 |Function은 함수라고 부르며, 수학의 함수를 생각한다면 비슷한 개념입니다. 간단히 수학에서의 간단한 함수 예제를 보면 y = f(x)의 형태입니다. 이 경우, 함수의 이름은 f 이며, 함수 f가 입력 파라메타로 x를 받아서, 작업을 마치면, 결과를 y에 저장한다는 의미인 것을 기억하면 됩니다. 마찬가지로 Dart 프로그램의 함수도 함수의 이름을 가지며, 입력 파라메타를 받아서 정해진 작업을 수행한 후, 결과를 return 하도록 만들어져 있습니다.
5 | 6 | 7 | 8 |이미 우리는 친숙한 두가지 함수를 지속적으로 사용하고 있습니다. 하나는 화면에 문자열을 출력하기 위한 용도의 print(..) 입니다. 입력 파라메타로 문자열을 하나 받는 함수로서, 이름이 print 이며, 정해진 작업은 화면에 글자를 출력하는 역할입니다.
13 | 14 | 15 | 16 |그 다음으로 익숙한 함수는 main 입니다. main 함수의 일반적인 형태는 void main() 이였습니다. 이는 함수 이름은 main 이며, 별도의 입력 파라메타를 받지 않는 용도로 하고, 별도의 결과를 return 하지 않는다는 의미(void)로 작성한 것 입니다. main 함수에서 수행하는 작업이 main() 뒤의 { ... }로서, 지금까지 우리가 채워놓은 내용이 바로 main 함수에서 수행한 내용들이 였던 겁니다.
17 | 18 | 19 | 20 |print() 함수가 수행하도록 명령을 내린 사람(함수를 호출(call 또는 invoke)한다고 함)은 개발자 본인입니다. main() 함수의 경우는, 개발자는 채울뿐 호출하는 쪽은 Dart 언어로 만든 프로그램을 수행하는 운영체제라고 보시면 됩니다.
21 | 22 | 23 | 24 |수학의 함수와 컴퓨터 프로그램의 함수가 유사한 개념인 것을 맞지만, 차이점도 존재 합니다. 예를들어, main 함수는 입력 파라메타와 출력 파라메타가 모두 없습니다. 호출되면 수행할 작업만 있습니다. print 함수는 입력 파라메타는 있지만 출력 파라메타는 없습니다. 여기서 수학의 경우는 숫자로된 결과를 출력 파라메타로 받기 위하여 함수를 호출하지만, 컴퓨터 프로그램의 경우는 정해진 작업만 마치면 되고, 굳이 결과값이 만들어질 필요가 없는 경우가 있습니다. 따라서 컴퓨터 프로그램에서는 입력 파라메타와 출력 파라메타가 '있다/없다'의 유무로 인한 4가지 조합이 가능한 점이 일단 수학의 함수와 다릅니다.
25 | 26 | 27 | 28 |간단하게 두 개의 숫자를 입력 파라메타로 받아서, 이 들의 합(sum)을 계산한 후, 계산한 결과 값을 돌려주는(return) 하는 함수를 만들어 보겠습니다. 함수의 이름은 calcSum()으로 하도록 하겠습니다. 이를 Dart 언어로 작성하면 다음과 같습니다.
33 | 34 | 35 | 36 |calcSum(var num1, var num2) { 37 | return num1 + num2; 38 | }39 | 40 | 41 | 42 |
새로운 문법으로 return 구문이 나타났습니다. 이 구문은 계산한 결과를 함수를 호출한 곳으로 돌려주기위한 용도로 사용합니다. 즉, y = f(x)의 경우를 생각해 보면, x를 받은 함수 f가 정해진 계산을 하고 나면, 그 결과가 y로 이동을 해야 합니다. 수학에서는 계산후 결과를 종이에서 y = ... 이라고 쓰면 되지만, 컴퓨터 프로그램에서는 f(x)의 결과가 컴퓨터 내부의 장치를 통해서 y라는 공간(컴퓨터 메모리)로 이동해야 합니다. 이런 기능을 수행하는 문법이 return 입니다. calcSum 함수 내부의 문법을 기술적으로 설명하면 다음과 같습니다. 먼저 num1과 num2의 값을 더합니다. 그렇게 구해진 결과를 컴퓨터 메모리를 통해서 calcSum을 호출한 지점으로 return 구문을 통해서 돌려줍니다. 그러면 이 함수를 실제 활용하는 예제 프로그램 darttutorial-12-01.dart을 통해서 이해해 보도록 합니다.
43 | 44 | 45 | 46 |// darttutorial-12-01.dart 47 | 48 | calcSum(var num1, var num2) { 49 | var sum = num1 + num2; // 3 50 | print("[calcSum()] $num1 + $num2 is $sum"); // 4 51 | return sum; // 5 52 | } 53 | 54 | void main() { 55 | print("[main()] start"); // 1 56 | var resNum = calcSum(5, 5); // 2, 6 57 | print("[main()] result is $resNum"); // 7 58 | }59 | 60 | 61 | 62 |
darttutorial-12-01.dart 프로그램이 수행되면, main() 안에서 바로 calcSum 함수를 호출하면서 입력 파라메타로 5와 5를 전달하는 것을 볼수 있습니다. main() 안에서 calSum 함수를 호출하면, 프로그램의 수행은 calcSum 함수로 이동합니다. calSum으로 이동후, 첫번째 파라메타인 5와 두번째 파라메타 5를 더한 값 10을 sum에 저장한 후, 이 값을 return 구문을 통해서 calcSum으로 돌려줍니다. 쉽게 이야기 하면, calcSum을 호출한 지점이 10이라는 숫자로 바뀐다고 보면 됩니다. 이해를 돕기 위해서, 각 문장의 뒤에, 해당 문장이 수행하는 순서를 숫자로 적어 두었습니다. 그리고 프로그램의 수행 결과를 다음에 적어 두었으니 참조하기 바랍니다.
63 | 64 | 65 | 66 |[main()] start 67 | [calcSum()] 5 + 5 is 10 68 | [main()] result is 10 69 |70 | 71 | 72 | 73 |
darttutorial-12-01.dart의 calcSum 함수처럼 1줄 정도의 작업만 하면 되는 간단한 함수들이 있습니다. 이 경우는 Dart 언어의 shorthand syntax를 사용하여 보다 간단하고 편리하게 나타낼 수 있습니다. 이를 위해서 darttutorial-12-01.dart을 단순한 형태로 바꾼 darttutorial-12-02.dart를 참조하시기 바랍니다.
78 | 79 | 80 | 81 |// darttutorial-12-02.dart 82 | 83 | calcSum(var num1, var num2) { 84 | return num1 + num2; 85 | } 86 | 87 | void main() { 88 | print("[main()] start"); 89 | var resNum = calcSum(5, 5); 90 | print("[main()] result is $resNum"); 91 | }92 | 93 | 94 | 95 |
// darttutorial-12-02.dart 96 | 97 | calcSum(var num1, var num2) { 98 | return num1 + num2; 99 | } 100 | 101 | void main() { 102 | print("[main()] start"); 103 | var resNum = calcSum(5, 5); 104 | print("[main()] result is $resNum"); 105 | }106 | 107 | 108 | 109 |
calcSum이 한줄로만 이루어진 단순한 함수 이기에 shorthand syntax로 변환할 예정으로, 문법은 =>의 화살표입니다. 예제를 먼저 보고 이해를 하도록 합니다. 다음의 darttutorial-12-02.dart는 calcSum 함수를 shorthand syntax로 변경한 형태입니다.
110 | 111 | 112 | 113 |// darttutorial-12-03.dart 114 | 115 | calcSum(var num1, var num2) => num1 + num2; 116 | 117 | void main() { 118 | print("[main()] start"); 119 | var resNum = calcSum(5, 5); 120 | print("[main()] result is $resNum"); 121 | } 122 |123 | 124 | 125 | 126 |
함수의 이름과 입출력 부분은 동일 하지만, 이를 수행할 함수의 몸체는 매우 단순해 졌는데, 먼저 해당 함수가 호출되면 수행할 내용을 단순하게 =>의 오른쪽에 한줄로만 작성하면 됩니다. 위의 예제를 좀 더 자세하게 설명하면, calcSum() 함수는 shorthand 함수로서, 입력 파라메타 두개를 받으면, 이둘을 더한 값을 "return" 한다고 보면 됩니다. 즉, calcSum() 함수는 "return num1 + num2"라는 의미로 해석합니다. 프로그램의 수행결과는 앞서의 함수의 경우와 다르지 않습니다.
127 | 128 | 129 | 130 |Anonymous 라는 단어는 프로그램 개발에서 자주 등장 하는 단어입니다. 여러 곳에서 등장 하는데, anonymous function이라고 하면, 함수이기는 한데, 이름이 없다는 의미입니다. 이름이 없다는 것은, 굳이 이름까지 붙일 정도의 함수는 아니다 라고 보면 됩니다. 이런 함수는 프로그램 전체적인 측면에서 사용하는 것이 아니고, 일부의 지역 혹은 호출 하는 지점에서만 함수가 의미를 갖는 다는 정도로 이해하면 됩니다. 그러면 다시 앞서의 darttutorial-12-03.dart을 다시 한번 수정하여 anonymous function을 적용해 보겠습니다. 수정한 내용이 darttutorial-12-04.dart로 아래와 같습니다.
135 | 136 | 137 | 138 |// darttutorial-12-04.dart 139 | 140 | void main() { 141 | var myFunc = (var num1, var num2) => num1 + num2; 142 | 143 | print("[main()] start"); 144 | var resNum = myFunc(5, 5); 145 | print("[main()] result is $resNum"); 146 | } 147 |148 | 149 | 150 | 151 |
먼저 (var num1, var num2) => num1 + num2;를 보면, 함수의 이름이 없으며 입력은 두개이고, 이를 받으면 둘을 더한 값을 return 하는 함수를 표현합니다. 이를 "var myFunc ="로 한것은, 이 함수를 그대로 변수 myFunc에 넣었다는 것입니다. 변수에 함수를 넣는다는 것이 생소할 수 있지만, 객체지향 프로그래밍 언어들에서 변수에는 데이타 외에 함수도 저장이 가능합니다. 따라서, "함수의 이름은 없지만, 이제 부턴 이 함수는 myFunc라고 부르겠어" 정도의 의미로 이해하면 되겠습니다. 이렇게 myFunc로 부르기로 한 함수의 호출은 일반 함수의 호출과 같은 방법으로 호출하면 됩니다. 따라서 var resNum = myFunc(5, 5);에서 나타나듯이, 두개의 5를 입력 파라메타로 받아, 이름 없는 함수를 통해 더 한후, 그 결과인 10을 return 받아서 resNum 변수에 넣는 동작을 의미합니다.
152 | 153 | 154 | 155 |함수의 입력 파라메타를 전달하는 방법에는 몇가지 추가적인 기능들이 있습니다. 이들의 차이점을 한번에 비교해 가면서 이해할 수 있도록 darttutorial-12-05.dart 프로그램안에 설명하고자 하는 모든 가짓수를 포함하였습니다.
160 | 161 | 162 | 163 |// darttutorial-12-05.dart 164 | 165 | // Case.1 Positional parameters 166 | int calcFraction1(var denominator, var nominator) { 167 | return denominator ~/ nominator; 168 | } 169 | 170 | // Case.2 Positional and optional parameters 171 | int calcFraction2(var denominator, [var nominator]) { 172 | if(nominator == null){ 173 | nominator = 1; 174 | } 175 | return denominator ~/ nominator; 176 | } 177 | 178 | // Case.3 Positional, optional and default parameters 179 | int calcFraction3(var denominator, [var nominator = 1]) { 180 | return denominator ~/ nominator; 181 | } 182 | 183 | // Case.4 Named parameters (all parameters are optional) 184 | int calcFraction4({var denominator, var nominator}) { 185 | return denominator ~/ nominator; 186 | } 187 | 188 | // Case.5 Named parameters with initial value checking 189 | int calcFraction5({var denominator, var nominator}) { 190 | if(nominator == null){ 191 | nominator = 1; 192 | } 193 | return denominator ~/ nominator; 194 | } 195 | 196 | // Case.6 Named parameters with default parameters 197 | int calcFraction6({var denominator, var nominator = 1}) { 198 | return denominator ~/ nominator; 199 | } 200 | 201 | void main() { 202 | var result1 = calcFraction1(1, 1); 203 | var result2 = calcFraction2(2); 204 | var result3 = calcFraction3(3); 205 | 206 | // print 1 2 3 207 | print("$result1 $result2 $result3"); 208 | 209 | var result4 = calcFraction4(denominator: 4, nominator: 1); 210 | var result5 = calcFraction4(nominator: 1, denominator: 4); 211 | var result6 = calcFraction5(denominator: 5); 212 | var result7 = calcFraction6(denominator: 6); 213 | 214 | // print 4 4 5 6 215 | print("$result4 $result5 $result6 $result7"); 216 | } 217 |218 | 219 | 220 | 221 |
Positional Parameters 먼저 무심결에 사용하고 있지만, 이름을 언급하지 않은 방법입니다. 즉, 함수의 입력 파라메타 들에 대해서, 타입과 이름을 단순히 나열하는 형태입니다. darttutorial-12-05.dart의 Case.1에 해당하는 부분입니다.
222 | 223 | 224 | 225 |Optional Parameters in Positional Parameters Case.1에서 설명한 방법의 진보된 방법입니다. 먼저 calcFractionN 함수들의 기능은 모두 같은데, 단순하게 분모와 분자를 입력 차라메타로 전달받으면 분자로 분모를 나눈 몫을 정수 형태로 retuen 합니다. Case.1에서는 반드시 두개의 입력 파라메타인 분모와 분자를 입력 파라메타로 받아야 합니다. Case.2에서는 함수 입력 파라메타 중 var nominator를 [ ... ] 괄호로 묶었는데, 이 의미는 두번째 파라메타를 받을수도 있고, 안받아도 동작하는 optional 파라메타로 처리하겠다는 의미입니다. 따라서, main 함수의 호출부분을 보면 분모만 2를 전달하고, 분자는 없습니다. 이런 경우, optional 입력 파라메타가 주어졌는지 아닌지를 판단하기 위해서는, 예제처럼 null 값과의 비교를 수행한 후, 맞춰서 작업을 하도록 하면 됩니다.
226 | 227 | 228 | 229 |Optional & Default Parameters in Positional Parameters Case.2에서는 두번째 입력 파라메타를 optinal하게 해서, 만약 null 이라면 분자를 1로 바꾸는 작업을 했습니다. 이렇게 함으로써, 분자가 null 상태로 나누기를 해서 에러가 발생하는 것을 방지한 것 입니다. 이렇게 프로그램을 작성할때, optional한 입력 파라메타를 받지만, 만약 주어지지 않았을때 어떤 특정 값으로 설정을 해주는 값이 있다면, Case.3 처럼 optional 파라메타에 "= N" 처럼 합니다. 즉, 해당 파라메타가 optional 한데, 주어지지 않으면 N으로 초기화 하겠다는 겁니다. 따라서, calcFraction3 함수의 실행 부분에서 분모만 3으로 주었지만, 자동으로 분자를 1로 설정했기에, return 값이 3으로 나옵니다.
230 | 231 | 232 | 233 |Named Parameters 함수에 따라서, 모든 함수의 입력 파라메타가 optional 한 경우가 있습니다. 이런 경우는 named parameter 방법을 사용하는데, 문법은 간단 합니다. 입력 파라메타 들을 { ... } 기호로 묶으면 되며, 예제의 Case.4에 해당합니다. 이런 경우에, 함수 호출 방법이 바뀝니다. main에서 calcFraction4 함수 호출 부분을 보면, "입력 파라메타의 이름 : 전달하고자 하는 값"의 형태로 되어 있는 것을 볼수 있습니다. Named parameter 방식을 사용하면, calcFraction4를 호출하는 두가지 방법에서 처럼, 파라메타의 순서는 의미가 없습니다. Named parameter는 모든 입력 파라메타가 optional 하므로, 해당 파라메타가 주어지지 않은 경우를 점검해서 특별한 동작을 해야 한다면, Case.5 처럼 파라메타의 null 여부를 판단하면 됩니다.
234 | 235 | 236 | 237 |Default Parameters in Named Parameters Named parameter 방식에서 default 값을 주고 싶다면, 앞서의 calcFraction3와 유사하게, default 값을 주고자 하는 입력 파라메타 뒤에 "= N" 처럼 하여 초기화 값을 지정하면 됩니다. 단, [ ... ]의 기호는 필요가 없습니다.
238 | 239 | 240 | 241 |앞서 반복 구문을 배우는 내용에서, 반복문 안에서 만든 변수는 반복문 밖에서 사용할 수 없는 것을 배웠습니다. 유사하게 함수안에서 만들어진 변수는 함수밖에서는 사용할 수가 없습니다. 이를 darttutorial-12-06.dart 프로그램에서 확인할 수 있습니다. 마지막 print 구문은 프로그램 실행시 에러가 납니다. localSum을 함수 밖에서 접근하는 경우는 해당 변수가 없는 것을 Dart 언어가 인지하고, 없는 변수에 대한 접근으로 인식하여 에러가 납니다.
246 | 247 | 248 | 249 |// darttutorial-12-06.dart 250 | 251 | int calcSum(int num1, int num2) { 252 | var localSum = num1 + num2; 253 | return localSum; 254 | } 255 | 256 | void main() { 257 | var resNum = calcSum(1,1); 258 | print("$resNum"); 259 | print("$localSum"); // Error 260 | } 261 |262 | 263 | 264 | 265 |
수학의 함수와 유사한 프로그래밍 언어의 함수에 대해서 배웠습니다. 수학과의 차이점으로는 수학의 함수는 반드시 입력과 출력이 있지만, 프로그램에서는 둘중 하나가 없거나 둘다 없는 경우들도 일반적입니다. 그리고 Dart는 함수를 줄여서 나타내는 shorthand와 anonymous 기능이 있었습니다. 또한, 입력 파라메타를 나타내는 방식으로 positional과 named 방식이 있었습니다. 또한 입력 파라메타가 선택으로 없어도 되는 optional 모드가 있었고, 주어지지 않은 입력 파라메타에 초기화 값을 지정하는 defaul 방식도 있음을 확인 하였습니다.
270 | 271 | 272 | 273 |Creative Commons License (CC BY-NC-ND)
278 | 279 | -------------------------------------------------------------------------------- /doc/dart-tutorial-13.md: -------------------------------------------------------------------------------- 1 | # MS Visual Code를 통한 Debug 2 | 3 | 4 |지금까지 Dart 언어를 배워 왔으며, 이번 글에서는 MSVC를 통해서 지금까지 내운 Dart 언어의 특징을 보다 효율적으로 디버그 하기 위한 방법을 설명 합니다.
5 | 6 | 7 | 8 |MSVC의 디버그 기능을 활용해 보기 위해서 간단한 예제 프로그램을 아래의 darttutorial-13-01.dart 처럼 작성 하였습니다. 간단히, 지금까지 배운 내용들에 대한 기능의 에러를 어떻게 MSVC를 통해서 빠르고 정확하게 점검 하느냐에 집중 하겠습니다. 프로그램 자체는 특별한 의미가 있지 않습니다.
13 | 14 | 15 | 16 |17 | // darttutorial-13-01.dart 18 | 19 | int sampleFunction(var varA, [var varB = 1]) { 20 | var localSum; 21 | localSum = varA + varB; 22 | print("[sampleFunction] $localSum"); 23 | return localSum; 24 | } 25 | 26 | void main() { 27 | var num1 = 1; 28 | var num2 = 5; 29 | var num3; 30 | 31 | print("[main] start with num3:$num3."); 32 | 33 | num3 = sampleFunction(num1, num2); 34 | 35 | for(var temp = 1; temp < 3; temp++) { 36 | num3 = num3 + sampleFunction(num1 + temp, num2 + temp); 37 | } 38 | 39 | print("[main] end with num3:$num3."); 40 | } 41 |42 | 43 | 44 | 45 |
미리 보는 수행 결과는 다음과 같습니다.
46 | 47 | 48 | 49 |[main] start with num3:null. 50 | [sampleFunction] 6 51 | [sampleFunction] 8 52 | [sampleFunction] 10 53 | [main] end with num3:24. 54 |55 | 56 | 57 | 58 |
이제 디버그 기능을 수행하기 전의 화면은 다음과 같이 나타나 있어야 합니다.
59 | 60 | 61 | 62 |프로그램을 개발하다 보면, 에러가 의심이 되는 지점에 멈추고 싶을때가 있습니다. 이런 경우를 위해서 대부분의 개발도구 지원하는 기능은 break points 설정 기능입니다. 원하는 지점을 지정하여, 프로그램이 해당 지점에 도착하면 실행을 잠시 멈추는 것 입니다. Debug 모드로 작업하기 위해서, MSVC를 debug 모드로 전환합니다. 그리고 제대로 프로그램이 타이핑 되었는지를 확인 하기 위하여, 미리 프로그램을 수행시켜 봅니다. 이렇게 debug 모드로 바꾼후, 프로그램을 실행시킨 후의 화면이 다음과 같습니다.
71 | 72 | 73 | 74 |MSVC에서 원하는 지점에 멈추는 작업을 실제로 수행하겠습니다. 목표 작업은 sampleFunction 함수 내부에서 제대로 계산이 이루어 지는가 입니다. 따라서 sampleFunction 함수 안에 진입을 하면, 프로그램을 멈추도록 합니다. 이를 위해서, sampleFunction 함수의 첫번째 실행 라인인 라인 번호 4의 왼쪽으로 마우스를 이동해 봅니다. 그러면, 갑자기 붉은 색의 원이 나타날 겁니다. 이것이 break points 입니다. 해당 라인에 break points를 설정하고자 하면, 붉은 원을 마우스로 클릭합니다 그러면, 보다 붉은 원이 라인 번호의 왼쪽에 확정적으로 고정되는 것을 볼수 있습니다.
79 | 80 | 81 | 82 |이 상태에서 프로그램을 수행 시킵니다. 그러면 그림 4와 같은 화면이 나타납니다. 여러 변화가 있을텐데, 먼저 화면을 파란색으로 되어있던 테두리가 주황색으로 바뀌어 있습니다.
87 | 88 | 89 | 90 |또한, break points를 설정한 위치에 노랑색 화살표 도형이 있는 것을 볼수 있는데, 이는 프로그램이 이 지점에서 멈추어 있다는 것을 의미합니다. 그리고 왼쪽 화면에 "변수"라고 쓰여 있으며, 그 하단에 아래와 같이 나타난 것을 볼수 있습니다. Locals란 앞서 함수의 설명에서, 함수안에서 만들어진 변수들을 지칭하는 것입니다. 함수 sampleFunction 안으로 현재 들어왔는데, 해당 함수의 진입과 동시에, 이 함수의 내부 local 변수이면서 입력 파라메타인 varA와 varB가 각각 1과 5의 값을 가지고 있는 것을 보여줍니다. 따라서, 앞으로 함수 안의 값들이 어떤 값을 가지고 있는지, 해당 화면을 보면서 확인하면 됩니다.
95 | 96 | 97 | 98 |멈춰 있는 프로그램을 한줄 한줄 수행하게될 좋은 기능이 다음과 같이 생긴 (화면의 상위, 중간 부분에 있는) 여섯개의 아이콘들 입니다.
107 | 108 | 109 | 110 |왼쪽 아이콘 부터 차례대로 설명하면, 첫번째 아이콘은 멈춰있는 프로그램의 수행을 재개한다는 의미입니다. 이 단추를 누르게 되면, 다시 프로그램의 정상적인 수행을 이어 갑니다. 만약 다음에 만날 break point가 있다면, 해당 break point에서 다시 멈추게 됩니다. darttutorial-13-01.dart 프로그램의 경우라면, 총3번 sampleFunction 함수를 호출하므로, 다시 4번 라인에서 멈추게 되며, 이때에 왼쪽 화면을 보면 Locals의 varA는 2, varB는 6으로 바뀐 것을 볼 수 있습니다. 다시 첫번째 아이콘을 누르면, 다시 라인 4번에서 멈추며, Locals의 varA는 3, varB는 7으로 바뀐 것을 볼 수 있습니다. 한번더 첫번째 아이콘을 누르면 프로그램은 더이상의 break point가 없으므로, 프로그램은 종료하게 됩니다.
115 | 116 | 117 | 118 |마지막 여섯번째 아이콘은 프로그램을 종료하게 됩니다. 이를 위해서, 라인 4에 break point를 유지한 상태에서 다시 디버그를 시작하여, [그림 4] 상태로 전환 합니다. 이 상태에서 여섯번째 아이콘을 누르면, 디버그 모드가 종료되고, 테두리가 다시 파란색으로 바뀐 것을 볼 수 있습니다.
119 | 120 | 121 | 122 |다시 디버그 모드로 전환해서 [그림 4]의 상태로 유지합니다. 이번에 사용할 기능은 두번째 아이콘 입니다. 라인 4에서 프로그램이 멈추었다는 것은, 라인 4를 시작하기 바로 전단계인 것을 의합니다. 따라서 두번째 아이콘을 한번 누르면, 라인 4를 실행하고 라인 5로 이동합니다.
123 | 124 | 125 | 126 |프로그램의 수행 위치를 나타내는 노랑색 화살표가 5번 라인을 가르키고 있는 것을 볼 수 있습니다. 그리고 왼쪽을 보면 localSum 변수가 만들어져 있는 것을 볼 수 있습니다. 그리고, 두개의 입력 파라메타를 더하는 작업을 아직 진행하지 않았기에, 초기화되지 않은 변수가 갖는 값인 null로 채워져 있는 것을 볼 수 있습니다. 한번 더 두번째 버튼을 누르면, localSum의 값이 6으로 바뀐 것을 볼 수 있습니다. 다시 두번째 아이콘을 꾹꾹 누르다보면, print 구문으로 화면에 출력이 나오고, sampleFunction 함수를 벗어나 main의 호출 부분으로 돌아가는 것을 볼수 있습니다. 이 기능을 사용해서 한줄 한줄 프로그램을 수행해 가면서 본인이 개발한 프로그램이 제대로 동작하는지를 확인할 수 있습니다.
131 | 132 | 133 | 134 |두번째 단추와 세번째 단추는 비슷하지만 차이가 있는데, 이를 확인해 보겠습니다. 이를 위해서 여섯번째 아이콘을 눌러서 디버그를 중지 합니다. 그리고, 라인 4에 설정했던 break point를 다시 한번 눌러서 해제합니다. 그리고, 라인 20의 옆에 break point를 설정합니다. 이제 다시 프로그램을 실행시켜서 라인 20에서 멈추도록 합니다.
135 | 136 | 137 | 138 |이 상태에서, 두번째 아이콘을 눌러 봅니다. 그러면, sampleFunction 함수 안으로 들어가지 않고, sampleFunction 함수를 수행한 후 결과 만을 가져 옵니다. 따라서 함수를 실행하기 전에, 함수가 안정적이라고 믿어지기에 굳이 안으로 들어갈 필요가 없는 경우는 두번째 아이콘을 누르면 됩니다.
143 | 144 | 145 | 146 |그렇지 않고, 함수 안으로 들어가서 함수의 동작을 내부까지 들어가서 확인하고자 한다면, 세번째 아이콘을 누르면 됩니다. 프로그램의 디버그를 중지하고, 다시 [그림 8]의 상태로 돌아 갑니다. 이 상태에서 세번째 아이콘을 누르면, 함수 안으로 들어가서 프로그램의 흐름을 이어가는 것을 볼 수 있습니다. 따라서, 아이콘의 파란 점이 의미 하는 것이 바로 함수를 의미하고, 두번째 아이콘은 함수에 굳이 들어가지 않은 상태에서 결과만 취하는 의미이고, 세번째 아이콘은 함수 안으로 들어가는 것임을 알 수 있습니다.
147 | 148 | 149 | 150 |마지막으로 점에서 나오는 다섯번째의 의미는 충분히 상상이 가능하리라 봅니다. 함수안에서 디버그를 하다가 함수의 남은 부분을 수행하고 함수 밖으로 나오는 기능입니다.
151 | 152 | 153 | 154 |마지막으로 살펴볼 기능은 프로그램 수행 중의 상태를 간단한 코드를 통해서 확인해 보는 방법입니다. 이를 위해서 다시 [그림 8]의 상태로 프로그램을 동작 시킵니다. 그리고, 왼쪽의 "조사식"을 가봅니다. 현재는 빈칸으로 되어 있는데, 조사식 밑의 민칸을 클릭하거나 혹은 조사식이라는 글자에 마우스를 올려놓으면 오른쪽으로 안보이던 '+' 기호가 나타므로, 이 기호를 누르면 프롬프트가 나타나고 입력이 가능합니다. 여기서 num3를 타이핑 한후 엔터를 치고, 다음으로 sampleFunction(3,6)을 타이핑 하고 엔터를 칩니다. 그러면 다음과 같이 결과가 나오는 것을 볼 수 있습니다.
159 | 160 | 161 | 162 |간단하지만 꼭 필요한 기능 중심으로 MSVC의 주요 기능을 알아 보았습니다. 이 정도 기능만 이해해도 프로그램을 개발하면서 디버그 하는데 큰 도움을 받을 것 입니다. MSVC는 이보다 더 많은 기능을 제공하고 있으며, 특히 extension을 통해서 추가적인 디버그 기능을 확장할 수 있습니다. 도구를 잘 다루는 것도 생산성 측면에서 중요한 경쟁력이므로, 틈틈히 기능들을 알아가고 하나 하나 활용해 보기 바랍니다.
171 | 172 | 173 | 174 |Creative Commons License (CC BY-NC-ND)
179 | 180 | -------------------------------------------------------------------------------- /doc/dart-tutorial-14.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - Class 사용하기 2 | 3 | 4 |먼저 일반적인 개념에 대해서 설명합니다. 제품을 공장에서 출하하기 전에, 설계도(design document)를 만드는 작업이 있습니다. 예를들어 반도체 설계도가 만들어지면, 이를 통해서 실제 반도체 제품(product)을 공장에서 만들어 진다는 것을 생각하면 됩니다. 이와 유사하게, 객체지향 프로그래밍(object oriented programming)이라는 개발 기법에서, 소프트웨어에서 설계도와 같은 개념의 것을 클래스(class)라고 부릅니다. 그리고 제품에 해당하는 것을 객체/오브젝트(object)라고 부릅니다. 반도체를 예를 들었을때, 반도체에는 저장되는 데이타가 존재합니다. 그리고 데이타를 읽고 쓰는 행동과 지원된다면 의미있는 기능들이 동작합니다. 여기서 데이타를 말 그대로 데이타(data, properties)라고 부르고, 기능은 메소드(methods, operators)라고 부릅니다. 특별히 클래스/객체안에 포함되는 데이타는 클래스/객체의 멤버 데이타(member data)라고 하고, 기능은 멤버 메소드(member method)라고 부릅니다.
5 | 6 | 7 | 8 |Dart에서 모든 데이타 타입은 클래스로 만들어 집니다. 지금까지 알아온 int, double, String, bool, dynamic, var, Object가 모두 클래스 타입이라고 보면 됩니다. 그리고 int a;로 해서, 클래스 int 타입으로 만들어진 a 변수가 객체입니다. 따라서 int 설계도에서 실제 int 제품에 해당하는 a를 만든 겁니다. 설계도에서 만들어진 제품이 실제 설치 되고 동작이 되는 것처럼, 객체 들은 각각 고유의 값을 가지고 있으며, 같은 기능 이라도 각자가 가지고 있는 값에 따라서 다른 결과르 만들 수 있습니다. Dart에서 데이타 타입만 객체가 되는 것은 아니며, 함수와 null 값 마저도 객체이니 참조하기 바랍니다. 또한, Dart 언어에서는 모든 객체들이 Object 클래스 타입을 기반으로 해서 만들어 집니다. 이 부분에 대해서는 Class를 직접 만드는 단계에서 구체적으로 설명 하도록 하겠습니다. 클래스에 대한 개념을 배웠으니, 우리가 이미 알고 있는 클래스들에 대해서 좀 더 구체적으로 설명하면서, 클래스와 객체에 대해서 이해해 보도록 하겠습니다.
13 | 14 | 15 | 16 |Dart에서 int 클래스에 대한 설명은 공식 홈페이지에서 자세하게 확인이 가능합니다 [참조]. int 클래스에서 제공하는 몇가지 요긴한 메소드의 사용 예제를 darttutorial-14-01.dart 에서 확인 할 수 있습니다.
21 | 22 | 23 | 24 |// darttutorial-14-01.dart 25 | // Ref: https://api.dart.dev/stable/2.7.1/dart-core/int-class.html 26 | 27 | void main() { 28 | int num1 = 1; 29 | int num2; 30 | String myString; 31 | 32 | // Returns the absolute value 33 | num2 = num1.abs(); 34 | print("$num2"); 35 | 36 | // Returns the greatest common divisor 37 | num1 = 3; 38 | num2 = num1.gcd(6); 39 | print("$num2"); 40 | 41 | // Returns a string representation 42 | myString = num2.toString(); 43 | print("$myString"); 44 | } 45 |46 | 47 | 48 | 49 |
먼저, 객체의 메소드를 실행하는 방법은 간단합니다. "객체이름.메소드이름()"처럼 하기만 하면 됩니다. 먼저 num1 객체에 1 값으로 초기화한 것을 볼 수 있습니다. 그리고 num1 객체의 메소드인 abs()를 실행하고자 한다면, num1.abs() 처럼 하면됩니다. 이렇게 함으로써, num1 객체가 가지는 멤버 데이타인 1의 절대값을 abs() 함수로 계싼한 후, return 하게 됩니다.
50 | 51 | 52 | 53 |gcd()는 최대공약수(Greatest Common Divisor)를 계산하는 메소드입니다. num1.gcd(6)처럼 호출 하였는데, 이는 num1 객체의 값과 입력 파라메타로 받은 6간의 최대공약수를 계산합니다. 현재는 3이 결과입니다. 마지막 예제는 객체가 가지고 있는 정수 값을 문자열로 변환하는 기능입니다. 이번에는 num2 객체의 멤버 메소드로 호출하였으며, num2.toString()를 통해서 num2가 가진 값 3을 문자열로 바꿔서 출력하도록 하였습니다.
54 | 55 | 56 | 57 |Dart에서 double 클래스에 대한 설명은 공식 홈페이지에서 자세하게 확인이 가능합니다 [참조]. double 클래스에서 제공하는 몇가지 요긴한 메소드의 사용 예제를 darttutorial-14-02.dart 에서 확인 할 수 있습니다.
62 | 63 | 64 | 65 |// darttutorial-14-02.dart 66 | // Ref: https://api.dart.dev/stable/2.7.1/dart-core/double-class.html 67 | 68 | void main() { 69 | double num1 = 1.4; 70 | double num2; 71 | int num3; 72 | String myString; 73 | 74 | // Returns the absolute value 75 | num2 = num1.abs(); 76 | print("$num2"); 77 | 78 | // Returns a string representation 79 | myString = num2.toString(); 80 | print("$myString"); 81 | 82 | // Returns the greatest integer no greater than member data 83 | num3 = num1.floor(); 84 | print("$num3"); 85 | 86 | // Returns the greatest integer double value no greater than member data 87 | num2 = num1.floorToDouble(); 88 | print("$num2"); 89 | 90 | // Returns the greatest integer no greater than member data 91 | num3 = num1.round(); 92 | print("$num3"); 93 | 94 | // Returns the integer double value obtained by discarding any fractional digits 95 | num2 = num1.truncateToDouble(); 96 | print("$num2"); 97 | } 98 |99 | 100 | 101 | 102 |
예제에서 사용한 멤버 메소드는 순서대로 abs(), toString(), floor(), floorToDouble(), round(), truncateToDouble() 입니다. int 클래스의 예제와 겹치는 부분도 일부 있습니다. 각각의 자세한 의미는 해당 홈페이지를 통해서 직접 확인하고, 이해해 보는 것으로 하겠습니다. 추가로 아래의 darttutorial-14-03.dart 처럼, 정수, 실수 혹은 문자열에 "."을 이어서 찍고, 각각의 클래스 타입에 속한 멤버 메소드를 호출하는 방법도 가능합니다.
103 | 104 | 105 | 106 |// darttutorial-14-03.dart 107 | // Ref: https://api.dart.dev/stable/2.7.1/dart-core/double-class.html 108 | 109 | void main() { 110 | var num; 111 | 112 | // Returns the absolute value 113 | num = 1.4.abs(); 114 | print("$num"); 115 | 116 | // Returns a string representation 117 | num = 1.4.toString(); 118 | print("$num"); 119 | 120 | // Returns the greatest integer no greater than member data 121 | num = 1.4.floor(); 122 | print("$num"); 123 | 124 | // Returns the greatest integer double value no greater than member data 125 | num = 1.4.floorToDouble(); 126 | print("$num"); 127 | 128 | // Returns the greatest integer no greater than member data 129 | num = 1.4.round(); 130 | print("$num"); 131 | 132 | // Returns the integer double value obtained by discarding any fractional digits 133 | num = 1.4.truncateToDouble(); 134 | print("$num"); 135 | } 136 |137 | 138 | 139 | 140 |
눈으로 보기엔, 그냥 단순한 정수/실수 숫자 혹은 문자열로 보이지만, Dart는 이런 값들을 모두 클래스로 처리합니다.
141 | 142 | 143 | 144 |문자열은 숫자 만큼이나 프로그래밍에서 많이 사용하는 (어쩌면 숫자보다 저 많이 사용하는) 데이타 타입입니다. Dart의 문자열에 대한 자세한 설명은 홈페이지에서 참조할 수 있습니다 [참조]. 문자열에 대해서도 darttutorial-14-04.dart 예제 프로그램을 통해서 이래를 해보도록 하겠습니다.
149 | 150 | 151 | 152 |// darttutorial-14-04.dart 153 | // Ref: https://api.dart.dev/stable/2.7.1/dart-core/String-class.html 154 | 155 | void main() { 156 | var string1 = "Hello Dart!"; 157 | var string2; 158 | var num; 159 | 160 | // Sub-slicing (or Sub-string) 161 | string2 = string1.substring(0,5); 162 | print("$string2"); 163 | 164 | // Index Operator 165 | string2 = string1[0]; 166 | print("$string2"); 167 | 168 | // Concatenation 169 | string2 = "Hello" + " " + "Dart" + "!"; 170 | print("$string2"); 171 | 172 | // Using String Properties 173 | num = string1.length; 174 | print("$num"); 175 | 176 | // Interpolate the value of expressions within strings 177 | string2 = "$string1 has ${string1.length} letters"; 178 | print("$string2"); 179 | 180 | // Converts all characters to lower case 181 | string2 = string1.toLowerCase(); 182 | print("$string2"); 183 | 184 | // Converts all characters to upper case 185 | string2 = string1.toUpperCase(); 186 | print("$string2"); 187 | } 188 |189 | 190 | 191 | 192 |
substring()은 문자열 객체의 정해진 처음 위치 부터 마지막 위치의 바로 앞까지의 글자들을 꺼내서, 새로운 문자열을 만듭니다. string1에 현재 "Hello Dart!"가 있습니다. 명심해야 하는 사항은, Dart에서 순서를 세는 경우가 있다면, 0부터 센다는 점입니다. 사람은 1부터 세지만, Dart를 포함한 대부분의 프로그래밍 언어들은 0부터 셉니다. 따라서, string2 = string1.substring(0,5);은 0번째 글자인 H로 시작해서, 5번째 글자인 빈칸(space)의 바로 앞인 'o' 글자까지를 포함하는 새로운 문자열 "Hello"를 만든후, 이를 string2에 저장합니다.
193 | 194 | 195 | 196 |Index 연산자인 [ ] 기호는 문자열에 사용했을떄, 특정 위치의 글자를 꺼내는 역할을 합니다. 예제 프로그램에서 string2 = string1[0];은 0번째 문자열을 string1 문자열에서 꺼냅니다. 따라서, 글자 H를 string2에 저장하는 역할을 합니다.
197 | 198 | 199 | 200 |Concatenation은 수학으로 이야기 하면 "+" 연산자와 같다고 합니다. 이 연산자가 문자열에 사용되면, 문자열들을 연결해서 새로운 문자열을 만드는 역할을 합니다. 따라서 string2 = "Hello" + " " + "Dart" + "!";은 4개의 문자열을 하나로 합쳐서, string2에 저장하는 역할을 합니다.
201 | 202 | 203 | 204 |이번에는 정수와 실수에 사용하지 않았던 property 문법을 사용해 봅니다. Property는 데이타를 의미하며 함수는 아닙니다. 따라서 사용시 ()의 입력 파라메타를 기술하는 부분은 없습니다. 문자열이 가진 여러 property 중 length를 사용하는데, 이는 문자열이 가진 글자의 갯수를 나타냅니다. 따라서 num = string1.length;를 수행하면, "Hello Dart!"를 구성하는 글자 갯수(빈칸 포함)인 총11개 글자를 계산한 후, 숫자 11로 치환합니다.
205 | 206 | 207 | 208 |String Interpolation은 이미 우리가 print()을 사용하면서 익숙하게 사용한 기능의 정식 명칭 입니다. print("$string2");를 수행하면, 이 구문 위에서 string2가 있는 경우, string2의 값을 출력하도록 하였습니다. 이렇게 문자열에 ${ ... } 처럼 되어 있으면, { } 안에 해당하는 표현 혹은 변수를 찾아, 이의 결과 혹은 저장값을 해당 위치에 대체하여 줍니다. 변수 이름과 같이 하나의 단어인 경우는 { } 기호가 필요 없습니다. 따라서, 예제에서 string2 = "$string1 has ${string1.length} letters"; 문장은 앞서 string1에 저장한 문자열, 그리고 그 문자열에 속한 글자의 갯수를 문자열 안에 대체하여 저장해 줍니다. 그리고 이 값들이 포함된 새로운 문자열을 string2에 저장합니다.
209 | 210 | 211 | 212 |toLowerCase()와 toUpperCase()는 이름처럼 직관적인 기능을 제공합니다. 즉, 문자열의 모든 글자응 소문자 혹은 대문자로 하는 새로운 문자열을 만들어 줍니다.
213 | 214 | 215 | 216 |darttutorial-14-04.dart 예제 프로그램의 수행 결과를 아래에 명시하였으니, 본인이 생각한 결과와 맞는지 확인해 보고, 실제 프로그램을 수행하여 결과를 확인해 봅니다.
217 | 218 | 219 | 220 |Hello 221 | H 222 | Hello Dart! 223 | 11 224 | Hello Dart! has 11 letters 225 | hello dart! 226 | HELLO DART! 227 |228 | 229 | 230 | 231 |
Boolean 데이타 타입은 true 아니면 false의 매우 단순한 값을 저장하지만, 클래스로 만들어져 있으며, 몇가지 property와 method를 제공합니다. 아래의 darttutorial-14-05.dart 예제는 여러번 등장한 toString()이 Boolean 클래스에도 있으며, 유사한 용법으로 사용되는 것을 확인할 수 있습니다.
236 | 237 | 238 | 239 |// darttutorial-14-05.dart 240 | // Ref: https://api.dart.dev/stable/2.7.1/dart-core/bool-class.html 241 | 242 | void main() { 243 | bool bool1 = true; 244 | var myString; 245 | 246 | // Print boolean object 247 | print("$bool1"); // true 248 | 249 | // Convert boolean object to String 250 | myString = bool1.toString(); 251 | print("$myString"); // true 252 | } 253 |254 | 255 | 256 | 257 |
객체지향 프로그래밍을 하다보면, 현재 다루는 데이타가 어떤 타입인지 확인이 필요한 경우가 있습니다. 이런 경우 사용 가능한 문법인 is와 is! 입니다. 특히 동적으로 변수를 생성하는 var 혹은 dynamic의 경우는, 프고로그램이 수행되면서 저장하는 데이타 타입이 수시로 바뀔수 있습니다. 따라서, 프로그램 수행 시점에서 타입을 알아야 하는 경우에 요긴하게 사용할 수 있는 분법입니다. 아래의 darttutorial-14-06.dart 예제를 통해서 이 문법이 어떻게 동작하는지 확인할 수 있습니다.
262 | 263 | 264 | 265 |// darttutorial-14-06.dart 266 | 267 | void printType(var para) { 268 | if(para is int) { 269 | print("[type] int"); 270 | } else if(para is double) { 271 | print("[type] double"); 272 | } else if(para is String) { 273 | print("[type] string"); 274 | } else if(para is bool) { 275 | print("[type] double"); 276 | } else { 277 | print("[type] not identified"); 278 | } 279 | } 280 | 281 | void main() { 282 | int num1 = 1; 283 | var num2 = 1.1; 284 | dynamic num3; 285 | 286 | printType(num1); 287 | printType(num2); 288 | 289 | num3 = num1; 290 | printType(num3); 291 | 292 | num3 = num2; 293 | printType(num3); 294 | 295 | num3 = "number"; 296 | printType(num3); 297 | } 298 |299 | 300 | 301 | 302 |
printType() 함수를 보면, 입력 파라메타의 타입에 따라서 화면에 타입을 출력해 주는 기능을 확인 가능합니다.
303 | 304 | 305 | 306 |Dart는 객체지향 언어로서, 다루는 모든 데이타가 객체로 실현됩니다. 우리는 이번 글에서 설계도에 해당하는 클래스와 이들이 실제 데이타를 가지면서 실세계에 의미있는 형태로 동작하는 객체에 대해서 배웠습니다. 그리고, 지금까지 배워온 int, double, String, bool, var, Object, dynamic 등이 객체를 저장하는 것들 임을 배웠습니다. 이들은 멤버 데이타와 멤버 메소드를 가지며, 보다 구체적인 내용은 공식 홈페이지의 관련 메뉴얼을 참조하도록 합니다.
311 | 312 | 313 | 314 |Creative Commons License (CC BY-NC-ND)
319 | 320 | -------------------------------------------------------------------------------- /doc/dart-tutorial-15.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - List 2 | 3 | 4 |List는 같은 타입 혹은 서로 다른 타입의 데이타를 복수 개 저장하는데 매우 유용한 데이타 타입니다. List는 대부분의 프로그래밍 언어에서 지원하며, 배열(Array)라고도 불립니다.
5 | 6 | 7 | 8 |예제를 통해서도 알아보겠지만, 일반 변수를 선언하는 방식인 var myList; 문법을 사용하여 만들수도 있고, generic 문법이라고 하는 List<int> myList; 문법을 사용해서도 만들수 있습니다. 전자는 Dart 언어에게 알아서 List 데이타 타입을 정해 달라고 하는 것이고, 후자는 int 데이타 타입을 저장한 List를 만들어 달라는 것입니다. 아래의 darttutorial-15-01.dart예제 프로그램을 통해서 List의 의미와 사용 방법을 자세하게 알아보겠습니다. 다양한 내용이 들어가 있는 이유로 설명을 용이하게 하기 위하여 각각의 기능별로 #1과 같이 번호를 매기어 두었습니다.
13 | 14 | 15 | 16 |17 | // darttutorial-15-01.dart 18 | 19 | // Print the type of object 20 | String getObjectType(var para) { 21 | if(para is List79 | 80 | 81 | 82 |) { 22 | return ("_List _"); 23 | } else if(para is List ) { 24 | return ("_List _"); 25 | } else { 26 | return ("_Unknown_"); 27 | } 28 | } 29 | 30 | void main() { 31 | // #1 Simple list with three integers 32 | var myList1 = [1, 2, 3]; 33 | print("1> myList1(" + getObjectType(myList1) + ") has ${myList1.length} items: $myList1"); 34 | 35 | // #2 List element access using index operator 36 | for(var index = 0; index < myList1.length; index++) { 37 | myList1[index] *= index; 38 | } 39 | print("2> myList1(" + getObjectType(myList1) + ") has ${myList1.length} items: $myList1"); 40 | 41 | // #3 Generic List for heterogeneous data type encapsulation 42 | List myList2 = [1, 2.0, "myName"]; 43 | print("3> myList2(" + getObjectType(myList2) + ") has ${myList2.length} items: $myList2"); 44 | 45 | // #4 Constant value assignment for List - 1 46 | myList1 = const [11, 12, 13, 14, 15]; 47 | print("4> myList1(" + getObjectType(myList1) + ") has ${myList1.length} items: $myList1"); 48 | 49 | // #5 Constant value assignment for List - 2 50 | //myList1[0] = 1; // Error 51 | myList1 = const [101, 102, 103, 104, 105]; 52 | print("5> myList1(" + getObjectType(myList1) + ") has ${myList1.length} items: $myList1"); 53 | 54 | // #6 ... Operator in List creation - 1 55 | myList1 = [1, 2, 3]; 56 | var myList3 = [...myList1, 4, 5, 6]; 57 | print("6> myList3(" + getObjectType(myList3) + ") has ${myList3.length} items: $myList3"); 58 | 59 | // #7 ... Operator in List creation - 2 60 | var myList4; 61 | //myList1 = [...myList4, 4, 5, 6]; // Error 62 | myList1 = [...?myList4, 4, 5, 6]; 63 | print("7> myList1(" + getObjectType(myList1) + ") has ${myList1.length} items: $myList1"); 64 | 65 | // #8 if Statement in List creation 66 | var myList5 = [...?myList1, if(myList2.length != 0) ...?myList2 ]; 67 | print("8> myList5(" + getObjectType(myList5) + ") has ${myList5.length} items: $myList5"); 68 | 69 | // #9 for Statement for List manipulation 70 | for(var item in myList5) { 71 | print("9>> $item"); 72 | } 73 | 74 | // #10 for Statement in List creation 75 | var myList6 = ['#0', for(var item in myList5) "#$item"]; 76 | print("10> myList6(" + getObjectType(myList6) + ") has ${myList6.length} items: $myList6"); 77 | } 78 |
List를 만들고 간단하게 다루는 main()의 설명에 앞서, 간단한 함수가 하나 포함되어 있는 것을 볼 수 있습니다. 이름은 getObjectType() 으로, 입력 파라메타로 클래스 객체를 하나 받아서, 이의 타입에 해당하는 문자열을 return 하는 함수입니다. 앞서 설명한 generic한 형태로, 간단하게 int 데이타를 저장하는 List<int>와 dynamic 데이타를 저장하여, 사실상 서로 다른 형태의 데이타를 하나의 List에 저장할 수 있는 List<dynamic> 타입만 식별하는 간단한 함수입니다. 두가지 타입에 부합하지 않으면, "Unknown"으로 return 합니다. 그리고 프로그램의 수행결과를 아래와 같이 미리 나타내었습니다.
83 | 84 | 85 | 86 |#1은 가장 간단한 형태인 정수를 저장하는 List를 만드는 것을 보여줍니다. List는 [ ... ] 괄호로 만들어 지며, 안에 저장할 값들을 ',' 쉼표 기호를 사용하여 나열하면 됩니다. 저장하는 값들이 int 타입이지만, var 변수 타입을 사용함으로서, Dart 언어에게 알아서 타입을 만들어 달라고 하는 것이며, 이는 앞서에서 설명했던 type inference 기능의 일종입니다. print 구문이 실행되면, Dart 언어가 알아서 int 데이타가 저장된 List인 List<int> 타입으로 myList1을 만든것을 확인 할 수 있습니다. 그리고 클래스의 멤버 property인 length를 통해서 3개의 element가 들어 있음을 확인하였고, print 구문에 List 이름을 적어줌으로써, myList1 안의 Element들을 화면에 출력해 보았습니다.
91 | 92 | 93 | 94 |#2는 for 반복문을 사용해서, List의 element에 접근 하는 것을 보여줍니다. String에서 배운 index operator를 동일하게 사용하고 있으며, 맨 처음 element인 0번째 element 부터 List의 길이만큼 반복하면서, element 들에 간단한 곱셈을 하여 값을 업데이트 하는 것을 볼 수 있습니다.
95 | 96 | 97 | 98 |#3은 서로 다른 데이타 타입을 하나의 List에 저장하기 위하여, dynamic 타입으로 List를 만들어 보겠습니다. myList2를 보면 정수 1, 실수 2.0, 그리고 문자열 "myName"을 함꼐 저장하는 것을 볼 수 있습니다. 그리고 타입을 출력해 보면, List<dynamic>으로 나타나는 것을 확인 할 수 있습니다.
99 | 100 | 101 | 102 |#4는 상수형 List를 myList1에 저장합니다. 상수형 List라는 단어가 낯설수도 있지만, 말그대로 상수입니다. 즉, 등호의 오른쪽의 const [11, 12, 13, 14, 15]는 5개의 element를 가진 List인데, 이 다섯가지의 값은 바뀔수 없다는 의미입니다. #5의 myList1[0] = 1;를 주석 처리 하였는데, 이를 실제로 실행하면 에러가 납니다. 이유는 myList1이 가리키는 값이 상수인데, 이의 0번째 값을 바꾸려 하니, 상수 값 변경 시도로 인하여 에러가 나는 것 입니다.
103 | 104 | 105 | 106 |#5는 상수형 List를 저장하고 있는 myList1의 값을 다른 상수형 List로 변경하는 예제입니다. 헷갈리지 말아야할 사항은 myList1은 변수입니다. 따라서 저장하는 값은 바뀔수 있습니다. 저장하고 있는 값이 상수로서, 이 값 내부를 바꾸지 못할 뿐입니다.
107 | 108 | 109 | 110 |#6에는 새로운 List 문법인 "...List명"을 볼 수 있습니다. 이는 List명안의 모든 element를 가져온다는 의미입니다. 따라서 myList3 = [...myList1, 4, 5, 6];는 1) myList1의 모든 element를 꺼내서 새로운 List에 저장을 합니다. 2) 추가로 명시한 정수 4,5,6을 새로운 List의 뒷부분에 추가합니다. 이렇게 만든 새로운 List를 myList3에 저장합니다.
111 | 112 | 113 | 114 |#7은 #6에서 본 List 문법인 "...List명"에 추가 기능을 포함된 문법입니다. ?의 의미는 해당 List가 비어 있지 않으면, #6과 동일하게 처리하지만, 만약 List에 element가 없는 null 상태이면, 에러 없이 다음 단계로 넘어가서, 정수 4,5,6을 추가하겠다는 의미입니다. #6에서는 myList1이 null인 경우, 에러가 발생하게 됩니다.
115 | 116 | 117 | 118 |#8은 #7의 문법으로 새로운 리스트의 앞을 채운후, 새로운 문법을 통해서 후반부를 채우게 됩니다. 새로운 문법이 if(myList2.length != 0) ...?myList2로 되어 있는데, 특이해 보이지만, List 안을 채울때에도 if 조건문의 사용이 가능합니다. 이 경우는 myList2의 길이가 0이 아니면, myList2의 내용을 후반부에 추가한다는 내용입니다.
119 | 120 | 121 | 122 |#9는 for 구문이 List와 같이 복수의 데이타를 저장하는 데이타 타입과 쓰일때 용이한 for-in 문법을 보여줍니다. 의미는 단순합니다. myList5안에 있는 아이템을 반복문이 수행될때마다 하나씩 꺼내서, item에 저장한다는 의미입니다. 일반적으로 for 구문을 사용하면, 임시 변수를 둬서 처음부터 끝까지 인덱스를 매기면서 수행하지만, for-in 문법을 사용하면, 자동으로 element를 하나씩 꺼내기 때문에 인덱스를 사용하다가 실수로 List에 속하지 않은 데이타에 접근할수 있는 에러를 방지합니다.
123 | 124 | 125 | 126 |#10은 #8처럼 List를 처음 만드는 시점에서 if 구문처럼 for 반복문도 사용이 가능한 것을 보여주는 예제입니다. 수행 결과를 보면 알겠지만, 맨 처음 "#0"의 문자열을 저장한 후, 이후 숫자가 바뀐 문자열을 하나 하나 List의 끝에 추가하게 됩니다.
127 | 128 | 129 | 130 |List에 대해서 간단히 알아보았습니다. 이번에는 List 클래스 내부의 멤버 데이타와 멤버 메소드 등에 대해서 알아보겠습니다. 보다 구체적인 내용은 Dart 공식 홈페이지를 참조하시기 바랍니다 [참조]. 아래의 darttutorial-15-02.dart 예제 프로그램을 통해서 좀 더 List에 대해서 알아보겠습니다.
135 | 136 | 137 | 138 |139 | // darttutorial-15-02.dart 140 | // Reference: https://api.dart.dev/stable/2.7.1/dart-core/List-class.html 141 | 142 | // Print the type of object 143 | String getObjectType(var para) { 144 | if(para is List195 | 196 | 197 | 198 |) { 145 | return ("_List _"); 146 | } else if(para is List ) { 147 | return ("_List _"); 148 | } else { 149 | return ("_Unknown_"); 150 | } 151 | } 152 | 153 | void main() { 154 | // #1 List sample 155 | List myList1 = [1, 2, 3]; 156 | print("#1 " + getObjectType(myList1) + " :: $myList1 :: ${myList1.length}"); 157 | 158 | // #2 List properties 159 | myList1 = [1, 2, 3]; 160 | print("#2 " + "${myList1.first} :: ${myList1[1]} :: ${myList1.last} :: ${myList1.length}"); 161 | 162 | // #3 List manipulation using property 163 | myList1.length = 0; 164 | print("#3 " + getObjectType(myList1) + " :: $myList1 :: ${myList1.length}"); 165 | 166 | // #4 add() method 167 | myList1.add(4); 168 | print("#4 " + getObjectType(myList1) + " :: $myList1 :: ${myList1.length}"); 169 | 170 | // #5 addAll() method 171 | myList1.addAll([5, 6, 7]); 172 | print("#5 " + getObjectType(myList1) + " :: $myList1 :: ${myList1.length}"); 173 | 174 | // #6 clear() method 175 | myList1.clear(); 176 | print("#6 " + getObjectType(myList1) + " :: $myList1 :: ${myList1.length}"); 177 | 178 | // #7 indexOf() method 179 | var myList2 = ["C++", "Dart", "Python"]; 180 | print("#7 " + myList2.indexOf("Dart").toString()); 181 | 182 | // #8 insert() method 183 | myList2.insert(1, "Go"); 184 | print("#8 " + getObjectType(myList2) + " :: $myList2 :: ${myList2.length}"); 185 | 186 | // #9 sort() method (by length) 187 | myList2.sort((a,b) => a.length.compareTo(b.length)); 188 | print("#9 " + getObjectType(myList2) + " :: $myList2 :: ${myList2.length}"); 189 | 190 | // #10 sort() method (by alphabet sequence) 191 | myList2.sort((a,b) => a.compareTo(b)); 192 | print("#10 " + getObjectType(myList2) + " :: $myList2 :: ${myList2.length}"); 193 | } 194 |
먼저 darttutorial-15-02.dart 프로그램의 수행결과는 다음과 같습니다. 소스 프로그램을 눈으로 살펴보면서 결과를 미리 예측하고, 실제 수행 결과와 함께 비교해 보기 바랍니다.
199 | 200 | 201 | 202 |darttutorial-15-02.dart의 #1은 darttutorial-15-12.dart에서 이미 설명한 것 처럼, generic 형태로 int 데이타 타입을 복수개 가지는 myList1을 만든 겁니다.
207 | 208 | 209 | 210 |#2는 List 클래스의 property인 first, last를 통해서 List 안의 첫번째와 마지막 element의 내용을 확인하는 문법입니다.
211 | 212 | 213 | 214 |#3는 property인 length를 0으로 하는 것인데, 이렇게 하면, List가 가지고 있는 모든 데이타를 제거하라는 의미입니다.
215 | 216 | 217 | 218 |#4는 add() 메소드 입니다. 이는 현재 List의 마지막 끝에 입력 파라메타로 주어진 element를 추가합니다.
219 | 220 | 221 | 222 |#5는 addAll() 메소드로서, 주어진 입력 파라메타인 List의 element 들을 꺼내서, 주어진 List의 끝에 하나 하나 추가합니다.
223 | 224 | 225 | 226 |#6는 clear() 메소드로서, List의 모든 element를 삭제합니다. 따라서 앞서의 property인 length를 0으로 하는 것과 같은 내용입니다.
227 | 228 | 229 | 230 |#7에서는 문자열을 가지고 있는 List인 myList2를 만들었습니다. myList2.indexOf("Dart").toString()는 왼쪽부터 오른쪽으로 이해를 하면됩니다. 먼저, myList2에서 "Dart" 문자열이 몇번째에 들어가 있는지 인덱스 값을 확인합니다. 그러면 정수 1이 나옵니다. 그러면 1.toString()과 같은 의미로 다시 이어지는데, 정수 1을 문자열로 만든 것입니다.
231 | 232 | 233 | 234 |#8은 insert()로서 첫번째 입력파라메타는 List에서의 element의 위치입니다. 그리고 이 위치에 두번째 파라메타를 삽입하는 문법입니다.
235 | 236 | 237 | 238 |#9는 순서대로 정렬을 수행하는 sort() 메소드 입니다. List 자체를 주어진 순서에 맞도록 바꾸는 작업을 합니다. sort 메소드는 입력 파라메타로 어떻게 정렬을 할건지에 대한 조건을 비교 함수 하나를 주어야 합니다. 지금은 anonymous 함수로 (a,b) => a.length.compareTo(b.length)가 주어져 있는 것을 볼수 있습니다. 이 함수는 입력 파라메타 두개를 받습니다. 그리고 두 입력 파라메타의 길이를 비교합니다. 결국 지금의 sort는 짧은 문자열에서 긴 문자열로 List 안의 단어들을 정렬해 줍니다.
239 | 240 | 241 | 242 |#10은 다시 한번 정렬을 하는데, 이번에 비교 문법은 (a,b) => a.compareTo(b)로써, 가장 기본적인 정렬입니다. 즉, 크기에 따라서 정렬을 해줍니다. 문자열의 경우는 ㄱㄴㄷ, abc에 따라서 뒤로 갈수록 큰 것으로 간주합니다. 그리고 abc와 abcd처럼 앞이 같고, 뒤에 차이가 있으면, 더 긴 문자열이 큰 값으로 간주됩니다.
243 | 244 | 245 | 246 |List 클래스 안에는 설명한 것보다 더 많고 다양한 기능이 포함되어 있습니다. 반드시 Dart 언어 홈페이지의 List 클래스 설명 내용을 확인해 보기 바랍니다.
247 | 248 | 249 | 250 |이 글에서는 아마도 가장 많이 사용되는 데이타 타입 중 하나인 List에 대해서 알아 보았습니다. 일반 변수를 만들듯이 type inference 기능을 통해서 List를 만들수도 있고, 구체적으로 다룰 데이타 타입을 지정하는 generic 방법도 배웠습니다. 그리고 멤버 property와 method 들에 대해서도 배웠습니다. List는 아주 요긴하게 사용하는 데이타 타입이니, 활용법을 잘 이해해서 사용하기 바랍니다.
255 | 256 | 257 | 258 |Creative Commons License (CC BY-NC-ND)
263 | 264 | -------------------------------------------------------------------------------- /doc/dart-tutorial-16.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - Set 2 | 3 | 4 |Set은 한글로 집합으로 해석 되며, 실제 수학의 집합과 동일한 개념입니다. 따라서 몇가지 특징을 가지는데, 1) 중복된 element를 갖지 않습니다. 즉, 동일한 값이 중복해서 있으면, 하나로 처리합니다. 2) Element의 순서에 의미가 없습니다. List의 경우는 이와 다르게, 중복 Element를 허용하며, 프로그래머가 지정한 순서를 유지하였습니다. 그러면, Set에 대한 사항을 예제 프로그램을 통해서 알아 보도록 하겠습니다.
5 | 6 | 7 | 8 |아래의 darttutorial-16-01.dart는 Set의 선언하고 활용하는 내용으로 만들어져 있습니다. 총6가지 사항에 대해서 설명하고 있으며, 전체 내용에 대한 이해는 Dart 언어 공식 사이트의 내용을 참조하도록 합니다 [참조].
9 | 10 | 11 | 12 |// darttutorial-16-01.dart 13 | // Reference: https://api.dart.dev/stable/2.7.1/dart-core/Set-class.html 14 | 15 | void main() { 16 | // #1 Simple List and Set 17 | var myList1 = ["C++", "Dart", "Go", "Python", "Dart"]; 18 | print("#1.1 $myList1"); 19 | var mySet1 = {"C++", "Dart", "Go", "Python", "Dart"}; 20 | print("#1.2 $mySet1"); 21 | 22 | // #2 Empty Set and element addition 23 | var mySet2 =47 | 48 | 49 | 50 |{}; 24 | mySet2.add("C"); 25 | mySet2.addAll(mySet1); 26 | print("#2 $mySet2 :: " + mySet2.length.toString()); 27 | 28 | // #3 ...? operator usage 29 | var mySet3 = {...?mySet2, "R"}; 30 | print("#3 $mySet3"); 31 | 32 | // #4 contains() method 33 | if(mySet3.contains("Dart") == true) { 34 | print("#4 Dart language is included in mySet3"); 35 | } 36 | 37 | // #5 union() method 38 | var mySet4 = {"JavaScript", "TypeScript"}; 39 | var mySet5 = mySet3.union(mySet4); 40 | print("#5 $mySet5"); 41 | 42 | // #6 dynamic Set for heterogeneous types 43 | Set mySet6 = {"C++", "Dart", "Go", "Python", 1, 2, 3}; 44 | print("#6 $mySet6"); 45 | } 46 |
darttutorial-16-01.dart 프로그램의 수행 결과를 아래와 같이 미리 표기 하였으니, 참조하여 프로그램의 문법과 의미를 이해하도록 합니다.
51 | 52 | 53 | 54 |#1.1 [C++, Dart, Go, Python, Dart] 55 | #1.2 {C++, Dart, Go, Python} 56 | #2 {C, C++, Dart, Go, Python} :: 5 57 | #3 {C, C++, Dart, Go, Python, R} 58 | #4 Dart language is included in mySet3 59 | #5 {C, C++, Dart, Go, Python, R, JavaScript, TypeScript} 60 | #6 {C++, Dart, Go, Python, 1, 2, 3} 61 |62 | 63 | 64 | 65 |
#1은 List와 Set의 차이점을 간단하게 보여주고 있습니다. myList1과 mySet1은 모두 같은 element로 초기화를 하였습니다. 차이점은 List는 [ ... ] 괄호를 사용하지만, Set은 { ... }의 괄호를 사용합니다. Element를 보면 'Dart'가 중복되게 들어가 있습니다. 그러면, 이제 print 구문의 결과를 확인합니다. List는 'Dart' 문자열이 중복되어 2개 들어가 있는 것을 확인 가능합니다. 하지만 Set의 결과를 보면, Dart가 하나만 있는 것을 볼 수 있습니다. 이렇게 Set은 중복된 element를 하나만 유지합니다.
66 | 67 | 68 | 69 |#2는 비어있는 Set을 하나 만드는 예제를 보여줍니다. <String>{}의 의미는 String을 저장할 비어있는 Set이라는 의미입니다. 그리고 List의 설명에서도 있었던 메소드인 add()와 addAll()이 Set에도 있음을 보여주고 있습니다. 기능은 동일합니다. 마찬가지로 Set에도 length property가 있으면, List와 동일하게 Set내의 Element 갯수를 알려줍니다.
70 | 71 | 72 | 73 |#3는 List의 초기화 시에 사용했던 ...? 문법도 Set에서 지원하는 것을 보여줍니다.
74 | 75 | 76 | 77 |#4는 contains() 메소드를 설명하며, 이는 Set안에 입력 파라메타와 같은 문자열이 element로 포함되어 있으면 true를 그렇지 않으면 false를 return 합니다.
78 | 79 | 80 | 81 |#5는 수학의 집합에서 제공하는 합집합 기능을 지원하는 메소드 입니다. mySet3의 element와 입력 파라메타인 mySet4의 element를 모두 모아서, 새로운 Set을 만들어 return 하는 메소드입니다.
82 | 83 | 84 | 85 |#6은 Set의 element가 서로 다른 데이타 타입으로 되도록 만드는 경우입니다. List 경우와 마찬가지로 dynamic 객체 형태로 Set을 만드는 generic 문법입니다.
86 | 87 | 88 | 89 |풀어야 하는 문제가 수학의 집합 개념이 필요하다면, Dart 언어의 Set을 사용하여 편리하게 소프트웨어 개발을 할수 있습니다. 이 글에서는 Set 클래스에서 제공하는 다양한 property와 method 중 일부만 보였으며, 공식 사이트의 설명을 제대로 읽어 보기를 권합니다.
90 | 91 | 92 | 93 |Creative Commons License (CC BY-NC-ND)
98 | 99 | -------------------------------------------------------------------------------- /doc/dart-tutorial-17.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - Map 2 | 3 | 4 |Map은 한글로 번역을 하면 아마도 '지도'가 될 것 같습니다. 이렇게 되면 대부분의 반응은 "뭐지?"가 될 텐데, 다른 언어에서는 유사한 데이타 타입을 Dictionary로 명명하고 있습니다. Dictionary 단어가 사전이라는 의미듯이, Map은 키워드가 있고, 이 키워드에 해당하는 값을 갖는 사전과 같은 기능이 필요한 경우에 사용하는 데이타 타입 입니다. 우리가 익숙한 사전에서 알 수 있듯이, 일단 키워드가 있습니다. 이 키워드는 중복이 되면 안됩니다. 그리고 키워드에 상응하는 값이 있어야 합니다. 값은 중복이 되어도 상관 없습니다. 따라서, Map은 "Key : Value"의 형태를 가집니다. 그러면, Map에 대해서 darttutorial-17-01.dart 예제 프로그램을 통해서 이해하여 보도록 합니다.
5 | 6 | 7 | 8 |9 | // darttutorial-17-01.dart 10 | // Reference: https://api.dart.dev/stable/2.7.1/dart-core/Map-class.html 11 | 12 | void main() { 13 | // #1 Sample Map for programming languages 14 | var dictLanguageYear = { 15 | "C++" : 1983, 16 | "Dart" : 2011, 17 | "Go" : 2009, 18 | "Python" : 1991 19 | }; 20 | 21 | print("#1 $dictLanguageYear"); 22 | 23 | // #2 Sample Map for programming languages 24 | Map49 | 50 | 51 | 52 |dictLanguageAuthor = { 25 | "C++" : "Bjarne Stroustrup", 26 | "Dart" : "Lars Bak - Kasper Lund", 27 | "Go" : "Robert Griesemer", 28 | "Python" : "Guido van Rossum" 29 | }; 30 | 31 | print("#2 $dictLanguageAuthor"); 32 | 33 | // #3 Sample Map for programming languages 34 | var myMap = Map(); 35 | myMap["C++"] = 1983; 36 | myMap["Dart"] = 2011; 37 | myMap["Go"] = 2009; 38 | myMap["Python"] = 1991; 39 | 40 | print("#3 $myMap"); 41 | 42 | // #4 Manipulate an element of a Map 43 | var myElement = myMap["Dart"]; 44 | dictLanguageAuthor["Go"] = "Robert Griesemer - Rob Pike - Ken Thompson"; 45 | 46 | print("#4 $myElement :: ${dictLanguageAuthor["Go"]}"); 47 | } 48 |
이 프로그램의 수행 결과를 미리 아래와 같이 포함 하였으니, 한번 눈으로 코드를 읽고, 결과를 예상하고, 아래의 결과와 비교하여 본 후, 그 다음에 실행해 보기 바랍니다.
53 | 54 | 55 | 56 |#1 {C++: 1983, Dart: 2011, Go: 2009, Python: 1991} 57 | #2 {C++: Bjarne Stroustrup, Dart: Lars Bak - Kasper Lund, Go: Robert Griesemer, Python: Guido van Rossum} 58 | #3 {C++: 1983, Dart: 2011, Go: 2009, Python: 1991} 59 | #4 2011 :: Robert Griesemer - Rob Pike - Ken Thompson 60 |61 | 62 | 63 | 64 |
#1은 가장 기본적으로 초기화 값을 통해서 Map을 만드는 모습을 보여 줍니다. 먼저, Map은 { ... } 기호를 사용하여, 값을 전달 할 수 있습니다. 이는 Set과 동일합니다. 그리고 "Key : Value"의 형태로 값을 나타내기에, "Key : Value"의 값들을 "," 쉼표를 이용해서 복수개 나열한 것을 볼수 있습니다. Key와 Value에 들어 올 수 있는 데이타 타입은 지금까지 설명했던 Dart의 데이타 타입 들로 만들수 있습니다. 그리고 type inference 기능을 이용해서, 변수의 타입을 var로 정의하여 만든 것을 볼 수 있습니다.
65 | 66 | 67 | 68 |#2는 구체적으로 데이타 타입을 Map으로 하여 만들었습니다. 데이타 타입인 Map을 앞에 적은후, "Key : Value"에 상응하는 형태인 <String, String>으로 작성합니다.
69 | 70 | 71 | 72 |#3은 비어있는 Map을 만드는 것을 보여 줍니다. Map()을 변수에 전달함으로써, 비어있는 Map을 만들수 있습니다. 그리고는 인덱스를 의미하는 [ ... ] 기호 사이에 순서를 의미하는 숫자 대신에 키워드 값을 적어주면, 해당 키워드에 상응하는 값을 가져오거나 저장할 수 있습니다. 예제 에서는 비어있는 Map에 해당 키워드에 상응하는 값 들로 저장하는 것을 볼 수 있습니다.
73 | 74 | 75 | 76 |#4는 #3의 연장선 상에서, 키워드를 통해서 해당 키워드에 상응하는 값을 추출하고, 또한 키워드에 해당 하는 값들을 변경하는 것을 보여줍니다.
77 | 78 | 79 | 80 |Dart 언어의 공식 사이트에서 Map 클래스에 대한 자세한 설명을 볼 수 있습니다 [참조]. 앞서 다른 클래스 들에서 나타난 length property를 포함한 여러 property 들이 유사하게 Map 클래스에도 있습니다. 아울러 addAll(), clear() 등 다양한 메소드들도 Map 클래스에서 제공하고 있으니, 공식 사이트의 정보를 살펴보기 바랍니다.
81 | 82 | 83 | 84 |이 글에서는 Map에 대해서 알아 보았습니다. 본인의 프로그램에서 "Key : Value"의 pair로 다루는 프로그램을 작성하는 경우에는 Map 클래스를 사용합니다.
85 | 86 | 87 | 88 |Creative Commons License (CC BY-NC-ND)
93 | 94 | -------------------------------------------------------------------------------- /doc/dart-tutorial-18.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - Useful Dart Features 2 | 3 | 4 |이 글에서는 지금까지 설명했던 주요 Dart 언어의 특징 들에 추가해서, 알아두면 요긴하게 사용할 수 있거나, 아니면 타인이 만든 프로그램에서 사용된 내용을 이해하기 위한 용도로 몇가지 문법들을 설명하도록 합니다.
5 | 6 | 7 | 8 |if 구문을 사용하지 않고, 매우 간단하지만 조건적인 동작을 수행하는 문법으로 형태는 다음과 같이 합니다. condition이 true 이면 expr1이 수행되고, false 이면 expr2가 실행 됩니다.
13 | 14 | 15 | 16 |condition ? expr1 : expr 2 17 |18 | 19 | 20 | 21 |
Bitwise 연산자는 데이타를 비트(bit) 단위로 다루는 연산자를 의미합니다. 비트는 0과 1만 나타낼 수 있는 컴퓨터 프로그램에서 다룰수 있는 최소 단위입니다. 8개의 비트가 모이면 우리는 바이트(byte)라고 부릅니다. 컴퓨터 메모리를 대문자 B로 부르는 이유는 Byte 단위로 컴퓨터 메모리를 카운팅 하기 때문입니다. 예를 들어 1바이트로 정수 3을 부호 없는 1바이트 정수로 나타내면 00000110으로 나타납니다.
26 | 27 | 28 | 29 |Bitwise right-shift 연산자는 "N >> M"으로 표현나며, N의 값을 비트 단위로 바뀐후, M 비트 만큼 오른쪽으로 이동시킨다는 의미입니다. 즉 "3 >> 1"이라고 하면, 10진법의 3에 해당하는 정수를 8비트 형태인 00000011으로 표현한 후, 오른쪽으로 일괄 한자리씩 이동 합니다. 그렇게 되면 00000001이 됩니다. 이를 다시 10전법의 정수로 바꾸면 1이 됩니다.
30 | 31 | 32 | 33 |Bitwise left-shift 연산자는 "N << M"으로 표현하며, bitwise right-shift 연산자와 모든 부분은 같으며, 오른쪽이 아닌 왼쪽으로 이동하는 점만 다릅니다. 즉 "3 << 1"이라고 하면, 10진법의 3에 해당하는 정수를 8비트 형태인 00000011으로 표현한 후, 왼쪽으로 일괄 한자리씩 이동 합니다. 그렇게 되면 00000110이 됩니다. 이를 다시 10전법의 정수로 바꾸면 6이 됩니다.
34 | 35 | 36 | 37 |Bitwise OR 연산자는 "N | M"으로 표현합니다. OR 연산자의 의미는 앞서 배웠던 기본 연산자와 동일합니다. 둘 중 하나면 true여도 결과가 true가 되는 의미입니다. 단 bitwise 연산자가 되면, 앞서의 bitwise shift 연산자 처럼, 0과 1의 이진법으로 펼친후, 각각의 자리에 있는 비트 값을 확인해서, 같은 자리의 비트 값 중 1이 하나라도 있으면, 결과로 1로 처리합니다. 따라서, "3 | 4"로 하면, "00000011 | 00000100"이 되고, 결과를 "00000111"이 됩니다.
38 | 39 | 40 | 41 |Bitwise AND 연산자는 "N & M"으로 표현합니다. AND 연산자의 의미는 앞서 배웠던 기본 연산자와 동일합니다. 둘 모두 true여야 결과가 true가 되는 의미입니다. 단 bitwise 연산자가 되면, 앞서의 bitwise shift 연산자 처럼, 0과 1의 이진법으로 펼친후, 각각의 자리에 있는 비트 값을 확인해서, 같은 자리의 비트 값에 모두 1이 있으면, 결과로 1로 처리합니다. 따라서, "3 & 4"로 하면, "00000011 & 00000100"이 되고, 결과를 "00000000"이 됩니다.
42 | 43 | 44 | 45 |Dart 언어에는 몇가지 상수가 존재합니다. NaN은 Not-a-Number의 약자로써 숫자가 아니다 라는 의미입니다. 예를 들어, 0을 0으로 나눈다는 것과 같이 의미가 없는 경우를 나타냅니다. 따라서, 이런 경우를 대비해서 int와 double 데이타 타입은 객체가 가지고 있는 값이 유의미한지 아닌지를 검사하는 isNan이라는 property를 지원하며, 유의미한 값이 false를 의미가 있는 값이면 false를 return 합니다. 유사하게 1을 0으로 나누는 경우는 수학에서 분모가 0에 가까와 지는 것을 의미해서, 무한대를 나타내는 Infinity를 출력합니다.
50 | 51 | 52 | 53 |Dart는 소스코드 및 각종 문자들을 UTF(Unicode Transformation Format)-16 코딩 방식으로 표현 합니다. 코딩이란 사람이 볼 수 있는 기호들을 컴퓨터가 처리하기 용이한 숫자들로 매핑한 것 입니다. 유명한 코딩은 ASCII(American Standard Code for Information Interchange)가 있으며, 미국에서 영어와 숫자, 그리고 기호를 256개의 코드에 넣은 것 입니다. 영어권 국가가 아닌 한국과 같은 경우는 추가적인 비트수로 이루어진 코드가 필요했고, 그 중 하나가 Unicode 입니다. Dart의 UTF-16과 Unicode 간의 차이로, Dart에서는 Unicode를 표현하기 위해서는 문자열 앞에 \u 기호를 추가해야 합니다. 통상 16비트의 Unicode를 표현하기 위해서는 \u2665 (하트 기호 입니다) 처럼 합니다. 이보다 큰 비트 수의 Unicode를 표현하기 위해서는 \u{1f600} 처럼 { ... } (스마일 기호 입니다) 기호로 코드 값을 감싸 줍니다. Unicode와 같은 서로 다른 타입의 문자열 처리는 이후 library에서 설명할 characters 패키지를 사용하면 좀 더 복잡하고 유용한 기술을 사용할 수 있습니다.
58 | 59 | 60 | 61 |클래스의 메소드를 수행할 때, 앞의 결과를 뒤에서 이어 받아 실행하는 경우가 있습니다. 예를들어 A.method1().method2()와 같이 할 수 있습니다. 이는 A 객체의 멤버 메소드인 method1을 실행하고, 이의 결과가 다시 새로운 객체가 되어, 이 새로운 객체의 멤버 메소드인 method2를 실행 하라는 의미 입니다. 예를 들어 method1의 결과가 정수라면, 정수 객체의 method2를 실행 한다는 의미 입니다.
66 | 67 | 68 | 69 |이와 다르게, A의 method1을 실행하고, 이어서 다시 A의 method2를 실행하고 싶을때가 있습니다. 이런 경우를 위한 Dart 문법이 있으니, ".." 입니다. 예를들어 List<String> 타입의 비어있는 myList2가 있다고 가정합니다. 첫번째 하고 싶은 동작은 add("Go")로 element를 하나 추가하고, 다음으로 addAll(["C++", "Dart", "Python"])를 통해서 3개의 element가 들어간 List를 입력 파라메타로 주어서, 이안의 3개 element를 다시 List에 추가합니다. 마지막으로 이 List를 Sort하고 싶습니다. 각각을 별도의 문장으로 작성할 수도 있지만, Dart는 위의 작업을 ".." 연산자를 사용하여 다음처럼 프로그래밍 하면 됩니다.
70 | 71 | 72 | 73 |myList2 74 | ..add("Go") 75 | ..addAll(["C++", "Dart", "Python"]) 76 | ..sort((a,b) => a.compareTo(b)); 77 |78 | 79 | 80 | 81 |
사실 cascade 연산자라고 말은 했지만, ".." 문법은 Dart 고유의 문법 중 하나입니다.
82 | 83 | 84 | 85 |forEach 함수는 List, Set 등과 같이 복수의 데이타를 저장하는 데이타 타입에 활용 가능한 멤버 메소드 입니다. 따라서, 적용 하고자 데이타 타입의 멤버 메소드로 제공이 되는지 확인을 해야 합니다. 지원하는 기능은, 이름이 의미 하듯이, 반복적으로 데이타 타입 안의 첫번째 부터 마지막 element에 입력 파라메타로 전달받는 함수를 적용하는 것 입니다.
90 | 91 | 92 | 93 |예들 들어 위의 myList2는 최종적으로 ["C++", "Dart", "Go", "Python"] 값을 저장하는 리스트 입니다. 여기에 다음과 같은 함수를 적용해 보겠습니다. 아래의 printItem 함수는 간단합니다. 입력 파라메타로 문자열 하나를 받아서 화면에 출력합니다.
94 | 95 | 96 | 97 |void printItem(String item) { 98 | print(item); 99 | } 100 |101 | 102 | 103 | 104 |
이제 myList2에 printItem 함수를 적용하면, myList2.forEach(printItem); 처럼 하면 됩니다. 이러면, myList2의 각 element를 printItem 함수의 입력 파라메타로 전달하는 작업을 첫번째 element 부터 마지막 element 까지 수행합니다.
105 | 106 | 107 | 108 |forEach 함수는 Function 설명에서 언급한 anonymous function을 적용하는 대표적인 사례 중 하나입니다. 따라서 printItem 함수와 forEach 함수를 anonymous function 형태로 구현하면 다음과 같이 됩니다.
109 | 110 | 111 | 112 |myList2.forEach((item) => print(item)); 113 |114 | 115 | 116 | 117 |
Dart 언어는 특이하게 함수 안에서 함수를 만들수 있는 언어입니다. 많이 사용하지는 않겠지만, 그럴수가 있다는 점을 기억해 두도록 하겠습니다.
122 | 123 | 124 | 125 |아래와 같이 아무런 return 값이 없는 함수가 있다면, Dart 언어는 자동으로 "return null;" 구문을 함수의 끝 부분에 추가합니다. 따라서, return 값이 없는 함수는 null을 return 한다고 보아도 됩니다.
130 | 131 | 132 | 133 |doNothing() {} 134 |135 | 136 | 137 | 138 |
Dart 언어의 연산자 들은 수학의 연산자 들과 유사하게 우선순위가 있습니다. 먼저 수학에서의 +, - 연산 순위가 같으면, 오른쪽에서 왼쪽으로 하는 것은 수학과 같습니다. +, - 연산자가 *, / 연산자 보다 낮은 것도 동일합니다. 수학과 다르게 다양한 연산자 들이 많이 나타나는 만큼 공식 사이트의 연산자 우선순위 정보를 한번 살펴 보는 것이 좋겠습니다 [참조].
143 | 144 | 145 | 146 |a = a + 1; 처럼 프로그램을 개발하다 보면 특정 변수의 값을 업데이트 하는 경우가 많습니다. 이때 a += 1;로 하면 동일한 효과를 볼 수 있습니다. 이렇게 간단하게 만드는 연산자는 -=, /=, %=, >>=, ^=, *=, ~/=, <<=, &=, |=가 있습니다. 또한 a ??= 1 처럼 하면, ??= 연산자는 a가 null 인 경우에 대해서만 1을 a에 저장하며, 그렇지 않으면 아무런 작업을 하지 않습니다.
151 | 152 | 153 | 154 |만약 true 값을 저장한 변수 a가 있을때, !a로 하면 false가 됩니다. 즉 ! 연산자는 not의 의미를 가집니다. 변수 차원이 아닌 비트 차원에 대해서 이런 작업을 원한다면, ~ 연산자가 있습니다. 데이타를 비트로 펼친후 0을 1로, 1을 0으로 바꾸는 bitwise 연산자 입니다.
159 | 160 | 161 | 162 |앞서 설명한 사항들을 하나 하나 간단한 예제로 풀어낸 프로그램이 아래의 darttutorial-18-01.dart 입니다. 위의 내용을 이해하고, 먼저 결과를 예측해 보기 바랍니다.
167 | 168 | 169 | 170 |171 | // darttutorial-18-01.dart 172 | 173 | // #6 forEach function 174 | void printItem(String item) { 175 | print("#6 I \u2665 " + item + "!"); 176 | } 177 | 178 | void main() { 179 | // #1 Conditional Expression 180 | var myCondition1 = true; 181 | var myCondition2 = myCondition1 ? false : true; 182 | print("#1 $myCondition1 $myCondition2"); 183 | 184 | // #2 Bitwise Operators 185 | var myNum1 = 3 << 1; // 00000011 << 1 == 00000110 186 | var myNum2 = 3 >> 1; // 00000011 >> 1 == 00000001 187 | var myNum3 = 3 | 4; // 00000011 | 00000100 == 00000111 188 | var myNum4 = 3 & 4; // 00000011 & 00000100 == 00000000 189 | print("#2 $myNum1 $myNum2 $myNum3 $myNum4"); 190 | 191 | // #3 NaN Constant 192 | var myNaN = 0 / 0; 193 | var myInfinity = 1 / 0; 194 | print("#3 $myNaN $myInfinity"); 195 | 196 | // #4 Unicode presentation 197 | var myUnicode1 = '\u2665'; // 4-digit hexadecimal 198 | var myUnicode2 = '\u{1f600}'; // more or less than 4-digit 199 | print("#4 $myUnicode1 $myUnicode2"); 200 | 201 | // #5 Cascade (..) operator 202 | List227 | 228 | 229 | 230 |myList2 = []; 203 | myList2 204 | ..add("Go") 205 | ..addAll(["C++", "Dart", "Python"]) 206 | ..sort((a,b) => a.compareTo(b)); 207 | print("#5 $myList2"); 208 | 209 | // #6 forEach function - 1 210 | myList2.forEach(printItem); 211 | 212 | // #7 forEach function - 2 213 | myList2.forEach((item) => print("#7 I \u2665 " + item + "!")); 214 | 215 | // #8 forEach function 216 | void printItemNested(String item) { 217 | print("#8 I \u2665 " + item + "!"); 218 | } 219 | myList2.forEach(printItemNested); 220 | 221 | // #9 Not operators 222 | var myBool1 = true; 223 | var myBool2 = 0; 224 | print("#9 ${!myBool1} ${~myBool2}"); 225 | } 226 |
위의 darttutorial-18-01.dart 프로그램의 수행 결과를 다음과 같이 표시하였습니다. 본인이 생각한 결과와 동일한지 이해해 보기 바랍니다.
231 | 232 | 233 | 234 |#1 true false 235 | #2 6 1 7 0 236 | #3 NaN Infinity 237 | #4 ♥ 😀 238 | #5 [C++, Dart, Go, Python] 239 | #6 I ♥ C++! 240 | #6 I ♥ Dart! 241 | #6 I ♥ Go! 242 | #6 I ♥ Python! 243 | #7 I ♥ C++! 244 | #7 I ♥ Dart! 245 | #7 I ♥ Go! 246 | #7 I ♥ Python! 247 | #8 I ♥ C++! 248 | #8 I ♥ Dart! 249 | #8 I ♥ Go! 250 | #8 I ♥ Python! 251 | #9 false -1 252 |253 | 254 | 255 | 256 |
Dart 언어의 기본 문법을 알아오면서, 유용하지만 상대적으로 비중이 적었던 내용들을 모아서, 이번 글에서 설명을 하였습니다. 개발하는 프로그램에 따라서 사용 안할수도 있는 문법들 이지만, 알아두면 유용한 문법들이니 빠트리지 않고 이해 바랍니다.
261 | 262 | 263 | 264 |Creative Commons License (CC BY-NC-ND)
269 | 270 | -------------------------------------------------------------------------------- /doc/dart-tutorial-19.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - Exception Handling 2 | 3 | 4 |Exception은 프로그램의 동작 중에 예상하지 못한 상황이 발생하여, 더 이상 정상적인 동작이 불가능한 에러 상황을 의미합니다. 이런 Exception이 제대로 처리되면, 에러가 발생은 했지만, 프로그램을 동작을 이어갈 수 있습니다. 반대로 제대로 처리가 되지 않으면, 프로그램은 정상적인 작업을 더이상 수행할 수 없고 종료하게 됩니다.
5 | 6 | 7 | 8 |Dart 언어는 에러를 다루는 큰 범주인 Exception 내에 세부적으로 두가지 유형을 정의하고 있습니다. 첫째는 Exception이며, 둘째는 Error 입니다. 이들은 모두 고유명사로서, Dart 언어에서 에러를 의미하는 클래스들이 있는데, 각각의 클래스의 이름으로 사용됩니다.
13 | 14 | 15 | 16 |Exception 클래스는 프로그램의 오류에 대한 정보를 개발자에게 전달하여, 프로그래밍을 통한 오류 해결을 도와주는 기능입니다. Dart 언어는 자주 발생하는 Exception 들에 대해서 언어안에 내장해 두었는데, 이들은 Dart 언어의 Exception Class 설명 부분에서 확인 가능합니다. 대표적인 Exception이 아래에서 설명할 IntegerDivisionByZeroException 로써, 분모가 0인 나누기가 발생하는 경우를 의미합니다.
17 | 18 | 19 | 20 |Error 클래스는 Program이 Fail된 상태를 의미하여, 프로그래머가 피해야 하는 오류 상황을 의미합니다. 이 경우도 Dart 언어에서 미리 자주 발생하는 Error 들에 대해서 언어 안에 내장해 두었는데, 이들은 Dart 언어의 Error Class 설명 부분에서 확인 가능합니다. 대표적인 Error는 아래에서 설명할 인덱스 값을 제대로 주지 않아서 List 등이 오동작 하는 경우와, 함수의 입력 파라메타 갯수 혹은 타입이 틀린 경우 등 입니다.
21 | 22 | 23 | 24 |Exception과 Error를 구분하기는 애매할 수 있습니다. 문제가 발생 가능한 함수에 대해서, 만약 예측 가능하거나 catch 가능한 문제에 해당 한다면, Error가 아닌 Exception으로 볼 수 있으며, 이런 경우는 프로그램을 종료하는 것이 최선의 방법입니다 (An Exception "indicates conditions that a reasonable application might want to catch." [출처]). Error의 경우는 함수에 대한 설명에서 "must"와 "must not"과 같은 설명을 통해서, 에러가 나는 상황과 조건을 미리 명확하게 설명해서, 함수를 호출 하고자 하는 개발자가 Error를 만들지 않도록 해야 합니다(An Error "indicates serious problems that a reasonable application should not try to catch." [출처]). 따라서, 함수 안에서 error를 만든 다는 것은, 명확하게 제시한 조건을 어긴 예측 가능한 상황이며, 이전에 개발자는 이런 상황이 발생하지 않도록 숙지하고, 피해야 합니다. 어쩌면 이러한 이유로 (충분히 규칙을 지켜서 발생하면 안되는) Error Handling 보다는, Exception Handling이 많이 쓰이는 표현일지 모르겠습니다.
25 | 26 | 27 | 28 |실제 간단한 프로그램을 만들어서 Exception과 Error를 유발하여 보겠습니다. 아래의 darttutorial-19-01.dart 프로그램은 간단한 두 개의 함수를 가지고 있습니다. funcDivide 함수는 두번째 입력 파라메타로 첫번째 입력 파라메타를 정수 나누기 하여 몫을 return 하도록 만들어 졌습니다. 어떤 경우에 이 문법이 제대로 동작하지 못할지 예상이 됩니까? 두번때 함수은 funcGetIndexedValue 함수는 첫번째 입력 파라메타로 받은 List에서, 두번째 입력 파라메타를 인덱스로 사용하여 element 하나를 추출하여 return 합니다. 두번째의 경우도 어떤 상황에서 에러가 나는지 예측이 되는지요? main 함수를 보면 4가지 실행 구문이 있으며, 첫째와 셋째는 정상동작을 하고 둘째와 넷째는 에러를 발생하도록 만들었으니 천천히 에러가 왜 발생하는지와 이를 어떻게 막을 지를 생각해 봅니다.
33 | 34 | 35 | 36 |// darttutorial-19-01.dart 37 | // Corrected version of darttutorial-19-01.dart 38 | 39 | funcDivide(var para1, var para2) { 40 | return para1 ~/ para2; 41 | } 42 | 43 | funcGetIndexedValue(var paraList, var paraIndex) { 44 | return(paraList[paraIndex]); 45 | } 46 | 47 | funcPrintValue(var para1, var para2) { 48 | print("#$para1 $para2"); 49 | } 50 | 51 | void main() { 52 | var myResult; 53 | var myList = [1 ,2, 3]; 54 | 55 | myResult = funcDivide(1, 1); 56 | funcPrintValue(1, myResult); 57 | 58 | myResult = funcDivide(1, 0); 59 | funcPrintValue(2, myResult); 60 | 61 | myResult = funcGetIndexedValue(myList, 0); 62 | funcPrintValue(3, myResult); 63 | 64 | myResult = funcGetIndexedValue(myList, 4); 65 | funcPrintValue(4, myResult); 66 | } 67 |68 | 69 | 70 | 71 |
이 프로그램을 실행하면, myResult = funcDivide(1, 0); 부분에서 에러가 나고 아래와 같은 화면을 보여줍니다.
72 | 73 | 74 | 75 |무엇이 문제 일까요? 컴퓨터 프로그램은 분모가 0으로 되어 있으면, 매우 전설적인 오류인 "Divide by Zero" Exception이 발생합니다. 따라서 1을 0으로 나누려는 시도는 Exception의 발생을 야기하고 프로그램은 정상적인 동작을 하지 못합니다.
80 | 81 | 82 | 83 |이런 에러 상황을 만나는 대부분의 개발자는 일단 에러가 난 코드를 넘겨보려는 시도를 하는 경우가 종종 있습니다. 그러면, 에러 상황이 발생하는 funcPrintValue() 함수 호출 부분을 다 막아 보겠습니다. 해당 부분은 주석처리에서 봤던 다중 라인에 대한 주석 처리로 일단 막습니다. 그러면 이번에는 아래와 같은 새로운 에러 화면을 만나게 됩니다.
84 | 85 | 86 | 87 |화면의 왼쪽을 보면, Exception 상황인데 아래로 내려가 보면, __errorName이 "RangeError (Index)"로 되어 있고, 이유를 설명한 __errorExaplanation에 "Not in range 0..2, inclusive: 4"의 긴 설명이 되어 있습니다. 이름 부터 보면 range 값, 즉 List에서 적절하지 않은 Index를 주었다는 의미입니다. 그리고 설명을 보면, 0~2까지의 Index가 유의미 한 경우인데, 4를 주었으니 에러가 났다는 것을 구체적으로 알려줍니다.
92 | 93 | 94 | 95 |Dart 언어에서는 에러가 발생할 것 같은 구문을 실행하다가, 에러가 발생하면, 해당 에러를 대응하는 코드를 제공해서, 에러로 인한 프로그램의 중간이 없도록 하는 문법을 제공합니다. 에러가 발생할 지 모르는 코드에 대해서 실행을 시도하는 구문을 try { ... } 문법이라고 하며, 에러가 발생한 경우에 대처하도록 하는 구문을 catch { ... } 혹은 on Exception/Error { ... } 구문이라고 합니다. 이 문법을 사용해서, darttutorial-19-01.dart 프로그램을 개선해 보겠습니다. 에러가 발생하지 않으면 정상적인 동작을 하고, 에러가 발생하는 순간에는 에러를 무시하도록 하여, 이후에서 프로그램의 진행이 정상적으로 진행될 수 있는 부분들은 정상적으로 처리하고, 정상적인 상태에서 프로그램이 마쳐지도록 하는 것이 목표입니다.
100 | 101 | 102 | 103 |// darttutorial-19-02.dart 104 | // Corrected version of darttutorial-19-01.dart 105 | 106 | funcDivide(var para1, var para2) { 107 | try { 108 | return para1 ~/ para2; 109 | } on IntegerDivisionByZeroException { 110 | print('IntegerDivisionByZeroException'); 111 | return null; 112 | } catch (e) { 113 | print('Exception: $e'); 114 | return null; 115 | } 116 | } 117 | 118 | funcGetIndexedValue(var paraList, var paraIndex) { 119 | try { 120 | return(paraList[paraIndex]); 121 | } catch (e) { 122 | print('Exception: $e'); 123 | return null; 124 | } 125 | } 126 | 127 | funcPrintValue(var para1, var para2) { 128 | if(para2 != null) { 129 | print("#$para1 $para2"); 130 | } else { 131 | print("#$para1 null"); 132 | } 133 | } 134 | 135 | void main() { 136 | var myResult; 137 | var myList = [1 ,2, 3]; 138 | 139 | myResult = funcDivide(1, 1); 140 | funcPrintValue(1, myResult); 141 | 142 | myResult = funcDivide(1, 0); 143 | funcPrintValue(2, myResult); 144 | 145 | myResult = funcGetIndexedValue(myList, 0); 146 | funcPrintValue(3, myResult); 147 | 148 | myResult = funcGetIndexedValue(myList, 4); 149 | funcPrintValue(4, myResult); 150 | } 151 |152 | 153 | 154 | 155 |
main() 함수는 수정없이 동일합니다. 분모가 0인 경우에 에러가 나던 funcDivide() 함수가 매우 복잡해 진 것을 볼수 있습니다. 먼저 try { ... } 안에 원래 해야할 나누기와 return 구문이 포함되어 있는 것을 볼 수 있습니다. 영어 표현 그대로 "일단 시도하자"로 해석하면 되겠습니다. 따라서 이 구문을 수행하다가 분모가 0이면, 결국 에러가 날 겁니다. 이 에러를 처리하는 구문이 이어지는 on ... { ... } 구문과 catch( ... ) { ... } 입니다. 현재의 코드에서는 on IntegerDivisionByZeroException { ... }이 먼저 나타납니다. 이는 try 구문 안의 작업을 수행하다가 에러가 발생한 경우, 이어지는 on/catch 구문에 대해서 에러가 부합하는지를 확인 합니다. 첫번째 구문이 on IntegerDivisionByZeroException 인데, 이는 영어를 잘 읽어 보면, "0으로 정수 나누기를 하는 바람에 생긴 Exception"이라는 의미입니다. 현재 우리가 겪는 에러 상황이 바로 이 상황입니다. 따라서, main()에서 분모를 0으로 주는 경우는 이 경우에 해당합니다. 따라서, 프로그램의 흐름을 멈추지 않도록 추가적인 작업을 하고 있습니다. 현재의 경우는 일단 화면에 에러의 이유를 출력합니다. 그리고 null 값을 return 하여 프로그램이 이어져서 수행하도록 합니다. 만약 이 경우에 해당하지 않는 다른 에어라면, 다음의 구문이 이어져서 확인 되는데, 어떻게 보면 if { ... } else if { ... } else if { ... } else { ... }의 용법과 매우 흡사합니다. 현재의 경우는, 구체적으로 에러의 원인을 기술한 IntegerDivisionByZeroException가 아니면, 무조건 모든 에러를 catch(e)에서 처리하도록 한것 입니다. else { ... } 구문에 부합한다고 보면 되겠지요. 따라서, 이 경우는 화면에 Exception의 이름을 출력하곤, null 값을 return하여, 프로그램이 이어지도록 합니다. 만약, 우리가 on IntegerDivisionByZeroException { ... }을 지운다면 어떨 까요? 그렇다면, catch(e) 구문에 걸려서 처리가 되는 것을 볼 수 있습니다. 한번 on IntegerDivisionByZeroException { ... }을 지원서 직접 확인해 보기 바랍니다.
156 | 157 | 158 | 159 |funcGetIndexedValue() 함수도 마찬가지로 try와 catch 구문으로 업그레이드 되어 있습니다. try 구문안에 있는 내용을 수행하다가 에러가 나면 이어지는 catch 구문을 통해서 수행을 이어가고 있습니다.
160 | 161 | 162 | 163 |두개의 함수 안을 try와 catch 구문으로 에러가 나더라도 프로그램의 수행이 이어지도록 했습니다. 따라서, 에러가 났어야 하는 부분에서 null 값으로 처리가 된 것을 감안하여, funcPrintValue 함수안에서 조건문으로 null 여부를 확인 후, 에러가 나지 않은 부분은 결과를 제대로 출력하고, 에러가 났어야 하는 부분은 null 값을 출력한 이후, 프로그램을 정상적으로 종료하게 된다.
164 | 165 | 166 | 167 |업그레이드된 프로그램의 수행결과는 다음과 같습니다.
168 | 169 | 170 | 171 |#1 1 172 | Exception: IntegerDivisionByZeroException 173 | #2 null 174 | #3 1 175 | Exception: RangeError (index): Invalid value: Not in range 0..2, inclusive: 4 176 | #4 null 177 |178 | 179 | 180 | 181 |
darttutorial-19-02.dart 프로그램을 수정해서, try와 catch 구문을 main() 쪽으로 옮길 수 있습니다. 즉, 에러가 발생 가능한 함수를 호출하는 쪽에서 에러에 대한 대처를 하는 것도 가능합니다. 이런 방식으로 만든 프로그램이 darttutorial-19-03.dart 입니다.
182 | 183 | 184 | 185 |// darttutorial-19-03.dart 186 | // Corrected version of darttutorial-19-01.dart 187 | 188 | funcDivide(var para1, var para2) { 189 | return para1 ~/ para2; 190 | } 191 | 192 | funcGetIndexedValue(var paraList, var paraIndex) { 193 | return(paraList[paraIndex]); 194 | } 195 | 196 | funcPrintValue(var para1, var para2) { 197 | print("#$para1 $para2"); 198 | } 199 | 200 | void main() { 201 | var myResult; 202 | var myList = [1 ,2, 3]; 203 | 204 | try { 205 | myResult = funcDivide(1, 0); 206 | } on IntegerDivisionByZeroException { 207 | print('IntegerDivisionByZeroException'); 208 | myResult = null; 209 | } catch (e) { 210 | print('Exception: $e'); 211 | myResult = null; 212 | } 213 | 214 | funcPrintValue(2, myResult); 215 | 216 | try { 217 | myResult = funcGetIndexedValue(myList, 4); 218 | } catch (e) { 219 | print('Exception: $e'); 220 | myResult = null; 221 | } 222 | 223 | funcPrintValue(4, myResult); 224 | } 225 |226 | 227 | 228 | 229 |
우리가 catch 했던 exception/error 들은 모두 Dart 언어에서 제공하는 것들로, 프로그래머가 자주 만들어서 언어적인 차원에서 제공하는 exception/error 들이라고 보면 됩니다. Dart는 프로그램이 동작하다가 이렇게 언어 자체가 검출 가능한 에러들이 발생하면, catch 구문에서 에러를 잡을 수 있도록 일종의 신호를 만들어 줍니다. 이런 동작을 throw라고 하는데, 프로그래머가 인위적으로 exception/error를 유발할 수 있습니다. darttutorial-19-03.dart에 throw 문법을 적용하기 위하여 일부 수정을 하였습니다. 이는 throw와 이어질 rethrow 구문을 설명하기 위한 예제 일뿐 구조가 적절하게 만들어진 프로그램은 아닙니다.
234 | 235 | 236 | 237 |// darttutorial-19-04.dart 238 | // Corrected version of darttutorial-19-01.dart 239 | 240 | funcDivide(var para1, var para2) { 241 | try { 242 | if(para2 == null) { 243 | throw IntegerDivisionByZeroException; 244 | } else { 245 | return para1 ~/ para2; 246 | } 247 | } catch (e) { 248 | print('>> funcDivide:IntegerDivisionByZeroException'); 249 | rethrow; 250 | } 251 | } 252 | 253 | funcGetIndexedValue(var paraList, var paraIndex) { 254 | try { 255 | return(paraList[paraIndex]); 256 | } catch (e) { 257 | print('>> funcGetIndexedValue: $e'); 258 | rethrow; 259 | } 260 | } 261 | 262 | funcPrintValue(var para1, var para2) { 263 | print("#$para1 $para2"); 264 | } 265 | 266 | void main() { 267 | var myResult; 268 | var myList = [1 ,2, 3]; 269 | 270 | try { 271 | myResult = funcDivide(1, 0); 272 | } on IntegerDivisionByZeroException { 273 | print('>> main:IntegerDivisionByZeroException'); 274 | myResult = null; 275 | } catch (e) { 276 | print('Exception: $e'); 277 | myResult = null; 278 | } 279 | 280 | funcPrintValue(2, myResult); 281 | 282 | try { 283 | myResult = funcGetIndexedValue(myList, 4); 284 | } catch (e) { 285 | print('>> main: $e'); 286 | myResult = null; 287 | } 288 | 289 | funcPrintValue(4, myResult); 290 | } 291 |292 | 293 | 294 | 295 |
funcDivide() 함수의 try 구문 안을 보면, 조건문으로 분모가 0인지를 확인하도록 추가 코드를 넣었습니다. 그리곤 분모가 0인 경우 앞서에서 볼수 있었던 IntegerDivisionByZeroException를 throw 문법으로 인위적으로 생성하는 구문을 볼 수 있습니다. 이렇게 만들어진 프로그래머가 발생시킨 IntegerDivisionByZeroException exception은 이어지는 catch 구문에서 정상적으로 잡을 수 있으며, 이를 통해서 화면에 정해진 문장이 출렫되는 것을 볼 수 있습니다. 여기에 추가적인 구문인 rethrow가 있는데, 이는 re-throw 즉, throw를 또 한다는 의미입니다. 이렇게 함으로써, 해당 excpetion을 funcDivide() 함수를 호출했던 쪽으로 다시 재전달 합니다. 이렇게 함으로써, 함수안에서 에러에 대해서 처리할 일을 수행하고, 필요하다면 함수를 호출한 쪽에서도 추가적인 작업을 할 수 있도록 합니다.
296 | 297 | 298 | 299 |try/catch/throw/rethrow 구문을 어떻게 사용하는 것이 최적인지에 대한 답은 없으며, 프로그램의 구조와 동작에 맞는 형태를 프로그래머가 찾아가야 합니다. Exception/Error도 클래스이며, 이후 클래스를 만드는 글을 이해하면, 본인이 만든 프로그램에 맞는 새로운 Exception/Error를 만들어 낼수 있습니다. 즉, 본인 프로그램 내에서만 유효한 Exception과 Error를 정의하고, 이에 맞는 최적의 대응이 가능하도록 하게 하는 것이 가능합니다.
300 | 301 | 302 | 303 |마지막으로 살펴볼 문법은 finally 입니다. 이 문법에 대한 이해는 예제를 보면서 설명하는 것이 좋겠습니다. darttutorial-19-04.dart 프로그램에 finally를 추가하고, 에러가 나지 않는 경우에 대한 main() 함수의 코드를 추가한 결과가 darttutorial-19-05.dart 프로그램 입니다.
308 | 309 | 310 | 311 |// darttutorial-19-05.dart 312 | // Corrected version of darttutorial-19-01.dart 313 | 314 | funcDivide(var para1, var para2) { 315 | try { 316 | if(para2 == null) { 317 | throw IntegerDivisionByZeroException; 318 | } else { 319 | return para1 ~/ para2; 320 | } 321 | } catch (e) { 322 | print('>> funcDivide:IntegerDivisionByZeroException'); 323 | rethrow; 324 | } finally { 325 | print(':: completed...'); 326 | } 327 | } 328 | 329 | funcGetIndexedValue(var paraList, var paraIndex) { 330 | try { 331 | return(paraList[paraIndex]); 332 | } catch (e) { 333 | print('>> funcGetIndexedValue: $e'); 334 | rethrow; 335 | } finally { 336 | print(':: completed...'); 337 | } 338 | } 339 | 340 | funcPrintValue(var para1, var para2) { 341 | print("#$para1 $para2"); 342 | } 343 | 344 | void main() { 345 | var myResult; 346 | var myList = [1 ,2, 3]; 347 | 348 | try { 349 | print('#1'); 350 | myResult = funcDivide(1, 1); 351 | } on IntegerDivisionByZeroException { 352 | print('>> main:IntegerDivisionByZeroException'); 353 | myResult = null; 354 | } catch (e) { 355 | print('Exception: $e'); 356 | myResult = null; 357 | } 358 | 359 | funcPrintValue(1, myResult); 360 | 361 | try { 362 | print('#2'); 363 | myResult = funcDivide(1, 0); 364 | } on IntegerDivisionByZeroException { 365 | print('>> main:IntegerDivisionByZeroException'); 366 | myResult = null; 367 | } catch (e) { 368 | print('Exception: $e'); 369 | myResult = null; 370 | } 371 | 372 | funcPrintValue(2, myResult); 373 | 374 | try { 375 | print('#3'); 376 | myResult = funcGetIndexedValue(myList, 0); 377 | } catch (e) { 378 | print('>> main: $e'); 379 | myResult = null; 380 | } 381 | 382 | funcPrintValue(3, myResult); 383 | 384 | try { 385 | print('#4'); 386 | myResult = funcGetIndexedValue(myList, 4); 387 | } catch (e) { 388 | print('>> main: $e'); 389 | myResult = null; 390 | } 391 | 392 | funcPrintValue(4, myResult); 393 | } 394 |395 | 396 | 397 | 398 |
darttutorial-19-05.dart 프로그램의 funcDivide 함수와 funcGetIndexedValue 함수를 보면, finally 구문이 마지막에 추가된 것을 볼 수 있습니다. 이름이 문법의 역할을 이야기 하는데, finally는 에러가 없는 try 구문의 수행이 마쳐지거나, 혹은 catch/on의 에러 구문의 수행이 마쳐졌을때, 어떤 경우든 상관없이 마지막에 수행이 되는 문장입니다. 따라서 에러가 없는 경우는 try에 속한 구문을 수행하고, finally에 속한 구문을 수행합니다. 에러가 있는 경우는 에러를 처리하는 구문을 수행하고, finally에 속한 구문을 수행합니다.
399 | 400 | 401 | 402 |darttutorial-19-05.dart 프로그램의 수행 결과인 아래 부분을 확인하면, main() 함수의 4가지 함수 호출에서 에러가 없는 경우와 에러가 있는 모든 경우에 대해서 finally 구문이 수행된 것을 확인 할 수 있습니다.
403 | 404 | 405 | 406 |#1 407 | :: completed... 408 | #1 1 409 | #2 410 | >> funcDivide:IntegerDivisionByZeroException 411 | :: completed... 412 | >> main:IntegerDivisionByZeroException 413 | #2 null 414 | #3 415 | :: completed... 416 | #3 1 417 | #4 418 | >> funcGetIndexedValue: RangeError (index): Invalid value: Not in range 0..2, inclusive: 4 419 | :: completed... 420 | >> main: RangeError (index): Invalid value: Not in range 0..2, inclusive: 4 421 | #4 null 422 |423 | 424 | 425 | 426 |
이 글에서는 에러 환경이 발생하는 경우에도 프로그램이 정상적으로 동작할 수 있도록 합니다. 첫째로 에러가 발생할 수 있는 부분을 감싸주는 try 구문이 있습니다. 둘째로 에러가 발생하면, 에러에 대한 대응을 해서 프로그램이 종료되지 않도록 하는 on/catch 구문이 있습니다. 셋째로 on/catch 구문이 처리할 exception/error를 인위적으로 만드는 (원래 전문 용어로 raise라고 합니다) throw 구문을 배웠습니다. 넷째로 Exception/Error가 발생한 함수안에서 적절한 처리를 마친후, 함수를 호출한 쪽으로 Exception/Error를 재전달하여 필요한 조치를 하도록 하는 rethrow가 있었습니다. 마지막으로 try/on/catch 구문을 통해서 에러가 발생하거나 발생하지 않더라도, 모든 경우에 대해서 마무리 작업을 할 수 있도록 해주는 finally 구문을 다뤘습니다. Exception과 Error는 모두 Dart 언어의 표준 클래스 타입니다. 이후 클래스를 직접 개발자가 만드는 구문도 나올 예정인데, Exception과 Error도 이에 해당하는 내용이니, 시간을 내어 Dart 언어 설명에서 해당 내용을 숙독해 보기 바랍니다 [참조: Exception] [참조: Error]. 특히 Dart 언어는 에러가 발생할 만한 사항을 수행을 해봐야지 알수 있는 언어이기에 (unchecked excpetion), 이 글에서의 에러 검출 및 방지가 매우 중요한 언어입니다.
431 | 432 | 433 | 434 |Creative Commons License (CC BY-NC-ND)
439 | 440 | -------------------------------------------------------------------------------- /doc/dart-tutorial-22.md: -------------------------------------------------------------------------------- 1 | # Dart 기초문법 - Making Class - Part.3 2 | 3 | 4 |클래스에 대한 마지막 글로써, 소소하지만 알아두면 요긴한 클래그 관련 기능들(Auxiliary Useful Features)에 대해서 살펴보도록 하겠습니다.
5 | 6 | 7 | 8 |만약 p?.y = 4;와 같은 코드가 있다면, 이는 클래스 p가 null이 아닌 경우에 대해서만 멤버 변수 y에 4 값을 저장하는 동작을 한다는 의미입니다. 이렇게 함으로써, null 상태인 클래스에 실수로 접근하는 에러를 줄여 줍니다.
13 | 14 | 15 | 16 |Initialization List는 constructor의 표현을 좀 더 쉽게 합니다. 앞에서 만들었던 Point 클래스의 constructor를 다시 본다면, 매우 단순하게 x, y 좌표 값을 받아서 멤버 변수에 각각 저장 했습니다. 이런 경우 굳이 메소드의 본문을 채우지 않고, 다음과 같은 형태로 초기화가 가능합니다.
21 | 22 | 23 | 24 |Point([var numX = 0, var numY = 0]) 25 | : this.x = numX, 26 | this.y = numY { 27 | } 28 |29 | 30 | 31 | 32 |
이렇게 멤버 변수에 바로 대응이 되는 입력 파라메타만 전달 받는 다면, 더 간단한 형태로 다음과 같이 입력 파라메타를 바로 멤버 변수에 넣는 방법도 가능합니다.
33 | 34 | 35 | 36 |Point([this.x = 0, this.y = 0]) { 37 | } 38 |39 | 40 | 41 | 42 |
첫번째 입력 파라메타는 바로 객체의 x 값으로 할당되고, 두번째 입력 파라메타는 y 값으로 할당됩니다. 만약, 값을 주지 않으면 default 값으로 각각 0으로 설정하는 것은 동일 합니다.
43 | 44 | 45 | 46 |Factory constructor는 앞서에서 이야기한 일반적인 costructor가 매번 새로운 객체를 만드는 것과 다르게, 조건에 맞춰서 이미 만들어진 객체를 사용하는 등의 다른 동작을 할 수 있는 경우를 의미합니다. 예를 들어 공식 홈페이지의 다음 예제를 살펴봅니다.
51 | 52 | 53 | 54 |55 | class Logger { 56 | final String name; 57 | bool mute = false; 58 | 59 | // _cache is library-private, thanks to 60 | // the _ in front of its name. 61 | static final Map76 | 77 | 78 | 79 |_cache = {}; 62 | 63 | factory Logger(String name) { 64 | return _cache.putIfAbsent(name, () => Logger._internal(name)); 65 | } 66 | 67 | Logger._internal(this.name); 68 | 69 | void log(String msg) { 70 | if (!mute) print(msg); 71 | } 72 | 73 | static get cacheLength => _cache.length; 74 | } 75 |
가장 먼저 주의 깊게 볼 부분은 _cache 라는 Map 타입의 멤버 변수로서 static 타입으로 되어 있기에, 클래스에서 만들어지는 객체와 상관없이 존재합니다. 그리고 일단 비어있는 Map으로 만들어 지며, 내부적으로 문자열의 이름과 스스로의 객체를 저장하도록 되어 있습니다. 이로서, Logger는 매번 객체자 만들어지지 않게 하고, 만들어지는 객체를 내부적으로 저장하되, static으로 처리 하여 관리하기에, 필요할때 이 static한 Map안의 객체들로 constructor의 결과를 제어하겠다는 의지를 밝힌 셈입니다.
80 | 81 | 82 | 83 |다음으로 중요한 구문은 실제 constructor인 Logger(String name) 입니다. 내부를 보면, putIfAbsent 구문을 사용하여, 만약의 경우 _cache가 비어 있을때에만, constroctor로 받은 이름과 Logger 객체를 새롭게 Map에 저장하는 것을 볼 수 있습니다. 따라서 한번만 객체가 만들어 지는 것을 유추할 수 있습니다. 이에 대한 실행 구문을 다음과 같이 해서 실제 객체를 만들어 봅니다.
84 | 85 | 86 | 87 |var logger1 = Logger('UI'); 88 | var logger2 = Logger('UI'); 89 | print("[04]\$ ${Logger.cacheLength}"); 90 | logger1.log('[05]\$ [object:${logger1.hashCode}] Button clicked'); 91 | logger2.log('[06]\$ [object:${logger2.hashCode}] Button clicked'); 92 |93 | 94 | 95 | 96 |
분명 Logger constructor를 두번 호출했고, logger1과 logger2에 저장하는 과정을 거쳤습니다. 하지만, Logger 클래스의 static Getter인 cacheLength를 호출해서, _cache에 저장된 객체의 수를 확인하면, 1로 나오는 것을 볼 수 있습니다. 객체는 하나만 만든 것입니다. 이러지는 log 문장에서 객체의 식별자를 보면 모두 동일한 값입니다. 이런 식으로 Dart 언어에서는 factory constructor를 사용하여, 클래스에서 만들어 지는 개체들의 제어/관리하는 기능을 제공하고 있습니다.
97 | 98 | 99 | 100 |Enumerated types는 enums라고도 불리우며, enum 문법으로 구현합니다. 단어를 사용하여 순서를 나타내고 싶을때 사용하거나, 제한된 값을 가지는 문제를 좀 더 용이하게 개발하도록 하는 문법입니다. 예를 들어, enum Color { red, green, blue } 라고 하면, red는 0, green은 1, blue는 2 값을 갖게 됩니다. 따라서 세가지 값만을 가지는 문제에 사용하게 됩니다. 예를 들어 var aColor = Color.blue; 처럼하여, aColor 변수가 Color가 가진 세가지 값 외에 값을 갖게 함으로 발생 가능한 에러를 방지합니다.
105 | 106 | 107 | 108 |이번 글에서 다룬 문법들을 적용한 하나의 샘플 프로그램을 darttutorial-22-01.dart 프로그램으로 구현하였습니다. 한 줄 한 줄 읽어 가면서 이해를 해보기 바랍니다.
113 | 114 | 115 | 116 |117 | // darttutorial-22-01.dart 118 | // Reference: https://dart.dev/guides/language/language-tour#enumerated-types 119 | 120 | class Point { 121 | // Instance Variables 122 | var x; 123 | var y; 124 | 125 | // Constructor with optional and default parameters 126 | Point([this.x = 0, this.y = 0]) { 127 | } 128 | } 129 | 130 | class Logger { 131 | final String name; 132 | bool mute = false; 133 | 134 | // _cache is library-private, thanks to 135 | // the _ in front of its name. 136 | static final Map189 | 190 | 191 | 192 |_cache = {}; 137 | 138 | factory Logger(String name) { 139 | return _cache.putIfAbsent(name, () => Logger._internal(name)); 140 | } 141 | 142 | Logger._internal(this.name); 143 | 144 | void log(String msg) { 145 | if (!mute) print(msg); 146 | } 147 | 148 | static get cacheLength => _cache.length; 149 | } 150 | 151 | // Enumerated Types 152 | enum Color { red, green, blue } 153 | 154 | void main() { 155 | // ?. Operator 156 | var p0; 157 | p0?.x = 0; 158 | p0?.y = 0; 159 | print("[01]\$ $p0"); 160 | 161 | // Initialization List 162 | var p1 = Point(3,4); 163 | var p2 = Point(); 164 | print("[02]\$ (${p1.x},${p1.y})"); 165 | print("[03]\$ (${p2.x},${p2.y})"); 166 | 167 | // Factory Constructor 168 | var logger1 = Logger('UI'); 169 | var logger2 = Logger('UI'); 170 | print("[04]\$ ${Logger.cacheLength}"); 171 | logger1.log('[05]\$ [object:${logger1.hashCode}] Button clicked'); 172 | logger2.log('[06]\$ [object:${logger2.hashCode}] Button clicked'); 173 | 174 | // Enumerated Types 175 | print("[07]\$ ${Color.red.index} ${Color.green.index} ${Color.blue.index}"); 176 | var aColor = Color.blue; 177 | switch (aColor) { 178 | case Color.red: 179 | print('[08]\$ Red as roses!'); 180 | break; 181 | case Color.green: 182 | print('[08]\$ Green as grass!'); 183 | break; 184 | default: // Without this, you see a WARNING. 185 | print('[08]\$ ${aColor}'); // 'Color.blue' 186 | } 187 | } 188 |
darttutorial-22-01.dart 프로그램의 수행 결과도 다음과 같이 포함하였습니다.
193 | 194 | 195 | 196 |[01]$ null 197 | [02]$ (3,4) 198 | [03]$ (0,0) 199 | [04]$ 1 200 | [05]$ [object:271182360] Button clicked 201 | [06]$ [object:271182360] Button clicked 202 | [07]$ 0 1 2 203 | [08]$ Color.blue 204 |205 | 206 | 207 | 208 |
세 번에 걸쳐서 Dart 언어의 클래스에 대해서 알아 보았습니다. Dart 언어가 객체지향 프로그래밍 언어인 만큼 다양한 객체지향 기법들을 Class를 통해서 제공하고 있습니다. 본 글에서는 소소하지만 나름 유용한 이슈들을 마지막으로 다루어 보았습니다. 클래스를 설계하고 개발하는 작업은 명확하게 정해진 규칙이 없습니다. 여러 클래스를 만들어 보고, 좋은 reference 들을 공부하여 보기 바랍니다.
213 | 214 | 215 | 216 |Creative Commons License (CC BY-NC-ND)
221 | 222 | -------------------------------------------------------------------------------- /source/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drsungwon/dart-tutorial/93382bf3c103375b75490ad3687398b160b42a93/source/.DS_Store -------------------------------------------------------------------------------- /source/darttutorial-06/darttutorial-06-01.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | print("Hello World!"); 3 | } 4 | -------------------------------------------------------------------------------- /source/darttutorial-07/darttutorial-07-01.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-07-01.dart 2 | 3 | void main() { 4 | int myInt = 1; 5 | double myDouble = 1.0; 6 | String myString = "Dr.Sungwon"; 7 | 8 | print("$myInt $myDouble $myString"); 9 | } -------------------------------------------------------------------------------- /source/darttutorial-07/darttutorial-07-02.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-07-02.dart 2 | 3 | void main() { 4 | var myInt = 1; 5 | var myDouble = 1.0; 6 | var myString = "Dr.Sungwon"; 7 | 8 | print("$myInt $myDouble $myString"); 9 | } -------------------------------------------------------------------------------- /source/darttutorial-07/darttutorial-07-03.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-07-03.dart 2 | 3 | void main() { 4 | Object myInt = 1; 5 | Object myDouble = 1.0; 6 | Object myString = "Dr.Sungwon"; 7 | Object temp; 8 | 9 | temp = myInt; 10 | myInt = myDouble; 11 | myDouble = myString; 12 | myString = temp; 13 | 14 | print("$myInt $myDouble $myString"); 15 | } -------------------------------------------------------------------------------- /source/darttutorial-07/darttutorial-07-04.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-07-04.dart 2 | 3 | void main() { 4 | dynamic myInt = 1; 5 | dynamic myDouble = 1.0; 6 | dynamic myString = "Dr.Sungwon"; 7 | dynamic temp; 8 | 9 | print(temp); 10 | 11 | temp = myInt; 12 | myInt = myDouble; 13 | myDouble = myString; 14 | myString = temp; 15 | 16 | print("$myInt $myDouble $myString"); 17 | } -------------------------------------------------------------------------------- /source/darttutorial-07/darttutorial-07-05.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-07-05.dart 2 | 3 | void main() { 4 | final String firstName = "Sungwon"; 5 | final secondName = "Lee"; 6 | 7 | const double myPi = 3.141592; 8 | const changeRate = 1.3; 9 | 10 | print("$firstName $secondName $myPi $changeRate"); 11 | } -------------------------------------------------------------------------------- /source/darttutorial-08/darttutorial-08-01.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-08-01.dart 2 | 3 | void main() { 4 | dynamic num1 = 9; 5 | dynamic num2 = 4; 6 | dynamic res1 = num1 + num2; 7 | dynamic res2 = num1 - num2; 8 | dynamic res3 = num1 * num2; 9 | dynamic res4 = num1 / num2; 10 | dynamic res5 = num1 ~/ num2; 11 | dynamic res6 = num1 % num2; 12 | 13 | // print 13 5 36 2.25 2 1 14 | print("$res1 $res2 $res3 $res4 $res5 $res6"); 15 | 16 | num1 = 9.0; 17 | num2 = 4.0; 18 | res1 = num1 + num2; 19 | res2 = num1 - num2; 20 | res3 = num1 * num2; 21 | res4 = num1 / num2; 22 | res5 = num1 ~/ num2; 23 | res6 = num1 % num2; 24 | 25 | // print 13.0 5.0 36.0 2.25 2 1.0 26 | print("$res1 $res2 $res3 $res4 $res5 $res6"); 27 | 28 | num1 = 9.0; 29 | num2 = 4; 30 | res1 = num1 + num2; 31 | res2 = num1 - num2; 32 | res3 = num1 * num2; 33 | res4 = num1 / num2; 34 | res5 = num1 ~/ num2; 35 | res6 = num1 % num2; 36 | 37 | // print 13.0 5.0 36.0 2.25 2 1.0 38 | print("$res1 $res2 $res3 $res4 $res5 $res6"); 39 | } -------------------------------------------------------------------------------- /source/darttutorial-08/darttutorial-08-02.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-08-01.dart 2 | 3 | void main() { 4 | dynamic var1 = 1; 5 | print("$var1"); // Print 1 6 | 7 | var1 = ++var1 + 10; 8 | print("$var1"); // Print 12 9 | 10 | var1 = var1++ + 10; 11 | print("$var1"); // Print 22 12 | } -------------------------------------------------------------------------------- /source/darttutorial-08/darttutorial-08-03.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-08-03.dart 2 | 3 | void main() { 4 | // integer in hexadecimal format 5 | var num1 = 0x0F; 6 | // print 15 7 | print("$num1"); 8 | 9 | // exponential number format 10 | var num2 = 1.1e2; 11 | // print 110.0 12 | print("$num2"); 13 | 14 | // String -> int 15 | var num3 = int.parse('1'); 16 | // print 1 17 | print("$num3"); 18 | 19 | // String -> double 20 | var num4 = double.parse('1.1'); 21 | // print 1.1 22 | print("$num4"); 23 | } -------------------------------------------------------------------------------- /source/darttutorial-09/darttutorial-09-01.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-09-01.dart 2 | 3 | void main() { 4 | var s1 = 'Single quotes work well for string literals.'; 5 | var s2 = "Double quotes work just as well."; 6 | var s3 = 'It\'s easy to escape the string delimiter.'; 7 | var s4 = "It's even easier to use the other delimiter."; 8 | 9 | print(" $s1 \n $s2 \n $s3 \n $s4 "); 10 | } -------------------------------------------------------------------------------- /source/darttutorial-09/darttutorial-09-02.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-09-02.dart 2 | 3 | void main() { 4 | String s1 = 1.toString(); 5 | String s2 = 3.141592.toStringAsFixed(4); 6 | 7 | print("$s1 $s2"); 8 | } -------------------------------------------------------------------------------- /source/darttutorial-09/darttutorial-09-03.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-09-03.dart 2 | 3 | void main() { 4 | String s1 = "My name is "; 5 | String s2 = "Dr.Sungwon"; 6 | String s3 = s1 + s2; 7 | 8 | var s4 = 'My ' 9 | 'name ' 10 | 'is ' 11 | 'Dr.Sungwon'; 12 | 13 | dynamic s5 = ''' 14 | My name is Dr.Sungwon. 15 | Dart is lovely. 16 | '''; 17 | 18 | print("$s3\n$s4\n$s5"); 19 | } -------------------------------------------------------------------------------- /source/darttutorial-10/darttutorial-10-01.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-10-01.dart 2 | 3 | void main() { 4 | bool logic1 = true; 5 | dynamic logic2 = false; 6 | 7 | print("$logic1 $logic2"); 8 | } -------------------------------------------------------------------------------- /source/darttutorial-10/darttutorial-10-02.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-10-02.dart 2 | 3 | void main() { 4 | var cond1 = true; 5 | var cond2 = false; 6 | var num1 = 1; 7 | var num2 = 9; 8 | 9 | var res1 = (cond1 == cond2); 10 | var res2 = (cond1 != cond2); 11 | var res3 = (num1 > num2); 12 | var res4 = (num1 < num2); 13 | var res5 = (num1 >= num2); 14 | var res6 = (num1 <= num2); 15 | var res7 = (!cond1); 16 | var res8 = (!cond2); 17 | var res9 = (cond1 && cond2); 18 | var res10 = (cond1 || cond2); 19 | 20 | // print false true 21 | print("$res1 $res2"); 22 | 23 | // print false true false true 24 | print("$res3 $res4 $res5 $res6"); 25 | 26 | // print false true false true 27 | print("$res7 $res8 $res9 $res10"); 28 | } -------------------------------------------------------------------------------- /source/darttutorial-10/darttutorial-10-03.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-10-03.dart 2 | 3 | void main() { 4 | var num1 = 1; 5 | var num2 = 2; 6 | 7 | if(num1 > num2) { 8 | print("num1 is greater than num2"); 9 | } else if(num1 == num2) { 10 | print("num1 equal to num2"); 11 | } else { 12 | print("num1 is less than num2"); 13 | } 14 | } -------------------------------------------------------------------------------- /source/darttutorial-10/darttutorial-10-04.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-10-04.dart 2 | 3 | void main() { 4 | var command = 'OPEN'; 5 | switch (command) { 6 | case 'CLOSED': 7 | print("CLOSED"); 8 | break; 9 | case 'PENDING': 10 | print("PENDING"); 11 | break; 12 | case 'APPROVED': 13 | print("APPROVED"); 14 | break; 15 | case 'DENIED': 16 | print("DENIED"); 17 | break; 18 | case 'OPEN': 19 | print("OPEN"); 20 | break; 21 | default: 22 | print("default"); 23 | } 24 | } -------------------------------------------------------------------------------- /source/darttutorial-10/darttutorial-10-05.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-10-05.dart 2 | 3 | void main() { 4 | var command = 'close'; 5 | switch (command) { 6 | case 'close': 7 | case 'CLOSE': 8 | print("CLOSE"); 9 | break; 10 | case 'open': 11 | case 'OPEN': 12 | print("OPEN"); 13 | break; 14 | default: 15 | print("default"); 16 | } 17 | } -------------------------------------------------------------------------------- /source/darttutorial-10/darttutorial-10-06.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-10-06.dart 2 | 3 | void main() { 4 | var command = 'close'; 5 | switch (command) { 6 | case 'close': 7 | // print("Use uppercase."); // Error 8 | case 'CLOSE': 9 | print("CLOSE"); 10 | break; 11 | case 'open': 12 | // print("Use uppercase."); // Error 13 | case 'OPEN': 14 | print("OPEN"); 15 | break; 16 | default: 17 | print("default"); 18 | } 19 | } -------------------------------------------------------------------------------- /source/darttutorial-10/darttutorial-10-07.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-10-07.dart 2 | 3 | void main() { 4 | var bool1 = true; 5 | var bool2 = true; 6 | 7 | assert(bool1 == bool2); 8 | //assert(bool1 != bool2); 9 | 10 | print("Completed!"); 11 | } -------------------------------------------------------------------------------- /source/darttutorial-10/darttutorial-10-08.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-10-08.dart 2 | 3 | void main() { 4 | var bool1 = true; 5 | var bool2 = true; 6 | //assert(bool1 == bool2); 7 | assert(bool1 != bool2); 8 | 9 | print("Completed!"); 10 | } -------------------------------------------------------------------------------- /source/darttutorial-11/darttutorial-11-01.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-11-01.dart 2 | 3 | void main() { 4 | var sum = 0; 5 | for(var num = 1; num <= 10; num++) { 6 | sum = sum + num; 7 | print("sum is $sum and num is $num."); 8 | } 9 | print("Finally, sum is $sum."); 10 | } -------------------------------------------------------------------------------- /source/darttutorial-11/darttutorial-11-02.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-11-02.dart 2 | 3 | void main() { 4 | var temp = 0; 5 | 6 | for(var num1 = 1; num1 <= 9; num1++) { 7 | for(var num2 = 1; num2 <= 9; num2++) { 8 | temp = num1 * num2; 9 | print("$num1 * $num2 = $temp"); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /source/darttutorial-11/darttutorial-11-03.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-11-03.dart 2 | 3 | void main() { 4 | var oNum = 1; 5 | for(var iNum=1; iNum <3; iNum++){ 6 | print("[OUT] oNum is $oNum and iNum is $iNum."); 7 | } 8 | // print("[IN ] oNum is $oNum and iNum is $iNum."); // Error 9 | } -------------------------------------------------------------------------------- /source/darttutorial-11/darttutorial-11-04.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-11-04.dart 2 | 3 | void main() { 4 | var sum = 0; 5 | var num = 1; 6 | while(num <= 10) { 7 | sum = sum + num; 8 | print("sum is $sum and num is $num."); 9 | num++; 10 | } 11 | print("Finally, sum is $sum."); 12 | } -------------------------------------------------------------------------------- /source/darttutorial-11/darttutorial-11-05.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-11-05.dart 2 | 3 | void main() { 4 | var sum = 0; 5 | var num = 1; 6 | do { 7 | sum = sum + num; 8 | print("sum is $sum and num is $num."); 9 | num++; 10 | } while(num <= 10); 11 | print("Finally, sum is $sum."); 12 | } -------------------------------------------------------------------------------- /source/darttutorial-11/darttutorial-11-06.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-11-06.dart 2 | 3 | void main() { 4 | var sum = 0; 5 | for(var num = 1; num <= 10; num++) { 6 | if(num % 2 == 0){ 7 | sum = sum + num; 8 | } else { 9 | continue; 10 | } 11 | print("sum is $sum and num is $num."); 12 | } 13 | print("Finally, sum is $sum."); 14 | } -------------------------------------------------------------------------------- /source/darttutorial-11/darttutorial-11-07.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-11-07.dart 2 | 3 | void main() { 4 | var sum = 0; 5 | for(var num = 1; num <= 10; num++) { 6 | if(num % 2 == 0){ 7 | sum = sum + num; 8 | } else { 9 | continue; 10 | } 11 | print("sum is $sum and num is $num."); 12 | 13 | if(sum >= 20) { 14 | break; 15 | } 16 | } 17 | print("Finally, sum is $sum."); 18 | } -------------------------------------------------------------------------------- /source/darttutorial-12/darttutorial-12-01.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-12-01.dart 2 | 3 | calcSum(var num1, var num2) { 4 | var sum = num1 + num2; // 3 5 | print("[calcSum()] $num1 + $num2 is $sum"); // 4 6 | return sum; // 5 7 | } 8 | 9 | void main() { 10 | print("[main()] start"); // 1 11 | var resNum = calcSum(5, 5); // 2, 6 12 | print("[main()] result is $resNum"); // 7 13 | } 14 | -------------------------------------------------------------------------------- /source/darttutorial-12/darttutorial-12-02.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-12-02.dart 2 | 3 | calcSum(var num1, var num2) { 4 | return num1 + num2; 5 | } 6 | 7 | void main() { 8 | print("[main()] start"); 9 | var resNum = calcSum(5, 5); 10 | print("[main()] result is $resNum"); 11 | } 12 | -------------------------------------------------------------------------------- /source/darttutorial-12/darttutorial-12-03.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-12-03.dart 2 | 3 | calcSum(var num1, var num2) => num1 + num2; 4 | 5 | void main() { 6 | print("[main()] start"); 7 | var resNum = calcSum(5, 5); 8 | print("[main()] result is $resNum"); 9 | } 10 | -------------------------------------------------------------------------------- /source/darttutorial-12/darttutorial-12-04.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-12-04.dart 2 | 3 | void main() { 4 | var myFunc = (var num1, var num2) => num1 + num2; 5 | 6 | print("[main()] start"); 7 | var resNum = myFunc(5, 5); 8 | print("[main()] result is $resNum"); 9 | } 10 | -------------------------------------------------------------------------------- /source/darttutorial-12/darttutorial-12-05.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-12-05.dart 2 | 3 | // Case.1 Positional parameters 4 | int calcFraction1(var denominator, var nominator) { 5 | return denominator ~/ nominator; 6 | } 7 | 8 | // Case.2 Positional and optional parameters 9 | int calcFraction2(var denominator, [var nominator]) { 10 | if(nominator == null){ 11 | nominator = 1; 12 | } 13 | return denominator ~/ nominator; 14 | } 15 | 16 | // Case.3 Positional, optional and default parameters 17 | int calcFraction3(var denominator, [var nominator = 1]) { 18 | return denominator ~/ nominator; 19 | } 20 | 21 | // Case.4 Named parameters (all parameters are optional) 22 | int calcFraction4({var denominator, var nominator}) { 23 | return denominator ~/ nominator; 24 | } 25 | 26 | // Case.5 Named parameters with initial value checking 27 | int calcFraction5({var denominator, var nominator}) { 28 | if(nominator == null){ 29 | nominator = 1; 30 | } 31 | return denominator ~/ nominator; 32 | } 33 | 34 | // Case.6 Named parameters with default parameters 35 | int calcFraction6({var denominator, var nominator = 1}) { 36 | return denominator ~/ nominator; 37 | } 38 | 39 | void main() { 40 | var result1 = calcFraction1(1, 1); 41 | var result2 = calcFraction2(2); 42 | var result3 = calcFraction3(3); 43 | 44 | // print 1 2 3 45 | print("$result1 $result2 $result3"); 46 | 47 | var result4 = calcFraction4(denominator: 4, nominator: 1); 48 | var result5 = calcFraction4(nominator: 1, denominator: 4); 49 | var result6 = calcFraction5(denominator: 5); 50 | var result7 = calcFraction6(denominator: 6); 51 | 52 | // print 4 4 5 6 53 | print("$result4 $result5 $result6 $result7"); 54 | } 55 | -------------------------------------------------------------------------------- /source/darttutorial-12/darttutorial-12-06.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-12-06.dart 2 | 3 | int calcSum(int num1, int num2) { 4 | var localSum = num1 + num2; 5 | return localSum; 6 | } 7 | 8 | void main() { 9 | var resNum = calcSum(1,1); 10 | print("$resNum"); 11 | //print("$localSum"); // Error 12 | } 13 | -------------------------------------------------------------------------------- /source/darttutorial-13/darttutorial-13-01.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-13-01.dart 2 | 3 | int sampleFunction(var varA, [var varB = 1]) { 4 | var localSum; 5 | localSum = varA + varB; 6 | print("[sampleFunction] $localSum"); 7 | return localSum; 8 | } 9 | 10 | void main() { 11 | var num1 = 1; 12 | var num2 = 5; 13 | var num3; 14 | 15 | print("[main] start with num3:$num3."); 16 | 17 | num3 = sampleFunction(num1, num2); 18 | 19 | for(var temp = 1; temp < 3; temp++) { 20 | num3 = num3 + sampleFunction(num1 + temp, num2 + temp); 21 | } 22 | 23 | print("[main] end with num3:$num3."); 24 | } -------------------------------------------------------------------------------- /source/darttutorial-14/darttutorial-14-01.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-14-01.dart 2 | // Ref: https://api.dart.dev/stable/2.7.1/dart-core/int-class.html 3 | 4 | void main() { 5 | int num1 = 1; 6 | int num2; 7 | String myString; 8 | 9 | // Returns the absolute value 10 | num2 = num1.abs(); 11 | print("$num2"); 12 | 13 | // Returns the greatest common divisor 14 | num1 = 3; 15 | num2 = num1.gcd(6); 16 | print("$num2"); 17 | 18 | // Returns a string representation 19 | myString = num2.toString(); 20 | print("$myString"); 21 | } -------------------------------------------------------------------------------- /source/darttutorial-14/darttutorial-14-02.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-14-02.dart 2 | // Ref: https://api.dart.dev/stable/2.7.1/dart-core/double-class.html 3 | 4 | void main() { 5 | double num1 = 1.4; 6 | double num2; 7 | int num3; 8 | String myString; 9 | 10 | // Returns the absolute value 11 | num2 = num1.abs(); 12 | print("$num2"); 13 | 14 | // Returns a string representation 15 | myString = num2.toString(); 16 | print("$myString"); 17 | 18 | // Returns the greatest integer no greater than member data 19 | num3 = num1.floor(); 20 | print("$num3"); 21 | 22 | // Returns the greatest integer double value no greater than member data 23 | num2 = num1.floorToDouble(); 24 | print("$num2"); 25 | 26 | // Returns the greatest integer no greater than member data 27 | num3 = num1.round(); 28 | print("$num3"); 29 | 30 | // Returns the integer double value obtained by discarding any fractional digits 31 | num2 = num1.truncateToDouble(); 32 | print("$num2"); 33 | } -------------------------------------------------------------------------------- /source/darttutorial-14/darttutorial-14-03.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-14-03.dart 2 | // Ref: https://api.dart.dev/stable/2.7.1/dart-core/double-class.html 3 | 4 | void main() { 5 | var num; 6 | 7 | // Returns the absolute value 8 | num = 1.4.abs(); 9 | print("$num"); 10 | 11 | // Returns a string representation 12 | num = 1.4.toString(); 13 | print("$num"); 14 | 15 | // Returns the greatest integer no greater than member data 16 | num = 1.4.floor(); 17 | print("$num"); 18 | 19 | // Returns the greatest integer double value no greater than member data 20 | num = 1.4.floorToDouble(); 21 | print("$num"); 22 | 23 | // Returns the greatest integer no greater than member data 24 | num = 1.4.round(); 25 | print("$num"); 26 | 27 | // Returns the integer double value obtained by discarding any fractional digits 28 | num = 1.4.truncateToDouble(); 29 | print("$num"); 30 | } -------------------------------------------------------------------------------- /source/darttutorial-14/darttutorial-14-04.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-14-04.dart 2 | // Ref: https://api.dart.dev/stable/2.7.1/dart-core/String-class.html 3 | 4 | void main() { 5 | var string1 = "Hello Dart!"; 6 | var string2; 7 | var num; 8 | 9 | // Sub-slicing (or Sub-string) 10 | string2 = string1.substring(0,5); 11 | print("$string2"); 12 | 13 | // Index Operator 14 | string2 = string1[0]; 15 | print("$string2"); 16 | 17 | // Concatenation 18 | string2 = "Hello" + " " + "Dart" + "!"; 19 | print("$string2"); 20 | 21 | // Using String Properties 22 | num = string1.length; 23 | print("$num"); 24 | 25 | // Interpolate the value of expressions within strings 26 | string2 = "$string1 has ${string1.length} letters"; 27 | print("$string2"); 28 | 29 | // Converts all characters to lower case 30 | string2 = string1.toLowerCase(); 31 | print("$string2"); 32 | 33 | // Converts all characters to upper case 34 | string2 = string1.toUpperCase(); 35 | print("$string2"); 36 | } -------------------------------------------------------------------------------- /source/darttutorial-14/darttutorial-14-05.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-14-05.dart 2 | // Ref: https://api.dart.dev/stable/2.7.1/dart-core/bool-class.html 3 | 4 | void main() { 5 | bool bool1 = true; 6 | var myString; 7 | 8 | // Print boolean object 9 | print("$bool1"); // true 10 | 11 | // Convert boolean object to String 12 | myString = bool1.toString(); 13 | print("$myString"); // true 14 | } -------------------------------------------------------------------------------- /source/darttutorial-14/darttutorial-14-06.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-14-06.dart 2 | 3 | void printType(var para) { 4 | if(para is int) { 5 | print("[type] int"); 6 | } else if(para is double) { 7 | print("[type] double"); 8 | } else if(para is String) { 9 | print("[type] string"); 10 | } else if(para is bool) { 11 | print("[type] double"); 12 | } else { 13 | print("[type] not identified"); 14 | } 15 | } 16 | 17 | void main() { 18 | int num1 = 1; 19 | var num2 = 1.1; 20 | dynamic num3; 21 | 22 | printType(num1); 23 | printType(num2); 24 | 25 | num3 = num1; 26 | printType(num3); 27 | 28 | num3 = num2; 29 | printType(num3); 30 | 31 | num3 = "number"; 32 | printType(num3); 33 | } -------------------------------------------------------------------------------- /source/darttutorial-15/darttutorial-15-01.dart: -------------------------------------------------------------------------------- 1 | // darttutorial-15-01.dart 2 | 3 | // Print the type of object 4 | String getObjectType(var para) { 5 | if(para is List