OmniAICreator/Qiita-1.07M huggingface dataset:
[初心者向け]VSCode の使い方 for mac(備忘録)

[対象]
**・macユーザー**
・ターミナルコマンドを使えない、または苦手意識がある人
・VisualStudioCodeを使ってみたい人



#1.VSCodeをダウンロードする

[VScode公式サイト]

https://azure.microsoft.com/ja-jp/products/visual-studio-code/

↑このサイトにいく

![スクリーンショット 2021-12-19 17.03.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388076/101a3c10-bfcf-9f07-19fc-f2fcf65d39e6.png)


**[今すぐダウンロード]**を押して、VScodeを開く。

#2.VScodeの設定をする

![スクリーンショット 2021-12-19 17.06.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388076/e2997bbf-8a96-1c50-7252-8b58e4a89d1b.png)


**[拡張機能]**の画面に移動する

![スクリーンショット 2021-12-19 17.08.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388076/c012dbe0-9558-7862-f844-0e2d62a9ccd9.png)


ここで拡張機能を検索する。

:::note info

[Japanese Languegae Pack]と[LiveSrever]
の二つをインストールする。

:::

**[install]**を押したら、機能が追加される。


:::note warn

その後、[command] + [Q]または[Quit Visual Studio Code]を押して再起動する

:::

#####※再起動をすることで、追加した拡張機能が反映される

![スクリーンショット 2021-12-19 17.14.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388076/bd9269e0-3065-f755-c224-84fdbd1d5fe3.png)



-----------------------------------------------------------

###<拡張機能の説明>

####**[Japanese Languegae Pack]**
(日本語でVScodeを使用できる機能)

![スクリーンショット 2021-12-19 17.09.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388076/20fcc5f8-985e-6a49-bd77-639c9aecd626.png)

####**[LiveSrever]**
(編集したファイルをローカルサーバーで見ることができる機能)

![スクリーンショット 2021-12-19 17.10.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388076/ccb8ffe4-8f00-7057-6be4-4f9452754f75.png)

-------------------------------------------------------------


#3.使用したいファイルをドラッグ&ドロップで取り込む

![スクリーンショット 2021-12-19 13.04.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388076/0a1b3a71-31de-4f25-d4fb-861d40977b93.png)


#4.取り込んだファイルを確認できる

![スクリーンショット 2021-12-19 17.19.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388076/30f81105-8be1-1499-6b26-449d550f2362.png)
Tang-Nano-4KでBASIC

#目的
　Tang-Nano-4Kでモニタを動かしているけれど、本人が言うのも何だがショボい。
　此処はグレードアップが必要ではないかと愚考する。
　ゲームマシン化にする案も有るが、FPGA×MCUの有効活用が思いつかない。
　FPGA単体とか、MCUによるエミュなら、事例が手持ちに有るけど。
　などと考えつつ、此処は王道とも言える、BASICの使えるパソコン風味が良いのではないかと。
　　　◆◆◆　よし、Tang-Nano-4KでBASICを動かすぞ　◆◆◆
　Tang-Nano-4Kをパソコンの軛から解放し、BASICマシンとして独立させよう。

#経緯
　BASICマシン化と言っても、Tang-Nano-4KのMCUをネイティブで動かす事を方針とする。
　FPGA上に過去の名機を再現する方法も有るが、Tang-Nano-4Kに相応しいものにしたい。
　となると、Cortex-M3で動くBASICインタープリターが必要だが、そんなものは見つからなかった。
　そこで次候補として、C言語で書かれたものを探した。
　これはそこそこ有ったが俗にTinyと言われるもので、機能は余り無い。。
　まあ、Tinyが決してダメな訳では無く、移植が簡単という大きなメリットが存在する。
　　※TinyはBASICの基本コマンドが動作し、プログラム本体は2KB前後かな。
　　　過去のパソコンでも、8KB～32KB位は有ったのだけどね。
　その中で「[豊四季TinyBASIC](https://vintagechips.wordpress.com/2015/12/06/%E8%B1%8A%E5%9B%9B%E5%AD%A3%E3%82%BF%E3%82%A4%E3%83%8B%E3%83%BCbasic%E7%A2%BA%E5%AE%9A%E7%89%88/)」を選択、下記に示す様に移植性が高いと思われる。
　　◇比較的マニュアルが(解説)がしっかりしている。
　　◇他機種への移植例が存在。
　　◇基本部は、getch(),putch()さえ用意すれば動かせる。
　　◇日本製だし、住んでる場所も近い。※特に意味は無いか。

　BASICが動作したで終わりでは無く、下記を追加しスタンドアロン化する。
　　出力表示をHDMIに行う。
　　キーボードを接続。
　おっとその前に、デバッグコマンドを、BASICに追加しなくっちゃ。
　其れなりに時間が掛かってしまうかな。


#移植作業
　まずWindows版をダウンロード。
　EXEも付いて来るので、ウイルスチェックしてから正常動作を確認。
　　※windows版といってるが、DODS窓が開くコンソールアプリ。
　付属のプロジェクトを開いてコンパイル、動作確認。
　　※visualstudio2017で開けた。Ver14.0->14,1にUPした模様。
　次にMingw環境で動作確認。
　　※Windows環境では余計なものがついてるかも知れないので。
　多少の変更は必要だったが、動作した。
　　※getch(),putch()相当を定義、kbhitは0を返しておく。
　　　この結果、取り合えずrun中のブレークは効かないと言う事になる。
　　※monに戻す事を考慮し、stopコマンド発行で処理を終了させる。
　拡張子が.cppなので、g++でコンパイルして動作正常を確認。
　拡張子を.cに変えて、gccでコンパイルして動作正常を確認。

　動作確認が取れたので、Tang-Nano-4K開発環境にコピー。
　Tang-Nano-4Kの入出ルーチンと結合。
　main.cに呼び出し部を記述。
　コンパイル、書込みして動作正常を確認。
　モニタからBASIC(実はB一文字)で起動する。
　　※[上記変更を加えたものをgithubに上げた。](https://github.com/hi631/tang-nano-4K/tree/master/cm3_mon/USER)
　　　main.c,ttbasic.cの拡張子に、.txtが付いてるので消去して「[前回の](https://qiita.com/hi631/items/eef9072ecb7e452bef76)」と差し替えてね。
　左はSPI通信経由の操作、右はUART経由の操作。
![BASIC_4K.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159764/6de563a2-a44c-5cca-f452-4020950c2ec7.jpeg)

#注意事項
　コンパイル結果を下記に示すが、
　　　 text   　　data    　bss    　　dec    　　hex	　filename
　　　19076   　2356   　1824  　23256   　5ad8	　cm3_mon.elf
　dtaとbssセクションを加算すると、4kBを多少超えている。
　もしかするとmcsに割り当てた4KBでは収まらず、不具合の原因になる恐れも。
　　※mcsに8KB割り当てれば解消するが、デバックが窮屈になるので嫌だな。
　　　BASIC付加の本当の目的はデバッグ支援なので、障害出るまで放置予定(私の環境では)。

#感想
　豊四季TinyBASICの入手から、Tang-Nano-4Kでの動作確認まで、約休日一日の手仕事。
　これは当人の力量などでは無く、豊四季TinyBASICが良く出来ている所為なんだろう。
　公開された作者に感謝します。
　なお、上記記載内容は全て無保証であり、各自の責任においてご利用願います。
git-allでgitを入れるとWEBサーバがインストールされる件

## はじめに
https://git-scm.com/book/en/v2/Getting-Started-Installing-Git に記載してあるとおりにgit-allでインストールしてしまうと意図せずサーバが起動して様々なリスクとなる可能性がある。
インストールされる理由としてはGitWebを使用する為なので、GitWebを使用していない人はgit-allではなくgitのみをインストールしたほうがよい。（同ページ記載のhttps://git-scm.com/download/linux の方法）

## 簡単な検証
### 検証環境
- Ubuntu 20.04

### git-allインストール前の使用中のポート
```bash
$ sudo lsof -i
COMMAND   PID            USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
systemd-r 609 systemd-resolve   12u  IPv4  23743      0t0  UDP localhost:domain 
systemd-r 609 systemd-resolve   13u  IPv4  23744      0t0  TCP localhost:domain (LISTEN)
avahi-dae 644           avahi   12u  IPv4  26641      0t0  UDP *:mdns 
avahi-dae 644           avahi   13u  IPv6  26642      0t0  UDP *:mdns 
avahi-dae 644           avahi   14u  IPv4  26643      0t0  UDP *:52193 
avahi-dae 644           avahi   15u  IPv6  26644      0t0  UDP *:33549 
cupsd     646            root    6u  IPv6  24390      0t0  TCP ip6-localhost:ipp (LISTEN)
cupsd     646            root    7u  IPv4  24391      0t0  TCP localhost:ipp (LISTEN)
NetworkMa 650            root   23u  IPv4  37020      0t0  UDP VirtualBox:bootpc->_gateway:bootps 
cups-brow 730            root    7u  IPv4  27662      0t0  UDP *:631
```

### git-allインストール後の使用中のポート
```bash
$ sudo lsof -i
COMMAND    PID            USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
systemd-r  609 systemd-resolve   12u  IPv4  23743      0t0  UDP localhost:domain 
systemd-r  609 systemd-resolve   13u  IPv4  23744      0t0  TCP localhost:domain (LISTEN)
avahi-dae  644           avahi   12u  IPv4  26641      0t0  UDP *:mdns 
avahi-dae  644           avahi   13u  IPv6  26642      0t0  UDP *:mdns 
avahi-dae  644           avahi   14u  IPv4  26643      0t0  UDP *:52193 
avahi-dae  644           avahi   15u  IPv6  26644      0t0  UDP *:33549 
cupsd      646            root    6u  IPv6  24390      0t0  TCP ip6-localhost:ipp (LISTEN)
cupsd      646            root    7u  IPv4  24391      0t0  TCP localhost:ipp (LISTEN)
NetworkMa  650            root   23u  IPv4  37020      0t0  UDP VirtualBox:bootpc->_gateway:bootps 
cups-brow  730            root    7u  IPv4  27662      0t0  UDP *:631 
apache2   7073            root    4u  IPv6  65832      0t0  TCP *:http (LISTEN)
apache2   7074        www-data    4u  IPv6  65832      0t0  TCP *:http (LISTEN)
apache2   7075        www-data    4u  IPv6  65832      0t0  TCP *:http (LISTEN)
```

## 削除方法
完全に綺麗にしようと思うと追加されたユーザを消したりしないといけないのでいろいろ面倒くさい。調べた方がいれば教えてください。
とりあえずgit-allのアンインストールとWEBサーバを止めておく

```bash
$ sudo apt purge git-all
$ sudo systemctl stop apache2.service
$ sudo systemctl disable apache2.service
$ sudo apt purge apache2
```

## おわりに
`apt purge`や`autoremove`でなんでもかんでもなかったことに出来ると思わないこと！
M1macでGo言語の環境を整えた話

#1.はじめに
MacBook Air(M1,2020)において、Goの学習を始めるために環境を整えようと思います。ちなみにちゃんとした記事は今日が初めてですのでどうか温かい目で見てやってください。お願いします:baby_tone1:
#2.手順１
以下のサイトでダウンロード
https://go.dev/doc/install
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1514897/903ddd4c-2d2e-a729-96c3-6ad8c0dd2d7e.png)
そして特にこだわりがない場合はそのまま続けるを押下。(あれ、キャラクターこんな色だっけ)
#3.手順3
確認
ターミナルを開き
```go version
```
と押して、バージョンが出れば完了です！
#4.参考
https://techbowl.co.jp/techtrain/mypage/railway/5/quest/73

テックトレインをやる上でせっかくなら記事を書き始めようと思いました。
海外ベンチャーで働くエンジニアがよく使うサービス26選

# はじめに
これまでエンジニアとして数年以上働いてきた中で、色々と試行錯誤した結果、よく使うサービスが固まってきたので、まとめてみました。基本的にはWebサービスなので、Mac及びWindowsユーザ両方とも参考になるかと思います。

## 仕事効率化

### Google 系
#### Google Drive
プライベート及び会社それぞれで重宝している。基本的に全てのフォルダやファイルをURLで参照できるため、社内で情報共有する際には便利。

#### Google meet
社内及びクライアントとの打ち合わせで使う。

#### Google calendar
スケジュール管理で使う。

#### Google chat
基本的なコミュニケーションはSlackを使っているが、クライアントによっては本チャットツールを使う。Google chatはエンターで改行ができないため、使用する際には注意が必要。

#### Google spreadsheet
前職ではエクセルを使用していたが、Google spreadsheetの方が圧倒的に便利。編集履歴を簡単に管理できるし、特定の行や列、セルに対してリンクを取得できる。ファイルの加筆修正した際に修正したところのリンクを送ればわかりやすく、大変重宝している。

#### Google Slide
Google spreadsheet同様に編集履歴を簡単に管理できたり、スライドごとにリンクを取得できるため、修正したところの確認をしやすく、大変重宝している。

#### Google 翻訳
基本は後ほど紹介するDeepLを使用しているが、海外メンバーに日本語のWebページを見せる時にページ全体を翻訳したい場合に活用している。

### Chrome拡張系
#### Black menu for Google
Googleのサービスを新しいタブを開くことなく使用できるサービス。これまでGoogle CalendarやGoogle meetの会議URL取得のためにいちいち新しいタブを開いていたが、これを入れてからその必要がなくなり、生産性が爆上げした。

#### ブックマーク管理ツール Papaly
ブックマークをいい感じに管理できるツール。個人的にChrome拡張系のサービスで1番重宝している。

#### Create Link
現在のタブのタイトルとURLを取得できるツール。技術調査した際の参考Webサイトなどをメモする際に活用している。自分の場合はショートカットキーを登録しているが、これにより生産性が爆上げしたので試してほしい。

#### AdBlock
不要な広告の表示をブロックするツール。とりあえず入れておけばOK

#### Grammarly for Chrome
英語の文法やスペルチェックをしてくれるツール。SlackやBacklogで海外メンバーとコミュニケーションする際に英語を使うが、文章が間違っていればこのツールが指摘してくれるので重宝している。

#### Duplicate Tab Helper
同じURLのタブを複数表示しないためのツール。エンジニアとして仕事をしていると色々とタブを開くことが多いが、同じタブを複数開いていることも多いはず。そういう人はこれを入れることで少しはタブの数を減らせる。

### 業務で使うツール
#### DeepL
翻訳する際に使っている。Google翻訳よりも性能が良い気がしている。長文の英語のドキュメントを読む際にはとりあえずDeepLにコピペして、必要なところを読むような使い方をしている。

#### Slack
社内のコミュニケーションツールとして使用している。

#### Backlog
社内のタスク管理ツールとして使用している。

#### SimpleNote
プライベート及び仕事でのメモツールとして利用している。これまでメモツールはいくつも試してきたが「クロスプラットフォームであること」「動作が軽いこと」「ほぼリアルタイムで同期されること」などの理由から今はこのツールを使用している。ただ、画像を貼れなかったりメモをグループ管理できないなど、使いづらい点はいくつかある。

#### Markdown表テーブル作成ツール
その名の通り、Markdownの表テーブルを作成できるツール。BacklogではMardownを使っているが、表で整理したい時にこのツールを使う。わざわざこのツールを使わなくても、良く使うMarkdownをテンプレ化しても良さそう。

#### サクラエディタ
仕事でのテキストエディタとして使っている。無料かつ動作が高速であることから利用している。ショートカットで起動できるようにしているため、ちょっとしたメモを書きたい場合に使用している。

#### Zoom
ビデオ会議ツール。クライアントに応じてGoogle MeetまたはZoomを使い分けている。

### 情報収集
#### Google Discover
(おそらく)過去の検索結果に基づいておすすめ記事を紹介してくれるツール。個人的に割と良質な記事を紹介してくれている気がするので、信頼して使っている。
#### Qiita
エンジニア向け知識共有ツール。LGTM数やストック数などで有益な記事が数値化されているため、時間があればそれらでフィルタリングして良質な記事を読んで勉強している。
#### [Qiiguru](https://qiiguru.netlify.app/)
最近個人で開発した、Qiitaの記事を効率的に検索できるサービス。Qiitaではタイトルや本文などで検索したい場合に`title:2021`や`body:Ruby`のように検索する必要があり、多少面倒だと感じたので、簡単に検索できるツールとして開発した。

### その他
#### マネーフォワード
銀行口座やクレジットカードなど、あらゆる金融系の情報をこれにまとめて管理している。

#### 楽天証券 - 積み立てNISA
約2年前に開始して年40万円を投資している。現状、+20%なので好調。開始してから一切労力を書けずにお金が少しずつ増えていくのでありがたい。
####
Vue初心者がQiita APIを使ってみた

## はじめに
最近Vue.jsを学んだのでせっかくだから何か作ってみたいなと思い
Qiitaの投稿を取得するミニアプリ作ってみました。

Vue.jsの書き方にはあまり自信はありませんが動きますw

## 環境
- Mac
- Laravel 6系
- Vue.js 2系

## 操作方法
入力画面でQiita_IDを入力してJSONを取得します。
![top.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/969534/161a6ac7-28af-e528-3137-021bd84810f5.png)

Qiita_IDというのはこれです！↓
![qiita_id.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/969534/4d168ce7-6b2f-12ba-0fc6-1615963e8913.png)

結果はこちら↓
おー！JSON取得できたー！
![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/969534/fbb5f4e2-b89e-3cf0-38af-957772e2ed9c.png)

過去に投稿した記事のタイトル・URL・LGTM数・投稿日時が取得できます。
(投稿日時の整形はめんどくさくてサボりました)

ソースコードはこんな感じです↓

```.js
<template>
    <div>
        <input v-model="userId">
        <button @click="getQiitaData()">取得開始</button>
        <div v-if="isClick">
            <table class="table table-striped">
                <tr>
                    <th>記事タイトル</th>
                    <th>URL</th>
                    <th>LGTM数</th>
                    <th>投稿日時</th>
                </tr>
                <tr v-for="(item, index) in displayQiitaDataList" :key="index">
                    <td class="text-left">{{ item.title }}</td>
                    <td ><a :href="item.url">{{ item.url }}</a></td>
                    <td>{{ item.likes_count }}</td>
                    <td>{{ item.created_at }}</td>
                </tr>
            </table>
            <div>
                <h3>記事数 {{ totalArticle }}コ</h3>// h3で文字サイズ調整すな←
                <h3>LGTM数 {{ totalLGTM }}コ</h3>
            </div>
        </div>
    </div>
</template>

<script>
import axios from "axios";

export default {
    data() {
        return {
            userId: "",    
            displayQiitaDataList: "",
            totalArticle: 0,
            totalLGTM: 0,
            isClick: false,
        }
    },
    methods: {
        getQiitaData: function() {
            axios.get(`https://qiita.com/api/v2/users/${this.userId}/items?page=1&per_page=100`, {})
            .then(res => {
                let allQiitaData = [];
                allQiitaData = res.data;

                let displayQiitaDataList = [];
                let totalLGTM = 0;
                allQiitaData.forEach(function (item) {
                    displayQiitaDataList.push(item);
                    totalLGTM += item.likes_count;
                })
                // forEach内でthis.displayQiitaDataListへ格納できないので外でやる
                this.displayQiitaDataList = displayQiitaDataList.sort();
                this.totalLGTM = totalLGTM;
                // total記事数を取得
                this.totalArticle = displayQiitaDataList.length;
                // clickによる表示の制御
                this.isClick = true;
            })
        },
    }
}

</script>
```

参考:
Qiita APIの公式 https://qiita.com/api/v2/docs


### おわりに
いろんな人のQiita_IDを(勝手に)入れて、表示できることを確認できました！
Vue.jsの書き方に関して致命的なミスがあればご指摘いただけると嬉しいです。
(それにしても、トータルLGTM数少なくて悲しくなったので、コツコツ頑張ろうと思います〜)
クリエイティブ作品のオンライン展示会訪問

Withコロナ時代のデジタル技術の1つに、オンライン展示会があります。最近、オンライン展示会のイベント広告をよく見る様になりました。


クリエイティブ作品のオンライン展示会の例として、今回はNCU芸術工学部のイベントを紹介します。NCU芸術工学部では年2回、「卓展」と「芸工祭」というクリエイティブ作品の展示会イベントがあります。情報環境デザイン学科だけでなく、産業イノベーションデザイン学科、建築都市デザイン学科の3学科を合わせての企画展示会です。展示会イベントでは、学生がテーマ毎に集まり、自主的に参加して活動している様です。コロナ禍の影響で2020年度から展示場をオンラインに移行し、名前を芸工祭から芸工展に変更して開催したそうです。このため、開催期間の後でも作品鑑賞を楽しむ事が可能です。2021年度の卓展は8月に、芸工展は10月にオンラインで開催されました。オンライン展示会場は、基本的にWEB会場とCLUSTER会場の二本立てで作られています。
 
CLUSTER会場は、2020年度卓展で作成したVR北千種キャンパスを使っている様です。
![211201_1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77235/af290287-d457-6d5b-009e-388e10f5fc00.png)

　まず、2021年度の[卓展のWEBサイト](https://geikou-takuten.com/)を訪問してみました。各グループのクリエイティブ作品の内容と、作品制作ソフト(記載されていた分のみ)を書き出しました。

1. **WAV** / Web&Music=楽曲づくり、**DAWソフト**（具体名は書いていない）
2. **でじでじ** / digital content＝映像、ゲーム作成。**Maya, Unity, Blender**
3. **白-haku-** / photography＝写真を使った作品作り。
4. **aura** / car design＝車のデザイン。
5. **HALO** / product design＝プロダクトデザイン
6. **トバセ！** / product design＝プロダクトデザイン
7. **pag** / product&graphic design＝プロダクト＆グラフィックデザイン
8. **十出版** / graphic design＝グラフィックデザイン, **Illustrator,Photoshop**.
9. **idocoro** / space&interior design=空間&インテリアデザイン
![211201_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77235/cee7b100-51ef-de54-71c9-b1a021376a84.png)

その他に「SHOP」やオリジナルグッズが当たる「抽選会」の会場もありました。SHOPは「SUZURI」プラットフォームを使って、デジタル入稿グッズを販売していました。
![211201_3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77235/d57e1c77-7726-10cc-9a27-a3c03c3cd492.png)

　卓展2021の次は、[芸工展2021のWebサイト](http://geikousai-ncu.com/)を訪問しました。展示場内の各会場をリストアップしてみます。

・**作品展覧会場**（シャッターの部屋、フィルムの部屋、レンズの部屋）
・**芸工展模擬店**（グラフィック部署、ブスいステッカー屋さん、Petal、カフェ部署）
・**2021芸工展CM**（CM、コンセプトムービー、部署長紹介）
・**WEBサイト特集**（jQuery等での制作品展示）
・**コンテンツ班企画**（Unityroomでゲーム作品展示、Clusterで部屋企画展示）
・**ワークショップ**（建築模型、ペーパークラフト（飾り、パッケージ））
・**オンラインカフェ**（マグカップ、ステッカーなどグッズ販売、フードレシピ）
・**ステージ企画**（CM、動画）
![211201_6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77235/f601721f-2376-cbac-7615-00f096ba594f.png)

 
グッズの販売基盤は、卓展の「SUZURI」から芸工展では「BASE」に変更になっていました。
芸工展のカフェに出ていたフード企画「FOODxFIGHT」を紹介します。出来上がったケーキ（サイトに写真有り）がとても美味しそうでした。
![211201_4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77235/2c9157c9-ad32-d8c0-9e53-77404afc7a6f.png)

ワークショップのペーパークラフト（パッケージ）の「クロネコの三角パック」（サイトに写真と型紙有り）も紹介しておきます。出来上がりの可愛さと、折紙展開図のクロネコの首のシュールさとのギャップが面白かったです。

今回はWEBサイトのみの訪問でしたが、CLUSTER会場もまた覗いてみようと思います。皆さんも卓展や芸工祭のオンライン展示会サイトにアクセスして、クリエイティブ作品群を楽しんでみて下さい。
Androidのバックボタンで何も起こらない挙動を実現する方法

## 実装に至ったきっかけ
私は実務でモバイルアプリの開発を担当しているのですが、その際にAndroidのバックボタンの制御がだるいな…と思っていました。
特に認証が絡んだ部分の処理で、「バックボタンで戻られるとちょっとややこしいことになるな…」と感じていました。そこでリーダーに相談したところ、「じゃあバックボタンを押しても何も起こらないようにしよう」と言われたことで、実装に至りました。

## 参考にしたサイト
ReactNativeの公式ドキュメントですね。BackHandlerがあることを実装するまで知りませんでした。

https://reactnative.dev/docs/backhandler

公式ドキュメントには、こう書かれています。

>If one subscription returns true, then subscriptions registered earlier will not be called.
If no subscription returns true or none are registered, it programmatically invokes the default back button functionality to exit the app.

日本語訳すると、「あるサブスクリプションが true を返すと、それ以前に登録されたサブスクリプションは呼び出されません。サブスクリプションが true を返さない場合、または何も登録されていない場合、アプリを終了するためにデフォルトのバックボタン機能をプログラムで呼び出します。」となっています。

つまり、trueを返すと自分で設定したバックハンドラーのアクションが呼び出され、falseを返すとデフォルトのバックハンドラーの挙動(恐らく1つ前に戻る？)となります。

## 実装方法
以下が、公式ドキュメントに載っていたコードです。

```back.js
import React, { useEffect } from "react";
import { BackHandler, Alert } from "react-native";

const App = () => {
  useEffect(() => {
    const backAction = () => {
      Alert.alert("Hold on!", "Are you sure you want to go back?", [
        {
          text: "Cancel",
          onPress: () => null,
          style: "cancel"
        },
        { text: "YES", onPress: () => BackHandler.exitApp() }
      ]);
      return true;
    };

    const backHandler = BackHandler.addEventListener(
      "hardwareBackPress",
      backAction
    );

    return () => backHandler.remove();
  }, []);
```

バックボタンを押した際に、「Hold on!, Are you sure you want to go back?」とアラートを表示するようになっています。キャンセルを押すとnull、つまり何もなし。YESを押すと、BackHandler.exitApp()なので、これはアプリを終了するという挙動になります。

じゃあ、バックボタンを押した時に何も起こらない(アラートも表示させない)ようにするには…と考えた私のコードがこちら。

```back2.js
import React, { useEffect } from "react";
import { BackHandler, Alert } from "react-native";

const App = () => {
  useEffect(() => {
    const backAction = () => {
      return true;
    };

    const backHandler = BackHandler.addEventListener(
      "hardwareBackPress",
      backAction
    );

    return () => backHandler.remove();
  }, []);
```
trueを返さないままだと、falseが返ってデフォルトのバックハンドラーの処理が走ってしまいます。
「じゃあtrueだけ返してみたら良いんじゃね？」と思って書いたコードになります。これで、バックボタンを押しても何も起こらなくなりました。
ちなみに、hardwareBackPressという名前は何でも良いみたいです。

## 最後に
正直自分のやり方が正しいのかはわかりません。有識者の方、ベストプラクティスがあればご教授いただきたいです。
最後までお読みいただきありがとうございました！
nautilusで右クリックメニューから端末を開きたい

nautilusとは、Linuxで使えるファイルマネージャーのこと。

WSLでubuntu20.04LTSをインストールしたときに右クリックで
*端末を開く*メニューを作成するのに苦労したため、メモ書きする。

###　エラーがでるやつ
sudo apt install nautilus-open-terminal

![スクリーンショット 2021-12-19 160454.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405683/673eb7d8-2a18-cc88-1365-343db76ae10f.png)

このようにエラーが生じる。
このコマンドは現在(2021.12.19時点)使えなくなっているよう。

###　正しくインストールできるやつ

sudo apt-get install nautilus-extension-gnome-terminal

![スクリーンショット 2021-12-19 160842.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405683/1a9924e2-dfe9-a746-7cf6-366e4113e381.png)

このコマンドでインストールできると思う。
クロージャーを理解する


<h2>クロージャーの定義</h2>
クロージャは、組み合わされた（囲まれた）関数と、その周囲の状態（レキシカル環境）への参照の組み合わせです。言い換えれば、クロージャは内側の関数から外側の関数スコープへのアクセスを提供します。JavaScript では、関数が作成されるたびにクロージャが作成されます。by_<a href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Closures">MDN</a>

意味がわかりません。順を追ってみていきましょう。

<h3>クロージャーを理解するために理解するべきこと4つ</h3>

<h5>１： JavaScriptは関数の中に関数を定義できる</h5>
```javascript
function hello() {
  function child() {
    console.log("helloの子供だよ")
  }
  return child;
}

console.log(hello());

//コンソール
ƒ child() {
console.log("hello の子供だよ")
}

````

hello()を実行すると戻り値として、child関数を返しているため、コンソールのような結果になります。

普段JavaScriptを書いている方であれば、普通にやっていることですよね。

<h5>２： JavaScriptの関数は変数として持ち運べる</h5>

```javascript
const x = hello()
console.log(x)

//コンソール
ƒ child() {
    console.log("helloの子供だよ")
  }
````

先ほど実行した hello()関数をそのまま変数に入れてしまうことができます。

もちろんコンソールは変数に入れただけですので、同じ結果になります。

この変数 x に入った関数を実行すると

```javascript
x();

//コンソール
helloの子供だよ;
```

といった具合になります。

そして、変数としても運べるということは、引数として利用することもできます。

引数を一つ受け取る関数を用意し、変数に格納された関数を渡してみます。

```javascript
function receive(v) {
  console.log('引数で受け取ったvを実行するよ');
  v();
}

receive(x);

//コンソール
引数で受け取ったvを実行するよ;
helloの子供だよ;
```

ここまでが JavaScript の関数は変数として持ち運べるの説明になります。

<h5>３：JavaScriptの関数は親の関数の変数を扱える</h5>

前提として一方の関数からほかの関数の変数を使うことはできません

```javascript
function suda() {
  const masaki = '菅田将暉';
}

function komatsu() {
  console.log(masaki);
}

komatsu();

//コンソール
エラー;
```

しかし関数が親子関係にある場合、子供の関数は、親の変数を使うことができます。
この場合、親が suda()で子供が komatsu()になります。（本当は夫婦なんですけどね）

※親が子供の変数を使うことはできません

```javascript
function suda() {
  const masaki = '菅田将暉';
  function komatsu() {
    const nana = '小松奈菜';
    console.log(masaki);
    console.log(nana);
  }
  komatsu();
}

suda();

//コンソール
菅田将暉;
小松奈菜;
```

<h5>４：JavaScriptの関数の親は生みの親のことである</h5>

言葉の定義

・ダイナミックスコープ
関数を実行するときの親を親とする

・レキシカルスコープ
自分を定義した関数を親とする

関数の親の決め方には、上記の二種類があって、JavaScript はレキシカルスコープを採用している。

例えば、このような親子関係の関数があります。

```javascript
function suda() {
  const masaki = '菅田将暉';
  function komatsu() {
    const nana = '小松奈菜';
    console.log(masaki);
    console.log(nana);
  }
  return komatsu;
}
```

komatsu()関数の親は suda()関数です。

次に suda()関数を実行し、戻り値である komatsu()関数を変数 suda_return に格納します

```javascript
function receive(v) {
  const masaki = '相葉雅紀';
  v();
}

const suda_return = suda();

receive(suda_return);

//コンソール
菅田将暉;
小松奈菜;
```

レキシカルスコープの定義を見ると「自分を定義した関数を親とする」とあります。

receive()関数の引数 v()関数の中身を見ると

```javascript
function receive(v) {
  const masaki = '相葉雅紀';
  function komatsu() {
    const nana = '小松奈菜';
    console.log(masaki);
    console.log(nana);
  }
  komatus();
}
```

となっており変数 masaki には相葉雅紀が入りそうですが、これは自分を定義した関数ではありません。

従って、コンソールには komatus()関数を定義した親にあった masaki の変数に入っていた菅田将暉が現れるわけです。

そして、初めの意味わからなかったクロージャを簡単にした説明は、

<strong>「関数と生みの親（祖先含め全員）が持っている変数を合わせてクロージャと呼ぶ」</strong>

----------追記予定----------

<h3>具体的なクロージャーの使いかた</h3>
【GCP / Identity-Aware Proxy】社内からVM/GCEへRDP接続できない時に・・

#0.はじめに

これはまだ私がくらうど？ジーシーピー？？だった時、業務の一環でGCPにWindowsサーバー立てなきゃいけなくなって迷走したゆるーいお話です。(今もそこから毛が生えたレベルですが・・)。

以下はポエム風に書いてますので**忙しい方向けに結論だけ先に書いておきます。**


:::note
ファイアウォールで社内から外部へリモートデスクトップ接続(RDP:3389番)が許可されてない場合は「Identity-Aware Proxy」をGCPで有効にして、「IAP Desktop」を使用してHTTP経由でセキュアにアクセスしましょう！
:::

#1.当時のぼくのレベル

なんとなくクラウドに関わりたいなぁ・・
自社の人があまり取り組んでないことに手を出すことで価値を高めたいなぁ・・
JAVAとかのプログラム言語は多少触れるけど(Web系含めた)インフラ知識はほとんどなし。
ミーハーと言われても仕方ない状態。

そんな時にようやくクラウドに関われることに！！
聞いたことあるAWSかな？ Azureかな？　・・・　え？じーしーぴーですか？？？

#2.迷走の経緯

### 2-1.初めてのVMインスタンス構築

まずは自分のWindows環境をクラウドに再現するということでGCEにWindowsサーバーを立てました。とりあえずクイックスタートなるものがあったので、中身の理解は追いつかないが実行。

https://cloud.google.com/compute/docs/quickstart-windows?hl=ja&skip_cache=false

おぉ、とりあえずインスタンスができたっぽいぞ！後はRDPで繋げば終わり。楽勝でしょ！

![スクリーンショット 2021-12-09 075551.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373060/8ac41276-5583-a40b-6308-6ed7a69c823d.png)



が・・・ダメッ！！！

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373060/c9d0cb20-921a-b24e-18d9-075042b9383d.png" width=60%>

### 2-2.悪戦苦闘

https://cloud.google.com/compute/docs/troubleshooting/troubleshooting-rdp?hl=ja

ふむふむ。。なるほど3389を許可すればいいんやな

```:CloudShell(GCPターミナル)
gcloud compute firewall-rules create allow-rdp --allow tcp:3389
```

![スクリーンショット 2021-12-09 100125.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373060/9f5d7646-7bf3-6fd0-2972-fde70617b7e3.png)

が・・・ダメッ！！！
これは会社側の問題な気がしてきたので、社内のインフラに詳しい人に聞いてみたが・・・
「社内から80番(HTTP)、443番(SSH)しか許可されてないからなんじゃない？」
「RDPに変換すれば？」と言われたので色々調べて「RDPゲートウェイ」を使用すればいいことがわかった。

が、しかしこれは1回普通にWindowsサーバーに入らないと設定できないらしいことが判明。
その1回も無理なのでこの手法も断念。。
※素直に管理部門に3389開けてもらえばいいんですが、それも色々ありまして・・

でその後もネットの海をさまよい、色々試してみたがどれもNGだった。


#3.解決へ

たまたま社内からAzureでRDP接続したことある人がいたので、アドバイス求めたら「Azure Bastionってのでいけたよ！」とのこと。調べると「**RDPなどを使用しなくても、Web上のAzurePortalに接続し、そこから仮想マシンを操作する機能**」だと判明した。なるほど、これのGCPバージョンを探せばいいのか！というのがわかり、無事に解決に至るのであった。。。

<手順>
①まずはGCP(のセキュリティ)から[Identity-Aware Proxy](https://cloud.google.com/iap)を有効にする
②[IAP Desktop](https://github.com/GoogleCloudPlatform/iap-desktop/)をインストール
③IAP Desktopで対象プロジェクト/VMを選択し、Connectを押して接続を実行する

<details><summary>以下は簡単な説明。</summary><div>

ユーザーからはHTTP通信をCloud IAPに飛ばし、RDPをトンネリングすることでVMに接続できる。ユーザー側は[IAP Desktop](https://github.com/GoogleCloudPlatform/iap-desktop/)というWindows アプリケーションを通してセキュアにアクセスすることができる。



<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373060/a13117a6-2247-2368-977b-3a98088ab67e.png" width=100%>

</div></details>


#4.おわりに
知識が無かったので色々苦労してしまったが、社内からHTTPしか許可されてないケースはかなり多数あると思う。

Azureでは「Azure AD Application Proxy(Azure Bastion)」、AWSでは「Amazon WorkLink(たぶん)」、GCPでは「Identity-Aware Proxy(IAP Desktop)」の機能を使うことで社内からHTTP経由でVMへRDP接続ができることがわかったので、もしGCP/クラウド初心者で同じように困っている方の参考になれば幸いです。

##.色々参考にした記事たち
https://www.apps-gcp.com/gce-wininstance-fw-rdp/
https://cloud.google.com/iap/docs/using-tcp-forwarding
https://cloud.google.com/blog/ja/products/identity-security/zero-trust-remote-access-for-windows-vms
https://cloud.google.com/iap/docs/tcp-forwarding-overview
マルウェアのアンパックについて

Windowsマルウェアを対象とする。
#パックされているかの確認
###Detect It Easyで確認
![スクリーンショット 2021-12-19 14.12.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274361/ab2a7c30-3975-82a9-fb2c-a249acc60cb1.png)
下の方にパッカーなどの情報が表示される。
###pestudioのfunctionsの部分を確認
![スクリーンショット 2021-12-19 14.15.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274361/709a7a75-0d85-83ae-1457-335916e94c1d.png)
functionsの数が少ないとパックされている可能性がある。
###pestudioのsectionsの部分を確認
![スクリーンショット 2021-12-19 14.15.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274361/c751d848-a19e-a4cc-2252-96649906c522.png)
パックされていない場合はraw-sizeとvirtual-sizeはほぼ同じ値になっているはずだが、
上の画像では、raw-sizeの値が0x00000000となっている。
また、UPX0などの見慣れないセクションがあるため、パックされている可能性がある。
#アンパックする
###自動でアンパック
自動でアンパックしてくれるツールがあるので、それを試してみる価値あり(できないこともある)

https://github.com/hasherezade/mal_unpack

https://github.com/hasherezade/pe-sieve

https://github.com/hasherezade/hollows_hunter
###手動でアンパック
####1. 本来のエントリポイントの特定
![スクリーンショット 2021-12-19 14.15.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274361/d73a72ed-a90b-5aa3-13b6-6a0d1e8ad3eb.png)
UPX1にentry-pointがあるため、ここからアンパックが実行される。
Ghidraで確認してみる。
![スクリーンショット 2021-12-19 16.02.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274361/258afac5-bf62-d89f-ebef-a09223e76fd8.png)
上の画像は、アンパックのエントリポイントの部分を表示している。
![スクリーンショット 2021-12-19 15.00.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274361/b71a1b8c-a5ab-c8dc-10fd-d8aa29b3a49c.png)
![スクリーンショット 2021-12-19 15.01.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274361/40c16e03-03f5-e11b-5ad3-3c7582c038d9.png)
Function Graphの一部を写しているが、jmpの先が書かれていない部分がある。
ここがアンパックの終わり(本来のエントリポイントへのjmp)の可能性がある。
今回は004595e0が元のエントリポイントである。
####2. デバッガでメモリダンプ
x64dbgを使う。
まず、004595e0まで実行する。
![スクリーンショット 2021-12-19 15.29.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274361/1ffe517a-9669-28e5-b7d2-d9e556afcc00.png)
この状態でScyllaを使う。
何もしないで一旦ダンプする。
その後インポートテーブルを修正してパッチを適応する。
![スクリーンショット 2021-12-19 15.37.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274361/73806b08-2832-e65a-fa2a-dfbab212059e.png)
これでアンパックは完了。
####before
![スクリーンショット 2021-12-19 14.15.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274361/e18c9f40-ebe1-ea13-4d74-ccb875bae865.png)
####after
![スクリーンショット 2021-12-19 15.47.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274361/a3af5942-a18b-fd2c-449c-d915e8d8bfa0.png)
functionsの数が増えていることがわかる。
なお、アンパック後の検体のhash値がアンパック前と変わるため、注意が必要。
尊敬する人について


[ジャンルなしオンラインもくもく会 Advent Calendar 2021](https://adventar.org/calendars/6803)　19日目は
Qiita初投稿のおもちが担当します！！

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# はじめに
弊社は毎週の朝会で、1分間のLTをしています。
今週の議題は「尊敬する人」で私が担当となったので、真面目に考えてみました。
その時の気づきになります。
<a id="#Chapter2"></a>
# 選んだ尊敬する人
私の尊敬する人は「スティーブ・ウォズニアック」にしました。
アップルをスティーブジョブスと共に創業した、偉大なるエンジニアです。

理由として
・エンジニアとしてレジェンド級の技術力
・なにより性格が温厚で謙虚であり、優しく人格者

他の候補者として
・著名な歴史上の人物、戦国武将など
・まつもと ゆきひろさん（会社でRubyを使用しているので）
・その他、著名なエンジニア

社内での発表ということで「エンジニア」という自分のアイデンティティが活かせる、
著名なエンジニアに的を絞りました。

色々悩みましたが、「すごいエンジニアなのに温厚で優しい」という点が
自分の中で大切にしていることだと気づいたので、ウォズニアックにしました！
また、面接みたいに肩肘張って「成果をあげる必要がある発表」ではない点も大きかったです。
等身大の自分の憧れを素直に話しました。

定番として「両親」「学校の恩師」「会社でお世話になった人」を尊敬する人として挙げられますが、
会社でセンチメンタルになるのは、自分的にはちょっと違う感じがして候補から外れました。

<a id="#Chapter3"></a>
# 話した内容
発表内容を箇条書きで簡略化すると

・尊敬する人はアップルを「スティーブ・ジョブス」と共に創業した、エンジニアの「スティーブ・ウォズニアック」
・アップルの社員番号1番
・中学生で簡単なパソコンを作ったり、アップルの初期のパソコンをほぼ1人で作ったりした、大天才のエンジニア
・それでいて性格が温厚で謙虚であり人格者であると言われている
・自分が持っているアップルのストックオプションの権利を「ウォズプラン」として、権利を持ってない社員に配ったり、５倍の年収でヘッドハンティングされても断っている
・そんな器の大きく優しいエンジニアに私もなりたい

１分間しか話す時間はないので、かなり要約して詰め込みました。

<a id="#Chapter4"></a>
# 反省点
面接などでは、当たり前ですが単純に「尊敬する人」を言えばいいわけではなく


・「なぜ尊敬しているのか」を、ストーリーテリングなど使って分かりやすく伝える
・そうした思いから、どのように自分の行動や思想が変わったか、そしてどんな成果をあげたか
・ユーモアを交えて、聞き手に退屈させないネタを含める

などを考えなくてはいけません。

特にユーモアを交えるのは、とても大切だと思いました。
他の発表者を見ても、笑いが起きた発表の方が、聞き手にとって満足度が高そうに感じています。

そういう点では、今回の発表にはユーモアを入れなかったので、少し自分本意な発表となりました。
ただ、発表する前からそれは感じていて、周囲の感想が気になりました。

<a id="#Chapter5"></a>
# 発表を聞いた周囲の感想
「スティーブ・ウォズニアック」を知らなかった、という感想が多くありました。
知らない人を知れて良かったというポジティブな意見もありました。

自分本意な発表であったため、ネガティブな意見を受ける心の準備をしていたのですが、そのような負の感情を抱く人はいなかったようです。
1分間なので、そもそもそんな感情は湧かないのか、大人な心持ちをしてくれたのか分かりませんが
あまり考えすぎなくてもいいのかなと思いました。
何かの役に立ったり、記憶に残る発表でないとダメと思っていたのですが、
そのような発表の方が珍しく、ダメで元々でどんどん発表して経験を積んだ方がいいのかもしれません。

<a id="#Chapter6"></a>
# 最後に

皆さんの「尊敬する人」はどなたでしょうか？
Qiitaにはコメント機能があるようなので、ぜひコメントで教えてください！
記事を見て頂きありがとうございました！
snapd で certbot ssl letsencrypt


# snapd をインスコ
https://www.server-world.info/query?os=CentOS_Stream_8&p=snappy&f=1


```

dnf --enablerepo=epel -y install snapd
ln -s /var/lib/snapd/snap /snap
echo 'export PATH=$PATH:/var/lib/snapd/snap/bin' > /etc/profile.d/snap.sh
systemctl enable --now snapd.service snapd.socket

snap install core
snap refresh core

```

`snapd まで ok`



# centbot をインスコ

```

snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot
ls -la /usr/bin/certbot
ls -la /snap/bin/certbot

certbot --version//バージョンが表示されてりゃOK

```

#certbot 実行

以下をやって設問に答えていこう。

```

certbot --nginx

```

すると勝手に default.conf が変わって SSL 接続が可能になる。


参考までに。

```

server {
    server_name your.club;
    root   /var/www/html/yamada/;
    index index.php index.html index.htm;



         location ~ \.php$ {

       fastcgi_pass   unix:/var/run/php-fpm/www.sock;

       fastcgi_index  index.php;

       fastcgi_param  SCRIPT_FILENAME /var/www/html/yamada$fastcgi_script_name;
        include        fastcgi_params;

 }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/your.club/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/your.club/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = twikon.club) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    server_name your.club;
    return 404; # managed by Certbot


}



```


#自動更新の設定


一応テスト

```

certbot renew --dry-run

```

まぁ、動きそうかな？


cron用 ログ・ファイルを作っておいて


```/var/log/mycron.log

tekitou


```


#cron を設定

毎月 1日の1時0分に実行

```cron.txt

# SSL自動更新
0 1 1 * * certbot renew --renew-hook "systemctl restart nginx" > /var/log/mycron.log 2>&1


```

```

crontab cron.txt

```

これでOK！
TypeScriptで型を再エクスポートしたらエラーになった話

## まえがき
TypeScript でモジュール分割しようとした際に困ったことがあり、解決までに時間がかかったので忘れないように記事に残しておきます。TypeScript が得意な方であれば、もしかすると普通に知ってる話かもしれませんが、検索しても情報がなく、私のように躓く人もいそうなので参考になればと思います。

## 問題の起きたコード
まず初めに、問題の起きたコードを抜粋して載せておきます。問題に関わらない部分は簡略化して記載します。

見ればわかると思いますが、ごく普通のモジュール分割です。`myInterface`という型 と `myConfig`という値を定義して、`index.ts` で再エクスポートするようなありふれたパターンのコードです。

```ts:config/myInterface.ts
export interface myInterface {
  foo: number:
  bar: string;
}
```

```ts:config/myConfig.ts
export const myConfig = {
  lang: 'ja-JP',
  timezone: 'Asia/Tokyo',
}
```

```ts:config/index.ts
export { myInterface } from './myInterface';
export { myConfig } from './myConfig';
```

## 問題発生
特になんの変哲もないコードなのでメイン処理からインポートして動かしてみました。

```ts:main.ts
import { myConfig } from './config';
//メイン処理...
```

すると、あろうことか、SyntaxError が発生してしましました。😭

```
Uncaught SyntaxError: The requested module '/src/config/myInterface.ts' does not provide an export named 'myInterface'
```

しかも、メイン処理では使っていないはずの `myInterface` でエラーが発生しています。

## 調査と不可解な現象
使っていないはずの型でエラーが起きているので、一旦 `myConfig` を使用している箇所をコメントアウトして、`myInterface` をインポートしてみます。

```ts:main.ts
import { myInterface } from './ｃonfig';
//メイン処理...（調査用に`myConfig`をコメントアウト）
```

すると、えっ？ あれっ？ エラーがでない、、、？？？

`myInterface` で SyntaxError が出てるはずなのに `myInterface`を使うとエラーが出ない？？？ なんで、、、？

## 原因
これには、すごく悩まされました。なにせ、エラーが起きているはずのコードをインポートするとエラーが起きなくなるのです。

５時間位悩んだ結果、なんとか原因らしきものを見つけました。それは TypeScript の次の仕様によるものだったのです。

> 型のインポートはトランスパイル時に除去される

この仕様は、TypeScript の [isolatedModules](https://www.typescriptlang.org/tsconfig#isolatedModules) の箇所に書かれていました。

## 何が起きたか
まずは、原因調査のために書き換えた後の `main.ts` でエラーが起きなかった理由について。

`import { myInterface } from './myConfig'` は型のインポートなので、変数の型チェックのみに使用されて、トランスパイル後のコードからは除去されます。そのため、実行時には `index.ts` は読み込まれません。そもそも、問題のコードを読み込んでいないので、エラーも起きません。

次に、書き換え前の `main.ts` でエラーが発生した理由について。

書き換え前では、`import { myConfig } from './config'` となっていて、これは値のインポートなのでトランスパイル後でも除去されずに残っています。すると、実行時に `index.ts` が読み込まれて、各モジュールの再エクスポートを参照して、実装ファイルを読み込む、はずだったのですが、、、

なんと、`index.ts` 何に書かれている `export { myInterface } from './myInterface'` が除去されずに残っているではないですか！ しかも、`myInterface` 自体は型なので、トランスパイル後のコードには存在しません。

つまり、型のインポートは除去されるのですが、型の再エクスポートは除去されずに残ってしまうため SyntaxError が発生してしまったのでした。

## 解決策
この解決策は、TypeScript 3.8 のドキュメントに書かれていました。

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export

つまり、`index.ts` はこうなります。

```ts:config/index.ts
export type { myInterface } from './myInterface'; // <-typeをつける
export { myConfig } from './myConfig';
```

`export` の後ろに `type` を付けることで、型のみのエクスポートであることを明示的に指定して、トランスパイル後の出力結果から除去することができます。

そもそもの話、`export { myInterface } from './myInterface'` が自動的に除去されれば問題ない話なのですが、TypeScript（あるいは、JavaScritp）のコードには、エクスポートしているモジュール以外にも副作用を伴うコードが書かれている可能性があります。副作用を伴うコードを勝手に除去してしまうと、コードが実行されないため逆の問題が生じます。そのため、再エクスポート時は自動除去ルールが適用されていないのではと推測されます。

## おしまい
今回の問題はかなり悩まされました。構文はあってるように見えるし、検索しても全然情報がでてこないし、使ってない側のコードでエラーが出るし、、、

もし、私のように悩んでいる方がいましたら、参考にしていただければと思います。それでは。
環境構築 ～WindowsにShopify-CLIをインストールする～

WindowsのPCにShopify-CLIをインストールするまでの工程を解説します。
今までのShopifyのテーマ改修では[Theme Kit](https://github.com/Shopify/themekit)が使用されていましたが、
Online Store 2.0からShopify-CLIを使用する必要があります。

# 環境構築
Shopify-CLIをインストールする前に

- Ruby(バージョン2.7以上)
- Git

が必要になります。すでにインストールされているのかコマンドプロント/ターミナルで確認してください。
不足している場合はインストールします。

## 確認方法
```Linux
$ ruby -v
```
```Linux
$ git --version
```
インストールされている場合は、バージョン番号が返ってきます。

```linux
$ ruby -v
ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x64-mingw32]
```
```linux
$ git --version
git version 2.34.0.windows.1
```

::: note warm
初期状態のWindowsPCは、Rubyが入っていないことが多いみたいです。
:::

# Ruby Git 入っていない場合(Windows)
### Rubyをダウンロードします。
Shopify CLIを使用するためには、前提条件としてRuby2.7以上が必要です。
Rubyは以下のリンクからインストールしてください。
https://rubyinstaller.org/downloads/

:::note alert
必ず、DevKit付きをダウンロードしてください。gemが管理できなくてエラーが発生します。
:::

インストール内容は、標準のままで問題ありません。特別な設定は必要ありませんでした。
![](https://storage.googleapis.com/zenn-user-upload/826af05b06bd-20211120.jpg)

## Gitをダウンロードします。
Gitもダウンロードしておいてください。
https://gitforwindows.org/

もう一度バージョンのコマンドで確認してみてください。
# Shopify-CLIをダウンロードします。
コマンドプロンプトを開き、以下のコマンドを実行します。

RubyGem.orgでインストール(Windows)

```Linux
$ gem install shopify-cli
```
インストールに成功していたら、以下のコマンドでバージョンが出るか確認してください。

```Linux
$ shopify version
2.7.1
```


# 補足
この続きにCLIの使い方なども以下の書籍で公開しています。無料なのでもしよければ確認してみてください。
続き　Part 2 環境構築 ～Shopify-CLIの使い方からGithubの連携～
https://zenn.dev/shiba/books/e6530d44576194
![39e8941fe8dfc147790f2db9fc8b02a7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132133/dcebd5a2-c3a4-8d51-1299-de7e1d7b7c66.png)
【GAS】バイト先の塾でおすすめコース提案BOTを開発した話

# はじめに

私がバイトしている塾である日塾長がこんなことを仰り始めました。

塾長「見込み顧客向けにWeb上でアンケートを取ってその結果を元にお勧めのコースを判定できないかなぁ」
私「(大分ふわっとしとる要件やなぁ)な、なるほど...具体的には何か考えていらっしゃいますか？」
塾長「塾の公式HPにリンクを貼ってそこから診断フォームに飛ばしたいんだよね〜」
私「(Google Formの回答結果を受け取るバックエンドがあればいけるな)いけると思いますよ」
塾長「じゃあ1~2日くらいでいける？」
私「」

その後色々あり、とりあえず1週間ほど工数をもらって私は「塾の見込み顧客向けおすすめコース提案BOT」を開発することにしました。

# 概要

具体的にやりたいことは以下のようなものです。

- 見込み顧客が回答する部分はGoogle Formで良い。
- 全ての設問は選択肢式とし、それぞれの選択肢にポイントを割り当てる(後述)
- 回答者の回答に基づいてお勧めコースを判定し、結果をGmailで返す

# 技術選定・設計

今回はGoogle Formの結果を受け取る部分をGoogle Apps ScriptというGoogleの提供するサーバレスサービスに任せて以下のような構成を取ることにしました。

1. Google Formで回答させる
1. その回答をGoogle Apps Scriptで受けとる
1. Google Apps Script内で回答に基づいておすすめのコースを判定する
1. Google Apps Scriptの最後でおすすめコースごとにおすすめコースとそのアピールコンテンツを含んだGmailを送る(送信するにはGmailのSDKを使ってあげれば良いですね)

# やったこと

バックエンドをGoogle Apps Scriptで書けることになったので「どのようにお勧めコースを判定するかというアルゴリズム」を考えなくてはなりません。性格診断によくあるような「はい/いいえ」を選んでいくようなものや、各選択肢を選ぶごとに各コースに加点し最も多くの得点を獲得したコースを採用する...などを考えました。実装の複雑さや質問の追加の容易さを鑑みて結局後者で行くことにしました。以下のようなものです。

例題1:あなたは通塾に不安を感じますか？

| 回答 | 加点方式 |
|:-:|:-:|
| はい  |  オンライン授業コースに1点 |
| いいえ  |  加点を行わない |

例題2:今の学習課題を教えてください

| 回答 | 加点方式 |
|:-:|:-:|
| 英語が苦手  |  英数国コースに1点 |
| 数学が苦手  |  英数国コースに1点 |
| 理社が苦手  |  理社コースに1点 |

例題3:志望校がもし決まっていれば教えてください

| 回答 | 加点方式 |
|:-:|:-:|
|  私立専願 |  (理社は必要ないので)理社コースに1点 |
| 公立中堅校  |  通常の5科目コースに1点 |
| 公立進学校  |  ハイレベル5科目コースに1点 |

このように質問を繰り返して最終的に最多得点を獲得したコースをお勧めコースとして返すようにしました。また、この方法であれば新たに質問や選択肢や対象コースを追加・削除することも容易です。

# 果たして使ってもらえるのか!?

塾長によれば見込み顧客に返すメールに添付する各コースの紹介動画などが出来次第、私の所属している校舎で試験的に導入されることになりそうです。(いやアピール用のコンテンツ作ってなかったんかーい💦)
ただお勧めコースとともにそれをアピールするためのコンテンツも抱き合わせで送らないことにはあまり意味がないでしょうから仕方がありません。もし今後おすすめコース判定アルゴリズムやおすすめコース提示までの設計に改善があれば別記事にしたいと思います。

# 余談

実は開発当初「結果の提示にはGmailで回答の入力にはGoogle Formかぁ...別に悪くは無いんだけど画面遷移が多くてイケてないなぁ」と思いました。そこで塾の公式HP上にチャットbotを設置して、回答の入力も結果の提示もそこでやればええやん！と考えました。そして実際に初めのうちはGCPのDialogflowを用いてチャットbotを開発してみたのですが...オーバースペックすぎました。DiglogflowとはGCPの提供する自然言語処理を備えたAIを用いてチャットbotを簡単に作れる...というサービスなのですがAIを必要としない今回の要件では明らかに高機能すぎです。ということでDialogflow自体は面白そうだと思ったのですが、これはまたの機会に譲ることにしました。

#おわりに

今回Google Apps Scriptは初めて触ってみましたが比較的小規模かつ他のGoogleサービスとの連携が前提となるようなアプリを構築するのであれば十分有用なのではないかなと感じました。GmailやGoogle DriveなどのGoogle関連サービスの各種SDKが予め組み込まれていますしドキュメントも読み易くまとまっていたので、初学者にも優しいです。特にGoogle Apps Script自体がほぼJavaScriptみたいなものなのでJavaScriptの基本を習得した人で何か作ってみたいという人にはおすすめですね。

ここまで読んで下さってありがとうございます！Happy Coding!
AWS WAFでLog4Sellを防御してみる

# はじめに

AWS WAFでのLog4Sell防御についてです。
設定は、マネージドルールを追加するだけなので、少し落穂拾い的なことを。

# 設定

現状、Log4Shellのルールは、AWS-AWSManagedRulesKnownBadInputsRuleSetに含まれています。
AWSManagedRulesKnownBadInputsRuleSetとは、AWSが提供する、攻撃性のあるインプット情報を判定するルールセットになります。
`Log4JRCE`というルールが、Log4Sellのためのものになります。

今回のLog4Sellの流行を受けて、至急緩和策を入れたいけれど、WAFでの誤検知も怖い…
Log4Sellだけでも防げないかといった要望もあると思います。

そのような時は、Log4JRCEのみブロックモードで動かしつつ、他のルールはカウントモードで動かすといった対応が可能です。

AWS-AWSManagedRulesKnownBadInputsRuleSetを追加後、以下のような設定をすることで実現できます。

![Screenshot at Dec 19 13-55-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51320/a267c689-0d3a-ffe6-a336-6d9447c346db.png)

AWS-AWSManagedRulesKnownBadInputsRuleSetはルール数が少ないので、他をポチポチカウント設定することも苦ではないですが、大量のルールを保有するルールセットで同様のことを行う場合は、上方のall rule設定で操作すると楽です。

# ログで確認

以前はfirehoseでS3に格納したログをAthenaで解析していたのですが、今は直接Cloudwatch Logsで見れちゃいますし、Log Insightで解析することも可能なので、すごく楽になりました。

ログから、どのルールでBlockしたのかを確認することが可能です。

![canvas (18).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51320/51097d46-49bd-53d2-d198-8ab443d7a631.png)

ちなみに、カウントモードで運用していると、以下のようなログになります。ちょっと注意が必要です。
![Screenshot at Dec 19 15-15-41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51320/fa941c82-2d8b-949a-a5b2-15f4db6aa58a.png)


# まとめ

AWS WAFでの設定について、紹介させていただきました。
ただ、これはあくまで緩和と考えています。
緩和でバージョンアップまでの時間を稼ぎつつ、Log4Shellの根本対策のため、サーバー側でのLog4jのアップデートをしましょう！
【Salesforce】日付型の最小値と最大値

設立年が1700年以前みたいな超老舗の取引先とかがいれば、日付型使わないほうが良さそうですね。

#入力可能範囲
1700/01/01 ～ 4000/12/31

https://help.salesforce.com/apex/HTViewSolution?id=000319688&language=ja
Node.jsデザインパターン　並行処理

[Node.jsデザインパターン](https://www.oreilly.co.jp/books/9784873118734/)を読んでいる中で
並行処理を学んだので書いていく。

## 並行処理の基本パターン

```{javascript}
const tasks = [task1, task2, task3]

let completed = 0

tasks.forEach(task => {
  task(() => {
    if(++completed === tasks.length) {
      finish();
    }
  })
})

function finish() {
  console.log('all tasks finished!!')
}

function task1(callback) {
  setTimeout(()=> {
    console.log('task1 finished')
    callback()
  }, 1000)
}

function task2(callback) {
  setTimeout(()=> {
    console.log('task2 finished')
    callback()
  }, 2000)
}

function task3(callback) {
  setTimeout(()=> {
    console.log('task3 finished')
    callback()
  }, 3000)
}
```

これを実行すると、コンソールは以下のようになる。

```
> "task1 finished"
> "task2 finished"
> "task3 finished"
> "all tasks finished!!"
```

"all tasks finished!!"をコールバック関数にせず、同期的に処理すると以下のようになる。

```{javascript}
const tasks = [task1, task2, task3]

let completed = 0

tasks.forEach(task => {
  task()
  if(++completed === tasks.length) {
    finish();
  }
})

function finish() {
  console.log('all tasks finished!!')
}

function task1() {
  setTimeout(()=> {
    console.log('task1 finished')
//     callback()
  }, 1000)
}

function task2() {
  setTimeout(()=> {
    console.log('task2 finished')
//     callback()
  }, 2000)
}

function task3() {
  setTimeout(()=> {
    console.log('task3 finished')
//     callback()
  }, 3000)
}
```

コンソールには以下のように吐き出された。

```
> "all tasks finished!!"
> "task1 finished"
> "task2 finished"
> "task3 finished"
```


次に、本当に並列処理がなされているのかを `Data.now()` を使って確認してみる。

```{javascript}
const tasks = [task1, task2, task3]

let completed = 0

tasks.forEach(task => {
  const startTime = Date.now()
  task(startTime, () => {
    if(++completed === tasks.length) {
      finish();
    }
  })
})

function finish() {
  console.log('all tasks finished!!')
}

function task1(startTime, callback) {
  console.log(`task1 startTime: ${startTime}`)
  setTimeout(()=> {
    console.log(`task1 finished ${Date.now()}`)
    callback()
  }, 1000)
}

function task2(startTime, callback) {
  console.log(`task2 startTime: ${startTime}`)
  setTimeout(()=> {
    console.log(`task2 finished ${Date.now()}`)
    callback()
  }, 2000)
}

function task3(startTime, callback) {
  console.log(`task3 startTime: ${startTime}`)
  setTimeout(()=> {
    console.log(`task3 finished ${Date.now()}`)
    callback()
  }, 3000)
}
```

これを実行すると、以下のようになる。

```
> "task1 startTime: 1639648461429"
> "task2 startTime: 1639648461429"
> "task3 startTime: 1639648461429"
> "task1 finished 1639648462431"
> "task2 finished 1639648463430"
> "task3 finished 1639648464434"
> "all tasks finished!!"
```
タイムスタンプなので見づらいが、ちゃんと同じ時間に3つのタスクがstartしていて、それぞれ約1秒後、約2秒後、約3秒後に終了していることがわかる。
XcodeのInterface Builderでハンバーガーメニューの配置の制約を簡単に設定する方法

# TL;DR
画面上部にハンバーガーメニューなどを配置する際に便利な、制約の設定方法についてです。

# ポイント
iOSの標準コンポーネントではないものの、様々なアプリで利用されているハンバーガーメニュー
このメニューのボタンの制約をInterface Builder(Storyboard, xib)で設定するなど、画面上部のアイテムに配置の制約をつける際には気をつけなければいけないことがあります。（参考：[iOSアプリ　ノッチ判定](https://qiita.com/yannagi41/items/a6d215071a33ccce494e)）

|ボタン|開かれたメニュー|
|:-:|:-:|
|![IMG_5F1EE9A677D6-1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146971/8f3c76d1-7fa2-d895-d810-d2b389015fe0.jpeg "ハンバーガーメニューボタンの例")|![IMG_76A0872B9194-1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146971/5e876f90-d321-80da-f1fc-1ea6d82bac7a.jpeg "ハンバーガーメニューの例")|


もう一つ考慮すべき点として、ハンバーガーメニューは元の画面にオーバーレイするような形式で表示されます。
その際に、元の画面ではstatus barが表示されていたのに、オーバーレイした画面ではstatus barは非表示になるということがあります。
その場合ノッチのない端末では、status barの有無によってsafe areaの領域が変わります。
もしsafe areaのみに制約をつけた場合、status barの有無によって位置のズレが発生します。

**ノッチがない端末で、safe areaのみに制約をつけた場合**
（画面録画の赤いバーのせいで見えづらくてすみません）
![RPReplay_Final1639893013.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146971/06ad0043-50e9-add9-6d3f-30fece2058a6.gif)


この記事では、以上の問題に対処しながら、Interface Builderで簡単に設定する方法をご紹介します。

# 実装方法
## 要求仕様
画面の上部から任意の位置、かつstatus barより下

## 解決策
ノッチの有無に限らず対応できる、priorityの異なる2つの制約をつけます。
（ただし配置場所が、ノッチのサイズである44ptより下であるならば1.の制約のみで様々な端末に対応可能です。）

### 1. super viewからの制約をpriority 1000で設定

![スクリーンショット 2021-12-19 14.58.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146971/065f84b1-3a86-55ed-1dc0-a0e6da943d3b.png)

### 2. safe areaからの制約をpriority 750で設定

![スクリーンショット 2021-12-19 14.59.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146971/3525c795-039f-11bf-bee4-2af14d9abc9b.png)
Qiitaの人気記事を投稿するTwitter Botを作った

タイトルの通り、Qiitaの人気記事を投稿するTwitter Botを作成しました。(作成したのは約1年前なのですが、投稿するタイミングを逃していました...)

https://twitter.com/buzzrita

https://github.com/Kazuhiro-Mimaki/Trend-from-Qiita



# 仕様
Qiitaに投稿されたLGTM数の多い過去記事の中から、毎日1記事が投稿されます。
スクレイピングとTwitter Botの処理はPythonを、定期実行はHerokuのタイムスケジューラを利用しています。
「プログラミング」「エンジニア」というキーワードで検索した結果からLGTM数の多い順にソート。そこからランダムで取得した1記事を投稿するようにしています。

# 作ってみてどうだったか
作ってから1年ほど経過しているのですが、想像していたよりも幅広い分野から投稿されているように思います。
毎日1記事なので、通学・通勤時間などに読むことができるのもちょうど良いです。

### 投稿例
https://qiita.com/kahirokunn/items/d872b343ea1b89e8bec8

https://qiita.com/cannorin/items/eb062aae88bfe2ad6fe5

https://qiita.com/wildmouse/items/6310f5dc1e15b04fed6d

https://qiita.com/hirokidaichi/items/2e8e731acfd7b6c7e02f


# まとめ
フォローしていただけると喜びます！

https://twitter.com/buzzrita
[NodeJS] Yargsを使ったコマンドパラメタの処理

#最初に
NodeJSでこんな感じのパラメタを渡すアプリを作りたいとします

```sh
 node mynote add --title="title" --body="message body"
```
addは機能名、--titleはタイトル、--bodyは本文だったとします。
皆さん、こんな時どうします？
ゴリゴリ処理を書くこともできますが、結構面倒ですよね。
さらに別なlistという機能を追加してみたい…ってなったら…
ということで、今回は簡単パラメタパーサー”yargs"を紹介します。

yargsはnodeプログラム向けのパーサーです。
https://github.com/yargs/yargs

#インストール
インストールはnpmコマンドで行います。

```sh
npm i yargs
```

#使い方
基本的な手順は以下の通りです

1. requireする
1. コマンドを定義する
1. parseする

## コード

では、実際にコードを書いてみます。
ここではaddというメソッドに対しtitleとbodyというパラメタを持ったアプリを作り、以下のような形で呼び出せるようにしたいと思います。
また、listという別機能も用意したいと思います。

```sh
node app.js add --title="my title" --body="message body"
```

長くないので、ソースコードを全て載せます。

``` js:app.js
const yargs = require('yargs')

yargs.command({
	command: 'add',
	describe : '新規にノートを追加します',
	builder: {
		title: {
			describe: 'ノートのタイトル',
			demandOption: true,
			type: 'string'
		},
		body: {
			describe: '本文',
			demandOption: true,
			type: 'string'
		}
	},
	handler(argv) {
		// TODO: write a note to storage
		console.log(`add is called. title="${argv.title}", body="${argv.body}""`)
	}
})

yargs.command({
	command: 'list',
	describe: '作成したノートの一覧を表示します',
	builder: {
		keyword: {
			describe: '検索キーワードを指定できます',
			demandOption: false,
			type: 'string'
		}
	},
	handler(argv){
		// TODO: search string from storage
		console.log(`list is called. keyword=${argv.keyword}`)
	}
})

yargs.parse()
```

注目すべきはyargs.commandのところです。ここで機能、引数、実際の処理を定義します。
複数機能ある場合はyargs.commandを複数記述します。

- command: 機能
- describe: 機能の説明
- builder: パラメタを定義します。titleとすることで"--title"というオプションが追加され、demandOptionで必須かどうかを指定しています。
- handler: 実際の処理を記述します。この中にいろいろ書くとごちゃごちゃするので、実際の処理は外に置いて呼び出すだけにしましょう

ここではaddとlistという機能を追加しています。
最後のparse()で準備完了です。

#実験
では、きちんと動作するか見てみます。
まずはaddを確認。きちんと動いています。

```sh
>node app.js add --title="hello" --body="my mseeage"
add is called. title="hello", body="my mseeage"
```

では必須パラメタを指定しないとどうなるか見てみます。

```sh
>node app.js add --title="hello"
app2.js add

新規にノートを追加します

オプション:
  --help     ヘルプを表示                                                 [真偽]
  --version  バージョンを表示                                             [真偽]
  --title    ノートのタイトル                                    [文字列] [必須]
  --body     本文                                                [文字列] [必須]

必須の引数が見つかりません: body
```
エラーとなり何が足りないかもきちんと表示されますね。

2つ目の機能listが使えるか見てみます。

```sh
>node app.js list                  
list is called. keyword=undefined
```
listの処理が呼び出されました。また、こちらは"keyword"パラメタがオプションとなっているため、指定しなくてもエラーにはなりません。

ヘルプも自動で追加されていて便利ですね。

```sh
>node app.js --help    
app.js [コマンド]

コマンド:
  app.js add   新規にノートを追加します
  app.js list  作成したノートの一覧を表示します

オプション:
  --help     ヘルプを表示                                                 [真偽]
  --version  バージョンを表示                                             [真偽]
```

#まとめ
NodeはWebのバックエンドとして使うことが多いのですが、折角覚えた知識…  ちょっとしたツールとしてスクリプト作りたいなんて時に便利です。
試してみてください。