SYM's Tech Knowledge Index & Creation Records

「INPUT:OUTPUT=1:1以上」を掲げ構築する Tech Knowledge Stack and Index. by SYM@設計者足るため孤軍奮闘する IT Engineer.

React ステート管理 (TypeScript コード例)

React ステート管理 (TypeScript コード例)

ステート

ステート:コンポーネントの描画後に更新されるデータ

ステートの管理は上位コンポーネントで行う。下位コンポーネントの操作でステートを更新する場合は、 下位コンポーネントにステート更新用の関数を譲渡、それを実行することで更新を実現する。

// 親
type StarRatingProps = {
  totalStars?: number
}

const StarRating: FC<StarRatingProps> = ({ totalStars = 5 }) => {
  const [selectedStars, setSelectedStars] = useState(0);
  return (
    <>
      {[...Array(totalStars)].map((n, i) => (
        <Star
          key={i}
          selected={selectedStars > i}
          onSelect={() => setSelectedStars(i + 1)} // ステート更新用関数譲渡
        />
      ))}
      <p>{selectedStars} of {totalStars} stars </p>
    </>
  )
}

// 子
type StarProps = {
  selected: boolean,
  onSelect: (event: React.MouseEvent<SVGElement, MouseEvent>) => void
}

const Star: VFC<StarProps> = ({ selected = false, onSelect = fn => fn }) => (
  <FaStar color={selected ? "red" : "grey"} onClick={onSelect} />  // 操作されたらステート更新
);

フォーム

以下実現方法。制御されたコンポーネント推奨。

useRef (制御されていないコンポーネント)

  • DOMノードに直接アクセスする方法
  • 特徴:イミュータブルでもなければ宣言的でもない()
  • 用途:React以外のライブラリとデータをやり取りする場合(はDOMに直接アクセスが必要)
type AddColorFormProps = {
  onNewColor: (title: string, color: string) => void
}

export const AddColorForm: VFC<AddColorFormProps> = ({
  onNewColor = fn => fn
}) => {
  const textTitle = useRef<HTMLInputElement>(null!);
  const hexColor = useRef<HTMLInputElement>(null!);

  const onSubmit: (event: React.FormEvent<HTMLFormElement>) => void = event => {
    event.preventDefault(); // デフォルト動作(submit:POST送信)抑止
    const title = textTitle.current.value;
    const color = hexColor.current.value;
    onNewColor(title, color);
    textTitle.current.value = "";
    hexColor.current.value = "";
  }

  return (
    <form onSubmit={onSubmit}>
      <input ref={textTitle} type="text" placeholder="input title..." required />
      <input ref={hexColor} type="color" required />
      <button>Add Color</button>
    </form>
  );
}

useState (制御されたコンポーネント)

  • ステート経由でDOMにアクセスする

※制御されたコンポーネント内の描画関数内で重い処理の実行は避ける(パフォーマンス劣化に繋がる)

type AddColorFormProps = {
  onNewColor: (title: string, color: string) => void
}

export const AddColorForm: VFC<AddColorFormProps> = ({
  onNewColor = fn => fn
}) => {
  const [title, setTitle] = useState("");
  const [color, setColor] = useState("#000000")

  const onSubmit: (event: React.FormEvent<HTMLFormElement>) => void = event => {
    event.preventDefault(); // デフォルト動作(submit:POST送信)抑止
    onNewColor(title, color);
    setTitle("");
    setColor("#000000");
  }
  const onChangeTitle: (event: React.ChangeEvent<HTMLInputElement>) => void = event => {
    setTitle(event.target.value)
  }
  const onChangeColor: (event: React.ChangeEvent<HTMLInputElement>) => void = event => {
    setColor(event.target.value)
  }

  return (
    <form onSubmit={onSubmit}>
      <input
        value={title} onChange={onChangeTitle}
        type="text" placeholder="input title..." required
      />
      <input
        value={color} onChange={onChangeColor}
        type="color" required
      />
      <button>Add Color</button>
    </form>
  );
}

カスタムフック

制御されたコンポーネントから重複したコード切り出して抽象化できる

上記コードの以下が重複を変更

value={title} onChange={event => {setTitle(event.target.value)}
interface useInputHook {
  (initValue: string): useInputReturns;
}

type useInputReturns = [
  {
    value: string,
    onChange: (event: React.ChangeEvent<HTMLInputElement>) => void
  },
  () => void
]

export const useInput: useInputHook = (initValue: string) => {
  const [value, setValue] = useState<string>(initValue);
  return [
    {
      value,
      onChange: event => setValue(event.target.value)
    },
    () => setValue(initValue)
  ]
}
// コメント: カスタムフック変更前
type AddColorFormProps = {
  onNewColor: (title: string, color: string) => void
}

export const AddColorForm: VFC<AddColorFormProps> = ({
  onNewColor = fn => fn
}) => {
  // const [title, setTitle] = useState("");
  // const [color, setColor] = useState("#000000")
  const [titleProps, resetTitle] = useInput("");
  const [colorProps, resetColor] = useInput("#000000");

  const onSubmit: (event: React.FormEvent<HTMLFormElement>) => void = event => {
    event.preventDefault(); // デフォルト動作(submit:POST送信)抑止
    onNewColor(titleProps.value, colorProps.value);
    // setTitle("");
    // setColor("");
    resetTitle();
    resetColor();
  }
  // const onChangeTitle: (event: React.ChangeEvent<HTMLInputElement>) => void = event => {
  //   setTitle(event.target.value)
  // }
  // const onChangeColor: (event: React.ChangeEvent<HTMLInputElement>) => void = event => {
  //   setColor(event.target.value)
  // }

  return (
    <form onSubmit={onSubmit}>
      <input
        // value={title} onChange={onChangeTitle}
        {...titleProps}
        type="text" placeholder="input title..." required
      />
      <input
        // value={color} onChange={onChangeColor}
        {...colorProps}
        type="color" required
      />
      <button>Add Color</button>
    </form>
  );
}

Reactコンテキスト

  • コンテキスト:中継地を経由せずにステートを伝達する方法
  • コンテキストプロバイダー:データを渡す方 (親要素側)
  • コンテキストコンシューマー:データを読みだす方 (利用コンポーネント側)

メリット

const Contents = () => {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}

ref:

※ 複数のプロバイダーがある場合、フックははるかにクリーンになる傾向がある

基本形

const colors: ColorData[] = buildInitColors();
export const ColorContext = createContext<ColorData[]>([]);

ReactDOM.render(
  <React.StrictMode>
    <ColorContext.Provider value={colors}>
      <App />
    </ColorContext.Provider>
  </React.StrictMode>,
  document.getElementById('root')
);
export const ColorList: VFC<ColorListProps> = () => {
  const colors = useContext(ColorContext);
  if (colors.length === 0) {
    return <div>No Colors. (Add Color)</div>
  }
  return (
    <div>
      {colors.map(color => (
        <Color key={color.id} {...color.toObj()} />
      ))}
    </div>
  );
}

カスタムプロバイダー

基本はコンテキスト&カスタムフックを使う=コンテキストへのアクセスがカスタムフック経由のみになり安全

コンテキスト&ステート併用

コンテキストプロバイダーはデータ変更ができないため、データ変更を可能にする手段。

type ColorProviderProps = {
  children: React.ReactNode
}

export const ColorContext = createContext(undefined!);

export const ColorProvider = (props: ColorProviderProps) => {
  const [colors, setColors] = useState<ColorData[]>(buildInitColors());
  
  const removeColor = (id: string) => {
    const excludeColor = (id: string) => colors.filter(c => c.id !== id);
    setColors(excludeColor(id));
  }
  const addColor = (title: string, color: string) => {
    const newColors = [...colors, new ColorData(title, color)];
    setColors(newColors);
  }

  return (
    <ColorContext.Provider value={{ colors, addColor, removeColor }}>
      {props.children}
    </ColorContext.Provider>
  );
}

※setColors を公開するとどんな操作でも可能になる=バグが混入する可能性。必要な操作を行う関数のみを(value に設定して)公開する

// プロバイダー側:index.tsx
ReactDOM.render(
  <React.StrictMode>
    <ColorProvider>
      <App />
    </ColorProvider>
  </React.StrictMode>,
  document.getElementById('root')
);
// コンシューマー側
export const ColorList: VFC = () => {
  const { colors } = useContext(ColorContext);
  if (colors.length === 0) {
    return <div>No Colors. (Add Color)</div>
  }
  return (
    <div>
      {colors.map(color => (
        <Color key={color.id} {...color.toObj()} />
      ))}
    </div>
  );
}
コンテキスト&カスタムフック

カスタムフックを導入することで、コンテキストをコンシューマーに一切公開することなくデータ共有可能

コンテキストの操作をカスタムフックで隠ぺいする

type ColorContextValues = {
  colors: ColorData[],
  addColor: (title: string, color: string) => void,
  updateRateColor: (id: string, rating: number) => void,
  removeColor: (id: string) => void
}

const ColorContext = createContext<ColorContextValues>(undefined!);
export const useColors = () => useContext(ColorContext);

export const ColorProvider = (props: ColorProviderProps) => {
  // ... 同上のため省略
}
export const ColorList: VFC = () => {
  const { colors } = useColors();
  if (colors.length === 0) {
    return <div>No Colors. (Add Color)</div>
  }
  return (
    <div>
      {colors.map(color => (
        <Color key={color.id} colorData={color} />
      ))}
    </div>
  );
}

ref:

ステートが更新される=ColorProviderコンポーネント全体が再描画 -> コンポーネントツリー全体にデータ更新が反映

まとめ

ロジックをフックに分離する -> 関心の分離 = UI と ロジックを別々に開発/テスト&デプロイ可能

Golang ポインタ

Golang ポインタ

注意事項

これらにポインタを使うとすれば、やむなく値無し(nil) の表現が必要な時だけ

例えば jsonデータ。存在しえないフィールドはnil代入を許容する

type JsonData struct {
  id string `json:"id"`
  nickname *string `json:"nickname,omitempty"`
}

ポインタでむやみに値無しを表現するのは良くないのではないかと。 そうした場合、ポインタの表現する意味が本来のポインタの意味と値無しの2つの役割を持ってしまう。値オブジェクトにしてそのオブジェクトで値無しを表現する用にした方が良いのではないか?

  • (スライスではなく) 配列の場合は値渡し
var array = [3]string{"japanese", "math", "english"}
var array2 = array  // 値渡し

var array3 = array[:]  // 参照渡し
  • ポインタレシーバーでないと更新できない

setNameの中にあるpersonは、それを呼び出したインスタンスのコピー

そもそも struct がポインタ型でない場合、プロパティにアクセスするメソッドでも、インスタンスのコピーをわざわざ作り出している

type Person struct {
    name string
}
func (person Person) sayName() {
    fmt.Println(person.name)
}
func (person Person) setName(name string) {
    person.name = name
}

var hoge = Person{"Hoge"}
hoge.sayName() // Hoge  // インスタンスのコピー
hoge.setName("Fuge")  // インスタンスのコピーに対してsetするため反映されず
hoge.sayName() // Hoge
  • interface 実装 struct のポインタ

以下はエラー (Person does not implement Greeter (Greet method has pointer receiver)) になる。struct にポインタレシーバーを使用する時は、生成する際もポインタの使用必須(ポインタはポインタで合わせろと)

type Greeter interface {
    Greet()
}
type Person struct {
    name string
}
func (p *Person) Greet() {
    println("Hi! I'm ", p.name)
}
func NewGreeter(name string) Greeter {
    return Person{name}  // return &Person{name} である必要がある
}
func main() {
    p := NewGreeter("SYM")
    p.Greet() 
}
  • 再帰的な処理は深さに注意

深ければ深いほど、性能劣化に繋がる

Goでは再帰の深さが極端に処理性能が落ちる要因になります。 深さの数だけスタックを浪費する実装になっていますのでGoに慣れた人は自然と深い再帰処理はループ処理に書き換えます。(フォルダツリーのような有限な深さであることがわかっているような処理は再帰で書いても問題はありません)

Goの良さをまとめてみた

  • 使いすぎると性能劣化に繋がる

ポインタはアドレスを渡すだけのため効率的だが、使いすぎるとガベージコレクションに負荷がかかってしまい、多くのCPU時間を消費するようになる可能性がある

恐らくポインタでメモリを占有し続ける状態を作るとGC時に毎回見に行きコストがかかると思われる(string, map ,slice 等での定数宣言も同じことが言えるのでは?)

ref:Go のGCのオーバーヘッドが高くなるケースと、その回避策

ポインタの使い所

前提: レシーバ = メソッドの (s Square) の部分

  • ポインタを使うべきでない時
    • レシーバが map、func、チャネルの場合
    • レシーバがスライスで、メソッドがスライスを作りなおさない場合
  • ポインタを使う時
    • メソッドで値を変更する必要がある場合
    • レシーバが sync.Mutex か、似たような同期するフィールドを持つ構造体の場合、コピーを避けたいデータを扱う場合
    • レシーバが大きな構造体や配列の場合
    • 大きな構造体をスライスに持たせる場合

ref

Golangのポインタで詰まったので備忘録

Go言語でinterfaceをimpleしてるつもりが「does not implement (method has pointer receiver)」って叱られる【golang】【pointer】【ダックタイピング】

go Code Review Comments 日本語翻訳

Goにおけるポインタの使いどころ

【Go言語】構造体におけるポインタメソッドの使いどころ

JS 高階関数

JS 高階関数

  • 他の関数を引数に取るか
  • 戻り値として関数を返すか
  • もしくはその両方を満たすもの

利点

  • 純粋関数 = 副作用がないため、テスト容易性/再利用性/スケーラビリティの点で優れる

元のデータの不変性を保てる

カリー化

  • JSは非同期処理によりコードが複雑になりがち -> これの解消に高階関数が使われることがある
  • この目的で使用するのがカリー化
const serviceLogs = serviceName => message => 
    console.log(`${serviceName}: ${message}`);

const log = serviceLogs('test');
log('Synchronous execution')
promissFunc(100).then(
    arg => log(`Asynchronous execution (arg: ${arg})`)
).catch(
    err => log(`Asynchronous execution error`)
)

// test: Synchronous execution
// test: Asynchronous execution (arg: 100)
// test: Asynchronous execution error

再起

非同期処理と組み合わせた時に真価を発揮する。再起呼び出しタイミングの調整ができる

const countdown = (value, fn, delay = 1000) => {
    fn(value);
    return value > 0
        ? setTimeout(() => countdown(value - 1, fn, delay))
        : value;
}
const outputLog = value => console.log(value);
countdouwn(10, outputLog)

用途:

  • データ構造の探索
  • フォルダからファイル探索
  • HTMLドキュメントから DOM 要素探索
  • ネスト下オブジェクトとから指定のプロパティ値を取得
const obj = {
    type: 'person',
    data: {
        gender: 'male',
        info: {
            id: 555,
            name: {
                first: 'SYM',
                last: 'XXXX'    
            }
        }
    }
}

deepPick('type', obj);  // person
deepPick('data.info.name.first', obj);  // SYM

const deepPick = (field, data = {}) => {
    [first, ...remaing] = field.split('.');
    return remaing.length > 0 ? deepPick(reaming.join('.'), data[first]) : data[first];
}

関数の合成

関数の合成とは

  • 関数を順番に、もしくは並行に呼び出し、複数の関数呼び出しを束ねてより大きな関数を作ることで、アプリケーション全体を構築する過程

パターン

  • chaining (連鎖)
const template = 'HH:MM:SS TT';
const time = template
    .replace('HH', '01')
    .replace('MM', '23')
    .replace('SS', '45')
    .replace('TT', 'AM');

関数合成関数:

const compose = (...fns) => initFunc => {
    fns.reduce((composed, fn) => f(composed), initFunc)
}
const both = compose(civilianHours, appendAMPM);
both(new Date());

// compose(civilianHours, appendAMPM) で以下となる
// initFunc => {
//    [civilianHours, appendAMPM].reduce((composed, f) => f(composed), initFunc)
// }
//
// both(new Date()); で initFunc = new Date() に代入され、
// [civilianHours, appendAMPM].reduce((composed, fn) => fn(composed), new Date())
// となるので、reduce で 
//  (1) composed: new Date(), fn: civilianHours -> civilianHours(new Date())
//  (2) composed: civilianHours(new Date()), fn: appendAMPM 
//      -> appendAMPM(civilianHours(new Date()))  となる

サンプル

  • 命令型
setInterval(logClockTime, 1000);

function logClockTime() {
    let time = buildClockTime();
    console.clear();
    console.log(time);
}

function buildClockTime() {
    const date = new Date();
    let time = {
        hours: date.getHours(),
        minutes: date.getMinutes(),
        seconds: date.getSeconds(),
        ampm: 'AM'
    };
    
    if (time.hours == 12) {
        time.ampm = 'AM'
    } else if (time.hours > 12) {
        time.ampm = 'PM';
        time.hours -= 12;
    }
    
    if (time.hours < 10 {
        time.hours = '0' + time.hours;
    }
    if (time.minutes < 10) {
        time.minutes = '0' + time.minutes;
    }
    if (time.seconds < 10 {
        time.seconds = '0' + time.seconds;
    }
}
  • 関数型
    • 関数は非破壊的であるため、実行後も元のオブジェクトは不変
const oneSecond = () => 1000;  // 固定値を取得する時にも関数を用いる
const getCurrentTime = () => new Date();
const clear = () => console.clear();
const log = message => console.log(message);

const serializeClockTime = date => ({
    hours: date.getHours(),
    minutes: date.getMinutes(),
    seconds: date.getSeconds()
});

const civilianHours = clockTime => ({
    ...clockTime,
    hours: clockTime.hours > 12 ? clockTime.hours - 12 : clockTime.hours
});

const appendAMPM = clockTime => ({
    ...clockTime,
    ampm: clockTime.hours >= 12 ? 'PM' : 'AM';
});

const display = target => time => target(time);

const formatClock = format => time => 
    format.replace('HH', time.hours)
        .replace('MM', time.minutes)
        .replace('SS', time.seconds)
        .replace('TT', time.ampm)

const prependZero = key => clockTime => ({
    ...clockTime,
    [key]: clockTime[key] < 10 ? '0' + clockTime[key] : '' + clockTime[key]
});

const convertToCivilianTime = clockTime => compose(
    appendAMPM,
    civilianHours
)(clockTime);

const convertToDoubleDigits = civilianTime => compose(
    prependZero('hours'),
    prependZero('minutes'),
    prependZero('seconds')
)(civilianTime);

// 処理
const startTicking = () => setInterval(
    compose(
        clear,
        getCurrentTime,
        serializeClockTime,
        convertToCivilianTime,
        convertToDoubleDigits,
        formatClock('HH:MM:SS TT'),
        display(log)
    ),
    oneSecond()
);

startTicking();

Python 色々メモ

Python 色々メモ

某ツール作成時に使った物をメモ(毎度期間が空いて綺麗さっぱり忘れてはググりまくるため)

reference stack

python 色々まとめ

ArgumentParserを使ってpythonのコマンドライン引数をとことん使ってみた

Pythonでクラスの引数や戻り値の型アノテーションに自己のクラスを指定する

# これのみでいけた
from __future__ import annotations

[Python] インスタンスのプロパティへ動的にアクセスする

c = Container()

setattr(c, "key", "value")
print(c.key)  # => value

val = getattr(c, "key")
print(val)  # => value

date 系

Python日付型

Pythonで日付文字列からのdatetime変換やタイムゾーンの変更などをいい加減覚えたい

Pythonで文字列 <-> 日付(date, datetime) の変換

from datetime import datetime as dt

# 文字列 → 日時
tstr = '2012-12-29 13:49:37'
tdatetime = dt.strptime(tstr, '%Y-%m-%d %H:%M:%S')

# 日時 → 文字列
tdatetime = dt.now()
tstr = tdatetime.strftime('%Y/%m/%d %H:%M:%S')

文字列系

Pythonで文字列を比較(完全一致、部分一致、大小関係など)

Pythonの文字列フォーマット(formatメソッドの使い方)

Pythonのf文字列の使い方

# 書式設定 (インデックス番号は省略可)
"{0:<20,.3f}".format(12345.67)  # '12,345.670'        
"{:<20,.3f}".format(12345.67)  # '12,345.670'

# int型:左に空白
"{:2d}".format(100)  # '100'
"{:10d}".format(100)  # '       100'
# ゼロパティング
"{:010d}".format(100)  # '0000000100'
# 小数点以下
"{:.1f}".format(0.01)  # '0.0'
"{:.2f}".format(0.01)  # '0.01'
"{:.5f}".format(0.01)  # '0.01000'
# 指数表記
"{:.1e}".format(0.01)  # '1.0e-02'
"{:.2e}".format(0.01)  # '1.00e-02'
"{:.5e}".format(0.01)  # '1.00000e-02'
"{:.5E}".format(0.01)  # '1.00000E-02'

# str型:右に空白
"{:2}".format("abcde")  # 'abcde'
"{:10}".format("abcde")  # 'abcde     '
# 右寄せ
"{:>20}".format("abcde")  # '               abcde'
"{:*>20}".format("abcde")  # '***************abcde'
# 最大文字数指定
"{:.3}".format("abcde")  # 'abc'
"{:.7}".format("abcde")  # 'abcde'

Pythonで文字列を置換(replace, translate, re.sub, re.subn)

# 複数の文字を指定して置換
s = 'one two one two one'
print(s.translate(str.maketrans({'o': 'XXX', 't': None})))
# XXXne wXXX XXXne wXXX XXXne

Pythonで文字列の先頭と末尾から空白や文字列を削除する:strip()

# 先頭/末尾削除
"Hello World World Hello".strip('Hello')  # " World World "
# 先頭/末尾の空白/改行削除
s = "  Hello World\n".strip()  # "Hello World"
# 先頭のみ削除
s = "  Hello World\n".lstrip()  # "Hello World\n"
# 末尾のみ削除
s = "  Hello World\n".rstrip()  # "  Hello World"

List

Pythonでリストをソートするsortとsortedの違い


Dictionary

Pythonで辞書のキー・値の存在を確認、取得(検索)

Pythonで辞書を作成するdict()と波括弧、辞書内包表記

# 辞書内包表記
l = ['Alice', 'Bob', 'Charlie']
d = {s: len(s) for s in l}

Pythonで辞書に要素を追加、辞書同士を連結(結合)

d1 = {'k1': 1, 'k2': 2}
d2 = {'k1': 10, 'k3': 3, 'k4': 4}
# 別オブジェクトで生成
d = d1 | d2  # {'k1': 10, 'k2': 2, 'k3': 3, 'k4': 4}
# 上書き
d1 |= d2  # {'k1': 10, 'k2': 2, 'k3': 3, 'k4': 4}

ファイル系

[Python] フォルダやファイルのコピー、移動、削除(shutilモジュール)

Pythonでファイル・ディレクトリを削除するos.remove, shutil.rmtreeなど

import shutil

# フォルダ丸ごとコピー (既にフォルダがあると失敗)
shutil.copytree('./sample', './sample_backup')
# フォルダ丸ごと削除
shutil.rmtree()

【Python】 フォルダ内の特定のファイルを取得する

# フォルダ内の特定ファイル名のみ取得
import os

files = os.listdir('Folder\\')
extension = '.txt'
text_files = [file for file in files if extension in file]
for file_name in text_files:
    print(file_name)
# フォルダ内の特定ファイルフルパス取得
import glob

extension = '.txt'
text_files = glob.glob('Folder\\*' + extension)
for file_path in text_files:
    print(file_path)

Pythonでファイルのタイムスタンプ(作成日時や更新日時)を取得

# 最終アクセス日時
print(os.path.getatime('/temp/test.txt'))
# = os.stat('temp/test.txt')).st_atime

# 最終内容更新日時
print(os.path.getmtime('/temp/test.txt'))
# = os.stat('temp/test.txt')).st_mtime

# メタデータの最終更新日時(UNIX) / 作成日時(Windows)
print(os.path.getctime('/temp/test.txt'))
# = os.stat('temp/test.txt')).st_ctime

# 作成日時(macOSを含むFreeBSD系の一部のUNIXのみ)
# os.stat('temp/test.txt')).st_birthtime

PythonでWeb上の画像などのファイルをダウンロード(個別・一括)

import os
import urllib.error
import urllib.request


def download_file(url, dst_path):
    try:
        with urllib.request.urlopen(url) as web_file, open(dst_path, 'wb') as local_file:
            local_file.write(web_file.read())
    except urllib.error.URLError as e:
        print(e)


def download_file_to_dir(url, dst_dir):
    download_file(url, os.path.join(dst_dir, os.path.basename(url)))


url = 'xxxxxx'
dst_dir = 'data/temp'
download_file_to_dir(url, dst_dir)

XML 操作系

【Python】XMLのデータを読み取り・書き込みする(ElementTree)

【Python】XMLを解析・読み取りする(ElementTree)

PythonでのXMLファイル操作例

ElementTreeやlxmlで名前空間を含むXMLの要素を取得する


フロントエンド 技術要素概要集

フロントエンド 技術要素概要集

※随時追加

SPA (Single Page Application)

- 特徴
Not SPA サーバとのやり取りが同期的。ユーザ操作->ページ全体更新(ページ遷移)
SPA サーバとのやり取りが非同期的。ユーザ操作->部分更新が可能

※いつか追記

CSR (Client Side Rendering)

  • ユーザーのブラウザで全てレンダリングする方法
  • 空のHTMLとレンダリングするためのjsやCSSを受け取り、jsで描画のためのCSSとHTMLを生成、最終的なDOM/CSSMOMをもとにブラウザが描画
  • サーバへの負荷が少ない分、ユーザーサイドに負荷を強いる
  • 最初は空のHTMLしかないためSEO的に不利

SSR (Server Side Rendering)

  • サーバー側で JavaScript を実行し、生成結果のHTML,CSS,jsをユーザー側に返すため、ユーザー側の処理能力に依存しない
  • サーバーがリクエストを受けた時点で HTML を生成し、それをブラウザに返す
  • (ページを開いてからHTMLなどの生成を行うため)初期描画が遅い

CSRだとユーザのリソースに依存するし、ブラウザでのレンダリング処理速度にも限界はある。規模が大きくなればなるほど描画に時間がかかる->UX低下に繋がるから、サーバ側でできることはサーバ側で行うことでユーザ(クライアント) サイドの負荷軽減できるでしょう。

SSG (Static Site Generation)

  • ビルド時に全ての情報を取得して静的ファイルを作る(正確にはビルド時に静的なページを生成する機能を指す)
  • ビルド時に(APIで取得するコンテンツ等も含めて)全ての情報を取得してビルドするため、コンテンツ量が増えるとビルド時間も増える
  • キャッシュをすることができて、CDNを通して世界中に配信できるように
  • 頻繁に情報を更新するようなサイトには不向き(静的サイト向け)
  • ページ数の多いサイトはページの追加や内容の更新時に事前ビルドのコストが大きくなる

静的なものなら、都度サーバサイドでのレンダリングすらする必要なくて、ビルド時に完結させ、CDNに配置することで、役割分担/負荷分散にもなるでしょう。

ISR (Incremental Static Regeneration)

  • 増分静的再生成
  • アクセスされた時に初めてSSGのビルドが走りサイトを生成する方法(2回目以降はビルド済みなため結果のみを返す)
  • ビルド後に静的生成されたページの追加/更新ができる(動的に静的ページを更新可能に)

一度に全部ビルドしていては規模が大きくなればビルド時間が長くなるので、必要な時に必要な部分だけビルドしよう。そうすることでサイトの更新自体もスムーズにできるでしょう。

Remix

React フレームワーク

特徴

  • サーバー/クライアントモデル
  • 基盤となる技術を抽象化しないこと
  • ウェブの基礎となるブラウザ、HTTP、そしてHTMLなどと共に取り組んでいく
  • JavaScriptを使ってブラウザの動作をエミュレート(システムの挙動を別のソフトウェアなどによって模倣し、代替として動作させる)し、UXを向上させる

SSG,ISRは、(静的サイトを作るための手法故に) ビルド時間、ビルドの複雑さ、サイトの変更をするまでのタイムラグ、認証が必要なサービスには使用不可などその他様々な問題がある。

そのため、Next.jsやGatsbyとは路線が異なり、SSGなど予めデータを用意して高速化を測る手法をとらず、サーバーサイドレンダリングやブラウザのFetchAPIを活用してデータをやり取りするといった手法をとっている模様。

よりサーバサイドにを仕事を担わせることで、よりパフォーマンス最適化を行えるようにしようとしている?

ref

ref

CSR,SSR,SSG,ISRのまとめ

Webアプリパターンの歴史 - SST、AJAX、CSR、SSR、SSG、そしてISR

ブラウザの仕組み: 最新ウェブブラウザの内部構造

React スタイリング メモ

React スタイリング メモ

React スタイリング 手段

手法は大きく2つに分かれていそう

  • CSS Modules系:

    • CSS を分けて書く。(規約が定まる)
    • CSSのロードはJSと分かれるため、パフォーマンスは良い傾向(ビルド時に作成されたCSSファイルをロードする方がパフォーマンスがよいケースは多い)
    • CSS用のファイル分けるため、ファイルが増える/メンテしずらい等、開発者体験は悪くなる傾向
  • CSS-in-JS系:

    • jsx/tsxCSS も一緒に書く。CSSコンポーネントレベル自体に抽象化できる。
    • jsからスタイルを動的生成するため、物や規模によっては部分的にパフォーマンスの問題が出てくる?(例:styled componentの初期ロードが重くなる可能性)
    • 1ファイル内で完結し、強いメリットもあるため、開発者体験は良い傾向。ただし、ロジック側との境界が曖昧になるし、難しい面もあるみたいなので、失敗すると大変なことになる

ダイナミックスタイルが必要になるなら、CSS-in-JS。ただしCSS-in-JSはライブラリ等への依存度高い上、CSSをそのまま移植はできない。CSS Modulesで書くCSSは標準技術であり、CSSだけは結合度を低くできるから移植もしやすい。安定を取るならCSS Modules。

手段

  • CSS Modules
    • パフォーマンスが良いが、1コンポーネントに1cssファイル必要→ファイルが膨大になり開発しづらくなる(css-loaderが非推奨)
  • Vanilla-Extract (CSS Modules)

  • Styled Component (CSS-in-JS)

  • emotion (CSS-in-JS):メリットやデメリットはstyled-componentsとほぼ一緒

  • Tailwind CSS (CSS フレームワーク):

    • ユーティリティファーストのCSSフレームワークで非常にReactと相性がいい。
    • CSS Modulesにも@applyで組み合わせたり、CSS-in-JSにもtwin.macroというライブラリで組み合わせて使う事も可能で、汎用性が高い
    • ただし、複雑なスタイルはclassにたくさん書く必要が出てくるため分かりにくくなる(可読性が悪くなる)
  • Material UI (UI ライブラリ)

    • マテリアルデザインを簡単に実現することができるデザインライブラリ(故にデザインがマテリアルデザイン固定になる)
    • ver5 から カスタマイズ性能を持ち合わせた
    • emotion か styled-components を使用するようになった(=CSS-in-JSの特徴に当てはまる?)
  • Chakra UI (UI ライブラリ)

非推奨:

  • styleプロップ:パフォーマンスが悪い

ref

エンジニアを分類する3タイプから考える自身のタイプとその先と

※後日修正予定

エンジニアを分類する3タイプから考える自身のタイプとその先と

以下を多分に引用して記す。

エンジニアを分類する、3つのタイプ

「技術の人は技術の本質を追求しテックリード/アーキテクト、プロダクトの人は技術を手段と割り切りフルスタックエンジニア/PdM、組織の人は開発生産性を高めようとEM/PMOを目指すことが多い」とのこと。そのことについて突っ込んで書かれた記事。

3タイプ

3タイプについて見やすくまとめる

前提:エンジニアとしての志向性を以下3つに分類すると目指すキャリアが考えやすいと言う話

  • ※この3つのタイプはスキルの絶対量ではなく、何を「目的」とし、何を「手段」とするかというスタンスの違いとのこと
  • ※どの志向性が自分は強そうかというバランスとしてとらえておくと良い

3タイプとその特徴等まとめると以下

  • 技術が好きな人
    • 目的:技術の本質を追求すること
    • 手段:理論の実践・検証(としてプロダクト開発を行う)
    • 趣向:アーキテクチャの考え方自体、言語設計が興味深い言語を勉強の意味で学ぶ、できるだけ本質的なコードを書きたい、あるべきアーキテクチャを現実に落とし込みたい
  • プロダクトが好きな人
    • 目的:良いプロダクトをつくりユーザーに価値提供すること
    • 手段:技術が手段
    • 趣向:自分のつくりたい機能をまるっと実装したい(技術領域はフルスタックに広く浅くなりやすい傾向) 、UXやUIの領域やデータ分析などにも強いこだわりを持つ、どう実装するかよりもなぜ何をつくるか/どう改善していくのかを考えるようになる
  • 組織が好きな人
    • 目的:高い開発生産性を持つ優れた開発チームつくること
    • 手段:プロダクトのデリバリを重視(使用する技術やビジネス価値/事業インパクトそのものには深く踏み込まない)
    • 趣向:一定の制約条件のなかで最大の成果を出すことにコミット(スクラムなどアジャイル開発などに強く専門性を持っていく、ピープルマネジメント領域でチームコンディションやスキル開発/技術広報的な分野で活動を広げていく、等人それぞれ)

自身のタイプと特性

どれにも当てはまるバランス型…

タイプ的には、プロダクト好きが中心ではあるが、プロダクト開発の手段として必要となる技術やチーム(組織)も重視している。比率的には、「技術:プロダクト:組織 = 3.5:4:2.5」と考える(どちらかと言えば技術>組織のため)。

そう考えた詳細は以下の通り。

  • 技術の本質を追求したい
    • 引用記事にある数学的なアルゴリズムやバイナリ・プロトコルなど低レイヤの技術/プログラム言語そのもの/個々のフレームワークというよりもアーキテクチャの考え方自体に興味を持ちますチームの心理的安全性やカルチャーは重視しているが、非合理な人の感情と向き合うことは好きではなくにがっちり当てはまる。
  • が、技術は(プロダクトなど物を作る上での)手段と割り切っている = プロダクト指向
    • それ故か自分のつくりたい機能をまるっと実装したいフルスタック足りたいと考えている。
    • 自分がコードを書かなくてもいい とまでは思ってない。できれば書きたいが、必ずしも自身が書かなくても良い位の温度感(常にコードが書けるだけの実力は持っておき、必要な場面があれば自身がコードを書くor書けるようにしておきたいと思っている。≒サポート思考)
  • が、使用する技術やビジネス価値/事業インパクトそのものには深く踏み込まないには当てはまる
    • 使用する技術に強いこだわりはないように思う(が全くない訳ではなく、プロダクトや開発組織/チームの状態や特性等を踏まえて適切な物を選びたい欲を持っている) 。目的を果たせるなら細かい手段までは気にしないが、ある程度プロダクトにとって適切なものを選びたい
    • ビジネス価値/事業インパクトに関しては、そこを強く意識する機会が無かったため、正確にはまだ分かっていないだけかもしれない。(※詳細後述)
  • また、高い開発生産性を持つ優れた開発チームつくることにも関心がある
    • (2022/3現在) 現職にて自身が属するチーム(6名程)のチームの心理的安全性が低く、理想とはかけ離れた状態が故、制約の多い中少しでも良くできないかと試行錯誤し続けている。そういった実体験があり、プロダクト開発に立ち向かう上で欠かせない要素だと強く肌で感じているため(※詳細後述)

上記を踏まえつつ、先を考えるため、現状を整理する(自身の特性含む)(2022/3、31歳時)。

  • SIer業界2次請けサイドSE。別会社のプロダクト開発に従事(2019/12~参画中)
  • 現状(なんちゃって)フルスタック、サブチームのサブリーダー
    • バックエンドが中心だが、RDBの経験が浅かったり、フロントエンド経験(フレームワーク経験無し)が多少あったりと中途半端
    • フロントエンド~バックエンドのコードを見ていけるだけの力があり、機能追加する際に、どこにどういった修正が必要か見極められる
    • RDBの経験は薄いが、時系列データベースやGraQLライクな独自DBを扱った経験があり、データ層及び扱っているデータをしっかりと意識して考えていける
  • プロダクトの全体~詳細を見ていけるだけの力がある(それだけ既存物への理解力が高い)
    • 4~5システムが連携することで1つのサービスを形成する大規模プロダクトのうち2システムの改修に携わった。
    • 携わった一方はマイクロサービスアーキテクチャで、(触れてない部分もあるため全てではないが)おおよそ各パーツの役割とどう連携しているかを掴んでいる
    • もう一方は、6ヶ月前から開発に携わるようになったものであり、中身の詳細を着実に掴んでいき、実装面ではサブチームのメンバー全員を牽引しているレベル
    • 各システムの役割をしっかりと掴み、新規参画メンバーにプロダクトの全体アーキテクチャの概要や、どうやってサービスを実現しているのか、ユーザ視点を交えて各システムがどういったことができるのか/機能を提供しているかを説明。
  • 2次請けサイド故にプロダクトとの距離が遠い。
    • プロダクトの価値、役割、位置づけ等、概要的なことは押さえているが、実際のユーザからどういった要望/フィードバックが来ているのか全く分からない。知ることができない。
  • チームの不足部分を埋めるような動きをする
    • 所属サブチームで自身のみが突出しておりサポートに回るばかり≒サポート型
    • 所属サブチームの心理的安全性や、開発生産性が低いと感じている=不足部分をなんとか改善できないかを試行錯誤している

現状もやもやしている点&先のキャリアへの考えは以下

  • どちらかと言えばプロダクト中心指向にも関わらずプロダクトとの距離感が遠いこと(解消には転〇必須)
  • 本音はフルスタックを目指したいとどこかで思っている
    • が、これまでの経験/スキル/年齢的にも技術/エンジニア一本の方向は厳しい。自身の地頭的にも
    • 仮にそちらに倒すにしてもスタートアップが狙い目になるのかと思うが、0->1、1->10は経験がない上、不得意なため相性の問題から止めた方が良い。
  • (消去法的に)プロダクトor組織の方向かと考える
    • マネージャーを目指すのは良しと考えているが、上記にある自身の特性がより活きる方向はどの方向か
    • プロダクトコードとの距離が密接な方が自身の真価を発揮できると思われるし、将来的には少しでも手を動かしていたいため、プレイングマネージャー的な所を目指したいと考えている(プロダクト/組織の間?)。果たしてそんなポジションあるのか

キャリアに関しては、こだわるつもりはなく、(カメレオンタイプの気質もあるように思うため)、転〇先の状態等を考慮して柔軟に考えていきたい。ただ、確実に問われる部分ではあるため、ある程度整理した。

※気が向いたら適宜更新

以上

ref

エンジニアを分類する、3つのタイプ

「複雑さ」と「普遍性」とエンジニアのキャリアについて最近考えていること