技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

Chromeでharファイルを出力する手順

ChromeデベロッパーツールのNetworkタブでは、アクセスしたページとやりとりされたリクエストやレスポンスについて、その内容や所要時間を確認することができます。
そして、Networkタブで確認した内容は、harファイルと呼ばれるファイルに出力し、保存することができます。
 
harファイルを出力する手順は以下の通りです。
 
【手順】
1.Chromeを立ち上げる。
2.F12でデベロッパーツールを立ち上げる。
(「その他ツール」→「デベロッパーツール」、Ctrl+Shift+Iでも可)
3.「Network」タブをクリックする。

f:id:akira2kun:20210529224126j:plain

4.録画状態なら停止する(画像中③が赤丸ならクリックして黒丸にする)
5.既に出力されているリクエスト・レスポンスを削除する(画像中①をクリック)
6.「Preserve log」にチェックを入れる(画像中②のチェックを入れる)
7.録画状態にする(画像中③をクリックして赤丸にする)

f:id:akira2kun:20210529224138j:plain

8.ブラウザ上で任意の操作を行う(リクエスト・レスポンスが記録される)
9.画像黄枠内で右クリックし、"Save all as HAR with content"をクリック

f:id:akira2kun:20210529224151j:plain

10.ファイル保存ウインドウが開くので、任意のフォルダにharファイルを保存
 
----
 
ちなみに、harファイルはjson形式で保存されています。
そのままでは人間の目で確認するのが困難ですが、"HTTP Archive Viewer(http://www.softwareishard.com/har/viewer/)"のようなページでharファイルを解釈することで、ChromeのNetworkタブのような表示形式に加工することができます。

 

f:id:akira2kun:20210529224213j:plain

 

ChromeからJavaScriptを実行する

Chromeデベロッパーツールの機能の一つであるConsoleでは、任意のJavaScriptを実行することができます。
コマンドプロンプトDOSコマンドを実行するような感覚でJavaScriptを実行することができるので、JavaScriptの仕様のちょっとした確認を行いたい場合に便利です。
 
【手順】
1.Chromeを立ち上げる。
2.F12でデベロッパーツールを立ち上げる。
(「その他ツール」→「デベロッパーツール」、Ctrl+Shift+Iでも可)
3.「Console」タブをクリックする。
4.任意のJavaScriptを記述し、実行する。
(Shift+Enterで改行、Enterで実行)

f:id:akira2kun:20210529182900j:plain



 
----
 
また、Consoleで実行するJavaScriptにより、現在開いているページに影響を与えることもできます。
例えば、MDNのJavaScriptチュートリアルのトップページ(https://developer.mozilla.org/ja/docs/Web/JavaScript)にアクセスし、下記コードを実行すると、ページの文字列置換('JavaScript'を'[JavaScript]'に置換)を行うことができます。
 
document.body.innerHTML=document.body.innerHTML.replace(/JavaScript/g,'[JavaScript]');

 

f:id:akira2kun:20210529182915j:plain

 

ソースコード中に顧客情報を記述してはいけない

特定の顧客用に特別なビジネスロジックを用意している場合、以下のようにソースコード中に顧客情報を記述することで実装することはできます。
(顧客番号が'1111111'である場合に特別なビジネスロジックを実行するとします)
 



if (処理中の顧客番号が'1111111'の場合) {
  特別なビジネスロジック
}



 
----
 
しかし、この実装方法には以下の問題があり、望ましくありません。
特別なビジネスロジックを適用する対象の顧客の情報は、(データの管理にDBを使用しているシステムであれば)DBに持たせるべきです。
 
・対象となる顧客が追加されたり削除されたりする度にソースコード修正が必要になる
 コンパイルやデプロイが必要となり、対応工数や対応のリスクが増加する。
 →DBで管理すればDB内の情報の補正だけで済む。
 
・データを一元管理できなくなる
 対象となる顧客を調べるためにはソースコードを見なくてはならなくなる。
 →DBで一元管理すれば情報の検索が容易になる。
 
・セキュリティを確保するのが難しくなる
 ソースコードが流出した場合に顧客情報も同時に流出してしまう。
 (参考:フリマアプリ「メルカリ」で2021/05/21に発表された情報流出事故
 →DBで一元管理すれば、顧客情報の流出のリスクを減らすことができる。
 
----
 
対象となる顧客の情報をDBに持たせる場合、以下のような実装になります。
 
1.対象となる顧客の情報を管理するテーブルを別途持たせる場合

f:id:akira2kun:20210523182640j:plain




if (特定処理対象顧客テーブルに処理中の顧客番号が存在する場合) {
  特別なビジネスロジック
}



 
2.既存のテーブルにフラグを持たせる場合

f:id:akira2kun:20210523182702j:plain



if (処理中の顧客番号について顧客テーブル.特定処理対象フラグが'1'の場合) {
  特別なビジネスロジック
}


java:ソートキーが複数存在する場合、一時的なフォーマット変更ではなく独自Comparatorで対応するべき

複数のソートキーが存在するオブジェクトの配列について、固定長のフォーマットに直すことでソートキーを1つにできます。
しかし、フォーマットを直して1つのキーでソートできるようにするよりも、独自Comparatorを定義して複数のキーでソートした方が高速なので、そのようなケースでは独自Comparatorを定義して対応するべきです。
 
単純にフォーマット変更でコストがかかるだけでなく、ソート処理自体も複数のキーでソートした方が高速です。
複数のキーを1つのキーにまとめてソートする場合は必ずキー全体を見る必要がありますが、複数のキーでソートする場合はキーの一部を見るだけでソートできる場合があるので、複数のキーでソートした方が高速になるのだと思います。
 
以下、テスト結果です。
 

ソースコード
・SortTest.java
import java.util.ArrayList;
import java.util.Collections;

public class SortTest {

  public static void main(String[] args) {

    // ソート対象のArrayListの作成
    // 一意な10,000,000個のデータを作成
    // それをランダムにソートする
    ArrayList<SortTestRecord> array1 = new ArrayList<>();
    for (int i = 0; i < 1000000; i++) {
      array1.add(new SortTestRecord( (i / 100000) % 10,
                     (i / 10000) % 10,
                     (i / 1) % 10000) );
    }
    Collections.shuffle(array1);
    ArrayList<SortTestRecord> array2 = new ArrayList<>(array1);

    // 3つのソートキーによりソートする場合
    long startTime1 = System.currentTimeMillis();
    SortTestComparator comparator = new SortTestComparator();
    Collections.sort(array1, comparator);
    long endTime1 = System.currentTimeMillis();

    // フォーマット変更して1つのソートキーでソートする場合
    long startTime2 = System.currentTimeMillis();
    ArrayList<String> array2tmp = new ArrayList<>();
    for (int i = 0; i < array2.size(); i++) {
      array2tmp.add(String.format("%02d", array2.get(i).getSortkey1()) +
             String.format("%03d", array2.get(i).getSortkey2()) +
             String.format("%05d", array2.get(i).getSortkey3()));
    }
    long startTime2inside = System.currentTimeMillis();
    Collections.sort(array2tmp);
    long endTime2inside = System.currentTimeMillis();
    array2 = new ArrayList<>();
    for (int i = 0; i < array2tmp.size(); i++) {
      array2.add(new SortTestRecord(
          Integer.parseInt(array2tmp.get(i).substring(0,2)),
          Integer.parseInt(array2tmp.get(i).substring(2,5)),
          Integer.parseInt(array2tmp.get(i).substring(5,10))));
    }
    long endTime2 = System.currentTimeMillis();

    // 結果確認
    System.out.println("[サンプル出力]");
    System.out.println("(1つ目、123,457つ目、1,000,000つ目を出力)");
    array1.get(0).print();
    array1.get(123456).print();
    array1.get(999999).print();
    System.out.println();
    System.out.println("[配列の差異箇所出力]");
    int diffCount = 0;
    for (int i = 0; i < 1000000; i++) {
      if (array1.get(i).getSortkey1() != array2.get(i).getSortkey1() ||
        array1.get(i).getSortkey2() != array2.get(i).getSortkey2() ||
        array1.get(i).getSortkey3() != array2.get(i).getSortkey3()) {
        System.out.println( (i + 1) + "つ目の要素の内容を出力");
        array1.get(i).print();
        array2.get(i).print();
        diffCount++;
      }
    }
    if (diffCount == 0) {
      System.out.println("差異無し。");
    }
    System.out.println();
    System.out.println("[処理時間]");
    System.out.println("・3つのソートキーによりソートを実施");
    System.out.println( (endTime1 - startTime1) + " ms");
    System.out.println("・フォーマット変更を行いソートを実施");
    System.out.println( (endTime2 - startTime2) + " ms");
    System.out.println(" (内、ソート部分だけの時間)");
    System.out.println(" " + (endTime2inside - startTime2inside) + " ms");

  }

}
 
・SortTestRecord.java
public class SortTestRecord {

  private int sortkey1 = 0;
  private int sortkey2 = 0;
  private int sortkey3 = 0;

  // コンストラク
  SortTestRecord(int sortkey1, int sortkey2, int sortkey3) {
    this.sortkey1 = sortkey1;
    this.sortkey2 = sortkey2;
    this.sortkey3 = sortkey3;
  }

  // getter
  public int getSortkey1() {
    return this.sortkey1;
  }

  // getter
  public int getSortkey2() {
    return this.sortkey2;
  }

  // getter
  public int getSortkey3() {
    return this.sortkey3;
  }

  // プリント用クラス
  public void print() {
    System.out.println(sortkey1 + "," + sortkey2 + "," + sortkey3);
  }

}
 
・SortTestComparator.java
import java.util.Comparator;

public class SortTestComparator implements Comparator<SortTestRecord> {

  @Override
  public int compare(SortTestRecord o1, SortTestRecord o2) {
    if (o1.getSortkey1() < o2.getSortkey1()){
      return -1;
    } else if (o1.getSortkey1() > o2.getSortkey1()){
      return 1;
    } else if (o1.getSortkey2() < o2.getSortkey2()){
      return -1;
    } else if (o1.getSortkey2() > o2.getSortkey2()){
      return 1;
    } else {
      return o1.getSortkey3() - o2.getSortkey3();
    }
  }

}

 
【結果(1回目)】
[サンプル出力]
(1つ目、123,457つ目、1,000,000つ目を出力)
0,0,0
1,2,3456
9,9,9999

[配列の差異箇所出力]
差異無し。

[処理時間]
・3つのソートキーによりソートを実施
2750 ms
・フォーマット変更を行いソートを実施
19105 ms
 (内、ソート部分だけの時間)
 3827 ms
 
【結果(2回目)】
[サンプル出力]
(1つ目、123,457つ目、1,000,000つ目を出力)
0,0,0
1,2,3456
9,9,9999

[配列の差異箇所出力]
差異無し。

[処理時間]
・3つのソートキーによりソートを実施
1692 ms
・フォーマット変更を行いソートを実施
13627 ms
 (内、ソート部分だけの時間)
 3350 ms
 
【結果(3回目)】
[サンプル出力]
(1つ目、123,457つ目、1,000,000つ目を出力)
0,0,0
1,2,3456
9,9,9999

[配列の差異箇所出力]
差異無し。

[処理時間]
・3つのソートキーによりソートを実施
2177 ms
・フォーマット変更を行いソートを実施
15333 ms
 (内、ソート部分だけの時間)
 3224 ms
 
【結果まとめ】

f:id:akira2kun:20210522193143j:plain

 

Visual Studio Codeでdraw.ioを使う

フローチャートやネットワーク図等は、フリーの作図ツール「draw.io」を使うと楽に記述できます。
Excelでも記述はできますが、draw.ioでは各々の図の記述に適した素材が提供されているという特徴があります。
また、draw.ioはブラウザ上での使用が想定されていますが、フリーのコードエディタである「Visual Studio Code」を用いるとローカル上で編集が可能になります。
 
今回は、使用手順を簡単に紹介していきたいと思います。
 
1.draw.ioのホームページにアクセスする。
https://app.diagrams.net/
 
2.ファイルの保存方法を選択する。
今回は「Device」を選択します。これで図がローカルに保存できるようになります。
また、選択後、新規にファイルを作成するかどうか聞かれるので、新規にファイルを作成する(Create New Diagram)を選択します。

f:id:akira2kun:20210520025533j:plain
 
3.どのような図を作成するか選択する。
今回は「Flowchart」を選択します。

f:id:akira2kun:20210520025940j:plain
 
4.ブラウザ上でファイルを編集してみる。
例えば、図形を追加する場合は、「Shapes -> General」から追加することができます。
また、図形の中に文字を入力したい場合は「右クリック -> Edit」で入力できます。
図を保存したい場合は、画面上部の「Click here to save.」から保存できます。

f:id:akira2kun:20210520025901j:plain

f:id:akira2kun:20210520025916j:plain
 
5.Visual Studio Code拡張機能を入れる。
ローカルでVisual Studio Codeを開き、拡張機能の検索画面を開きます。
そして、「draw.io」を検索し、「Draw.io Integration」をインストールします。

 
6.4でローカルに保存したファイルをVisual Studio Codeで開く。
すると、以下のように、ローカルでdraw.ioの図を編集できるようになります。
図形の追加は「General」から、文字編集は図形をダブルクリックすることで可能です。



 

システム改修で発生するデータ移行とは

通常のシステムでは、データベースやファイル等にデータを蓄積します。
そして、システムの改修の際に、そのデータのフォーマットを変更することがあります。
 
データのフォーマットの変更には、「これまでに蓄積されたデータをどのように扱うのか」という問題が付きまといます。
開発者は、データを移行するのかしないのか、移行するのであればどのように移行するのか、ということを判断する必要があります。
ここでは、データ移行について、具体的な例を用いて見て行こうと思います。
 
【例】
商品購入履歴テーブルにて、清算番号をキーとし、いつ誰が何をどれだけ購入したのかを管理しているとします。
システム稼働開始時は、商品コードから単価は一意に求まるとします。
また、当該テーブルを見て、各々の顧客が過去に商品を購入したことがあるかどうかを見る必要があるとします。
 
ここで、各々の清算において値引きを可能にするシステム改修を行うとします。
また、売り上げを正しく求めるため、清算毎の単価を新たに管理する必要が出てきたとします。
 
この場合、商品購入履歴テーブルのフォーマットを以下のように変更する必要が出てきます。

f:id:akira2kun:20210410183802j:plain
 
ここで、過去に蓄積されたデータの扱いについて、以下のような方針を考える必要が出てきます。
 
【対応方針】
1.データ移行せず、旧商品購入履歴テーブルをそのまま残す
データ移行が困難であり、プログラムの改修箇所が少ないのであれば、データ移行しないという選択肢があります。
データ移行が困難な状況としては、データ量が多く時間がかかる、システムを停止できる時間が少ない、といった要因があります。
 
この方針の場合、過去の購入履歴を参照するプログラムに対して改修が必要になります。
新商品購入履歴テーブルと旧商品購入履歴テーブルの両方を見る、という改修を行う必要があります。
例えば、顧客コード"0000001"の顧客が過去に商品を購入したかどうかを見たい場合、新商品購入履歴テーブルになければ旧商品購入履歴テーブルを見る、とする必要があります。
 
この方針のデメリットとしては、プログラムロジックが複雑になり、保守性が低下することが挙げられます。
もし、自動化されていない運用作業で商品購入履歴テーブルを見る必要がある場合、その作業が煩雑になることにも注意する必要があります。運用ミスの原因にもなり得ます。
 
2.データ移行する
データ移行する場合、旧商品購入履歴テーブルのデータを、新商品購入履歴テーブルに移し替える必要があります。
システムを一時的に停止し、移行用のプログラムを実行させ、旧商品購入履歴テーブルのデータを新商品購入履歴テーブルのフォーマットに合わせた形で移行します。
(失敗に備えてバックアップを取得することも重要です)
 
データ移行する際には、フォーマットをどのように合わせるのかを考える必要があります。
今回の例で言うと、移行したデータの単価に何をセットするのか、ということを考える必要があります。
移行日以前の単価を見る必要が無いのであれば、nullで問題ありません。
見る可能性があるのであれば、現在の単価(過去に単価が変動しているのであれば当時の単価)をセットする必要があります。

f:id:akira2kun:20210410183830j:plain

 

Vue.js:componentで独自タグを定義する

Vue.jsの重要な機能の一つとして、componentという機能が存在します。
これは部品化に役立ちます。
 
この機能を利用することで、独自にタグを定義できるようになり、冗長な記述を減らすことができます。
例えば、今回はサンプルコードとしてsample1.htmlとsample2.htmlを用意していますが、sample1.htmlでは同じようなdivタグの記述が続くのに対し、sample2.htmlはdivタグの記述を独自のmy-componentタグにまとめることで同じような記述が続くのを防ぐことができています。
 
【サンプルコード】
・sample1.html
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <title>Vue.js - sample1</title>
 <style>
 .my-button {
  background-color: #ccc;
  text-align: center;
  border: 3px solid #000;
 }
 </style>

</head>
<body>

<div id="app">
 <div class="my-button" v-on:click="func1">
  <strong>機能:</strong>挨拶
 </div>
 <div class="my-button" v-on:click="func1">
  <strong>機能:</strong>ハローワールド
 </div>
</div>

<script src="https://unpkg.com/vue"></script>
<script>
// Vueオブジェクトの生成
var app = new Vue({
 el: "#app",
 methods: {
  func1: function() {
   alert('Hello World!')
  }
 }
});
</script>
</body>
</html>
 
・sample2.html
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <title>Vue.js - sample2</title>
 <style>
 .my-button {
  background-color: #ccc;
  text-align: center;
  border: 3px solid #000;
 }
 </style>

</head>
<body>

<div id="app">
 <my-component>挨拶</my-component>
 <my-component>ハローワールド</my-component>
</div>

<script src="https://unpkg.com/vue"></script>
<script>
// コンポーネント定義("my-component"という独自タグを作成)
// templateのslotタグにはmy-componentタグで与えられた値が挿入される
Vue.component('my-component', {
 template: `
 <div class="my-button" v-on:click="func1">
  <strong>機能:</strong>
  <slot></slot>
 </div>
 `,
 methods: {
  func1: function() {
   alert('Hello World!')
  }
 }
});

// Vueオブジェクトの生成
var app = new Vue({
 el: "#app"
});
</script>
</body>
</html>
 
【実行結果】
実行結果はsample1.htmlとsample2.htmlで同じです。
以下のように2つの部品が出てきます。

f:id:akira2kun:20210404013726j:plain

 
部品をクリックするとポップアップが表示されます。

f:id:akira2kun:20210404013737j:plain

 
----
  
この記事では紹介しませんが、componentの記述を外部ファイルに切り出すことも可能です。
外部ファイルに切り出すことで、同じ部品(component)を複数ファイルで利用することができるようになります。
これについては、Webで「Vue.js components」と検索すれば情報が出てきます。
 
なお、現在はhtmlファイルをChromeで開いて実行していますが、外部ファイルのimportを行う場合はこの方法での実行は困難です。
(セキュリティ対策のため)
外部ファイルのimportの実行を試すには、Vue CLIでサーバを立てる等の対応が必要になります。