【エンジニア勉強会】議事録
目的
エンジニア同士で技術情報を共有し、新たな知識を獲得するための場。参加者が興味のある技術分野について深く掘り下げるとともに、現在抱えている業務上の課題に対する解決策を模索する。毎週金曜日に開催。
概要
技術情報の共有
- 最新の技術トレンド、重要なセキュリティアップデート、便利なツールに関する情報を共有
- 実際のプロジェクトで役立った具体的な記事やコードの例を紹介
個人発表
- 各自が興味を持っている技術分野や新しいプログラミング言語について学習し、学んだ内容を発表する。
- スライドがあればspeakerdeckにまとめる
コーディング
- アルゴリズムのチャレンジの取り組み
業務課題のブレインストーミング
- 業務で直面している問題について話し合い、チームでの解決策を模索する。
- 各メンバーからのフィードバックや提案を集め、業務プロセスを改善する。
パネルディスカッション
- 特定のテーマについてディスカッションする形式。
5月2日 勉強会
1. Typescriptの`InstanceType`の共有。
Vue Component の型は InstanceTypeで取得する。
5月10日 勉強会
1. sass-mapでutiltyを作成するTips
$palettes: (
'blue': (
'default': #0075d1,
'l1': #e6f8ff,
'l2': #23445e
),
'green': (
'default': #1ed535,
'd1': #10b02b
),
);
@function p($group, $val: 'default') {
@return map-get(map-get($palettes, $group), $val);
}
2. Electronアプリでtexteraeが反応しない不具合の解消の共有
結論: bodyタグにドラック可能領域を設定していた為、不具合が発生していた。
-webkit-app-region: drag;
公式ドキュメント
ウインドウ全体をドラッグ可能にした場合、ボタンをドラッグ不可として同時にマークしなければなりません。そうでなければ、ユーザーがボタンをクリックすることができなくなります。
button {
cursor: pointer;
-webkit-app-region: no-drag;
}
textarea {
word-break: break-all;
-webkit-app-region: no-drag;
}
Electronアプリでは、コンテンツセキュリティポリシー(CSP)が厳格に設定されているため、インラインスクリプトや外部のリソースがブロックされてしまう。
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
- script-src 'self': スクリプトは自サイトのもののみ許可。
- style-src 'self' 'unsafe-inline': スタイルは自サイトのものとインラインスタイルを許可。
style-src <source> <source>;
は、スタイルシートの有効なソースを指定する。
インラインスタイル(HTML内に直接書かれたスタイルや、JavaScript経由で動的に適用されるスタイル)を許可するための、 unsafe-inline
を削除した場合、ブロックされるためCSS modulesのStyleが崩れる。
その他の要因
ブラウザーウィンドウのプリロードスクリプト
ElectronでReactアプリケーションを実行する場合、BrowserWindowのwebPreferencesオプションでプリロードスクリプトを設定している。
new BrowserWindow({
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
preload: path.join(__dirname, 'preload.js')
}
});
ファイルパスの問題
Electronでローカルのファイル(HTMLやJavaScript)をロードする際は、パスが正しいかどうかを再確認
win.loadURL(`file://${__dirname}/index.html`);
5月18日 勉強会
1. React状態管理ライブラリの紹介
状態管理の管理は困難
JavaScript のシングルページアプリケーションの要件がますます複雑になるにつれて、コードはこれまで以上に多くの状態を管理する必要があります。この状態には、サーバーのレスポンスやキャッシュされたデータ、まだサーバーに永続化されていないローカルに作成されたデータなどが含まれます。UI の状態も複雑化しており、アクティブなルート、選択されたタブ、スピナー、ページネーションコントロールなどを管理する必要があります。
この常に変化する状態を管理するのは大変なことです。モデルが別のモデルを更新できるなら、ビューはモデルを更新でき、そのモデルが別のモデルを更新し、さらにそれが別のビューを更新させるかもしれません。ある時点で、アプリで何が起こるかわからなくなり、いつ、なぜ、どのように状態を制御するのかがわからなくなります。システムが不透明で非決定的であると、バグの再現や新機能の追加が難しくなります。
1〜2個であれば問題ないが、10、20と増えていった場合、いつ、なぜ、どのように状態を制御するのかが把握しきれずバグに繋がりやすい。
Redux
特徴
- Flux アーキテクチャ
- ルールが厳密で大規模開発向き
- DispatchとReducerが存在し、メンテナンスコストが大きい。
- すべてのステートを一つのStoreで管理し、selectorにより必要なデータのみ抽出する。
デメリット
- コードが膨大になりやすい。より簡単に記載できるRedux Toolkitが存在する。)
- actionsが不要。
Recoil
- AtomとSelector:Atomは一つの状態を保持する(Reduxとは異なりデータのソースとなるAtomが複数存在する)
- ReduxのDispatchとReducerによる中間操作が不要
- 状態定義は分散型であるためコード分割が可能。
- 対象のコンポーネントを
<RecoilRoot/>
で囲む。Atom を作成する際はkey
を指定する必要あり。
import { RecoilRoot } from 'recoil';
function App() {
return (
<RecoilRoot>
<Component />
</RecoilRoot>
);
}
コンポーネントから Atom のデータを読み取り、その値を更新する。
// Atom を作成
const fontSizeState = atom({
key: 'fontSizeState',
default: 14, // 初期値
});
function FontButton() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
return (
<button
onClick={() => setFontSize((size) => size + 1)}
style={{ fontSize }}
>
Click to Enlarge
</button>
);
}
Selector:。Selector は Atom や他の Selector を受け取り派生データを計算する純粋関数です。依存関係が変更されると再計算される。
// Selector を作成
const fontSizeLabelState = selector({
key: 'fontSizeLabelState',
get: ({ get }) => {
const fontSize = get(fontSizeState);
const unit = 'px';
return `${fontSize}${unit}`;
},
});
Recoil は複数に分割することが可能で、Redux のストアに入れるほどでもない場合でも、気軽に拡張が可能。1つの Atom にステートを詰め込めば Redux のようにも使用可能。開発者にステートの分割方法やフォルダ構成、ロジックの置き場所などの決定権が大きく委ねられられている。
Recoilの運用について...
Jotai
コンポーネントで Atomの使用が可能で、プロバイダーでラップする必要なし。keyも不要
シンプルで直感的に操作できるJotaiを採用する。
- Zustand
Redux に近い
文献
5月24日 勉強会
1. EC2環境 Ineffective mark-compacts near heap limit Allocation failedのErrorを解消するStep
症状
EC2環境でnpm run buildしたことろ以下のエラーが発生
<--- JS stacktrace --->
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
1: 0xa24ed0 node::Abort() [node]
2: 0x966115 node::FatalError(char const*, char const*) [node]
3: 0xb9acde v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
4: 0xb9b057 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
5: 0xd56ea5 [node]
6: 0xd57a2f [node]
7: 0xd65abb v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
8: 0xd6967c v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]
9: 0xd2ee1d v8::internal::Factory::AllocateRaw(int, v8::internal::AllocationType, v8::internal::AllocationAlignment) [node]
~
ビルドプロセスの監視
まずは、EC2環境で、ビルドを開始した後、別のターミナルで free -h コマンドを定期的に実行してメモリ使用量を確認する。
Freeコマンドについて
1秒ごとにメモリ使用状況を更新するコマンド。
watch -n 1 free -h
ビルド時にはメモリ使用量が急激に増えることがあるため、ビルド中にメモリが不足している模様。
(Node.jsがJavaScriptのヒープ領域を拡張しようとした際に、メモリが不足してヒープ領域の割り当てに失敗している模様)
※ その他監視方法、ログの確認する。
ビルドプロセス中のログを確認し、メモリ不足に関連する警告やエラーを特定する。
npm run build 2>&1 | tee build.log
1. Node.jsのメモリ制限を増やす
node --max-old-space-size=4096 $(which npm) run build
2. スワップメモリの追加
EC2インスタンスにスワップメモリを追加することでもメモリ不足を緩和可能。
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
永続化の設定。
/swapfile swap swap defaults 0 0
スワップ領域(swap領域)について「使っていないメモリの内容を一時的にしまっておくための場所
」
スワップ領域はディスク・ドライブに配置され、アクセス時間は物理メモリーより大幅に遅くなります。スワップ領域に書き込むと、システムのパフォーマンスが実質的に低下します。スワップが頻繁に使用される場合は、スワップ領域ではなく、物理メモリーをさらに追加してください。
3. ビルドの最適化
プロジェクトのビルド設定を見直し、不要なプラグインや依存関係を削減する。
例)不要なプラグインや依存関係を削減
export default {
buildModules: [
// '@nuxtjs/eslint-module', // 使用していない場合コメントアウト
],
modules: [
// '@nuxtjs/axios', // 使用していない場合コメントアウト
],
}
例)webpackの設定見直し
xport default {
build: {
// Webpackのキャッシュを有効にする
cache: true,
// メモリ使用量を抑えるために並列ビルドを制限する
parallel: false,
// プラグインの最適化
optimization: {
splitChunks: {
chunks: 'all',
},
},
// トランスパイルするモジュールを限定する
transpile: [
// 'your-module-name', // 必要に応じて追加
],
},
}
インスタンスのサイズを増やす
メモリ不足が頻繁に発生する場合、EC2インスタンスのサイズを大きくすることも検討。より多くのRAMを持つインスタンスに変更する。ただし、料金が加算される為、まずはビルド最適化など別の方針で対応できないか確認する。
5月31日 勉強会
1. Electronの共有
1. サーバーに問い合わせる間隔をハンドリングする
負荷対策(mouse move イベント等マウスの座標が動くたびにサーバーに問い合わせするのではなく、少し待ってから同期や問い合わせが走るように実装する)
- debounce: 特定の時間間隔が過ぎるまで、イベントが発生し続ける場合にはそのイベントを無視し、最後のイベントだけを処理
import { debounce } from 'lodash';
const handleMouseMove = debounce((event) => {
// サーバーに問い合わせ
}, 300);
window.addEventListener('mousemove', handleMouseMove);
- throttleは、一定時間に1度しか実行されないようにするためのもの
import { throttle } from 'lodash';
const handleMouseMove = throttle((event) => {
// サーバーに問い合わせ
}, 300);
window.addEventListener('mousemove', handleMouseMove);
その他
- requestAnimationFrame:ブラウザのリペイントタイミングに合わせてイベントを処理する。
- Custom Timeout Handling:自身でカスタムする
let timeoutId = null;
const handleMouseMove = (event) => {
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => {
// サーバーに問い合わせ
}, 300);
};
window.addEventListener('mousemove', handleMouseMove);
- Observable Libraries (RxJS)
RxJSのライブラリを使用する。
import { fromEvent } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
const mouseMove$ = fromEvent(window, 'mousemove')
.pipe(debounceTime(300))
.subscribe(event => {
// サーバーに問い合わせ
});
RxJSとPromiseの違い
6月7日 勉強会
leetcode
replace Word
function replaceWords(dictionary: string[], sentence: string) {
const findRoot = (root: string) => {
let matched = []
for (let word of dictionary) {
if(root.startsWith(word)) {
matched= [...matched, word]
}
}
matched.sort((a, b) => a.length - b.length)
return matched.length > 0 ? matched[0] : root
}
const replaceWord = sentence.split(' ').map((word) => findRoot(word))
return replaceWord.join(' ');
};
6月14日 勉強会
PL(プロジェクトリーダー)の動き
6月21日 勉強会
AtomicDesignを改めて評価する
プロジェクトで採用している、AtomicDesignを改めて評価する
AtomicDesign
atom・・・
molecule・・・
organisms・・・
AtomicDesignのatom と molecule の境界が曖昧であり、チーム編成が変わったりすると、コンポーネントの粒度の秩序が失われやすい。
7月5日 勉強会
uuidについて
uuidについて
Cherry-pick運用
ReadOnlyArray<T>