OmniAICreator/Qiita-1.07M huggingface dataset:
iPhoneアプリでURLスキーム機能を実装する方法

## 宣伝

アプリ作っているので入れてみてください！！
よろしくお願いいたします！！

https://apps.apple.com/app/6446189340


https://apps.apple.com/jp/app/meime/id6446994707?l=ja


## 本題



iPhoneアプリでURLスキーム機能を実装するには、以下の手順に従います。

アプリのInfo.plistにURLスキームを追加します。以下の手順で編集できます。
        プロジェクトナビゲータでInfo.plistファイルを選択します。
        表示されるプロパティリストエディタで右クリックし、"Add Row"（行の追加）を選択します。
        追加した行のキーにCFBundleURLTypes、タイプにArrayを設定します。
        追加した行の下に新しい行が作成されるので、その行のキーにCFBundleURLSchemes、タイプにArrayを設定します。
        CFBundleURLSchemesの値として、アプリが利用するURLスキームを追加します。例えば、myappなどのカスタムのスキームを指定できます。

この場合、myapp://　というリンクをSafariなどのアドレスバーに貼ると、そのアプリを開くことができます。
【2023年6月】未経験転職者の基本情報技術者試験受験記

## はじめに
2023年6月頭に基本情報技術者試験を受験したので、勉強法など書き残します。

:::note warn
2023年6月現在、試験問題は完全非公開です。出題内容の開示は固く禁止されています。
本記事はその点に留意して記述しています。
:::

## 筆者について
タイトルにも書きましたが、未経験でIT業界に転職した人間です。

・経済学部卒
・社会人4年目
・異業種から転職し、2023年1月入社
・2023年2月にITパスポート取得

ITパスポート試験については入社前から勉強していました。
基本情報技術者試験とも試験範囲が重なっていますので、未経験者が最初に勉強するのに適しているかと思います。

2023年3月から基本情報技術者試験の勉強を始め、6月頭に受験し無事に合格基準点を超えることが出来ました。（科目A：730点、科目B：750点）

## 基本情報技術者試験の基本情報
詳細はIPAの公式サイトに掲載されていますので、そちらを参照してください。

【2023年4月～ 新形式】
・CBT方式
・科目A：90分60問
・科目B：100分20問
・科目A,Bともに600点以上/1,000点で合格
・受験料：7,500円

**※旧形式との違いについて**
旧形式の午前試験が科目A、午後試験が科目Bに該当します。
出題範囲に関しては科目Bが大きく異なります（選択問題廃止）。

科目A：午前試験に準じる
科目B：「情報セキュリティ」と「データ構造及びアルゴリズム（擬似言語）」の二つの分野を中心にした構成に変更
（出題範囲・シラバスの改訂関連情報より）

## 勉強方法・期間
勉強期間はトータル3か月で、科目Aに2.5か月、科目Bに1か月かけました。
1日の勉強時間は30分～2時間程度。平日は試験直前以外は、やっていない日もありました。
詰め込みが嫌い＆苦手なので、通勤時間を活用し勉強時間を小分けにして、無理せず続けられるようにしていました（頭が働かないなと思った時は潔く諦めてました）。
### 科目A
ひと月ほどかけてテキストを1周し、その後はひたすら過去問を解いていました。
試験1ヶ月を切ったところで総復習にyoutubeの解説動画を活用しました。
個人的に、テキストは「分かっていない部分が炙り出せるまで」を目安にきちんとやった方がいいように思います。
過去問は強い味方ではありますが、複数回解くと回答を覚えてしまうので頼りすぎも危ないかもしれません。
特に未経験者は「勘どころ」みたいなものも持ち合わせていないので、自分が理解できているか、必要事項を覚えられているかを最重要視して、過去問演習をするのが良いと思います。

### 科目B
こちらは科目A対策で基本知識を押さえるのが大前提です。

**・アルゴリズムとプログラミング**
全くプログラミングの経験が無いので、まずテキストを購入しトレース練習を行いました。
ただ、時間も限られる中でテキストばかりやっても仕方ないと思い、テキスト内の問題を6割ほど解いたところでIPAが公開しているサンプル問題に移りました。
youtubeの解説動画を見て理解しながら１周したのち、自力で１周解いて試験当日を迎えました。
問題によっては理解するのに時間がかかりましたが、めげずに自力で解けるまで粘ったのが良かったかなと思います。

**・情報セキュリティ**
科目Aの重要事項の総復習を行い、サンプル問題を解きました。
アルゴリズムに時間を割きたかったので、特に重点的な対策は行いませんでした。

## おわりに
ほぼ個人的な備忘録ですが、どこかのだれかのお役に立てば幸いです。
【React】useEffectを使って無限ループを試してみる

# はじめに

仕事でReactを使うようになって`useEffect`を使うことがありました。
まだなんとなくでしか理解できていないので、試してみたいと思います。

# useEffectとは

Reactのライフサイクルの中で、`componentDidMount`や`componentDidUpdate`などのタイミングで処理を実行するためのフックです。

https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

## ライフサイクルってなに？

Reactのコンポーネントは、マウント、アップデート、アンマウントの3つのフェーズを通過します。
それぞれのフェーズで、コンポーネントに対して処理を実行することができます。

マウントのフェーズの中には、コンポーネントがDOMに追加されたときに実行される`componentDidMount`があります。
アップデートのフェーズの中には、コンポーネントが更新されたときに実行される`componentDidUpdate`があります。
このタイミングでuseEffectが実行されます。

# useEffectを使ってみる

https://codesandbox.io/embed/useeffect-sample-countup-ywc9kq?fontsize=14&hidenavigation=1&theme=dark

```js:App.js
import "./styles.css";
import React, { useState, useEffect } from "react";

export default function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    if (count < 100000) {
      setCount((c) => c + 1);
    }
  }, [count]);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>

      <div>
        <div>Count: {count}</div>
      </div>
    </div>
  );
}
```

- `useEffect`の第2引数にcountを渡しています。
  - countが変更されたときに`useEffect`が実行されます。
  - `useEffect`の中で`setCount`を実行しているので、またコンポーネントが更新されます。
    - 無限ループの完成です。

## 余談
- アップデーター関数を使っているのは、正しくカウントアップされるようにするためです。
  - `setCount(count + 1)`とすると、`count`の値が更新される前に`setCount`が実行されてしまうため、正しくカウントアップされません。
  - https://react.dev/reference/react/useState#updating-state-based-on-the-previous-state


## Chrome DevToolsで確認する

コンソールのところに以下のようなエラーが表示されます。
上部にある`codesandbox`の`console`でも確認できます

:::note alert
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
:::

翻訳すると以下のようになります。

:::note alert
警告 最大更新深度を超えました。これは、コンポーネントがuseEffectの内部でsetStateを呼び出したが、useEffectが依存関係の配列を持っていないか、レンダリングごとに依存関係の1つが変更された場合に発生する可能性があります。
:::

# 終わりに

`useEffect`を使って無限ループを作ることができました。
今回の無限ループを止めるなら、`useEffect`の第2引数を空にすることで、マウント時・アンマウント時のみ実行されるようになります。

もっと詳しくなりたいので、勉強していきたいと思います。
これを読むをいいらしいので、読んでみます。

https://overreacted.io/ja/a-complete-guide-to-useeffect/

Reactが楽しくなってきました
東証 J-Quants API

J-Quants はJPX(日証券取引所)の提供するAPI
無料プランの場合、3か月遅れの情報までしか取得できないのがネック

# alphavantage APIとか使って、以下のような株アプリを作ってる

https://stonehills.tokyo/financer/


# 1. まず最初にアカウント登録する

https://jpx-jquants.com/

以下は動作確認したサンプルです

# 2. パスワードからトークンを取得するコード
```
import json
import requests
import pandas as pd


# POSTするデータを作る。
email  = "xxxxx@gmail.com"
password = "xxxx"
account_data = json.dumps({
        "mailaddress": email,
        "password": password,
    })

auth_user_url = "https://api.jquants.com/v1/token/auth_user"
auth_result = requests.post(auth_user_url, data=account_data)
refresh_token = auth_result.json()["refreshToken"]

auth_refresh_url=f"https://api.jquants.com/v1/token/auth_refresh?refreshtoken={refresh_token}"

refresh_result = requests.post(auth_refresh_url)
id_token = refresh_result.json()["idToken"]
```

# 3. 株式コードと期間を指定して投稿する
```
code = "7203" # 4桁のコードでも5桁のコード72030でもよい。
from_ = "2023-03-01"
to_ = "2023-03-01"

daily_quotes_url = f"https://api.jquants.com/v1/prices/daily_quotes?code={code}&from={from_}&to={to_}"
target_url= f"https://api.jquants.com/v1/fins/statements?code={code}"
```

# 4. idトークンはヘッダーにセットする
```

headers = {"Authorization": f"Bearer {id_token}"}
result = requests.get(target_url, headers=headers)
print(result.json())
```

まあ、無料じゃないのと、Alphavantege とかと比べると情報が貧弱なのがネック

いろんなAPIがある↓

https://stonehills.tokyo/stockinvest/index.php/financeapis/
MinIo(S3互換)をpythonから操作する

## 目的
今作成しているサービスが、現在はオンプレで構築しているが、いずれクラウドへ移行するかもしれないので、なるべくクラウドにのっけた時に作業が少ないソリューションを選びたい。そこで、今回はlocalstackではなくMinIoでオブジェクトストレージを構築する。

## 導入
### バイナリ
```bash
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
./minio server /data
```

### Docker
```docker-compose.yml
version: '3.9'

services:
  minio:
    image: quay.io/minio/minio:latest
    container_name: s3-minio
    environment:
      MINIO_ROOT_USER: admin123
      MINIO_ROOT_PASSWORD: admin123
    command: server --console-address ":9090" /data
    volumes:
      - ./minio/data:/data
    ports:
      - 9000:9000
      - 9090:9090
```

## アクセス
https://IP:9090にアクセスする事によってログイン画面が立ち上がる
パスワードとユーザーはDocker-compose.ymlで指定したものを使う
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2738475/8d0e7c99-c31c-9004-39cf-7e022c11cf67.png)


## python側
### pipのアップグレード
```bash
$ pip3 install --upgrade pip
```

### ライブラリのインストール
```
pip3 install boto3
pip3 install botocore
```

### バケット作成
```python
import boto3

s3_resource = boto3.resource(
    's3',
    endpoint_url='http://localhost:9000',
    aws_access_key_id='admin123',
    aws_secret_access_key='admin123'
)

bucket_name = "lesson"

s3_resource.create_bucket(Bucket=bucket_name)
```

### バケット、ディレクトリ作成
```
import botocore
import boto3

# minioをendpointにする
s3 = boto3.resource(
    's3',
    endpoint_url='http://localhost:9000',
    aws_access_key_id='admin123',
    aws_secret_access_key='admin123'
)

# bucket名
bucket_name = "lessontest3"

#s3.create_bucket(Bucket=bucket_name)



dir = '/kokugo/'
bucket_name='lessontest3'

#result = s3.list_objects(Bucket=bucket_name, Prefix=dir)

#if not "Contents" in result:
#s3.put_object(Bucket=bucket_name, Key=dir)
s3.Object(bucket_name, dir).put()
```
Pythonで特定のキーワードに関連するキーワードを表示する簡単なプログラム

## 目的
特定のキーワードに関連するキーワードを調べる事が必要になった。

## コード
```python
import pytrends
from pytrends.request import TrendReq

def get_related_keywords(keyword):
    pytrends = TrendReq(hl='en-US', tz=360)
    pytrends.build_payload([keyword], cat=0, timeframe='today 5-y', geo='', gprop='')
    related_queries = pytrends.related_queries()
    related_keywords = related_queries[keyword]['top']['query'].tolist()
    return related_keywords

search_keyword = input("キーワードを入力してください: ")
related_keywords = get_related_keywords(search_keyword)

print("関連キーワード:")
for keyword in related_keywords:
    print(keyword)
```


## 動作確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2738475/b3c43ebc-a080-0eda-2577-84ee6c0e597e.png)
(Python)無料で長文英語を翻訳するツール

## 目的
論文を読むとき、一括で翻訳をするのにとても困ってしまった。今回は英文を一文づつ分割して、翻訳するツールを作成する

## 入力文章
text.txtで翻訳したい文字列を保存する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2738475/b450bc4d-b5f1-7958-e7a9-483277980910.png)


## 出力結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2738475/f2b562c8-1323-04b2-f784-9d36992ad3df.png)


## コード
```python
from deep_translator import GoogleTranslator
from pysummarization.nlpbase.auto_abstractor import AutoAbstractor
from pysummarization.tokenizabledoc.mecab_tokenizer import MeCabTokenizer
from pysummarization.abstractabledoc.top_n_rank_abstractor import TopNRankAbstractor
import pymsteams, re, time

translist=[]
translists=[]

f = open('masa3.txt', 'r')
intext = f.read()
f.close()



#split_text = [intext[x:x+100] for x in range(0, len(intext), 100)]
#print(split_text)


split_text = intext.split('. ')

for sourcetext in split_text:
    sourcetext = sourcetext.replace('\n+', ' ')
    sourcetext = re.sub('\s+', ' ', sourcetext)

    try:
        document = GoogleTranslator(source='auto',target='ja').translate(sourcetext)
    except:
        pass
    translist = document
    translist = str(sourcetext)+"\n"+ str(translist) + "\n\n"
    translists.append(translist)
    print(translist)


transresult = ''.join(translists)
print(transresult)


f = open('masaseihon3.txt', 'x')
f.write(transresult)
f.close()
```
TextViewの自動サイズ調整について

### はじめに
先日プロダクト開発中に掲題の、TextViewで自動サイズ調整ができることを知って、ろくに[公式ドキュメント](https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview?hl=ja)も見ずに実装したら、思ったように動かなかったので備忘録として整理します

### TextView自動サイズ調整使用経緯
以下のように3つの種別の可変長文字列が横に並ぶことで、Android端末内でケースによってはレイアウトが崩れてしまうため、TextView自動サイズ調整を用いて回避を試みました

|デイリーアクティブユーザ数 | ウィークリーアクティブユーザ数 | マンスリーアクティブユーザ数|
|:-:|:-:|:-:|
100,000 | 40,000 | 10,00,000

### 失敗例
実際のコードは[こちら](https://github.com/sefwgweo/AutoSizeText/blob/master/app/src/main/res/layout/activity_main.xml)
自動サイズ調整したいTextViewそれぞれに以下のみを追加しました

```
app:autoSizeMaxTextSize="xxsp"
app:autoSizeMinTextSize="xxsp"
app:autoSizeTextType="uniform"
```

結果は以下の通り、左端と真ん中のテキストが被ってしまっています
![screenshot-1687780362426.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541072/f9f977a2-cdf3-7f7e-6921-b1b3b9c11f59.png)

これはなぜおきてしまっているかというと、公式に以下のような注意があります(まさに予期しない結果でした)

```
注: XML ファイルで自動サイズ調整を設定する場合、TextView の layout_width 属性または layout_height 属性に値「wrap_content」を使用することはおすすめしません。そのようにすると、予期しない結果が発生する可能性があります。
```

### 成功例
上記失敗を回避するために、自動サイズ調整を指定したTextViewのwidth指定を、今回はConstraintlayoutなので0dpを指定すると以下のようになります
実際のコードは[こちら](https://github.com/sefwgweo/AutoSizeText/blob/master/app/src/main/res/layout/activity_main2.xml)

![screenshot-1687780373948.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541072/d6d0d9a2-513a-4338-c8a1-e427c7c8f943.png)

### おまけ1
上記成功例では真ん中のTextViewのみwidthに0dpを指定し、右端はwrap_contentのままとなっています。
では右端もwidthに0dpを指定するとどうなるかというと、以下のようになります。
検証した結果、文字数が多いTextViewが優先的に加工(文字サイズや折返し)されてそうなことがわかりました。

実際のコードは[こちら](https://github.com/sefwgweo/AutoSizeText/blob/master/app/src/main/res/layout/activity_main3.xml)

![screenshot-1687780383671.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541072/0bc0b30e-8422-3e4e-374a-c9d566603568.png)

### おまけ2
せっかくなのでwidthだけでなくheightのケースも紹介します(失敗例は載せません)。
右端の文字数を減らし、 `autoSizeMinTextSize` を少し大きめの値に変更、さらにheightをwrap_contentから40dpに指定すると、ちゃんと自動でサイズ調整していそうな状態になりました。
注意点としては、widthの時と同様heightをwrap_contentのままにすると文字サイズは大きくなりますが下がみきれてしまいます。

実際のコードは[こちら](https://github.com/sefwgweo/AutoSizeText/blob/master/app/src/main/res/layout/activity_main4.xml)

![screenshot-1687790672173.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541072/b2e079d3-ae78-5094-8157-3adbd12fcc96.png)

### おまけ3
最後に、Constraintlayoutなので失敗ケースに対して悪あがきをしてみます。
layout_constrainedWidthを使うとどうなるかを検証しました
文字数を減らし、 `autoSizeMinTextSize` を少し大きめの値に変更、 `layout_constrainedWidth="true"` にしたところ、左右見切れは解消され文字サイズも大きくなりました。
ただし、文字数を減らしたとはいえちょっとたりないですね。
こちらは文字サイズを小さくすることで折返し表示されることを確認しましたが、やはりwrap_contentのままだと意図しない結果になってしまっているようです。

実際のコードは[こちら](https://github.com/sefwgweo/AutoSizeText/blob/master/app/src/main/res/layout/activity_main5.xml)

![screenshot-1687790672173.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541072/b2e079d3-ae78-5094-8157-3adbd12fcc96.png)


リポジトリは[こちら](https://github.com/sefwgweo/AutoSizeText)

### まとめ
公式ドキュメントはちゃんと読みましょう。
その上で予期しない挙動については興味があれば自分で実装して、どうなるのか検証するのがいいなと痛感しました。
しばらく休眠していたOpenShiftを起動する際の備忘録

## しばらく休眠していたOpenShiftとは
一般的なRed Hat OpenShiftは常時稼働する大規模なクラスターなのでこのようなことはないと思いますが、Single Node OpenShift(SNO)のような小規模なものではクラスター全体をシャットダウンして必要なときだけ起動することもあると思います。そして後日ブートすると、起動が完了しない（`oc get node`でNot Readyのまま）ということに・・・

## 1ヶ月以上休眠していると
調べてみると、ちょうど1ヶ月以上の間、停止状態のままだと、証明書の有効期限が切れてこのようなことになるようです。これはブートしたノードにsshでログインしてみるとログからわかりました。証明書がexpiredとなった日付はちょうどノードをシャットダウンしてから一ヶ月後です。
```
ssh -i ./id_rsa core@ノードのIPアドレス
journalctl -b -u kubelet | grep expired
(メッセージの例1）
May 10 03:17:30 xx-xx-xx-xx-xx-xx hyperkube[3466]: E0510 03:17:30.096854    3466 bootstrap.go:265] part of the existing bootstrap client certificate in /var/lib/kubelet/kubeconfig is expired: 2023-04-24 07:39:18 +0000 UTC
(メッセージの例2)
6月 26 06:05:48 xx-xx-xx-xx-xx-xx kubenswrapper[2779]: I0626 06:05:48.869508    2779 csi_plugin.go:993] Failed to contact API server when waiting for CSINode publishing: Get "https://api-int.sno-vmware2.localdomain:6443/apis/storage.k8s.io/v1/csinodes/xx-xx-xx-xx-xx-xx": x509: certificate has expired or is not yet valid: current time 2023-06-26T06:05:48Z is after 2023-06-09T02:11:12Z
```

## 対策：証明書署名要求(CSR)の承認
そこで、`oc`コマンドを使ってペンディング状態になっている証明書を承認してあげます。この操作は`kubeconfig`ファイルを保管しているPCなどから実施します。
```
export KUBECONFIG=./kubeconfig-noingress
oc get csr
(CONDITIONがPendingとなっているCSRがあることを確認)
oc get csr -o name | xargs oc adm certificate approve
(certificatesigningrequest.certificates.k8s.io/csr-bsrdt approved のようなメッセージが表示される)
```
その後、`oc get node`でReadyとなり、OpenShiftのWebコンソールにも接続できるようになりました。

## メモ
[OpenShift Assisted Installer](https://console.redhat.com/openshift/assisted-installer/clusters)でSNOをセットアップする場合は完了後に`kubeconfig`ファイルをダウンロードして保管することを忘れずに。そうしないと`oc`コマンド操作ができません。
ハッシュ値でファイルがアクセスしても安全か悪意があるかを確認法

 ハッシュまたは HMAC 計算の実行の応用となります。


1
[データ形式]ドロップダウン リストで、ファイル形式を[ファイル]として選択します。
![ハッシュの算出_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/b138b950-5ac8-3e86-f570-013a3704fd6c.png)

2
「データ」フィールドに関連付けられた省略記号ボタンをクリックしてファイルを選択します。検索ウィンドウが表示されます。対象のディレクトリに移動し、今回検査するInfected.pdfを選択して、[開く]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/3f0fdde2-461b-4c3e-3fe1-ad7f651e75eb.png)

3
 選択したファイルが「データ」フィールドに表示されます。
![ハッシュの算出_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/32e4525c-9d29-1eb4-69b8-3bf55d545698.png)

4
 「計算」をクリックしてファイルのハッシュ値を計算します。
![ハッシュの算出_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/2307fc16-eb61-3ff4-126e-db6e0c727c04.png)

5
 MD5 アルゴリズムがすでに選択されているため、HashCalc は MD5 値を計算し、その後にファイルの他のハッシュ (選択された) を計算します。次のスクリーンショットに示すように、MD5 ハッシュ値をコピーします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/3c77d312-d34d-d9f3-2c54-be245ee06414.png)

6
 Firefox Web ブラウザを起動し、URL https://www.virustotal.com/gui/home/searchを参照します。以下のスクリーンショットに示すように、VirusTotal Searchページが表示されます。
![ハッシュの算出_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/fc611aad-fc32-83ce-6a8a-069203b26648.png)

7
 MD5ハッシュを [検索]フィールドに貼り付け、Enterをクリックします。
![ハッシュの算出_18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/ec0fb2df-f575-2bd0-c8d1-980e28252a78.png)

8
 VirusTotal はデータベース内でこの値を検索し、次のスクリーンショットに示すような結果を返します。
![ハッシュの算出_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/17033657-0a31-801d-21e1-d022313b9663.png)
※ウイルス対策エンジンの数と検出率は、ラボ環境によって異なる場合があります。

9
 結果は、多くのウイルス対策エンジンがファイルを悪意のあるものとして検出したことを示しています。リアルタイムで、ファイルが疑わしいと思われる場合、VirusTotalなどのマルウェア スキャン アプリケーションにファイルまたはそのハッシュ値をアップロードして、ファイルが悪意のあるものであるかどうかを確認できます。
　このようにして、ファイルとテキスト文字列の MD5 ハッシュを計算し、オンライン マルウェア スキャナーで疑わしいファイルのハッシュ値を検索できます。
　
悪意のある PDF 分析については、 マルウェア フォレンジック を参照
RubyでAtCoder ABC307(A, B, D)を解いてみた

# はじめに
Webエンジニアを目指して、RubyやRailsをいじってます。
今回は、RubyでAtCoder ABC307のA, B, Dを解きました。備忘録として解き方をまとめていきたいと思います。

# A - Weekly Records
https://atcoder.jp/contests/abc307/tasks/abc307_a
```ruby:a-307.rb
gets
a = gets.split.map(&:to_i)
a.each_slice(7){ puts _1.sum }
```
## 解説
`each_slice`メソッドを使うことで、与えられた配列を7つごとに取得することができます。

# B - racecar
https://atcoder.jp/contests/abc307/tasks/abc307_b
```ruby:b-307.rb
array = Array.new(gets.to_i){ gets.chomp }
puts array.permutation(2).any?{ _1 + _2 == (_1 + _2).reverse } ? "Yes": "No"
```
## 解説
`permutation`メソッドを使って全探索することで、判定することができます。

# D - Mismatched Parentheses
https://atcoder.jp/contests/abc307/tasks/abc307_d
```ruby:d-307.rb
gets
s = gets.chomp.chars

ans = []
checker = []
s.each do
  if _1 == "("
    ans << _1
    checker << ans.size - 1
  elsif _1 == ")" && !checker.empty?
    ans = ans[...checker.pop]
  else
    ans << _1
  end
end
puts ans.join
```
## 解説
[公式解説](https://atcoder.jp/contests/abc307/editorial/6647)を参考にしました

https://qiita.com/shoya15/items/50fd17e37d7c64f2b166
正規表現

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/8515879b-bff4-a26c-9de6-ba3aba1fbe16.png)
１：全角かな/カナ漢字の区別
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/2f8f4860-53b5-596d-2a55-5c0a98067789.png)
①[ぁ-んァ-ヶ一-龥々ー]
　かな/カナ、または漢字についても[a-z]同様に[ぁ-んァ-ン一-龥]と表現することで、「ぁ」から「ん」まで、「ァ」から「ン」まで、「一」から「龥」までの文字でチェックを行います。これらの文字の範囲は、Unicodeにおける範囲であり、上記の範囲が一般的に指定されます。ただし、[ぁ-んァ-ン一-龥]と表現すると、「ヴ、ヵ、ヶ、々」という文字が弾かれてしまいます。なぜなら、utf-8文字コード上で「ヴ」「ヵ」「ヶ」はカタカナ「ン」の後に配列されているためです。そのため「ァ-ヶ」と記載しています。

　また「々」はutf-8文字コード上で記号として認識されているため、別途「々」の指定を追加する必要があります。そのため、[ぁ-んァ-ン一-龥]ではなく、[ぁ-んァ-ヶ一-龥々]と表現することで、あらゆる日本語に対応する言葉ができます。

　さらに、長音記号が含まれた文字列を区別する場合を考えましょう。例えば「メアリー」や「ジョニー」のような文字列です。このような長音を区別する場合は、[ぁ-んァ-ヶ一-龥々]の末尾に「ー」を追記して、[ぁ-んァ-ヶ一-龥々ー]と記載します。

例：モデルへの記述
with_options presence: true, format: { with: /\A[ぁ-んァ-ヶ一-龥々ー]+\z/, message: '全角文字を使用してください' } do
  　validates :first_name
 　 validates :last_name
end

２：郵便番号
　郵便番号は、3桁の数字とハイフンと4桁の数字で構成されています。郵便番号を正規表現らしく言い換えると、先頭に3桁の数字、そしてハイフン、末尾に4桁の数字 となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/122670a0-f611-975c-89f8-ea0690ae0c1f.png)
 ①{n}
直前の文字が n 回出現する場合にマッチします。
例えば、a{5}は、「『a』という文字が5回出現するものにマッチする」という意味になります。
今回はハイフンを挟んでd{3}とd{4}なので、ハイフン前は「数字が3回出現するものにマッチする」、ハイフン後は「数字が4回出現するものにマッチする」という意味になります。

②\A
\Aの直後に置いた文字を、文字列の先頭に持つ場合にマッチします。

　今回は\A\d{3}としているので、文字列の先頭に3桁の数字があるとマッチします。postal_codeの先頭は150ですので正しくチェックできています。

③\z
\zの直前に置いた文字を、文字列の末尾に持つ場合にマッチします。

　今回は\d{4}\zとしているので、文字列の末尾に4桁の数字があるとマッチします。postal_codeの末尾は0044ですので正しくチェックできています。

例：モデルの記述
validates :postal_code, format: { with: /\A\d{3}[-]\d{4}\z/, message: 'を入力してください' }

３：passwordの英数字混合の判断
　チェックするのがパスワードであることから、英字もしくは数字が少なくとも0回以上続き、末尾までそれが続くことが前提条件となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/0fadcb61-0b28-3d95-2a27-e99c66882ee4.png)
例：モデルへの記述
PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?[\d])[a-z\d]+\z/i.freeze
validates_format_of :password, with: PASSWORD_REGEX, message: 'には英字と数字の両方を含めて設定してください' 
※.freezeは、PASSWORD_REGEXという変数が編集されないようにするメソッドです。正規表現とは関係ありません。

４：正規表現のパターン表記一覧
パターン	　　　　　　意味
[a-z]　　　　　　　　	角括弧で囲まれた文字のいずれか 1個にマッチ
\d　　　　	　　　　　　数字にマッチ
{n, m}	　　　　　　　　直前の文字が少なくともn回、多くてもm回出現するものにマッチ
{n}	　　　　　　　　　直前の文字がn回出現するものにマッチ
.	　　　　　　　　　　改行以外のどの1文字にもマッチ
+	　　　　　　　　　直前の文字の1回以上の繰り返しにマッチ
\A　　　　　　　　	直後の文字が先頭にある文字列にマッチ
\z	　　　　　　　　直前の文字が末尾にある文字列にマッチ
[ぁ-んァ-ヶ一-龥々]	　角括弧に囲まれたかな、カナ、漢字のいずれかにマッチ（ヴ、ヵ、ヶ、々を含む）
?=	　　　　　　　　直後に設定した文字が続く文字列にマッチ
*?	　　　　　　　　直前に設定した文字が0回以上続く文字列にマッチ
ハッシュまたは HMAC 計算の実行


目的は、次の方法を実証することです。
◆ファイルとテキスト文字列のハッシュを計算します
◆VirusTotal のハッシュをチェックして、ファイルに悪意があるかどうかを確認します。

※ハッシュまたはハッシュ値はチェックサムとも呼ばれます。

概要

今回は、Serverさまざまなファイルのハッシュ値を決定するのに役立つツールであるHashCalcについて学びます。ファイルのハッシュ値を決定することで、調査者はファイルの整合性を確立できます。

1
　HashCalcをダウンロードし、setup.exeをダブルクリックし、ウィザードによるインストール手順に従ってアプリケーションをインストールします。
![ハッシュの算出_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/cc3d80cb-3a7f-97c3-e9c8-45a6fd846336.png)

2
　インストールの最後のステップで、「READMEファイルを表示」オプションをオフにし、「Launch HashCalc」オプションをオンにして、「完了」をクリックします。
![ハッシュの算出_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/06e75791-378d-1183-36c7-e1d8e40bccbb.png)

3
 インストールが終わるとHashCalcアプリケーションのメイン ウィンドウが表示されます。
![ハッシュの算出_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/a33e4be7-a260-6b62-5973-7f76e50d46c2.png)

4
 [データ形式]ドロップダウン リストで、データ形式として[ファイル]を選択し、[データ]フィールドに関連付けられた省略記号ボタンをクリックして[File]を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/dad1da46-0711-348d-be0f-8ebf7123c6aa.png)

5
 次に、「検索」ウィンドウが表示されます。ハッシュ値を計算する必要がある証拠ファイルを選択する必要があります。今回は、 Fan-oven.pngを選択しました。ファイルを選択したら、「開く」をクリックします。
![ハッシュの算出_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/5d695839-3c63-20f8-48d4-df181a8a2dc4.png)

6
 選択したファイルが「データ」フィールドに表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/2afc25c8-4538-ee53-e553-497776a3ac23.png)
　※データのメッセージ ダイジェスト/チェックサムを計算するには、[HMAC]ボックスのチェックを外す必要があります。

7
 適切な名前のボックスをチェックして計算に使用するアルゴリズムを選択し、「計算」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/945384a7-7058-3ad9-fb27-cc8a77c81c6c.png)

8
 次のスクリーンショットに示すように、選択したファイルのハッシュ値が表示されます。
![ハッシュの算出_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/0c4098f7-002c-2dce-5f9c-36baf5c820d2.png)

9
 データのキー付きハッシュ メッセージ認証コード ( HMAC ) を計算するには、次の手順を実行します。
![ハッシュの算出_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/6ea7dab3-9757-b4c7-e2d7-1061be39079b.png)

10
 テキスト文字列の計算を実行する場合は、まず[データ形式]ドロップダウン リストから[テキスト文字列]を選択し、[ HMAC]ボックスのチェックを外してから、データにテキスト (ここでは「Hello David, how have you be? 」)を入力します。
 必要なアルゴリズムにチェックを入れて計算に使用するアルゴリズムを選択し、「計算」ボタンをクリックします。
![ハッシュの算出_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/43eb409e-1065-c9a7-effb-8d84f7f9c528.png)

11
 次のスクリーンショットに示すように、選択したアルゴリズムのハッシュ値が表示されます。
![ハッシュの算出_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477527/e9e6214f-80f6-269a-d509-ebea0624e6ee.png)
【SwiftUI】@FocusStateを子Viewに渡す

# はじめに
`@FocusState`を子Viewに渡して使うときの方法がちょっと特殊だったので記録しておきます。

# 実装
### 親View
```diff_swift
struct ContentView: View {
    @State private var text: String = ""

+   @FocusState private var focused: Bool

    var body: some View {
        VStack(spacing: 50) {
+           SubView(text: $text, focused: $focused)
            
            Button {
                focused.toggle()
            } label: {
                Text("切り替え")
            }
        }
        .padding(20)
    }
}
```

### 子View
```diff_swift
struct SubView: View {
+   @Binding var text: String
    
    var focused: FocusState<Bool>.Binding

    var body: some View {
        TextField("", text: $text)
+           .focused(focused.projectedValue, equals: true)
            .textFieldStyle(.roundedBorder)
    }
}
```

# 参考記事
https://stackoverflow.com/questions/70141230/passing-down-focusstate-to-another-view

# おわり
初知りでした
【Python】sample_cd01...sample_cd50のような連続したパターンをリスト内包表記で効率的に生成する方法

# 概要
- sample_cd01...sample_cd50のような、連続したパターンを効率的に生成しリストに格納する方法として、Pythonのリスト内包表記を利用しました。
- 本記事ではそのサンプルコードを紹介します。ついでに、「文字列の連結」で行う場合も記載しています。

# サンプルコードの解説

- 以下のように、`sample_cd01`、`sample_cd02`...って連番のDBカラムってよくありますよね。そんな時に、リストリテラルで作成すると面倒です。

```sample.py
sample_cd_values = ['sample_cd01', 'sample_cd02', 'sample_cd03', 'sample_cd04', 'sample_cd05',
                    'sample_cd06', 'sample_cd07', 'sample_cd08', 'sample_cd09', 'sample_cd10']
```

- Pythonのリスト内包表記を使用することで、効率的に書くことができます。
    - リスト内包メソッドはPythonicで読みやすいとか
- もし`sample_cd50`までほしい場合は、以下のように書きます。

```py
sample_cd_values = [f"sample_cd{i:02d}" for i in range(1, 51)]
```

- 上記により、`sample_cd01`から`sample_cd50`までの値を生成できます。
- `f"sample_cd{i:02d}"`はフォーマット済みの文字列を生成するための記法です
    - `i`：ループ変数
    - `:02d`：2桁の0埋め数値であることを指定。これを消すと`01`ではなく`1`になってしまいます
        - `{i:02d}`：値を2桁のゼロパディングされた数字にフォーマットするための構文
    - `range(1, 51)`：1から50までの範囲を指定
- これにより、手動で01から50までの値を書く必要がなくなりました。


### `format()`メソッドを使用する場合...

```sample.py
sample_cd_values = ["sample_cd{:02d}".format(i) for i in range(1, 51)]
```

### `for`文を普通に使用する場合...

```sample.py
sample_cd_values = []
for i in range(1, 51):
    sample_cd = f"sample_cd{i:02d}"
    sample_cd_values.append(sample_cd)
```

- こちらは、簡潔さに欠け、コード行数も多くなります。ただ、リスト内包に不慣れな場合直感的なわかりやすさはあるなと思いました。


## 文字列の連結を使用する場合
- 以下の方法でも可能です。実際に会社で扱ったプログラムでは以下の`for`文の方法をみたことがあります。

```sample.py
sample_cd_values = ["sample_cd" + str(i).zfill(2) for i in range(1, 51)]
```

```sample.py
sample_cd_values = []
for i in range(1, 51):
    sample_cd = 'sample_cd' + str(i).zfill(2)
    sample_cd_values.append(sample_cd)
```
【OpenWrt】23.05系列 野良ビルドファームウェア (Buffalo WSR-6000AX8など)

### はじめに

ここは筆者が作成した OpenWrt 23.05 系列の野良ビルドファームウェアを紹介するページ

### 対応機種

* Buffalo WSR-6000AX8
* Buffalo WSR-2533DHP3 / WSR-A2533DHP3
* ELECOM WRC-G01 - WRC-X3200GST3-Bとほぼ同じハードウェア

### ダウンロード、注意点など

https://github.com/ssyysy2021/openwrt_files/ のREADME.mdを参照
AWS Systems Manager Session Managerでプライベートサブネット内のEC2へSSHポートフォーワーディング

# 概要
ローカルマシンから**プライベートサブネット内のEC2インスタンス**へポートフォーワーディング接続を行い、ローカルマシンにインストールされた「[pgAdmin 4](https://www.pgadmin.org/)」を使用して、Aurora PostgreSQLへ接続する手順について記載します。

以前に書いた[こちらの記事](https://qiita.com/bloodiadotnet/items/5fb450dabfd4289a7c7c)ではEC2 Instance ConnectやSSHコンフィグの変更も必要でしたが、今回の手順では、AWS Systems Manager Session ManagerのみでSSHポートフォーワーディング接続が可能です。


# 前提
* ローカルマシンに[AWS CLI](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html)がインストール済みである事。
* ローカルマシンにアクセスキー・シークレットキーおよび環境変数「AWS_PROFILE」が設定済みである事。
* ローカルマシンに[pgAdmin 4](https://www.pgadmin.org/)がインストール済みである事。
* EC2インスタンスに[SSMエージェント](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/sysman-manual-agent-install.html)がインストール済みである事。
* EC2インスタンスにIAMポリシー「AmazonEC2RoleforSSM」がアタッチ済みである事。

# 準備
以下のリンクからSessionManagerPluginのダウンロード、またはコマンドを実行してローカルマシンにセッションマネージャー用プラグインをセットアップ。

**Windowsの場合**

https://s3.amazonaws.com/session-manager-downloads/plugin/latest/windows/SessionManagerPluginSetup.exe

**Macの場合**
```bash
$ brew install --cask session-manager-plugin
$ session-manager-plugin --version
  1.2.463.0
```

# VPCエンドポイントの作成
EC2インスタンスが属するプライベートサブネットに以下のVPCエンドポイント群を作成。
* ssm.*\<AWSリージョン\>*.amazonaws.com
* ssmmessages.*\<AWSリージョン\>*.amazonaws.com
* ec2messages.*\<AWSリージョン\>*.amazonaws.com

# ポートフォーワーディング接続
以下のコマンドでAurora PostgreSQLにポートフォーワーディング接続を実行、「Waiting for connections...」と表示されたらOKです。

```bash
$ aws ssm start-session \
          --target <EC2インスタンスID> \
          --document-name AWS-StartPortForwardingSessionToRemoteHost \
          --parameters 'portNumber=5432,localPortNumber=15432,host=<接続先DBエンドポイント名>'
```

:::note info
上記コマンドの例ではローカルマシンのポートは「15432」でリッスン、接続先DBエンドポイントへはポート「5432」で接続しています。
:::

# pgAdmin 4の接続設定
ローカルマシンにインストールした「pgAdmin 4」を起動して、以下の画像の通り「Host name/address」に「localhost」、「Port」に「15432」を入力、Saveを実行して下さい。

|Connectionタブ|
|--|
|![2023062601.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143947/beae1004-7cbc-a8c8-6065-5bbf450c6ee4.jpeg)|

# ポートフォーワーディング切断
「Ctrl + C」でキャンセルして下さい。
【オブジェクト指向】移譲じゃないよ、委譲だよ！

# はじめに
みなさん、オブジェクト指向で開発してますか？
オブジェクト指向の開発でよく言われる「委譲を使おう」ですが、たまーに「委譲と移譲」間違えているケースがあります。
同音異義語でややこしいですが、何が違うのか明らかにしたいと思います。

### まずは言葉の意味から

【委譲】
意味：上のものが下のものに対して権限などを委ねること。
英語：delegate。

【移譲】
意味：対等の相手に譲り渡すこと。
英語：trasfer。


### サンプル説明

このサンプルでは、Somethingクラスがある責務を持っていて、その責務の一つであるmethod_aメソッドを委譲する例を示したいと思います。
サンプルはrubyです。

サンプル（元のコード）

```ruby
class Something
    def method_a
        # 何らかの処理
    end
end
    
class Main
    def self.call
        Something.new.method_a
    end
end

Main.call
```

method_aメソッドを委譲する理由としては

- method_aのコードが肥大化したので、Somethingクラスから切り出したい
- あるいは、Somethingクラス自体が肥大化したのでスリムにしたい

などが考えられます。


### 委譲（正しい例）

```ruby
class Something	
	def method_a
		other = Other.new
		other.method_a
	end
end

class Other
	def method_a
		# 何らかの処理
	end
end

class Main
	def self.call
		# SomethingがOtherに委ねている
		 Something.new.method_a
	end
end

Main.call
```

SomethingクラスからOtherクラスに責務を委譲します。
委譲には上下関係があるので、上にあたるSomthingクラスが窓口の役割を持つ点が変わらないことに注意しましょう。


### 移譲（間違った例）

```ruby
class Something	
	# 移譲したので、method_aがなくなっている
end

class Other
	def method_a
		# 何らかの処理	
	end
end

class Main
	def self.call
		# 移譲したので、Otherを直接呼ぶ
		Other.new.method_a
	end
end

Main.call
```

こちらは委譲ではなく、移譲してしまった例です。

SomethingクラスとOtherクラスは対等な関係であり、Mainクラスからは、Somethingクラスを通さずにOtherクラスが呼び出されています。


### どうして移譲じゃダメなのか？

その前に「カプセル化」と「凝集度」について触れておきたいと思います。


### 「カプセル化」について

冒頭でこのサンプルの説明として、
Somethingクラスがある責務を持っていて、その責務の一つであるmethod_aメソッドを委譲するという説明をしました。
Somethingクラスがどんなプロパティ（内部変数）を持っているかはわかりませんが、それらプロパティの管理についても責任があります。
そしてプロパティを外部から自由に操作できるように公開するのではなく、そのクラスのメソッドの中でしか操作できないようにすることを「カプセル化」と言います。

この「カプセル化」をくだけた感じで説明すると
- 「プロパティについて、僕（Somethingクラス）に何でも聞いてください」
- 「値を変更するにしても、何するにしても、僕（Somethingクラス）に命じてくれるのなら、責任を持って管理します」
- 「僕（Somethingクラス）が管理してるので、外から無断で変更しないでください」

といった感じでしょうか。

### 「凝集度」について

「凝集」とは何でしょうか？
「散らばったりしていたものが、一つに集まること」という意味があります。

これを踏まえて「凝集度」をウィキペディアで見てみましょう。

:::note info
凝集度は、あるコードがどれだけそのクラスの責任分担に集中しているかを示す尺度である。オブジェクト指向プログラミングでは、クラスの凝集度を高めるようにそのクラスの責任範囲を設定することが有益とされている。
:::

凝集度が高いと、コードの読みやすさと再利用の容易さが増すとされています。

注意しなければならないのは、なんでもかんでも一つのクラスに集めれば良いということではないことです。クラスは「単一責任の原則」に則るべきであり、複数の責任を負うべきではありません。凝集度を高めるためには関係のある処理だけを集めるべきです。

そうやって単一責任を処理するためのメソッドがクラスに集まっている状態を「通信的凝集」が高いと言います。

### あらためて、どうして移譲じゃダメなのか？

移譲すると

- 凝集度が下がる
- カプセル化がこわれることがある

からです。

凝集度が下がるとはどういうことでしょうか？

「散らばったりしていたものを一つに集めること」が凝集度を高めることであり
移譲するということは、集めることに反して、別の場所に移すことだからです。

カプセル化がこわれることがある？

プロパティ（インスタンス変数と読み替えても良いかもしれません）にアクセスできるのは
そのクラスのメソッドの中だけです。accessorにしたりgetter、setterを用意すると、それはカプセル化ではありません。
すべてのメソッドの移譲がカプセル化をこわすとは思いませんが、プロパティへのアクセスが必要なメソッドを移譲するには、カプセル化したままではできないことがあることに留意すべきです。

### 委譲だと良いの？

- 凝集度は下がらない
- カプセル化を維持できる

委譲の場合は、元のクラスに窓口としてのメソッドを残しますので、凝集度は下がっていないと言えます。
諸説ありますが、少なくともそのクラスに責務が集まっていることで、コードの理解やメンテナンス性は低下しないでしょう。

カプセル化を維持できる。
元のクラスにメソッドがあるので、元クラスが持つプロパティ（インスタンス変数）に自由にアクセスすることができます。必要であれば委譲先のクラスに対して引数としてプロパティを渡すことができます。
（この場合の注意点として、参照型を引数に渡して委譲先のクラスで書き換えられてしまうことによるカプセル化の破壊が考えられますが、ここではその対応には触れません）

### 最後に

私自身、オブジェクト指向の委譲を文字で書く時、委譲と移譲の漢字を間違うことがありましたが、もうこれで間違うことはないでしょう。
REST APIとGraphQLのメリット・デメリットを調べてみた

## イントロダクション
REST APIとGraphQLそれぞれのメリット・デメリットについて調べてみました。

## REST APIとは
昔からあるアーキテクチャです。
HTTPメソッド(GET、POST、PUT、DELETEなど）を使用してAPIがどういう振る舞いをするか定義します。
一つのURLに対してHTTPメソッドを指定することで「GET」なら取得、「POST」なら作成、「PUT」なら更新、「DELETE」なら削除のように処理を定義する考え方です。

## GraphQLとは
Facebook社(現在はMeta社)がREST APIが抱える問題を解決するために、開発したクエリ言語です。
APIのためのクエリ言語であり、必要なデータの構造を定義することで、必要なデータのみが返されます。
したがって、必要以上に大きなデータが返されるのを防ぐことができるのが特徴です。

GraphQL 公式サンプル
https://graphql.org/learn/queries/

## メリット
### REST API
- HTTPメソッドにGET、POST、PUT、DELETEなどを指定し、振る舞いを定義するので、理解しやすく、開発しやすい。
- HTTPのキャッシング使える。これにより、クライアントは同じリソースに対してキャッシュを使用することで、ネットワークトラフィックを削減することができる。
- リクエストに対してエンドポイントが分かれているので、複雑なリクエストも管理しやすい。

### GraphQL
- クライアントが必要なデータのみを取得できる。
例えば、クエリによってデータのフィールドやリレーションシップを指定することができるため、余計なデータを取得することを回避できる。
- 1つのエンドポイントを通じてデータにアクセスするので、複数のリクエストを送る必要がない。

## デメリット
### REST API
- エンドポイントで定義してるレスポンスは全て取得する。
それによってオーバーフェッチ（過剰取得）が発生したり、逆にレスポンスに定義の少なすぎてアンダーフェッチ（過小取得）が発生する。
- REST APIは思想なのでGETなのに実はinsertしてたり、POSTなのに取得していたり、実装者によってばらつきが生まれやすい。

### GraphQL
- 比較的新しい技術なので学習コストが生まれる。
- クエリ言語なのでクエリが複雑になる可能性がある。
- そのため、小規模なシステムには向かない。
- REST APIのようなWEBのキャッシングが効かないので、その辺を考慮する必要がある。
DynamoDBからPrtiQLエディタでSELECTしたいのに取れない

すごく絶望的にハマったのに、大した問題ではなくて悲しかったのでメモ残しておきます。

## テーブル作りました

DyanmoDBに下記のような`table-a`を作りました。

テーブル名： `table-a`
| id | name |
|:-----------|------------:|
| bbb7adc0-0742-4907-94ef-cb18d1d6c012 | Aくん |
| 7f1d08e9-b3f9-4c0a-98ef-295386d69861 | Bさん |

※idがパーティションキーです

## やりたかったこと
単純に、Aくんのレコードがほしいです。

## ハマりどころ１：パーティション入ってるテーブル名
### Let's SELECT
初めにこんな感じで投げました。
```SQL
SELECT * FROM table-a
```
### 結果
![スクリーンショット 2023-06-26 19.01.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396492/c14947a1-2d7e-b402-ef55-dcd7461e08d3.png)

### 答え
`テーブル名にパーティション入っている場合は、ダブルクォートで囲まないと取れません`
```SQL
SELECT * FROM "table-a"
```
![スクリーンショット 2023-06-26 19.08.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396492/f99ec28a-6331-2443-7a52-6676831f951b.png)

## ハマりどころ２：WHERE句の書き方
### Let's SELECT
```
SELECT * FROM "table-a" WHERE id="bbb7adc0-0742-4907-94ef-cb18d1d6c012"
```
### 結果
![スクリーンショット 2023-06-26 19.12.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396492/785985d9-3b5b-4cda-e8e9-969192c70d3f.png)

え？
テーブル名にダブルクォートだったから、条件もダブルクォートではないの...?

### 正解
`WHERE句の条件はシングルクォートで囲みましょう`
```
SELECT * FROM "table-a" WHERE id='bbb7adc0-0742-4907-94ef-cb18d1d6c012'
```
![スクリーンショット 2023-06-26 19.14.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396492/de387987-5223-46fc-9eeb-d86d7e3ba1e8.png)


## ※おまけ※ハマりどころ３：全部SELECTすると料金かかるので、取得レコード数を絞りたい
### Let's SELECT
```
SELECT * FROM "table-a" LIMIT 1
```

### 結果
![スクリーンショット 2023-06-26 19.17.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396492/e32b1626-d794-9302-440e-2080bc98da30.png)

### 正解
PrtiQLエディタは、2023年6月の時点でLIMITを現在サポートしてません。
DynamoDBのAPIにて`ExecuteStatement`をする際に、リクエストパラメータに`Limit`を指定することで、取得レコード数を制限できるそうです。
[DynamoDB API Reference ExecuteStatement](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/APIReference/API_ExecuteStatement.html#API_ExecuteStatement_RequestSyntax)

## まとめ
それぞれのハマったポイントで、解決したときには「なんだーーーーー」と叫びました。

まじめにDynamoDBからデータを取得するなら、
PrtiQLエディタよりDynamoDBのAPIで取得するほうがやりやすそうです。

また使うかわからないけど、同じ穴にハマらないように備忘録として残しておきます。
ありがとうございました。
Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.

# はじめに

apt-keyコマンドは非推奨らしいので、gpgコマンドでdearmorする練習とメモ

Ubuntu 22.04 で apt update すると以下のように警告を受けるようになった

```
# apt update
Hit:1 https://download.newrelic.com/infrastructure_agent/linux/apt jammy InRelease
...
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.
W: https://download.newrelic.com/infrastructure_agent/linux/apt/dists/jammy/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.
#
```

# 対処方法

これで警告が消えました

```
# curl -fsSL https://download.newrelic.com/infrastructure_agent/gpg/newrelic-infra.gpg | gpg --dearmor -o /etc/apt/trusted.gpg.d/newrelic-infra.gpg
# apt update
Hit:1 https://download.newrelic.com/infrastructure_agent/linux/apt jammy InRelease
...
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.
#
```
Accompanist の WebView Wrapper メモ2: 状態が画面回転に生き残らない

Accompanist の WebView Wrapper を使って
Jetpack Compose で WebView を使うときのメモです。

https://google.github.io/accompanist/web/

👇 前回

https://qiita.com/mangano-ito/items/077e1c08862376f635f0

:::note warn
このときのバージョンは `com.google.accompanist:accompanist-webview:0.30.1` です。
:::


## 今回のケースのサンプルコード

Google を WebView で表示する最小限のコードを書いてみます:

```kotlin:MyWebViewTest.kt
@Composable
private fun MyWebViewTest() {
    MyWebView(
        url = "https://www.google.co.jp/",
    )
}

@SuppressLint("SetJavaScriptEnabled")
@Composable
private fun MyWebView(
    url: String,
    modifier: Modifier = Modifier,
) {
    val state = rememberWebViewState(url = url)
    WebView(
        state = state,
        onCreated = { webView ->
            
            webView.settings.javaScriptEnabled = true
            webView.settings.domStorageEnabled = true
        },
        modifier = modifier,
    )
}
```


## 状態は安定版では画面回転に生き残らない

ページのナビゲーションの履歴や `WebViewState` が保持するページのタイトル, URL などの状態は画面回転を生き残りません:

![accwebview-state-lost-by-rotation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/084f26bc-396a-112e-90c7-47ecc57a0aae.gif)

そのままだと状態が失われるのは
他の Jetpack Compose で使われる種々の State でも同じ話題がありますが、
WebView だと結構予想を裏切る感じで違和感があるかもしれません。


## どうすればいいの？

今の安定版では `rememberSaveable` な API は用意されていないので、
必要な状態は `onCreated`, `onDispose` 時に自分で保持する必要があるということを意味しています。

これに関しては Issue が立てられていまして、ここで Workaround がコメントされていたりします:

https://github.com/google/accompanist/issues/1178

そしてつい最近 `v0.31.1-alpha` で対応がされていて、
`Saver` が指定された `rememberSaveableWebViewState` という関数が追加されています:

https://github.com/google/accompanist/pull/1557/files#diff-b5163a8c363c7945c7ace7a7ab683c93d64ddabb2c8017494ebd8e9a12192c71

```diff_kotlin:MyWebViewTest.kt
+import androidx.compose.runtime.LaunchedEffect

-import com.google.accompanist.web.rememberWebViewState
+import com.google.accompanist.web.rememberSaveableWebViewState
+import com.google.accompanist.web.rememberWebViewNavigator

// 略…

-    val state = rememberWebViewState(url = url)
+    val state = rememberSaveableWebViewState()
+    val navigator = rememberWebViewNavigator()
+    LaunchedEffect(navigator) {
+        val bundle = state.viewState
+        if (bundle == null) {
+            navigator.loadUrl(url)
+        }
+    }
     WebView(
         state = state,
+        navigator = navigator,
         onCreated = { webView ->
             webView.settings.javaScriptEnabled = true
             webView.settings.domStorageEnabled = true
```

以前の状態がなければロードすることになるので、
`LaunchedEffect` で `WebViewState.viewState` が `null` か確認して `WebViewNavigator#loadUrl` をすることになるのがポイントですね。

これで画面回転しても状態が生き残るので違和感がない感じにできました:

![acwebview-state-restored.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/e8468ec5-913f-de6b-5b5b-7c1a3f8a3ed8.gif)


まだ安定版ではないですが、必要に応じてこれを使うことになるでしょう。
(`Bundle` の容量の話題はありつつ)

---

以上です。
javascriptでjsonを使って管理したい時のプログラム一例

 javascriptでのjson記述に慣れていない人向けに、jsonに加工して保存するサンプルソースの一例を書きました。
　もっといい書き方がある気がしますか、私が慣れていないので、ここまで



## サンプルコード
```javascript
//javascriptでjsonを使って管理したい時のプログラム一例　また追加項目を加えることも可能

//対象のデータ
terget_data1 = ["apple","banana","piati"];
terget_data2 = [3,2,4];

//jsonの初期化
json_data = {}
json_data.data1 = []
json_data.data2 = []

//一つ一つ代入していく、その時加工したい内容を入れられるように、文字列を使いした。
for(
    var count = 0;
    count < terget_data1.length;
    count++
){
    json_data.data1[count] = "追加項目３:"+terget_data1[count]+":追加項目１";
    json_data.data2[count] = "追加項目４:"+terget_data2[count]+":追加項目２";
}



```
杉浦光夫 解析入門 I p9 命題1.6 証明

$a \leq {\rm sup} A$
$b \leq {\rm sup} B$
なので
$a+b \leq {\rm sup} A + {\rm sup} B$
$a+b$をどんどん大きくしていくと、
${\rm sup}\left ( A + B \right ) \leq {\rm sup} A + {\rm sup} B$
となる。

つまり、
${\rm sup} A + {\rm sup} B - {\rm sup}\left ( A + B \right ) \geq 0$
である。

一方、
${\rm sup} A + {\rm sup} B - {\rm sup}\left ( A + B \right ) \leq {\rm sup} A + {\rm sup} B - \left ( a + b \right ) \equiv \varepsilon$
のように定義された$\varepsilon$は、$\varepsilon > 0$であり、任意に小さな値にすることができる。そこで、教科書にある通り 1.5 を適用すれば、
${\rm sup}\left ( A + B \right ) = {\rm sup} A + {\rm sup} B$　
を証明することができる。

ところで、別解として、
$a \leq {\rm sup} A$,　$b \leq {\rm sup} B$
であることから
${\rm sup}\left ( A + B \right ) \leq {\rm sup} A + {\rm sup} B$　　(1)
とし、一方で、
$a+b \leq {\rm sup} \left ( A + B \right )$
であることから、
${\rm sup} A + {\rm sup} B \leq {\rm sup}\left ( A + B \right )$　　(2)
とすると、(1) および (2) から
${\rm sup}\left ( A + B \right ) = {\rm sup} A + {\rm sup} B$　
であると結論付けられるが、これは、R 12 ($a\leq b$ かつ $b\leq a$ ならば $a = b$) を使った証明であり、こちらの方が、教科書に示されている証明よりも、ずっとわかりやすいと思う。
IGVでGC%グラフを見る

IGVでGC contentを見る方法を調べたが、調べたサイトでいくつか間違いがあったので、実際に動いたコードを以下にまとめておく。

```
$ samtools faidx ex.fasta

$ cut -f 1,2 ex.fasta.fai > ex.sizes

$ bedtools makewindows -g ex.sizes -w 1000 > ex_1000.bed

$ bedtools nuc -fi ex.fasta -bed ex_1000.bed > ex_num.txt

$ gawk -v w=1000 'BEGIN{FS="\t"; OFS="\t"} {if (FNR>1) {print $1,$2,$3,"GCpc_"w"bps",$5}}' ex_num.txt > ex_1000.igv
```

出力されたex_1000.igvファイルをIGVで開けば、GC contentを見ることができるはずである。
Unityでビルドなしに実機環境で挙動を確認する方法

# はじめに
Unityでタッチイベントなどモバイルデバイス特有のイベントを確認する際、毎回ビルドしていたのですが、面倒なので他に方法がないか探していたら便利な機能がありましたので紹介します。結論、**Unity Remote 5**というアプリを使用します！Unity Remote 5は、Unityエディタ上で開発しているアプリを、モバイルデバイスでリアルタイムにプレビューするための非常に便利なツールです。これにより、開発者はコードを書き換える度にアプリをビルドすることなく、デバイスの実行環境での挙動を確認することができます。この記事ではUnity Remote 5の基本的な使い方を紹介します。

# Unity Remote 5の使用手順
1. Google Play StoreまたはApple App Storeから「Unity Remote 5」を検索し、モバイルデバイスにインストールします。
2. Unityエディタを開き、Edit > Project Settings > Editorを選択します。
3. "Unity Remote"セクションを見つけ、"Device"を"Any Android Device"または"Any iOS Device"に設定します。
4. モバイルデバイスをPCに接続します。
5. Androidの場合は、開発者設定からUSBデバッグを有効にします。
6. モバイルデバイスでUnity Remote 5アプリを起動します。
7. Unityエディタ上でシーンを開き、通常通りPlayボタンを押します。
8. モバイルデバイス上でリアルタイムプレビューが表示され、タッチイベントなどのデバッグができます。(表示されない場合は、Unity側のプロジェクトを再起動すると表示されるようになります。)

# 注意点
Unity Remote 5はプレビュー専用であり、最終的なビルドとしては使用できません。
高度なグラフィックスや処理を含むプロジェクトの場合、パフォーマンスに差が生じる可能性があります。
リアルタイムプレビューは便利ですが、最終確認は実際のデバイスでビルドを行うことをお勧めします。
MariaDBにMroongaプラグインを適用する

こんにちは、皆さん。今日は高性能な全文検索エンジンであるMroongaをMariaDBに適用する方法について解説します。Mroongaは日本語の全文検索に特化しており、非常にパワフルな機能を持っています。

まず初めに、Mroongaプラグインがインストールされていることを確認しましょう。それがまだの場合は、まずプラグインをインストールする必要があります。

# Mroongaプラグインの有効化

Mroongaプラグインを有効化するためには、以下のSQLコマンドをMariaDBのコマンドラインインターフェイスから実行します。

```sql
INSTALL PLUGIN Mroonga SONAME 'ha_mroonga.so';
CREATE FUNCTION last_insert_grn_id RETURNS INTEGER SONAME 'ha_mroonga.so';
CREATE FUNCTION mroonga_snippet RETURNS STRING SONAME 'ha_mroonga.so';
CREATE FUNCTION mroonga_command RETURNS STRING SONAME 'ha_mroonga.so';
CREATE FUNCTION mroonga_escape RETURNS STRING SONAME 'ha_mroonga.so';
```

これらのコマンドを実行した後、Mroongaプラグインとその関連機能がMariaDBに有効化されます。

# Mroongaの確認

次に、以下のコマンドを実行して、Mroongaが有効になったかを確認します。

```sql
SHOW ENGINES;
```

実行結果で `Mroonga` という行が `YES` になっていれば、Mroongaが有効になっていることが確認できます。

# Mroongaの適用

最後に、Mroongaエンジンを適用したいテーブルに対して以下のコマンドを実行します。

```sql
ALTER TABLE your_table 
ENGINE=Mroonga;
```

このコマンドで、`your_table` のストレージエンジンはMroongaに変更され、Mroongaが提供する全文検索エンジンが提供する全文検索やカラムストアなどの機能が利用可能になります。この例では `your_table` を変更するためのコマンドを示していますが、実際にはあなたがMroongaを適用したいテーブル名に置き換えてください。

ただし、実際にこのステートメントを実行する前に、現在のテーブルのストレージエンジンがMroongaに変更して問題ないか、また、Mroongaが必要とするインデックスやデータ型がテーブル内に存在するかなど、互換性の確認を行ってください。必要に応じてバックアップを取ることも重要です。

以上がMariaDBにMroongaプラグインを適用する方法です。Mroongaは日本語の全文検索を行う際に非常に有用なツールであり、そのパワフルな機能を活用して、より効率的なデータ検索を実現しましょう。
AWS EC2 の Ubuntu 22.04 インスタンスに Tera Term で、SSH接続する方法

AWS EC2 で作成した `Ubuntu Server 22.04 LTS` インスタンスに `Tera Term` を使った SSH接続ができず、詰みかけたので備忘録として残します。

# 環境
今回の環境は下記のとおりです。

- ホストOS：Windows 11 Pro
- Tera Term：<font color="red">Ver 4.106</font>

# 現象
AWS EC2 で作った Ubuntu Server にアクセスする場合、本来であれば下記の情報で接続ができるはずです。
しかし、なぜか接続ができませんでした！＼(^o^)／ｵﾜﾀ

- ユーザー名：ubuntu
- パスフレーズ：空欄
- 秘密鍵：インスタンス作成時に設定したキーペア（.ppkファイル）



# 原因と解決方法
この原因は Ubuntu の OS アップデートで SSH 接続時のセキュリティが強化され、 `SHA-1 RSA認証` がデフォルトで無効化されたためです。
そして、 `Teraterm Ver 5.0 beta 1` 以前のバージョンでは `rsa-sha2-256` や `rsa-sha2-512` のプロトコルが利用できないため、認証に失敗します。
解決方法は下記の2通りがあります。

1. Tera Term をアップデートする。
1. SSH の設定を変更し、 `SHA-1 RSA認証` を有効化する。

セキュリティ上の問題があるため、素直に Tera Term のアップデートすることをオススメしますが、何らかの理由で古いバージョンの Tera Term を使う必要がある人向けに、2つ目の方法を解説します。

## EC2 Instance Connect の起動
**EC2 Instance Connect** を使って SSH の設定を変更し、 SSH接続ができるようにします。

インスタンスの画面を開き `接続` をクリックします。
![Qiita_AWS-InstanceConnect_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212972/695c6d2b-c007-bd2f-85d5-1c6fbf7a4f60.png)

`接続` をクリックします。
![Qiita_AWS-InstanceConnect_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212972/723a8331-d940-da8e-45b2-a2eafcb6c822.png)

`EC2 Instance Connect` が起動したら、ターミナルと同様にコマンド操作ができるようになります。
![Qiita_AWS-InstanceConnect_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212972/f3051c85-3eb7-6703-3363-ac27cf8db92f.png)


## SSH の設定変更
`sshd_config` をエディタで開きます。

```shell
sudo nano /etc/ssh/sshd_config
```

最終行に下記を追加します。

```sshd_config
PubkeyAcceptedAlgorithms=+ssh-rsa
```


## SSH の再起動
SSH を再起動します。

```shell
sudo systemctl restart sshd
```


## SSH アプリケーションでの接続確認
今回は **Tera Term** を使って接続確認を行いました。

`ホスト(T)` に EC2 の `パブリック IPv4 アドレス` を入力し、 `OK` をクリックします。
![Qiita_AWS-TeraTerm_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212972/fbba5f77-0880-9612-9760-cc8fcfa512ac.png)

`ユーザー名(N)` に `ubuntu` を入力します。
※デフォルトから変更してない場合

`認証方式` は `RSA/DSA/ECDSA/ED25519鍵を使う` にチェックを入れ、 EC2 の `キーペア` を選択します。（.ppkファイル）
![Qiita_AWS-TeraTerm_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212972/e09db16d-aea9-a51b-9db4-a4c90d7158c4.png)

接続に成功するとコンソールが表示されます。
![Qiita_AWS-TeraTerm_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212972/db73f462-926c-913b-6b57-51800a8d7513.png)



# 参考文献
下記のサイトを参考にさせて頂きました。

https://blog.hitsujin.jp/entry/2022/04/24/010000

https://qiita.com/sugimount-a/items/78b5c02d4c286b7c266b#%E3%82%AD%E3%83%BC%E3%83%9A%E3%82%A2%E3%81%AE%E4%BD%9C%E6%88%90

<!-- EOF -->
valorantのユーザーと繋がれるアプリ

https://valorant-with-radiant.fly.dev/
タイトル：With Radiant
5vs5のFPSゲームValorantのユーザーと繋がることを目的としたアプリです。
![dc8cb10493297de646a8fa1b208ab37e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3455557/7a7b9afe-026d-3373-0968-9bca05bcd3ff.png)
【Go】Graceful Shutdownではまったところ

# 概要
net/httpパッケージのServer.Shutdownを利用しているのになぜかGraceful Shutdownされないことがあったので、原因と対策をまとめました。

# GracefulにShutdownしない実装
以下のコードを実行し、ブラウザから http://localhost:8080/ にアクセスすると5秒後に「hello world」と表示されます。
ブラウザから http://localhost:8080/ にアクセスした直後にCtrl＋Cでプログラムを終了すると、GracefulにShutdownするなら「hello world」と表示されるはずですが、実際は表示されません。
```go
package main

import (
	"context"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"
)

func main() {
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
	defer stop()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		time.Sleep(5 * time.Second)
		io.WriteString(w, "hello world")
	})

	server := &http.Server{
		Addr:    ":8080",
		Handler: nil,
	}
	go func() {
		<-ctx.Done()
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		server.Shutdown(ctx)
	}()
	log.Println(server.ListenAndServe())
}


```

# 原因
最後の行で実行しているListenAndServe()は、Shutdown()が実行されると直ちにErrServerClosedが返されます。
そのためゴルーチンで実行しているShutdown()が完了するより前にmain関数が終了し、
main関数が終了したことでmain関数で実行しているゴルーチンが強制終了されてしまいます。
Shutdown()の実装にも以下のコメントが記載されています。
```
// When Shutdown is called, Serve, ListenAndServe, and
// ListenAndServeTLS immediately return ErrServerClosed. Make sure the
// program doesn't exit and waits instead for Shutdown to return.
```

# 対策
対策は以下２つのパターンが考えられます。
### １.WaitGroupを利用する
Shutdown()のコメントに記載されている通りに、main関数がShutdown()が完了を待ってから終了するように実装を修正します。
修正にはWaitGroupを利用します。
```go
package main

import (
	"context"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"time"
)

func main() {
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
	defer stop()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		time.Sleep(5 * time.Second)
		io.WriteString(w, "hello world")
	})

	server := &http.Server{
		Addr:    ":8080",
		Handler: nil,
	}
	wg := &sync.WaitGroup{}
    //WaitGroupのカウンタを1増やす
	wg.Add(1)
	go func() {
        //ゴルーチンが完了してからWaitGroupのカウンタを1減らす
		defer wg.Done()
		<-ctx.Done()
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		server.Shutdown(ctx)
	}()
	log.Println(server.ListenAndServe())
    //WaitGroupのカウントが0になるまで待機する
	wg.Wait()
}

```
### ２.ゴルーチンでサーバーを起動し、main関数でシャットダウンする実装に修正する
main関数でサーバを起動し、ゴルーチンでシャットダウンする実装ではmain関数側でゴルーチンの完了を待機する必要がありましたが、ゴルーチンでサーバを起動してmain関数でシャットダウンするように実装すればゴルーチンの完了を待機する必要はありません。
```go
package main

import (
	"context"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"
)

func main() {
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
	defer stop()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		time.Sleep(5 * time.Second)
		io.WriteString(w, "hello world")
	})

	server := &http.Server{
		Addr:    ":8080",
		Handler: nil,
	}
	go func() {
		log.Println(server.ListenAndServe())
	}()
	<-ctx.Done()
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	server.Shutdown(ctx)
}

```
# まとめ
net/httpパッケージのServer.Shutdownではまったところについてまとめました。
GracefulにShutdownしない実装を例としてまとめているWebページもいくつかありましたので、参考にされる際はご注意ください。
実践Viｍメモ②

# やりたいことと操作
```
cities = %w{London Berlin New York}
```
を
```
cities = [London Berlin New York]
```
にしたい

①"%w"を削除
```
dt{
```

②対応する括弧へ移動
```
%
```

③"}"を"]"に置換
```
r]
```

④元あった対応括弧元へ移動
**ここが一番のポイント**
```
''
```

⑤"{"を"["に置換
```
r[
```

# その他
[vim-sandwich](https://github.com/machakann/vim-sandwich)を使わせていただいているので登場出番は少ないかもしれないが
覚えておきたい