blog 이전
- git blog io 사용할때 쓴 포스팅 이전 - 현재 형식에 맞게 수정
This commit is contained in:
256
src/content/blog/dev/2022-04-07-slicknlist.md
Normal file
256
src/content/blog/dev/2022-04-07-slicknlist.md
Normal file
@@ -0,0 +1,256 @@
|
||||
---
|
||||
title: 블로그 기능 추가
|
||||
date: 2022-04-07
|
||||
tags: 개발
|
||||
excerpt: slick 슬라이더, 목차, 페이지 버튼, 유튜브 임베드 추가 정리.
|
||||
---
|
||||
## slick image
|
||||
[Slick](https://kenwheeler.github.io/slick/)
|
||||
slick 사이트에 들어가서 `get it now`를 눌러 다운로드 받아주어 slick 폴더를 `/assets/css/slick`에 복사>붙여넣기 해준다.
|
||||
|
||||
|
||||
게시글 원하는 위치에 아래와 같이 넣어주면 된다.
|
||||
|
||||
```html
|
||||
<div class="main_center">
|
||||
<div><img src= "/assets/img/blog/hydejack-8.jpg" style="width: auto; height: 500px;"></div>
|
||||
<div><img src="/assets/img/blog/hydejack-8.png" style="width: auto; height: 500px;"></div>
|
||||
<div><img src= "/assets/img/blog/hydejack-9-dark.jpg" style="width: auto; height: 500px;"></div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('.main_center').slick({
|
||||
autoplay : true, /*자동으로 슬라이딩됨*/
|
||||
dots : true, /* 하단 점 버튼 */
|
||||
speed : 300 /* 이미지가 슬라이딩시 걸리는 시간 */,
|
||||
infinite : true,
|
||||
autoplaySpeed : 30000 /* 이미지가 다른 이미지로 넘어 갈때의 텀 */,
|
||||
arrows : true,
|
||||
slidesToShow : 1,
|
||||
slidesToScroll : 1,
|
||||
touchMove : true, /* 마우스 클릭으로 끌어서 슬라이딩 가능여부 */
|
||||
nextArrows : true, /* 넥스트버튼 */
|
||||
prevArrows : true,
|
||||
arrow : true, /*false면 좌우 버튼 없음, true면 좌우 버튼 보임*/
|
||||
fade : false
|
||||
});
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
.slick() 안의 옵션을 원하는대로 설정하여 사용 할 수 있다.
|
||||
|
||||
<div class="main_center">
|
||||
<div><img src= "/assets/img/blog/hydejack-8.jpg" style="width: auto; height: 500px;"></div>
|
||||
<div><img src="/assets/img/blog/hydejack-8.png" style="width: auto; height: 500px;"></div>
|
||||
<div><img src= "/assets/img/blog/hydejack-9-dark.jpg" style="width: auto; height: 500px;"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('.main_center').slick({
|
||||
autoplay : true, /*자동으로 슬라이딩됨*/
|
||||
dots : true, /* 하단 점 버튼 */
|
||||
speed : 300 /* 이미지가 슬라이딩시 걸리는 시간 */,
|
||||
infinite : true,
|
||||
autoplaySpeed : 30000 /* 이미지가 다른 이미지로 넘어 갈때의 텀 */,
|
||||
arrows : true,
|
||||
slidesToShow : 1,
|
||||
slidesToScroll : 1,
|
||||
touchMove : true, /* 마우스 클릭으로 끌어서 슬라이딩 가능여부 */
|
||||
nextArrows : true, /* 넥스트버튼 */
|
||||
prevArrows : true,
|
||||
arrow : true, /*false면 좌우 버튼 없음, true면 좌우 버튼 보임*/
|
||||
fade : false
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
그리고 scss를 수정하여 좌우로 화살표를 넣어서 표출 하는것도 가능하도록 변경 할 수 있다.
|
||||
이미지는 별도로 구해서 사용하시면 됩니다.
|
||||
|
||||
> assests/css/slick/slick-theme.css
|
||||
|
||||
```css
|
||||
.slick-prev:before
|
||||
{
|
||||
content: url(left.png);
|
||||
}
|
||||
[dir='rtl'] .slick-prev:before
|
||||
{
|
||||
content: url(right.png);
|
||||
}
|
||||
|
||||
.slick-next:before
|
||||
{
|
||||
content: url(right.png);
|
||||
}
|
||||
[dir='rtl'] .slick-next:before
|
||||
{
|
||||
content: url(left.png);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
## 게시글 목차 만들기
|
||||
h1 타이틀 바로 아래에
|
||||
|
||||
```
|
||||
* toc
|
||||
{:toc .large-only}
|
||||
```
|
||||
|
||||
를 추가하여 헤더를 기준으로 목차 생성
|
||||
|
||||
---
|
||||
## 페이지 버튼 만들기
|
||||
|
||||
> _include/components/page-button.html
|
||||
|
||||
```html
|
||||
<div class="page-control">
|
||||
<div>
|
||||
{% if page.previous.url %}
|
||||
<a id="prev" class="w-btn-outline w-btn-gray-outline" href="{{ page.previous.url }}">« {{ page.previous.title }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
{% if page.next.url %}
|
||||
<a id="next" class="w-btn-outline w-btn-gray-outline" href="{{ page.next.url }}">{{ page.next.title }} »</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
> _layouts/post.html
|
||||
|
||||
```console
|
||||
{% include components/page-button.html %}
|
||||
```
|
||||
|
||||
About 위에 위치하여 작성자 소개 위에 위치하도록 한다.
|
||||
|
||||
원하는 스타일대로 변경을 위해서 다음과 같이 작업해준다.
|
||||
|
||||
> _sass/my-style.scss
|
||||
|
||||
```scss
|
||||
.page-control {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.page-control > div {
|
||||
max-width: 50%;
|
||||
}.page-control {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.page-control > div {
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.w-btn-outline {
|
||||
position: relative;
|
||||
padding: 15px 30px;
|
||||
border-radius: 15px;
|
||||
font-family: "paybooc-Light", sans-serif;
|
||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
transition: 0.25s;
|
||||
}
|
||||
|
||||
.w-btn-outline:hover {
|
||||
letter-spacing: 2px;
|
||||
transform: scale(1.2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.w-btn-outline:active {
|
||||
transform: scale(1.5);
|
||||
}
|
||||
|
||||
.w-btn-gray-outline {
|
||||
border: 3px solid #a3a1a1;
|
||||
color: #6e6e6e;
|
||||
}
|
||||
|
||||
.w-btn-gray-outline:hover {
|
||||
background-color: #a3a1a1;
|
||||
color: #e3dede;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 게시글에 유튜브플레이어 보기
|
||||
|
||||
> _includes/components/youtubePlayer.html
|
||||
|
||||
```html
|
||||
<style>
|
||||
.embed-container { position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%; }
|
||||
.embed-container iframe,
|
||||
.embed-container object,
|
||||
.embed-container embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
||||
</style>
|
||||
|
||||
<div class="embed-container" >
|
||||
<iframe src="https://www.youtube.com/embed/{{ include.id }}" frameborder="0" allowfullscreen="" onclick="ga('send', 'event', 'post', 'click', 'youtubePlayer');">
|
||||
</iframe>
|
||||
</div>
|
||||
```
|
||||
|
||||
위 처럼 youtube 영상의 id를 가져와서 표출 할 수 있도록 만들고
|
||||
|
||||
```console
|
||||
`{% include components/youtubePlayer.html id="{youtube ID}" %}`
|
||||
```
|
||||
|
||||
게시글 원하는 위치에 youtube id를 넣어주면 된다.
|
||||
|
||||

|
||||
|
||||
위와 같이 `watch?v=` 이후에 오는 id를 가져다가 사용한다.
|
||||
|
||||
{% include components/youtubePlayer.html id='0TNFb5zgpbg' %}
|
||||
|
||||
---
|
||||
|
||||
## 게시물 조회수 보이기
|
||||
Hits 이용
|
||||
> [Hits](https://hits.seeyoufarm.com/)
|
||||
|
||||
TARGET URL에 나의 블로그 주소를 입력하고, Options에서 여러 설정들을 조정하여 내가 원하는 스타일의 hits를 만들 수 있다. 미리보기를 통해 표시되는것을 볼 수 있으니 참고해서 만들면 된다.
|
||||
|
||||
그렇게 생성된 아래의 HTML Link를 복사해서
|
||||
|
||||
> _layouts/post.html
|
||||
|
||||
에 넣어서 각 게시물 별로 조회수가 보이는 위치를 조정할 수 있다.
|
||||
|
||||
> _includes/body/sidebar-sticky.html
|
||||
|
||||
에 넣으면 사이드바는 어떤 게시물을 봐도 같이 보이는것이기 때문에 전체 조회수를 체크할 수 있다.
|
||||
|
||||
만약 조회수 가장 오른쪽에 생성되는 링크모양 아이콘이 싫다면
|
||||
|
||||
> _sass/my-style.scss
|
||||
|
||||
```scss
|
||||
a.external::after, a::after {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
|
||||
를 추가하여 제거 할 수 있다.
|
||||
|
||||
---
|
||||
|
||||
|
||||
12
src/content/blog/dev/flutter/2022-04-05-flutter-envbuild.md
Normal file
12
src/content/blog/dev/flutter/2022-04-05-flutter-envbuild.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Flutter 개발
|
||||
date: 2022-04-05
|
||||
tags: flutter, 개발
|
||||
excerpt: Flutter 개발 환경 세팅
|
||||
---
|
||||
# Flutter 개발 환경
|
||||
|
||||
플러터 개발을 위해 Android Studio를 설치하고 개발 프로젝트를 만들어서 emulator를 실행시켰다.
|
||||

|
||||
|
||||
생소하다면 생소한 디렉토리 구성과 환경을 구축해보고 나니 이제부터 시작이겠구나 막막하면서도 두근거림이 있다.
|
||||
108
src/content/blog/dev/flutter/2022-04-05-flutter-start.md
Normal file
108
src/content/blog/dev/flutter/2022-04-05-flutter-start.md
Normal file
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: Flutter 개발 일지
|
||||
date: 2022-04-05
|
||||
tags: flutter, 개발
|
||||
excerpt: Flutter에 대한 이해
|
||||
---
|
||||
# Flutter?
|
||||
|
||||
* toc
|
||||
{:toc .large-only}
|
||||
|
||||
> Flutter란 생소한 언어의 출현과 동시에 정말 흥미가 생겼다.
|
||||
네이티브 앱에 근접한 속도를 내면서 하나의 코드로 Android, iOS를 동시에 개발할 수 있다니!
|
||||
때문에 배워보면서, 개발해보면서 시대의 흐름을 따라가보려고 한다.
|
||||
|
||||
### 작성되는 글
|
||||
|
||||
이 디렉토리에는 플러터에 대해서 공부하고 내용을 정리하면서 내가 이해한 내용을 작성하는 저장소이다.
|
||||
|
||||
플러터는 다트를 기반으로 하는 애플리케이션 개발 언어이다. 여러 장점을 가지고 있으며 무엇보다도 안드로이드와 애플을 구분없이 개발 할 수 있다는 점에서 내 관심을 끌었다.
|
||||
|
||||
또한 네이티브 앱 못지 않은 빠른 속도를 자랑한다니 이런 어썸한 언어에 관심이 가는게 당연하지 않겠는가?
|
||||
|
||||
때문에 이 카테고리는 플러터를 배우며, 개발하며 익힌 지식이나 정리할 내용들을 작성하는 저장소이다.
|
||||
|
||||
지금 구상하고 있는 앱이 두 가지 정도 있는데, 이것을 실용화 할 수 있을 정도의 수준까지 더 노력해보려 한다.
|
||||
|
||||
--------------------------------------------------------
|
||||
|
||||
## 플러터의 등장 배경
|
||||
|
||||
2007년 애플의 아이폰이 등장한 이후 스마트폰은 끊임없이 발전하고 있습니다. 애플은 iOS 운영체제를 발표하면서 오브젝트브-C(Objective-C)로 만든 아이폰용 앱을 출시할 수 있는 앱스토어를 만들었습니다. 그로부터 1년 뒤 구글도 안드로이드 운영체제를 발표하면서 자바(Java)를 사용해 안드로이드용 앱을 판매할 수 있는 구글 플레이를 만들었습니다.
|
||||
|
||||
다양한 기업이 잇따라 이 싸움에 뛰어들었지만 10년이 훨씬 지난 지금도 안드로이드와 iOS의 점유율이 거의 100%라고 해도 과언이 아닐 정도로 두 플랫폼이 모바일 시장을 장악하고 있습니다.
|
||||
")
|
||||
|
||||
애플과 구글은 각자의 앱 개발 생태계를 확장하려는 목적으로 새로운 언어를 내놓았습니다. 애플은 오브젝티브-C 대신에 스위프트(Swift)라는 언어를 만들었고, 구글은 코틀린(Kotlin)이라는 새로운 언어로 자바를 대체하려고 합니다. 새롭게 등장한 두 언어는 지금도 계속 진화하면서 앱 개발 환경을 더 나은 방향으로 이끌고 있습니다. 오브젝티브-C나 스위프트로 iOS 앱을 개발하거나 자바나 코틀린으로 안드로이드 앱을 개발하는 것처럼 각 모바일 운영체제에 맞는 언어로 개발한 앱을 네이티브 앱(native app)이라고 합니다.
|
||||
|
||||
## 웹앱, 하이브리드 앱의 등장
|
||||
|
||||
두 회사가 시장을 나눠 먹는 사이 개발자들은 더 많은 사용자가 앱을 사용하게 하려고 똑같은 앱을 iOS용과 안드로이드용으로 두 번 개발해야 했습니다. 개발자에게는 조금 피곤한 상황이지 않을 수 없었죠. 그래서 "하나의 소스로 안드로이드와 iOS 모두에서 실행할 수 있는 방법이 없을까?" 를 고민하게 되었고, 그 결과로 웹앱과 하이브리드 앱이 등장했습니다.
|
||||
|
||||
**웹앱** (web apps)은 웹 기술을 이용해서 만든 앱입니다. 앱의 화면을 나타내는 뷰(view)를 모바일용 웹으로 만들어서 다양한 기종과 해상도에 대응하며 빠르게 개발할 수 있습니다. 요즘은 네이티브 앱처럼 알림도 보내고 오프라인에서도 동작하는 **프로그레시브 웹앱** (progressive web apps, PWA)도 주목받고 있습니다.
|
||||
그리고 **하이브리드 앱** (hybride apps)은 웹앱을 만든 후 별도의 프레임워크를 이용해 운영체제별로 동작하는 앱을 만드는 기술입니다.
|
||||
|
||||
하지만 이러한 기술로 만든 앱은 네이티브 앱과 비교해 상대적으로 속도가 느리고 애니메이션 사용에도 제약이 있는 등 스마트폰의 성능을 충분히 활용할 수 없었습니다. 이때 리액트 네이티브가 등장했습니다.
|
||||
|
||||
## 리액트 네이티브와 플러터
|
||||
|
||||
페이스북에서 만든 **리액트 네이티브** (React Native)는 여러 운영체제에서 동작하는 앱을 개발 할 수 있는 크로스 플랫폼 앱 개발 프레임워크입니다. 특히 웹 개발자에게 익숙한 자바스크립트를 사용하므로 웹 개발자가 새로운 언어를 배우지 않고서도 앱을 개발할 수 있는 길을 터주었습니다. 또한 네이티브 언어로 개발할 때는 사용자 인터페이스(UI, user interface)를 변경할 때마다 다시 빌드해야 했지만, 리액트 네이티브는 코드를 변경하면 화면에 바로 표시되므로 개발 효율도 좋습니다.
|
||||
|
||||
리액트 네이티브에서는 자바스크립트가 다리 역할을 하면서 안드로이드나 iOS의 네이티브 API에 접근합니다. 똑같은 자바스크립트 코드가 안드로이드와 iOS처럼 각기 다른 운영체제에서 실행되게 연결해 주는 거죠. 따라서 웹앱이나 하이브리드 앱보다는 속도가 빠르지만 화면에 표시할 내용이 많으면 느려질 수 있습니다. 그리고 운영체제가 업데이트되면 디자인이 의도한 바와 달라질 수 있습니다.
|
||||
|
||||
반면에 **플러터** (Flutter)는 똑같이 크로스 플랫폼 앱 개발 프레임워크지만 구글에서 만든 다트(Dart)라는 언어를 사용합니다. 따라서 자바나 C# 같은 컴파일 언어가 가진 특징을 활용해 앱을 개발할 수 있습니다.
|
||||
|
||||
플러터는 크게 프레임워크와 엔진, 임베더 계층으로 구성되어 있습니다. 프레임워크 계층에는 다트 언어로 개발된 여러 가지 클래스가 있으며 이러한 클래스를 이용해 앱을 개발합니다. 그 다트 언어로 개발된 여러 가지 클래스가 있으며 이러한 클래스를 이용해 앱을 개발합니다. 그리고 엔진 계층은 플러터의 코어를 담당하는데 대부분 C와 C++ 언어로 만들어졌습니다. 여기서는 데이터 통신, 다트 컴파일, 렌더링 그리고 시스템 이벤트 등을 처리합니다.
|
||||
|
||||

|
||||
|
||||
마지막으로 임베더 계층에는 플러터 앱이 크로스 플랫폼에서 동작하도록 플러터 엔진이 렌더링한 결과를 플랫폼별 네이티브 언어로 뷰를 만들어 화면에 보여줍니다. 안드로이드 앱은 자바와 C, C++ 언어로 만들고, iOS 앱은 오브젝티브-C와 오브젝티브-C++ 언어로 만듭니다. 그리고 리눅스와 윈도우 앱은 C++ 언어로 만듭니다. 따라서 다트 언어로 소스 파일만 작성하면 플러터의 각 계층을 거쳐 플랫폼별 앱을 개발할 수 있습니다. 이 가운데 내부적인 처리는 신경 쓰지 않고서 플러터나 다트 언어를 업데이트만 하면 됩니다.
|
||||
|
||||
| 구분 | 리액트 네이티브 | 플러터 |
|
||||
|-----|-------------|------|
|
||||
|개발 주체| 페이스북 | 구글 |
|
||||
| 언어 | 자바스크립트 | 다트 |
|
||||
| 출시 | 2015년도 |2017년도|
|
||||
| 성능 | 빠르지만 네이티브 앱큼은 아님 | 네이티브 앱에 근접한 속도 |
|
||||
|학습 곡선| 높음(네이티브 앱 개발자 기준) | 낮음(네이티브 앱 개발자 기준) |
|
||||
|대표 앱| 페이스북, 인스타그램, 핀터레스트 등 | 알리바바, 구글 애드센스, 리플렉틀리 등 |
|
||||
|장점| - 저변이 넓은 자바스크립트 생태계 | - 다양한 위젯 |
|
||||
| | - 웹 개발자의 접근성 | - 강력한 애니메이션 기능 |
|
||||
| | - npm으로 많은 패키지 이용 가능 | - 블루투스 등 네이티브 하드웨어와의 연결성 |
|
||||
|단점| - 기본 위젯이 부족해 커스텀해서 사용 | - 플러터 SDK로 앱 크기가 큼(네이티브 대비) |
|
||||
| | - 안드로이드/iOS 네이티브 위젯을 이용하기에 OS 판올림에 따른 업데이트 필요 | - 아직 개발 생태계가 성숙하지 않아 빠른 피드백 얻기가 어려움 |
|
||||
| | - 블루투스 등 네이티브 커스텀해 통신하는 부분 개발이 어려움 | - 업데이트 주가가 빠름(분기별) |
|
||||
|최종 목표| 자바스크립트로 웹, 앱, 데스크톱 모든 플랫폼을 개발할 수 있는 통합 솔루션 개발 | 안드로이드, iOS, 웹, 윈도우 10 앱을 같은 코드로 개발할 수 있는 플랫폼 개발|
|
||||
|
||||
## 플러터가 주목받는 이유
|
||||
|
||||
플러터를 상징하는 대표적인 특징 3가지!
|
||||
|
||||
### 하나, 높은 개발 효율
|
||||
|
||||
플러터를 이용하면 안드로이드와 iOS 앱을 동시에 개발할 수 있어서 효율적입니다. 이렇게 개발된 앱은 어떤 운영체제에서도 똑같은 사용자 인터페이스와 사용자 경험(UX, user experience)을 제공합니다. 또한, 플러터의 **핫 리로드** (hot reload) 기능은 소스 수정 후 번거로운 빌드 과정 없이 결과 화면에 바로 표시해 주므로 개발 시간을 줄일 수 있습니다.
|
||||
|
||||
### 둘, 유연한 사용자 인터페이스
|
||||
|
||||
역동적이면서도 유연한 사용자 인터페이스는 플러터의 큰 장점입니다. 다양한 **위젯** (widget)을 제공하므로 사용자 맞춤형 앱을 쉽게 만들 수 있습니다. 만약 원하는 위젯이 없으면 선과 도형으로 직접 그려서 만들 수도 있습니다.
|
||||
또한, 강력한 애니메이션 기능을 제공하여 복잡한 계산식 없이 적은 노력으로 만족스러운 사용자 경험을 줄 수 있습니다. 플로터의 위젯을 활용하면 iOS에서 구글의 머터리얼 디자인이 적용된 앱을 만들거나 반대로 안드로이드에서 iOS스타일 앱을 개발할 수도 있습니다.
|
||||
> 플러터에서는 iOS 스타일의 위젯을 쿠퍼티노(cupertino)라고 부릅니다.
|
||||
|
||||
### 셋, 빠른 속도
|
||||
|
||||
플러터는 전체 화면을 그릴 때 **스키아** (skia) 엔진을 이용합니다. 예를 들어 배경은 노란색으로 하고 그 위에 아이콘을 그린다고 했을 때, 플러터는 노란색을 전체 화면에 칠한 다음 아이콘을 그리는 두 번의 작업을 한 번에 해서 초당 60프레임 이상의 속도로 화면을 갱신합니다.
|
||||
이처럼 빠르고 자연스러운 화면 전환 덕분에 네이티브 앱과 속도 차이를 거의 느낄 수 없습니다.
|
||||
> 스키아는 C++로 개발된 오픈소스 2D 그래픽 엔진으로 플러터뿐만 아니라 크롬, 안드로이드, 파이어폭스, 리브레오피스 등 다양한 플랫폼과 제품에서 사용되고 있습니다.
|
||||
|
||||
---
|
||||
|
||||
### 플러터 갤러리
|
||||
|
||||
[](https://gallery.flutter.dev/#/)
|
||||
|
||||
|
||||
-----
|
||||
|
||||
플러터의 주목도가 최근 로켓성장하는 이유가 다 있는것 같다. <br/>
|
||||
그래서 Dart 언어를 익히면서 플러터 공부를 해보려고 한다. <br/>
|
||||
496
src/content/blog/dev/flutter/2022-04-06-flutter-dart.md
Normal file
496
src/content/blog/dev/flutter/2022-04-06-flutter-dart.md
Normal file
@@ -0,0 +1,496 @@
|
||||
---
|
||||
title: Dart 언어
|
||||
date: 2022-04-06
|
||||
tags: flutter, 개발
|
||||
excerpt: 다트? 알아보자
|
||||
---
|
||||
# 플러터를 위한 필수! 다트를 알자!
|
||||
|
||||
* toc
|
||||
{:toc .large-only}
|
||||
|
||||
플러터는 다트(Dart)라는 프로그래밍 언어로 개발됐습니다. 따라서 플러터를 사용하려면 다트라는 새로운 언어를 알아야 합니다.
|
||||
다트 홈페이지에는 `"자바나 C# 개발자라면 하루면 배울 수 있다."` 라고 소개하는 것처럼 다트는 생각보다 어렵지 않은 언어입니다.
|
||||
|
||||
다트 언어의 철학은 "간단하게 배워서 다양한 플랫폼에 써먹자!" 입니다. 지금 다트를 익혀 두면 모바일 앱뿐만 아니라 서버와 웹 프런트엔드, 데스크톱 앱을 만들 수도 있습니다.
|
||||
다만, 플러터가 주제이므로 다트를 전문적으로 배우지는 않고 핵심 내용만 간략하게 다룰 예정입니다.
|
||||
|
||||
## 다트(Dart)!
|
||||
|
||||
다트는 구글이 Web Front-end 구현을 목적으로 개발한 프로그래밍 언어로 2011년 10월에 공개되었습니다.
|
||||
다트는 마치 카멜레온처럼 어떻게 활용하느냐에 따라 서버나 웹, 앱을 만들 때 사용할 수 있습니다.
|
||||
|
||||
### 다트 언어의 9가지 특징
|
||||
|
||||
다트는 다른 언어와 비교해 9가지 두드러진 특징이 있습니다.
|
||||
|
||||
1. 다트는 main() 함수로 시작합니다.
|
||||
2. 다트는 어디에서나 변수를 선언하고 사용할 수 있습니다.
|
||||
3. 다트에서는 모든 변수가 객체입니다. 그리고 모든 객체는 Object 클래스를 상속받습니다.
|
||||
4. 다트는 자료형이 엄격한 언어입니다. 이 말은 변수에 지정한 자료형과 다른 유형의 값을 저장하면 오류가 발생한다는 의미입니다. 만약 여러 자료형을 허용하려면 dynamic 타입을 이용할 수 있습니다.
|
||||
5. 다트는 제네릭 타입을 이용해 개발할 수 있습니다. 그리고 `List<int>`처럼 int형을 넣을 수도 있고, `List<dynamic>`처럼 다양한 데이터를 넣을 수도 있습니다.
|
||||
6. 다트는 public, protected 같은 키워드가 없습니다. 만약 외부로 노출하고 싶지 않다면 변수나 함수 이름 앞에 언더스코어(_)를 이용해 표시할 수 있습니다.
|
||||
7. 변수나 함수의 시작은 언더스코어 또는 문자열로 시작하고 그 이후에 숫자를 입력할 수 있습니다.
|
||||
8. 다트는 삼항 연산자를 사용할 수 있습니다.
|
||||
9. Null safety를 지원합니다. 이는 2.0에서 새롭게 추가된 기능으로, Null safety를 이용하면 컴파일 전에 널 예외(Null Exception)를 알 수 있으므로 널에 대한 오류가 발생하지 않도록 작업할 수 있습니다.
|
||||
|
||||
#### 8) 삼항연산자
|
||||
|
||||
다음 코드에서 첫 번째 줄을 보면 isPublic이 참이면 "public", 참이 아니면 "private"를 반환하여 visibility에 지정합니다. 두 번째 줄은 매개변수로 전달받은 name이 null이면 "Guest"를 반환하고, 아니면 매개변수로 전달 받은 값을 그대로 반환합니다.
|
||||
```dart
|
||||
var visibility = isPublic ? 'public' : 'private';
|
||||
String playerName(String name) => name ?? 'Guest';
|
||||
```
|
||||
|
||||
### 간단한 코드로 다트의 특징 이해하기
|
||||
|
||||
다트로 만든 프로그램의 시작점은 자바나 C처럼 **main()** 함수입니다.
|
||||
|
||||
```dart
|
||||
// 함수 정의
|
||||
printInteger(int aNumber) {
|
||||
print('The number is $aNumber.'); // 콘솔에 출력
|
||||
}
|
||||
|
||||
// main() 함수에서 시작
|
||||
main() {
|
||||
var number = 42; // 동적 타입 변수 지정
|
||||
printInteger(number); // 함수 호출
|
||||
}
|
||||
```
|
||||
```console
|
||||
결과 >
|
||||
The number is 42.
|
||||
```
|
||||
> `var 키워드로 변수를 선언하면 해당 변수에 저장되는 값의 유형에 따라 자료형이 정해집니다. 이것을 자료형 추론(type inference)이라고 합니다.`
|
||||
|
||||
다트에서는 문자열을 표현할 때는 큰따옴표나 작은따옴표를 이용하는데, 이때 따옴표 안에 **`${표현식}`**과 같은 형태로 사용하면 표현식에 변수를 직접 넣을 수 있습니다.
|
||||
|
||||
| 구분 | 자료형 | 설명 |
|
||||
|:---:|:----:|:-----:|
|
||||
| 숫자 | int | 정수형 숫자 |
|
||||
| | double | 실수형 숫자 |
|
||||
| | num | 정수형 또는 실수형 숫자 |
|
||||
|문자열| String | 텍스트 기반 문자 |
|
||||
|논리형| bool | True나 false |
|
||||
| 자료형 추론 | var | 입력받은 값에 따라 자료형 결정. 한 번 결정된 자료형은 변경 불가 |
|
||||
| | dynamic | 입력받은 값에따라 자료형 결정. 다른 변수 입력하면 자료형 변경 가능 |
|
||||
|
||||
|
||||
### Null safety
|
||||
|
||||
Null safety를 사용하려면 pubspec.yaml 파일의 SDK 환경을 변경해야 합니다. 다음과 같이 2.12.0 버전 이상부터 Null safety를 지원합니다.
|
||||
```yaml
|
||||
enviroment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
```
|
||||
> [버전 표기 이해](https://gahusb.github.io/devlog/web-npmcarrot.html)
|
||||
|
||||
변수를 선언할 때 사용하는 것으로, 자료형 다음에 `?`를 붙이면 Null이 가능하고 붙이지 않으면 Null이 불가능합니다. 그리고 식 다음에 `!`를 붙이면 Null이 아님을 직접 표시합니다.
|
||||
|
||||
```dart
|
||||
int? couldReturnNullButDoesnt() => -3;
|
||||
|
||||
void main() {
|
||||
int? couldBeNullButIsnt = 1; // null로 변경 가능
|
||||
List<int?> listThatCouldHoldNulls = [2, null, 4]; // List의 int에 null 값 포함 가능
|
||||
List<int>? nullsList; // List 자체가 null일 수 있음
|
||||
int a = couldBeNullButIsnt; // null을 넣으면 오류
|
||||
int b = listThatCouldHoldNulls.first; // int b는 ?가 없으므로 오류
|
||||
int b = listThatCouldHoldNulls.first!; // null이 아님을 직접 표시
|
||||
int c = couldReturnNullButDoesnt().abs(); // null일 수도 있으므로 abs()에서 오류
|
||||
int c = couldReturnNullButDoesnt()!.abs(); // null이 아님을 직접 표시
|
||||
|
||||
print('a is $a.');
|
||||
print('b is $b.');
|
||||
print('c is $c.');
|
||||
}
|
||||
```
|
||||
|
||||
Null safety를 사용하는 이유는 프로그램 실행 중 널 예외가 발생하면 프로그램이 중지되는데, 이를 코드 단계에서 구분하여 작성할 수 있도록 하기 위해서입니다. 이런 이유로 요즘 나오는 언어 중에는 Null safety를 제공하는 언어가 많습니다.
|
||||
|
||||
### 다트가 제공하는 키워드
|
||||
|
||||
| 키워드 | - | - | - |
|
||||
| :--: |:--:|:--:|:--:|
|
||||
| abstract | dynamic | implements | show |
|
||||
| as | else | import | static |
|
||||
| assert | enum | in | super |
|
||||
| async | export | interface | switch |
|
||||
| await | extends | is | sync |
|
||||
| break | external | library | this |
|
||||
| case | factory | mixin | throw |
|
||||
| catch | false | new | true |
|
||||
| class | final | null | try |
|
||||
| const | finally | on | typedef |
|
||||
| continue | for | operator | var |
|
||||
| convariant | Function | part | void |
|
||||
| default | get | rethrow | while |
|
||||
| deferred | hide | return | with |
|
||||
| do | if | set | yield |
|
||||
|
||||
> [다트가 제공하는 키워드](https://dart.dev/guides/language/language-tour#keywords)
|
||||
|
||||
---
|
||||
## 비동기 처리 방식
|
||||
|
||||
다트는 비동기 처리를 지원하는 언어입니다. 비동기(asynchronous)란 언제 끝날지 모르는 작업을 기다리지 않고 다음 작업을 처리하게 하는 것을 의미합니다. 만약 비동기를 지원하지 않고 동기(synchronous)로만 처리한다면 어떤 작업이 오래 걸릴 경우 사용자는 실행이 멈춘 것으로 생각하고 프로그램을 종료할 수 있습니다. 일반적으로 네트워크에서 데이터를 가져오거나 데이터베이스 쓰기, 파일 읽기 등의 작업은 상황에 따라 언제 끝날지 알 수 없으므로 비동기로 처리합니다.
|
||||
|
||||
|
||||
 <sup>[1](#footnote_1)</sup>
|
||||
<a name="footnote_1">1</a>: 출처: DEV.to
|
||||
|
||||
### 비동기 프로세스의 작동 방식
|
||||
|
||||
다트는 async와 await 키워드를 이용해 비동기 처리를 구현합니다.
|
||||
1. 함수 이름 뒤, 본문이 시작하는 중괄호 `{` 앞에 async 키워드를 붙여 비동기로 만든다.
|
||||
2. 비동기 함수 안에서 언제 끝날지 모르는 작업 앞에 await 키워드를 붙인다.
|
||||
3. 2번 작업을 마친 결과를 받기 위해 비동기 함수 이름 앞에 Future(값이 여러 개면 Stream) 클래스를 지정한다.
|
||||
|
||||
다음 예시를 보겠습니다.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
checkVersion();
|
||||
print('end process');
|
||||
}
|
||||
Future checkVersion() async {
|
||||
var version = await lookUpVersion();
|
||||
print(version);
|
||||
}
|
||||
|
||||
int lookUpVersion() {
|
||||
return 12;
|
||||
}
|
||||
```
|
||||
|
||||
일반적인 생각으로 코드를 보면 main() 함수에서 제일 먼저 checkVersion() 함수를 호출했으므로 checkVersion() 함수에 있는 lookUpVersion() 함수가 호출되어 12를 전달받아 출력한 다음, 다시 main()으로 돌아와서 'end process'가 출력될 것 같습니다. 하지만 결과는 다음과 같습니다.
|
||||
|
||||
```console
|
||||
실행 결과 >
|
||||
end process
|
||||
12
|
||||
```
|
||||
|
||||
이러한 결과가 나오는 이유는 먼저 checkVersion() 함수를 보면 이름 앞뒤로 Future와 async가 붙었습니다. 이렇게 하면 checkVersion() 함수를 비동기로 만들겠다는 의미입니다. 즉, checkVersion() 함수 안에 await가 붙은 함수를 비동기로 처리한 다음 그 결과는 Future 클래스에 저장해 둘 테니 먼저 checkVersion() 함수를 호출한 main() 함수의 나머지 코드를 모두 실행하라는 의미입니다. 그리고 main() 함수를 모두 실행했으면 그때 Future 클래스에 저장해 둔 결과를 이용해서 checkVersion() 함수의 나머지 코드를 실행합니다.
|
||||
|
||||
앞선 코드에서 lookUpVersion() 함수 앞에 await 키워드가 붙었습니다. await 키워드는 처리를 완료하고 결과를 반환할 때까지 이후 코드의 처리를 멈춥니다. 따라서 lookUpVersion() 함수를 호출해 version 변수에 12가 저장된 다음에야 비로소 print(version) 문으로 이를 출력합니다.
|
||||
이처럼 비동기 함수에서 어떤 결과값이 필요하다면 해당 코드를 await로 지정합니다. 그러면 네트워크 지연 등으로 제대로 된 값을 반환받지 못한 채 이후 과정이 실행되는 것을 방지할 수 있습니다.
|
||||
|
||||
이러한 비동기 처리를 이용하면 지연이 발생하는 동안 애플리케이션이 멈춰 있지 않고 다른 동작을 하게 할 수 있습니다.
|
||||
|
||||
### 비동기 함수가 반환하는 값 활용하기
|
||||
|
||||
비동기 함수가 반환하는 값을 처리하려면 then() 함수를 이용합니다.
|
||||
|
||||
```dart
|
||||
void main() async {
|
||||
await getVersionName().then((value) => {
|
||||
print(value);
|
||||
});
|
||||
print('end process');
|
||||
}
|
||||
|
||||
Future<String> getVersionName() async {
|
||||
var versionName = await lookUpVersionName();
|
||||
return versionName;
|
||||
}
|
||||
|
||||
String lookUpVersionName() {
|
||||
return 'Android Q';
|
||||
}
|
||||
```
|
||||
```console
|
||||
실행 결과 >
|
||||
Android Q
|
||||
end process
|
||||
```
|
||||
|
||||
코드를 보면 Future<String>이라는 반환값을 정해 놓은 getVersionName() 이라는 함수가 있습니다. 이 함수는 async 키워드가 붙었으므로 비동기 함수입니다. 이처럼 비동기 함수가 데이터를 성공적으로 반환하면 호출하는 쪽에서 then() 함수를 이용해 처리할 수 있습니다.
|
||||
|
||||
then() 이외에 error() 함수도 이용할 수 있습니다. error() 함수는 실행 과정에서 오류가 발생했을 때 호출되므로 이를 이용해 예외를 처리할 수 있습니다.
|
||||
|
||||
### 다트와 스레드
|
||||
|
||||
다트는 **하나의 스레드(thread)로 동작**하는 프로그래밍 언어입니다. 그래서 await 키워드를 잘 사용해야 합니다.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
printOne();
|
||||
printTwo();
|
||||
printThree();
|
||||
}
|
||||
|
||||
void printOne() {
|
||||
print('One');
|
||||
}
|
||||
|
||||
void printThree() {
|
||||
print('Three');
|
||||
}
|
||||
|
||||
void printTwo async {
|
||||
Future.delayed(Duration(seconds: 1), () {
|
||||
print('Future!!');
|
||||
});
|
||||
print('Two');
|
||||
}
|
||||
```
|
||||
```console
|
||||
실행 결과 >
|
||||
One
|
||||
Two
|
||||
Three
|
||||
Future!!
|
||||
```
|
||||
'One' 출력 이후에 printTwo() 함수에 진입하면 Future를 1초 지연했으므로 async로 정의한 비동기 함수의 특징에 대해 'Two'가 먼저 출력됩니다. 그리고 'Three'를 출력하고 'Future!!'가 가장 늦게 출력됩니다.
|
||||
|
||||
이때, printTwo() 함수를 다음과 같이 수정한다면?
|
||||
```dart
|
||||
void printTwo() async {
|
||||
await Future.delayed(Duration(seconds: 2), () {
|
||||
print('Future Method');
|
||||
});
|
||||
print('Two');
|
||||
}
|
||||
```
|
||||
Future.delayed() 코드 앞에 await 키워드를 붙였으므로 이후 코드의 실행이 멈춥니다.
|
||||
따라서 printTwo() 함수를 벗어나 main() 함수의 나머지 코드를 모두 실행하고, 그 다음에 await가 붙은 코드부터 차례대로 실행합니다.
|
||||
```console
|
||||
실행 결과 >
|
||||
One
|
||||
Three
|
||||
Future Method
|
||||
Two
|
||||
```
|
||||
이처럼 await 키워드를 이용하면 await가 속한 함수를 호출한 쪽의 프로세스가 끝날 때까지 기다리기 때문에 이를 잘 고려해서 프로그램을 작성해야 합니다.
|
||||
|
||||
---
|
||||
|
||||
## JSON 데이터 주고 받기
|
||||
|
||||
애플리케이션을 개발하다 보면 서버와의 통신이 중요하다는 것을 알게 됩니다. 대부분 앱은 서버와 데이터를 주고 받으며 상호 작용하고 화면에 필요한 데이터를 출력합니다. 이러한 데이터를 교환할 때 가장 많이 쓰는 형식이 **JSON**입니다.
|
||||
직접 문자열 형태나 XML을 이용해 데이터를 주고 받기도 하지만, 가장 편리하면서 파일 크기도 작은 JSON 형식을 주로 이용합니다. 다트에서는 이러한 JSON 통신을 간편하게 이용할 수 있습니다.
|
||||
JSON을 사용하려면 소스에 convert라는 라이브러리를 포함해야 합니다.
|
||||
```dart
|
||||
import 'dart:conver';
|
||||
|
||||
void main() {
|
||||
var jsonString = '''
|
||||
[
|
||||
{"score": 40},
|
||||
{"score": 80}
|
||||
]
|
||||
''';
|
||||
var scores = jsonDecode(jsonString);
|
||||
print(scores is List); // true
|
||||
var firstScore = scores[0];
|
||||
print(firstScore is Map); // true
|
||||
print(firstScore['score'] == 40); // true
|
||||
}
|
||||
```
|
||||
위 코드에서 jsonString 변수에 저장된 데이터가 JSON을 형태의 문자열입니다. 이 데이터를 convert 라이브러리에 있는 jsonDecode() 함수에 전달한 후 그 결과를 scores 변수에 저장했습니다. jsonDecode() 함수는 JSON 형태의 데이터를 dynamic 형식의 리스트로 변환해서 반환해 줍니다.
|
||||
scores 변수가 리스트인지는 True/False로 점검할 수 있습니다.
|
||||
|
||||
그리고 scores 리스트에서 첫 번째 값을 firstScore에 저장합니다. 이 값은 키(key)와 값(values)이 있는 Map 형태입니다. print(firstScore['score'] == 40) 코드는 firstScore 데이터의 score 키에 해당하는 값이 40이라는 것을 나타냅니다. 이처럼 jsonDecode() 함수를 이용하면 서버에서 JSON 데이터를 받아서 사용할 수 있습니다.
|
||||
|
||||
이제 애플리케이션에서 서버로 데이터를 보내는 예도 살펴보겠습니다. 이때는 jsonEncode() 함수를 이용해 JSON 형태로 변환한 데이터를 서버로 보낼 수 있습니다.
|
||||
|
||||
```dart
|
||||
import 'dart:convert';
|
||||
|
||||
void main() {
|
||||
var scores = [
|
||||
{'score': 40},
|
||||
{'score': 80},
|
||||
{'score': 100, 'overtime': true, 'special_guest': null},
|
||||
];
|
||||
|
||||
var jsonText = jsonEncode(scores);
|
||||
print(jsonText ==
|
||||
'[{"score": 40},{"score": 80},'
|
||||
'{"score": 100, "overtime": true,'
|
||||
'"special_guest": null}]'); // true 출력
|
||||
}
|
||||
```
|
||||
|
||||
scores 데이터는 배열로 이루어졌고 각 항목은 score값으로 구성되며 마지막 항목에는 overtime과 special_guest값을 추가했습니다.
|
||||
앞의 코드에서는 {"score": 40}처럼 키에 큰따옴표를 사용해 JSON 데이터임을 표시했고, 지금 코드는 {'score': 40}처럼 작은따옴표를 이용해 변수임을 표시했습니다.
|
||||
|
||||
이 scores 데이터를 인자로 jsonEncode() 함수를 호출하면 키값이 큰따옴표로 묶이고 전체 데이터를 작은따옴표로 한 번 묶어서 JSON 형태의 데이터가 됩니다.
|
||||
이처럼 다트는 간단하게 JSON을 만들고 파싱하여 데이터를 주고받는 기능을 제공합니다.
|
||||
|
||||
---
|
||||
|
||||
## 스트림 통신하기
|
||||
|
||||
애플리케이션을 개발하다 보면 데이터를 순서대로 주고받아야 할 때가 있습니다. 데이터를 순서대로 주고받을 것으로 생각해서 화면을 구성했는데 네트워크나 와이파이 연결이 끊기거나 특정 API 호출이 늦어져 순서가 달라지면 애플리케이션이 원하는 흐름대로 작동하지 않을 수도 있습니다.
|
||||
|
||||
이처럼 순서를 보장받고 싶을 때 스트림(stream)을 이용합니다.
|
||||
스트림은 처음에 넣은 데이터가 꺼낼 때도 가장 먼저 나오는 데이터 구조로 생각할 수 있습니다. 따라서 스트림을 이용하면 데이터를 차례대로 주고받는 코드를 작성할 수 있습니다.
|
||||
|
||||
```dart
|
||||
import 'dart:async';
|
||||
|
||||
Future<int> sumStream(Stream<int> stream) async {
|
||||
var sum = 0;
|
||||
await for (var value in stream) {
|
||||
print('sumStream : $value');
|
||||
sum += value;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
Stream<int> countStream(int to) async* {
|
||||
for (int i = 0; i <= to; i++) {
|
||||
print('countStream : $i');
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
main() async {
|
||||
var stream = countStream(10);
|
||||
var sum = await sumStream(stream);
|
||||
print(sum); // 55
|
||||
}
|
||||
```
|
||||
|
||||
main() 함수를 살펴보면 먼저 countStream(10) 함수를 호출합니다. 이 함수는 async*dhk yield 키워드를 이용해 비동기 함수로 만들었습니다. 이 함수는 for 문을 이용해 1부터 int형 매개변수 to로 전달받은 숫자까지 반복합니다.
|
||||
|
||||
async* 명령어는 앞으로 yield를 이용해 지속적으로 데이터를 전달하겠다는 의미입니다. 위 코드에서 yield는 int형 i를 반환하는데, return은 한 번 반환하면 함수가 끝나지만 yield는 반환 후에도 계속 함수를 유지합니다.
|
||||
|
||||
이렇게 받은 yield값을 인자ㅗ sumStream() 함수를 호출하면 이 값이 전달될 때마다 sum 변수에 누적해서 반환해 줍니다. 그리고 main() 함수에서 이 값을 받아서 출력하면 55가 나옵니다.
|
||||
출력 결과를 보면 함수가 어떤 흐름으로 진행되는지 알 수 있을겁니다.
|
||||
|
||||
```console
|
||||
실행 결과 >
|
||||
countStream : 1
|
||||
sumStream : 1
|
||||
countStream : 2
|
||||
sumStream : 2
|
||||
...
|
||||
countcountStream : 9
|
||||
sumStream : 9
|
||||
countStream : 10
|
||||
sumStream : 10
|
||||
55
|
||||
```
|
||||
이처럼 스트림을 이용하면 데이터를 차례대로 받아서 처리할 수 있습니다.
|
||||
|
||||
아니면 then() 함수를 이용해 스트림 코드를 작성할 수도 있습니다.
|
||||
```dart
|
||||
main() {
|
||||
var stream = Stream.fromIterable([1, 2, 3, 4, 5]);
|
||||
|
||||
// 가장 앞의 데이터 결과: 1
|
||||
stream.first.then((value) => print('first: $value'));
|
||||
// 가장 마지막 데이터의 결과 : 5
|
||||
stream.last.then((value) => print('last: $value'));
|
||||
// 현재 스트림이 비어 있는지 확인: false
|
||||
stream.isEmpty.then((value) => print('isEmpty: $value'));
|
||||
// 전체 길이: 5
|
||||
stream.length.then((value) => print('length: $value'));
|
||||
}
|
||||
```
|
||||
코드를 보면 Stream클래스를 이용해 배열을 하나 만든 후 함수를 이용해서 값을 가져옵니다.
|
||||
그런 다음 then() 함수로 가져다 사용합니다.
|
||||
그런데 이 코드는 그대로 실행하면 오류가 발생합니다. 일단 스트림을 통해 데이터를 사용하면 데이터는 사라지기 때문입니다. 따라서 다음처럼 한 번만 실행하도록 변경해야 합니다.
|
||||
|
||||
```dart
|
||||
main() {
|
||||
var stream = Stream.fromIterable([1, 2, 3, 4, 5]);
|
||||
|
||||
// 가장 마지막 데이터의 결과: 5
|
||||
stream.last.then((value) => print('last: $value'));
|
||||
}
|
||||
```
|
||||
|
||||
스트림은 실시간으로 서버를 살펴보다가 서버에서 데이터가 변경되면 화면을 새로 고침하지 않더라도 자동으로 변경된 데이터가 반영되어야 할 때 사용할 수 있는 유용한 클래스입니다.
|
||||
|
||||
---
|
||||
|
||||
## 다트 프로그램 만들기
|
||||
### 1. 구구단 프로그램 작성하기
|
||||
```dart
|
||||
void main() {
|
||||
for(int i = 0; i <= 9; i++) {
|
||||
for(int j = 0; j <=9; j++) {
|
||||
print('$i * $j = ${i * j}');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 자동차 클래스 구현하기
|
||||
다음과 같은 속성을 포함하는 클래스를 만들어 봅니다.
|
||||
| 이름 | 자료형 | 의미 |
|
||||
|:---:|:----:|:---:|
|
||||
|maxSpeed|int|최고 속도|
|
||||
|price|num|가격|
|
||||
|name|String|이름|
|
||||
|
||||
Car 클래스 안에 saleCar()라는 이름으로 함수를 작성합니다. 이 함수는 자동차 가격을 10% 할인해서 반환합니다. 그리고 main() 함수에서 Car 클래스를 이용해 다음과 같은 속성값으로 3종류의 자동차를 선언합니다. 새로운 객체를 선언할 때 자바에선는 new라는 키워드를 사용하지만 다트에서는 생략할 수 있습니다. 물론 사용해도 무방합니다.
|
||||
|
||||
|maxSpeed|price|name|
|
||||
|:----:|:----:|:----:|
|
||||
|320|100000|BMW|
|
||||
|250|70000|BENZ|
|
||||
|200|80000|FORD|
|
||||
|
||||
BMW를 3번 할인하는 함수를 호출한 뒤에 차량 가격을 출력해봅니다.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
Car bmw = Car(320, 100000, BMW);
|
||||
Car benz = Car(250, 70000, BENZ);
|
||||
Car ford = Car(200, 80000, FORD);
|
||||
|
||||
bmw.saleCar();
|
||||
bmw.saleCar();
|
||||
bmw.saleCar();
|
||||
print(bmw.price);
|
||||
}
|
||||
|
||||
class Car {
|
||||
int maxSpeed;
|
||||
num price;
|
||||
String name;
|
||||
|
||||
Car(int maxSpeed, num price, String name) {
|
||||
this.maxSpeed = maxSpeed;
|
||||
this.price = price;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
num saleCar() {
|
||||
price = price * 0.9;
|
||||
return price;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 로또 번호 생성기
|
||||
무작위 수를 생성하는 랜덤 함수를 이용하려면 dart:math 라이브러리를 사용해야 합니다.
|
||||
<code>import 'dart:math' as math;</code>
|
||||
as math 코드는 import한 dart:math 라이브러리를 math라는 이름으로 사용하겠다는 의미입니다. 이 math를 이용해 6개의 무작위 수를 만드는 로또 번호 생성기를 만들어 봅시다. 만약, 생성한 번호가 중복일 경우에는 다시 생성합니다.
|
||||
|
||||
```dart
|
||||
import 'dart:collection';
|
||||
import 'dart:math' as math;
|
||||
|
||||
void main() {
|
||||
var rand = math.Random();
|
||||
HashSet<int> lotteryNumber HashSet();
|
||||
|
||||
while(lotteryNumber.length < 6) {
|
||||
lotteyNumber.add(rand.nextInt(45) + 1);
|
||||
}
|
||||
print(lotteryNumber);
|
||||
}
|
||||
```
|
||||
```console
|
||||
실행 결과 >
|
||||
{2, 9, 13, 24, 33, 42}
|
||||
```
|
||||
|
||||
HashSet를 사용하려면 dart:collection이라는 라이브러리를 import해야 합니다.
|
||||
47
src/content/blog/dev/sideprojects/2022-11-01-glotto.md
Normal file
47
src/content/blog/dev/sideprojects/2022-11-01-glotto.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: GLotto
|
||||
date: 2022-11-01
|
||||
tags: sideprojects, 개발
|
||||
excerpt: GLotto 추첨기에 대한 아이디어
|
||||
---
|
||||
# 사이드 프로젝트 - GLotto 추첨기
|
||||
## GLotto 개요
|
||||
|
||||
사이드 프로젝트를 시작하면서 제일 먼저 만들어볼 수 있는게 무엇이 있을까 생각해보았다. <br />
|
||||
그래서 생각한게 로또 추첨기를 이전 데이터를 통계적 기반으로 추첨해주는 추첨기를 만들어보면 어떨까라고 생각을 해보았다. <br />
|
||||
|
||||
사실 로또라는게 독립적 수행이기 때문에 이전의 결과가 이후의 결과에 영향을 미치지는 않다는것을 알지만 이것을 통계적으로 누적된 데이터를 활용하여 번호를 뽑아낼 수 있다면 어떨까라고 생각하면서 시작하게 되었다. <br />
|
||||
이전의 데이터를 학습시키고 이 데이터를 기반으로 통계에 근거하여 좀 더 맞아 떨어지는 번호를 생성해내면 어떨까라는게 아이디어의 시작이었다. <br />
|
||||
|
||||
---
|
||||
|
||||
## 설계
|
||||
- 클라이언트 : 안드로이드 어플리케이션
|
||||
- 서버 : 스프링 서버
|
||||
- 데이터베이스 : mariaDB
|
||||
|
||||
간단히 언제든지 들고다니면서 번호를 받을 수 있고, 이것으로 수익성을 낼 수 있는 방법이 뭐가 있을까 고민하다보니 안드로이드에 출시하여 사용해보면 어떨까 생각해보았다. 안드로이드 앱 출시 경험도 해보고, 서버와 데이터베이스를 연동하여 이전 실제 데이터들은 서버에 계속해서 누적하면서 만들어내는 것이다.
|
||||
|
||||
Synology NAS를 활용하여 Spring Server를 구동하고, MariaDB를 구축하여 해당 서비스를 구현하고자 한다.<br />
|
||||
(진작에 이렇게 좀 활용해보지 NAS사고 지금까지 그냥 저장소로만 사용하다니 멍충..)
|
||||
|
||||
---
|
||||
|
||||
## 구현
|
||||
### 클라이언트
|
||||
이 사이드 프로젝트를 제일 먼저 하게된 이유는 앞으로 사이드 프로젝트를 만들기 위해 포토샵을 필수적으로 배울수밖에 없게 되었는데, 가장 간단하게 UI를 뽑아낼 수 있을것 같아서이다.
|
||||
|
||||
그냥 로또 공만 그려서 번호를 뿌려주거나 로또 용지를 그려서 위에 뿌려주면 되니 참 쉽지 않은가? <br />
|
||||
그 외에 다른 앱적인 요소는 Material 3.0을 적극 활용해보려고 한다. <br />
|
||||
|
||||
### 서버
|
||||
사실 제일 걱정되는 부분이 이 부분이었다. 나만의 서버를 구축하여 서비스를 만들어낸다는것 자체가 경험이 많이 부족하고, 이전에 배웠던 Spring 내용은 내 머릿속에 거의 남아있지 않았기 때문이랄까....? <br />
|
||||
그래도 NAS에 Spring war를 올려서 구동할 수 있게끔 제공하고 있어서 다행이었다. <br />
|
||||
서버를 따로 구입하기에는 좀...😕<br />
|
||||
|
||||
### 데이터베이스
|
||||
데이터베이스는 NAS에 MediaWiki를 만들어본 경험이 있었기 때문에 MariaDB를 그대로 사용하기로 했다. 그나마 알고 있는게 SQL문이기 때문에 적극적으로 활용할 수 있고, 사실 추첨 번호만 저장하면 되기 때문에 그리 복잡하지도 않다. <br />
|
||||
CRUD만 구현해두면 앞으로 변동 가능한 부분도 많고, 손 쉽게 활용할 수 있을것 같았다. <br />
|
||||
|
||||
대충 위에서 정리한대로 진행 할 예정이고, 가능하면 구현하면서 새롭게 배운 내용들도 같이 정리하려고 한다. <br />
|
||||
사이드 프로젝트 가보자구우~~~!
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: 사이드 프로젝트 시작?!
|
||||
date: 2022-11-01
|
||||
tags: sideprojects, devlog, ??
|
||||
excerpt: 사이드 프로젝트 시작?!
|
||||
---
|
||||
# 사이드 프로젝트 시작?!
|
||||
## 사이드 프로젝트를 시작하게된 계기
|
||||
사실 사이드 프로젝트를 시작하려고 생각하고 메모 했던 내용은 늘 있었다. 그럼에도 시간이 없다는 핑계로.. 아이디어가 없다는 핑계로.. 처음부터 만들어 볼 여유가 좀처럼 생기지 않았다.<br />
|
||||
그래도 뭔가 작은 프로젝트를 만들기 시작하고, 회사 일에도 여유가 생기고부터 만들어보고 싶었던것들을 리스트화하고 이것을 정리하면서 만들어갈 필요성을 느꼈다.<br />
|
||||
뭐, 이것도 결국에는 내 경험치가 될 것이니까?
|
||||
|
||||
## 사이드 프로젝트 계획
|
||||
- 리스트 업
|
||||
- 실현 가능성을 기반으로 우선순위 정리
|
||||
- 설계
|
||||
- 프로젝트 별 관리
|
||||
|
||||
## 리스트 업
|
||||
중간 중간 생각나는대로 리스트업을 업데이트 할 예정인데 우선 정리한것들은 이렇다.<br />
|
||||
|
||||

|
||||
|
||||
NAS Note에 정리한 아이디어의 일부인데 진작에 좀 활용할껄 너무 늦은거 아니니.. <br />
|
||||
|
||||
## 계획
|
||||
우선적으로 포토샵을 아직 배우는중이기 때문에 UI작업이 제일 까다롭지 않을것 같은 로또앱을 출시해보려고 한다. <br />
|
||||
해당 내용은 다른 포스트로 업로드 할 예정이다.
|
||||
40
src/content/blog/dev/synology/2022-11-23-synology-start.md
Normal file
40
src/content/blog/dev/synology/2022-11-23-synology-start.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: Synology NAS 사용 정리
|
||||
date: 2022-11-23
|
||||
tags: synology, 개발
|
||||
excerpt: 시놀로지 NAS
|
||||
---
|
||||
## 왜 NAS?
|
||||
요즘에는 사람들이 NAS를 많이들 활용하고 있다.<br />
|
||||
NAS는 Network Attached Storage의 약자로 네트워크에 연결된 저장장치로 사람들이 흔히 쓰는 클라우드의 기능을 하고 있는 일종의 서버이다.
|
||||
|
||||
단순히 클라우드 서비스와 같이 저장소로 활용을 많이 하고 있지만, 그 뿐만이 아닌 서버 운영, DB, 공유 등의 다양한 기능을 제공하기도 한다. <br />
|
||||
또한 어디서든 외장하드 같은 것을 번거롭게 들고 다니지 않아도 된다. <br />
|
||||
이 부분이 내가 NAS를 구매한 이유 중 하나이다. <br />
|
||||
|
||||
사실 NAS를 구매한건 22년 05월로 사용한지는 벌써 반년이 지났지만 이제서야 글로 작성하면서 사용했던 방식이나 활용법을 글로 적어보려고 한다...ㅎ <br />
|
||||
구매한 이유는 가족들의 사진을 저장하고 어디서든 공유하면서 사용하기 위한 목적이 우선적이면서 무료 클라우드가 가득 찼기에 매달 요금을 내면서 사용하기 싫었기 때문에 큰 출혈이 있더라고 구매하였다. <br />
|
||||
|
||||
그러면서 더더욱 욕심을 내며 사이드 프로젝트를 위한 서버로도 활용하면 좋을 것 같다는 생각을 했기 때문에 관련 기능을 지원하는 NAS를 찾아보았다. <br />
|
||||
(실제로 이 글을 쓰는 지금 서버로 활용하여 개발한 프로젝트가 2~3개는 된다.)
|
||||
|
||||
## Synology
|
||||
여러가지 장비를 비교하며 찾아보다가 Synology 사의 장비를 구매하기로 결정했다. <br />
|
||||
그 이유는 DSM센터를 통해서 사용하기 편리한 UI를 제공하고 있으며, Synology사의 다양한 내부 패키지 기능을 활용하면 사용 범위성이 무궁무진하기 때문이다. <br />
|
||||
|
||||
현재 구매한 NAS는 DS220+ 2Bay 제품으로, 가격적으로도 그렇고 내가 원하는 기능 정도로만 사용하기에 적당한 제품이다. <br />
|
||||

|
||||
|
||||
내가 NAS를 사용하면서 현재 쓰고 있는 기능들은 아래와 같다. <br />
|
||||
> - DS Photo를 통해 가족들이랑 연결해서 모바일의 사진을 저장하고 공유하는 기능
|
||||
> - Video Station 기능을 통해 나만의 OTT 서비스
|
||||
> - 개인 파일들의 저장
|
||||
> - Note Station을 통한 나만의 노트 정리
|
||||
> - 웹 서비스 구동
|
||||
> - MariaDB 구동
|
||||
> - 안드로이드 앱을 위한 Spring 서버 (사이드 프로젝트)
|
||||
> - Docker 활용하기
|
||||
|
||||
이제와서 봐도 참 알차게 사용한것 같아서 뿌듯하네! <br />
|
||||
|
||||
이렇게 다양하게 활용하면서 익혔던 기능이나 까먹기 쉬운 기능들을 정리하려고 한다. <br />
|
||||
Reference in New Issue
Block a user