タタッと!ドラゴンさん🐉

タタッ!と書いてく記事まとめ

なぜバルクインサート・バルクデリートで統一しないのか

なぜ「複数(バルク)対応」だけにしないのか? すべてを配列([{...}])で受け取るようにすれば、確かにエンドポイントは一つで済みます。 しかし、以下の点がネックになる。

エラーハンドリングが複雑になる: 3件送って「1件成功、2件失敗」したとき、レスポンスをどう返すべきでしょうか?

一部成功を許すのか(HTTP 207 Multi-Status などが必要)。

一つでもダメなら全部ロールバックするのか。 単体APIなら「成功(201)か失敗(400/500)」の2択で済むので、フロントエンド側の処理が圧倒的に楽になえう。

バリデーション(入力チェック)のコスト: 100件のデータを一気に送られた場合、サーバーのメモリやDBの負荷が急増します。単体用APIなら、一回のリクエストに対する負荷を予測・制限しやすくなる。

開発者体験(DX): たった1件更新したいだけなのに、わざわざ [ と ] で囲って送らなければいけないのは、地味にフロントエンド開発者のストレス(とバグの元)になります。

lazygit おすすめの設定

# 1. 差分表示の強化 (Delta)
git:
  paging:
    colorArg: always
    pager: delta --dark --paging=never
  commit:
    signOff: false
    autoWrapCommitMessage: true
    autoWrapWidth: 72
  merging:
    args: '--no-edit'  # マージコミットメッセージ編集スキップ

# 2. UI の見た目と使い勝手
gui:
  language: 'ja'            # 日本語化
  border: 'rounded'         # 角丸でモダンな枠線
  sidePanelWidth: 0.33      # パネル幅を黄金比に
  showIcons: true           # アイコン表示 (フォントが必要)

# 3. パフォーマンス向上
refresher:
  refreshInterval: 10  # 自動更新間隔(秒)
  fetchInterval: 60    # fetch間隔(秒)

# 4. キーバインド調整
keybinding:
  universal:
    quit: 'q'
    return: '<esc>'
    nextTab: ']'
    prevTab: '['
  commits:
    copyCommitMessageToClipboard: '<c-y>'  # コミットメッセージをコピー

# 5. 作業を爆速にするカスタムコマンド
customCommands:
  # 'C' で Conventional Commits 形式のメニューを開く
  - key: 'C'
    command: 'git commit -m "{{index .PromptResponses 0}}: {{index .PromptResponses 1}}"'
    context: 'files'
    description: 'Conventional Commit を作成'
    prompts:
      - type: 'menu'
        title: 'コミット種別を選択'
        options:
          - name: 'feat'
            description: '新機能'
            value: 'feat'
          - name: 'fix'
            description: 'バグ修正'
            value: 'fix'
          - name: 'docs'
            description: 'ドキュメント'
            value: 'docs'
          - name: 'refactor'
            description: 'リファクタリング'
            value: 'refactor'
          - name: 'chore'
            description: '雑用'
            value: 'chore'
      - type: 'input'
        title: 'メッセージを入力'
        initialValue: ''
  
  # 'D' でマージ済みブランチを掃除
  - key: 'D'
    command: 'git branch --merged | grep -v "\*" | xargs -n 1 git branch -d'
    context: 'localBranches'
    description: 'マージ済みのブランチを一括削除'
    loadingText: '削除中...'
  
  # 'F' でブランチから直接ファイルを取得
  - key: 'F'
    command: 'git checkout {{.SelectedLocalBranch.Name}} -- {{.SelectedFile.Name}}'
    context: 'files'
    description: '他ブランチから該当ファイルを取得'
  
  # 'P' でプッシュ前に差分確認
  - key: 'P'
    command: 'git diff {{.SelectedLocalBranch.UpstreamRemote}}/{{.SelectedLocalBranch.UpstreamBranch}}..HEAD'
    context: 'localBranches'
    description: 'プッシュされる差分を表示'

DXF TEXT エンティティの読み方まとめ

はじめに

DXF ファイルは「グループコード(番号)」と「」の組み合わせで構成されます。\ グループコードは「次の値が何を意味するか」を決める役割を持ちます。

この資料では、TEXT エンティティを例に\ DXF の読み方をわかりやすく整理したメモをまとめています。


基本:DXF の行はこう読む

10
130.39
  • 10 → グループコード(意味:X 座標)\
  • 130.39 → 値(X = 130.39)

DXF はすべてこの形式です。


代表的なグループコード(TEXT 用)

グループコード 意味


0 エンティティ種別 1 テキスト内容 5 ハンドル(ID) 7 テキストスタイル 8 レイヤ名 10 / 20 / 30 挿入位置(X / Y / Z) 11 / 21 / 31 第2基準点 40 文字高さ 50 回転角度 72 水平揃え(左・中央など) 73 生成フラグ


■サンプル TEXT エンティティ(DXF 生データ)

0
TEXT
5
A9
330
C
100
AcDbEntity
8
info
100
AcDbText
10
130.39496288806708
20
40.2434320017132
30
0
40
2.6458333333333335
1
4F : 11840mm
50
0
7
STANDARD
72
0
11
130.39496288806708
21
40.2434320017132
31
0
100
AcDbText
73
2

■サンプルの内容を読み解いた結果(整形表示)

ENTITY TYPE : TEXT
──────────────────────────────────
ハンドル           : A9
レイヤ             : info

表示文字列         : "4F : 11840mm"
文字高さ           : 2.6458333333333335
回転角             : 0°
整列方法           : 左揃え (72 = 0)
文字生成フラグ     : 2

挿入位置(基準点)
  X : 130.39496288806708
  Y : 40.2434320017132
  Z : 0

第2基準点
  X : 130.39496288806708
  Y : 40.2434320017132
  Z : 0

フォントスタイル   : STANDARD

DXF の理解を深めるポイント

  • DXF は すべてが「番号 → 意味 → 値」で構成される\
  • TEXT の本文は グループコード 1\
  • 座標系の基本は 10・20・30\
  • 文字の見た目は 40(高さ)、50(角度)、7(フォント)\
  • エンティティは 0 で始まり、次の 0 の直前までがひとつの塊

zshでIssueごとにBranch作る運用を助ける

Git Helper Functions

Issue単位でブランチを切り替える際の手間を軽減するためのZsh関数集。

gnew - 新しいIssue用ブランチへの切り替え

現在のブランチを削除し、main を最新化してから新しいブランチを作成する。

使い方

gnew <new-branch-name>

# feature/issue-123 ブランチで作業完了後、次のIssueへ
gnew feature/issue-456

# 実行される処理:
# 1. git checkout main
# 2. git branch -D feature/issue-123  (現在のブランチを削除)
# 3. git pull
# 4. git checkout -b feature/issue-456

注意事項

  • 未コミットの変更がある場合: git checkout main が失敗するため、先にコミットまたはstashしてください
  • main/master上で実行した場合: ブランチ削除をスキップし、pull → 新ブランチ作成のみ実行
  • PRがマージされていない場合: リモートブランチは残るため、GitHub上で別途削除が必要

セットアップ

~/.zshrc に以下が追加済み:

gnew() {
  local current_branch=$(git branch --show-current)
  local new_branch=$1
  
  if [[ -z "$new_branch" ]]; then
    echo "Usage: gnew <new-branch-name>"
    return 1
  fi
  
  if [[ "$current_branch" == "main" || "$current_branch" == "master" ]]; then
    echo "Already on main/master. Pulling and creating new branch..."
    git pull && git checkout -b "$new_branch"
    return
  fi
  
  echo "Switching from '$current_branch' to new branch '$new_branch'..."
  git checkout main && \
  git branch -D "$current_branch" && \
  git pull && \
  git checkout -b "$new_branch"
}

反映方法

新しいターミナルを開くか、以下を実行:

source ~/.zshrc

【TypeScript】残余プロパティを使った分割代入

残余プロパティを使った分割代入

オブジェクトから特定のプロパティを除外して、残りのプロパティを新しいオブジェクトとして取得するパターン。 書いていて思いついたので、メモしておく!

パターン例

例えば特大オブジェクトがあるとする

// オリジナルのユーザーデータから特定フィールドを除外してコピーを作成
const {
  id: _id,
  password: _password,
  createdAt: _createdAt,
  updatedAt: _updatedAt,
  ...userDataWithoutMetadata
} = originalUserData

このパターンの優れた点

ポイント 説明
Immutable 元のoriginalUserDataを変更せず、新しいオブジェクトを生成
型安全 TypeScriptがuserDataWithoutMetadataの型を自動推論(除外プロパティを除いたOmit<T, 'id' | 'password' | ...>相当)
宣言的 「何を除外するか」が明確に見える
_プレフィックス 「意図的に未使用」を示す慣例(ESLintのno-unused-vars対策にもなる)

代替案の問題点

// NG例1: 手動コピー(DRY違反、メンテナンス困難)
const userDataWithoutMetadata = {
  name: originalUserData.name,
  email: originalUserData.email,
  // ... 大量のプロパティを列挙
}

// NG例2: delete使用(型が崩れる、意図が不明瞭)
const userDataWithoutMetadata = { ...originalUserData }
delete userDataWithoutMetadata.id
delete userDataWithoutMetadata.password

汎用テンプレート

// 特定プロパティを除外してオブジェクトをコピー
const { 
  propToRemove1: _propToRemove1,  // _ = 「意図的に捨てる」
  propToRemove2: _propToRemove2,
  ...rest                          // 残りのプロパティを取得
} = sourceObject

// TypeScriptの型推論
// rest → Omit<typeof sourceObject, 'propToRemove1' | 'propToRemove2'>

ユースケース

  • 新規作成用データ: 既存データからidを除外してPOSTリクエスト用に変換
  • セキュリティ: passwordtokenを除外してログ出力やレスポンスに使用
  • 状態リセット: 特定のフィールドをクリアしたコピーを作成
  • APIレスポンス整形: 不要なメタデータを除外してフロントに返却

【拡張記事】structuredCloneで関数がクローンできない理由

structuredCloneで関数がクローンできない理由は、もともとStructured Clone Algorithmは他の実行コンテキストにデータを移送したり、データを永続化したりする目的で定義されていたからだと思われます。例えば、他のWindowにpostMessageでオブジェクトを送信するとき、あるいはオブジェクトをIndexedDBに保存する場合にStructured Clone Algorithmが使用されます。

とくに永続化のことを考えると、structuredCloneでクローンできるためにはデータがシリアライズできる必要があります。実際の実装はともかく、structuredCloneの挙動は仕様書上では「シリアライズしてからデシリアライズする」というアルゴリズムで定義されています。

zenn.dev

  1. structuredCloneの元々の目的 javascript// 他のウィンドウにデータ送信 window.postMessage({ name: "太郎", age: 25 }, "*")

// データベースに保存 indexedDB.put({ name: "太郎", age: 25 }) → データを別の場所に移すための仕組み 2. シリアライズの問題 シリアライズ = データを文字列や bytes に変換すること javascript// これはOK(文字列に変換可能) const data = { name: "太郎", age: 25 } JSON.stringify(data) // '{"name":"太郎","age":25}'

// これはNG(関数は文字列にできない) const func = () => console.log("hello") JSON.stringify(func) // undefined 3. 関数が特殊な理由 javascriptlet x = 100 const myFunc = () => x + 1 // 外の変数xを参照

// この関数をコピーしても... // 新しい場所にxは存在しない! 環境依存 = 関数は周りの変数やスコープに依存している → 関数だけコピーしても動かない 簡単に言うと 「関数は周りの環境とセットじゃないと意味がないから、単体ではコピーできない」

【拡張記事】コンポーネントの中にコンポーネントを作るということ

zenn.dev

Reactにおける再レンダリングの詳細な仕組み(Reconciliation)

1. 再描画の流れ(Reconciliation)

Reactの再描画処理は、2段階の比較を行って差分を検出します。

(1) Elementの種類(type)の比較

  • 同じ位置のElement同士のtypeを比較。
  • 異なればDOMを破棄し、再生成。
  • 同じならDOMを維持し、次の段階へ進む。

(2) propsや子要素の差分比較

  • propsや子要素を比較し、差分を検出。
  • 差分があればその部分のみをDOMに反映。
  • 差分がなければ再描画は行われない。

2. {renderInner()}が常にtype安定でも再描画される理由

疑問点

常にtypeが安定(例:'div')なら、再描画されずpropsやstateの変更を認識しないのではないか?

回答

  • 「typeが安定」は「DOMの破棄を防ぐ」ことを意味し、「再描画しない」ことではない。
  • propsや子要素に変更があれば、差分のみが再描画される。

初回レンダリング時:

<div>0</div>

再レンダリング時(stateが更新):

<div>1</div>
  • Reactはtype('div')が同じであるため、DOMを維持。
  • 子要素の差分(0 → 1)のみ更新される。

3. コンポーネントの再生成でtypeが毎回異なる場合の挙動

function OuterComponent() {
  const [count, setCount] = useState(0);

  const InnerComponent = () => <div>{count}</div>;

  return <InnerComponent />;
}

起こる現象

  • typeが毎回異なるため、DOMが毎回破棄・再生成される。
  • 差分比較が意味をなさず、全て再描画される。
  • 非効率でパフォーマンスが悪化する。

4. 再描画処理のまとめ(比較)

状態 typeが毎回変わる typeが安定
DOMの操作 全て破棄・再生成 DOMを再利用
再描画範囲 全て 差分のみ
パフォーマンス 悪化 良好
副作用(useEffect等) 毎回発生 初回のみ発生
Reactの挙動 非効率 効率的

5. React公式ドキュメントでの表現(参考)

Reactは最初にElementの種類(type)が変わったかを調べます。種類が変わった場合、新しいElementのツリーが構築されます。同じ種類であれば、Reactはpropsを調べ、変更されたpropsだけを更新します。


6. 結論

  • 「typeが安定していると再描画がされない」わけではなく、「typeが安定しているからDOMが維持され、propsや子要素の差分更新のみが効率的に行われる」。

🚩 実務上の注意点

  • コンポーネントは外部で定義する(毎回新たに生成しない)。
  • JSX要素のtypeを安定させることで、Reactの再レンダリング効率を向上させる。