OmniAICreator/Qiita-1.07M huggingface dataset:
Qiita記事をSlackへ見やすく通知するBotの改善

# はじめに

Qiitaの記事をSlackへ自動投稿するBotを運用している中で、投稿フォーマットが見づらいなーって思うことがしばしばありました。  
特に、QiitaのMarkdown記法やHTMLタグがそのまま表示されることがあり、記事の概要が読みにくい状態でした。

そこで、Slackでの見やすさを向上させるために、フォーマットの改善を行いました。本記事では、その実装手順についてまとめます。


**関連記事**
- [Pythonのslack_sdkライブラリで最新記事のキャッチアップができるSlackボットを作る](https://qiita.com/banksline_tus/items/7577dfe3467f9d762195)
- [Qiita記事をSlackに自動投稿するBotの機能拡張案](https://qiita.com/banksline_tus/items/7f4a2c21a74d34ea4156)


# 概要

現在のBotは、QiitaのAPIを利用して特定のタグを持つ最新の記事を取得し、Slackへ通知する仕組みになっています。  
しかし、記事の概要部分にQiita特有の記法（Markdown、HTMLタグ、カスタムブロックなど）が含まれており、そのままではSlack上で見づらくなっていました。

そこで、以下の3点を改善しました。

1. **QiitaのMarkdownやHTMLタグを適切に整形**（HTMLタグ削除、Markdownの整形）
2. **Slackの見やすいフォーマットへ変換**（リスト記号変換、余計な改行削除、見出しを角括弧で囲む）
3. **スレッド機能を活用し、整理された投稿にする**



## 改善手順

### 1. Qiita APIのレスポンスを確認

Qiita APIで取得できる記事データには、以下のような情報が含まれています。

```json
{
  "title": "記事タイトル",
  "url": "https://qiita.com/example",
  "body": "この記事では〜",
  "likes_count": 15,
  "user": {
    "id": "username",
    "profile_image_url": "https://qiita-user-icon.com/icon.png"
  }
}
```

この `body` フィールドにはMarkdownやHTMLタグが含まれているため、Slackへそのまま投稿すると**視認性が悪く**なります。

---

### 2. QiitaのMarkdownやHTMLタグを整形

#### **処理内容**
- **HTMLタグの削除**（`<dl>`, `<dt>` など）
- **Markdownの整形**（`# 見出し → [*見出し*]`、`- リスト → • リスト`）
- **Qiita特有のカスタムブロック（`:::note warn` など）の削除**
- **余計な改行の削除**

#### **実装コード**

```python
import re
from bs4 import BeautifulSoup

def clean_text(markdown_text):
    """QiitaのMarkdownをSlack用に整形"""

    # Qiitaのカスタムブロックのラベル（:::note warn など）を削除し、内容は保持
    markdown_text = re.sub(r":::\s*\w+\s*\n", "", markdown_text, flags=re.DOTALL)
    markdown_text = re.sub(r":::", "", markdown_text)  # 閉じタグの削除

    # HTMLタグを除去（BeautifulSoupを使用）
    soup = BeautifulSoup(markdown_text, "html.parser")
    text = soup.get_text()

    # Markdownの余計な記号を削除
    text = re.sub(r"^#+\s*(.*)", r"[*\1*]", text, flags=re.MULTILINE)  # 見出し（# 1. → 1.）
    text = re.sub(r"\*\*(.*?)\*\*", r"*\1*", text)  # 強調（**bold** → bold）
    text = re.sub(r":メモ:", "", text)  # `:メモ:` の削除
    text = re.sub(r"^\s*[-*]\s+", "• ", text, flags=re.MULTILINE)  # 残ったHTMLタグの削除（<dl>, <dt>など）

    # 余計な改行を整理
    text = re.sub(r"\n{2,}", "\n", text)  # 2つ以上の改行を1つに統一

    # 最初の200文字のみ取得
    return text[:200]
```

この関数をQiita APIから取得した `body` に適用することで、記事概要をSlackで読みやすく整形します。

---

### 3. Slackのメッセージフォーマットを改善

#### **改善後のフォーマット**

```
親スレッド
📢 最新のQiita記事まとめ

子スレッド
"text": f"📌 *タイトル : * {title}\n"
        f"🔗 *URL : * {url}\n"
        f"👍 *LGTM数 : * {likes}\n"
        f"📝 *概要 : * \n{description}...\n"
```



## まとめ

本記事では、Slack通知のフォーマットを改善するために下記の3つを行いました。

- QiitaのMarkdownやHTMLを適切に整形する処理を実装  
- Slackのメッセージフォーマットを見やすく変更  
- スレッド機能を活用し、通知を整理  

これにより、SlackでのQiita記事通知が格段に見やすくなりました！

今後も改善点があれば、さらに調整していきます。
個人的備忘録：ハッカソンで作成予定の CloudFront & ALB の Route 53 設定を自分なりにまとめた

## はじめに
このメモは、実際に試したわけではなく、個人的な走り書きのメモとしてまとめたものです。

:::note alert
**後で調べたり、実際に設定する際の参考にするための備忘録になります。**
:::

## 書こうと思ったきっかけ
フロントエンドとバックエンドを AWS 上で運用する際に、Route 53 の設定方法について調べたので、その内容を整理しました。

同じような構成を考えている人の参考になればと思い、このメモを残しています。

## 前提の構成
フロントエンドとバックエンドのインフラ構成は以下のようになっています。

### フロントエンド (honda333.blog)
- **S3 + CloudFront** による静的サイトホスティング
- **ACM (バージニア北部 us-east-1)** を利用した SSL 証明書
- **Route 53** で **CloudFront に向ける設定**

### バックエンド (back.honda333.blog)
- **ALB (東京 ap-northeast-1)** によるトラフィック管理
- **ACM (東京 ap-northeast-1)** を利用した SSL 証明書
- **Route 53** で **ALB に向ける設定**

## 今回作成したAWS構成図について
詳細は省略しますが、今回は受講しているITスクールで、以下のようなAWSインフラ構成をコード化しています。

![Screenshot 2025-02-03 at 18.50.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/b5c205af-9d3e-ceca-3db5-5e3164e6b296.png)

このTerraform設定では、AWS上にVPC・サブネット・インターネットゲートウェイ・ECS・RDS・S3・CloudFrontを含むインフラ環境を構築しています。

https://qiita.com/free-honda/items/fb7a0ef46647ff459d78

### Route 53 の設定

#### 1. フロントエンド (honda333.blog) の設定
CloudFront は固定 IP を持たないため、**エイリアスレコード (Alias Record) を使用**します。

- **ドメイン名:** `honda333.blog`
- **レコードタイプ:** A (エイリアス)
- **エイリアス先:** CloudFront のドメイン (`xxxxx.cloudfront.net`)
- **TTL:** 自動
- **ルーティングポリシー:** シンプル (Simple)

また、`www.honda333.blog` を `honda333.blog` にリダイレクトする場合、**CNAME レコードを設定**します。

- **サブドメイン:** `www.honda333.blog`
- **レコードタイプ:** CNAME
- **CNAME の値:** `honda333.blog`

#### 2. バックエンド (back.honda333.blog) の設定
ALB も固定 IP を持たないため、**エイリアスレコード (Alias Record) を使用**して ALB に向けるのが最適です。

- **ドメイン名:** `back.honda333.blog`
- **レコードタイプ:** A (エイリアス)
- **エイリアス先:** ALB の DNS (`xxxxx.ap-northeast-1.elb.amazonaws.com`)
- **TTL:** 自動
- **ルーティングポリシー:** シンプル (Simple)

## 補足

### CloudFront の設定
- `honda333.blog` を CloudFront に登録する際、**ACM (バージニア北部 us-east-1)** の証明書を適用してください。
- CloudFront のオリジンとして S3 を使用する場合、**カスタムドメイン (`honda333.blog`) を設定**しておくと、リダイレクトや静的サイトの動作がスムーズになります。

### ALB の設定
- ALB の **HTTPS リスナー** で、**東京リージョン (ap-northeast-1) の ACM 証明書 (`back.honda333.blog`) を設定**してください。
- ALB でリダイレクトを管理する場合は、ターゲットグループに適切なルールを設定しておくと便利です。

## 最終的な Route 53 設定 まとめ

| ドメイン名 | ACM リージョン | Route 53 設定 |
|------------|--------------|---------------|
| honda333.blog (フロント) | バージニア北部 (us-east-1) | CloudFront にエイリアス (Aレコード) |
| back.honda333.blog (バックエンド) | 東京 (ap-northeast-1) | ALB にエイリアス (Aレコード) |

この設定により、**フロント (`honda333.blog`) は CloudFront を通じてアクセス**し、**バックエンド (`back.honda333.blog`) は ALB を経由してアクセス**できるようになります。
mysqlで苦しんだ話

なんかさ、急にへそ曲げられたんだよね。ありえなくない？（死んだ目）

mysqlで遊んでいたときに、急にSelect文が使えなくなる事象が発生。Workbenchでもしっかりダメ。

use databaseとかshow table statusとかはできるのに、肝心のテーブルの中身が取れない。そんなんデータベースって呼べるかいな。

---

### エラー内容

```
Error Code: 2013 Lost connection to MySQL server during query
```

**いやさっきまで元気に動いとったやん。なんでやねん。**

そんな突然サーバー落ちるとかある？ていうかローカルなのにサーバーって概念あるんだ・・・と思いながら調査開始。

・・・やっぱ全部書くのめんどいからおおまかな結果だけ書くね。

---

### 結果

```
mysqld
```

を打ってみたら、

```
2025-02-16T20:51:40.203207Z 0 [System] [MY-010116] [Server] C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqld.exe (mysqld 8.0.30) starting as process 33016
2025-02-16T20:51:40.206008Z 0 [Warning] [MY-010091] [Server] Can't create test file C:\Program Files\MySQL\MySQL Server 8.0\data\mysqld_tmp_file_case_insensitive_test.lower-test
2025-02-16T20:51:40.206138Z 0 [Warning] [MY-010091] [Server] Can't create test file C:\Program Files\MySQL\MySQL Server 8.0\data\mysqld_tmp_file_case_insensitive_test.lower-test
2025-02-16T20:51:40.206390Z 0 [ERROR] [MY-013276] [Server] Failed to set datadir to 'C:\Program Files\MySQL\MySQL Server 8.0\data\' (OS errno: 2 - No such file or directory)
2025-02-16T20:51:40.206893Z 0 [ERROR] [MY-010119] [Server] Aborting
2025-02-16T20:51:40.207033Z 0 [System] [MY-010910] [Server] C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqld.exe: Shutdown complete (mysqld 8.0.30)  MySQL Community Server - GPL.
```

**しっかりサーバー落としとるやんけ。Shutdown complete（ドヤァ）ちゃうんじゃ。**

---

### 原因

エラー文中４行目、「data」フォルダってやつがないよ！と言われたので大人しく追加。

ほんで再度mysqld打ったらすんなり通りやがりました。

**なんで？さっきまでのは何だったの？こわ。意味不明。**

というわけで似たようなエラーに遭遇したときは上記を参考にしていただけると助かるかもです。信じる者は救われますおそらく。

---

### 後日談

再度死んでました。救われません。

DBの中身もSelect文自体も重いものじゃないから、処理のタイムアウトじゃないよな・・・。サーバーとの接続が切れたっつったって、showできるし再接続もできたっぽいし、ンなわけなくねぇか・・・？

まさかと思い、開きっぱなしにしてた、DB接続したままの自作中Webを閉じてみた。

**通るようになった！**

二つ以上の接続がダメってこと・・・？しばらく調査しやす・・・。
M5NanoC6、アナログジョイスティックの値を最小二乗法(線形単回帰)して遊ぶ(実装編



x 過去ログを見よ!!!

x 3.1.2 (ESP32 Arduino 3.1.2)
x 片チャンネル
x 3秒ごと
x 送信直前の値をサンプリングする予定
x M5NanoC6の場合は、電圧が直接に返つてくる(mV(ミリボルト))
x nVで計算 (mVを1000倍する)
x nが10個とか15個とかが多いのは計算しやすいから。10の時は、合計は、55,平均は、5.5 
15の時は、合計は、120,平均は、8,小数点以下がないのは、15,n=15のx偏差×y偏差の合計は、280






結果
----






![Screenshot from 2025-02-17 05-33-05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/dcc3ffe9-b3d9-486d-a59e-79121352bf23.jpeg)


イメージ

![image_original (35).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/360ca3fd-ece8-41ea-893f-7d9765a0c995.jpeg)

配線

![o_coq782.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/e3ef7814-7c68-4999-894c-f2f318917c2a.jpeg)


こんなのが、たくさん出てきて、分からん。


![o_coq816.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/490105de-cef2-4d5a-8919-ce24dad43f72.jpeg)



プログラム
----




```c++


//ser_analog_joystick_min2j_M5NanoC6

//nは、15固定、1,2,3,4...12,13,14,15
//xの平均は、8 (計算済み)
//xの偏差の二乗の積は、280 (計算済み)


#define G_X 2
#define G_Y 1


void setup() {
  // put your setup code here, to run once:

  //gpio inte
  pinMode(G_X, ANALOG);  //gpio init
  pinMode(G_Y, ANALOG);  //gpio init

  //シリアルの初期化
  Serial.begin(9600);
  Serial.println();
  //シリアルの待ちが0.5*9
  for (int i = 0; i < 9; i++) {
    Serial.print('.');delay(500);  //接続待ち
  } //for
  Serial.println();

} //setup

void loop() {
  // put your main code here, to run repeatedly:

  float c_yl = 0;
  //float c_yr = 0;

  int y[20];
  int s = 0;  //input
  for (int i = 1; i <= 15; i++) {
    //センサー入力する
    s = s + (y[i] = (analogRead(G_X)*1000));             //センサーの値
  } //for
  s = s / 15;                            // s / 15

  int xydie = 0;
  for(int i = 1;i<=15;i++){    
    xydie = xydie + (   (i - 8) *  (y[i]-s)  );
    Serial.printf("%d,",       (i - 8) *  (y[i]-s)  );
  } //for

  int b = xydie / 280;
  //int a = s -(xydie / 280)*8;
  int a = s -((xydie*8) / 280);

  Serial.printf("\n");
  Serial.printf("xydie = %d\n",xydie);
  Serial.printf("\n");
  Serial.printf("b = %d\n",b);
  Serial.printf("a = %d\n",a);

  //c_yl = ((float)(a+b*15)) * (3.3f / 4096.0f);  //  電圧こと
  //c_yl = ((float)(a+((xydie*15) / 280))) * (3.3f / 4096.0f);  //  電圧こと
  c_yl = ((float)(a+((xydie*15) / 280))) * 0.000001f; //  電圧こと  x0.000001 = /1000000
  

  //表示
  //Serial.println(c_yl);
  Serial.printf("%f\n",c_yl);
  Serial.println();
  delay(3000);  //ダミー 0.3秒待つ

} //loop



```
React Native 「`」バッククォートでないと失敗するケース

APIを利用する上でAPIKeyを「.env」ファイルに記述してそこから呼び出す仕様としたが、ここで躓いてしまった。

「`バッククォート」「'シングルクォーテーション」「"ダブルクォーテーション」での使い分けを理解していないために日を跨いで試行錯誤することになった。


```react native : 失敗.tsx
const responce = await axios.get('https://newsapi.org/v2/everything?q=keyword&apiKey=${process.env.EXPO_PUBLIC_NEWS_API_KEY}');
```
```react native : 成功.tsx
const responce = await axios.get(`https://newsapi.org/v2/everything?q=keyword&apiKey=${process.env.EXPO_PUBLIC_NEWS_API_KEY}`);
```
原因は「｀」バッククォートと「'」シングルクォーテーションの違い。

これまでネット上にあるコードを参考にしてやってきたけど、例えば「'」部分を「"」で記述する等の違いがあっても問題なく動いたため気にせずにここまで来てしまった。
変数の呼び出しを含む場合には「｀」バッククォートで囲むとか文法上のルールがあるのかもしれない。


〜原因を突き止めるのに時間を要した理由〜
「'」シングルクォーテーションで囲った状態で「APIKey」を直接記述して情報を取得できるか試したところ上手くいったため、変数に書き換えた途端に失敗した原因を別の部分に求めてしまった。

実は「.env」ファイルで管理する場合は先頭に「EXPO_PUBLIC_」をつけるという変数名のルールも理解できていなかった。
個人的備忘録：なぜログを残さないと詰むのか？ エラーログの重要性と実践例（Go、Python、JavaScript）

## はじめに  
システム開発や運用において、エラーログはかなり重要な役割を持っています。

:::note warn
**特に、トラブルが発生したときに原因を素早く特定できるかどうかは、ログの管理次第といっても過言ではありません。**
:::

この記事では、エラーログの重要性を整理しつつ、Go、Python、JavaScript それぞれでのログの出力方法を紹介します。  

## 書こうと思ったきっかけ  
IT スクールでの学習やハッカソンの経験を通じて、「***ちゃんとログを残しておけばもっとスムーズに解決できたのに…！***」と思う場面が何度かありました。

:::note info
**エラーログがなかったせいで、どこで何が起きたのか分からず、チーム全員で手探り状態になることも。**
:::

実際の開発現場でも、ログをうまく活用できるかどうかで、トラブル対応のスピードやシステムの安定性が大きく変わると学びました。

そんな経験から、「**ちゃんとしたログの出力方法を押さえておこう！**」と思い、この記事を書いてみました。  

## エラーログの重要性

システム開発や運用において、エラーログの出力は非常に重要な役割を果たします。適切なログ管理を行うことで、以下のようなメリットがあります。

1. **デバッグの容易化**：エラー発生時にログを確認することで、問題の原因を特定しやすくなります。
2. **障害対応の迅速化**：システム障害が発生した際、ログを活用して素早く対処できます。
3. **セキュリティ対策**：異常なアクセスや攻撃の兆候をログから検知できます。
4. **システムの保守性向上**：運用中の問題やパフォーマンスのボトルネックを把握し、改善につなげられます。


## 各言語でのログの出力方法

### Goでのログ出力

Goでは標準の `log` パッケージを使用してログを出力できます。

```go
package main

import (
    "log"
    "os"
)

func main() {
    file, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()
    log.SetOutput(file)

    log.Println("通常のログメッセージ")
    log.Printf("エラー: %s", "サーバーが応答しません")
    log.Fatal("致命的なエラー: システム停止")
}
```

**実際のコマンド結果**

![Screenshot 2025-02-17 at 3.47.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/d4094bd4-af9d-4b15-a435-2660e8d435fb.png)

### Pythonでのログ出力

Pythonでは `logging` モジュールを使用します。

```python
import logging

logging.basicConfig(
    filename='app.log',
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

logging.debug("デバッグ用のログ")
logging.info("情報レベルのログ")
logging.warning("警告: データが見つかりません")
logging.error("エラー: ファイルが開けません")
logging.critical("致命的なエラー: システムダウン")
```

**実際のコマンド結果**

![Screenshot 2025-02-17 at 3.49.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/c9bc77f4-f415-4917-adac-530537bf72e4.png)

### JavaScript (Node.js) でのログ出力

Node.js では `console` を使ったログ出力や、`winston` などのライブラリを使用できます。

#### `console` を使用する場合

```javascript
console.log("情報: アプリケーションが起動しました");
console.warn("警告: メモリ使用量が高いです");
console.error("エラー: データベース接続に失敗しました");
```

**実際のコマンド結果**

![Screenshot 2025-02-17 at 3.52.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/2cbb86ae-cb73-4c1d-80a2-dbd7eebe3170.png)


## まとめ

適切なエラーログの管理は、システムの安定性と保守性を高める上で不可欠です。Go、Python、JavaScript それぞれの言語で適切な方法でログを出力し、効果的に活用することが重要です。特に、

- **ログレベルを適切に設定する**
- **ログファイルを定期的にローテーションする**
- **監視ツールと連携させる**

といった工夫をすることで、より良い運用が可能になるかと思います！
Streamlitの基本的なUIコンポーネント

## 1. ボタン
```python
import streamlit as st

# ボタンを押した際の挙動を設定する。
if st.button("ボタン"):
    st.write("ボタンが押されました!")
else:
    st.write("ボタンはまだ押されていません。")
```

## 2. チェックボックス
```python
# チェックボックスの状態を確認する。
check = st.checkbox("チェックボックス")
if check:
    st.write("チェックされました。")
else:
    st.write("まだチェックされていません。")
```

## 3. トグル
```python
# トグルスイッチの状態を取得する。
toggle = st.toggle("トグル")
if toggle:
    st.write("現在はONです。")
else:
    st.write("現在はOFFです。")
```

## 4. ラジオボタン
```python
# ラジオボタンを使って色を選択する。
color = st.radio(
    "色を選んでください",
    ("赤", "青", "緑")
)
st.write(f"選んだ色: {color}")
```

## 5. ドロップダウンリスト
```python
# 1つの値を選択できるドロップダウンリスト。
fruits = st.selectbox(
    "フルーツを選んでください",
    ("りんご", "オレンジ", "バナナ")
)

st.write(f"選んだフルーツ: {fruits}")
```

## 6. 複数選択可能なリスト
```python
# 複数の値を選択できるドロップダウンリスト。
colors = st.multiselect(
    "色を選んでください",
    ["赤", "青", "緑", "黄", "黒", "白"]
)
# 選択した値はリストで取得される。
st.write(f"選んだ色: {colors}")
```

## 7. スライダー
```python
# スライダーで数値を選択する。
# 0歳から100歳まで5歳刻みで選べる設定（初期値は20歳）。
age = st.slider('年齢を選択してください', 0, 100, 20, 5)
st.write(f"選んだ年齢: {age}")
```

## 8. 日付入力
```python
# 日付を選択する。
date = st.date_input("日付")
st.write(f"選択した日付: {date}")
```

## 9. 時間入力
```python
# 時刻を入力する。
time = st.time_input("時刻")
st.write(f"設定した時刻: {time}")
```

## 10. テキスト入力（1行）
```python
# 1行のテキストを入力する。
text_single = st.text_input("テキストを入力してください。")
st.write(f"入力内容: {text_single}")
```

## 11. テキスト入力（複数行）
```python
# 複数行のテキストを入力する。
text_multi= st.text_area("テキストを入力してください。")
# 出力はマークダウン形式で表示される。
st.write(f"入力内容: {text_multi}")
```
プログラミング初学者

# 初投稿
今回初めてQiitaに投稿するわけですが、現時点でプログラミングの勉強をしているので記録として残していきます。

# とりあえず1000時間
プログラミング初学者の私ですが、25年2月16日時点でのトータル勉強時間が約300時間くらい。いつかはIT業界への転職を考えているので、スタートラインに立つため1000時間の勉強時間を目標にしています。
ただ勉強するわけでなく、教材を2周ほどし自作のアプリケーションを作っていこうと思います。

# 勉強時間0～200時間
2024年10月から12月まで、プログラミングスクールに通いました。
前半はHTML\CSS、後半はバックエンドのRuby（Sinatra）とPostgreSQLを使いオリジナルアプリを作成しました。
スクールの推奨ではX(旧Twitter)やインスタグラムのようなアプリでしたが、ホテルシステム(Property Manegement System)風なアプリを作成しました。

# 勉強時間200時間～300時間
スクール卒業後、現在独学でRailsの教科書とRuby on railsチュートリアル、プロになるためのWeb技術入門、プロを目指す人のためのRuby入門を中心に学習しています。

正直、知識量が半端ないので挫折しそうな毎日ですが、こつこつめげずに頑張ろうと思います。やはり一人では孤独＆質問ができないので、プログラミングスクール卒業生の集まり（勉強会）や、Okinawa.rbというコミュニティに時々参加しています。

# 参考にしている教材紹介
[Railsの教科書](https://railstutorial.jp/textbook/preface)

https://railstutorial.jp/textbook/preface

[Ruby on Rails Tutorial](https://railstutorial.jp/chapters/beginning?version=7.0#cha-beginning)

https://railstutorial.jp/chapters/beginning?version=7.0#cha-beginning

[プロを目指す人のためのRuby入門](https://ruby-book.jnito.com/)

https://ruby-book.jnito.com/

[プロになるためのWeb技術入門](https://gihyo.jp/book/2024/978-4-297-14571-2)

https://gihyo.jp/book/2024/978-4-297-14571-2

# 最後に
これから、学んだ知識や分からないことがあればQiitaに投稿し、先人たちの知恵を拝借していこうと思います。（図々しくてごめんなさい）
地方でエンジニア転職成功までの記録

# 未経験30代からのエンジニア転職への挑戦
---

## はじめに

地方で未経験30代からのエンジニア転職を目指して勉強中の者です。昨日が現職での最終出勤日でした。

仕事を辞め、腹を括ってフルコミットすることに決めましたが、正直不安はあります。ただ同時に、やりたいことへのチャレンジにワクワクする気持ちも大きいです。

転職までの間にやりたいこと・作りたいものがたくさんある為、スピード感を持って取り組んでいきたいと思います。

知識の定着化と転職までの記録が目的のため、投稿に多くの時間をかけるつもりはありませんが、「絶対にエンジニアになる」という強い決意とともに、これから少しでも発信していこうと思います。

---

## スケジュール感

自分の中で様々な選択肢を模索した結果、今回はスクールに通いながら、ポートフォリオを3つ作成し、転職活動を成功させる道を選びました。

現在考えているポートフォリオ案は以下の3つです：

1. **ラーメンランキングサイト** 📊  
   自分が純粋に作りたいと思ったサービス。

2. **勤怠管理システム** ⏰  
   実家（飲食店）の困りごとを解決するシステム。現在独学で開発進行中。

3. **買い物メモアプリ** 📝  
   自分が日常で使いたいと思ったサービス。長期的な運用・収益化も目指したい。

これらのポートフォリオは明確な目的意識を持って取り組み、「まずはしっかり完成させて、実際に運用する」ことを目標とします。

約3ヶ月間で3つのポートフォリオを作るのはかなり厳しいチャレンジだと認識していますが、スピード感を持ってやり抜きます。（凝り性な自分の性格を自覚し、ミニマム開発を意識して進めること！）

---

## 終わりに

未経験からの転職に対して不安はありますが、それでも人生で最大と言えるこのチャレンジ、やっと見つけることができた「本気で取り組みたい」と思える仕事への挑戦を楽しみたいと思います。

この道のりを楽しみながら、支えてくれる周りの人たちへの感謝を決して忘れずに。

**本気で、確固たる決意と情熱を持って、これからのエンジニア転職に向けて全力で取り組んでいきます！** 💪
docker コンテナ内でnpm install中、「npm warn tar TAR_ENTRY_ERROR ENOSPC: no space left on device, write」が出たら

## 結論
「no space left on device, write」とある通り、Docker が使える領域の不足が原因。
Docker 関連の不要データを削除して対応する。
## 手順
### 1. 実行中のコンテナを全停止
```    
docker stop $(docker ps -aq)
```    
    
### 2. 未使用のコンテナ、イメージ、ネットワーク、ボリュームを削除
```
docker system prune -af --volumes
```
* -a：未使用イメージ含むすべて削除
* -f：確認プロンプトなしで削除
* --volumes：未使用ボリュームも削除
## おまけ
45GBも溜まってた、たまには掃除しよう

    Deleted build cache objects:
    ua4vhqvsp2d7llxv4q0qrh100
    6smib8bz6fb3wyixxfq35czee
    ....
    zahxlctv5hl0vo802d6r4h0w6

    Total reclaimed space: 44.91GB
[初学者向け]意外と難しいSwiftのデリゲートについて

## はじめに

Swiftでアプリを開発していると「デリゲート（Delegate）」という概念を目にすることが多いです。デリゲートは、オブジェクト間の通信をスムーズに行うためのデザインパターンで、特にUIKitの開発でよく使われます。

学習内容の整理も兼ねて、デリゲートの基本概念とシンプルなサンプルコードを使って初心者向けに解説します。

---

## デリゲートとは？

デリゲートとは、あるオブジェクトの処理を他のオブジェクトに委譲する仕組みのことです。主に以下の場面で使われます。

- テーブルビュー（UITableViewDelegate）
- テキストフィールドの入力管理（UITextFieldDelegate）
- カスタムクラス間のデータ受け渡し

**デリゲートを使うことで、以下のメリットがあります。**
- クラス間の結合度を下げることができる  
- コードの再利用性が向上する  
- シンプルで直感的なイベント管理が可能  

---

## デリゲートの基本実装

1.プロトコルを定義する

```swift
protocol SampleDelegate: AnyObject {
    func didUpdateMessage(_ message: String)
}
```

2.デリゲートを持つクラス（委譲元）を作成

```swift
class Sender {
    weak var delegate: SampleDelegate?
    
    func sendMessage() {
        let message = "Hello from Sender!"
        delegate?.didUpdateMessage(message)
    }
}
```

3.デリゲートを受け取るクラス（委譲先）を作成

```swift
class Receiver: SampleDelegate {
    func didUpdateMessage(_ message: String) {
        print("Received message: \(message)")
    }
}
```

4.デリゲートをセットして動作を確認

```swift
let sender = Sender()
let receiver = Receiver()

sender.delegate = receiver
sender.sendMessage()
```

実行結果
```
Received message: Hello from Sender!
```

---

## まとめ

| 項目 | 内容 |
|------|------|
| デリゲートとは？ | 他のクラスに処理を委譲する仕組み |
| メリット | クラス間の結合度を下げ、再利用性を高める |
| 実装の手順 | ①プロトコル定義 ②委譲元クラス ③委譲先クラス ④デリゲート設定 |

デリゲートはUIKit開発で頻繁に使用される重要なパターンです。
基本を理解すれば、Swift開発の幅が大きく広がるので、ぜひ試してみてください！
[Power Automate for Desktop] Microsoft の個人アカウントで作成したフローを、組織アカウントに移行する方法メモ

## 前提
- Power Automate for Desktop にフローをエクスポート／インポートする機能はない
- 個人アカウントの場合、Windows のどこに Power Automate for Desktop のフローが保存されるのか不明

## 個人アカウントのフローを組織アカウントへ移行する方法
答え：コピペ！

![スクリーンショット 2025-02-17 011837.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629955/5049a153-6d05-408b-b720-e904eda91388.png)


## 手順
1. 個人アカウントの Power Automate for desktop で移行したいフローを開く
1. フローの編集画面で `Ctrl` + `A` → `Ctrl` + `C` の順に押下し、全てのアクションをコピー
1. テキストエディタで`Ctrl` + `V` を押下し、テキストをペースト
1. このテキストファイルを保存し、組織アカウントの環境に送る
1. 組織アカウントで Power Automate for desktop を開き、フローを新規作成
1. テキストファイルのテキスト全文を、Power Automate for desktop のフロー編集画面にペースト

なおサブフローが存在する場合は、各フローごとに全アクションのコピーとテキストファイルへのペーストを行う必要がある。

また、組織アカウントで接続や認証情報の再設定が必要。
競プロ日記#25/02/15

# アルゴ式-[ダーツ](https://algo-method.com/tasks/9c9448bfdbed406a/editorial)
- ダーツが外れた場合、つまり0点の場合も考慮する。
- また、3回の投擲のうち同じ点数を複数回取得する場合や、それに伴って得点の仕方が異なるが合計点が同じになる場合なども考慮する必要がある。

ポイントは
```
vector<int> scores(N + 1);
    scores.at(0) = 0;
    for (int i = 1;i <= N;i++)
    {
        cin >>  scores.at(i);
    }
```
N + 1個の整数型配列を用意し、[0]に0を代入した上でfor文でN個の標準入力を得る。
あとは以下の様にN + 1回のfor文を3重でループして、合計点が同じ場合は排除したいのでset型（集合型）を定義して.insert()でついかしていく
```
set<int> sumScores;
    for(int i = 0;i <= N;i++){
        for (int j = 0;j <= N;j++){
            for (int k = 0;k <= N;k++){
                int sum = scores.at(i) + scores.at(j) + scores.at(k);
                sumScores.insert(sum);
            }
        }
    }
```
2年目インフラエンジニアが読んで良かった書籍6選

# はじめに

はじめまして！入社2年目のSIer勤務のひよっこインフラエンジニアです！

2024年度ももうすぐ終わりが近づいていますね。
私はもうすぐ3年目になるということで、配属後はちんぷんかんぷんだったPJチームの会話が、ある程度理解できるようになってきたところです。

そんな私がこの1年間で読んで、これは勉強になった！と思う書籍を紹介します！（いわゆる名著はできるだけ避けて、個人的に特に良いと感じた書籍を取り上げます）

インフラエンジニア1年目の方、あるいはもうすぐインフラエンジニアとして新社会人になる方の参考になれば幸いです！

⚠️クラウドに関する書籍は1冊のみです！
個人的な印象ですが、頻繁にアップデートが入ることや、手元で試すときにできるだけ最新情報が欲しいことからクラウドに限っては[Udemy](https://www.udemy.com/)の方がおすすめと感じています。


# おすすめ書籍（技術系）

## 3分間基礎講座シリーズ

https://gihyo.jp/book/2010/978-4-7741-4373-6

[こちら](https://www5e.biglobe.ne.jp/aji/3min/)のHPにて紹介されていたネットワーク系統の知識を書籍としてまとめたものになります。

個人的にはこの書籍シリーズが今年度のMVPだと思っています！

インフラエンジニアにとってネットワークの知識が重要ということはよく言われているかと思います。とは言っても技術書をただ読むだけでは暗記になる感が否めなく、個人的に苦手でした。

この書籍は元のHPを踏襲したイラスト豊富な会話形式で、もしこうしたらこうなるからダメ、ここの知識は以前紹介したこの要素と絡んでいるなどと、ネットワークに馴染みのない私でもかなりわかりやすかったです！！

おすすめ度：★★★★★

## インフラ／ネットワークエンジニアのためのネットワーク技術＆設計入門
https://www.sbcr.jp/product/4797396805/

タイトルの通り、サーバーシステムを設計する際に考慮すべき点をまとめた書籍になります。

この本の良いところは、実務に沿った内容かつそこで使われているネットワーク技術が丁寧に解説されている点です。

このケースにはこの構成、そこで使われている技術は、、、と言ったような形でスムーズに学習することができます！

この本を購入したのは2年目の頭でした。ネットワーク図も実務に慣れてきた今ではサッと読めますが、当時の私としてはちょっとレベルが高いかもと感じました。

しかし、前述の3分間シリーズからステップアップしたいならおすすめだと思います！

おすすめ度：★★★★☆



## Amazon Web Services基礎からのネットワーク＆サーバー構築

https://bookplus.nikkei.com/atcl/catalog/23/04/06/00757/

ネットワークを学習したのはいいものの、自分が触れる環境で実践してみたいと思い、後述のUdemy講座と併用で購入しました！

クラウド上であれば簡単に実践できるので、前述の書籍で溜まった知識（IPアドレスやサブネットなど）が活用でき、個人的には面白かったです。

内容もインフラエンジニアの初心者にとっつきやすいの内容になっていますし、何より自分で環境を構築して進めて行く感じがとても楽しかったです！

おすすめ度：★★★★★

ちなみに冒頭の方で、クラウドに限ってはUdemyがおすすめと記載しましたが、この書籍と代用して下記の講座1つでも良いと思います！実画面や豊富なスライド、注意点が丁寧に紹介され、初めてAWSを触る身でしたが問題なくハンズオンができました！

https://www.udemy.com/course/aws-and-infra/?couponCode=KEEPLEARNING

# おすすめ書籍（技術系以外）

## 「技術書」の読書術 達人が教える選び方・読み方・情報発信＆共有のコツとテクニック

https://www.shoeisha.co.jp/book/detail/9784798171548

タイトルで惹かれた人が自分以外にもいるんじゃないかな〜と思っています笑

社会人になり、上記に紹介させていただいた書籍を読む中で気になったのは書籍の読み方でした。

というのも、書籍によっては私のようは駆け出しエンジニアを対象にしたものもあれば、10年以上働かれているベテランの方を対象にしている書籍もあるかと思います。

内心、早くステップアップしたいと思い、今の自分よりも明らかにレベルが高い本を身につけるのにはどうしたらいいかと思い、この書籍を購入しました。

タイトルには「読書術」とありますが、本の読み方以外にも本の選び方やアウトプットの仕方など、一通りの方法が記載されています。

正直この書籍に2年目の段階で読むことができて良かったと感じています！

おすすめ度：★★★★★



## 世界一流エンジニアの思考法

https://www.amazon.co.jp/%E4%B8%96%E7%95%8C%E4%B8%80%E6%B5%81%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%AE%E6%80%9D%E8%80%83%E6%B3%95-%E7%89%9B%E5%B0%BE-%E5%89%9B/dp/4163917683

ITエンジニア本大賞2025にて特別賞を受賞したので、ひょっとしたらすでにご存知の方もいるかと思います。

世界一流のIT企業で働いている方がどのように過ごされているかってすっごく気になりませんか？
この本の著者はなんとアメリカのマイクロソフトのシニアエンジニアの方です！

1日の限られた労働時間の中で、どれだけ生産性を上げるために何をしているのかをマインドセット・記憶術・コミュニケーションなどのカットから記載されており、非常に参考になりました！

忘れた頃に定期的に読み返そう、と思うくらいにはとても良い本でした！

おすすめ度：★★★★★



## 解像度を上げる

https://eijipress.co.jp/products/2318

業務を行ううち、技術面にせよビジネス面にせよ、思うようにいかず詰まることもあるかと思います。
私の場合、とりあえず思い浮かんだ解決策を試してみるが時間がかかってしまい上司から進捗を気にされてしまうことがしばしばありました。

その時に上司から、起こっている事象に対しての理解度・解像度を上げた方がいいかもしれないとアドバイスいただいたことがこの本を手に取るきっかけでした。

この本の良かったところは、ビジネス本でありながら業務以外に日常にも転用できる点です。
タイトルの通り、曖昧な思考になっているところから「深さ」「広さ」「構造」「時間」の４つの視点から物事を考え、解像度を高めることができると思います。

おすすめ度：★★★★☆

# 終わりに

以上、私が2年目で読んで良かった書籍を紹介させていただきました。

私個人としてはとても苦労した一年だったので、こちらで紹介した書籍が今後インフラエンジニアとして活躍されて行く方にとって役に立つものであればなと思います！

ここまで読んでいただきありがとうございました！
gitのチーム開発

# gitを使ってチーム開発するときに気を付けるところ
### 1.コミットメッセージとブランチ名はわかりやすくする
さまざまな人と開発を行っていくため,一目見るだけでどのようなものなのかがかかるようなコミットメッセージやブランチ名を付けることがチーム開発では重要である.

### 2.mainブランチにコミットしてプッシュしない
mainブランチにそのまま変更内容をコミットしてプッシュしていまうとコンフリクトが発生しやすくなったり,コードの巻き戻しが難しくなってしまうため作業ブランチを作成してからそこで作業し,その作業ブランチをmainブランチにマージするようにすべきである.

### 3.Pull Requestを出して他の開発者に見てもらう
自分の作業ブランチをmainブランチにマージするときに,Pull Requestを出して他の開発者に見てもらうようにすべきである.他の開発者に見てもらうことによりコードのバグやセキュリティ対策やメンバーの進捗状況を把握できるメリットがある.

### 4.コンフリクトが起きないようにgit pullを頻繁にする
ローカルリポジトリにリモートリポジトリの内容をプルして更新しておくことでコンフリクトが起きずらくなる.
Laravel　「Permission denied」のエラーでブラウザ表示ができない時の解決法

XAMPPでLaravelのWebアプリを開発しようかなーと思った矢先にエラーで挫折しそうになったので、ここにメモしておく。
　

まず初めにXAMPPへデプロイした際に出てきたエラーの内容はこんな感じ。
　

file_put_contents(/Applications/XAMPP/xamppfiles/htdocs/プロジェクト名/storage/framework/views/95f7127b54544d8bcf9a632a1de54c4e.php): Failed to open stream: Permission denied
　
　
とりあえずPermission deniedって書いてあったら、編集権限がないよーというエラーである。

　
パーミッションをコマンドラインから変更する時どーするんだっけなーと毎回調べる下記のコードでパーミッションを変更する。

```
sudo chmod 777 ファイル名orディレクトリ名
```

上のエラーの場合

```
sudo chmod 777 /Applications/XAMPP/xamppfiles/htdocs/プロジェクト名/storage/framework/views/
```

これで解決した。

　
次に出てきたエラーはこんな感じ。

The stream or file "/Applications/XAMPP/xamppfiles/htdocs/プロジェクト名/storage/logs/laravel.log" could not be opened in append mode: Failed to open stream: Permission denied The exception occurred while attempting to log:〜あーだこーだ

　
 これもさっきと同じ要領で下記のように書いて解決。

 ```
 sudo chmod 777 /Applications/XAMPP/xamppfiles/htdocs/プロジェクト名/storage/logs/
 ```

 :::note warn
警告
XAMPPを使ったローカル環境での開発だから777で設定しちゃってます。
本番公開する際は、安易に777を設定しちゃだめ。
:::

　
 これでやっと正常に表示されるかと思ったらさ、

 Illuminate\Database\QueryException
 
 SQLSTATE[HY000]: General error: 8 attempt to write a readonly database (Connection: sqlite, SQL: update "sessions" set "payload" =　〜あーだこーだ


ちょっとググったら、どうやらmysqlが接続されていないというエラーらしい。

.envファイルを見てみると

```
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=
```
こんな記述を発見したので、

```
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
```
上記のように、”sqlite”を”mysql”に変更し、コメントアウトされている部分を解除。

　
すると、また次のエラー。

　
SQLSTATE[HY000] [2002] Connection refused (Connection: mysql, SQL: select * from `sessions` where `id` = 92uOlagoTUifczmprxySArf5EZfPp3wJQSPQxwI6 limit 1)

　
はーん。

　
ここは勘が鋭くて、ポートが違うのかもなぁと思ったらその通り。

XAMPPのMySQLのConfigureを確認したところ、ポート番号が3307でした。
　


.envファイルの該当箇所を

```
DB_PORT=3307
```
に修正して
```
php artisan config:clear
```
でキャッシュクリア。

　
しかし、まだ映らない。

　
.envファイルでSESSION_DRIVER=databaseと指定しているので、DBにsession用のテーブルがないのかも思い、色々ググってみたところ、マイグレーションってのをしないといけないらしい。
たしかに、DBの接続だけしてDBの設定は何もしないんだなーと思っていた。


マイグレーションのコマンドはこちら。

```
php artisan migrate
```

これを打つとテーブルないけど作っちゃってOK？みたいなメッセージがでるので、そのままエンターを押す。

そうすると、色々テーブルが作られたので、再度ブラウザをリロード。

　
やっと映った。

　
こんなの青本に書いてなかったよ。

この記事を書くのに、1時間半くらいかかってるし、laravelをインストールしてXAMPPを使ってブラウザ表示するだけで丸一日かかった。

　
弱弱エンジニアのくせに挫折しなかった私を褒めたい。チョコ食べたい。

　
この記事が誰かの役に立つことを心から願っている。
OllamaとContinueを利用したdevcontainerのテンプレート

:::note warn
注意
この環境でのモデルの精度やパフォーマンスはPCのスペックに依存します。
モデルに対してスペックが不足している状態で利用すると、回答が全く返ってこないなどの事象が当然のように発生するため、自分のPCのスペックに合わせてモデルを選んでください。
:::

# 初めに
皆さんGitHub Copilot使っていますか？
筆者はGitHub Copilotをコーディングの補助などでかなり利用しています。
今となっては、なしの生活に戻れる気がしない・・・

とはいっても、実際に利用しようとすると、制限のあるFreeプランを使うか、課金するかの2択になってしまいます。
そうなると、制限におびえながら使うのもなんだかなぁと思っていたところ、ContinueというGitHub Copilotのように、コードの作成などを補助する拡張機能で、ローカルLLMのOllamaが使えることを知りました。


ということで、今回はOllamaとContinueを使って、devcontainerを利用するためのテンプレートを作ってみました。

# 今回のテンプレートの構成
今回は、docker-composeをdevcontainerで開いていく形で作っています。

# 使い方
## テンプレートを入手
以下のGit Hubからプレートを入手。（クローンしていただければ問題ないです）
https://github.com/togra1997/llm_dev_template

入手したフォルダをVscodeで開く
Vscodeで入手したフォルダを開いてください。
開いた際に、コンテナで再度開くという表示が出るのでクリックするとDev Containerが開きます。![スクリーンショット 2025-02-17 0.01.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780680/3c8c1551-fef3-4f07-8a25-6790d9173ba9.png)

## ollamaで使用するモデルのダウンロード
初回のみOllamaで利用するモデルをダウンロードします。
Ollamaのコンテナにアタッチ(VscodeだとAttach Shell)して以下のコマンドを入力します。
![スクリーンショット 2025-02-17 0.13.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780680/02449913-7b26-47fa-a9e2-d36e27a9873d.png)

この際、HugginfaceにあるモデルGGUFも使えます。
```bash
# Ollamaにあるモデルを使う
ollama run 動かしたいモデル
```
```bash
## Hugging faceを使う場合
ollama run hf.co/{ユーザー名}/{リポジトリ}
```

## Dev ContainerでContinueを利用する
モデルをダウンロードするとDev Container内のContinueでダウンロードしたモデルを使うことができます。
![スクリーンショット 2025-02-17 0.16.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780680/31b2dbc5-b791-4071-ac39-4c6f1a39aff6.png)
あとは、チャットボックスを使ってContinueを利用しましょう！

<details><summary>GPUを利用する場合</summary>

以下のページを参考にGPUを使えるようにした上で、dockercompose.yamlにあるコメントアウトを解除してください。
https://hub.docker.com/r/ollama/ollama

</details>


# 参考文献
https://note.com/schroneko/n/n6a7c34f0a50c

https://qiita.com/Tadataka_Takahashi/items/0485159e444056892529

https://qiita.com/Tadataka_Takahashi/items/5ae89d641bc0e460f313
ReactとNext.jsでデフォルトと名前付きエクスポートをimport(インポート)する時の注意点

# 名前付きエクスポート (export {})
そのファイルで***複数のエクスポート（他のボタンコンポーネントなど）がある時***に使います。

```PrimaryButton.tsx
const PrimaryBtn = ({children} : PropsType  ) => {
  return (
    <Button 
        bg="lime.800"
        color="white" 
        p="8" 
        borderRadius="lg" 
        fontSize="20" _hover={{ bg: "lime.700" }}>
            { children }
    </Button>
  )
}

export default PrimaryButton;
```

# デフォルトエクスポート(export default)
そのモジュールに***1つのエクスポートしかない***時に使います。
```PrimaryButton.tsx
export const PrimaryBtn = ({children} : PropsType  ) => {
  return (
    <Button 
        bg="lime.800"
        color="white" 
        p="8" 
        borderRadius="lg" 
        fontSize="20" _hover={{ bg: "lime.700" }}>
            { children }
    </Button>
  )
}
```


# importの書き方の違い
### 名前付きエクスポートをインポート
```{}```を使ってインポートします。
```tsx
import { PrimaryButton } from "@/components/atoms/PrimaryButton.tsx";
```
### デフォルトエクスポートをインポート
特に ```{}などはつけない``` です。
```tsx
import  PrimaryButton  from "@/components/atoms/PrimaryButton.tsx";
```
「入力したURLと違うページが表示された」→httpのリダイレクトの挙動を確認してみる


2025/02/16

## 状況
- URL欄へ入力したのと違うページが表示された。という相談を受けた。

## 目的
- 身近な例で挙動を説明する。
- 開発者ツールでの可視化の方法を連携する。
- 必要性や注意点の情報を連携する。

## 事象
- 「 http://www.google.co.jp/ 」をブラウザで入力してエンター。
- 「 https://www.google.co.jp/ 」が表示された。( ﾟДﾟ)

## 挙動可視化
- F12で開発者ツールを開く。ネットワークタブで「ログを保持」「キャッシュを無効化」にチェック。
- 「 http://www.google.co.jp/ 」をブラウザで入力してエンター。
- 「 https://www.google.co.jp/ 」が表示された。( ﾟДﾟ)
- httpのステータスコード300系があることを確認する。
  - ステータスコードの意味は以下のような公式ドキュメント参照。

https://developer.mozilla.org/ja/docs/Web/HTTP/Status

## イメージ

ツール開いて

![http300_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562068/f18f5d52-b69f-48bd-9f91-8dc16a456bac.png)

↓エンター

![http300_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562068/4d14b133-a5ee-4478-ba1c-1308a996ca44.png)

↓300系のところクリック

![http300_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562068/0947a14e-db29-4f44-b0f6-2522df036398.png)

↓こんなやり取りのイメージ

![http300_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562068/45ad2ba3-0e6e-41c6-b7cb-e5864d7cfee8.png)

↓200系のところクリック

![http300_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562068/7bc83890-3f33-46e2-9f76-efd7d3ee5102.png)

↓こんなやり取りのイメージ。自分はエンターを一回しか押してないのに、ブラウザがサーバへ2回リクエストを投げている。

![http300_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562068/4e3e02af-f022-4a8f-9295-aa3dd96e3362.png)

## 必要性や注意点
以下のような公式ドキュメント参照。
今回の例はセキュリティ的な意味でhttpのプロトコルを利用しないように、セキュアな通信へ誘導するものでした。

https://developer.mozilla.org/ja/docs/Web/HTTP/Redirections

以上
Nordic nRF9160 DKをはじめよう5～1nceOS編1～　

__Nordic nRF9160 DKをはじめよう4～toolchains&SDK追加編～__ の続きです。

https://qiita.com/usashirou/items/6f62cebbae64e1b7fc28

今回は __1nceOS__ を使ってみましょう。
# 1nce OSのインストール
__Manage toolchains⇒Open Terminal Profile__ から任意のディレクトリに __Github__ のプログラムを __ダウンロード__ します。
__Manage toolchains__ を __クリック__ し、__Open Terminal Profile__ を __クリック__ します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/3796d6dd-1242-346f-5ba6-dcf5f59f77d6.png)
任意のディレクトリにて

    git clone https://github.com/1NCE-GmbH/blueprint-zephyr.git
を実行し、__Github__ のデータを __クローン__ します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/fd250754-232a-6903-163d-3bd0cdc35582.png)
__Open an exsiting application⇒nce_lwm2m_demo__ を選択し、__Open__ を __クリック__ します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/8e35fe56-bfa7-dbfa-3a8e-5beed09c4b61.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/1937127d-12fc-0a44-3f35-fa2dd70bef36.png)
__SDK__ と __Toolchain__ を __v2.2.0__ であることを確認し、__Board target__ を __nrf9160dk_nrf9160_ns__ を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/7f246389-e0b8-f527-977d-3123f76662af.png)
__Build Configuration__ を __クリック__ します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/2f0c329a-0bf6-af0f-129e-e1e3b707dd82.png)
# 接続できていない場合
このまま接続した場合、下記のようにエラーが出ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/7f368235-61a1-e8ee-998f-a8d3af2d73dc.png)
このためprj.confを編集します。
__９６・９７行目をコメントアウト__ しICCIDとPSKを使用しない __Unsecureモード__ にします。
```
CONFIG_LWM2M_DTLS_SUPPORT=n
CONFIG_LWM2M_CLIENT_UTILS_SERVER="coap://lwm2m.os.1nce.com:5683"
```
編集後、__Build⇒Flash__ します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/eaef7d3a-b2b0-9bab-02e3-d143505cfa3b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/44b24dbf-b7c2-87ec-5943-29409cc72fd3.png)
# 接続できている場合
接続できた場合、以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/e0a5d456-7902-8f7c-7198-c7f7ccdf875e.png)
# 1nce OS
次は、1nceのサイトから見てみましょう。
ブレイクアウト設定をヨーロッパ（フランクフルト）にしないと1nce OSの認識がされません。

__ブレイクアウト設定⇒ヨーロッパ（フランクフルト）__ 保存を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/aaee4d15-56f5-85ad-e9c5-45007cade8ae.png)
Nordic nRF9160 DKをはじめよう6～1nceOS編2～に続きます。
Cognitoのマネージドログインをnext.jsで実装

# はじめに
Cognitoのマネージドログインをnext.jsで実装してみました。next.js初心者なのであまり自信ないですが、参考になれば幸いです。実際のCognitoと連携できるかは確認できていないソースなので注意です。

# ソース
```page.tsx
"use client";
import { useAuth } from "react-oidc-context";

function Page() {
  const auth = useAuth();
  console.log("page.tsx起動");

  const signOutRedirect = () => {
    const clientId = "YOUR_COGNITO_CLIENT_ID";
    const logoutUri = "http://localhost:3000";
    const cognitoDomain = "YOUR_COGNITO_DOMAIN";
    window.location.href = `${cognitoDomain}/logout?client_id=${clientId}&logout_uri=${encodeURIComponent(
      logoutUri
    )}`;
  };

  if (auth.isLoading) {
    return <div>Loading...</div>;
  }

  if (auth.error) {
    return <div>Error: {auth.error.message}</div>;
  }

  if (auth.isAuthenticated) {
    return (
      <div>
        <p>Hello: {auth.user?.profile.email}</p>
        <button onClick={() => auth.removeUser()}>Sign out</button>
      </div>
    );
  }

  return (
    <div>
      <button onClick={() => auth.signinRedirect()}>Sign in</button>
      <button onClick={signOutRedirect}>Sign out</button>
    </div>
  );
}

export default Page;

```
```AuthProviderWrapper.tsx
"use client";
import React from "react";
import { AuthProvider } from "react-oidc-context";

// layout.tsxでMetadataを使用するためには"use client";を書いてはいけない
// AuthProviderを使用するためには"use client";が必要
// →AuthProviderを別コンポーネントに分離
export default function AuthProviderWrapper({
  children,
}: {
  children: React.ReactNode;
}) {
  console.log("AuthProviderWrapper.tsx起動");
  const oidcConfig = {
    authority: "～",
    client_id: "～",
    redirect_uri: "～",
    response_type: "～",
    scope: "～",
    extraQueryParams: {
      ui_locales: "ja",
    },
  };

  return <AuthProvider {...oidcConfig}>{children}</AuthProvider>;
}
```
```layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import AuthProviderWrapper from "./AuthProviderWrapper";
import "./globals.css";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  console.log("layout.tsx起動");
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <AuthProviderWrapper>{children}</AuthProviderWrapper>
      </body>
    </html>
  );
}
```
# おわりに
TypeScript、React、next.jsもっと勉強が必要そうです。
Neovim のターミナルでコマンドが完了したら通知する

## 裏で動いているコマンドが完了したら通知が欲しい

<img width="524" alt="スクリーンショット 2024-11-09 15.05.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8880/12ec0421-0923-10f4-0942-af4cf89706d5.png">

小ネタ。

長く掛かる作業、例えば、コンパイルとかデプロイとか大きなファイルの圧縮とか、そういう作業を開始した後、待つ間に別のタブやアプリで作業を行う、こういうことは良くあると思います。その時に、開始しておいた作業が完了したら何らかの通知が欲しいです。

こういうソリューションはすでにあり、fish を使うなら [franciscolourenco/done](https://github.com/franciscolourenco/done) が有名です。僕もこれ使ってたのですが、一度動かなくなった時に理由が分からず、コードを見ようとしたら複雑過ぎて断念したので代替策を探していました。

すると、↓こちらの記事に同じような課題感から独自に機能を実装してるコードがあり、これを修正して僕の環境で動くようにしました。

> [Fish Shellの設定ファイルを見直して起動時間を 470ms -> 14.7ms に短縮した話](https://zenn.dev/ryoppippi/articles/de6c931cc1028f)

## 「裏」で動いているかどうか判定する

先に載せた記事のコードには一点不満があり、それは、「現在まさに WezTerm で作業中のコマンドが完了した場合も通知が来る」というものでした。Neovim で文書を書いていて、それを終了させた時も通知が来ます。終了させたのは自分ですから通知は必要ありません。通知は「裏側」で動いているコマンドが終了した時のみにしたいのです。

僕は表題の通り、[WezTerm](https://wezfurlong.org/wezterm/index.html) を使っています。WezTerm で上記の問題を解決するなら以下のような方法を使えば良さそうです。

* コマンドが実行された pane の ID とアクティブな pane の ID が異なる時だけ通知する。

これだけですね。

しかし、一つ問題があります。僕はセッションを永続化するために [Multiplexing](https://wezfurlong.org/wezterm/multiplexing.html) 機能も利用しており、平たく言うなら [tmux](https://github.com/tmux/tmux/wiki) のような使い方をしています。

この場合何が困るかと言うと、「アクティブな pane を判定するのが難しい」という点です。WezTerm は [`wezterm cli`](https://wezfurlong.org/wezterm/cli/cli/index.html) コマンドにより起動したシェルから様々な情報を取得できるようになっています。しかし、Multiplexing 機能を使っている関係上、一つのシェルが複数の WezTerm クライアントで表示される訳で、「今どのクライアントのどの pane がアクティブになっているか」は、シェルから知ることができません。

### OS の機能で「アクティブ」か判定する

また、別の問題もありました。いくら WezTerm 上でアクティブな pane だとしても、別のアプリで作業中の時は通知が欲しいです。ウィンドウ自体が見えなくなってるかも知れませんからね。

これらの理由から、OS の機能を使って「アクティブ」かどうかを判定することにしました。「アクティブ」ということは GUI ウィンドウが最前面に来ているはずですから、そのウィンドウを開いているプロセスが何か分かれば良いです。このために僕の完成版スクリプトは macOS 限定のものになっています（元の記事のスクリプトは Linux でも動きます）。

ChatGPT に「最前面にあるウィンドウのプロセス番号を知りたい」と聞いたら一発で教えてくれました。Apple Script は良く知らないので助かります。

```bash
# 最前面のウィンドウを表示しているプロセスの ID を得る
osascript -e 'tell application "System Events" to get the unix id of first process whose frontmost is true'
```

このプロセス番号で [`wezterm cli list-clients`](https://wezfurlong.org/wezterm/cli/cli/list-clients.html) を検索すれば、「アクティブな pane の ID」が分かります。もし WezTerm が最前面でないなら、結果は空になる訳です。

```bash
# 指定したクライアントが表示しているアクティブな pane の ID を得る
wezterm cli list-clients --format json | /usr/bin/ruby -r json -e 'puts JSON.parse($<.read).find{|x|x["pid"]=='$active_pid'}["focused_pane_id"]'
```

:::note info

WezTerm を nightly にした所、list-clients の書式が変わってることに気付きました。そのため、初稿より堅実な書き方に修正しています。

:::

## 完成版のスクリプト

長くなりましたが、以下のスクリプトが完成版です。これを `config.fish` に書いておけば、5 秒以上掛かったコマンドについて通知が来ます。

https://github.com/delphinus/dotfiles/blob/d01324d1ceb9bb45f6ea53b918f11acdf50019c4/.config/fish/config.fish#L207-L237

https://gist.github.com/delphinus/d1280fd9c9f32218f2c65477ec3640db

## 既知の問題

かなりレアなパターンですが、別の端末で接続しているクライアントのプロセス番号が現在の端末の最前面のウィンドウと一致すると、「裏側」なのに通知が出ちゃいますね。厳密にやるならばホスト名も含めて比較すべきでしょう。これは宿題にしときます。


## 終わりに

WezTerm、多機能ですが痒い所に手が届く作りになっていていいですね！
XserverのDBをBudibaseで使えるようにするまで

## XserverのDBをBudibaseで使用するまで

### 背景
Xserverは外部にSQLを公開する機能がないため、直接接続することができません。そこで、ポートフォワーディングを使用し、SSHトンネル接続を行うことで問題を解決します。

### 環境
- **Xserver**: レンタルサーバー
- **Budibase側**
  - AWS: t2.micro
  - Docker: Budibaseのホスティング

### 方法
BudibaseのDockerコンテナ内部でSSHポートフォワーディングを実行します。前提として、Budibaseをホスティングできている環境が整っていることとします。

---

### 手順

#### 1. コンテナへ入る
まず、Webアプリのコンテナを特定するため、以下のコマンドを実行します。
```bash
docker ps
```
すると、次のような表示がされるはずです。

```
CONTAINER ID   IMAGE                 COMMAND                  CREATED          STATUS          PORTS                   NAMES
20200f858a97   budibase/proxy        "/docker-entrypoint.…"   53 seconds ago   Up 50 seconds   80/tcp, 10000/tcp       bbproxy
cbee044be031   budibase/apps         "docker-entrypoint.s…"   53 seconds ago   Up 51 seconds   4001/tcp                 bbapps
597b48847e53   budibase/worker       "docker-entrypoint.s…"   53 seconds ago   Up 52 seconds   4001/tcp                 bbworker
```

今回は **bbapps** のコンテナを使用するため、以下のコマンドでコンテナに入ります。
```bash
docker exec -it bbapps bash
```

#### 2. OSの確認
コンテナに入ると、次のような表示になります。
```bash
8d146fc87543:/app#
```
どのOSが使われているかを確認するため、以下のコマンドを実行します。
```bash
cat /etc/os-release
```

結果として、**Alpine Linux** であることが判明しました。
```bash
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.21.2
PRETTY_NAME="Alpine Linux v3.21"
```

#### 3. SSHとautosshのインストール
ポートフォワーディングを行うために `ssh` を使用可能にし、また常時 `ssh` を維持する `autossh` もインストールします。
```bash
apk update && apk add --no-cache openssh autossh
```

#### 4. SSH接続のテスト
ポートフォワーディングの前に、通常の `ssh` で接続テストを行います。
※ 事前に `docker cp` コマンドなどで **秘密鍵** をコンテナ内にコピーしておくこと。
```bash
ssh -i /鍵のパス -p 10022 xserver@ホスト名.xserver.jp
```
鍵が正しく設定されていれば、SSH接続が成功します。

#### 5. SSHトンネルの作成
次に、以下のコマンドを実行し、Xserverの3306ポートをローカルの3307ポートにトンネル接続します。
```bash
autossh -M 0 -N -o ServerAliveInterval=60 -o ServerAliveCountMax=3 \
    -L 0.0.0.0:3307:localhost:3306 \
    -i /鍵のパス -p 10022 xserver@ホスト名.xserver.jp &
```
この設定により、
- **Xserverの3306ポート** が **ローカルの3307ポート** にマッピングされる
- `0.0.0.0:3307` を開放することで、他のサービスからのアクセスも可能になる

#### 6. Budibaseの設定
最後に、Budibase側で以下の設定を行います。
```
HOST: localhost
PORT: 3307
```
加えて、任意で **ユーザー名・パスワード・DB名** を入力すれば、正常に接続されるはずです。

---

### まとめ
これで、XserverのデータベースにBudibaseからアクセスできるようになります。AWSのセキュリティ設定で**アクセス可能なIPを制限する**ことで、よりセキュアな運用が可能です。
type-challenges解いてみた 初級 4-Pick編

https://github.com/type-challenges/type-challenges/blob/main/questions/00004-easy-pick/README.ja.md

### 問題

組み込みの型ユーティリティ`Pick<T, K>`を使用せず、`T`から`K`のプロパティを抽出する型を実装します。

例えば：
```ts
  interface Todo {
    title: string
    description: string
    completed: boolean
  }

  type TodoPreview = MyPick<Todo, 'title' | 'completed'>

  const todo: TodoPreview = {
      title: 'Clean room',
      completed: false,
  }
  
```

ユーティリティ関数Pickを実装せよという問題です。
回答は以下です

↓
↓















## 回答
```ts
type MyPick<T, K extends keyof T> = {
  [key in K]:T[key] 
}

```

簡単に解説をします。

- `K extends keyof T`
`extends`は「型引数に制約をつける」ものです。

https://typescriptbook.jp/reference/generics/type-parameter-constraint

つまり、ここでは`K`を`keyof T`であると型システムに知らせている形になります。
これによって、後続の`key in K`や`T[key]`がエラーとならずにすみます。

- `[key in K]`
`in`はここでは**Mapped Types**として使用されます。
`K`(＝`keyof T`)を反復している感じですね

https://typescriptbook.jp/reference/type-reuse/mapped-types
【Git】git stash pop実行時のneeds mergeエラーの対処法

## エラー内容

`git stash pop`を実行したところ以下のようなエラーが発生しました。

```
$ git stash pop
needs merge
unable to refresh index
```

## 対処法

`git add yourFile`を実行することでエラーが解消します。

## 参考

https://stackoverflow.com/questions/9739352/git-stash-pop-needs-merge-unable-to-refresh-index
Django・DRF・フロントエンド/バックエンドのイメージ理解

#### 私はプログラマーでも何でもないですが、Webアプリについて勉強することがあり、簡単なアプリをふわっと作れるようになってきた似非エンジニアです。そんな初心者視点での理解も参考になればと思い、簡単なイメージをまとめました。

## Djangoにおけるリクエストの流れとアーキテクチャ

Djangoにおいて、プログラム初心者の私の理解をまとめました。
一つじゃないよってことを理解するのに役立てばと思います

## ① 一つのサーバに全部構築
例えばDjangoをフルスタックで使う場合
#### HTML ⇒ ボタン ⇒ view.py ⇒ models.py　⇒DB

ユーザーがブラウザ上でボタンをクリックしたり、フォームを送信したりすると、リクエストがサーバに送られます。
そのリクエストは `view.py` で受け取り、処理を行った後に、データベースの操作（保存や取得）を `models.py` で行います。

- **models.py**: データベースの操作を担当します。DjangoのORM（Object-Relational Mapping）を使用して、データの保存や取得を行います。
- **view.py**: ユーザーからのリクエストを受け取り、適切なレスポンスを返します。ビューは、必要に応じて `models.py` を利用してデータベースの操作をします。

この流れでは、サーバがリクエストを受けてレスポンスを返す単純な構造となります。

## ② 一つのサーバに全部構築（フォームデータ処理）

#### HTML ⇒ ボタン ⇒ forms.py ⇒ view.py ⇒ models.py　⇒DB

この流れは、フォームデータを扱う場合に適しています。`forms.py` は、ユーザーが入力したフォームデータを検証したり、整形したりする役割を持っています。

1. ユーザーがフォームを送信すると、`forms.py` がそのデータを受け取ります。
2. `forms.py` では、バリデーションやクリーニングを行い、適切なデータに変換します。
3. 次に、`view.py` でそのデータを処理し、最終的に `models.py` を通じてデータベースに保存します。

この流れは、ユーザーからのデータ入力を処理する際に非常に便利です。`forms.py` を使うことで、コードが整理され、フォームの処理が簡潔に行えるようになります。

## ③ フロントエンドとバックエンドを分ける（APIアーキテクチャ）
### フロントエンド
#### HTML ⇒ ボタン ⇒ JavaScript (fetch関数) ⇒ JSONペイロードに乗せる
### バックエンド
#### router ⇒ JSONをシリアライズ（serializer.py） ⇒ models.py　⇒DB

この流れは、フロントエンドとバックエンドを分けて、APIを使った通信を行う構成です。フロントエンドがユーザーインターフェースを担当し、バックエンドがデータの処理を担当します。

1. **フロントエンド**: HTMLとJavaScriptを使ってユーザーインターフェースを構築し、ボタンをクリックすると `fetch` 関数を使ってAPIリクエストを送信します。このリクエストは、JSON形式でデータをサーバに送信します。
2. **バックエンド**: サーバ側では、`router` と `view` がAPIのエンドポイントを提供します。受け取ったデータは `serializer.py` でJSONに変換され、データベースに保存されます。

このアーキテクチャは、モダンなWebアプリケーションでよく使われる構成です。フロントエンドとバックエンドが疎結合になっており、データのやり取りがJSONで行われるため、柔軟性が増します。特に、モバイルアプリケーションや他のサービスと連携する場合に便利です。

---

### 結論

初心者ながらまとめてみました。
本当の初心者はいくつもwebアプリのアーキテクチャがあることに戸惑います。
だいぶ理解が進んできたので、まとめてみました。
③についてはちょっと怪しい部分があるのでまた整理してきます。
プロフィール生成AIのプロンプト設計と開発の裏側

# はじめに
こんにちは！
「WAKE Career」の開発に携わっているエンジニアのかなです。
今回は、最近リリースした「プロフィールAI生成機能」について、どんな思いで開発したのかと、どのように開発したのかをプロンプトエンジニアリングの観点で書いていきます！

# WAKE Careerとは
WAKE Careerは、すべての女性IT人材のエンパワメントとサポートを目的としたサービスです。転職支援やコミュニティの提供を通じて、誰もがバイアスに囚われることなく、自分の「なりたい」を実現できる世界を目指し運営しているサービスになります。

https://wake-career.jp/

# プロフィール生成AIの開発背景（Why）
## 課題
女性エンジニアのキャリア支援に取り組む中で、以下のような課題がありました。

- 自分のスキルや経験を低く見積もる傾向にある
    - 自分の技術力や実績を過小評価し、自信を持ちづらい
- 強みを認識していないため、何を書けばいいか分からない
    - プロフィールの自己PR欄などで、適切な表現が思いつかずなかなか筆が進まない
- 自由入力が面倒くさい

これらの課題を解決するために、プロフィール入力のハードルを下げつつ、自分の強みを発見し、転職活動に繋げられるような機能を開発しました。

## 実現したいこと
1. 入力のハードルを下げる
    - 選択肢を用意したり、簡単な箇条書きを入力するだけでAIが文章を生成する
2. 強みの発見の機会を提供する
    - AIが「こんな強みがありますよ」と提案し、自分では気づかなかったスキルを認識できる
3. エンパワメントな表現を用いてユーザーの背中を押す
    - 積極的な言葉や前向きな言葉を使い、ユーザーの転職活動の背中を押す

# どんな機能を作ったか（What）
[プロフィール情報の対象項目]
- 直近の開発実績
- 自己PR
- この先やってみたいこと

### 1. AIでプロフィール作成
対象項目について、選択肢や箇条書きで簡単に入力すると、AIが文章を自動生成します。これにより、文章作成が苦手なユーザーでも簡単に自分の経験や強みを表現できるようになりました。

### 2. 文章の磨き上げ
既存のプロフィール文章をAIが添削し、改善提案を行う機能です。
また、入力された文章を解析し、ユーザーの魅力ポイントを伝えてくれます。

詳しくは以下を参照ください↓

https://prtimes.jp/main/html/rd/p/000000035.000105436.html


# 技術的アプローチ（How）
ユーザーが入力した情報と、既にプロフィールに登録済みの「経験したことのある業種」や「開発言語」をもとに適切な文章を生成します。

文章生成には、Azure OpenAI を活用しました。
具体的な実装方法の詳細は割愛しますが、プロンプトエンジニアリングの設計についてご紹介します。

## プロンプト設計で意識したポイント
「自己PR」の文章生成と、文章の磨き上げ機能を例に意識したポイントを紹介します。

1. ソフトスキルに偏る場合があるので、技術的な強みもアピールするような指示をする
2. 本人の強みを気づかせるような文章を生成するように指示する
3. 成果を過小評価せず、しっかりと自信を持った表現にする
4. 採用担当者にしっかりアピールできるように、これまでの経験や強みを具体的に書くようにする
5. 出力される文章はポジティブな表現、背中を押す表現にする

特に女性は自分の成果を過小評価する傾向にあることや、ソフトスキルを中心に自己PRをしがちであることを踏まえ、普段多くの女性エンジニアと面談をしているbizチームの意見をもとに、そういったバイアスを排除した文章を生成するようにプロンプトを作成しました。

# まとめ
プロフィール生成AIの機能は、

- 入力のハードルを下げる
- 強みの発見をサポートする
- エンパワメントな表現でユーザーの自信を高める

といった目的を持ち、生成AIを活用して開発しました。
今後もさらにユーザーに寄り添った改善を行い、女性エンジニアが自信を持ってキャリアを築けるようなプロダクトを提供していきます！

ぜひ、この記事を読んで「WAKE Career」に興味を持っていただけたら幸いです。