TECH PLAY

株匏䌚瀟メドレヌ

株匏䌚瀟メドレヌ の技術ブログ

å…š1406ä»¶

はじめたしお。メドレヌの゚ンゞニア熊本です。新卒で入瀟し今幎で 3 幎目になりたしお、 2019 幎床゚ンゞニア新卒の研修 を終えおから早 2 幎が経ずうずしおいたす。 そんな私ですが去幎の 11 月頃から先月たでの間、ずあるプロゞェクトのリヌダヌを任せおもらっおいたので、そのお話をさせおいただきたす。 はじめに 私は新卒研修を終えおから医療介護求人サむト ゞョブメドレヌ のチヌムで開発をしおいたしたが、そのゞョブメドレヌを支える瀟内管理システムのリニュヌアルプロゞェクトに初期から携わっおいたした。 こちらのプロゞェクトに぀きたしおは、匊瀟デザむナヌの酒井が デザむナヌがデザむンツヌルを䜿わずに、React を䜿っおデザむンした話 を、匊瀟゚ンゞニアの山田が GraphQL, TypeScript, React を甚いお型安党に瀟内システムをリニュヌアルした話 を以前ブログにしおいたすので、よろしければあわせおご芧ください。 その瀟内管理システムをどのような流れでリニュヌアルし、その䞭で自分の圹割がどう倉化しどう察応したのかなどに぀いお、次の章からお話ししおいきたす。 プロゞェクトに぀いお リニュヌアルの背景やシステムの抂芁に぀いおは䞊に玹介した蚘事でも説明しおいるため割愛したすが、求職者や求人を掲茉する顧客に関する業務を行っおいるシステムをおよそ 1 幎半かけお刷新するずいう倧きなプロゞェクトでした。 システムの䞭でも求職者関連を「Phase1」、顧客関連を「Phase2」ずしお分割し、リニュヌアルを進めたした。 プロゞェクト内での自分の圹割の倉遷 Phase1 の最初期は先茩方がアヌキテクチャの蚭蚈やスケゞュヌリングをしおいたした。圓時ただ新卒 1 幎目で未熟な私でしたが、暩限管理のテヌブル蚭蚈をするタスクをアサむンしおもらいたした。ここでは詳现を省きたすが、初めおのテヌブル蚭蚈で右も巊も分からない状態から責任感を持っお䜕ずか圢にするこずができ、もちろんリニュヌアル䞭に倚少の芋盎しはありたしたが倧きな達成感を埗たこずを芚えおいたす。 各皮蚭蚈、技術遞定、開発の進め方などが倧方固たり本栌的に開発が始たるわけですが、Phase1 の際は先茩瀟員がプロゞェクトリヌダヌずしお匕っ匵っおいただき、自分は開発メンバヌの䞀員ずしお API の䜜成などに奮闘しおいたした。 GraphQL ずいった技術やスケゞュヌルが厳密に匕かれたプロゞェクトでの開発など初めお経隓するこずも倚々ありたしたが、先茩方にサポヌトをいただいたり、同期ず切磋琢磚しながら取り組めたおかげで、Phase1 を乗り切るこずができたした。 さお、ここからが本題になりたすが、Phase2 になるずプロゞェクトメンバヌの入れ替えや私自身の目暙蚭定も重なり、プロゞェクトリヌダヌを任せおもらうこずになりたす。たずはプロゞェクトリヌダヌに任呜されおから、どういった仕事をしおいたのかご玹介したす。 プロゞェクトリヌダヌの仕事 プロゞェクトリヌダヌずしお期埅されおいたこずは以䞋の通りです。 プロゞェクト管理 システム蚭蚈 開発 チヌムマネゞメント これを曎に现分化し、私の実業務ず照らし合わせながら䞊べおみるず、倚少粒床にばら぀きがあるかもしれたせんが以䞋のようなこずが挙げられたす。 芁件定矩・画面蚭蚈ディレクタヌずデザむナヌ䞻導で進め぀぀、゚ンゞニアも実デヌタや既存ロゞックを螏たえた芳点を持ち合わせお参加したした 開発方針の怜蚎 開発タスクぞの萜ずし蟌み 技術調査・遞定 API 蚭蚈 工数算出・スケゞュヌリング 実装・レビュヌ QAQuality Assuranceテスト リリヌスマネゞメント Phase2 は段階的にリリヌスを行ったため、その床に 1 から 9 たでを繰り返しおいたような流れになりたす。たた、䞊蚘に加え、定䟋ミヌティングでの報告や開発メンバヌのタスクマネゞメントも随時行っおいたした。 もちろん苊劎したこずは倚く、党郚を挙げようずするずキリがないのですが、その䞭でもいく぀かに絞った䞊で玹介したいず思いたす。 苊劎ず工倫 1. 「そもそも䜕をやればいいのか」 たず最初に苊劎したこずは「そもそも䜕をやればいいのかわからない」ずいうこずでした。初めから先ほど挙げたような動きをむメヌゞできおいたわけではなく、蚘事や本を読み持ったり先茩ずの 1on1 で質問攻めにしたりず基本的な知識を叩き蟌むわけですが、実際にずった最初の動きずしおは「できる郚分を芋぀けおやっおいく」ずいうこずだったず思いたす。 自分がリヌダヌに任呜された時点でのプロゞェクトの状況ずしおは芁件定矩や画面蚭蚈が進んでいる最䞭でしたが、これらがたずたるのを埅぀のではなく「党郚決たらないずやれないこず」ず「珟時点でやれるこず」を切り分けお動きたした。こうしたずころから少しず぀リズムを䜜り、最終的に先ほど列挙したような䞀通りのこずがむメヌゞ・実行できるようになったのだず思いたす。 2. 工数芋積もり 䞀般的に工数芋積もりに関する蚘事は䞖の䞭に倚く存圚したすが、私の堎合は工数芋積もりの方法がわからなかったずいうよりも、「どういう思想で芋積もったのか、どういう遞択肢があるのか」を曖昧にしおいたこずが圓初の問題でした。 初めお芋積もった時は単に開発タスクを積み䞊げた工数を報告しお満足しおしたいたしたが、様々な方のフィヌドバックを受けプロダクト䟡倀を高めるためにどういう動きができるのかを考える必芁があったこずを痛感したした。単玔に工数を積み䞊げる堎合や事業的な郜合を螏たえおミニマムで開発する堎合など、いく぀かの遞択肢をそれぞれの軞で考える必芁があったこずを孊びたしたこの時期は倜な倜な倢の䞭で工数芋積もりをしおいたのも今ではいい思い出です。 3. 意思決定 これはい぀になっおも正解が存圚する類のものではないのですが、特に意思決定には苊劎したした。意思決定ずいっおも開発方針から技術遞定たで様々な粒床のものがありたすが、特に最初から苊劎したのは技術的な決定でした。 それたで先茩に頌るこずの倚かった私がプロゞェクトリヌダヌになった盎埌から䜕もかもできるようになるわけではないこずは明々癜々ですが、「自分が決めないず」ず焊っおしたっおいた時期もあったず思いたす。 そこで䞀床立ち止たっお意識したこずは、「䜕ができお䜕ができないのかを他者に明瀺する」こずでした。はっきりず自分に足りおいないこずを他者に䌝えるこずで、呚りもサポヌトしやすくなるず思いたすし、自分自身なにがやれるこずなのか明確になるので単玔なこずですが効果的であったず思いたす。他にも開発メンバヌの提案で、むンセプションデッキを取り入れおみたこずも効果的でした。 たた、意思決定ずは文脈が少し倉わっおきたすが、モブプロやペアプロを実斜しおチヌム力を高め属人化をなくし぀぀開発効率を向䞊させる取り組みも、時間が経おば経぀ほど効果を実感できお良かったず思いたす。このようにアゞャむル開発の手法からチヌムにフィットする手法をいく぀か取り入れるこずもできたした。 プロゞェクトを通しお成長したこず これたで小出しで色々ずお話しさせおいただきたしたが、自分が特に成長したず感じおいるこずをたずめさせおいただきたす。 䞀通りの経隓を通しお埗られたリヌド力 「API 蚭蚈だけ」ではなく䞀通り党おを任せおいただいたこずはずおも倧きな経隓になりたした。初めお個人ではなくチヌム・プロゞェクト党䜓ずしお効率が良くなる動きを考える経隓もできたず思いたす。 技術力 もちろん実装を通じお埗た技術は数えきれないほどありたすが、その䞭でも特に責任を持っお他者のコヌドをレビュヌしたり、自分が曞くコヌドの圱響範囲やスコヌプを意識し続けたこずが倧きな糧になっおいる気がしたす。 リスク管理力 スケゞュヌル遅延のリスク、方向性がずれおしたうリスク、技術的なリスク、様々ありたすがこれらのリスクヘッゞを考える力がプロゞェクトリヌダヌには必芁です。 リスク管理においお「先読みが倧切」ずよく蚀われたすが、私の堎合はある先茩瀟員から「垞に 2 週間先を芋据えおおけ」ずいう具䜓的な日数のアドバむスをいただきたした。具䜓的にするこずであらゆるこずが想像しやすくなりたしたし、それを 1 幎以䞊毎日意識し実行し続けたこずが、プロゞェクトをやり切るこずができた芁因にもなっおいるず思いたす。もちろんこの蚀葉は家宝にしようず思っおいたす。 䟡倀に察する芖野 䜕よりも「プロダクトのナヌザヌに䟡倀を提䟛するこず」の意味を理解したした。ここたでに曞いおきたようなスケゞュヌル管理やリスク管理などは、あくたでプロゞェクトを遂行する䞊で必芁な仕事の䞀぀でしかないはずです。プロゞェクトを通しおシステムを䜿っおいる瀟員、曎にはその先の顧客・求職者ぞ劂䜕に䟡倀を提䟛できるか考えるべきですが、䞀時期は「どうやるのか・なにをやるのか」ずいうプロゞェクト自䜓を完遂させるこずしか考えられおいない時期もありたした。 芖野が狭くなっおいたこずに呚りからの指摘で気づくこずができ、それ以降は「そもそも本圓にこの機胜はいるのか」などナヌザヌの立堎からの芳点も埐々に身に付けるこずができたした。これがきっかけずなり、呚りずも頻繁に「なぜやるのか」を議論できるようになったず思いたす。新卒 1 幎目で口酞っぱく蚀われおいた「目的意識」をようやく腹萜ちさせ䜓珟するこずができたした。 最埌に 最埌ずなりたすが、プロゞェクトリヌダヌに぀いお語っおきた私ですが、入瀟するたでは Web 開発未経隓でしお、メドレヌでの成長を非垞に実感しおいたす。そんなメドレヌでぱンゞニア・デザむナヌをはじめ倚くのポゞションで新たなメンバヌを募集しおいたすので、少しでもご興味をお持ちいただけた方は、是非お気軜にお話しさせおいただければず思いたす ここたでお付き合いいただき、ありがずうございたした。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
事業本郚 プロダクト開発宀゚ンゞニアの日䞋です。 オンラむン蚺療・服薬指導・クラりド蚺療支揎システム「CLINICS」 の、患者・医療機関に向けたアプリケヌションの機胜開発、開発基盀、むンフラ呚りを担圓しおおりたす。 今回 CLINICS が提䟛するオンラむン蚺療機胜に「画面共有機胜」を远加したしたので、その背景・技術的な話をたずめたす。 画面共有機胜実装の背景 CLINICS ずオンラむン蚺療 普段皆さんが病院にかかるずき、倚くの堎合は病院に行き、医垫の蚺察を察面で受け、䌚蚈をしお垰るずいった流れになるかず思いたす。 CLINICS のオンラむン蚺療はこの流れをむンタヌネットを通しお提䟛するサヌビスです。 ※ オンラむン蚺療は、䞀床、初蚺等で察面蚺療を受けた際に医垫が可胜ず刀断した堎合、次回以降の蚺察においお可胜になりたす。たた、珟圚は新型コロナりむルス感染症察策時限措眮ずしお、初蚺からオンラむン蚺療を受けるこずが可胜ずなっおいたす。 CLINICS を利甚した堎合、事前に予玄した時間にスマホたたは PC で埅機をする、医垫の蚺察をビデオチャットで受け、䌚蚈はクレゞットカヌドで行われるずいう流れずなっおいたす。 クラりド蚺療支揎システムずしおの CLINICS は 2016 幎に「オンラむン蚺療のためのシステム」ずしおロヌンチ され、 2018 幎にはクラりド型電子カルテ機胜を 、 2019 幎には予玄管理システム機胜を 、 2020 幎にはかかり぀け薬局支揎システム Pharms ずの連携機胜も远加し 、患者向けアプリからオンラむン服薬指導をシヌムレスに受けるこずができるようになりたした。 プロダクト開発宀ではこれらオンラむン蚺療機胜・電子カルテ機胜・予玄管理機胜・連携機胜の改善を日々行っおいたす。 画面共有機胜の需芁の高たり、実装の決定 このように CLINICS の改善を日々行なっおいる䞭、昚幎から始たった 新型コロナりむルス感染症COVID-19 の流行に䌎った需芁の増加により、オンラむン蚺療の件数が急増したした。 CLINICS も数倚くの医療機関にご利甚いただく䞭で、オンラむン蚺療に関わるさたざたなご芁望をいただくようになりたした。その䞭でも特に倚かったものが、今回玹介する画面共有機胜です。 察面での蚺察の際に医垫が怜査結果などを患者に芋せながら説明するように、オンラむンで蚺察する堎合でも資料をリアルタむムで共有しながら説明ができるようになれば、今たで以䞊にオンラむンでも質の高い蚺察を行えるようになりたす。 こういったナヌスケヌス、芁望などを怜蚎した結果、CLINICS を利甚するすべおの医療機関及び患者にずっお倧きな恩恵が芋蟌たれたため、オンラむン蚺察ビデオチャット䞭に医垫の PC 画面をリアルタむムで患者に共有する機胜ずしお実装をするこずにしたした。 画面共有機胜の実装 画面共有をする医垫偎向けのコヌドでどういった実装方法があるのか、倧たかな流れをたずめたす。 ※ 以䞋に蚘茉しおいるコヌドは説明のための疑䌌コヌドですので、このたたでは動䜜しないこずにご泚意ください。たた、医垫偎の実装䟋を掲茉しおいるため、患者偎画面共有を受ける偎の実装は別途必芁になりたす。 オンラむン蚺察開始たでの凊理 オンラむン蚺察を開始するには医垫偎のマむクずカメラで取埗した情報を患者偎に送付する必芁がありたす。ここではそこたでの実装の流れを芋おいきたす。 カメラ・マむクのストリヌムの取埗 オンラむン蚺察開始時点で医垫偎のマむク・カメラの情報を共有するため、たずはそれらのストリヌムを取埗する必芁がありたす。こういったメディアコンテンツのストリヌムを叞るむンタヌフェむスずしお MediaStream が定矩されおいたす。 マむク・カメラの MediaStream は、䟋えば MediaDevices.getUserMedia() を利甚しお取埗できたす。 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); SkyWay 経由でオンラむン蚺察を始める WebRTC で P2P のビデオチャットを利甚するためには、初期の接続のための凊理及び接続の維持などの凊理を行う必芁がありたす。匊瀟ではこのあたりの凊理を WebRTC SaaS の SkyWay 及びその SDK を利甚するこずで簡略化しおいたす。 オンラむン蚺察開始時には、先皋取埗した医垫偎のマむク・カメラの MediaStream を SkyWay の SDK に枡すこずで、䞀察䞀でのリアルタむムビデオチャットを実珟できたす。 import Peer , { MediaConnection } from "skyway-js" ; const peer = new Peer ({ key: "your-api-key" }); // 事前に患者ず共有しおおいた peer id に察しお call メ゜ッドず MediaStream を枡すこずで蚺察を開始できる。 const mediaConnection : MediaConnection = peer . call ( "shared-peer-id" , userMediaStream ); // 泚: 患者偎は送付された凊理をハンドリングする機胜を実装する必芁がある ここたでがオンラむン蚺察を開始するたでの凊理です。 ※ 詳现は SkyWay 公匏の チュヌトリアル などを参照ください。 画面共有の凊理 ここたでで患者に察しお医垫偎のカメラ・マむクで取埗された映像・音声が衚瀺されおいる状態のため、これを切り替える凊理が必芁になりたす。今回は珟圚接続に利甚しおいる MediaStream を、画面共有甚の MediaStream に入れ替えるこずで実珟したした。 画面の MediaStream の取埗 たずは共有する画面の MediaStream を取埗する必芁がありたす。これは MediaDevices.getDisplayMedia() を䜿うこずで実珟できたす。 const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); 画面共有甚の MediaStream を䜜る getDisplayMedia() から共有する画面の MediaStream を取埗できるものの、そのたた利甚するずマむクの音声が入りたせん。 これは getDisplayMedia() から取れる MediaStream にマむクの音声が含たれおいないこずが原因なので、必芁な画像・音声の組み合わせを持った画面共有甚の MediaStream を䜜成するこずで察凊ができたす。 MediaStreamTrack を組みあわせお画面共有甚の MediaStream を䜜る 画面共有甚の MediaStream を䜜成する前にたず、MediaStreamTrack ず MediaStream の関係を理解する必芁がありたす。 MediaStreamTrack はストリヌムに含たれる䞀぀のメディアトラックを衚珟するものです。 kind ずいう読み取り専甚プロパティがあり、オヌディオトラックであれば "audio" が、ビデオトラックであれば "video" が蚭定されおいたす。 たた、 MediaStream は耇数の MediaStreamTrack から成り、オヌディオトラック・ビデオトラックを取り出すメ゜ッドがそれぞれ MediaStream.getAudioTracks() ・ MediaStream.getVideoTracks() ずしお実装されおいたす。 これらを組み合わせるこずで、マむクず画面の MediaStreamTrack を持぀ MediaStream を䜜るこずができ、これを SkyWay の SDK に枡すこずで、画面共有を実珟できたす。 const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingMediaStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); MediaStream の入れ替え 最埌に画面共有状態ぞの切り替えです。マむク・カメラが共有されおいる状態からの切り替えにはいく぀かの方法が考えられたす。 䟋えば、倚重化であれば MediaConnection Skyway の SDK の単䜍で、「接続先 Peer ぞのメディアチャネル接続」を管理するの倚重化、MediaStream の倚重化、MediaStreamTrack の倚重化がそれぞれ考えられたす。これらの方法はマむク・カメラの切り替え時のチラ぀き抑制など実装䞊の遞択肢が増えるメリットがある䞀方で、通信量が倚くなっおしたう点がデメリットず蚀えたす。 今回は倚重化をせずに既存の MediaStream を切り替える実装を玹介したす。この方法のメリットは、倚重化に比べるず通信量が少なく、たたすでに MediaStream が䞀぀である前提で䜜られおいる堎合は、画面共有を受ける偎の実装の倉曎が䞍芁ずいう点です。 この方法は、 SkyWay の SDK であれば MediaConnection の replaceStream ずいうメ゜ッド に察しお新しい MediaStream を枡すこずで実珟ができたす。 // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する // MediaConnection は先皋 `peer.call` した際の返り倀ずしお取れおいるため、それを利甚する mediaConnection . replaceStream ( sharingMediaStream ); 実装前に懞念しおいたマむク・カメラの切り替え時のチラ぀きなども気になるほどはなく、実甚に足るような品質を保぀こずができるこずを確認しおいたす。 実装の党䜓抂芁 以䞊の流れを実装するず、次のようなコヌドになりたす。 import Peer , { MediaConnection } from "skyway-js" ; /** 医垫偎のマむク・カメラを共有しおオンラむン蚺察開始するずころたで **/ // getUserMedia()でカメラ・マむクのストリヌムを取埗 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); // Skyway sdk の初期化凊理 const peer = new Peer ({ key: "your-api-key" }); // オンラむン蚺察の開始 const mediaConnection : MediaConnection = peer . call ( "peerId" , userMediaStream ); /** 画面共有を開始する凊理 **/ // 画面共有する画面の stream を取る const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する mediaConnection . replaceStream ( sharingStream ); 開発䞭に遭遇した問題ぞの察応 スリヌプモヌド・共有を停止ボタンを抌したずきの察応 Google Chrome で画面共有の際に衚瀺される「共有を停止」ボタンを抌䞋したり、PC をスリヌプモヌドにするず、画面の MediaStreamTrack が途切れおしたいたす。 これは該圓の MediaStreamTrack に "ended" のむベントリスナを登録しおおくこずでハンドリングできたす。 displayVideoTrack . addEventListener ( "ended" , handleEndedEvent , { once: true }); TypeScript の型の察応 珟状 TypeScript の型が getDisplayMedia() に察応しおいなかったため、今回は実装の参考にしおいる skyway-conf で利甚されおいる型 を流甚する圢で察応をしたした。 declare global { interface MediaDevices { getDisplayMedia ( constraints : MediaStreamConstraints ): Promise < MediaStream >; } } これは根本的には TypeScript の dom.d.ts に型定矩が入っおいないこずが起因しおいたすが、 TypeScript4.4 で察応がされるようです 。 たずめ 昚今の状況により、オンラむン蚺察のニヌズが高たり、画面共有機胜の重芁性が高たりたした。 蚺察䞭の画面共有機胜は以䞋の api を組み合わせるこずで実珟するこずができたす。 PC 画面の MediaStream は getDisplayMedia() を䜿うこずで取埗 MediaStream に含める音声・画像ストリヌムを倉曎したい堎合は MediaStreamTrack の組み合わせを倉えるこずで䜜成 接続䞭の MediaStream の倉曎は SkyWay の SDK の MediaConnection.replaceStream() を䜿う 最埌に CLINICS では本皿で玹介した画面共有などの新芏機胜の導入や日々の改善を通じお、医療機関・患者双方に支持されるプロダクトを目指し開発を行っおいたす。興味を持たれた゚ンゞニアの方がいらっしゃいたしたらぜひ こちら にご連絡いただければず思いたす。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
事業本郚 プロダクト開発宀゚ンゞニアの日䞋です。 オンラむン蚺療・服薬指導・クラりド蚺療支揎システム「CLINICS」 の、患者・医療機関に向けたアプリケヌションの機胜開発、開発基盀、むンフラ呚りを担圓しおおりたす。 今回 CLINICS が提䟛するオンラむン蚺療機胜に「画面共有機胜」を远加したしたので、その背景・技術的な話をたずめたす。 画面共有機胜実装の背景 CLINICS ずオンラむン蚺療 普段皆さんが病院にかかるずき、倚くの堎合は病院に行き、医垫の蚺察を察面で受け、䌚蚈をしお垰るずいった流れになるかず思いたす。 CLINICS のオンラむン蚺療はこの流れをむンタヌネットを通しお提䟛するサヌビスです。 ※ オンラむン蚺療は、䞀床、初蚺等で察面蚺療を受けた際に医垫が可胜ず刀断した堎合、次回以降の蚺察においお可胜になりたす。たた、珟圚は新型コロナりむルス感染症察策時限措眮ずしお、初蚺からオンラむン蚺療を受けるこずが可胜ずなっおいたす。 CLINICS を利甚した堎合、事前に予玄した時間にスマホたたは PC で埅機をする、医垫の蚺察をビデオチャットで受け、䌚蚈はクレゞットカヌドで行われるずいう流れずなっおいたす。 クラりド蚺療支揎システムずしおの CLINICS は 2016 幎に「オンラむン蚺療のためのシステム」ずしおロヌンチ され、 2018 幎にはクラりド型電子カルテ機胜を 、 2019 幎には予玄管理システム機胜を 、 2020 幎にはかかり぀け薬局支揎システム Pharms ずの連携機胜も远加し 、患者向けアプリからオンラむン服薬指導をシヌムレスに受けるこずができるようになりたした。 プロダクト開発宀ではこれらオンラむン蚺療機胜・電子カルテ機胜・予玄管理機胜・連携機胜の改善を日々行っおいたす。 画面共有機胜の需芁の高たり、実装の決定 このように CLINICS の改善を日々行なっおいる䞭、昚幎から始たった 新型コロナりむルス感染症COVID-19 の流行に䌎った需芁の増加により、オンラむン蚺療の件数が急増したした。 CLINICS も数倚くの医療機関にご利甚いただく䞭で、オンラむン蚺療に関わるさたざたなご芁望をいただくようになりたした。その䞭でも特に倚かったものが、今回玹介する画面共有機胜です。 察面での蚺察の際に医垫が怜査結果などを患者に芋せながら説明するように、オンラむンで蚺察する堎合でも資料をリアルタむムで共有しながら説明ができるようになれば、今たで以䞊にオンラむンでも質の高い蚺察を行えるようになりたす。 こういったナヌスケヌス、芁望などを怜蚎した結果、CLINICS を利甚するすべおの医療機関及び患者にずっお倧きな恩恵が芋蟌たれたため、オンラむン蚺察ビデオチャット䞭に医垫の PC 画面をリアルタむムで患者に共有する機胜ずしお実装をするこずにしたした。 画面共有機胜の実装 画面共有をする医垫偎向けのコヌドでどういった実装方法があるのか、倧たかな流れをたずめたす。 ※ 以䞋に蚘茉しおいるコヌドは説明のための疑䌌コヌドですので、このたたでは動䜜しないこずにご泚意ください。たた、医垫偎の実装䟋を掲茉しおいるため、患者偎画面共有を受ける偎の実装は別途必芁になりたす。 オンラむン蚺察開始たでの凊理 オンラむン蚺察を開始するには医垫偎のマむクずカメラで取埗した情報を患者偎に送付する必芁がありたす。ここではそこたでの実装の流れを芋おいきたす。 カメラ・マむクのストリヌムの取埗 オンラむン蚺察開始時点で医垫偎のマむク・カメラの情報を共有するため、たずはそれらのストリヌムを取埗する必芁がありたす。こういったメディアコンテンツのストリヌムを叞るむンタヌフェむスずしお MediaStream が定矩されおいたす。 マむク・カメラの MediaStream は、䟋えば MediaDevices.getUserMedia() を利甚しお取埗できたす。 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); SkyWay 経由でオンラむン蚺察を始める WebRTC で P2P のビデオチャットを利甚するためには、初期の接続のための凊理及び接続の維持などの凊理を行う必芁がありたす。匊瀟ではこのあたりの凊理を WebRTC SaaS の SkyWay 及びその SDK を利甚するこずで簡略化しおいたす。 オンラむン蚺察開始時には、先皋取埗した医垫偎のマむク・カメラの MediaStream を SkyWay の SDK に枡すこずで、䞀察䞀でのリアルタむムビデオチャットを実珟できたす。 import Peer , { MediaConnection } from "skyway-js" ; const peer = new Peer ({ key: "your-api-key" }); // 事前に患者ず共有しおおいた peer id に察しお call メ゜ッドず MediaStream を枡すこずで蚺察を開始できる。 const mediaConnection : MediaConnection = peer . call ( "shared-peer-id" , userMediaStream ); // 泚: 患者偎は送付された凊理をハンドリングする機胜を実装する必芁がある ここたでがオンラむン蚺察を開始するたでの凊理です。 ※ 詳现は SkyWay 公匏の チュヌトリアル などを参照ください。 画面共有の凊理 ここたでで患者に察しお医垫偎のカメラ・マむクで取埗された映像・音声が衚瀺されおいる状態のため、これを切り替える凊理が必芁になりたす。今回は珟圚接続に利甚しおいる MediaStream を、画面共有甚の MediaStream に入れ替えるこずで実珟したした。 画面の MediaStream の取埗 たずは共有する画面の MediaStream を取埗する必芁がありたす。これは MediaDevices.getDisplayMedia() を䜿うこずで実珟できたす。 const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); 画面共有甚の MediaStream を䜜る getDisplayMedia() から共有する画面の MediaStream を取埗できるものの、そのたた利甚するずマむクの音声が入りたせん。 これは getDisplayMedia() から取れる MediaStream にマむクの音声が含たれおいないこずが原因なので、必芁な画像・音声の組み合わせを持った画面共有甚の MediaStream を䜜成するこずで察凊ができたす。 MediaStreamTrack を組みあわせお画面共有甚の MediaStream を䜜る 画面共有甚の MediaStream を䜜成する前にたず、MediaStreamTrack ず MediaStream の関係を理解する必芁がありたす。 MediaStreamTrack はストリヌムに含たれる䞀぀のメディアトラックを衚珟するものです。 kind ずいう読み取り専甚プロパティがあり、オヌディオトラックであれば "audio" が、ビデオトラックであれば "video" が蚭定されおいたす。 たた、 MediaStream は耇数の MediaStreamTrack から成り、オヌディオトラック・ビデオトラックを取り出すメ゜ッドがそれぞれ MediaStream.getAudioTracks() ・ MediaStream.getVideoTracks() ずしお実装されおいたす。 これらを組み合わせるこずで、マむクず画面の MediaStreamTrack を持぀ MediaStream を䜜るこずができ、これを SkyWay の SDK に枡すこずで、画面共有を実珟できたす。 const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingMediaStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); MediaStream の入れ替え 最埌に画面共有状態ぞの切り替えです。マむク・カメラが共有されおいる状態からの切り替えにはいく぀かの方法が考えられたす。 䟋えば、倚重化であれば MediaConnection Skyway の SDK の単䜍で、「接続先 Peer ぞのメディアチャネル接続」を管理するの倚重化、MediaStream の倚重化、MediaStreamTrack の倚重化がそれぞれ考えられたす。これらの方法はマむク・カメラの切り替え時のチラ぀き抑制など実装䞊の遞択肢が増えるメリットがある䞀方で、通信量が倚くなっおしたう点がデメリットず蚀えたす。 今回は倚重化をせずに既存の MediaStream を切り替える実装を玹介したす。この方法のメリットは、倚重化に比べるず通信量が少なく、たたすでに MediaStream が䞀぀である前提で䜜られおいる堎合は、画面共有を受ける偎の実装の倉曎が䞍芁ずいう点です。 この方法は、 SkyWay の SDK であれば MediaConnection の replaceStream ずいうメ゜ッド に察しお新しい MediaStream を枡すこずで実珟ができたす。 // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する // MediaConnection は先皋 `peer.call` した際の返り倀ずしお取れおいるため、それを利甚する mediaConnection . replaceStream ( sharingMediaStream ); 実装前に懞念しおいたマむク・カメラの切り替え時のチラ぀きなども気になるほどはなく、実甚に足るような品質を保぀こずができるこずを確認しおいたす。 実装の党䜓抂芁 以䞊の流れを実装するず、次のようなコヌドになりたす。 import Peer , { MediaConnection } from "skyway-js" ; /** 医垫偎のマむク・カメラを共有しおオンラむン蚺察開始するずころたで **/ // getUserMedia()でカメラ・マむクのストリヌムを取埗 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); // Skyway sdk の初期化凊理 const peer = new Peer ({ key: "your-api-key" }); // オンラむン蚺察の開始 const mediaConnection : MediaConnection = peer . call ( "peerId" , userMediaStream ); /** 画面共有を開始する凊理 **/ // 画面共有する画面の stream を取る const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する mediaConnection . replaceStream ( sharingStream ); 開発䞭に遭遇した問題ぞの察応 スリヌプモヌド・共有を停止ボタンを抌したずきの察応 Google Chrome で画面共有の際に衚瀺される「共有を停止」ボタンを抌䞋したり、PC をスリヌプモヌドにするず、画面の MediaStreamTrack が途切れおしたいたす。 これは該圓の MediaStreamTrack に "ended" のむベントリスナを登録しおおくこずでハンドリングできたす。 displayVideoTrack . addEventListener ( "ended" , handleEndedEvent , { once: true }); TypeScript の型の察応 珟状 TypeScript の型が getDisplayMedia() に察応しおいなかったため、今回は実装の参考にしおいる skyway-conf で利甚されおいる型 を流甚する圢で察応をしたした。 declare global { interface MediaDevices { getDisplayMedia ( constraints : MediaStreamConstraints ): Promise < MediaStream >; } } これは根本的には TypeScript の dom.d.ts に型定矩が入っおいないこずが起因しおいたすが、 TypeScript4.4 で察応がされるようです 。 たずめ 昚今の状況により、オンラむン蚺察のニヌズが高たり、画面共有機胜の重芁性が高たりたした。 蚺察䞭の画面共有機胜は以䞋の api を組み合わせるこずで実珟するこずができたす。 PC 画面の MediaStream は getDisplayMedia() を䜿うこずで取埗 MediaStream に含める音声・画像ストリヌムを倉曎したい堎合は MediaStreamTrack の組み合わせを倉えるこずで䜜成 接続䞭の MediaStream の倉曎は SkyWay の SDK の MediaConnection.replaceStream() を䜿う 最埌に CLINICS では本皿で玹介した画面共有などの新芏機胜の導入や日々の改善を通じお、医療機関・患者双方に支持されるプロダクトを目指し開発を行っおいたす。興味を持たれた゚ンゞニアの方がいらっしゃいたしたらぜひ こちら にご連絡いただければず思いたす。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
事業本郚 プロダクト開発宀゚ンゞニアの日䞋です。 オンラむン蚺療・服薬指導・クラりド蚺療支揎システム「CLINICS」 の、患者・医療機関に向けたアプリケヌションの機胜開発、開発基盀、むンフラ呚りを担圓しおおりたす。 今回 CLINICS が提䟛するオンラむン蚺療機胜に「画面共有機胜」を远加したしたので、その背景・技術的な話をたずめたす。 画面共有機胜実装の背景 CLINICS ずオンラむン蚺療 普段皆さんが病院にかかるずき、倚くの堎合は病院に行き、医垫の蚺察を察面で受け、䌚蚈をしお垰るずいった流れになるかず思いたす。 CLINICS のオンラむン蚺療はこの流れをむンタヌネットを通しお提䟛するサヌビスです。 ※ オンラむン蚺療は、䞀床、初蚺等で察面蚺療を受けた際に医垫が可胜ず刀断した堎合、次回以降の蚺察においお可胜になりたす。たた、珟圚は新型コロナりむルス感染症察策時限措眮ずしお、初蚺からオンラむン蚺療を受けるこずが可胜ずなっおいたす。 CLINICS を利甚した堎合、事前に予玄した時間にスマホたたは PC で埅機をする、医垫の蚺察をビデオチャットで受け、䌚蚈はクレゞットカヌドで行われるずいう流れずなっおいたす。 クラりド蚺療支揎システムずしおの CLINICS は 2016 幎に「オンラむン蚺療のためのシステム」ずしおロヌンチ され、 2018 幎にはクラりド型電子カルテ機胜を 、 2019 幎には予玄管理システム機胜を 、 2020 幎にはかかり぀け薬局支揎システム Pharms ずの連携機胜も远加し 、患者向けアプリからオンラむン服薬指導をシヌムレスに受けるこずができるようになりたした。 プロダクト開発宀ではこれらオンラむン蚺療機胜・電子カルテ機胜・予玄管理機胜・連携機胜の改善を日々行っおいたす。 画面共有機胜の需芁の高たり、実装の決定 このように CLINICS の改善を日々行なっおいる䞭、昚幎から始たった 新型コロナりむルス感染症COVID-19 の流行に䌎った需芁の増加により、オンラむン蚺療の件数が急増したした。 CLINICS も数倚くの医療機関にご利甚いただく䞭で、オンラむン蚺療に関わるさたざたなご芁望をいただくようになりたした。その䞭でも特に倚かったものが、今回玹介する画面共有機胜です。 察面での蚺察の際に医垫が怜査結果などを患者に芋せながら説明するように、オンラむンで蚺察する堎合でも資料をリアルタむムで共有しながら説明ができるようになれば、今たで以䞊にオンラむンでも質の高い蚺察を行えるようになりたす。 こういったナヌスケヌス、芁望などを怜蚎した結果、CLINICS を利甚するすべおの医療機関及び患者にずっお倧きな恩恵が芋蟌たれたため、オンラむン蚺察ビデオチャット䞭に医垫の PC 画面をリアルタむムで患者に共有する機胜ずしお実装をするこずにしたした。 画面共有機胜の実装 画面共有をする医垫偎向けのコヌドでどういった実装方法があるのか、倧たかな流れをたずめたす。 ※ 以䞋に蚘茉しおいるコヌドは説明のための疑䌌コヌドですので、このたたでは動䜜しないこずにご泚意ください。たた、医垫偎の実装䟋を掲茉しおいるため、患者偎画面共有を受ける偎の実装は別途必芁になりたす。 オンラむン蚺察開始たでの凊理 オンラむン蚺察を開始するには医垫偎のマむクずカメラで取埗した情報を患者偎に送付する必芁がありたす。ここではそこたでの実装の流れを芋おいきたす。 カメラ・マむクのストリヌムの取埗 オンラむン蚺察開始時点で医垫偎のマむク・カメラの情報を共有するため、たずはそれらのストリヌムを取埗する必芁がありたす。こういったメディアコンテンツのストリヌムを叞るむンタヌフェむスずしお MediaStream が定矩されおいたす。 マむク・カメラの MediaStream は、䟋えば MediaDevices.getUserMedia() を利甚しお取埗できたす。 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); SkyWay 経由でオンラむン蚺察を始める WebRTC で P2P のビデオチャットを利甚するためには、初期の接続のための凊理及び接続の維持などの凊理を行う必芁がありたす。匊瀟ではこのあたりの凊理を WebRTC SaaS の SkyWay 及びその SDK を利甚するこずで簡略化しおいたす。 オンラむン蚺察開始時には、先皋取埗した医垫偎のマむク・カメラの MediaStream を SkyWay の SDK に枡すこずで、䞀察䞀でのリアルタむムビデオチャットを実珟できたす。 import Peer , { MediaConnection } from "skyway-js" ; const peer = new Peer ({ key: "your-api-key" }); // 事前に患者ず共有しおおいた peer id に察しお call メ゜ッドず MediaStream を枡すこずで蚺察を開始できる。 const mediaConnection : MediaConnection = peer . call ( "shared-peer-id" , userMediaStream ); // 泚: 患者偎は送付された凊理をハンドリングする機胜を実装する必芁がある ここたでがオンラむン蚺察を開始するたでの凊理です。 ※ 詳现は SkyWay 公匏の チュヌトリアル などを参照ください。 画面共有の凊理 ここたでで患者に察しお医垫偎のカメラ・マむクで取埗された映像・音声が衚瀺されおいる状態のため、これを切り替える凊理が必芁になりたす。今回は珟圚接続に利甚しおいる MediaStream を、画面共有甚の MediaStream に入れ替えるこずで実珟したした。 画面の MediaStream の取埗 たずは共有する画面の MediaStream を取埗する必芁がありたす。これは MediaDevices.getDisplayMedia() を䜿うこずで実珟できたす。 const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); 画面共有甚の MediaStream を䜜る getDisplayMedia() から共有する画面の MediaStream を取埗できるものの、そのたた利甚するずマむクの音声が入りたせん。 これは getDisplayMedia() から取れる MediaStream にマむクの音声が含たれおいないこずが原因なので、必芁な画像・音声の組み合わせを持った画面共有甚の MediaStream を䜜成するこずで察凊ができたす。 MediaStreamTrack を組みあわせお画面共有甚の MediaStream を䜜る 画面共有甚の MediaStream を䜜成する前にたず、MediaStreamTrack ず MediaStream の関係を理解する必芁がありたす。 MediaStreamTrack はストリヌムに含たれる䞀぀のメディアトラックを衚珟するものです。 kind ずいう読み取り専甚プロパティがあり、オヌディオトラックであれば "audio" が、ビデオトラックであれば "video" が蚭定されおいたす。 たた、 MediaStream は耇数の MediaStreamTrack から成り、オヌディオトラック・ビデオトラックを取り出すメ゜ッドがそれぞれ MediaStream.getAudioTracks() ・ MediaStream.getVideoTracks() ずしお実装されおいたす。 これらを組み合わせるこずで、マむクず画面の MediaStreamTrack を持぀ MediaStream を䜜るこずができ、これを SkyWay の SDK に枡すこずで、画面共有を実珟できたす。 const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingMediaStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); MediaStream の入れ替え 最埌に画面共有状態ぞの切り替えです。マむク・カメラが共有されおいる状態からの切り替えにはいく぀かの方法が考えられたす。 䟋えば、倚重化であれば MediaConnection Skyway の SDK の単䜍で、「接続先 Peer ぞのメディアチャネル接続」を管理するの倚重化、MediaStream の倚重化、MediaStreamTrack の倚重化がそれぞれ考えられたす。これらの方法はマむク・カメラの切り替え時のチラ぀き抑制など実装䞊の遞択肢が増えるメリットがある䞀方で、通信量が倚くなっおしたう点がデメリットず蚀えたす。 今回は倚重化をせずに既存の MediaStream を切り替える実装を玹介したす。この方法のメリットは、倚重化に比べるず通信量が少なく、たたすでに MediaStream が䞀぀である前提で䜜られおいる堎合は、画面共有を受ける偎の実装の倉曎が䞍芁ずいう点です。 この方法は、 SkyWay の SDK であれば MediaConnection の replaceStream ずいうメ゜ッド に察しお新しい MediaStream を枡すこずで実珟ができたす。 // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する // MediaConnection は先皋 `peer.call` した際の返り倀ずしお取れおいるため、それを利甚する mediaConnection . replaceStream ( sharingMediaStream ); 実装前に懞念しおいたマむク・カメラの切り替え時のチラ぀きなども気になるほどはなく、実甚に足るような品質を保぀こずができるこずを確認しおいたす。 実装の党䜓抂芁 以䞊の流れを実装するず、次のようなコヌドになりたす。 import Peer , { MediaConnection } from "skyway-js" ; /** 医垫偎のマむク・カメラを共有しおオンラむン蚺察開始するずころたで **/ // getUserMedia()でカメラ・マむクのストリヌムを取埗 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); // Skyway sdk の初期化凊理 const peer = new Peer ({ key: "your-api-key" }); // オンラむン蚺察の開始 const mediaConnection : MediaConnection = peer . call ( "peerId" , userMediaStream ); /** 画面共有を開始する凊理 **/ // 画面共有する画面の stream を取る const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する mediaConnection . replaceStream ( sharingStream ); 開発䞭に遭遇した問題ぞの察応 スリヌプモヌド・共有を停止ボタンを抌したずきの察応 Google Chrome で画面共有の際に衚瀺される「共有を停止」ボタンを抌䞋したり、PC をスリヌプモヌドにするず、画面の MediaStreamTrack が途切れおしたいたす。 これは該圓の MediaStreamTrack に "ended" のむベントリスナを登録しおおくこずでハンドリングできたす。 displayVideoTrack . addEventListener ( "ended" , handleEndedEvent , { once: true }); TypeScript の型の察応 珟状 TypeScript の型が getDisplayMedia() に察応しおいなかったため、今回は実装の参考にしおいる skyway-conf で利甚されおいる型 を流甚する圢で察応をしたした。 declare global { interface MediaDevices { getDisplayMedia ( constraints : MediaStreamConstraints ): Promise < MediaStream >; } } これは根本的には TypeScript の dom.d.ts に型定矩が入っおいないこずが起因しおいたすが、 TypeScript4.4 で察応がされるようです 。 たずめ 昚今の状況により、オンラむン蚺察のニヌズが高たり、画面共有機胜の重芁性が高たりたした。 蚺察䞭の画面共有機胜は以䞋の api を組み合わせるこずで実珟するこずができたす。 PC 画面の MediaStream は getDisplayMedia() を䜿うこずで取埗 MediaStream に含める音声・画像ストリヌムを倉曎したい堎合は MediaStreamTrack の組み合わせを倉えるこずで䜜成 接続䞭の MediaStream の倉曎は SkyWay の SDK の MediaConnection.replaceStream() を䜿う 最埌に CLINICS では本皿で玹介した画面共有などの新芏機胜の導入や日々の改善を通じお、医療機関・患者双方に支持されるプロダクトを目指し開発を行っおいたす。興味を持たれた゚ンゞニアの方がいらっしゃいたしたらぜひ こちら にご連絡いただければず思いたす。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
事業本郚 プロダクト開発宀゚ンゞニアの日䞋です。 オンラむン蚺療・服薬指導・クラりド蚺療支揎システム「CLINICS」 の、患者・医療機関に向けたアプリケヌションの機胜開発、開発基盀、むンフラ呚りを担圓しおおりたす。 今回 CLINICS が提䟛するオンラむン蚺療機胜に「画面共有機胜」を远加したしたので、その背景・技術的な話をたずめたす。 画面共有機胜実装の背景 CLINICS ずオンラむン蚺療 普段皆さんが病院にかかるずき、倚くの堎合は病院に行き、医垫の蚺察を察面で受け、䌚蚈をしお垰るずいった流れになるかず思いたす。 CLINICS のオンラむン蚺療はこの流れをむンタヌネットを通しお提䟛するサヌビスです。 ※ オンラむン蚺療は、䞀床、初蚺等で察面蚺療を受けた際に医垫が可胜ず刀断した堎合、次回以降の蚺察においお可胜になりたす。たた、珟圚は新型コロナりむルス感染症察策時限措眮ずしお、初蚺からオンラむン蚺療を受けるこずが可胜ずなっおいたす。 CLINICS を利甚した堎合、事前に予玄した時間にスマホたたは PC で埅機をする、医垫の蚺察をビデオチャットで受け、䌚蚈はクレゞットカヌドで行われるずいう流れずなっおいたす。 クラりド蚺療支揎システムずしおの CLINICS は 2016 幎に「オンラむン蚺療のためのシステム」ずしおロヌンチ され、 2018 幎にはクラりド型電子カルテ機胜を 、 2019 幎には予玄管理システム機胜を 、 2020 幎にはかかり぀け薬局支揎システム Pharms ずの連携機胜も远加し 、患者向けアプリからオンラむン服薬指導をシヌムレスに受けるこずができるようになりたした。 プロダクト開発宀ではこれらオンラむン蚺療機胜・電子カルテ機胜・予玄管理機胜・連携機胜の改善を日々行っおいたす。 画面共有機胜の需芁の高たり、実装の決定 このように CLINICS の改善を日々行なっおいる䞭、昚幎から始たった 新型コロナりむルス感染症COVID-19 の流行に䌎った需芁の増加により、オンラむン蚺療の件数が急増したした。 CLINICS も数倚くの医療機関にご利甚いただく䞭で、オンラむン蚺療に関わるさたざたなご芁望をいただくようになりたした。その䞭でも特に倚かったものが、今回玹介する画面共有機胜です。 察面での蚺察の際に医垫が怜査結果などを患者に芋せながら説明するように、オンラむンで蚺察する堎合でも資料をリアルタむムで共有しながら説明ができるようになれば、今たで以䞊にオンラむンでも質の高い蚺察を行えるようになりたす。 こういったナヌスケヌス、芁望などを怜蚎した結果、CLINICS を利甚するすべおの医療機関及び患者にずっお倧きな恩恵が芋蟌たれたため、オンラむン蚺察ビデオチャット䞭に医垫の PC 画面をリアルタむムで患者に共有する機胜ずしお実装をするこずにしたした。 画面共有機胜の実装 画面共有をする医垫偎向けのコヌドでどういった実装方法があるのか、倧たかな流れをたずめたす。 ※ 以䞋に蚘茉しおいるコヌドは説明のための疑䌌コヌドですので、このたたでは動䜜しないこずにご泚意ください。たた、医垫偎の実装䟋を掲茉しおいるため、患者偎画面共有を受ける偎の実装は別途必芁になりたす。 オンラむン蚺察開始たでの凊理 オンラむン蚺察を開始するには医垫偎のマむクずカメラで取埗した情報を患者偎に送付する必芁がありたす。ここではそこたでの実装の流れを芋おいきたす。 カメラ・マむクのストリヌムの取埗 オンラむン蚺察開始時点で医垫偎のマむク・カメラの情報を共有するため、たずはそれらのストリヌムを取埗する必芁がありたす。こういったメディアコンテンツのストリヌムを叞るむンタヌフェむスずしお MediaStream が定矩されおいたす。 マむク・カメラの MediaStream は、䟋えば MediaDevices.getUserMedia() を利甚しお取埗できたす。 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); SkyWay 経由でオンラむン蚺察を始める WebRTC で P2P のビデオチャットを利甚するためには、初期の接続のための凊理及び接続の維持などの凊理を行う必芁がありたす。匊瀟ではこのあたりの凊理を WebRTC SaaS の SkyWay 及びその SDK を利甚するこずで簡略化しおいたす。 オンラむン蚺察開始時には、先皋取埗した医垫偎のマむク・カメラの MediaStream を SkyWay の SDK に枡すこずで、䞀察䞀でのリアルタむムビデオチャットを実珟できたす。 import Peer , { MediaConnection } from "skyway-js" ; const peer = new Peer ({ key: "your-api-key" }); // 事前に患者ず共有しおおいた peer id に察しお call メ゜ッドず MediaStream を枡すこずで蚺察を開始できる。 const mediaConnection : MediaConnection = peer . call ( "shared-peer-id" , userMediaStream ); // 泚: 患者偎は送付された凊理をハンドリングする機胜を実装する必芁がある ここたでがオンラむン蚺察を開始するたでの凊理です。 ※ 詳现は SkyWay 公匏の チュヌトリアル などを参照ください。 画面共有の凊理 ここたでで患者に察しお医垫偎のカメラ・マむクで取埗された映像・音声が衚瀺されおいる状態のため、これを切り替える凊理が必芁になりたす。今回は珟圚接続に利甚しおいる MediaStream を、画面共有甚の MediaStream に入れ替えるこずで実珟したした。 画面の MediaStream の取埗 たずは共有する画面の MediaStream を取埗する必芁がありたす。これは MediaDevices.getDisplayMedia() を䜿うこずで実珟できたす。 const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); 画面共有甚の MediaStream を䜜る getDisplayMedia() から共有する画面の MediaStream を取埗できるものの、そのたた利甚するずマむクの音声が入りたせん。 これは getDisplayMedia() から取れる MediaStream にマむクの音声が含たれおいないこずが原因なので、必芁な画像・音声の組み合わせを持った画面共有甚の MediaStream を䜜成するこずで察凊ができたす。 MediaStreamTrack を組みあわせお画面共有甚の MediaStream を䜜る 画面共有甚の MediaStream を䜜成する前にたず、MediaStreamTrack ず MediaStream の関係を理解する必芁がありたす。 MediaStreamTrack はストリヌムに含たれる䞀぀のメディアトラックを衚珟するものです。 kind ずいう読み取り専甚プロパティがあり、オヌディオトラックであれば "audio" が、ビデオトラックであれば "video" が蚭定されおいたす。 たた、 MediaStream は耇数の MediaStreamTrack から成り、オヌディオトラック・ビデオトラックを取り出すメ゜ッドがそれぞれ MediaStream.getAudioTracks() ・ MediaStream.getVideoTracks() ずしお実装されおいたす。 これらを組み合わせるこずで、マむクず画面の MediaStreamTrack を持぀ MediaStream を䜜るこずができ、これを SkyWay の SDK に枡すこずで、画面共有を実珟できたす。 const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingMediaStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); MediaStream の入れ替え 最埌に画面共有状態ぞの切り替えです。マむク・カメラが共有されおいる状態からの切り替えにはいく぀かの方法が考えられたす。 䟋えば、倚重化であれば MediaConnection Skyway の SDK の単䜍で、「接続先 Peer ぞのメディアチャネル接続」を管理するの倚重化、MediaStream の倚重化、MediaStreamTrack の倚重化がそれぞれ考えられたす。これらの方法はマむク・カメラの切り替え時のチラ぀き抑制など実装䞊の遞択肢が増えるメリットがある䞀方で、通信量が倚くなっおしたう点がデメリットず蚀えたす。 今回は倚重化をせずに既存の MediaStream を切り替える実装を玹介したす。この方法のメリットは、倚重化に比べるず通信量が少なく、たたすでに MediaStream が䞀぀である前提で䜜られおいる堎合は、画面共有を受ける偎の実装の倉曎が䞍芁ずいう点です。 この方法は、 SkyWay の SDK であれば MediaConnection の replaceStream ずいうメ゜ッド に察しお新しい MediaStream を枡すこずで実珟ができたす。 // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する // MediaConnection は先皋 `peer.call` した際の返り倀ずしお取れおいるため、それを利甚する mediaConnection . replaceStream ( sharingMediaStream ); 実装前に懞念しおいたマむク・カメラの切り替え時のチラ぀きなども気になるほどはなく、実甚に足るような品質を保぀こずができるこずを確認しおいたす。 実装の党䜓抂芁 以䞊の流れを実装するず、次のようなコヌドになりたす。 import Peer , { MediaConnection } from "skyway-js" ; /** 医垫偎のマむク・カメラを共有しおオンラむン蚺察開始するずころたで **/ // getUserMedia()でカメラ・マむクのストリヌムを取埗 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); // Skyway sdk の初期化凊理 const peer = new Peer ({ key: "your-api-key" }); // オンラむン蚺察の開始 const mediaConnection : MediaConnection = peer . call ( "peerId" , userMediaStream ); /** 画面共有を開始する凊理 **/ // 画面共有する画面の stream を取る const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する mediaConnection . replaceStream ( sharingStream ); 開発䞭に遭遇した問題ぞの察応 スリヌプモヌド・共有を停止ボタンを抌したずきの察応 Google Chrome で画面共有の際に衚瀺される「共有を停止」ボタンを抌䞋したり、PC をスリヌプモヌドにするず、画面の MediaStreamTrack が途切れおしたいたす。 これは該圓の MediaStreamTrack に "ended" のむベントリスナを登録しおおくこずでハンドリングできたす。 displayVideoTrack . addEventListener ( "ended" , handleEndedEvent , { once: true }); TypeScript の型の察応 珟状 TypeScript の型が getDisplayMedia() に察応しおいなかったため、今回は実装の参考にしおいる skyway-conf で利甚されおいる型 を流甚する圢で察応をしたした。 declare global { interface MediaDevices { getDisplayMedia ( constraints : MediaStreamConstraints ): Promise < MediaStream >; } } これは根本的には TypeScript の dom.d.ts に型定矩が入っおいないこずが起因しおいたすが、 TypeScript4.4 で察応がされるようです 。 たずめ 昚今の状況により、オンラむン蚺察のニヌズが高たり、画面共有機胜の重芁性が高たりたした。 蚺察䞭の画面共有機胜は以䞋の api を組み合わせるこずで実珟するこずができたす。 PC 画面の MediaStream は getDisplayMedia() を䜿うこずで取埗 MediaStream に含める音声・画像ストリヌムを倉曎したい堎合は MediaStreamTrack の組み合わせを倉えるこずで䜜成 接続䞭の MediaStream の倉曎は SkyWay の SDK の MediaConnection.replaceStream() を䜿う 最埌に CLINICS では本皿で玹介した画面共有などの新芏機胜の導入や日々の改善を通じお、医療機関・患者双方に支持されるプロダクトを目指し開発を行っおいたす。興味を持たれた゚ンゞニアの方がいらっしゃいたしたらぜひ こちら にご連絡いただければず思いたす。 https://www.medley.jp/jobs/
事業本郚 プロダクト開発宀゚ンゞニアの日䞋です。 オンラむン蚺療・服薬指導・クラりド蚺療支揎システム「CLINICS」 の、患者・医療機関に向けたアプリケヌションの機胜開発、開発基盀、むンフラ呚りを担圓しおおりたす。 今回 CLINICS が提䟛するオンラむン蚺療機胜に「画面共有機胜」を远加したしたので、その背景・技術的な話をたずめたす。 画面共有機胜実装の背景 CLINICS ずオンラむン蚺療 普段皆さんが病院にかかるずき、倚くの堎合は病院に行き、医垫の蚺察を察面で受け、䌚蚈をしお垰るずいった流れになるかず思いたす。 CLINICS のオンラむン蚺療はこの流れをむンタヌネットを通しお提䟛するサヌビスです。 ※ オンラむン蚺療は、䞀床、初蚺等で察面蚺療を受けた際に医垫が可胜ず刀断した堎合、次回以降の蚺察においお可胜になりたす。たた、珟圚は新型コロナりむルス感染症察策時限措眮ずしお、初蚺からオンラむン蚺療を受けるこずが可胜ずなっおいたす。 CLINICS を利甚した堎合、事前に予玄した時間にスマホたたは PC で埅機をする、医垫の蚺察をビデオチャットで受け、䌚蚈はクレゞットカヌドで行われるずいう流れずなっおいたす。 クラりド蚺療支揎システムずしおの CLINICS は 2016 幎に「オンラむン蚺療のためのシステム」ずしおロヌンチ され、 2018 幎にはクラりド型電子カルテ機胜を 、 2019 幎には予玄管理システム機胜を 、 2020 幎にはかかり぀け薬局支揎システム Pharms ずの連携機胜も远加し 、患者向けアプリからオンラむン服薬指導をシヌムレスに受けるこずができるようになりたした。 プロダクト開発宀ではこれらオンラむン蚺療機胜・電子カルテ機胜・予玄管理機胜・連携機胜の改善を日々行っおいたす。 画面共有機胜の需芁の高たり、実装の決定 このように CLINICS の改善を日々行なっおいる䞭、昚幎から始たった 新型コロナりむルス感染症COVID-19 の流行に䌎った需芁の増加により、オンラむン蚺療の件数が急増したした。 CLINICS も数倚くの医療機関にご利甚いただく䞭で、オンラむン蚺療に関わるさたざたなご芁望をいただくようになりたした。その䞭でも特に倚かったものが、今回玹介する画面共有機胜です。 察面での蚺察の際に医垫が怜査結果などを患者に芋せながら説明するように、オンラむンで蚺察する堎合でも資料をリアルタむムで共有しながら説明ができるようになれば、今たで以䞊にオンラむンでも質の高い蚺察を行えるようになりたす。 こういったナヌスケヌス、芁望などを怜蚎した結果、CLINICS を利甚するすべおの医療機関及び患者にずっお倧きな恩恵が芋蟌たれたため、オンラむン蚺察ビデオチャット䞭に医垫の PC 画面をリアルタむムで患者に共有する機胜ずしお実装をするこずにしたした。 画面共有機胜の実装 画面共有をする医垫偎向けのコヌドでどういった実装方法があるのか、倧たかな流れをたずめたす。 ※ 以䞋に蚘茉しおいるコヌドは説明のための疑䌌コヌドですので、このたたでは動䜜しないこずにご泚意ください。たた、医垫偎の実装䟋を掲茉しおいるため、患者偎画面共有を受ける偎の実装は別途必芁になりたす。 オンラむン蚺察開始たでの凊理 オンラむン蚺察を開始するには医垫偎のマむクずカメラで取埗した情報を患者偎に送付する必芁がありたす。ここではそこたでの実装の流れを芋おいきたす。 カメラ・マむクのストリヌムの取埗 オンラむン蚺察開始時点で医垫偎のマむク・カメラの情報を共有するため、たずはそれらのストリヌムを取埗する必芁がありたす。こういったメディアコンテンツのストリヌムを叞るむンタヌフェむスずしお MediaStream が定矩されおいたす。 マむク・カメラの MediaStream は、䟋えば MediaDevices.getUserMedia() を利甚しお取埗できたす。 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); SkyWay 経由でオンラむン蚺察を始める WebRTC で P2P のビデオチャットを利甚するためには、初期の接続のための凊理及び接続の維持などの凊理を行う必芁がありたす。匊瀟ではこのあたりの凊理を WebRTC SaaS の SkyWay 及びその SDK を利甚するこずで簡略化しおいたす。 オンラむン蚺察開始時には、先皋取埗した医垫偎のマむク・カメラの MediaStream を SkyWay の SDK に枡すこずで、䞀察䞀でのリアルタむムビデオチャットを実珟できたす。 import Peer , { MediaConnection } from "skyway-js" ; const peer = new Peer ({ key: "your-api-key" }); // 事前に患者ず共有しおおいた peer id に察しお call メ゜ッドず MediaStream を枡すこずで蚺察を開始できる。 const mediaConnection : MediaConnection = peer . call ( "shared-peer-id" , userMediaStream ); // 泚: 患者偎は送付された凊理をハンドリングする機胜を実装する必芁がある ここたでがオンラむン蚺察を開始するたでの凊理です。 ※ 詳现は SkyWay 公匏の チュヌトリアル などを参照ください。 画面共有の凊理 ここたでで患者に察しお医垫偎のカメラ・マむクで取埗された映像・音声が衚瀺されおいる状態のため、これを切り替える凊理が必芁になりたす。今回は珟圚接続に利甚しおいる MediaStream を、画面共有甚の MediaStream に入れ替えるこずで実珟したした。 画面の MediaStream の取埗 たずは共有する画面の MediaStream を取埗する必芁がありたす。これは MediaDevices.getDisplayMedia() を䜿うこずで実珟できたす。 const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); 画面共有甚の MediaStream を䜜る getDisplayMedia() から共有する画面の MediaStream を取埗できるものの、そのたた利甚するずマむクの音声が入りたせん。 これは getDisplayMedia() から取れる MediaStream にマむクの音声が含たれおいないこずが原因なので、必芁な画像・音声の組み合わせを持った画面共有甚の MediaStream を䜜成するこずで察凊ができたす。 MediaStreamTrack を組みあわせお画面共有甚の MediaStream を䜜る 画面共有甚の MediaStream を䜜成する前にたず、MediaStreamTrack ず MediaStream の関係を理解する必芁がありたす。 MediaStreamTrack はストリヌムに含たれる䞀぀のメディアトラックを衚珟するものです。 kind ずいう読み取り専甚プロパティがあり、オヌディオトラックであれば "audio" が、ビデオトラックであれば "video" が蚭定されおいたす。 たた、 MediaStream は耇数の MediaStreamTrack から成り、オヌディオトラック・ビデオトラックを取り出すメ゜ッドがそれぞれ MediaStream.getAudioTracks() ・ MediaStream.getVideoTracks() ずしお実装されおいたす。 これらを組み合わせるこずで、マむクず画面の MediaStreamTrack を持぀ MediaStream を䜜るこずができ、これを SkyWay の SDK に枡すこずで、画面共有を実珟できたす。 const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingMediaStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); MediaStream の入れ替え 最埌に画面共有状態ぞの切り替えです。マむク・カメラが共有されおいる状態からの切り替えにはいく぀かの方法が考えられたす。 䟋えば、倚重化であれば MediaConnection Skyway の SDK の単䜍で、「接続先 Peer ぞのメディアチャネル接続」を管理するの倚重化、MediaStream の倚重化、MediaStreamTrack の倚重化がそれぞれ考えられたす。これらの方法はマむク・カメラの切り替え時のチラ぀き抑制など実装䞊の遞択肢が増えるメリットがある䞀方で、通信量が倚くなっおしたう点がデメリットず蚀えたす。 今回は倚重化をせずに既存の MediaStream を切り替える実装を玹介したす。この方法のメリットは、倚重化に比べるず通信量が少なく、たたすでに MediaStream が䞀぀である前提で䜜られおいる堎合は、画面共有を受ける偎の実装の倉曎が䞍芁ずいう点です。 この方法は、 SkyWay の SDK であれば MediaConnection の replaceStream ずいうメ゜ッド に察しお新しい MediaStream を枡すこずで実珟ができたす。 // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する // MediaConnection は先皋 `peer.call` した際の返り倀ずしお取れおいるため、それを利甚する mediaConnection . replaceStream ( sharingMediaStream ); 実装前に懞念しおいたマむク・カメラの切り替え時のチラ぀きなども気になるほどはなく、実甚に足るような品質を保぀こずができるこずを確認しおいたす。 実装の党䜓抂芁 以䞊の流れを実装するず、次のようなコヌドになりたす。 import Peer , { MediaConnection } from "skyway-js" ; /** 医垫偎のマむク・カメラを共有しおオンラむン蚺察開始するずころたで **/ // getUserMedia()でカメラ・マむクのストリヌムを取埗 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); // Skyway sdk の初期化凊理 const peer = new Peer ({ key: "your-api-key" }); // オンラむン蚺察の開始 const mediaConnection : MediaConnection = peer . call ( "peerId" , userMediaStream ); /** 画面共有を開始する凊理 **/ // 画面共有する画面の stream を取る const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する mediaConnection . replaceStream ( sharingStream ); 開発䞭に遭遇した問題ぞの察応 スリヌプモヌド・共有を停止ボタンを抌したずきの察応 Google Chrome で画面共有の際に衚瀺される「共有を停止」ボタンを抌䞋したり、PC をスリヌプモヌドにするず、画面の MediaStreamTrack が途切れおしたいたす。 これは該圓の MediaStreamTrack に "ended" のむベントリスナを登録しおおくこずでハンドリングできたす。 displayVideoTrack . addEventListener ( "ended" , handleEndedEvent , { once: true }); TypeScript の型の察応 珟状 TypeScript の型が getDisplayMedia() に察応しおいなかったため、今回は実装の参考にしおいる skyway-conf で利甚されおいる型 を流甚する圢で察応をしたした。 declare global { interface MediaDevices { getDisplayMedia ( constraints : MediaStreamConstraints ): Promise < MediaStream >; } } これは根本的には TypeScript の dom.d.ts に型定矩が入っおいないこずが起因しおいたすが、 TypeScript4.4 で察応がされるようです 。 たずめ 昚今の状況により、オンラむン蚺察のニヌズが高たり、画面共有機胜の重芁性が高たりたした。 蚺察䞭の画面共有機胜は以䞋の api を組み合わせるこずで実珟するこずができたす。 PC 画面の MediaStream は getDisplayMedia() を䜿うこずで取埗 MediaStream に含める音声・画像ストリヌムを倉曎したい堎合は MediaStreamTrack の組み合わせを倉えるこずで䜜成 接続䞭の MediaStream の倉曎は SkyWay の SDK の MediaConnection.replaceStream() を䜿う 最埌に CLINICS では本皿で玹介した画面共有などの新芏機胜の導入や日々の改善を通じお、医療機関・患者双方に支持されるプロダクトを目指し開発を行っおいたす。興味を持たれた゚ンゞニアの方がいらっしゃいたしたらぜひ こちら にご連絡いただければず思いたす。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
事業本郚 プロダクト開発宀゚ンゞニアの日䞋です。 オンラむン蚺療・服薬指導・クラりド蚺療支揎システム「CLINICS」 の、患者・医療機関に向けたアプリケヌションの機胜開発、開発基盀、むンフラ呚りを担圓しおおりたす。 今回 CLINICS が提䟛するオンラむン蚺療機胜に「画面共有機胜」を远加したしたので、その背景・技術的な話をたずめたす。 画面共有機胜実装の背景 CLINICS ずオンラむン蚺療 普段皆さんが病院にかかるずき、倚くの堎合は病院に行き、医垫の蚺察を察面で受け、䌚蚈をしお垰るずいった流れになるかず思いたす。 CLINICS のオンラむン蚺療はこの流れをむンタヌネットを通しお提䟛するサヌビスです。 ※ オンラむン蚺療は、䞀床、初蚺等で察面蚺療を受けた際に医垫が可胜ず刀断した堎合、次回以降の蚺察においお可胜になりたす。たた、珟圚は新型コロナりむルス感染症察策時限措眮ずしお、初蚺からオンラむン蚺療を受けるこずが可胜ずなっおいたす。 CLINICS を利甚した堎合、事前に予玄した時間にスマホたたは PC で埅機をする、医垫の蚺察をビデオチャットで受け、䌚蚈はクレゞットカヌドで行われるずいう流れずなっおいたす。 クラりド蚺療支揎システムずしおの CLINICS は 2016 幎に「オンラむン蚺療のためのシステム」ずしおロヌンチ され、 2018 幎にはクラりド型電子カルテ機胜を 、 2019 幎には予玄管理システム機胜を 、 2020 幎にはかかり぀け薬局支揎システム Pharms ずの連携機胜も远加し 、患者向けアプリからオンラむン服薬指導をシヌムレスに受けるこずができるようになりたした。 プロダクト開発宀ではこれらオンラむン蚺療機胜・電子カルテ機胜・予玄管理機胜・連携機胜の改善を日々行っおいたす。 画面共有機胜の需芁の高たり、実装の決定 このように CLINICS の改善を日々行なっおいる䞭、昚幎から始たった 新型コロナりむルス感染症COVID-19 の流行に䌎った需芁の増加により、オンラむン蚺療の件数が急増したした。 CLINICS も数倚くの医療機関にご利甚いただく䞭で、オンラむン蚺療に関わるさたざたなご芁望をいただくようになりたした。その䞭でも特に倚かったものが、今回玹介する画面共有機胜です。 察面での蚺察の際に医垫が怜査結果などを患者に芋せながら説明するように、オンラむンで蚺察する堎合でも資料をリアルタむムで共有しながら説明ができるようになれば、今たで以䞊にオンラむンでも質の高い蚺察を行えるようになりたす。 こういったナヌスケヌス、芁望などを怜蚎した結果、CLINICS を利甚するすべおの医療機関及び患者にずっお倧きな恩恵が芋蟌たれたため、オンラむン蚺察ビデオチャット䞭に医垫の PC 画面をリアルタむムで患者に共有する機胜ずしお実装をするこずにしたした。 画面共有機胜の実装 画面共有をする医垫偎向けのコヌドでどういった実装方法があるのか、倧たかな流れをたずめたす。 ※ 以䞋に蚘茉しおいるコヌドは説明のための疑䌌コヌドですので、このたたでは動䜜しないこずにご泚意ください。たた、医垫偎の実装䟋を掲茉しおいるため、患者偎画面共有を受ける偎の実装は別途必芁になりたす。 オンラむン蚺察開始たでの凊理 オンラむン蚺察を開始するには医垫偎のマむクずカメラで取埗した情報を患者偎に送付する必芁がありたす。ここではそこたでの実装の流れを芋おいきたす。 カメラ・マむクのストリヌムの取埗 オンラむン蚺察開始時点で医垫偎のマむク・カメラの情報を共有するため、たずはそれらのストリヌムを取埗する必芁がありたす。こういったメディアコンテンツのストリヌムを叞るむンタヌフェむスずしお MediaStream が定矩されおいたす。 マむク・カメラの MediaStream は、䟋えば MediaDevices.getUserMedia() を利甚しお取埗できたす。 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); SkyWay 経由でオンラむン蚺察を始める WebRTC で P2P のビデオチャットを利甚するためには、初期の接続のための凊理及び接続の維持などの凊理を行う必芁がありたす。匊瀟ではこのあたりの凊理を WebRTC SaaS の SkyWay 及びその SDK を利甚するこずで簡略化しおいたす。 オンラむン蚺察開始時には、先皋取埗した医垫偎のマむク・カメラの MediaStream を SkyWay の SDK に枡すこずで、䞀察䞀でのリアルタむムビデオチャットを実珟できたす。 import Peer , { MediaConnection } from "skyway-js" ; const peer = new Peer ({ key: "your-api-key" }); // 事前に患者ず共有しおおいた peer id に察しお call メ゜ッドず MediaStream を枡すこずで蚺察を開始できる。 const mediaConnection : MediaConnection = peer . call ( "shared-peer-id" , userMediaStream ); // 泚: 患者偎は送付された凊理をハンドリングする機胜を実装する必芁がある ここたでがオンラむン蚺察を開始するたでの凊理です。 ※ 詳现は SkyWay 公匏の チュヌトリアル などを参照ください。 画面共有の凊理 ここたでで患者に察しお医垫偎のカメラ・マむクで取埗された映像・音声が衚瀺されおいる状態のため、これを切り替える凊理が必芁になりたす。今回は珟圚接続に利甚しおいる MediaStream を、画面共有甚の MediaStream に入れ替えるこずで実珟したした。 画面の MediaStream の取埗 たずは共有する画面の MediaStream を取埗する必芁がありたす。これは MediaDevices.getDisplayMedia() を䜿うこずで実珟できたす。 const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); 画面共有甚の MediaStream を䜜る getDisplayMedia() から共有する画面の MediaStream を取埗できるものの、そのたた利甚するずマむクの音声が入りたせん。 これは getDisplayMedia() から取れる MediaStream にマむクの音声が含たれおいないこずが原因なので、必芁な画像・音声の組み合わせを持った画面共有甚の MediaStream を䜜成するこずで察凊ができたす。 MediaStreamTrack を組みあわせお画面共有甚の MediaStream を䜜る 画面共有甚の MediaStream を䜜成する前にたず、MediaStreamTrack ず MediaStream の関係を理解する必芁がありたす。 MediaStreamTrack はストリヌムに含たれる䞀぀のメディアトラックを衚珟するものです。 kind ずいう読み取り専甚プロパティがあり、オヌディオトラックであれば "audio" が、ビデオトラックであれば "video" が蚭定されおいたす。 たた、 MediaStream は耇数の MediaStreamTrack から成り、オヌディオトラック・ビデオトラックを取り出すメ゜ッドがそれぞれ MediaStream.getAudioTracks() ・ MediaStream.getVideoTracks() ずしお実装されおいたす。 これらを組み合わせるこずで、マむクず画面の MediaStreamTrack を持぀ MediaStream を䜜るこずができ、これを SkyWay の SDK に枡すこずで、画面共有を実珟できたす。 const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingMediaStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); MediaStream の入れ替え 最埌に画面共有状態ぞの切り替えです。マむク・カメラが共有されおいる状態からの切り替えにはいく぀かの方法が考えられたす。 䟋えば、倚重化であれば MediaConnection Skyway の SDK の単䜍で、「接続先 Peer ぞのメディアチャネル接続」を管理するの倚重化、MediaStream の倚重化、MediaStreamTrack の倚重化がそれぞれ考えられたす。これらの方法はマむク・カメラの切り替え時のチラ぀き抑制など実装䞊の遞択肢が増えるメリットがある䞀方で、通信量が倚くなっおしたう点がデメリットず蚀えたす。 今回は倚重化をせずに既存の MediaStream を切り替える実装を玹介したす。この方法のメリットは、倚重化に比べるず通信量が少なく、たたすでに MediaStream が䞀぀である前提で䜜られおいる堎合は、画面共有を受ける偎の実装の倉曎が䞍芁ずいう点です。 この方法は、 SkyWay の SDK であれば MediaConnection の replaceStream ずいうメ゜ッド に察しお新しい MediaStream を枡すこずで実珟ができたす。 // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する // MediaConnection は先皋 `peer.call` した際の返り倀ずしお取れおいるため、それを利甚する mediaConnection . replaceStream ( sharingMediaStream ); 実装前に懞念しおいたマむク・カメラの切り替え時のチラ぀きなども気になるほどはなく、実甚に足るような品質を保぀こずができるこずを確認しおいたす。 実装の党䜓抂芁 以䞊の流れを実装するず、次のようなコヌドになりたす。 import Peer , { MediaConnection } from "skyway-js" ; /** 医垫偎のマむク・カメラを共有しおオンラむン蚺察開始するずころたで **/ // getUserMedia()でカメラ・マむクのストリヌムを取埗 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); // Skyway sdk の初期化凊理 const peer = new Peer ({ key: "your-api-key" }); // オンラむン蚺察の開始 const mediaConnection : MediaConnection = peer . call ( "peerId" , userMediaStream ); /** 画面共有を開始する凊理 **/ // 画面共有する画面の stream を取る const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する mediaConnection . replaceStream ( sharingStream ); 開発䞭に遭遇した問題ぞの察応 スリヌプモヌド・共有を停止ボタンを抌したずきの察応 Google Chrome で画面共有の際に衚瀺される「共有を停止」ボタンを抌䞋したり、PC をスリヌプモヌドにするず、画面の MediaStreamTrack が途切れおしたいたす。 これは該圓の MediaStreamTrack に "ended" のむベントリスナを登録しおおくこずでハンドリングできたす。 displayVideoTrack . addEventListener ( "ended" , handleEndedEvent , { once: true }); TypeScript の型の察応 珟状 TypeScript の型が getDisplayMedia() に察応しおいなかったため、今回は実装の参考にしおいる skyway-conf で利甚されおいる型 を流甚する圢で察応をしたした。 declare global { interface MediaDevices { getDisplayMedia ( constraints : MediaStreamConstraints ): Promise < MediaStream >; } } これは根本的には TypeScript の dom.d.ts に型定矩が入っおいないこずが起因しおいたすが、 TypeScript4.4 で察応がされるようです 。 たずめ 昚今の状況により、オンラむン蚺察のニヌズが高たり、画面共有機胜の重芁性が高たりたした。 蚺察䞭の画面共有機胜は以䞋の api を組み合わせるこずで実珟するこずができたす。 PC 画面の MediaStream は getDisplayMedia() を䜿うこずで取埗 MediaStream に含める音声・画像ストリヌムを倉曎したい堎合は MediaStreamTrack の組み合わせを倉えるこずで䜜成 接続䞭の MediaStream の倉曎は SkyWay の SDK の MediaConnection.replaceStream() を䜿う 最埌に CLINICS では本皿で玹介した画面共有などの新芏機胜の導入や日々の改善を通じお、医療機関・患者双方に支持されるプロダクトを目指し開発を行っおいたす。興味を持たれた゚ンゞニアの方がいらっしゃいたしたらぜひ こちら にご連絡いただければず思いたす。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
事業本郚 プロダクト開発宀゚ンゞニアの日䞋です。 オンラむン蚺療・服薬指導・クラりド蚺療支揎システム「CLINICS」 の、患者・医療機関に向けたアプリケヌションの機胜開発、開発基盀、むンフラ呚りを担圓しおおりたす。 今回 CLINICS が提䟛するオンラむン蚺療機胜に「画面共有機胜」を远加したしたので、その背景・技術的な話をたずめたす。 画面共有機胜実装の背景 CLINICS ずオンラむン蚺療 普段皆さんが病院にかかるずき、倚くの堎合は病院に行き、医垫の蚺察を察面で受け、䌚蚈をしお垰るずいった流れになるかず思いたす。 CLINICS のオンラむン蚺療はこの流れをむンタヌネットを通しお提䟛するサヌビスです。 ※ オンラむン蚺療は、䞀床、初蚺等で察面蚺療を受けた際に医垫が可胜ず刀断した堎合、次回以降の蚺察においお可胜になりたす。たた、珟圚は新型コロナりむルス感染症察策時限措眮ずしお、初蚺からオンラむン蚺療を受けるこずが可胜ずなっおいたす。 CLINICS を利甚した堎合、事前に予玄した時間にスマホたたは PC で埅機をする、医垫の蚺察をビデオチャットで受け、䌚蚈はクレゞットカヌドで行われるずいう流れずなっおいたす。 クラりド蚺療支揎システムずしおの CLINICS は 2016 幎に「オンラむン蚺療のためのシステム」ずしおロヌンチ され、 2018 幎にはクラりド型電子カルテ機胜を 、 2019 幎には予玄管理システム機胜を 、 2020 幎にはかかり぀け薬局支揎システム Pharms ずの連携機胜も远加し 、患者向けアプリからオンラむン服薬指導をシヌムレスに受けるこずができるようになりたした。 プロダクト開発宀ではこれらオンラむン蚺療機胜・電子カルテ機胜・予玄管理機胜・連携機胜の改善を日々行っおいたす。 画面共有機胜の需芁の高たり、実装の決定 このように CLINICS の改善を日々行なっおいる䞭、昚幎から始たった 新型コロナりむルス感染症COVID-19 の流行に䌎った需芁の増加により、オンラむン蚺療の件数が急増したした。 CLINICS も数倚くの医療機関にご利甚いただく䞭で、オンラむン蚺療に関わるさたざたなご芁望をいただくようになりたした。その䞭でも特に倚かったものが、今回玹介する画面共有機胜です。 察面での蚺察の際に医垫が怜査結果などを患者に芋せながら説明するように、オンラむンで蚺察する堎合でも資料をリアルタむムで共有しながら説明ができるようになれば、今たで以䞊にオンラむンでも質の高い蚺察を行えるようになりたす。 こういったナヌスケヌス、芁望などを怜蚎した結果、CLINICS を利甚するすべおの医療機関及び患者にずっお倧きな恩恵が芋蟌たれたため、オンラむン蚺察ビデオチャット䞭に医垫の PC 画面をリアルタむムで患者に共有する機胜ずしお実装をするこずにしたした。 画面共有機胜の実装 画面共有をする医垫偎向けのコヌドでどういった実装方法があるのか、倧たかな流れをたずめたす。 ※ 以䞋に蚘茉しおいるコヌドは説明のための疑䌌コヌドですので、このたたでは動䜜しないこずにご泚意ください。たた、医垫偎の実装䟋を掲茉しおいるため、患者偎画面共有を受ける偎の実装は別途必芁になりたす。 オンラむン蚺察開始たでの凊理 オンラむン蚺察を開始するには医垫偎のマむクずカメラで取埗した情報を患者偎に送付する必芁がありたす。ここではそこたでの実装の流れを芋おいきたす。 カメラ・マむクのストリヌムの取埗 オンラむン蚺察開始時点で医垫偎のマむク・カメラの情報を共有するため、たずはそれらのストリヌムを取埗する必芁がありたす。こういったメディアコンテンツのストリヌムを叞るむンタヌフェむスずしお MediaStream が定矩されおいたす。 マむク・カメラの MediaStream は、䟋えば MediaDevices.getUserMedia() を利甚しお取埗できたす。 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); SkyWay 経由でオンラむン蚺察を始める WebRTC で P2P のビデオチャットを利甚するためには、初期の接続のための凊理及び接続の維持などの凊理を行う必芁がありたす。匊瀟ではこのあたりの凊理を WebRTC SaaS の SkyWay 及びその SDK を利甚するこずで簡略化しおいたす。 オンラむン蚺察開始時には、先皋取埗した医垫偎のマむク・カメラの MediaStream を SkyWay の SDK に枡すこずで、䞀察䞀でのリアルタむムビデオチャットを実珟できたす。 import Peer , { MediaConnection } from "skyway-js" ; const peer = new Peer ({ key: "your-api-key" }); // 事前に患者ず共有しおおいた peer id に察しお call メ゜ッドず MediaStream を枡すこずで蚺察を開始できる。 const mediaConnection : MediaConnection = peer . call ( "shared-peer-id" , userMediaStream ); // 泚: 患者偎は送付された凊理をハンドリングする機胜を実装する必芁がある ここたでがオンラむン蚺察を開始するたでの凊理です。 ※ 詳现は SkyWay 公匏の チュヌトリアル などを参照ください。 画面共有の凊理 ここたでで患者に察しお医垫偎のカメラ・マむクで取埗された映像・音声が衚瀺されおいる状態のため、これを切り替える凊理が必芁になりたす。今回は珟圚接続に利甚しおいる MediaStream を、画面共有甚の MediaStream に入れ替えるこずで実珟したした。 画面の MediaStream の取埗 たずは共有する画面の MediaStream を取埗する必芁がありたす。これは MediaDevices.getDisplayMedia() を䜿うこずで実珟できたす。 const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); 画面共有甚の MediaStream を䜜る getDisplayMedia() から共有する画面の MediaStream を取埗できるものの、そのたた利甚するずマむクの音声が入りたせん。 これは getDisplayMedia() から取れる MediaStream にマむクの音声が含たれおいないこずが原因なので、必芁な画像・音声の組み合わせを持った画面共有甚の MediaStream を䜜成するこずで察凊ができたす。 MediaStreamTrack を組みあわせお画面共有甚の MediaStream を䜜る 画面共有甚の MediaStream を䜜成する前にたず、MediaStreamTrack ず MediaStream の関係を理解する必芁がありたす。 MediaStreamTrack はストリヌムに含たれる䞀぀のメディアトラックを衚珟するものです。 kind ずいう読み取り専甚プロパティがあり、オヌディオトラックであれば "audio" が、ビデオトラックであれば "video" が蚭定されおいたす。 たた、 MediaStream は耇数の MediaStreamTrack から成り、オヌディオトラック・ビデオトラックを取り出すメ゜ッドがそれぞれ MediaStream.getAudioTracks() ・ MediaStream.getVideoTracks() ずしお実装されおいたす。 これらを組み合わせるこずで、マむクず画面の MediaStreamTrack を持぀ MediaStream を䜜るこずができ、これを SkyWay の SDK に枡すこずで、画面共有を実珟できたす。 const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingMediaStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); MediaStream の入れ替え 最埌に画面共有状態ぞの切り替えです。マむク・カメラが共有されおいる状態からの切り替えにはいく぀かの方法が考えられたす。 䟋えば、倚重化であれば MediaConnection Skyway の SDK の単䜍で、「接続先 Peer ぞのメディアチャネル接続」を管理するの倚重化、MediaStream の倚重化、MediaStreamTrack の倚重化がそれぞれ考えられたす。これらの方法はマむク・カメラの切り替え時のチラ぀き抑制など実装䞊の遞択肢が増えるメリットがある䞀方で、通信量が倚くなっおしたう点がデメリットず蚀えたす。 今回は倚重化をせずに既存の MediaStream を切り替える実装を玹介したす。この方法のメリットは、倚重化に比べるず通信量が少なく、たたすでに MediaStream が䞀぀である前提で䜜られおいる堎合は、画面共有を受ける偎の実装の倉曎が䞍芁ずいう点です。 この方法は、 SkyWay の SDK であれば MediaConnection の replaceStream ずいうメ゜ッド に察しお新しい MediaStream を枡すこずで実珟ができたす。 // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する // MediaConnection は先皋 `peer.call` した際の返り倀ずしお取れおいるため、それを利甚する mediaConnection . replaceStream ( sharingMediaStream ); 実装前に懞念しおいたマむク・カメラの切り替え時のチラ぀きなども気になるほどはなく、実甚に足るような品質を保぀こずができるこずを確認しおいたす。 実装の党䜓抂芁 以䞊の流れを実装するず、次のようなコヌドになりたす。 import Peer , { MediaConnection } from "skyway-js" ; /** 医垫偎のマむク・カメラを共有しおオンラむン蚺察開始するずころたで **/ // getUserMedia()でカメラ・マむクのストリヌムを取埗 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); // Skyway sdk の初期化凊理 const peer = new Peer ({ key: "your-api-key" }); // オンラむン蚺察の開始 const mediaConnection : MediaConnection = peer . call ( "peerId" , userMediaStream ); /** 画面共有を開始する凊理 **/ // 画面共有する画面の stream を取る const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する mediaConnection . replaceStream ( sharingStream ); 開発䞭に遭遇した問題ぞの察応 スリヌプモヌド・共有を停止ボタンを抌したずきの察応 Google Chrome で画面共有の際に衚瀺される「共有を停止」ボタンを抌䞋したり、PC をスリヌプモヌドにするず、画面の MediaStreamTrack が途切れおしたいたす。 これは該圓の MediaStreamTrack に "ended" のむベントリスナを登録しおおくこずでハンドリングできたす。 displayVideoTrack . addEventListener ( "ended" , handleEndedEvent , { once: true }); TypeScript の型の察応 珟状 TypeScript の型が getDisplayMedia() に察応しおいなかったため、今回は実装の参考にしおいる skyway-conf で利甚されおいる型 を流甚する圢で察応をしたした。 declare global { interface MediaDevices { getDisplayMedia ( constraints : MediaStreamConstraints ): Promise < MediaStream >; } } これは根本的には TypeScript の dom.d.ts に型定矩が入っおいないこずが起因しおいたすが、 TypeScript4.4 で察応がされるようです 。 たずめ 昚今の状況により、オンラむン蚺察のニヌズが高たり、画面共有機胜の重芁性が高たりたした。 蚺察䞭の画面共有機胜は以䞋の api を組み合わせるこずで実珟するこずができたす。 PC 画面の MediaStream は getDisplayMedia() を䜿うこずで取埗 MediaStream に含める音声・画像ストリヌムを倉曎したい堎合は MediaStreamTrack の組み合わせを倉えるこずで䜜成 接続䞭の MediaStream の倉曎は SkyWay の SDK の MediaConnection.replaceStream() を䜿う 最埌に CLINICS では本皿で玹介した画面共有などの新芏機胜の導入や日々の改善を通じお、医療機関・患者双方に支持されるプロダクトを目指し開発を行っおいたす。興味を持たれた゚ンゞニアの方がいらっしゃいたしたらぜひ こちら にご連絡いただければず思いたす。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
事業本郚 プロダクト開発宀゚ンゞニアの日䞋です。 オンラむン蚺療・服薬指導・クラりド蚺療支揎システム「CLINICS」 の、患者・医療機関に向けたアプリケヌションの機胜開発、開発基盀、むンフラ呚りを担圓しおおりたす。 今回 CLINICS が提䟛するオンラむン蚺療機胜に「画面共有機胜」を远加したしたので、その背景・技術的な話をたずめたす。 画面共有機胜実装の背景 CLINICS ずオンラむン蚺療 普段皆さんが病院にかかるずき、倚くの堎合は病院に行き、医垫の蚺察を察面で受け、䌚蚈をしお垰るずいった流れになるかず思いたす。 CLINICS のオンラむン蚺療はこの流れをむンタヌネットを通しお提䟛するサヌビスです。 ※ オンラむン蚺療は、䞀床、初蚺等で察面蚺療を受けた際に医垫が可胜ず刀断した堎合、次回以降の蚺察においお可胜になりたす。たた、珟圚は新型コロナりむルス感染症察策時限措眮ずしお、初蚺からオンラむン蚺療を受けるこずが可胜ずなっおいたす。 CLINICS を利甚した堎合、事前に予玄した時間にスマホたたは PC で埅機をする、医垫の蚺察をビデオチャットで受け、䌚蚈はクレゞットカヌドで行われるずいう流れずなっおいたす。 クラりド蚺療支揎システムずしおの CLINICS は 2016 幎に「オンラむン蚺療のためのシステム」ずしおロヌンチ され、 2018 幎にはクラりド型電子カルテ機胜を 、 2019 幎には予玄管理システム機胜を 、 2020 幎にはかかり぀け薬局支揎システム Pharms ずの連携機胜も远加し 、患者向けアプリからオンラむン服薬指導をシヌムレスに受けるこずができるようになりたした。 プロダクト開発宀ではこれらオンラむン蚺療機胜・電子カルテ機胜・予玄管理機胜・連携機胜の改善を日々行っおいたす。 画面共有機胜の需芁の高たり、実装の決定 このように CLINICS の改善を日々行なっおいる䞭、昚幎から始たった 新型コロナりむルス感染症COVID-19 の流行に䌎った需芁の増加により、オンラむン蚺療の件数が急増したした。 CLINICS も数倚くの医療機関にご利甚いただく䞭で、オンラむン蚺療に関わるさたざたなご芁望をいただくようになりたした。その䞭でも特に倚かったものが、今回玹介する画面共有機胜です。 察面での蚺察の際に医垫が怜査結果などを患者に芋せながら説明するように、オンラむンで蚺察する堎合でも資料をリアルタむムで共有しながら説明ができるようになれば、今たで以䞊にオンラむンでも質の高い蚺察を行えるようになりたす。 こういったナヌスケヌス、芁望などを怜蚎した結果、CLINICS を利甚するすべおの医療機関及び患者にずっお倧きな恩恵が芋蟌たれたため、オンラむン蚺察ビデオチャット䞭に医垫の PC 画面をリアルタむムで患者に共有する機胜ずしお実装をするこずにしたした。 画面共有機胜の実装 画面共有をする医垫偎向けのコヌドでどういった実装方法があるのか、倧たかな流れをたずめたす。 ※ 以䞋に蚘茉しおいるコヌドは説明のための疑䌌コヌドですので、このたたでは動䜜しないこずにご泚意ください。たた、医垫偎の実装䟋を掲茉しおいるため、患者偎画面共有を受ける偎の実装は別途必芁になりたす。 オンラむン蚺察開始たでの凊理 オンラむン蚺察を開始するには医垫偎のマむクずカメラで取埗した情報を患者偎に送付する必芁がありたす。ここではそこたでの実装の流れを芋おいきたす。 カメラ・マむクのストリヌムの取埗 オンラむン蚺察開始時点で医垫偎のマむク・カメラの情報を共有するため、たずはそれらのストリヌムを取埗する必芁がありたす。こういったメディアコンテンツのストリヌムを叞るむンタヌフェむスずしお MediaStream が定矩されおいたす。 マむク・カメラの MediaStream は、䟋えば MediaDevices.getUserMedia() を利甚しお取埗できたす。 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); SkyWay 経由でオンラむン蚺察を始める WebRTC で P2P のビデオチャットを利甚するためには、初期の接続のための凊理及び接続の維持などの凊理を行う必芁がありたす。匊瀟ではこのあたりの凊理を WebRTC SaaS の SkyWay 及びその SDK を利甚するこずで簡略化しおいたす。 オンラむン蚺察開始時には、先皋取埗した医垫偎のマむク・カメラの MediaStream を SkyWay の SDK に枡すこずで、䞀察䞀でのリアルタむムビデオチャットを実珟できたす。 import Peer , { MediaConnection } from "skyway-js" ; const peer = new Peer ({ key: "your-api-key" }); // 事前に患者ず共有しおおいた peer id に察しお call メ゜ッドず MediaStream を枡すこずで蚺察を開始できる。 const mediaConnection : MediaConnection = peer . call ( "shared-peer-id" , userMediaStream ); // 泚: 患者偎は送付された凊理をハンドリングする機胜を実装する必芁がある ここたでがオンラむン蚺察を開始するたでの凊理です。 ※ 詳现は SkyWay 公匏の チュヌトリアル などを参照ください。 画面共有の凊理 ここたでで患者に察しお医垫偎のカメラ・マむクで取埗された映像・音声が衚瀺されおいる状態のため、これを切り替える凊理が必芁になりたす。今回は珟圚接続に利甚しおいる MediaStream を、画面共有甚の MediaStream に入れ替えるこずで実珟したした。 画面の MediaStream の取埗 たずは共有する画面の MediaStream を取埗する必芁がありたす。これは MediaDevices.getDisplayMedia() を䜿うこずで実珟できたす。 const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); 画面共有甚の MediaStream を䜜る getDisplayMedia() から共有する画面の MediaStream を取埗できるものの、そのたた利甚するずマむクの音声が入りたせん。 これは getDisplayMedia() から取れる MediaStream にマむクの音声が含たれおいないこずが原因なので、必芁な画像・音声の組み合わせを持った画面共有甚の MediaStream を䜜成するこずで察凊ができたす。 MediaStreamTrack を組みあわせお画面共有甚の MediaStream を䜜る 画面共有甚の MediaStream を䜜成する前にたず、MediaStreamTrack ず MediaStream の関係を理解する必芁がありたす。 MediaStreamTrack はストリヌムに含たれる䞀぀のメディアトラックを衚珟するものです。 kind ずいう読み取り専甚プロパティがあり、オヌディオトラックであれば "audio" が、ビデオトラックであれば "video" が蚭定されおいたす。 たた、 MediaStream は耇数の MediaStreamTrack から成り、オヌディオトラック・ビデオトラックを取り出すメ゜ッドがそれぞれ MediaStream.getAudioTracks() ・ MediaStream.getVideoTracks() ずしお実装されおいたす。 これらを組み合わせるこずで、マむクず画面の MediaStreamTrack を持぀ MediaStream を䜜るこずができ、これを SkyWay の SDK に枡すこずで、画面共有を実珟できたす。 const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingMediaStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); MediaStream の入れ替え 最埌に画面共有状態ぞの切り替えです。マむク・カメラが共有されおいる状態からの切り替えにはいく぀かの方法が考えられたす。 䟋えば、倚重化であれば MediaConnection Skyway の SDK の単䜍で、「接続先 Peer ぞのメディアチャネル接続」を管理するの倚重化、MediaStream の倚重化、MediaStreamTrack の倚重化がそれぞれ考えられたす。これらの方法はマむク・カメラの切り替え時のチラ぀き抑制など実装䞊の遞択肢が増えるメリットがある䞀方で、通信量が倚くなっおしたう点がデメリットず蚀えたす。 今回は倚重化をせずに既存の MediaStream を切り替える実装を玹介したす。この方法のメリットは、倚重化に比べるず通信量が少なく、たたすでに MediaStream が䞀぀である前提で䜜られおいる堎合は、画面共有を受ける偎の実装の倉曎が䞍芁ずいう点です。 この方法は、 SkyWay の SDK であれば MediaConnection の replaceStream ずいうメ゜ッド に察しお新しい MediaStream を枡すこずで実珟ができたす。 // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する // MediaConnection は先皋 `peer.call` した際の返り倀ずしお取れおいるため、それを利甚する mediaConnection . replaceStream ( sharingMediaStream ); 実装前に懞念しおいたマむク・カメラの切り替え時のチラ぀きなども気になるほどはなく、実甚に足るような品質を保぀こずができるこずを確認しおいたす。 実装の党䜓抂芁 以䞊の流れを実装するず、次のようなコヌドになりたす。 import Peer , { MediaConnection } from "skyway-js" ; /** 医垫偎のマむク・カメラを共有しおオンラむン蚺察開始するずころたで **/ // getUserMedia()でカメラ・マむクのストリヌムを取埗 const userMediaStream : MediaStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); // Skyway sdk の初期化凊理 const peer = new Peer ({ key: "your-api-key" }); // オンラむン蚺察の開始 const mediaConnection : MediaConnection = peer . call ( "peerId" , userMediaStream ); /** 画面共有を開始する凊理 **/ // 画面共有する画面の stream を取る const displayStream : MediaStream = await navigator . mediaDevices . getDisplayMedia ( { video: true } ); const [ displayVideoTrack ]: MediaStreamTrack [] = displayStream . getVideoTracks (); // 画面共有の音声はマむクの音声を利甚したいので、userMediaStream から audioTrack を取り出しおおく const [ userAudioTrack ]: MediaStreamTrack [] = userMediaStream . getAudioTracks (); // 画面共有するための MediaStream を䜜成する(画面の videoTrack、マむクの audioTrack を持぀ MediaStream を䜜る) const sharingStream : MediaStream = new MediaStream ([ displayVideoTrack , userAudioTrack , ]); // 画面共有甚の MediaStream を枡すこずで、画面共有を開始する mediaConnection . replaceStream ( sharingStream ); 開発䞭に遭遇した問題ぞの察応 スリヌプモヌド・共有を停止ボタンを抌したずきの察応 Google Chrome で画面共有の際に衚瀺される「共有を停止」ボタンを抌䞋したり、PC をスリヌプモヌドにするず、画面の MediaStreamTrack が途切れおしたいたす。 これは該圓の MediaStreamTrack に "ended" のむベントリスナを登録しおおくこずでハンドリングできたす。 displayVideoTrack . addEventListener ( "ended" , handleEndedEvent , { once: true }); TypeScript の型の察応 珟状 TypeScript の型が getDisplayMedia() に察応しおいなかったため、今回は実装の参考にしおいる skyway-conf で利甚されおいる型 を流甚する圢で察応をしたした。 declare global { interface MediaDevices { getDisplayMedia ( constraints : MediaStreamConstraints ): Promise < MediaStream >; } } これは根本的には TypeScript の dom.d.ts に型定矩が入っおいないこずが起因しおいたすが、 TypeScript4.4 で察応がされるようです 。 たずめ 昚今の状況により、オンラむン蚺察のニヌズが高たり、画面共有機胜の重芁性が高たりたした。 蚺察䞭の画面共有機胜は以䞋の api を組み合わせるこずで実珟するこずができたす。 PC 画面の MediaStream は getDisplayMedia() を䜿うこずで取埗 MediaStream に含める音声・画像ストリヌムを倉曎したい堎合は MediaStreamTrack の組み合わせを倉えるこずで䜜成 接続䞭の MediaStream の倉曎は SkyWay の SDK の MediaConnection.replaceStream() を䜿う 最埌に CLINICS では本皿で玹介した画面共有などの新芏機胜の導入や日々の改善を通じお、医療機関・患者双方に支持されるプロダクトを目指し開発を行っおいたす。興味を持たれた゚ンゞニアの方がいらっしゃいたしたらぜひ こちら にご連絡いただければず思いたす。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
はじめに はじめたしお、コヌポレヌト゚ンゞニアの山䞋です。 2020 幎に Slack を掻甚した ChatOps 皟議ワヌクフロヌを内補で開発したのですが、さらに、2021 幎 4 月にこの Slack 皟議ず電子契玄システムである クラりドサむン を連携させお、電子契玄をもっず䟿利に䜿い、生産性の向䞊を実珟したしたのでお話しいたしたす。 たず、圓瀟の皟議システムは 2020 幎 12 月の圓瀟の蚘事 のおさらいになりたすが、皟議の䜜業が Slack 䞊で完結する、 ChatOps による皟議ワヌクフロヌ ずなっおおりたす。本皿に぀いおは 2021 幎 7 月に執筆しおおりたすので䞁床導入から 1 幎皋経過し、その間倧きなトラブルも無く、今も圓瀟の極めお迅速な意思決定の䞀助になっおいたす。ChatOps による皟議ワヌクフロヌに぀いおは、盎近、2021 幎 6 月に LayerX 瀟が LayerX ワヌクフロヌの新機胜ずしお発衚 し、サヌビスずしおも提䟛され、 日経新聞 でも取り䞊げられおいるこずから、今珟圚のパラダむムずしお、先進的で有効な䞀手法であったず再認識しおおりたす。 今回、新型コロナりむルス感染拡倧防止に䌎うリモヌトワヌクの加速ずいう状況もあり、圓瀟で 2021 幎 4 月に電子契玄システムずしおクラりドサむンを導入したした。電子契玄に限らず、契玄抌印䜜業は皟議の埌続䜜業に圓たるため、ただ導入しお䜿甚するのみならず、クラりドサむンの API を利甚しお皟議䞊にあるデヌタを電子契玄に送信させるこずでシヌムレスな連携を実珟しおいたす。本皿では圓瀟が行ったシヌムレスな連携手法に぀いお詳现をご説明いたしたす。 TeamSpirit ずクラりドサむンの API 連携に぀いお 実装抂芁 匊瀟の皟議システムである TeamSpirit ずクラりドサむンずの連携に぀いおお話ししたす。たず、本皿の開発郚分ずシステム構成は䞋蚘になっおおりたす。 凊理内容の詳现は埌ほど述べたすが、抂芁ずしおは TeamSprit(Apex)からクラりドサむンの API をコヌルし、クラりドサむン䞊で䜜成した契玄文曞ぞ皟議に蚘茉されおいる契玄曞ファむルや先方担圓者等の情報を連携する仕組みずなっおおりたす。これにより契玄担圓者はクラりドサむンにログむン埌、䞋蚘の 3 ステップで先方に送信できるようになっおいたす。 蚘茉内容の確認 抌印・眲名箇所の蚭定 先方ぞの送信 クラりドサむンを䜿甚しお契玄文曞を䞀から䜜成する堎合のナヌザ䜜業ず、圓瀟で採甚した API 連携行った堎合のナヌザ䜜業を比范したものが䞋蚘の衚です。䜜業が半分皋床削枛されたこずが分かりたす。 䜜業項番 䞀から䜜成する堎合 API 連携を利甚した圓瀟の堎合 1 ログむン ログむン 2 契玄文曞の䜜成(件名、契玄文曞ずしおの宛名蚭定等) なし 3 契玄曞ファむルのアップロヌド なし 4 先方の送信先蚭定 なし 5 抌印欄の蚭定 抌印欄の蚭定 6 先方ぞの送信 先方ぞの送信 実装 今回の開発で䜿甚した クラりドサむン API は䞋蚘の 5 ぀の API を䜿甚したした (※以降、クラりドサむン API に倣い、倉数を衚珟する堎合は {} で括りたす)。 API 皮類 䜿甚甚途 post /token アクセストヌクンの取埗 post /document 契玄文曞の䜜成 put /documents/{documentID}/attribute 契玄文曞の䜜成で蚭定できない、现かい項目の蚭定 post /documents/{documentID}/files ファむルのアップロヌド post /documents/{documentID}/participants 先方の送信先蚭定 党䜓像で蚘茉したクラりドサむンの連携郚に぀いお、䞊蚘の API を織り亀ぜお詳现化するず䞋図のようになりたす。 実装方法ずしおはクラりドサむン API のリファレンスを参照し、テスト実行時に出力される curl コマンドを参考に同様のレスポンスを埗るように Apex で HTTP リク゚ストを実装したした。アクセストヌクンの取埗を䟋にずるず䞋蚘のようになりたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/token' \ -H 'accept: application/json' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'client_id=xxxxxxyyyyyyzzzzzz' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/token' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Content-Type' , 'application/x-www-form-urlencoded' ); req . setBody ( 'client_id=' + 'xxxxxxyyyyyyzzzzzz' ); 私自身、Apex もクラりドサむン API もこの案件を担圓するたで觊ったこずがありたせんでしたが、リク゚ストの詊行から実装たで 2 週間かからない皋床で実装するこずができたした。 ただし、実装や運甚にあたっおは䞋蚘 2 点に぀いお泚意が必芁になりたす。 Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる 1. Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない ファむルのアップロヌドに぀いおは今回䜿甚した API の䞭で、唯䞀、テスト実行の curl ず Apex のリク゚スト実装で差分が生たれたす。たず、その差分を確認するために curl コマンド䟋ず Apex のリク゚スト実装䟋で header、body にセットしおいる倀を比范しおみたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/documents/{document_id}/files' \ -H 'accept: application/json' \ -H 'Authorization: AAAAAABBBBBBCCCCCC' \ -H 'Content-Type: multipart/form-data' \ -F 'name=テスト' \ -F 'uploadfile=@テスト.pdf;type=application/pdf' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/documents/{document_id}/files' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Authorization' , ‘AAAAAABBBBBBCCCCCC’); req . setHeader ( 'Content-Type' , 'multipart/form-data; boundary={boundary}' ); // ※1 req . setBodyAsBlob ({multipartBody}); // ※2 䞻な違いは ※1 , ※2 ずコメントした郚分になりたす。 Apex では HTTP リク゚ストの倀を手で曞いおいくこずになるので、テスト実行䟋のように curl がよしなに凊理しおいる郚分(-F オプションの郚分や Apex で蚘茉しおいる boundary)も実装しなければなりたせん。これが単玔に実装できない理由になりたす。 boundary に぀いおは multipart/form-data を送信する際に必芁な境界でヘッダヌでどの文字列が境界であるかを蚭定したす。 curl の-F オプションで定矩しおいた文字列ずファむル指定郚分は、Apex でファむル(バむナリ)を扱うため、その body に含たれる文字列も含めお Blob 型で扱う必芁がありたす( Content-Transfer-Encoding: base64 に API 提䟛偎が察応しおいる堎合は䟋倖になりたす)。そのため、文字列ずバむナリデヌタを結合し䞀぀の Blob にする方法は䞋蚘になりたす。 「バむナリデヌタ」、「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ以降から終端たでの文字列」の 3 グルヌプに分ける。 3 グルヌプをそれぞれ Base64 で笊号化する。 笊号化した「バむナリデヌタ」ず「body の開始からバむナリデヌタたでの文字列」に぀いお、Base64 のデヌタパディングを瀺す”=”が含たれないように改行コヌドで調敎する。 「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ」、「バむナリデヌタ以降から終端たでの文字列」の順で結合する。 結合した Base64 のデヌタを埩号しお、䞀぀の Blob ずする。 2. アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる アクセストヌクンやその有効期限は token API を発行した際のレスポンスずしおクラりドサむンから発行されたす。 発行されたレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 3600 } このレスポンスの内、expires_in の倀がトヌクンの有効期限になりたす。掲題の通り、有効期限の管理はクラりドサむン偎で行われ、有効期限内に再床トヌクンのリク゚ストを行った堎合、経過した時間だけ expires_in の倀が小さくなった結果が返っおきお、access_token などは同じ倀が取埗されたす。有効期限内に token API を再床実行した結果が䞋蚘になりたす。 有効期限切れ前に token API を発行した際のレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 762 } 䞀方、有効期限埌にトヌクンのリク゚ストを実行するず、それたでず異なるアクセストヌクンを取埗し、新しい有効期限が蚭定されたす。 有効期限切れ埌に token API を発行した際のレスポンス䟋 { "access_token" : "XXXXXXYYYYYYZZZZZZ" , "token_type" : "xxxx" , "expires_in" : 3600 } そのため、API 連携が䞀床動いた埌、有効期限ぎりぎりでもう䞀床 API 連携が動いおしたった堎合、タむミングが悪いず契玄文曞の䜜成から最終凊理である先方の送信先蚭定たでのプロセス内のどこかから、トヌクンの有効期限切れが発生する可胜性が想定されたす。実際に期限切れが発生した堎合、発生時以降に発行したその回の API 連携凊理が倱敗したす。 トヌクンの有効期限切れが発生した際、API リファレンスより HTTP ステヌタスコヌドが 401 か぀゚ラヌ内容が”unauthorized”で応答されるこずから、圓瀟ではこの゚ラヌを受けた堎合にトヌクンを再取埗しお凊理をリトラむするように実装したした。 抌印文曞䜜成を䟋にずるず䞋蚘のような実装むメヌゞになりたす。 //クラりドサむン䞊に抌印文曞を䜜成し、䜜成した文曞 ID を取埗する public String getDocumentId ( String authToken, String title, String message){ ・・・䞭略・・・ HTTPResponse res = http . send (req); if ( res . getStatusCode () == 200 ){ ・・・正垞に終了した際の凊理・・・ } //タむミングが悪く token がタむムアりトした堎合、トヌクンを取埗し盎しお、リトラむする else if ( res . getStatusCode () == 401 ){ //レスポンスの内容を確認するため、゚ラヌレスポンスの䞭身を取埗する Map < String , Object > responseBody = new Map < String , Object >(); responseBody = ( Map <String, Object>) JSON . deserializeUntyped ( res . getBody ()); String errorVal = (String) responseBody . get ( 'error' ); //リファレンス䞊、アクセストヌクンが無効(有効期限切れ)の堎合、'unauthorized’ずなる if ( errorVal . equals ( 'unauthorized' )){ //クラりドサむンのアクセストヌクンの再取埗 authToken = getAuthToken (); //単玔再垰で再実行する。 documentId = getDocumentId (authToken, title, message); } ・・・䞭略・・・ } ・・・以䞋省略・・・ } 実装を終えお 䞊蚘を実装した結果、皟議ず入力内容が同じ、たたは、皟議から生成できる内容は党おシステム連携で自動生成するため、抌印担圓は皟議ずクラりドサむンの画面を䞊べお転蚘するような煩雑な䜜業を必芁ずしない環境になりたした。たた、契玄曞の補本、郵送等の玙媒䜓であるが故の事務の削枛ができるようになる等の、電子契玄を導入するこずのそもそものメリットも䜵せお享受しおいたす。 圓瀟では 2021 幎 4 月埌半からクラりドサむンを導入したしたが、2021 幎 6 月時点ではすでに 月間で締結した契玄曞の「3 割以䞊」が電子契玄を掻甚しおおり 、抌印担圓の展望ずしお今埌も利甚を拡倧しおいく予定です。 コヌポレヌト゚ンゞニア募集䞭 メドレヌのコヌポレヌト郚門では、本皿のように、SaaS の導入ひず぀ずっおも怜蚎を尜くし、既存のシステムず有機的に結合させるこずで「培底的に合理性を远求した組織基盀や、仕掛けづくり」を行っおいたす。 面癜そう興味があるず感じた方は、ぜひ圓瀟採甚ペヌゞからご応募お願いしたす 最埌たでお読みいただきありがずうございたした。 https://www.medley.jp/jobs/
はじめに はじめたしお、コヌポレヌト゚ンゞニアの山䞋です。 2020 幎に Slack を掻甚した ChatOps 皟議ワヌクフロヌを内補で開発したのですが、さらに、2021 幎 4 月にこの Slack 皟議ず電子契玄システムである クラりドサむン を連携させお、電子契玄をもっず䟿利に䜿い、生産性の向䞊を実珟したしたのでお話しいたしたす。 たず、圓瀟の皟議システムは 2020 幎 12 月の圓瀟の蚘事 のおさらいになりたすが、皟議の䜜業が Slack 䞊で完結する、 ChatOps による皟議ワヌクフロヌ ずなっおおりたす。本皿に぀いおは 2021 幎 7 月に執筆しおおりたすので䞁床導入から 1 幎皋経過し、その間倧きなトラブルも無く、今も圓瀟の極めお迅速な意思決定の䞀助になっおいたす。ChatOps による皟議ワヌクフロヌに぀いおは、盎近、2021 幎 6 月に LayerX 瀟が LayerX ワヌクフロヌの新機胜ずしお発衚 し、サヌビスずしおも提䟛され、 日経新聞 でも取り䞊げられおいるこずから、今珟圚のパラダむムずしお、先進的で有効な䞀手法であったず再認識しおおりたす。 今回、新型コロナりむルス感染拡倧防止に䌎うリモヌトワヌクの加速ずいう状況もあり、圓瀟で 2021 幎 4 月に電子契玄システムずしおクラりドサむンを導入したした。電子契玄に限らず、契玄抌印䜜業は皟議の埌続䜜業に圓たるため、ただ導入しお䜿甚するのみならず、クラりドサむンの API を利甚しお皟議䞊にあるデヌタを電子契玄に送信させるこずでシヌムレスな連携を実珟しおいたす。本皿では圓瀟が行ったシヌムレスな連携手法に぀いお詳现をご説明いたしたす。 TeamSpirit ずクラりドサむンの API 連携に぀いお 実装抂芁 匊瀟の皟議システムである TeamSpirit ずクラりドサむンずの連携に぀いおお話ししたす。たず、本皿の開発郚分ずシステム構成は䞋蚘になっおおりたす。 凊理内容の詳现は埌ほど述べたすが、抂芁ずしおは TeamSprit(Apex)からクラりドサむンの API をコヌルし、クラりドサむン䞊で䜜成した契玄文曞ぞ皟議に蚘茉されおいる契玄曞ファむルや先方担圓者等の情報を連携する仕組みずなっおおりたす。これにより契玄担圓者はクラりドサむンにログむン埌、䞋蚘の 3 ステップで先方に送信できるようになっおいたす。 蚘茉内容の確認 抌印・眲名箇所の蚭定 先方ぞの送信 クラりドサむンを䜿甚しお契玄文曞を䞀から䜜成する堎合のナヌザ䜜業ず、圓瀟で採甚した API 連携行った堎合のナヌザ䜜業を比范したものが䞋蚘の衚です。䜜業が半分皋床削枛されたこずが分かりたす。 䜜業項番 䞀から䜜成する堎合 API 連携を利甚した圓瀟の堎合 1 ログむン ログむン 2 契玄文曞の䜜成(件名、契玄文曞ずしおの宛名蚭定等) なし 3 契玄曞ファむルのアップロヌド なし 4 先方の送信先蚭定 なし 5 抌印欄の蚭定 抌印欄の蚭定 6 先方ぞの送信 先方ぞの送信 実装 今回の開発で䜿甚した クラりドサむン API は䞋蚘の 5 ぀の API を䜿甚したした (※以降、クラりドサむン API に倣い、倉数を衚珟する堎合は {} で括りたす)。 API 皮類 䜿甚甚途 post /token アクセストヌクンの取埗 post /document 契玄文曞の䜜成 put /documents/{documentID}/attribute 契玄文曞の䜜成で蚭定できない、现かい項目の蚭定 post /documents/{documentID}/files ファむルのアップロヌド post /documents/{documentID}/participants 先方の送信先蚭定 党䜓像で蚘茉したクラりドサむンの連携郚に぀いお、䞊蚘の API を織り亀ぜお詳现化するず䞋図のようになりたす。 実装方法ずしおはクラりドサむン API のリファレンスを参照し、テスト実行時に出力される curl コマンドを参考に同様のレスポンスを埗るように Apex で HTTP リク゚ストを実装したした。アクセストヌクンの取埗を䟋にずるず䞋蚘のようになりたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/token' \ -H 'accept: application/json' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'client_id=xxxxxxyyyyyyzzzzzz' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/token' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Content-Type' , 'application/x-www-form-urlencoded' ); req . setBody ( 'client_id=' + 'xxxxxxyyyyyyzzzzzz' ); 私自身、Apex もクラりドサむン API もこの案件を担圓するたで觊ったこずがありたせんでしたが、リク゚ストの詊行から実装たで 2 週間かからない皋床で実装するこずができたした。 ただし、実装や運甚にあたっおは䞋蚘 2 点に぀いお泚意が必芁になりたす。 Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる 1. Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない ファむルのアップロヌドに぀いおは今回䜿甚した API の䞭で、唯䞀、テスト実行の curl ず Apex のリク゚スト実装で差分が生たれたす。たず、その差分を確認するために curl コマンド䟋ず Apex のリク゚スト実装䟋で header、body にセットしおいる倀を比范しおみたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/documents/{document_id}/files' \ -H 'accept: application/json' \ -H 'Authorization: AAAAAABBBBBBCCCCCC' \ -H 'Content-Type: multipart/form-data' \ -F 'name=テスト' \ -F 'uploadfile=@テスト.pdf;type=application/pdf' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/documents/{document_id}/files' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Authorization' , ‘AAAAAABBBBBBCCCCCC’); req . setHeader ( 'Content-Type' , 'multipart/form-data; boundary={boundary}' ); // ※1 req . setBodyAsBlob ({multipartBody}); // ※2 䞻な違いは ※1 , ※2 ずコメントした郚分になりたす。 Apex では HTTP リク゚ストの倀を手で曞いおいくこずになるので、テスト実行䟋のように curl がよしなに凊理しおいる郚分(-F オプションの郚分や Apex で蚘茉しおいる boundary)も実装しなければなりたせん。これが単玔に実装できない理由になりたす。 boundary に぀いおは multipart/form-data を送信する際に必芁な境界でヘッダヌでどの文字列が境界であるかを蚭定したす。 curl の-F オプションで定矩しおいた文字列ずファむル指定郚分は、Apex でファむル(バむナリ)を扱うため、その body に含たれる文字列も含めお Blob 型で扱う必芁がありたす( Content-Transfer-Encoding: base64 に API 提䟛偎が察応しおいる堎合は䟋倖になりたす)。そのため、文字列ずバむナリデヌタを結合し䞀぀の Blob にする方法は䞋蚘になりたす。 「バむナリデヌタ」、「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ以降から終端たでの文字列」の 3 グルヌプに分ける。 3 グルヌプをそれぞれ Base64 で笊号化する。 笊号化した「バむナリデヌタ」ず「body の開始からバむナリデヌタたでの文字列」に぀いお、Base64 のデヌタパディングを瀺す”=”が含たれないように改行コヌドで調敎する。 「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ」、「バむナリデヌタ以降から終端たでの文字列」の順で結合する。 結合した Base64 のデヌタを埩号しお、䞀぀の Blob ずする。 2. アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる アクセストヌクンやその有効期限は token API を発行した際のレスポンスずしおクラりドサむンから発行されたす。 発行されたレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 3600 } このレスポンスの内、expires_in の倀がトヌクンの有効期限になりたす。掲題の通り、有効期限の管理はクラりドサむン偎で行われ、有効期限内に再床トヌクンのリク゚ストを行った堎合、経過した時間だけ expires_in の倀が小さくなった結果が返っおきお、access_token などは同じ倀が取埗されたす。有効期限内に token API を再床実行した結果が䞋蚘になりたす。 有効期限切れ前に token API を発行した際のレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 762 } 䞀方、有効期限埌にトヌクンのリク゚ストを実行するず、それたでず異なるアクセストヌクンを取埗し、新しい有効期限が蚭定されたす。 有効期限切れ埌に token API を発行した際のレスポンス䟋 { "access_token" : "XXXXXXYYYYYYZZZZZZ" , "token_type" : "xxxx" , "expires_in" : 3600 } そのため、API 連携が䞀床動いた埌、有効期限ぎりぎりでもう䞀床 API 連携が動いおしたった堎合、タむミングが悪いず契玄文曞の䜜成から最終凊理である先方の送信先蚭定たでのプロセス内のどこかから、トヌクンの有効期限切れが発生する可胜性が想定されたす。実際に期限切れが発生した堎合、発生時以降に発行したその回の API 連携凊理が倱敗したす。 トヌクンの有効期限切れが発生した際、API リファレンスより HTTP ステヌタスコヌドが 401 か぀゚ラヌ内容が”unauthorized”で応答されるこずから、圓瀟ではこの゚ラヌを受けた堎合にトヌクンを再取埗しお凊理をリトラむするように実装したした。 抌印文曞䜜成を䟋にずるず䞋蚘のような実装むメヌゞになりたす。 //クラりドサむン䞊に抌印文曞を䜜成し、䜜成した文曞 ID を取埗する public String getDocumentId ( String authToken, String title, String message){ ・・・䞭略・・・ HTTPResponse res = http . send (req); if ( res . getStatusCode () == 200 ){ ・・・正垞に終了した際の凊理・・・ } //タむミングが悪く token がタむムアりトした堎合、トヌクンを取埗し盎しお、リトラむする else if ( res . getStatusCode () == 401 ){ //レスポンスの内容を確認するため、゚ラヌレスポンスの䞭身を取埗する Map < String , Object > responseBody = new Map < String , Object >(); responseBody = ( Map <String, Object>) JSON . deserializeUntyped ( res . getBody ()); String errorVal = (String) responseBody . get ( 'error' ); //リファレンス䞊、アクセストヌクンが無効(有効期限切れ)の堎合、'unauthorized’ずなる if ( errorVal . equals ( 'unauthorized' )){ //クラりドサむンのアクセストヌクンの再取埗 authToken = getAuthToken (); //単玔再垰で再実行する。 documentId = getDocumentId (authToken, title, message); } ・・・䞭略・・・ } ・・・以䞋省略・・・ } 実装を終えお 䞊蚘を実装した結果、皟議ず入力内容が同じ、たたは、皟議から生成できる内容は党おシステム連携で自動生成するため、抌印担圓は皟議ずクラりドサむンの画面を䞊べお転蚘するような煩雑な䜜業を必芁ずしない環境になりたした。たた、契玄曞の補本、郵送等の玙媒䜓であるが故の事務の削枛ができるようになる等の、電子契玄を導入するこずのそもそものメリットも䜵せお享受しおいたす。 圓瀟では 2021 幎 4 月埌半からクラりドサむンを導入したしたが、2021 幎 6 月時点ではすでに 月間で締結した契玄曞の「3 割以䞊」が電子契玄を掻甚しおおり 、抌印担圓の展望ずしお今埌も利甚を拡倧しおいく予定です。 コヌポレヌト゚ンゞニア募集䞭 メドレヌのコヌポレヌト郚門では、本皿のように、SaaS の導入ひず぀ずっおも怜蚎を尜くし、既存のシステムず有機的に結合させるこずで「培底的に合理性を远求した組織基盀や、仕掛けづくり」を行っおいたす。 面癜そう興味があるず感じた方は、ぜひ圓瀟採甚ペヌゞからご応募お願いしたす 最埌たでお読みいただきありがずうございたした。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
はじめに はじめたしお、コヌポレヌト゚ンゞニアの山䞋です。 2020 幎に Slack を掻甚した ChatOps 皟議ワヌクフロヌを内補で開発したのですが、さらに、2021 幎 4 月にこの Slack 皟議ず電子契玄システムである クラりドサむン を連携させお、電子契玄をもっず䟿利に䜿い、生産性の向䞊を実珟したしたのでお話しいたしたす。 たず、圓瀟の皟議システムは 2020 幎 12 月の圓瀟の蚘事 のおさらいになりたすが、皟議の䜜業が Slack 䞊で完結する、 ChatOps による皟議ワヌクフロヌ ずなっおおりたす。本皿に぀いおは 2021 幎 7 月に執筆しおおりたすので䞁床導入から 1 幎皋経過し、その間倧きなトラブルも無く、今も圓瀟の極めお迅速な意思決定の䞀助になっおいたす。ChatOps による皟議ワヌクフロヌに぀いおは、盎近、2021 幎 6 月に LayerX 瀟が LayerX ワヌクフロヌの新機胜ずしお発衚 し、サヌビスずしおも提䟛され、 日経新聞 でも取り䞊げられおいるこずから、今珟圚のパラダむムずしお、先進的で有効な䞀手法であったず再認識しおおりたす。 今回、新型コロナりむルス感染拡倧防止に䌎うリモヌトワヌクの加速ずいう状況もあり、圓瀟で 2021 幎 4 月に電子契玄システムずしおクラりドサむンを導入したした。電子契玄に限らず、契玄抌印䜜業は皟議の埌続䜜業に圓たるため、ただ導入しお䜿甚するのみならず、クラりドサむンの API を利甚しお皟議䞊にあるデヌタを電子契玄に送信させるこずでシヌムレスな連携を実珟しおいたす。本皿では圓瀟が行ったシヌムレスな連携手法に぀いお詳现をご説明いたしたす。 TeamSpirit ずクラりドサむンの API 連携に぀いお 実装抂芁 匊瀟の皟議システムである TeamSpirit ずクラりドサむンずの連携に぀いおお話ししたす。たず、本皿の開発郚分ずシステム構成は䞋蚘になっおおりたす。 凊理内容の詳现は埌ほど述べたすが、抂芁ずしおは TeamSprit(Apex)からクラりドサむンの API をコヌルし、クラりドサむン䞊で䜜成した契玄文曞ぞ皟議に蚘茉されおいる契玄曞ファむルや先方担圓者等の情報を連携する仕組みずなっおおりたす。これにより契玄担圓者はクラりドサむンにログむン埌、䞋蚘の 3 ステップで先方に送信できるようになっおいたす。 蚘茉内容の確認 抌印・眲名箇所の蚭定 先方ぞの送信 クラりドサむンを䜿甚しお契玄文曞を䞀から䜜成する堎合のナヌザ䜜業ず、圓瀟で採甚した API 連携行った堎合のナヌザ䜜業を比范したものが䞋蚘の衚です。䜜業が半分皋床削枛されたこずが分かりたす。 䜜業項番 䞀から䜜成する堎合 API 連携を利甚した圓瀟の堎合 1 ログむン ログむン 2 契玄文曞の䜜成(件名、契玄文曞ずしおの宛名蚭定等) なし 3 契玄曞ファむルのアップロヌド なし 4 先方の送信先蚭定 なし 5 抌印欄の蚭定 抌印欄の蚭定 6 先方ぞの送信 先方ぞの送信 実装 今回の開発で䜿甚した クラりドサむン API は䞋蚘の 5 ぀の API を䜿甚したした (※以降、クラりドサむン API に倣い、倉数を衚珟する堎合は {} で括りたす)。 API 皮類 䜿甚甚途 post /token アクセストヌクンの取埗 post /document 契玄文曞の䜜成 put /documents/{documentID}/attribute 契玄文曞の䜜成で蚭定できない、现かい項目の蚭定 post /documents/{documentID}/files ファむルのアップロヌド post /documents/{documentID}/participants 先方の送信先蚭定 党䜓像で蚘茉したクラりドサむンの連携郚に぀いお、䞊蚘の API を織り亀ぜお詳现化するず䞋図のようになりたす。 実装方法ずしおはクラりドサむン API のリファレンスを参照し、テスト実行時に出力される curl コマンドを参考に同様のレスポンスを埗るように Apex で HTTP リク゚ストを実装したした。アクセストヌクンの取埗を䟋にずるず䞋蚘のようになりたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/token' \ -H 'accept: application/json' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'client_id=xxxxxxyyyyyyzzzzzz' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/token' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Content-Type' , 'application/x-www-form-urlencoded' ); req . setBody ( 'client_id=' + 'xxxxxxyyyyyyzzzzzz' ); 私自身、Apex もクラりドサむン API もこの案件を担圓するたで觊ったこずがありたせんでしたが、リク゚ストの詊行から実装たで 2 週間かからない皋床で実装するこずができたした。 ただし、実装や運甚にあたっおは䞋蚘 2 点に぀いお泚意が必芁になりたす。 Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる 1. Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない ファむルのアップロヌドに぀いおは今回䜿甚した API の䞭で、唯䞀、テスト実行の curl ず Apex のリク゚スト実装で差分が生たれたす。たず、その差分を確認するために curl コマンド䟋ず Apex のリク゚スト実装䟋で header、body にセットしおいる倀を比范しおみたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/documents/{document_id}/files' \ -H 'accept: application/json' \ -H 'Authorization: AAAAAABBBBBBCCCCCC' \ -H 'Content-Type: multipart/form-data' \ -F 'name=テスト' \ -F 'uploadfile=@テスト.pdf;type=application/pdf' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/documents/{document_id}/files' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Authorization' , ‘AAAAAABBBBBBCCCCCC’); req . setHeader ( 'Content-Type' , 'multipart/form-data; boundary={boundary}' ); // ※1 req . setBodyAsBlob ({multipartBody}); // ※2 䞻な違いは ※1 , ※2 ずコメントした郚分になりたす。 Apex では HTTP リク゚ストの倀を手で曞いおいくこずになるので、テスト実行䟋のように curl がよしなに凊理しおいる郚分(-F オプションの郚分や Apex で蚘茉しおいる boundary)も実装しなければなりたせん。これが単玔に実装できない理由になりたす。 boundary に぀いおは multipart/form-data を送信する際に必芁な境界でヘッダヌでどの文字列が境界であるかを蚭定したす。 curl の-F オプションで定矩しおいた文字列ずファむル指定郚分は、Apex でファむル(バむナリ)を扱うため、その body に含たれる文字列も含めお Blob 型で扱う必芁がありたす( Content-Transfer-Encoding: base64 に API 提䟛偎が察応しおいる堎合は䟋倖になりたす)。そのため、文字列ずバむナリデヌタを結合し䞀぀の Blob にする方法は䞋蚘になりたす。 「バむナリデヌタ」、「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ以降から終端たでの文字列」の 3 グルヌプに分ける。 3 グルヌプをそれぞれ Base64 で笊号化する。 笊号化した「バむナリデヌタ」ず「body の開始からバむナリデヌタたでの文字列」に぀いお、Base64 のデヌタパディングを瀺す”=”が含たれないように改行コヌドで調敎する。 「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ」、「バむナリデヌタ以降から終端たでの文字列」の順で結合する。 結合した Base64 のデヌタを埩号しお、䞀぀の Blob ずする。 2. アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる アクセストヌクンやその有効期限は token API を発行した際のレスポンスずしおクラりドサむンから発行されたす。 発行されたレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 3600 } このレスポンスの内、expires_in の倀がトヌクンの有効期限になりたす。掲題の通り、有効期限の管理はクラりドサむン偎で行われ、有効期限内に再床トヌクンのリク゚ストを行った堎合、経過した時間だけ expires_in の倀が小さくなった結果が返っおきお、access_token などは同じ倀が取埗されたす。有効期限内に token API を再床実行した結果が䞋蚘になりたす。 有効期限切れ前に token API を発行した際のレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 762 } 䞀方、有効期限埌にトヌクンのリク゚ストを実行するず、それたでず異なるアクセストヌクンを取埗し、新しい有効期限が蚭定されたす。 有効期限切れ埌に token API を発行した際のレスポンス䟋 { "access_token" : "XXXXXXYYYYYYZZZZZZ" , "token_type" : "xxxx" , "expires_in" : 3600 } そのため、API 連携が䞀床動いた埌、有効期限ぎりぎりでもう䞀床 API 連携が動いおしたった堎合、タむミングが悪いず契玄文曞の䜜成から最終凊理である先方の送信先蚭定たでのプロセス内のどこかから、トヌクンの有効期限切れが発生する可胜性が想定されたす。実際に期限切れが発生した堎合、発生時以降に発行したその回の API 連携凊理が倱敗したす。 トヌクンの有効期限切れが発生した際、API リファレンスより HTTP ステヌタスコヌドが 401 か぀゚ラヌ内容が”unauthorized”で応答されるこずから、圓瀟ではこの゚ラヌを受けた堎合にトヌクンを再取埗しお凊理をリトラむするように実装したした。 抌印文曞䜜成を䟋にずるず䞋蚘のような実装むメヌゞになりたす。 //クラりドサむン䞊に抌印文曞を䜜成し、䜜成した文曞 ID を取埗する public String getDocumentId ( String authToken, String title, String message){ ・・・䞭略・・・ HTTPResponse res = http . send (req); if ( res . getStatusCode () == 200 ){ ・・・正垞に終了した際の凊理・・・ } //タむミングが悪く token がタむムアりトした堎合、トヌクンを取埗し盎しお、リトラむする else if ( res . getStatusCode () == 401 ){ //レスポンスの内容を確認するため、゚ラヌレスポンスの䞭身を取埗する Map < String , Object > responseBody = new Map < String , Object >(); responseBody = ( Map <String, Object>) JSON . deserializeUntyped ( res . getBody ()); String errorVal = (String) responseBody . get ( 'error' ); //リファレンス䞊、アクセストヌクンが無効(有効期限切れ)の堎合、'unauthorized’ずなる if ( errorVal . equals ( 'unauthorized' )){ //クラりドサむンのアクセストヌクンの再取埗 authToken = getAuthToken (); //単玔再垰で再実行する。 documentId = getDocumentId (authToken, title, message); } ・・・䞭略・・・ } ・・・以䞋省略・・・ } 実装を終えお 䞊蚘を実装した結果、皟議ず入力内容が同じ、たたは、皟議から生成できる内容は党おシステム連携で自動生成するため、抌印担圓は皟議ずクラりドサむンの画面を䞊べお転蚘するような煩雑な䜜業を必芁ずしない環境になりたした。たた、契玄曞の補本、郵送等の玙媒䜓であるが故の事務の削枛ができるようになる等の、電子契玄を導入するこずのそもそものメリットも䜵せお享受しおいたす。 圓瀟では 2021 幎 4 月埌半からクラりドサむンを導入したしたが、2021 幎 6 月時点ではすでに 月間で締結した契玄曞の「3 割以䞊」が電子契玄を掻甚しおおり 、抌印担圓の展望ずしお今埌も利甚を拡倧しおいく予定です。 コヌポレヌト゚ンゞニア募集䞭 メドレヌのコヌポレヌト郚門では、本皿のように、SaaS の導入ひず぀ずっおも怜蚎を尜くし、既存のシステムず有機的に結合させるこずで「培底的に合理性を远求した組織基盀や、仕掛けづくり」を行っおいたす。 面癜そう興味があるず感じた方は、ぜひ圓瀟採甚ペヌゞからご応募お願いしたす 最埌たでお読みいただきありがずうございたした。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
はじめに はじめたしお、コヌポレヌト゚ンゞニアの山䞋です。 2020 幎に Slack を掻甚した ChatOps 皟議ワヌクフロヌを内補で開発したのですが、さらに、2021 幎 4 月にこの Slack 皟議ず電子契玄システムである クラりドサむン を連携させお、電子契玄をもっず䟿利に䜿い、生産性の向䞊を実珟したしたのでお話しいたしたす。 たず、圓瀟の皟議システムは 2020 幎 12 月の圓瀟の蚘事 のおさらいになりたすが、皟議の䜜業が Slack 䞊で完結する、 ChatOps による皟議ワヌクフロヌ ずなっおおりたす。本皿に぀いおは 2021 幎 7 月に執筆しおおりたすので䞁床導入から 1 幎皋経過し、その間倧きなトラブルも無く、今も圓瀟の極めお迅速な意思決定の䞀助になっおいたす。ChatOps による皟議ワヌクフロヌに぀いおは、盎近、2021 幎 6 月に LayerX 瀟が LayerX ワヌクフロヌの新機胜ずしお発衚 し、サヌビスずしおも提䟛され、 日経新聞 でも取り䞊げられおいるこずから、今珟圚のパラダむムずしお、先進的で有効な䞀手法であったず再認識しおおりたす。 今回、新型コロナりむルス感染拡倧防止に䌎うリモヌトワヌクの加速ずいう状況もあり、圓瀟で 2021 幎 4 月に電子契玄システムずしおクラりドサむンを導入したした。電子契玄に限らず、契玄抌印䜜業は皟議の埌続䜜業に圓たるため、ただ導入しお䜿甚するのみならず、クラりドサむンの API を利甚しお皟議䞊にあるデヌタを電子契玄に送信させるこずでシヌムレスな連携を実珟しおいたす。本皿では圓瀟が行ったシヌムレスな連携手法に぀いお詳现をご説明いたしたす。 TeamSpirit ずクラりドサむンの API 連携に぀いお 実装抂芁 匊瀟の皟議システムである TeamSpirit ずクラりドサむンずの連携に぀いおお話ししたす。たず、本皿の開発郚分ずシステム構成は䞋蚘になっおおりたす。 凊理内容の詳现は埌ほど述べたすが、抂芁ずしおは TeamSprit(Apex)からクラりドサむンの API をコヌルし、クラりドサむン䞊で䜜成した契玄文曞ぞ皟議に蚘茉されおいる契玄曞ファむルや先方担圓者等の情報を連携する仕組みずなっおおりたす。これにより契玄担圓者はクラりドサむンにログむン埌、䞋蚘の 3 ステップで先方に送信できるようになっおいたす。 蚘茉内容の確認 抌印・眲名箇所の蚭定 先方ぞの送信 クラりドサむンを䜿甚しお契玄文曞を䞀から䜜成する堎合のナヌザ䜜業ず、圓瀟で採甚した API 連携行った堎合のナヌザ䜜業を比范したものが䞋蚘の衚です。䜜業が半分皋床削枛されたこずが分かりたす。 䜜業項番 䞀から䜜成する堎合 API 連携を利甚した圓瀟の堎合 1 ログむン ログむン 2 契玄文曞の䜜成(件名、契玄文曞ずしおの宛名蚭定等) なし 3 契玄曞ファむルのアップロヌド なし 4 先方の送信先蚭定 なし 5 抌印欄の蚭定 抌印欄の蚭定 6 先方ぞの送信 先方ぞの送信 実装 今回の開発で䜿甚した クラりドサむン API は䞋蚘の 5 ぀の API を䜿甚したした (※以降、クラりドサむン API に倣い、倉数を衚珟する堎合は {} で括りたす)。 API 皮類 䜿甚甚途 post /token アクセストヌクンの取埗 post /document 契玄文曞の䜜成 put /documents/{documentID}/attribute 契玄文曞の䜜成で蚭定できない、现かい項目の蚭定 post /documents/{documentID}/files ファむルのアップロヌド post /documents/{documentID}/participants 先方の送信先蚭定 党䜓像で蚘茉したクラりドサむンの連携郚に぀いお、䞊蚘の API を織り亀ぜお詳现化するず䞋図のようになりたす。 実装方法ずしおはクラりドサむン API のリファレンスを参照し、テスト実行時に出力される curl コマンドを参考に同様のレスポンスを埗るように Apex で HTTP リク゚ストを実装したした。アクセストヌクンの取埗を䟋にずるず䞋蚘のようになりたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/token' \ -H 'accept: application/json' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'client_id=xxxxxxyyyyyyzzzzzz' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/token' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Content-Type' , 'application/x-www-form-urlencoded' ); req . setBody ( 'client_id=' + 'xxxxxxyyyyyyzzzzzz' ); 私自身、Apex もクラりドサむン API もこの案件を担圓するたで觊ったこずがありたせんでしたが、リク゚ストの詊行から実装たで 2 週間かからない皋床で実装するこずができたした。 ただし、実装や運甚にあたっおは䞋蚘 2 点に぀いお泚意が必芁になりたす。 Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる 1. Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない ファむルのアップロヌドに぀いおは今回䜿甚した API の䞭で、唯䞀、テスト実行の curl ず Apex のリク゚スト実装で差分が生たれたす。たず、その差分を確認するために curl コマンド䟋ず Apex のリク゚スト実装䟋で header、body にセットしおいる倀を比范しおみたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/documents/{document_id}/files' \ -H 'accept: application/json' \ -H 'Authorization: AAAAAABBBBBBCCCCCC' \ -H 'Content-Type: multipart/form-data' \ -F 'name=テスト' \ -F 'uploadfile=@テスト.pdf;type=application/pdf' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/documents/{document_id}/files' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Authorization' , ‘AAAAAABBBBBBCCCCCC’); req . setHeader ( 'Content-Type' , 'multipart/form-data; boundary={boundary}' ); // ※1 req . setBodyAsBlob ({multipartBody}); // ※2 䞻な違いは ※1 , ※2 ずコメントした郚分になりたす。 Apex では HTTP リク゚ストの倀を手で曞いおいくこずになるので、テスト実行䟋のように curl がよしなに凊理しおいる郚分(-F オプションの郚分や Apex で蚘茉しおいる boundary)も実装しなければなりたせん。これが単玔に実装できない理由になりたす。 boundary に぀いおは multipart/form-data を送信する際に必芁な境界でヘッダヌでどの文字列が境界であるかを蚭定したす。 curl の-F オプションで定矩しおいた文字列ずファむル指定郚分は、Apex でファむル(バむナリ)を扱うため、その body に含たれる文字列も含めお Blob 型で扱う必芁がありたす( Content-Transfer-Encoding: base64 に API 提䟛偎が察応しおいる堎合は䟋倖になりたす)。そのため、文字列ずバむナリデヌタを結合し䞀぀の Blob にする方法は䞋蚘になりたす。 「バむナリデヌタ」、「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ以降から終端たでの文字列」の 3 グルヌプに分ける。 3 グルヌプをそれぞれ Base64 で笊号化する。 笊号化した「バむナリデヌタ」ず「body の開始からバむナリデヌタたでの文字列」に぀いお、Base64 のデヌタパディングを瀺す”=”が含たれないように改行コヌドで調敎する。 「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ」、「バむナリデヌタ以降から終端たでの文字列」の順で結合する。 結合した Base64 のデヌタを埩号しお、䞀぀の Blob ずする。 2. アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる アクセストヌクンやその有効期限は token API を発行した際のレスポンスずしおクラりドサむンから発行されたす。 発行されたレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 3600 } このレスポンスの内、expires_in の倀がトヌクンの有効期限になりたす。掲題の通り、有効期限の管理はクラりドサむン偎で行われ、有効期限内に再床トヌクンのリク゚ストを行った堎合、経過した時間だけ expires_in の倀が小さくなった結果が返っおきお、access_token などは同じ倀が取埗されたす。有効期限内に token API を再床実行した結果が䞋蚘になりたす。 有効期限切れ前に token API を発行した際のレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 762 } 䞀方、有効期限埌にトヌクンのリク゚ストを実行するず、それたでず異なるアクセストヌクンを取埗し、新しい有効期限が蚭定されたす。 有効期限切れ埌に token API を発行した際のレスポンス䟋 { "access_token" : "XXXXXXYYYYYYZZZZZZ" , "token_type" : "xxxx" , "expires_in" : 3600 } そのため、API 連携が䞀床動いた埌、有効期限ぎりぎりでもう䞀床 API 連携が動いおしたった堎合、タむミングが悪いず契玄文曞の䜜成から最終凊理である先方の送信先蚭定たでのプロセス内のどこかから、トヌクンの有効期限切れが発生する可胜性が想定されたす。実際に期限切れが発生した堎合、発生時以降に発行したその回の API 連携凊理が倱敗したす。 トヌクンの有効期限切れが発生した際、API リファレンスより HTTP ステヌタスコヌドが 401 か぀゚ラヌ内容が”unauthorized”で応答されるこずから、圓瀟ではこの゚ラヌを受けた堎合にトヌクンを再取埗しお凊理をリトラむするように実装したした。 抌印文曞䜜成を䟋にずるず䞋蚘のような実装むメヌゞになりたす。 //クラりドサむン䞊に抌印文曞を䜜成し、䜜成した文曞 ID を取埗する public String getDocumentId ( String authToken, String title, String message){ ・・・䞭略・・・ HTTPResponse res = http . send (req); if ( res . getStatusCode () == 200 ){ ・・・正垞に終了した際の凊理・・・ } //タむミングが悪く token がタむムアりトした堎合、トヌクンを取埗し盎しお、リトラむする else if ( res . getStatusCode () == 401 ){ //レスポンスの内容を確認するため、゚ラヌレスポンスの䞭身を取埗する Map < String , Object > responseBody = new Map < String , Object >(); responseBody = ( Map <String, Object>) JSON . deserializeUntyped ( res . getBody ()); String errorVal = (String) responseBody . get ( 'error' ); //リファレンス䞊、アクセストヌクンが無効(有効期限切れ)の堎合、'unauthorized’ずなる if ( errorVal . equals ( 'unauthorized' )){ //クラりドサむンのアクセストヌクンの再取埗 authToken = getAuthToken (); //単玔再垰で再実行する。 documentId = getDocumentId (authToken, title, message); } ・・・䞭略・・・ } ・・・以䞋省略・・・ } 実装を終えお 䞊蚘を実装した結果、皟議ず入力内容が同じ、たたは、皟議から生成できる内容は党おシステム連携で自動生成するため、抌印担圓は皟議ずクラりドサむンの画面を䞊べお転蚘するような煩雑な䜜業を必芁ずしない環境になりたした。たた、契玄曞の補本、郵送等の玙媒䜓であるが故の事務の削枛ができるようになる等の、電子契玄を導入するこずのそもそものメリットも䜵せお享受しおいたす。 圓瀟では 2021 幎 4 月埌半からクラりドサむンを導入したしたが、2021 幎 6 月時点ではすでに 月間で締結した契玄曞の「3 割以䞊」が電子契玄を掻甚しおおり 、抌印担圓の展望ずしお今埌も利甚を拡倧しおいく予定です。 コヌポレヌト゚ンゞニア募集䞭 メドレヌのコヌポレヌト郚門では、本皿のように、SaaS の導入ひず぀ずっおも怜蚎を尜くし、既存のシステムず有機的に結合させるこずで「培底的に合理性を远求した組織基盀や、仕掛けづくり」を行っおいたす。 面癜そう興味があるず感じた方は、ぜひ圓瀟採甚ペヌゞからご応募お願いしたす 最埌たでお読みいただきありがずうございたした。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
はじめに はじめたしお、コヌポレヌト゚ンゞニアの山䞋です。 2020 幎に Slack を掻甚した ChatOps 皟議ワヌクフロヌを内補で開発したのですが、さらに、2021 幎 4 月にこの Slack 皟議ず電子契玄システムである クラりドサむン を連携させお、電子契玄をもっず䟿利に䜿い、生産性の向䞊を実珟したしたのでお話しいたしたす。 たず、圓瀟の皟議システムは 2020 幎 12 月の圓瀟の蚘事 のおさらいになりたすが、皟議の䜜業が Slack 䞊で完結する、 ChatOps による皟議ワヌクフロヌ ずなっおおりたす。本皿に぀いおは 2021 幎 7 月に執筆しおおりたすので䞁床導入から 1 幎皋経過し、その間倧きなトラブルも無く、今も圓瀟の極めお迅速な意思決定の䞀助になっおいたす。ChatOps による皟議ワヌクフロヌに぀いおは、盎近、2021 幎 6 月に LayerX 瀟が LayerX ワヌクフロヌの新機胜ずしお発衚 し、サヌビスずしおも提䟛され、 日経新聞 でも取り䞊げられおいるこずから、今珟圚のパラダむムずしお、先進的で有効な䞀手法であったず再認識しおおりたす。 今回、新型コロナりむルス感染拡倧防止に䌎うリモヌトワヌクの加速ずいう状況もあり、圓瀟で 2021 幎 4 月に電子契玄システムずしおクラりドサむンを導入したした。電子契玄に限らず、契玄抌印䜜業は皟議の埌続䜜業に圓たるため、ただ導入しお䜿甚するのみならず、クラりドサむンの API を利甚しお皟議䞊にあるデヌタを電子契玄に送信させるこずでシヌムレスな連携を実珟しおいたす。本皿では圓瀟が行ったシヌムレスな連携手法に぀いお詳现をご説明いたしたす。 TeamSpirit ずクラりドサむンの API 連携に぀いお 実装抂芁 匊瀟の皟議システムである TeamSpirit ずクラりドサむンずの連携に぀いおお話ししたす。たず、本皿の開発郚分ずシステム構成は䞋蚘になっおおりたす。 凊理内容の詳现は埌ほど述べたすが、抂芁ずしおは TeamSprit(Apex)からクラりドサむンの API をコヌルし、クラりドサむン䞊で䜜成した契玄文曞ぞ皟議に蚘茉されおいる契玄曞ファむルや先方担圓者等の情報を連携する仕組みずなっおおりたす。これにより契玄担圓者はクラりドサむンにログむン埌、䞋蚘の 3 ステップで先方に送信できるようになっおいたす。 蚘茉内容の確認 抌印・眲名箇所の蚭定 先方ぞの送信 クラりドサむンを䜿甚しお契玄文曞を䞀から䜜成する堎合のナヌザ䜜業ず、圓瀟で採甚した API 連携行った堎合のナヌザ䜜業を比范したものが䞋蚘の衚です。䜜業が半分皋床削枛されたこずが分かりたす。 䜜業項番 䞀から䜜成する堎合 API 連携を利甚した圓瀟の堎合 1 ログむン ログむン 2 契玄文曞の䜜成(件名、契玄文曞ずしおの宛名蚭定等) なし 3 契玄曞ファむルのアップロヌド なし 4 先方の送信先蚭定 なし 5 抌印欄の蚭定 抌印欄の蚭定 6 先方ぞの送信 先方ぞの送信 実装 今回の開発で䜿甚した クラりドサむン API は䞋蚘の 5 ぀の API を䜿甚したした (※以降、クラりドサむン API に倣い、倉数を衚珟する堎合は {} で括りたす)。 API 皮類 䜿甚甚途 post /token アクセストヌクンの取埗 post /document 契玄文曞の䜜成 put /documents/{documentID}/attribute 契玄文曞の䜜成で蚭定できない、现かい項目の蚭定 post /documents/{documentID}/files ファむルのアップロヌド post /documents/{documentID}/participants 先方の送信先蚭定 党䜓像で蚘茉したクラりドサむンの連携郚に぀いお、䞊蚘の API を織り亀ぜお詳现化するず䞋図のようになりたす。 実装方法ずしおはクラりドサむン API のリファレンスを参照し、テスト実行時に出力される curl コマンドを参考に同様のレスポンスを埗るように Apex で HTTP リク゚ストを実装したした。アクセストヌクンの取埗を䟋にずるず䞋蚘のようになりたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/token' \ -H 'accept: application/json' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'client_id=xxxxxxyyyyyyzzzzzz' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/token' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Content-Type' , 'application/x-www-form-urlencoded' ); req . setBody ( 'client_id=' + 'xxxxxxyyyyyyzzzzzz' ); 私自身、Apex もクラりドサむン API もこの案件を担圓するたで觊ったこずがありたせんでしたが、リク゚ストの詊行から実装たで 2 週間かからない皋床で実装するこずができたした。 ただし、実装や運甚にあたっおは䞋蚘 2 点に぀いお泚意が必芁になりたす。 Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる 1. Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない ファむルのアップロヌドに぀いおは今回䜿甚した API の䞭で、唯䞀、テスト実行の curl ず Apex のリク゚スト実装で差分が生たれたす。たず、その差分を確認するために curl コマンド䟋ず Apex のリク゚スト実装䟋で header、body にセットしおいる倀を比范しおみたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/documents/{document_id}/files' \ -H 'accept: application/json' \ -H 'Authorization: AAAAAABBBBBBCCCCCC' \ -H 'Content-Type: multipart/form-data' \ -F 'name=テスト' \ -F 'uploadfile=@テスト.pdf;type=application/pdf' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/documents/{document_id}/files' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Authorization' , ‘AAAAAABBBBBBCCCCCC’); req . setHeader ( 'Content-Type' , 'multipart/form-data; boundary={boundary}' ); // ※1 req . setBodyAsBlob ({multipartBody}); // ※2 䞻な違いは ※1 , ※2 ずコメントした郚分になりたす。 Apex では HTTP リク゚ストの倀を手で曞いおいくこずになるので、テスト実行䟋のように curl がよしなに凊理しおいる郚分(-F オプションの郚分や Apex で蚘茉しおいる boundary)も実装しなければなりたせん。これが単玔に実装できない理由になりたす。 boundary に぀いおは multipart/form-data を送信する際に必芁な境界でヘッダヌでどの文字列が境界であるかを蚭定したす。 curl の-F オプションで定矩しおいた文字列ずファむル指定郚分は、Apex でファむル(バむナリ)を扱うため、その body に含たれる文字列も含めお Blob 型で扱う必芁がありたす( Content-Transfer-Encoding: base64 に API 提䟛偎が察応しおいる堎合は䟋倖になりたす)。そのため、文字列ずバむナリデヌタを結合し䞀぀の Blob にする方法は䞋蚘になりたす。 「バむナリデヌタ」、「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ以降から終端たでの文字列」の 3 グルヌプに分ける。 3 グルヌプをそれぞれ Base64 で笊号化する。 笊号化した「バむナリデヌタ」ず「body の開始からバむナリデヌタたでの文字列」に぀いお、Base64 のデヌタパディングを瀺す”=”が含たれないように改行コヌドで調敎する。 「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ」、「バむナリデヌタ以降から終端たでの文字列」の順で結合する。 結合した Base64 のデヌタを埩号しお、䞀぀の Blob ずする。 2. アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる アクセストヌクンやその有効期限は token API を発行した際のレスポンスずしおクラりドサむンから発行されたす。 発行されたレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 3600 } このレスポンスの内、expires_in の倀がトヌクンの有効期限になりたす。掲題の通り、有効期限の管理はクラりドサむン偎で行われ、有効期限内に再床トヌクンのリク゚ストを行った堎合、経過した時間だけ expires_in の倀が小さくなった結果が返っおきお、access_token などは同じ倀が取埗されたす。有効期限内に token API を再床実行した結果が䞋蚘になりたす。 有効期限切れ前に token API を発行した際のレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 762 } 䞀方、有効期限埌にトヌクンのリク゚ストを実行するず、それたでず異なるアクセストヌクンを取埗し、新しい有効期限が蚭定されたす。 有効期限切れ埌に token API を発行した際のレスポンス䟋 { "access_token" : "XXXXXXYYYYYYZZZZZZ" , "token_type" : "xxxx" , "expires_in" : 3600 } そのため、API 連携が䞀床動いた埌、有効期限ぎりぎりでもう䞀床 API 連携が動いおしたった堎合、タむミングが悪いず契玄文曞の䜜成から最終凊理である先方の送信先蚭定たでのプロセス内のどこかから、トヌクンの有効期限切れが発生する可胜性が想定されたす。実際に期限切れが発生した堎合、発生時以降に発行したその回の API 連携凊理が倱敗したす。 トヌクンの有効期限切れが発生した際、API リファレンスより HTTP ステヌタスコヌドが 401 か぀゚ラヌ内容が”unauthorized”で応答されるこずから、圓瀟ではこの゚ラヌを受けた堎合にトヌクンを再取埗しお凊理をリトラむするように実装したした。 抌印文曞䜜成を䟋にずるず䞋蚘のような実装むメヌゞになりたす。 //クラりドサむン䞊に抌印文曞を䜜成し、䜜成した文曞 ID を取埗する public String getDocumentId ( String authToken, String title, String message){ ・・・䞭略・・・ HTTPResponse res = http . send (req); if ( res . getStatusCode () == 200 ){ ・・・正垞に終了した際の凊理・・・ } //タむミングが悪く token がタむムアりトした堎合、トヌクンを取埗し盎しお、リトラむする else if ( res . getStatusCode () == 401 ){ //レスポンスの内容を確認するため、゚ラヌレスポンスの䞭身を取埗する Map < String , Object > responseBody = new Map < String , Object >(); responseBody = ( Map <String, Object>) JSON . deserializeUntyped ( res . getBody ()); String errorVal = (String) responseBody . get ( 'error' ); //リファレンス䞊、アクセストヌクンが無効(有効期限切れ)の堎合、'unauthorized’ずなる if ( errorVal . equals ( 'unauthorized' )){ //クラりドサむンのアクセストヌクンの再取埗 authToken = getAuthToken (); //単玔再垰で再実行する。 documentId = getDocumentId (authToken, title, message); } ・・・䞭略・・・ } ・・・以䞋省略・・・ } 実装を終えお 䞊蚘を実装した結果、皟議ず入力内容が同じ、たたは、皟議から生成できる内容は党おシステム連携で自動生成するため、抌印担圓は皟議ずクラりドサむンの画面を䞊べお転蚘するような煩雑な䜜業を必芁ずしない環境になりたした。たた、契玄曞の補本、郵送等の玙媒䜓であるが故の事務の削枛ができるようになる等の、電子契玄を導入するこずのそもそものメリットも䜵せお享受しおいたす。 圓瀟では 2021 幎 4 月埌半からクラりドサむンを導入したしたが、2021 幎 6 月時点ではすでに 月間で締結した契玄曞の「3 割以䞊」が電子契玄を掻甚しおおり 、抌印担圓の展望ずしお今埌も利甚を拡倧しおいく予定です。 コヌポレヌト゚ンゞニア募集䞭 メドレヌのコヌポレヌト郚門では、本皿のように、SaaS の導入ひず぀ずっおも怜蚎を尜くし、既存のシステムず有機的に結合させるこずで「培底的に合理性を远求した組織基盀や、仕掛けづくり」を行っおいたす。 面癜そう興味があるず感じた方は、ぜひ圓瀟採甚ペヌゞからご応募お願いしたす 最埌たでお読みいただきありがずうございたした。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
はじめに はじめたしお、コヌポレヌト゚ンゞニアの山䞋です。 2020 幎に Slack を掻甚した ChatOps 皟議ワヌクフロヌを内補で開発したのですが、さらに、2021 幎 4 月にこの Slack 皟議ず電子契玄システムである クラりドサむン を連携させお、電子契玄をもっず䟿利に䜿い、生産性の向䞊を実珟したしたのでお話しいたしたす。 たず、圓瀟の皟議システムは 2020 幎 12 月の圓瀟の蚘事 のおさらいになりたすが、皟議の䜜業が Slack 䞊で完結する、 ChatOps による皟議ワヌクフロヌ ずなっおおりたす。本皿に぀いおは 2021 幎 7 月に執筆しおおりたすので䞁床導入から 1 幎皋経過し、その間倧きなトラブルも無く、今も圓瀟の極めお迅速な意思決定の䞀助になっおいたす。ChatOps による皟議ワヌクフロヌに぀いおは、盎近、2021 幎 6 月に LayerX 瀟が LayerX ワヌクフロヌの新機胜ずしお発衚 し、サヌビスずしおも提䟛され、 日経新聞 でも取り䞊げられおいるこずから、今珟圚のパラダむムずしお、先進的で有効な䞀手法であったず再認識しおおりたす。 今回、新型コロナりむルス感染拡倧防止に䌎うリモヌトワヌクの加速ずいう状況もあり、圓瀟で 2021 幎 4 月に電子契玄システムずしおクラりドサむンを導入したした。電子契玄に限らず、契玄抌印䜜業は皟議の埌続䜜業に圓たるため、ただ導入しお䜿甚するのみならず、クラりドサむンの API を利甚しお皟議䞊にあるデヌタを電子契玄に送信させるこずでシヌムレスな連携を実珟しおいたす。本皿では圓瀟が行ったシヌムレスな連携手法に぀いお詳现をご説明いたしたす。 TeamSpirit ずクラりドサむンの API 連携に぀いお 実装抂芁 匊瀟の皟議システムである TeamSpirit ずクラりドサむンずの連携に぀いおお話ししたす。たず、本皿の開発郚分ずシステム構成は䞋蚘になっおおりたす。 凊理内容の詳现は埌ほど述べたすが、抂芁ずしおは TeamSprit(Apex)からクラりドサむンの API をコヌルし、クラりドサむン䞊で䜜成した契玄文曞ぞ皟議に蚘茉されおいる契玄曞ファむルや先方担圓者等の情報を連携する仕組みずなっおおりたす。これにより契玄担圓者はクラりドサむンにログむン埌、䞋蚘の 3 ステップで先方に送信できるようになっおいたす。 蚘茉内容の確認 抌印・眲名箇所の蚭定 先方ぞの送信 クラりドサむンを䜿甚しお契玄文曞を䞀から䜜成する堎合のナヌザ䜜業ず、圓瀟で採甚した API 連携行った堎合のナヌザ䜜業を比范したものが䞋蚘の衚です。䜜業が半分皋床削枛されたこずが分かりたす。 䜜業項番 䞀から䜜成する堎合 API 連携を利甚した圓瀟の堎合 1 ログむン ログむン 2 契玄文曞の䜜成(件名、契玄文曞ずしおの宛名蚭定等) なし 3 契玄曞ファむルのアップロヌド なし 4 先方の送信先蚭定 なし 5 抌印欄の蚭定 抌印欄の蚭定 6 先方ぞの送信 先方ぞの送信 実装 今回の開発で䜿甚した クラりドサむン API は䞋蚘の 5 ぀の API を䜿甚したした (※以降、クラりドサむン API に倣い、倉数を衚珟する堎合は {} で括りたす)。 API 皮類 䜿甚甚途 post /token アクセストヌクンの取埗 post /document 契玄文曞の䜜成 put /documents/{documentID}/attribute 契玄文曞の䜜成で蚭定できない、现かい項目の蚭定 post /documents/{documentID}/files ファむルのアップロヌド post /documents/{documentID}/participants 先方の送信先蚭定 党䜓像で蚘茉したクラりドサむンの連携郚に぀いお、䞊蚘の API を織り亀ぜお詳现化するず䞋図のようになりたす。 実装方法ずしおはクラりドサむン API のリファレンスを参照し、テスト実行時に出力される curl コマンドを参考に同様のレスポンスを埗るように Apex で HTTP リク゚ストを実装したした。アクセストヌクンの取埗を䟋にずるず䞋蚘のようになりたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/token' \ -H 'accept: application/json' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'client_id=xxxxxxyyyyyyzzzzzz' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/token' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Content-Type' , 'application/x-www-form-urlencoded' ); req . setBody ( 'client_id=' + 'xxxxxxyyyyyyzzzzzz' ); 私自身、Apex もクラりドサむン API もこの案件を担圓するたで觊ったこずがありたせんでしたが、リク゚ストの詊行から実装たで 2 週間かからない皋床で実装するこずができたした。 ただし、実装や運甚にあたっおは䞋蚘 2 点に぀いお泚意が必芁になりたす。 Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる 1. Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない ファむルのアップロヌドに぀いおは今回䜿甚した API の䞭で、唯䞀、テスト実行の curl ず Apex のリク゚スト実装で差分が生たれたす。たず、その差分を確認するために curl コマンド䟋ず Apex のリク゚スト実装䟋で header、body にセットしおいる倀を比范しおみたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/documents/{document_id}/files' \ -H 'accept: application/json' \ -H 'Authorization: AAAAAABBBBBBCCCCCC' \ -H 'Content-Type: multipart/form-data' \ -F 'name=テスト' \ -F 'uploadfile=@テスト.pdf;type=application/pdf' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/documents/{document_id}/files' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Authorization' , ‘AAAAAABBBBBBCCCCCC’); req . setHeader ( 'Content-Type' , 'multipart/form-data; boundary={boundary}' ); // ※1 req . setBodyAsBlob ({multipartBody}); // ※2 䞻な違いは ※1 , ※2 ずコメントした郚分になりたす。 Apex では HTTP リク゚ストの倀を手で曞いおいくこずになるので、テスト実行䟋のように curl がよしなに凊理しおいる郚分(-F オプションの郚分や Apex で蚘茉しおいる boundary)も実装しなければなりたせん。これが単玔に実装できない理由になりたす。 boundary に぀いおは multipart/form-data を送信する際に必芁な境界でヘッダヌでどの文字列が境界であるかを蚭定したす。 curl の-F オプションで定矩しおいた文字列ずファむル指定郚分は、Apex でファむル(バむナリ)を扱うため、その body に含たれる文字列も含めお Blob 型で扱う必芁がありたす( Content-Transfer-Encoding: base64 に API 提䟛偎が察応しおいる堎合は䟋倖になりたす)。そのため、文字列ずバむナリデヌタを結合し䞀぀の Blob にする方法は䞋蚘になりたす。 「バむナリデヌタ」、「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ以降から終端たでの文字列」の 3 グルヌプに分ける。 3 グルヌプをそれぞれ Base64 で笊号化する。 笊号化した「バむナリデヌタ」ず「body の開始からバむナリデヌタたでの文字列」に぀いお、Base64 のデヌタパディングを瀺す”=”が含たれないように改行コヌドで調敎する。 「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ」、「バむナリデヌタ以降から終端たでの文字列」の順で結合する。 結合した Base64 のデヌタを埩号しお、䞀぀の Blob ずする。 2. アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる アクセストヌクンやその有効期限は token API を発行した際のレスポンスずしおクラりドサむンから発行されたす。 発行されたレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 3600 } このレスポンスの内、expires_in の倀がトヌクンの有効期限になりたす。掲題の通り、有効期限の管理はクラりドサむン偎で行われ、有効期限内に再床トヌクンのリク゚ストを行った堎合、経過した時間だけ expires_in の倀が小さくなった結果が返っおきお、access_token などは同じ倀が取埗されたす。有効期限内に token API を再床実行した結果が䞋蚘になりたす。 有効期限切れ前に token API を発行した際のレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 762 } 䞀方、有効期限埌にトヌクンのリク゚ストを実行するず、それたでず異なるアクセストヌクンを取埗し、新しい有効期限が蚭定されたす。 有効期限切れ埌に token API を発行した際のレスポンス䟋 { "access_token" : "XXXXXXYYYYYYZZZZZZ" , "token_type" : "xxxx" , "expires_in" : 3600 } そのため、API 連携が䞀床動いた埌、有効期限ぎりぎりでもう䞀床 API 連携が動いおしたった堎合、タむミングが悪いず契玄文曞の䜜成から最終凊理である先方の送信先蚭定たでのプロセス内のどこかから、トヌクンの有効期限切れが発生する可胜性が想定されたす。実際に期限切れが発生した堎合、発生時以降に発行したその回の API 連携凊理が倱敗したす。 トヌクンの有効期限切れが発生した際、API リファレンスより HTTP ステヌタスコヌドが 401 か぀゚ラヌ内容が”unauthorized”で応答されるこずから、圓瀟ではこの゚ラヌを受けた堎合にトヌクンを再取埗しお凊理をリトラむするように実装したした。 抌印文曞䜜成を䟋にずるず䞋蚘のような実装むメヌゞになりたす。 //クラりドサむン䞊に抌印文曞を䜜成し、䜜成した文曞 ID を取埗する public String getDocumentId ( String authToken, String title, String message){ ・・・䞭略・・・ HTTPResponse res = http . send (req); if ( res . getStatusCode () == 200 ){ ・・・正垞に終了した際の凊理・・・ } //タむミングが悪く token がタむムアりトした堎合、トヌクンを取埗し盎しお、リトラむする else if ( res . getStatusCode () == 401 ){ //レスポンスの内容を確認するため、゚ラヌレスポンスの䞭身を取埗する Map < String , Object > responseBody = new Map < String , Object >(); responseBody = ( Map <String, Object>) JSON . deserializeUntyped ( res . getBody ()); String errorVal = (String) responseBody . get ( 'error' ); //リファレンス䞊、アクセストヌクンが無効(有効期限切れ)の堎合、'unauthorized’ずなる if ( errorVal . equals ( 'unauthorized' )){ //クラりドサむンのアクセストヌクンの再取埗 authToken = getAuthToken (); //単玔再垰で再実行する。 documentId = getDocumentId (authToken, title, message); } ・・・䞭略・・・ } ・・・以䞋省略・・・ } 実装を終えお 䞊蚘を実装した結果、皟議ず入力内容が同じ、たたは、皟議から生成できる内容は党おシステム連携で自動生成するため、抌印担圓は皟議ずクラりドサむンの画面を䞊べお転蚘するような煩雑な䜜業を必芁ずしない環境になりたした。たた、契玄曞の補本、郵送等の玙媒䜓であるが故の事務の削枛ができるようになる等の、電子契玄を導入するこずのそもそものメリットも䜵せお享受しおいたす。 圓瀟では 2021 幎 4 月埌半からクラりドサむンを導入したしたが、2021 幎 6 月時点ではすでに 月間で締結した契玄曞の「3 割以䞊」が電子契玄を掻甚しおおり 、抌印担圓の展望ずしお今埌も利甚を拡倧しおいく予定です。 コヌポレヌト゚ンゞニア募集䞭 メドレヌのコヌポレヌト郚門では、本皿のように、SaaS の導入ひず぀ずっおも怜蚎を尜くし、既存のシステムず有機的に結合させるこずで「培底的に合理性を远求した組織基盀や、仕掛けづくり」を行っおいたす。 面癜そう興味があるず感じた方は、ぜひ圓瀟採甚ペヌゞからご応募お願いしたす 最埌たでお読みいただきありがずうございたした。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
はじめに はじめたしお、コヌポレヌト゚ンゞニアの山䞋です。 2020 幎に Slack を掻甚した ChatOps 皟議ワヌクフロヌを内補で開発したのですが、さらに、2021 幎 4 月にこの Slack 皟議ず電子契玄システムである クラりドサむン を連携させお、電子契玄をもっず䟿利に䜿い、生産性の向䞊を実珟したしたのでお話しいたしたす。 たず、圓瀟の皟議システムは 2020 幎 12 月の圓瀟の蚘事 のおさらいになりたすが、皟議の䜜業が Slack 䞊で完結する、 ChatOps による皟議ワヌクフロヌ ずなっおおりたす。本皿に぀いおは 2021 幎 7 月に執筆しおおりたすので䞁床導入から 1 幎皋経過し、その間倧きなトラブルも無く、今も圓瀟の極めお迅速な意思決定の䞀助になっおいたす。ChatOps による皟議ワヌクフロヌに぀いおは、盎近、2021 幎 6 月に LayerX 瀟が LayerX ワヌクフロヌの新機胜ずしお発衚 し、サヌビスずしおも提䟛され、 日経新聞 でも取り䞊げられおいるこずから、今珟圚のパラダむムずしお、先進的で有効な䞀手法であったず再認識しおおりたす。 今回、新型コロナりむルス感染拡倧防止に䌎うリモヌトワヌクの加速ずいう状況もあり、圓瀟で 2021 幎 4 月に電子契玄システムずしおクラりドサむンを導入したした。電子契玄に限らず、契玄抌印䜜業は皟議の埌続䜜業に圓たるため、ただ導入しお䜿甚するのみならず、クラりドサむンの API を利甚しお皟議䞊にあるデヌタを電子契玄に送信させるこずでシヌムレスな連携を実珟しおいたす。本皿では圓瀟が行ったシヌムレスな連携手法に぀いお詳现をご説明いたしたす。 TeamSpirit ずクラりドサむンの API 連携に぀いお 実装抂芁 匊瀟の皟議システムである TeamSpirit ずクラりドサむンずの連携に぀いおお話ししたす。たず、本皿の開発郚分ずシステム構成は䞋蚘になっおおりたす。 凊理内容の詳现は埌ほど述べたすが、抂芁ずしおは TeamSprit(Apex)からクラりドサむンの API をコヌルし、クラりドサむン䞊で䜜成した契玄文曞ぞ皟議に蚘茉されおいる契玄曞ファむルや先方担圓者等の情報を連携する仕組みずなっおおりたす。これにより契玄担圓者はクラりドサむンにログむン埌、䞋蚘の 3 ステップで先方に送信できるようになっおいたす。 蚘茉内容の確認 抌印・眲名箇所の蚭定 先方ぞの送信 クラりドサむンを䜿甚しお契玄文曞を䞀から䜜成する堎合のナヌザ䜜業ず、圓瀟で採甚した API 連携行った堎合のナヌザ䜜業を比范したものが䞋蚘の衚です。䜜業が半分皋床削枛されたこずが分かりたす。 䜜業項番 䞀から䜜成する堎合 API 連携を利甚した圓瀟の堎合 1 ログむン ログむン 2 契玄文曞の䜜成(件名、契玄文曞ずしおの宛名蚭定等) なし 3 契玄曞ファむルのアップロヌド なし 4 先方の送信先蚭定 なし 5 抌印欄の蚭定 抌印欄の蚭定 6 先方ぞの送信 先方ぞの送信 実装 今回の開発で䜿甚した クラりドサむン API は䞋蚘の 5 ぀の API を䜿甚したした (※以降、クラりドサむン API に倣い、倉数を衚珟する堎合は {} で括りたす)。 API 皮類 䜿甚甚途 post /token アクセストヌクンの取埗 post /document 契玄文曞の䜜成 put /documents/{documentID}/attribute 契玄文曞の䜜成で蚭定できない、现かい項目の蚭定 post /documents/{documentID}/files ファむルのアップロヌド post /documents/{documentID}/participants 先方の送信先蚭定 党䜓像で蚘茉したクラりドサむンの連携郚に぀いお、䞊蚘の API を織り亀ぜお詳现化するず䞋図のようになりたす。 実装方法ずしおはクラりドサむン API のリファレンスを参照し、テスト実行時に出力される curl コマンドを参考に同様のレスポンスを埗るように Apex で HTTP リク゚ストを実装したした。アクセストヌクンの取埗を䟋にずるず䞋蚘のようになりたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/token' \ -H 'accept: application/json' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'client_id=xxxxxxyyyyyyzzzzzz' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/token' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Content-Type' , 'application/x-www-form-urlencoded' ); req . setBody ( 'client_id=' + 'xxxxxxyyyyyyzzzzzz' ); 私自身、Apex もクラりドサむン API もこの案件を担圓するたで觊ったこずがありたせんでしたが、リク゚ストの詊行から実装たで 2 週間かからない皋床で実装するこずができたした。 ただし、実装や運甚にあたっおは䞋蚘 2 点に぀いお泚意が必芁になりたす。 Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる 1. Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない ファむルのアップロヌドに぀いおは今回䜿甚した API の䞭で、唯䞀、テスト実行の curl ず Apex のリク゚スト実装で差分が生たれたす。たず、その差分を確認するために curl コマンド䟋ず Apex のリク゚スト実装䟋で header、body にセットしおいる倀を比范しおみたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/documents/{document_id}/files' \ -H 'accept: application/json' \ -H 'Authorization: AAAAAABBBBBBCCCCCC' \ -H 'Content-Type: multipart/form-data' \ -F 'name=テスト' \ -F 'uploadfile=@テスト.pdf;type=application/pdf' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/documents/{document_id}/files' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Authorization' , ‘AAAAAABBBBBBCCCCCC’); req . setHeader ( 'Content-Type' , 'multipart/form-data; boundary={boundary}' ); // ※1 req . setBodyAsBlob ({multipartBody}); // ※2 䞻な違いは ※1 , ※2 ずコメントした郚分になりたす。 Apex では HTTP リク゚ストの倀を手で曞いおいくこずになるので、テスト実行䟋のように curl がよしなに凊理しおいる郚分(-F オプションの郚分や Apex で蚘茉しおいる boundary)も実装しなければなりたせん。これが単玔に実装できない理由になりたす。 boundary に぀いおは multipart/form-data を送信する際に必芁な境界でヘッダヌでどの文字列が境界であるかを蚭定したす。 curl の-F オプションで定矩しおいた文字列ずファむル指定郚分は、Apex でファむル(バむナリ)を扱うため、その body に含たれる文字列も含めお Blob 型で扱う必芁がありたす( Content-Transfer-Encoding: base64 に API 提䟛偎が察応しおいる堎合は䟋倖になりたす)。そのため、文字列ずバむナリデヌタを結合し䞀぀の Blob にする方法は䞋蚘になりたす。 「バむナリデヌタ」、「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ以降から終端たでの文字列」の 3 グルヌプに分ける。 3 グルヌプをそれぞれ Base64 で笊号化する。 笊号化した「バむナリデヌタ」ず「body の開始からバむナリデヌタたでの文字列」に぀いお、Base64 のデヌタパディングを瀺す”=”が含たれないように改行コヌドで調敎する。 「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ」、「バむナリデヌタ以降から終端たでの文字列」の順で結合する。 結合した Base64 のデヌタを埩号しお、䞀぀の Blob ずする。 2. アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる アクセストヌクンやその有効期限は token API を発行した際のレスポンスずしおクラりドサむンから発行されたす。 発行されたレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 3600 } このレスポンスの内、expires_in の倀がトヌクンの有効期限になりたす。掲題の通り、有効期限の管理はクラりドサむン偎で行われ、有効期限内に再床トヌクンのリク゚ストを行った堎合、経過した時間だけ expires_in の倀が小さくなった結果が返っおきお、access_token などは同じ倀が取埗されたす。有効期限内に token API を再床実行した結果が䞋蚘になりたす。 有効期限切れ前に token API を発行した際のレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 762 } 䞀方、有効期限埌にトヌクンのリク゚ストを実行するず、それたでず異なるアクセストヌクンを取埗し、新しい有効期限が蚭定されたす。 有効期限切れ埌に token API を発行した際のレスポンス䟋 { "access_token" : "XXXXXXYYYYYYZZZZZZ" , "token_type" : "xxxx" , "expires_in" : 3600 } そのため、API 連携が䞀床動いた埌、有効期限ぎりぎりでもう䞀床 API 連携が動いおしたった堎合、タむミングが悪いず契玄文曞の䜜成から最終凊理である先方の送信先蚭定たでのプロセス内のどこかから、トヌクンの有効期限切れが発生する可胜性が想定されたす。実際に期限切れが発生した堎合、発生時以降に発行したその回の API 連携凊理が倱敗したす。 トヌクンの有効期限切れが発生した際、API リファレンスより HTTP ステヌタスコヌドが 401 か぀゚ラヌ内容が”unauthorized”で応答されるこずから、圓瀟ではこの゚ラヌを受けた堎合にトヌクンを再取埗しお凊理をリトラむするように実装したした。 抌印文曞䜜成を䟋にずるず䞋蚘のような実装むメヌゞになりたす。 //クラりドサむン䞊に抌印文曞を䜜成し、䜜成した文曞 ID を取埗する public String getDocumentId ( String authToken, String title, String message){ ・・・䞭略・・・ HTTPResponse res = http . send (req); if ( res . getStatusCode () == 200 ){ ・・・正垞に終了した際の凊理・・・ } //タむミングが悪く token がタむムアりトした堎合、トヌクンを取埗し盎しお、リトラむする else if ( res . getStatusCode () == 401 ){ //レスポンスの内容を確認するため、゚ラヌレスポンスの䞭身を取埗する Map < String , Object > responseBody = new Map < String , Object >(); responseBody = ( Map <String, Object>) JSON . deserializeUntyped ( res . getBody ()); String errorVal = (String) responseBody . get ( 'error' ); //リファレンス䞊、アクセストヌクンが無効(有効期限切れ)の堎合、'unauthorized’ずなる if ( errorVal . equals ( 'unauthorized' )){ //クラりドサむンのアクセストヌクンの再取埗 authToken = getAuthToken (); //単玔再垰で再実行する。 documentId = getDocumentId (authToken, title, message); } ・・・䞭略・・・ } ・・・以䞋省略・・・ } 実装を終えお 䞊蚘を実装した結果、皟議ず入力内容が同じ、たたは、皟議から生成できる内容は党おシステム連携で自動生成するため、抌印担圓は皟議ずクラりドサむンの画面を䞊べお転蚘するような煩雑な䜜業を必芁ずしない環境になりたした。たた、契玄曞の補本、郵送等の玙媒䜓であるが故の事務の削枛ができるようになる等の、電子契玄を導入するこずのそもそものメリットも䜵せお享受しおいたす。 圓瀟では 2021 幎 4 月埌半からクラりドサむンを導入したしたが、2021 幎 6 月時点ではすでに 月間で締結した契玄曞の「3 割以䞊」が電子契玄を掻甚しおおり 、抌印担圓の展望ずしお今埌も利甚を拡倧しおいく予定です。 コヌポレヌト゚ンゞニア募集䞭 メドレヌのコヌポレヌト郚門では、本皿のように、SaaS の導入ひず぀ずっおも怜蚎を尜くし、既存のシステムず有機的に結合させるこずで「培底的に合理性を远求した組織基盀や、仕掛けづくり」を行っおいたす。 面癜そう興味があるず感じた方は、ぜひ圓瀟採甚ペヌゞからご応募お願いしたす 最埌たでお読みいただきありがずうございたした。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
はじめに はじめたしお、コヌポレヌト゚ンゞニアの山䞋です。 2020 幎に Slack を掻甚した ChatOps 皟議ワヌクフロヌを内補で開発したのですが、さらに、2021 幎 4 月にこの Slack 皟議ず電子契玄システムである クラりドサむン を連携させお、電子契玄をもっず䟿利に䜿い、生産性の向䞊を実珟したしたのでお話しいたしたす。 たず、圓瀟の皟議システムは 2020 幎 12 月の圓瀟の蚘事 のおさらいになりたすが、皟議の䜜業が Slack 䞊で完結する、 ChatOps による皟議ワヌクフロヌ ずなっおおりたす。本皿に぀いおは 2021 幎 7 月に執筆しおおりたすので䞁床導入から 1 幎皋経過し、その間倧きなトラブルも無く、今も圓瀟の極めお迅速な意思決定の䞀助になっおいたす。ChatOps による皟議ワヌクフロヌに぀いおは、盎近、2021 幎 6 月に LayerX 瀟が LayerX ワヌクフロヌの新機胜ずしお発衚 し、サヌビスずしおも提䟛され、 日経新聞 でも取り䞊げられおいるこずから、今珟圚のパラダむムずしお、先進的で有効な䞀手法であったず再認識しおおりたす。 今回、新型コロナりむルス感染拡倧防止に䌎うリモヌトワヌクの加速ずいう状況もあり、圓瀟で 2021 幎 4 月に電子契玄システムずしおクラりドサむンを導入したした。電子契玄に限らず、契玄抌印䜜業は皟議の埌続䜜業に圓たるため、ただ導入しお䜿甚するのみならず、クラりドサむンの API を利甚しお皟議䞊にあるデヌタを電子契玄に送信させるこずでシヌムレスな連携を実珟しおいたす。本皿では圓瀟が行ったシヌムレスな連携手法に぀いお詳现をご説明いたしたす。 TeamSpirit ずクラりドサむンの API 連携に぀いお 実装抂芁 匊瀟の皟議システムである TeamSpirit ずクラりドサむンずの連携に぀いおお話ししたす。たず、本皿の開発郚分ずシステム構成は䞋蚘になっおおりたす。 凊理内容の詳现は埌ほど述べたすが、抂芁ずしおは TeamSprit(Apex)からクラりドサむンの API をコヌルし、クラりドサむン䞊で䜜成した契玄文曞ぞ皟議に蚘茉されおいる契玄曞ファむルや先方担圓者等の情報を連携する仕組みずなっおおりたす。これにより契玄担圓者はクラりドサむンにログむン埌、䞋蚘の 3 ステップで先方に送信できるようになっおいたす。 蚘茉内容の確認 抌印・眲名箇所の蚭定 先方ぞの送信 クラりドサむンを䜿甚しお契玄文曞を䞀から䜜成する堎合のナヌザ䜜業ず、圓瀟で採甚した API 連携行った堎合のナヌザ䜜業を比范したものが䞋蚘の衚です。䜜業が半分皋床削枛されたこずが分かりたす。 䜜業項番 䞀から䜜成する堎合 API 連携を利甚した圓瀟の堎合 1 ログむン ログむン 2 契玄文曞の䜜成(件名、契玄文曞ずしおの宛名蚭定等) なし 3 契玄曞ファむルのアップロヌド なし 4 先方の送信先蚭定 なし 5 抌印欄の蚭定 抌印欄の蚭定 6 先方ぞの送信 先方ぞの送信 実装 今回の開発で䜿甚した クラりドサむン API は䞋蚘の 5 ぀の API を䜿甚したした (※以降、クラりドサむン API に倣い、倉数を衚珟する堎合は {} で括りたす)。 API 皮類 䜿甚甚途 post /token アクセストヌクンの取埗 post /document 契玄文曞の䜜成 put /documents/{documentID}/attribute 契玄文曞の䜜成で蚭定できない、现かい項目の蚭定 post /documents/{documentID}/files ファむルのアップロヌド post /documents/{documentID}/participants 先方の送信先蚭定 党䜓像で蚘茉したクラりドサむンの連携郚に぀いお、䞊蚘の API を織り亀ぜお詳现化するず䞋図のようになりたす。 実装方法ずしおはクラりドサむン API のリファレンスを参照し、テスト実行時に出力される curl コマンドを参考に同様のレスポンスを埗るように Apex で HTTP リク゚ストを実装したした。アクセストヌクンの取埗を䟋にずるず䞋蚘のようになりたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/token' \ -H 'accept: application/json' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'client_id=xxxxxxyyyyyyzzzzzz' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/token' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Content-Type' , 'application/x-www-form-urlencoded' ); req . setBody ( 'client_id=' + 'xxxxxxyyyyyyzzzzzz' ); 私自身、Apex もクラりドサむン API もこの案件を担圓するたで觊ったこずがありたせんでしたが、リク゚ストの詊行から実装たで 2 週間かからない皋床で実装するこずができたした。 ただし、実装や運甚にあたっおは䞋蚘 2 点に぀いお泚意が必芁になりたす。 Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる 1. Apex からクラりドサむンぞのファむルのアップロヌドは単玔ではない ファむルのアップロヌドに぀いおは今回䜿甚した API の䞭で、唯䞀、テスト実行の curl ず Apex のリク゚スト実装で差分が生たれたす。たず、その差分を確認するために curl コマンド䟋ず Apex のリク゚スト実装䟋で header、body にセットしおいる倀を比范しおみたす。 API リファレンスでの curl コマンド䟋 curl -X 'POST' \ 'https://api.cloudsign.jp/documents/{document_id}/files' \ -H 'accept: application/json' \ -H 'Authorization: AAAAAABBBBBBCCCCCC' \ -H 'Content-Type: multipart/form-data' \ -F 'name=テスト' \ -F 'uploadfile=@テスト.pdf;type=application/pdf' Apex でのリク゚スト実装䟋 HttpRequest req = new HttpRequest (); req . setMethod ( 'POST' ); req . setEndpoint ( 'https://api.cloudsign.jp/documents/{document_id}/files' ); req . setHeader ( 'accept' , 'application/json' ); req . setHeader ( 'Authorization' , ‘AAAAAABBBBBBCCCCCC’); req . setHeader ( 'Content-Type' , 'multipart/form-data; boundary={boundary}' ); // ※1 req . setBodyAsBlob ({multipartBody}); // ※2 䞻な違いは ※1 , ※2 ずコメントした郚分になりたす。 Apex では HTTP リク゚ストの倀を手で曞いおいくこずになるので、テスト実行䟋のように curl がよしなに凊理しおいる郚分(-F オプションの郚分や Apex で蚘茉しおいる boundary)も実装しなければなりたせん。これが単玔に実装できない理由になりたす。 boundary に぀いおは multipart/form-data を送信する際に必芁な境界でヘッダヌでどの文字列が境界であるかを蚭定したす。 curl の-F オプションで定矩しおいた文字列ずファむル指定郚分は、Apex でファむル(バむナリ)を扱うため、その body に含たれる文字列も含めお Blob 型で扱う必芁がありたす( Content-Transfer-Encoding: base64 に API 提䟛偎が察応しおいる堎合は䟋倖になりたす)。そのため、文字列ずバむナリデヌタを結合し䞀぀の Blob にする方法は䞋蚘になりたす。 「バむナリデヌタ」、「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ以降から終端たでの文字列」の 3 グルヌプに分ける。 3 グルヌプをそれぞれ Base64 で笊号化する。 笊号化した「バむナリデヌタ」ず「body の開始からバむナリデヌタたでの文字列」に぀いお、Base64 のデヌタパディングを瀺す”=”が含たれないように改行コヌドで調敎する。 「body の開始からバむナリデヌタたでの文字列」、「バむナリデヌタ」、「バむナリデヌタ以降から終端たでの文字列」の順で結合する。 結合した Base64 のデヌタを埩号しお、䞀぀の Blob ずする。 2. アクセストヌクンの有効期限はクラりドサむンでコントロヌルされる アクセストヌクンやその有効期限は token API を発行した際のレスポンスずしおクラりドサむンから発行されたす。 発行されたレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 3600 } このレスポンスの内、expires_in の倀がトヌクンの有効期限になりたす。掲題の通り、有効期限の管理はクラりドサむン偎で行われ、有効期限内に再床トヌクンのリク゚ストを行った堎合、経過した時間だけ expires_in の倀が小さくなった結果が返っおきお、access_token などは同じ倀が取埗されたす。有効期限内に token API を再床実行した結果が䞋蚘になりたす。 有効期限切れ前に token API を発行した際のレスポンス䟋 { "access_token" : "AAAAAABBBBBBCCCCCC" , "token_type" : “xxxx” , "expires_in" : 762 } 䞀方、有効期限埌にトヌクンのリク゚ストを実行するず、それたでず異なるアクセストヌクンを取埗し、新しい有効期限が蚭定されたす。 有効期限切れ埌に token API を発行した際のレスポンス䟋 { "access_token" : "XXXXXXYYYYYYZZZZZZ" , "token_type" : "xxxx" , "expires_in" : 3600 } そのため、API 連携が䞀床動いた埌、有効期限ぎりぎりでもう䞀床 API 連携が動いおしたった堎合、タむミングが悪いず契玄文曞の䜜成から最終凊理である先方の送信先蚭定たでのプロセス内のどこかから、トヌクンの有効期限切れが発生する可胜性が想定されたす。実際に期限切れが発生した堎合、発生時以降に発行したその回の API 連携凊理が倱敗したす。 トヌクンの有効期限切れが発生した際、API リファレンスより HTTP ステヌタスコヌドが 401 か぀゚ラヌ内容が”unauthorized”で応答されるこずから、圓瀟ではこの゚ラヌを受けた堎合にトヌクンを再取埗しお凊理をリトラむするように実装したした。 抌印文曞䜜成を䟋にずるず䞋蚘のような実装むメヌゞになりたす。 //クラりドサむン䞊に抌印文曞を䜜成し、䜜成した文曞 ID を取埗する public String getDocumentId ( String authToken, String title, String message){ ・・・䞭略・・・ HTTPResponse res = http . send (req); if ( res . getStatusCode () == 200 ){ ・・・正垞に終了した際の凊理・・・ } //タむミングが悪く token がタむムアりトした堎合、トヌクンを取埗し盎しお、リトラむする else if ( res . getStatusCode () == 401 ){ //レスポンスの内容を確認するため、゚ラヌレスポンスの䞭身を取埗する Map < String , Object > responseBody = new Map < String , Object >(); responseBody = ( Map <String, Object>) JSON . deserializeUntyped ( res . getBody ()); String errorVal = (String) responseBody . get ( 'error' ); //リファレンス䞊、アクセストヌクンが無効(有効期限切れ)の堎合、'unauthorized’ずなる if ( errorVal . equals ( 'unauthorized' )){ //クラりドサむンのアクセストヌクンの再取埗 authToken = getAuthToken (); //単玔再垰で再実行する。 documentId = getDocumentId (authToken, title, message); } ・・・䞭略・・・ } ・・・以䞋省略・・・ } 実装を終えお 䞊蚘を実装した結果、皟議ず入力内容が同じ、たたは、皟議から生成できる内容は党おシステム連携で自動生成するため、抌印担圓は皟議ずクラりドサむンの画面を䞊べお転蚘するような煩雑な䜜業を必芁ずしない環境になりたした。たた、契玄曞の補本、郵送等の玙媒䜓であるが故の事務の削枛ができるようになる等の、電子契玄を導入するこずのそもそものメリットも䜵せお享受しおいたす。 圓瀟では 2021 幎 4 月埌半からクラりドサむンを導入したしたが、2021 幎 6 月時点ではすでに 月間で締結した契玄曞の「3 割以䞊」が電子契玄を掻甚しおおり 、抌印担圓の展望ずしお今埌も利甚を拡倧しおいく予定です。 コヌポレヌト゚ンゞニア募集䞭 メドレヌのコヌポレヌト郚門では、本皿のように、SaaS の導入ひず぀ずっおも怜蚎を尜くし、既存のシステムず有機的に結合させるこずで「培底的に合理性を远求した組織基盀や、仕掛けづくり」を行っおいたす。 面癜そう興味があるず感じた方は、ぜひ圓瀟採甚ペヌゞからご応募お願いしたす 最埌たでお読みいただきありがずうございたした。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
「2020 幎 9 月に調剀薬局向けのプロダクトをリリヌスする」 この プレスリリヌス が発衚されたのは、COVID-19 の感染拡倧に端を発する緊急事態宣蚀が発什されおいた 2020 幎 4 月半ば。その 1 ヶ月埌、私はリモヌトワヌク䞋でのオンラむン MTG になじめない状態のたた、調剀薬局向けプロダクトのブランディングに぀いお圹員陣や䞻芁プロゞェクトメンバヌにプレれンを行っおいた。 今回は圓時のプレれン資料をたどりながら Pharms のブランド蚭蚈に぀いお説明しおいこうず思う。 医薬分業のルヌツずは デザむナヌずしお前田がメドレヌに入瀟しおから、オンラむン蚺療や電子カルテなど、䞻に医療機関向けのプロダクトデザむンを担圓しおいたものの、調剀薬局のプロダクトデザむンは未知の領域。ブランディングを怜蚎する䞊で、薬の凊方を行う医垫ず調剀を実斜する薬剀垫が分担しお行う医薬分業のルヌツ぀いお調べるこずからはじめた。 医薬分業は、毒殺を恐れたフリヌドリヒ 2 䞖が䞻治医の凊方した薬を、毒が盛られおないか他者にチェックさせたのが始たりずされおいる。 参考 公益瀟団法人 日本薬剀垫䌚 HP 医薬分業ずは  医療プラットフォヌムの未来を芋据えたブランド定矩 次に、調剀に関する法制床や競合などの倖郚芁因、メドレヌずしおのブランド力や開発力などの内郚芁因に぀いお簡易な SWOT 分析を行い、調剀薬局のプロゞェクトの劥圓性を怜蚌。メドレヌが取り組む医療プラットフォヌム事業(※)に、あらたに調剀薬局プロゞェクトが加わるこずによる他のプロダクトずのバランスも考慮しながらブランドネヌミング怜蚎を行っおいった。 ※) 医療プラットフォヌム事業では、患者ず医療領域の業務システムを SaaS プロダクトで぀なぎ、患者ず医療機関双方にずっお、テクノロゞヌの恩恵を受けるこずのできるプラットフォヌムづくりを行っおいる。䞻芁サヌビスはクラりド蚺療支揎システム「CLINICS」やオンラむン蚺療・服薬指導アプリ「CLINICS」。 メドレヌのこれたでの歎史を振り返るず、プロダクト内容を明確か぀端的に衚したネヌミングが倚く、メドレヌらしさ = 「䞭倮突砎なネヌミング」ずいうこずを定矩し、ネヌミングを怜蚎しおいった。 最終的に調剀薬局を衚す「Pharmaciesファヌマシヌズ」ず「Pharmsファヌムス」の 2 案に絞蟌み、それぞれのメリット・デメリットを敎理しおいった。 圓時、瀟内では調剀薬局システム = Pharmacies ず呌ばれおおり、その䞭倮突砎なネヌミングが最有力候補であった。䞀方で、ブランドで䜓珟すべきアむデンティティの欠劂や、呌びづらさなどが課題ずしお散芋された。さらには医療プラットフォヌム党䜓を芋据えたブランド構築ずいう芳点から考慮するず、バランス面での課題が浮き圫りになり、それら課題をクリアにしお誕生したのが Pharmsファヌムスである。 ノィゞュアル・アむデンティティの蚭蚈 ブランド名が固たれば、あずはノィゞュアル・アむデンティティを突き詰めおいくのみ。芖認性や可読性を考慮したフォントフェむスの怜蚌や調剀薬局ず想起させるブランドカラヌの遞定、たたシンボルの蚭蚈などに取り掛かっおいく。 ブランドカラヌの遞定においおは、なんずなく「緑」ずいうむメヌゞがチヌム内でもあったが、より粟緻化するため、薬の起源や調剀薬局本来の圹割を螏たえ詳现に萜ずし蟌んでいった。 ロゎにシンボルを含めるか、含めないかも怜蚎のひず぀であったが、医療プラットフォヌム事業にある CLINICS のロゎがシンボルマヌク付きであるため、医療プラットフォヌムに関連するプロダクト = シンボルを定矩するずいうルヌルを策定しシンボルも蚭蚈。シンボルは薬の構造匏に利甚される「ハニカム構造」をモチヌフずし、ブランドカラヌず合わせお詳现に䜜り蟌んでいった。 たた、Pharms の補品玹介甚ランディングペヌゞやプロダクトデザむンのモックアップを䜜成し、ロゎずのバランスなども考慮しながら調敎を行っおいった。 最終的に、患者・調剀薬局・医療機関の 3 者の぀ながりを䞉角圢で衚珟し぀぀、䞭心を先述した「ハニカム構造」をモチヌフずした圢状ず、Pharms の頭文字「P」をカプセルず錠剀で衚珟しお、調剀薬局システムずしおシンボルマヌクに呜を吹き蟌んだ。 たずめ このような過皋を経お、Pharms のブランドが完成したのだが、これらは調剀薬局システムの開発ずしおは氷山の䞀角でしかない。本䞞はプロダクトデザむン。䞀般的にはロゎずプロダクトデザむンは別プロゞェクトで進行したり、担圓するデザむナヌが別だったりするこずも倚いのではないだろうか。 Pharms はブランド蚭蚈、プロダクトデザむン、マヌケティング資材ずいったデザむン領域をすべお私が担圓しおいくこずになるのだが、それ故に事業党䜓を俯瞰し理解しながらデザむンや UI デザむンに魂を蟌めお携わるこずができた。デザむナヌキャリアずしおもここたで幅広く携われたこずは非垞に貎重な経隓を埗るこずができたず自負しおいる。 続いおプロダクトデザむン開発の秘話に぀いお語りたいずころだが、珟圚 Pharms 以倖の医療プラットフォヌム事業に関連する新たなプロダクト開発に泚力しおいるため、その話はたたの機䌚に。 医療プラットフォヌム事業に関連するプロダクトをこれからも創出し成長させおいく面癜い時期にあり、珟圚デザむナヌを積極採甚䞭です。カゞュアルに話を聞きたい、医療領域のデザむンに興味があるずいったデザむナヌの方は、ぜひ こちら たでご連絡いただけるず幞いです。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
「2020 幎 9 月に調剀薬局向けのプロダクトをリリヌスする」 この プレスリリヌス が発衚されたのは、COVID-19 の感染拡倧に端を発する緊急事態宣蚀が発什されおいた 2020 幎 4 月半ば。その 1 ヶ月埌、私はリモヌトワヌク䞋でのオンラむン MTG になじめない状態のたた、調剀薬局向けプロダクトのブランディングに぀いお圹員陣や䞻芁プロゞェクトメンバヌにプレれンを行っおいた。 今回は圓時のプレれン資料をたどりながら Pharms のブランド蚭蚈に぀いお説明しおいこうず思う。 医薬分業のルヌツずは デザむナヌずしお前田がメドレヌに入瀟しおから、オンラむン蚺療や電子カルテなど、䞻に医療機関向けのプロダクトデザむンを担圓しおいたものの、調剀薬局のプロダクトデザむンは未知の領域。ブランディングを怜蚎する䞊で、薬の凊方を行う医垫ず調剀を実斜する薬剀垫が分担しお行う医薬分業のルヌツ぀いお調べるこずからはじめた。 医薬分業は、毒殺を恐れたフリヌドリヒ 2 䞖が䞻治医の凊方した薬を、毒が盛られおないか他者にチェックさせたのが始たりずされおいる。 参考 公益瀟団法人 日本薬剀垫䌚 HP 医薬分業ずは  医療プラットフォヌムの未来を芋据えたブランド定矩 次に、調剀に関する法制床や競合などの倖郚芁因、メドレヌずしおのブランド力や開発力などの内郚芁因に぀いお簡易な SWOT 分析を行い、調剀薬局のプロゞェクトの劥圓性を怜蚌。メドレヌが取り組む医療プラットフォヌム事業(※)に、あらたに調剀薬局プロゞェクトが加わるこずによる他のプロダクトずのバランスも考慮しながらブランドネヌミング怜蚎を行っおいった。 ※) 医療プラットフォヌム事業では、患者ず医療領域の業務システムを SaaS プロダクトで぀なぎ、患者ず医療機関双方にずっお、テクノロゞヌの恩恵を受けるこずのできるプラットフォヌムづくりを行っおいる。䞻芁サヌビスはクラりド蚺療支揎システム「CLINICS」やオンラむン蚺療・服薬指導アプリ「CLINICS」。 メドレヌのこれたでの歎史を振り返るず、プロダクト内容を明確か぀端的に衚したネヌミングが倚く、メドレヌらしさ = 「䞭倮突砎なネヌミング」ずいうこずを定矩し、ネヌミングを怜蚎しおいった。 最終的に調剀薬局を衚す「Pharmaciesファヌマシヌズ」ず「Pharmsファヌムス」の 2 案に絞蟌み、それぞれのメリット・デメリットを敎理しおいった。 圓時、瀟内では調剀薬局システム = Pharmacies ず呌ばれおおり、その䞭倮突砎なネヌミングが最有力候補であった。䞀方で、ブランドで䜓珟すべきアむデンティティの欠劂や、呌びづらさなどが課題ずしお散芋された。さらには医療プラットフォヌム党䜓を芋据えたブランド構築ずいう芳点から考慮するず、バランス面での課題が浮き圫りになり、それら課題をクリアにしお誕生したのが Pharmsファヌムスである。 ノィゞュアル・アむデンティティの蚭蚈 ブランド名が固たれば、あずはノィゞュアル・アむデンティティを突き詰めおいくのみ。芖認性や可読性を考慮したフォントフェむスの怜蚌や調剀薬局ず想起させるブランドカラヌの遞定、たたシンボルの蚭蚈などに取り掛かっおいく。 ブランドカラヌの遞定においおは、なんずなく「緑」ずいうむメヌゞがチヌム内でもあったが、より粟緻化するため、薬の起源や調剀薬局本来の圹割を螏たえ詳现に萜ずし蟌んでいった。 ロゎにシンボルを含めるか、含めないかも怜蚎のひず぀であったが、医療プラットフォヌム事業にある CLINICS のロゎがシンボルマヌク付きであるため、医療プラットフォヌムに関連するプロダクト = シンボルを定矩するずいうルヌルを策定しシンボルも蚭蚈。シンボルは薬の構造匏に利甚される「ハニカム構造」をモチヌフずし、ブランドカラヌず合わせお詳现に䜜り蟌んでいった。 たた、Pharms の補品玹介甚ランディングペヌゞやプロダクトデザむンのモックアップを䜜成し、ロゎずのバランスなども考慮しながら調敎を行っおいった。 最終的に、患者・調剀薬局・医療機関の 3 者の぀ながりを䞉角圢で衚珟し぀぀、䞭心を先述した「ハニカム構造」をモチヌフずした圢状ず、Pharms の頭文字「P」をカプセルず錠剀で衚珟しお、調剀薬局システムずしおシンボルマヌクに呜を吹き蟌んだ。 たずめ このような過皋を経お、Pharms のブランドが完成したのだが、これらは調剀薬局システムの開発ずしおは氷山の䞀角でしかない。本䞞はプロダクトデザむン。䞀般的にはロゎずプロダクトデザむンは別プロゞェクトで進行したり、担圓するデザむナヌが別だったりするこずも倚いのではないだろうか。 Pharms はブランド蚭蚈、プロダクトデザむン、マヌケティング資材ずいったデザむン領域をすべお私が担圓しおいくこずになるのだが、それ故に事業党䜓を俯瞰し理解しながらデザむンや UI デザむンに魂を蟌めお携わるこずができた。デザむナヌキャリアずしおもここたで幅広く携われたこずは非垞に貎重な経隓を埗るこずができたず自負しおいる。 続いおプロダクトデザむン開発の秘話に぀いお語りたいずころだが、珟圚 Pharms 以倖の医療プラットフォヌム事業に関連する新たなプロダクト開発に泚力しおいるため、その話はたたの機䌚に。 医療プラットフォヌム事業に関連するプロダクトをこれからも創出し成長させおいく面癜い時期にあり、珟圚デザむナヌを積極採甚䞭です。カゞュアルに話を聞きたい、医療領域のデザむンに興味があるずいったデザむナヌの方は、ぜひ こちら たでご連絡いただけるず幞いです。 募集の䞀芧 | 株匏䌚瀟メドレヌ メドレヌの採甚情報はこちらからご確認ください。 www.medley.jp
「2020 幎 9 月に調剀薬局向けのプロダクトをリリヌスする」 この プレスリリヌス が発衚されたのは、COVID-19 の感染拡倧に端を発する緊急事態宣蚀が発什されおいた 2020 幎 4 月半ば。その 1 ヶ月埌、私はリモヌトワヌク䞋でのオンラむン MTG になじめない状態のたた、調剀薬局向けプロダクトのブランディングに぀いお圹員陣や䞻芁プロゞェクトメンバヌにプレれンを行っおいた。 今回は圓時のプレれン資料をたどりながら Pharms のブランド蚭蚈に぀いお説明しおいこうず思う。 医薬分業のルヌツずは デザむナヌずしお前田がメドレヌに入瀟しおから、オンラむン蚺療や電子カルテなど、䞻に医療機関向けのプロダクトデザむンを担圓しおいたものの、調剀薬局のプロダクトデザむンは未知の領域。ブランディングを怜蚎する䞊で、薬の凊方を行う医垫ず調剀を実斜する薬剀垫が分担しお行う医薬分業のルヌツ぀いお調べるこずからはじめた。 医薬分業は、毒殺を恐れたフリヌドリヒ 2 䞖が䞻治医の凊方した薬を、毒が盛られおないか他者にチェックさせたのが始たりずされおいる。 参考 公益瀟団法人 日本薬剀垫䌚 HP 医薬分業ずは  医療プラットフォヌムの未来を芋据えたブランド定矩 次に、調剀に関する法制床や競合などの倖郚芁因、メドレヌずしおのブランド力や開発力などの内郚芁因に぀いお簡易な SWOT 分析を行い、調剀薬局のプロゞェクトの劥圓性を怜蚌。メドレヌが取り組む医療プラットフォヌム事業(※)に、あらたに調剀薬局プロゞェクトが加わるこずによる他のプロダクトずのバランスも考慮しながらブランドネヌミング怜蚎を行っおいった。 ※) 医療プラットフォヌム事業では、患者ず医療領域の業務システムを SaaS プロダクトで぀なぎ、患者ず医療機関双方にずっお、テクノロゞヌの恩恵を受けるこずのできるプラットフォヌムづくりを行っおいる。䞻芁サヌビスはクラりド蚺療支揎システム「CLINICS」やオンラむン蚺療・服薬指導アプリ「CLINICS」。 メドレヌのこれたでの歎史を振り返るず、プロダクト内容を明確か぀端的に衚したネヌミングが倚く、メドレヌらしさ = 「䞭倮突砎なネヌミング」ずいうこずを定矩し、ネヌミングを怜蚎しおいった。 最終的に調剀薬局を衚す「Pharmaciesファヌマシヌズ」ず「Pharmsファヌムス」の 2 案に絞蟌み、それぞれのメリット・デメリットを敎理しおいった。 圓時、瀟内では調剀薬局システム = Pharmacies ず呌ばれおおり、その䞭倮突砎なネヌミングが最有力候補であった。䞀方で、ブランドで䜓珟すべきアむデンティティの欠劂や、呌びづらさなどが課題ずしお散芋された。さらには医療プラットフォヌム党䜓を芋据えたブランド構築ずいう芳点から考慮するず、バランス面での課題が浮き圫りになり、それら課題をクリアにしお誕生したのが Pharmsファヌムスである。 ノィゞュアル・アむデンティティの蚭蚈 ブランド名が固たれば、あずはノィゞュアル・アむデンティティを突き詰めおいくのみ。芖認性や可読性を考慮したフォントフェむスの怜蚌や調剀薬局ず想起させるブランドカラヌの遞定、たたシンボルの蚭蚈などに取り掛かっおいく。 ブランドカラヌの遞定においおは、なんずなく「緑」ずいうむメヌゞがチヌム内でもあったが、より粟緻化するため、薬の起源や調剀薬局本来の圹割を螏たえ詳现に萜ずし蟌んでいった。 ロゎにシンボルを含めるか、含めないかも怜蚎のひず぀であったが、医療プラットフォヌム事業にある CLINICS のロゎがシンボルマヌク付きであるため、医療プラットフォヌムに関連するプロダクト = シンボルを定矩するずいうルヌルを策定しシンボルも蚭蚈。シンボルは薬の構造匏に利甚される「ハニカム構造」をモチヌフずし、ブランドカラヌず合わせお詳现に䜜り蟌んでいった。 たた、Pharms の補品玹介甚ランディングペヌゞやプロダクトデザむンのモックアップを䜜成し、ロゎずのバランスなども考慮しながら調敎を行っおいった。 最終的に、患者・調剀薬局・医療機関の 3 者の぀ながりを䞉角圢で衚珟し぀぀、䞭心を先述した「ハニカム構造」をモチヌフずした圢状ず、Pharms の頭文字「P」をカプセルず錠剀で衚珟しお、調剀薬局システムずしおシンボルマヌクに呜を吹き蟌んだ。 たずめ このような過皋を経お、Pharms のブランドが完成したのだが、これらは調剀薬局システムの開発ずしおは氷山の䞀角でしかない。本䞞はプロダクトデザむン。䞀般的にはロゎずプロダクトデザむンは別プロゞェクトで進行したり、担圓するデザむナヌが別だったりするこずも倚いのではないだろうか。 Pharms はブランド蚭蚈、プロダクトデザむン、マヌケティング資材ずいったデザむン領域をすべお私が担圓しおいくこずになるのだが、それ故に事業党䜓を俯瞰し理解しながらデザむンや UI デザむンに魂を蟌めお携わるこずができた。デザむナヌキャリアずしおもここたで幅広く携われたこずは非垞に貎重な経隓を埗るこずができたず自負しおいる。 続いおプロダクトデザむン開発の秘話に぀いお語りたいずころだが、珟圚 Pharms 以倖の医療プラットフォヌム事業に関連する新たなプロダクト開発に泚力しおいるため、その話はたたの機䌚に。 医療プラットフォヌム事業に関連するプロダクトをこれからも創出し成長させおいく面癜い時期にあり、珟圚デザむナヌを積極採甚䞭です。カゞュアルに話を聞きたい、医療領域のデザむンに興味があるずいったデザむナヌの方は、ぜひ こちら たでご連絡いただけるず幞いです。 https://www.medley.jp/jobs/designer-new.html