見出し画像

kintoneユーザーの僕が「不便すぎる」を副業で解決してみた

どうも、拓海です。

今日はちょっと個人的な話から始めさせてほしい。

僕は本業でkintoneをバリバリ使っている。営業管理、案件進捗、日報、見積書の管理。会社の業務の大半がkintoneで回っている。

kintoneって、本当によくできたツールだと思う。ノーコードでアプリを作れて、レコード管理が簡単で、ワークフローも組める。会社に導入されたときは「これは便利だ」と素直に感動した。

でも、使えば使うほど「ここ、なんでこんな不便なんだろう」と感じる場面が出てくる。

特にずっと引っかかっていたのが、スプレッドシートとの連携だ。


kintoneユーザーの「あるある不満」

こういう場面、kintoneを使っている人なら絶対に経験があると思う。

取引先のリストをExcelかスプレッドシートで管理していて、それをkintoneに移行しようとするとき。あるいは、kintoneに溜まったデータを加工・分析しようとして、スプレッドシートに書き出そうとするとき。

kintoneにはCSVインポート・エクスポートの機能がある。だから一応できる。でも手順がいちいち面倒くさい。

kintoneにデータを入れるとき

  1. スプレッドシートをCSVでダウンロード

  2. kintoneのインポート画面を開く

  3. CSVをアップロード

  4. 列のマッピングを確認

  5. インポート実行

  6. エラーがあれば修正して再実行

kintoneからデータを出すとき

  1. kintoneで一覧を開く

  2. CSVエクスポートを実行

  3. スプレッドシートにインポート

  4. ヘッダーが日本語で汚いので整形

  5. ようやく分析できる

月に1回ならまあ我慢できる。でも週次でやるとなると、この手順だけで毎回30分近く消える。年間で計算すると20時間以上。これ、完全に無駄だと思っていた。

しかも一番腹が立つのが、「少しだけ修正して戻す」という作業だ。

kintoneのデータをスプレッドシートに出して、10件だけ内容を修正して、またkintoneに入れ直す。この場合、CSVを使うとデータが全部上書きされてしまう可能性があって怖い。かといって1件ずつkintoneの画面で手打ちするのも時間がかかる。

「なんかいい方法ないかな」と思いながら、ずっとモヤモヤしていた。


副業でkintone開発を始めた理由

本業でkintoneを使いながら、僕は副業でkintoneカスタマイズの開発もやっている。

最初は「JavaScriptの勉強がてら」くらいの軽い気持ちだった。kintoneはJavaScriptでカスタマイズできる。業務で毎日触っているツールだから、仕様も理解しやすい。これは相性がいいなと思って、Coconalaに出品を始めた。

やっていく中で気づいたのが、「本業でkintoneを使っているからこそ見える課題がある」ということだった。

外部の開発者は、クライアントから要件を聞いてツールを作る。でも僕は、日常的にkintoneを使うユーザーとして「これが不便だ」という感覚を持っている。その不満が、そのまま開発のアイデアになる。

ユーザー目線で作ったものは、同じくkintoneを使っている人に刺さる。これが僕の副業の軸になった。

で、ずっと感じていた「スプレッドシートとの連携が面倒くさい」という不満を、いよいよ自分で解決しようと思い立った。


最初の構想:「スプレッドシートからkintoneへ登録するだけ」

最初に考えた仕様は、シンプルだった。

スプレッドシートに入力したデータを、ボタン1つでkintoneに一括登録する。

それだけ。

kintoneのREST APIを使えば、GAS(Google Apps Script)から直接POSTリクエストが投げられる。スプレッドシートの各行をkintoneのレコードとして登録する。これは技術的には難しくない。

でも一つ問題があった。重複登録だ。

スプレッドシートのデータをkintoneに登録するとき、すでに登録済みのデータまで二重に入れてしまうリスクがある。「前回実行したときのデータと、今回追加したデータが混在している」というのはよくある状況だ。

だから「重複チェック付き」を最初から仕様に入れた。登録前にkintone側の既存レコードと照合して、すでにあるものはスキップする。

スプレッドシートのデータ
        ↓
kintoneの既存レコードと照合
        ↓
未登録のものだけPOST
        ↓
結果を「登録結果」列に書き込み

これでv1.0が完成した。動いた。テストも通った。「よし、出品しよう」と思った。


コードレビューで発覚した「時限爆弾」

出品前に、最新のkintone APIの仕様を確認しながらコードをレビューした。

そこで発覚した問題が二つあった。

一つ目:offsetの10,000件上限

重複チェックのためにkintoneから既存レコードを全件取得する処理で、こういうコードを書いていた。

while (true) {
  const query = `... offset ${offset}`;
  offset += 500;
}

kintoneのAPIには「offsetで指定できる上限は10,000件まで」という制限がある。つまり、レコードが1万件を超えた瞬間にこのコードは壊れる。

本業でkintoneを使っている僕は知っていた。データは確実に積み上がっていく。最初は数百件でも、2年後には余裕で1万件を超える。「時限爆弾」そのものだった。

解決策は「$idシーク法」。offsetを使わず、前回取得した最後のレコードIDを起点に次の500件を取り続ける方式だ。これでどれだけレコードが増えても対応できる。

二つ目:GASの6分タイムアウト

GASには実行時間の上限がある。1回約6分。登録件数が多いと途中で切断される。どこまで処理したか分からなくなるのは最悪だ。

PropertiesServiceを使って進捗を保存し、タイムアウトになっても続きから再開できる仕組みを実装した。

この二つを修正してv2.0を完成させたとき、「これで出品できる」と思った。でもそこで、もっと大事なことに気づいた。


「APIトークンをコードに書くのはまずい」

v2.0のコードには、こう書いてあった。

const CONFIG = {
  SUBDOMAIN: "your-subdomain",
  APP_ID: 123,
  API_TOKEN: "your-api-token-here",
};

Coconalaで販売するということは、このコードをお客様に渡すということだ。コードをそのまま誰かに見せれば、APIトークンが漏洩する。

本業でkintoneを管理している立場として、これがどれだけまずいか分かる。APIトークンが漏れれば、kintoneのデータに外部からアクセスできてしまう。セキュリティ事故になりかねない。

解決策は「スクリプトプロパティへの分離」だった。GASにはコードとは別に値を保存できる仕組みがある。APIトークンはそこに入れて、コードからは実行時に読み込むだけにする。

function getCredentials() {
  const props = PropertiesService.getScriptProperties();
  return {
    subdomain: props.getProperty("KINTONE_SUBDOMAIN"),
    appId:     Number(props.getProperty("KINTONE_APP_ID")),
    apiToken:  props.getProperty("KINTONE_API_TOKEN"),
  };
}

これでコード上にAPIトークンは一切存在しない。納品物のコードを誰に見せても、接続情報は漏れない。

さらに「初期設定ウィザード」も追加した。スプレッドシートのメニューから実行すると、ダイアログが順番に表示されてサブドメイン・アプリID・APIトークンを入力できる。技術に詳しくないお客様でも迷わずセットアップできる設計だ。

これでv2.1が完成した。


ここで仕様が大きく変わった

v2.1を完成させて、もう一度自分でテスト運用してみた。

本業のkintoneデータを実際に使って動かしてみると、「あ、やっぱり足りない」と気づいた。

スプレッドシートからkintoneへの登録はできる。でも、kintoneのデータをスプレッドシートに出してきて、それを編集して、またkintoneに戻すという操作ができない。

ずっと感じていたあの不満、「少しだけ修正して戻したい」という課題が、まだ解決されていなかった。

これを解決するには、双方向同期が必要だ。

  • kintone → スプレッドシート(PULL)

  • スプレッドシート → kintone(PUSH)

この両方向が一つのツールで完結する仕様にすれば、僕が本業で感じていた不満を根本から解決できる。

「あ、これ仕様変更しよう」と即決した。


$idによる自動判定という設計の妙

双方向同期を実装するとき、一番悩んだのが「新規登録と更新をどう区別するか」という問題だった。

スプレッドシート上に100行のデータがある。そのうち80行はkintoneから引っ張ってきた既存データで、20行は新しく追加したデータだ。PUSHを実行したとき、既存の80行は「更新(PUT)」、新規の20行は「新規登録(POST)」として処理しないといけない。

お客様に「この行は更新です」「この行は新規です」と手動で選ばせるのは面倒くさい。それはツールとして失格だ。

答えはシンプルだった。kintoneのレコード番号($id)を使う。

PULLでkintoneからデータを取得するとき、各レコードの$idも一緒に取得してシートのA列に入れる。新しく追加した行にはA列が空欄になる。PUSHのとき、A列に値があれば「更新」、空欄なら「新規登録」と自動判定する。

if (idVal === "") {
  // $idなし → 新規POST
} else {
  // $idあり → 更新PUT
}

お客様がやることは、シートを見ながらデータを編集して、新しい行を追加するだけ。$idの列は触らなくていい。ツールが勝手に判断してくれる。

本業でkintoneを使ってきた経験から「ユーザーが意識しなくていい設計」がどれだけ大事かを知っていたから、ここは絶対に妥協しなかった。


フィールドコードの問題も解決した

もう一つ、本業ユーザーとして「これ毎回面倒だな」と思っていたことがある。

kintoneのフィールドコードの確認だ。

kintoneのフィールドコードは、アプリ設定画面でフィールドをひとつひとつクリックして確認しないといけない。フィールドが10個あれば10回クリック。しかも確認した値を手動でコードに入力する。これが地味にストレスだった。

調べたら、kintoneのAPIで一発取得できることが分かった。

GET /k/v1/app/form/fields.json

このエンドポイントで全フィールドの情報が返ってくる。これをGASでスプレッドシートに書き出す機能を追加した。メニューから「フィールドコード一覧を取得」を実行するだけで、専用シートに全フィールドが一覧表示される。

さらに進めて、このシートをそのまま「設定ファイル」として使う仕組みにした。D列に「✅」が入っているフィールドだけを登録・更新の対象にする。✅を外せば除外される。コードを一切触らずに、設定が完結する。

⚙️ kintone設定シート
┌─────────────┬──────────────┬────────────────┬──────┐
│ 列名        │ フィールドコード│ 種別          │ 対象 │
├─────────────┼──────────────┼────────────────┼──────┤
│ 会社名      │ 文字列__1行_ │ SINGLE_LINE_TEXT│ ✅   │
│ 見積金額    │ 数値         │ NUMBER         │ ✅   │
│ 受注予定日  │ 日付         │ DATE           │ ✅   │
│ 更新者      │ 更新者       │ MODIFIER       │      │ ← 除外
└─────────────┴──────────────┴────────────────┴──────┘

技術に詳しくないkintoneユーザーでも、直感的に操作できる。これは本業ユーザーの視点がなければ出てこないアイデアだと思っている。


操作パネルを作ったら、製品感が出た

最後に「ボタンで操作できないか」という話になって、操作パネルを実装した。

GASにはキーボードショートカットを直接登録する機能がないので、スプレッドシート上に「🎮 操作パネル」という独立したシートを作って、そこにボタンを配置した。

設計上の工夫として、データシートにボタンを置かなかった。PULLを実行するとデータシートがリフレッシュされるので、そこにボタンを置くと消えてしまうリスクがある。だから専用の操作パネルシートを分けて、ここだけは絶対に触らない領域にした。

🎮 操作パネル    ← ボタン6個(触らない)
⚙️ kintone設定  ← ✅で設定するだけ
kintoneデータ   ← kintoneのデータが展開される

createControlPanel()という関数を1回実行するだけで、パネルが自動生成される。コード納品の形を保ちながら、ボタン操作という使いやすい体験を提供できるようになった。

このパネルが完成したとき、「あ、ちゃんとしたプロダクトになったな」と感じた。


完成したv3.0でできること

最終的にできあがったツールを改めて整理するとこうなる。

PULL(kintone → スプレッドシート)

  • kintoneの全レコードをスプレッドシートに一括展開

  • レコード番号($id)をA列に自動付与

  • 初回のみ列幅を自動調整(2回目以降はユーザー設定を維持)

PUSH(スプレッドシート → kintone)

  • $idあり → 更新(PUT)を自動実行

  • $idなし → 新規登録(POST)を自動実行

  • 結果(登録済/更新済/エラー)を操作結果列に自動書き込み

安全性

  • APIトークンはスクリプトプロパティで管理(コード上に記載なし)

  • 10,000件超のレコードも$idシーク法で安全に取得

  • GAS6分タイムアウト対策(自動中断・再開機能)

使いやすさ

  • 初期設定ウィザード(ダイアログ入力3回でセットアップ完了)

  • フィールドコード一覧を自動取得・設定シートに展開

  • ✅を選ぶだけのFIELD_MAPPING設定(コード修正不要)

  • ボタン付き操作パネルシート(コードのメニューより直感的)

最初の「スプレッドシートから登録するだけ」という構想から比べると、できることが5倍以上になった。


kintoneユーザーだからこそ作れたツール

このツールを作り終えて改めて思うのは、「ユーザーとして使ってきた経験が全部活きた」ということだ。

offsetの上限問題は、本業でkintoneのデータが蓄積していく様子を見てきたから「将来必ず問題になる」と分かった。

APIトークンの管理は、本業でkintoneの管理者もやっているから、セキュリティリスクの重大さを肌で知っていた。

$idによる自動判定は、「ユーザーに余計な操作をさせたくない」という感覚が、kintoneを実際に使っているからこそ強くあった。

フィールドコードの一覧取得は、自分が毎回「面倒だな」と思っていたから出てきたアイデアだ。

外部の開発者がヒアリングで引き出せる要件は、お客様が言語化できているものだけだ。でも「なんとなく不便」「毎回ちょっとストレス」という感覚は、自分が使っていないと分からない。

副業でkintone開発をやるなら、本業でkintoneを使っているというのは、明確な強みになる。


次回予告

今回作ったツールはCoconalaで近日出品予定だ。

ただ、まだ終わっていない。

このツールの次のバージョンでは、スプレッドシート上で操作した内容を、リアルタイムに近い形でkintoneに反映させる仕組みを作ろうと考えている。

GASにはトリガー機能があって、「スプレッドシートが編集されたとき」「指定した時間になったとき」に自動でスクリプトを実行できる。これを使えば、スプレッドシートを編集するだけで自動的にkintoneが更新される、という体験が作れる。

「スプレッドシートを触っていたら、kintoneも自動で最新になっている」。

本業でずっと感じていた不満の、最終回答がそこにある気がしている。

次回もnoteに書くので、ぜひフォローしておいてほしい。

ではまた。

拓海(Takumi)



いいなと思ったら応援しよう!