スプーキーズのちょっとTech。

SPOOKIES社内のより技工的な、専門的なブログページです。

CSV文字コード変換アプリのエンコーディング判定

こんにちは、spookies歴2か月目の下釜です!
今回は、CSVファイルの文字コード自動判定処理で発生した問題と、その対策について共有します。特に、jschardet を使っている方や、CSVの文字コードでハマった経験がある方に参考になれば嬉しいです。

問題内容

jschardet を使ってCSVファイルの文字コードを自動判定していたが、以下のようなケースでエラーが発生した:

  • 文字コードの confidence が低すぎて undefined になる

  • ファイルの内容が英数字中心で判定が困難(ASCIIと誤認識)

  • 結果として "xxx の文字コードを特定できませんでした" という例外が発生

原因

jschardet は、文字数が少なかったり、内容が英数字ばかりのCSVなどは正しく判定できない
UTF-8でもShift_JISでも解釈できるようなファイルだと、confidenceが低くなりやすい

対応内容

元のコード(変更前)

const detected = jschardet.detect(buffer);
const encoding = detected?.encoding?.toLowerCase();
if (!encoding || detected.confidence < 0.3) {
  throw new Error(`${file.name} の文字コードを特定できませんでした`);
}
const supportedEncodings = ['utf-8', 'ascii', 'shift_jis', 'windows-1252'];
if (!supportedEncodings.includes(encoding)) {
  throw new Error(`${file.name} は未対応の文字コードです(推定: ${encoding})`);
}
const decodedText = iconv.decode(buffer, encoding);

変更後のコード(修正後)

const detected = jschardet.detect(buffer);
let encoding = detected?.encoding?.toLowerCase() || '';
// 英数字のみか判定
const asciiOnly = /^[\x00-\x7F]*$/.test(buffer.toString('binary'));
// encoding 不明 or confidence 低い or 英数字だけなら shift_jis にフォールバック
if (!encoding || detected.confidence < 0.2 || asciiOnly) {
  encoding = 'shift_jis';
}
const supportedEncodings = ['utf-8', 'ascii', 'shift_jis', 'windows-1252'];
if (!supportedEncodings.includes(encoding)) {
  throw new Error(`${file.name} は未対応の文字コードです(推定: ${encoding})`);
}
const decodedText = iconv.decode(buffer, encoding);

修正内容の要点

  • confidence の判定閾値を緩和 0.3 → 0.2 に変更

  • エンコーディング未判定時に、shift_jisにフォールバックする

  • 英数字だけのファイル対策でASCII文字だけか判定し、同様に shift_jis へフォールバック

  • iconv-lite による変換処理自体は変更なし、判定部分のみ修正で済ませた

検証結果

  • マイナビのCSVなど、これまで読み込み時にエラーになっていたファイルもすべて取り込み可能になった

  • 日本語や記号の文字化けも発生せず、変換後はBOM付きUTF-8として正しくダウンロードされる

まとめ

CSVの文字コードは「なんとなくUTF-8でしょ」で済まないことが多く、特に日本語圏では Shift_JIS や Windows-1252 の混在がよくあります。 jschardet は便利ですが過信せず、フォールバック戦略を用意しておくことが安定運用のカギだと痛感しました。

同じように CSV ファイルの読み込み処理で困っている方の参考になれば幸いです!

NuxtのコーポレートサイトをNext.jsに置き換えたら、GithubCopilotに仕事を奪われかけた話

こんにちは。Spookies歴3か月のエンジニア、鈴木です。

この度、Nuxt.jsで作られていた弊社のコーポレートサイトをNext.jsに移植しました。

新年一発目の技術ブログは、本件についてまとめようと思います。

リプレイスに至った経緯

Reactのポートフォリオがない

Spookiesでは、今までJavaScriptフレームワークにvue.jsが採択されがちでした。

もちろんReactの経験値がゼロだったわけではなく、最近に入って一部の小さなプロジェクトや業務等で利用されることはあったようなのですが、ナレッジ量的に無難な選択肢としてVue.jsやNuxt.jsが使われてきた経緯があるようです。

しかし、Spookiesの新たな挑戦の一つとして、VueだけでなくReactも大々的に取り扱うという目標ができました。 とはいえ、大々的に取り扱うにはまず実績があるべき。

一番手っ取り早く、自社のReactの実力を表現する方法はないか。

そうだ、まずはコーポレートサイトをReactベースにしてしまおう。

僕のスプーキーズチュートリアル

この仕事、スプーキーズにおける僕の初仕事でした。

この社内プロジェクトでスプーキーズのプロジェクト運営になれて、 以降のプロジェクトでエンジニアとしてアサインできるようになる必要がありました。

社内で完結する業務で水合わせってワケです。

リプレイスのキモ

NuxtとNextjsみたいな超有名フレームワーク間の移行なんて、ドキュメントがネットに無限に転がっており、具体的にやり方を今更書いてもしゃーないのでキモだけ書きます。

アーキテクチャの変更点

従来はNuxtでSSGした静的サイトをGithub Pagesでホストするという構成でした。

この部分をNextjsのSSGに置き換えたので、そのほかのインフラに大きな変更はありません。 RouterにはPage RouterではなくApp Routerを使いました。

nuxtからの移行作業という点だけで言えばPage Routerの方がやりやすくはあったのですが、 今後はAppRouterを扱う案件が増加すると思われることと、 Page Routerが将来的に破棄されるんじゃないかという漠然とした不安もあったため、上司と相談の上App Routerを採用することになりました。

GithubCopilotを使おう

Github Copilotを使いましょう。

単純な移植ならGithub Copilotは優秀です。 VS CodeのCopilot Chatに貼り付け、「これをnextjsに移行してください」と書くだけです。 これだけで、作業の七〜八割を終わらせてくれます。

残り生成ミスによるエラーの修正等ですが、これも大体Copilotに修正させることができます。 人間はCtrlとCとTabとクリックさえ押せればいいのです。

こういった、やりたいことが明確に定義されているが知識理解を要する単純作業は、我々人類はAIにかてっこないです。

感想

結局、Next.jsが良いか、Nuxt.jsが良いか

今回の移行では、スプーキーズの戦略的意図があったためNuxt.jsではなくNext.jsが選択されましたが、この戦略的意図がなければ、Next.jsとNuxtどちらが良いのでしょうか。

コーポレートサイトというプロジェクト単体で考えればNext.jsにこだわる必要はなく、Nuxt.jsで十分でしょう。

ただ、個人的な意見を述べると、社内の技術スタックをReactに統一するメリットは、Nuxt.jsやVue.jsの迅速な開発というメリットよりも大きいと考えます。 Vue.jsだと苦戦するけどReactであれば容易に対応できる実装というのは、やはり多数あるように感じるためです。

仮にコーポレートサイトのようなVue.jsで十分なプロジェクトであったとしても、ブログ・プレスリリース機能をつけてSSGするだとか、絞り込みや検索機能を持たせたうえでSSGするだとか、そういったちょっと捻った実装をする場合はReactのほうがわかりやすく書けるように僕は思います。

また、今回は使いませんでしたがNext.jsにはISRという機能があるので、SSGとSSRのいいとこ取りができるのもコーポレートサイトにNext.jsを利用するメリットです。

AIの使い方と未来

タイトルにもあるように、この仕事でやりたいロジックが明確なコーディングであればほぼGithubCopilotに任せられることを肌で感じました。

ただ、最終的に生み出したコードに責任を持つのが人間であることは、どれだけAIが進歩しても変わらないでしょう。

「プログラマーはいらなくなる。が、エンジニアという職業は必要であり続ける。」

「だからこそ、エンジニアのキャリアや成長の仕方が大きく変わる」

というのが、僕のAIとの初仕事の感想でした。

SECCON Beginners CTF 2024 参加記

こんにちは。CTF部に入部したてほやほやのみーさんです。

実は5月の末金会後にCTF勉強会をやったこともあり部員が2人増えました!🎉

ちょうど良いタイミングで初心者向けのコンテストがあるとのことで、2024/6/15-16 に開催されたSECCON Beginners CTF 2024に参加してきました!

spookiesチームとしての参加メンバーは4名、個人参加・他チームでの参加をしているメンバーもいました。 それぞれ予定の合間を縫っての参加でしたが、spookies全体としてかなり盛り上がったコンテストでした。

spookiesチームとしては個人参加している部長を超えることを目標に頑張りました。 そしてもう一人の新入部員もなんと個人参加!ガッツがありますね!

結果は全体237位! まだまだ伸び代があります...。ありすぎます...!

そしてなんと我がCTF部部長はソロ参加で3位! 流石すぎます。

ちなみに 2024/6/21-23 は Wani CTF 2024 に参加もしていました。 まずは部長を目指して、部員同士、切磋琢磨していきたいですね。

以下、各メンバーの参加記まとめです。

続きを読む

2024軽井沢合宿 メインプログラム開発!! 後編

前回の前編記事から引き続き、開発アルバイトの山本です!!
2024 2/23 ~ 2/25 の2泊3日で軽井沢合宿を行いました。そしてメインプログラムとして3人ずつの5チームに分かれて開発を行いました!!
テーマは「業務改善ツールを作りまくる」です!!

※案件の内容などを含む場合は一部省略して記載しております。ご了承ください。

下記5チームのうち、今回は前編で紹介しきれなかった「soy-beans」「Over bet」「d3」の成果物を公開したいと思います!

soy-beans

メンバーのあらゆるアクションを可視化!成果をビジュアライズ!仕事を楽しくする!

JoySoy

私たちは普段、案件ごとにチームが分かれて仕事をしています。チーム内ではみんなが今何をしているのか、どんな成果を上げていっているのか把握できていてもチーム間では少し見えにくい。また、チーム内でもいつどれくらいの仕事を成し遂げたのか、みんなが把握するのは難しい。

チームに関わらず、すべてのメンバーの成果、アクションを可視化したい、いい意味で競い合って高まりあいたい。

そんな気持ちからSpookiesメンバーの成果を確認できる!しかもなんだか楽しい!そんなツールを作りました。

技術構成

  • バックエンド
    • Pure PHP
    • Slack API
  • フロントエンド
    • TS + Vite
    • matter.js(物理演算)
    • PixiJS(2Dグラフィック)

今回は合宿の期間中に動くところまで持っていくべく、メンバーのアクションとしてはSlackへの投稿を拾い上げるまでにスコープを絞りました。(今後アクションのバリエーションを増やしてできるだけたくさんのものを見える化したい。PRのマージなど!)

バックエンドはSlack投稿の巻き上げと、フロントエンド用のメンバーアクション取得APIを作成しています。

フロントエンドではそのAPIをポーリングしてビジュアライゼーションを行います。 最近流行った某ゲームから着想を得て、アクション=メンバーアイコンの球として画面上に溜まっていくところまで実現しました。ここは初めて使いましたがmatter.jsとPixiJSを導入し、ゲームライクな演出とともに見せています。

今回の合宿中では球が溜まっていくだけになっていますが、もっとゲーム的な仕組みを入れる&スコア化するなどしてより楽しいものにしていきたいですね。

Over bet

タスクをこなすとポイントをもらえる制度があまりにも使いづらいので、改良しようのチーム!!

集計も表現も評価もなんもかんも足りてないけどまずは集計だけ!

(やりたいこと多すぎて話が発散しかけ、実装の動き出しが遅くなり後悔)

Overbet_システム構成図

  • 技術: Google Apps Script + clasp により git 管理しながらGASスクリプトの開発
  • 技術: 生成AIを駆使して開発言語のスキル差を吸収
    • python で実装して、GASへの変換
  • マネジメント: 役割をしっかり分担して進めた
    1. 資料作成・発表・取りまとめ
    2. 技術検証・GAS環境の整備と調査
    3. pythonでメインプロトコル実装

d3

ちなみに私(山本)はこのチームで開発させていただきました!このチームのメンバーは実は普段から一緒にいることが多いチームなので、default3を表すd3というチーム名になりました(笑) このチームは二つ開発しました!!

一つ目:作業チケット作成の簡略化

clickupを使って作業チケットを管理していますが、redmineの内容をもとに作っています。ただ、期日やタイトル、担当などを毎回入力して作成しなければなりません…。なのでこの作業を簡略化するためのツールを作りました!
chromeの拡張機能を使って、redmineとclickupのAPIキーを入力して、redmineの作業チケットが表示されているページに作成した「チケット作成ボタン」を押すと……なんとタイトルなどが綺麗に入力された状態のclickupのチケットが作成されます!!ちなみにchromeの拡張機能でのAPIキーの入力は一度行えば半永久的に保存されるので、一度APIキーを保存してしまえば、ボタンを押すだけでチケット作成ができてしまいます!!やったーー!!
ただ実は、担当や期日まで自動入力されたclickupのチケットは作成できませんでした。redmine上でのデータの型と、clickup上でのデータの型が違っていたことなどが原因で、時間内に実装することができませんでした…今後も是非機会を見つけて実装していきたいと思います!

技術構成

  • バックエンド:NestJS
  • フロントエンド(chrome拡張):TS + Vite

二つ目:作業チケットの検索です!

redmine上でチケットの検索を行うことはできますが、チケットのアサインが自分以外の人になってしまうと…悲しいことに検索することができません!!そこで、過去一度でもアサインされたチケットを検索できるようなWebアプリケーションを開発しました! フリーワード検索や、チケットIDでの検索、担当したことがあるチケットなどの項目で絞り込みを行うことができます!

技術構成

  • Next.js v14.1.0
    • App Router
    • SSG ビルド
      • linaria
    • Router Handler
  • Redmine REST API

見つかった課題

  • 実は「現在担当している」による絞り込みは実装できていない(間に合わず...)
  • SpoGPT による MR の自動生成も実装できなかった
    • チケットの詳細から MR の内容と、検証すべきテスト内容の自動生成
  • Redmine API の使いにくさ
    • 例えば「〇〇というキーワードを含んでいる、過去に自分が対応したチケット」という検索は API のみでは実現できない
      • 「過去に自分が対応した」で検索できる手段はない
        • 本システムを通してチケットを作成・チケットデータを蓄積していくことで、このような検索にも対応できるようになる
        • あくまでもタスク管理ツールであるので、複雑な検索には対応できない

終わりに

それぞれのチームが違う視点で業務改善を行うツールを開発しており、最後の成果発表会では大盛り上がりでした!ここで開発したものを今後も拡張していって、運用までしていけたら幸せだなと思いました!

3日間とても楽しかったです!ありがとうございました!