こんにちは、シンクロ・フードの大久保です。
10年以上運用しているWebサービスだと、文字コードがShift_JIS、という状況は多いのではないでしょうか。
弊社もそうだったのですが、昨年、自社で運用するWebサイトすべての文字コードをShift_JISからUTF-8に変更しました。
今回はこの対応について実施したことをご紹介したいと思います。
前提条件
WebサーバはApache、WebアプリケーションサーバはTomcat、DBサーバはMySQL5.6を利用しています。
Webサイトは全部で6サイト、すべてJavaで書かれたWebアプリケーションで、その中のメインである飲食店.COMのJavaファイル数は約2000ファイル、cssやjs、htmlファイルなどを含めると約10000ファイルくらいです。Webアプリケーションですとそこそこ大きい部類だと思います。
なぜUTF-8にするのか
Shift_JISのままで良いのでは?という意見があるのかもしれませんので、念のため弊社がUTF-8化した代表的な理由を挙げておきます。
- 入力できる文字の増加(㈱、㈲、①など)
- UTF-8で書かれたJavaScriptとの連動がスムーズになる
- nodejs系フロントエンドツールがShift_JISに対応していないことがある
1は昔からある問題なので、今回変更した理由の動機としては薄いです。
2はJavaScriptのMV◯◯系フレームワーク(AngularJSやKnockoutJS等)を使う度に悩まされている問題で、JavaScriptを使ったリッチな動きのサービスを作る機会が増えたため、避けて通れない問題となっていました。
3は、GruntやGulpで実行するようなプラグインが、Shift_JISだと動かないことがあるという問題で、そもそもそんな制約に縛られるのが嫌だ、と開発者のストレスが溜まっていました。
一言で言うと、モダンなフロントエンド開発を進めていくたびに、「UTF-8だったら楽だなあ…」と思うことが増えてきたため、対応に踏み切ったというわけです。
実施したこと
文字コードを変更するにあたって実施したことを以下に挙げます。
実際にはこれ以外にも細かい対応がありましたが、大きくは以下の対応です。
プログラムファイルそのものの文字コードを変更する
ファイルの文字コード変更です。エディタ等で一気に変えることができるので、簡単です。
弊社では、Linuxのnkf
コマンドを使って一気に変換をしました。
プログラムファイル中に記載されている文字コードを変更する
ファイル内に登場する、Shift_JISやsjisなどといった文字コード指定を、UTF-8に修正します。
こちらもエディタ等で一気に置換をすることができます。
弊社では、Linuxのsed
コマンドを使って一気に置換しました。
メールの文字コードをiso-2022-jpからutf8に変更する
サイトをUTF-8にするので、こちらも一緒に対応しました。
弊社のサービスは年配の方やWebに疎い方が使うことが多いため、古いメールクライアントを使っている可能性があり、少し不安なポイントでした。結果的には、文字化けする、という問い合わせは数件のみでした。
DBデータの文字コードを変更する
alter文を使って、DBのデータを変換します。
弊社ではMySQLを使っているため、alter文はinformation_schemaから一気に自動生成するスクリプトを書いて生成しました。
実際に流してみると、変換に2時間くらい必要で、ここはメンテナンスとしてサイトを停止することで対応しました。
データ量が大きいサービスを運営している方は、ここが一番のネックになるかもしれません。
各種サーバ設定の文字コードを変更する
ここは普通に変更するだけです。特に問題はありませんでした。
特別な対応をしたこと
上記の対応とは別に、特別な対応をしたことが2点あります。
- 検索エンジンにインデックスされている、Shift_JISでURLエンコードされたパラメータをどうするか
- 開発中コードとの衝突問題
1つずつ説明いたします。
検索エンジンにインデックスされている、Shift_JISでURLエンコードされたパラメータをどうするか
フリーワード検索の結果などで、日本語の文字列を含むパラメータが、検索エンジンにインデックスされていることがあると思うのですが、その文字列はShift_JISでURLエンコードされています。Shift_JISでサイトを運営していれば当然だと思います。
その状態で、WebサイトをUTF-8に変更すると、検索エンジンからはShift_JISでURLエンコードされたパラメータが飛んでくるにも関わらず、UTF-8でURLデコードすることになってしまい、文字化けが発生してしまいます。
リンク元である検索エンジンにインデックスされているURLを変更できれば良いのですが、それはできません。
対応策
まず思いつく対応策は、URLエンコードされた文字列から、文字コードを逆引きする、という方法です。
これができれば、文字コードに応じてURLデコードするだけなので簡単です。
ですが、URLエンコードされた文字列から文字コードを判定するは困難である、と結論付け、この方向はやめました。
今思うと、この方向性を進めることもアリだったのでは?と思っています…。
[参考] http://clikington-saito.com/UrlEncode/UrlEncode.html
そこで僕たちが取った手法は、判定ができないのであれば、パラメータ名を分ければいい!という、かなり強引な手法です。
まず日本語がやりとりされるであろうパラメータを抽出し、パラメータ名を別名に変えます。具体的にはパラメータ名に_u
を付与しました。
そして、旧名(_u
が付かないパラメータ)の場合はShift_JISでURLデコードし、_u
を付けて301リダイレクトを実施。
新名(_u
が付いているパラメータ)の場合はUTF-8でURLデコードしました。
これで問題は解決するのですが、この別名変換処理は、UTF-8への変更を反映すると同時に有効にしなければならないため、事前にコード内に処理を埋め込みつつ、設定値によって無効化しておき、UTF-8への変更と同時に有効化する、という方法も行なっています。
文字コード変更全体の中で、工数を一番かけたのがココです。文字化けを受容する選択肢もあったのですが、やはり検索エンジンからのアクセスは少しでも無駄にしたくなかったため、丁寧に実施しました。概ね成功したと思います(一部文字化けは発生してしまいましたが…)。
開発中コードとの衝突問題
弊社は、バージョン管理にgitを使っており、常時10名以上のエンジニアがブランチを切って開発を繰り返しています。
もし、文字コードを変えるのであれば、以下のような手順になります。
- gitのmasterブランチから作業用ブランチを切り
- 文字コード変更処理を加え
- テストを実施
- masterへの取り込み
この手順の中で、時間がかかるのは3番目のテストです。テストをしている間に、他の開発者がmasterからブランチを切って開発を実施し、先にmasterに取り込まれてしまうと、文字コード変更のための修正がmasterに取り込まれる時点で確実に競合が発生してしまいます。
対応策
これを解決する方法としては、リリース直前まで、gitのブランチを切らない、ということで対応することにしました。
まず、gitのmasterからブランチを切ることと、文字コード変更処理をすること、この2つを一気に実施するシェルスクリプトを書きます。
テストをする際は、毎回このスクリプトを使うことで、現時点の最新状態をUTF-8にしてからテストを行い、毎回破棄していました。
そしてデプロイ前日のみ、開発者にはブランチを切ることを一時停止してもらい、上記スクリプトを実行、masterへマージ、本番デプロイ、という作業を行ないました。
尚、この状態だと、開発者の手元にShift_JISで開発中のブランチが残ってしまうのですが、こちらについては、手元のブランチをUTF-8化するスクリプトを用意し、こちらを個別に実行してもらうことにしました。
インターンを含めると10名以上が同時に開発しているリポジトリですが、この方法を用いることで、最小限の工数でプログラムの文字コードを変更することができました。
文字コード変更後の感想、反省など
その他、実際にやってみて起こった出来事などを書いておきます。
MySQLの文字コードは、utf8mb4を使うべきだった
はじめはMySQLの文字コードはutf8にしていたのですが、サービスを運用している中で、登録できない文字がある…ということに気づきました(𠮟、など)。
これは4バイトの文字で、こういった文字を格納するには、utf8mb4という文字コードにしなければならない、ということを後で知りました。
ですので、この変換のためにもう一度深夜にメンテナンスを実施しています…。
MySQLの設定値、group_concat_max_lenを伸ばす必要があった
Shift_JIS->utf8mb4ということで、MySQL内で格納されるバイト数が2バイトから最大4バイトまで増えています。
この変更で見落としていたのが、group_concat_max_lenという設定値…。
場所は少ないのですが、group_concat関数を使っている部分があり、この結合結果文字数が、デフォルトは1024バイトまでしか保持できず、文字コード変更後はこれを超えてしまい、エラーになってしまいました(厳密にいうと、group_concatの結果が切断されます)。
こちらは、group_concat_max_lenを1024から2048に変更することで対応しました。
[mysqld] group_concat_max_len=1024 // これを2048に変更する
最後に
以上、かなり大雑把な説明ですが、弊社が行なったWebサイトをUTF-8にする際に実施したことをご紹介しました。
工数はかかりましたが、移行後は文字コード関連のトラブルはほぼ発生しなくなり、かなり快適に開発することができています。
対応自体は一部かなりの力技になってしまいスマートとは言えませんが、Shift_JISのWebサイトをUTF-8化する際の参考になれば幸いです。
尚、シンクロ・フードではエンジニアを大募集しています!
こういったレガシー環境をモダン化する対応以外にも色々なことをやっていますので、少しでもご興味があれば、気軽にお話しする場を設けますので、以下よりご連絡ください!