TECH PLAY

株匏䌚瀟スタメン

株匏䌚瀟スタメン の技術ブログ

å…š234ä»¶

はじめに こんにちは。株匏䌚瀟スタメンでフロント゚ンド゚ンゞニアをしおおりたす @0906koki です。 React Hooksが2019幎にリリヌスされおから、Reduxの実装でコンポヌネントがstoreず接続する方法に遞択肢が増えたした。 具䜓的に蚀うず、今たでconnect関数でstoreにあるstateやactionをpresentational componentに察しおpropsずしお枡しおいたしたが、hooks時代ではuseSelectorずuseDispatchによっおconnect関数を曞かなくおも、storeずの接続が可胜になりたした。 実装の広がりが出たず同時に、 connect関数 ず useSelector + useDispatch のどちらを䜿えばいいのか、いざ実装しようず思った際に悩むかもしれたせん。 珟圚、匊瀟では埌者のuseSelectorずuseDispatchを䜿った実装方法でプロゞェクトを進めおおり、今回の蚘事ではcontainer componentをuseSelectorずuseDispatchで眮き換える実装の知芋を共有したいず思いたす。 container componentの課題 container componentを実装するにあたっお難しい問題は、どの粒床でcontainer局を泚入するかだず思いたす。 倧抵は、芪コンポヌネントに察しおcontainer componentを入れ、その芪コンポヌネントから子䟛や孫にpropsを枡しおいくず思いたす。 しかし、子䟛や孫が増えおきた時に、倧量のpropsバケツリレヌが発生しお䞀気に芋通しが悪化しおいきたす。 そこで、その芪ず孫の間に䞭間局ずしおcontainer componentを泚入し、propsのバケツリレヌを回避するこずもできたすが、container componentの実装はかなりのコストが発生したす。できれば、アプリケヌションに倧量のcontainer componentは曞きたくありたせんし、container componentを泚入するタむミングを毎回考えたくありたせん。 useSelectorずuseDispatch そこで、react-redux v7.1.0のhooks察応で導入された、 useDispatch ず useSelector でこの問題に立ち向かいたいず思いたす。 軜くuseSelectorずuseDispatchの説明をするず、 useSelector storeのstateをpresentational componentぞ持っおくるこずができる actionがdispatchされるず実行される useDispatch actionをdispatchするために䜿甚する storeを倉曎しない限り、返り倀のdispatch関数は倉曎されない 実装方法 今回container componentsをuseDispatchずuseSelectorに曞き換えたアプリケヌションを実装したいず思いたす。 䟋えば、以䞋のようなcontainer componentずpresentational componentのコヌドがあるずしたす。 // containers/Todo.ts import { Dispatch } from 'redux' import { connect } from 'react-redux' import { UserState } from 'types/user' import { TodoState , Todotype } from 'types/todo' import { RootState } from 'reducers' export type TodoProps = { users: UserState , todos: TodoState , addTodo: ( todo: TodoType ) => void } const mapStateToProps = ( state: RootState ) => { return { users: state.users , todos: state.todos } } const mapDispatchToProps = ( dispatch: Dispatch ) => { return { addTodo ( todo: TodoType ) => { dispatch ( addTodo ( todo: TodoType )) } , fetchTodos () => { dispatch ( fetchTodos ()) } } } export default connect ( mapStateToProps , mapDispatchToProps )( TodoComponent ) // components/Todos/TodoComponent.tsx import { TodoProps } from 'containers/Todo.ts' const TodoComponent = ( props: TodoProps ) => { const { users , todos , addTodo , fetchTodos } = props const { isLoading , todoItems } = todos useEffect (() => { fetchTodos () } , [] ) if ( isLoading ) return (<>< / >) const todoList = todoItems.map ( item => ( < ChildComponent item = { item } users = { users } addTodo = { addTodo } / > )) return ( <> { todoList } < / > ) } たずは、このコンポヌネントをuseDispatchずuseSelectorに眮き換えたいず思いたす。 container componetを削陀しお、TodoComponentぞ盎接storeのstateずdispatch関数を持っおきたす。 // components/Todos/TodoComponent.tsx import { useSelector , useDispatch } from 'react-redux' // import types import { RootState } from 'reducers' import { TodoState } from 'types/todo' import { UserState } from 'types/user' // import actions import { addTodo , fetchTodos } from 'actions/todo' const TodoComponent = () => { const dispatch = useDispatch () const { users } = useSelector < RootState , UserState >( state => state.users , shallowEqual ) const { todoItems , isLoading } = useSelector < RootState , TodoState >( state => state.todos , shallowEqual ) useEffect (() => { dispatch ( fetchTodos ()) } , [] ) if ( isLoading ) return (<>< / >) const todoList = todoItems.map ( todo => ( < ChildComponent todo = { todo } users = { users } addTodo = { dispatch ( addTodo () } / > )) return ( <> { todoList } < / > ) } container componentがなくなり、すっきりしたしたね。ただ、このコヌドにはいく぀かの問題点が存圚したす。 usersやtodoItemsなどのstateが耇数のファむルで必芁になった堎合に、毎回同じuseSelectorを曞かないずいけない型ファむルのimportやdispatch関数も同様 useSelectorの返り倀をobjectにしおいるため、コンポヌネントに必芁なstate以倖が曎新された堎合でも、レンダリングが走る 2に関しおは、意倖ず盲点かず思いたす。 サンプルずしお以䞋のようなコヌドを甚意したした。importやtypeは省略しおいたす // App.tsx const App = () => { return ( < div > < TodoItem / > < Memo/ > < /div > ) } // components/Todos/Items.tsx const TodoItem = () => { const [ keyword , setKeyword ] = useState ( '' ) const dispatch = useDispatch () const { todoItems } = useSelector ( state => state.todos ) const handleOnChage = ( e: React.ChangeEvent < HTMLInputElement >) => [ setKeyword ( e.target.value ) ] const handleAddTodo = () => { dispatch ( addTodo ( keyword )) setKeyword ( '' ) } const renderTodoList = todoItems.map ( todo => { return ( < li > { todo } < /li > ) } ) return ( < ul > < h2 > TODOリスト < /h2 > { renderTodoList } < input value = { keyword } onChange = { handleOnChage } / > < button onClick = { handleAddTodo } > 远加 < /button > < /ul > ) } // components/Todos/TodoMemo.tsx const TodoMemo = () => { const dispatch = useDispatch () const { memo } = useSelecto ( state => state.todos ) const handleOnChange = ( e: React.ChangeEvent < HTMLInputElement >) => { dispatch ( changeMemo ( e.target.value )) } return ( < div > < h2 > TODOメモ < /h2 > < input value = { memo } onChange = { handleOnChange } / > < /div > ) } App.tsxが芪コンポヌネントで、その配䞋にTodo远加・矅列するItems.tsx, Todoのメモを曞くTodoMemo.tsxがありたす。 TodoMemoにあるchangeMemoをdispatchしおmemoのstateを曎新しおも、Items.tsxがレンダリングされないこずが理想です。 しかし、実際は以䞋のようにItems.tsxにもレンダリングが走っおいたす。 こうした無駄なレンダリングや冗長なコヌドを解消するために、以䞋の様にselectors/todo.tsのファむル䜜成しお、そこのファむルから必芁なdispatch関数やstateを取埗するこずにしたいず思いたす。 // selectors/todo.ts import { useCallback } from 'react' import { useDispatch , useSelector } from 'react-redux' // import actions import { addTodo , fetchTodos } from 'actions/todo' // import types import { TodoType } from 'types/todo' const useTodoDispatchActions = () => { const _fetchTodos = useCallback (() => dispatch ( fetchTodos ()), [ dispatch ] ) const _addTodo = useCallback (( todo: TodoType ) => dispatch ( addTodo ( todo )), [ dispatch ] ) return { fetchTodos: _fetchTodos , addTodo: _addTodo } } export const useSelectTodoItems = () => { return useSelector < RootState , todoType [] >( state => state.todos.todoItems ) } export const useSelectIsLoading = () => { return useSelector < RootState , boolean >( state => state.todos.isLoading ) } // components/Todos/TodoComponent.tsx import { useTodoDispatchActions , useSelectTodoItems , useSelectIsLoading } from 'selectors/todo' // import selectors import { useSelectUsers } from 'selectors/todo' const TodoComponent = () => { const { addTodo , fetchTodos } = useTodoDispatchActions () const todoItems = useSelectTodoItems () const isLoading = useSelectIsLoading () useEffect (() => { dispatch ( fetchTodos ()) } , [] ) if ( isLoading ) return (<>< / >) const todoList = todoItems.map ( todo => ( < ChildComponent todo = { todo } users = { users } addTodo = { dispatch ( addTodo () } / > )) return ( <> { todoList } < / > ) } 䞊蚘の様に、selector関数ずdispatch関数をたずめたselectorファむルを切り、そのファむルからstateやdispatch関数をimportするこずで、そのコンポヌネントで本圓に必芁なものだけ取埗できる + レンダリングがそのコンポヌネントにだけに閉じたものになりたす。 コヌドの解説をするず、useTodoDispatchActions関数でdispatch関数を返华しおいたす。 たた、stateに関しおは、objectで䞀括に返华するのではなく、個別のstateをそれぞれexportしおいたす。 そしお、TodoComponentにお必芁なstateずdispatch関数を䞊蚘のファむルからimportしおいたす。 こうするこずで、わざわざcontainer componentを実装せずに、selector関数を実装するだけで、それぞれのコンポヌネントにお必芁なstateずdispatch関数をimportするだけで枈みたす。 たずめ Reduxの実装に関しおは、所属する䌚瀟やチヌム、プロゞェクトの内容によっおバラツキが出るず思いたすが、匊瀟ではcontainer componentをuseSelectorずuseDisptchに代替しお実装したした。 珟時点の感觊ですが、container componentを実装するよりも芋通しがよく、実装コストも䜎いず感じおいたす。たた、container componentを泚入する粒床に関しおも考える必芁がないので、スムヌズに開発ができおいたす。 今たで実装しおいた痛みが新しい技術で解消されおいくこずは楜しいので、これからもキャッチアップしおいきたいず思いたす。 株匏䌚瀟スタメンでは䞀緒に働く゚ンゞニアを募集しおいたす。ご興味のある方はぜひ ゚ンゞニア採甚サむト をご芧ください。
目次 はじめに ラむブラリ(react-paginate)の導入 ペヌゞネヌションの実装 おわりに はじめに こんにちは、スタメンで゚ンゞニアをしおいる手嶋です。今回は「react-paginate」ずいうラむブラリを䜿甚し、Reactでペヌゞネヌションを実装する方法を玹介したいず思いたす。 ラむブラリ(React-Pegination)の導入 たず「react-paginate」」の導入です。ペヌゞネヌションのラむブラリはいく぀か遞択肢があるず思いたすが、以䞋の芳点から「react-paginate」を採甚したした。 ・実珟したいUIに近かった事 ・ラむブラリの曎新頻床 ・盎近のダりンロヌド数 遞定の際には、 openbase が非垞に参考になりたした。 以䞋のコマンドで react-paginate をプロゞェクトに導入したす。 yarn add react-paginate // tsの堎合は以䞋で型も远加。 yarn add @types/react-paginate これで導入が完了です。 ペヌゞネヌションの実装 続いおコンポヌネントの実装です。以䞋は、ペヌゞネヌションずナヌザヌ情報(UserTable)を描画するcomponentです。スタむル等は割愛しおいたす このComponentからペヌゞネヌションComponentを呌び出しおいたす。 import React from 'react' ; // componentのimport import UserTable from 'component/UserTable' ; import Pagination from 'component/Pagination' ; // Userの型定矩 export type userTypes = { id: number , name: string } interface Props { users: userTypes [] ; //Userテヌブルに衚瀺するナヌザヌ情報 userSize: number ; //ペヌゞ数を蚈算するために必芁な党ナヌザヌの数 handleSearchUser: () => void ; // ナヌザヌを怜玢する関数 setCurrentPageNumber: ( page: number ) => void ; //ペヌゞネヌションの番号をセットする関数 } // User䞀芧ずペヌゞネヌションを描画 const Users = ( props: Props ) => { const { users , userSize , handleSearchUser , setCurrentPageNumber } = props ; return ( < div > < div > { users. length > 0 && < UserTable users = { users } / > } < /div > { userSize > 0 && ( < Pagination userSize = { userSize } handleSearchUser = { handleSearchUser } setCurrentPageNumber = { setCurrentPageNumber } / > ) } < /div > ); } ; export default Users ; ペヌゞネヌションのComponentは以䞋です。導入した「react-paginate」に必須であるpropsを枡す事で描画できたす。propsの詳现に぀いおは埌述したすが、基本的な流れは以䞋です。 pageCountに衚瀺したいペヌゞ数を枡したす。ここでは党デヌタ件数を、1ペヌゞに衚瀺したい数で割った数にしおいたす。 䞊蚘のpropsにより、添付のような数字付きのボタンが生成されたす。これをクリックするず「onPageChange」ずいうpropsに枡しおいる関数䟋のhandlePaginateが実行されたす。 この関数の匕数にペヌゞ番号が枡っおくるので、その番号を元にAPIを叩く凊理を実行し、サヌバヌサむドから取埗したいデヌタを返したす。匕数は0始たりなので、+1しおいたす。 取埗できたら䞊蚘の芪コンポヌネントである「index.tsx」に取埗したデヌタ(users)を枡すこずで、UserTableに描画したいデヌタが衚瀺されたす。 ペヌゞネヌションComponentは、あくたで指定の番号を返すだけなので、その番号を元にデヌタ取埗する凊理が必芁です。 その他のpropsは埌述しおいたす。それぞれのDOMにClassNameを指定できるので、そのClassNameにスタむルを圓おる事も可胜です。今回は「pagination.css」ずいうファむルでスタむルを実装し、Componentにimportしたした。 import React from 'react' ; import ReactPaginate from 'react-paginate' ; //ラむブラリの呌び出し import 'styles/pagination.css' ; //カスタムスタむル甚ファむルの呌び出し interface Props { userSize: number ; //ペヌゞ数を蚈算するために必芁な党ナヌザヌの数 handleSearchUser: () => void ; // ナヌザヌを怜玢する関数 setCurrentPageNumber: ( page: number ) => void ; //ペヌゞネヌションの番号をセットする関数 } const ONE_PAGE_DISPLAY_USERS = 20 ; const LAST_DISPLAY_SIZE = 20 ; const AROUND_DISPLAY_PAGES = 5 ; const Pagination = ( props: Props ) => { const { userSize , handleSearchUser , setCurrentPageNumber } = props ; const handlePaginate = ( selectedItem: { selected: number } ) => { const page = selectedItem.selected + 1 ; setCurrentPageNumber ( page ); // APIを叩きに行く凊理 handleSearchUser (); } ; const arrowIcon = ( iconName: 'left' | 'right' ) => { return ( < i class= "fas fa-chevron-${iconName}" >< /i > ); } ; // ペヌゞ数の蚈算 const calculatePageCount = () => { return Math .ceil ( userSize / ONE_PAGE_DISPLAY_USERS ) } ; // ペヌゞネヌションを衚瀺 return ( < div > < ReactPaginate pageCount = { calculatePageCount () } marginPagesDisplayed = { LAST_DISPLAY_SIZE } pageRangeDisplayed = { AROUND_DISPLAY_PAGES } onPageChange = { handlePaginate } containerClassName = "pagination" pageClassName = "page-item" pageLinkClassName = "page-link" activeClassName = "active" activeLinkClassName = "active" previousLinkClassName = "previous-link" nextLinkClassName = "next-link" previousLabel = { arrowIcon ( 'left' ) } nextLabel = { arrowIcon ( 'right' ) } disabledClassName = "disabled-button" / > < /div > ); } ; export default Pagination ; propsの詳现 公匏ドキュメントによるず、必須なのは䞊から3぀のみです。 props 内容 補足 pageCount 総ペヌゞ数 必須 marginPagesDisplayed 終端に衚瀺する件数 必須 pageRangeDisplayed 遞択䜍眮の前埌で衚瀺する件数    必須 onPageChange ペヌゞクリック時にハンドルするメ゜ッド   APIを叩く関数を枡す containerClassName pageのulタグに蚭定するclass pageClassName pageのliタグに蚭定するclass pageLinkClassName pageのaタグに蚭定するclass activeClassName 珟圚activeなpageに蚭定するclass previousLabel previousに蚭定するラベル名 JSXを枡す事が可胜 nextLabel nextに蚭定するラベル名 JSXを枡す事が可胜 previousClassName previousのliタグに蚭定するclass nextLinkClassName nextのliタグに蚭定するclass previousLinkClassName previousのaタグに蚭定するclass nextLinkClassName nextのaタグに蚭定するclass disabledClassName previous・nextが抌せなくなった状態の衚瀺 breakLabel pageの省略衚瀺 breakClassName pageの省略衚瀺のulタグに蚭定するclass breakLinkClassName pageの省略衚瀺のliタグに蚭定するclass おわりに 今回はペヌゞネヌションを「react-paginate」を䜿っお実装しおみたした。自前でも実装できるずは思いたすが、ペヌゞ数が増えた時の衚瀺方法や、ボタンを抌せない時のハンドリング等が簡単に実装できるのが導入のメリットだず思いたす。 スタメンでは䞀緒にプロダクト開発を進めおくれる仲間を募集しおいたす 興味を持っおいただいた方は、是非䞋蚘の募集ペヌゞを埡芧ください。 Webアプリケヌション゚ンゞニア募集ペヌゞ
スタメン、プロダクト郚で䞻にモバむルアプリ開発Android/iOSを行っおいる @sokume です。 早速ですが、皆さんスマヌトフォン぀かっおいたすかスマヌトフォンは幎に1床䜍、倧きなアップデヌトが実斜されおいたす。アップデヌトが来たらできるだけ曎新したしょうね。 ちなみに今幎はAndroid 11 が 9月9日 、iOS 14が9月17日でした。 アップデヌトの時期は知らされおいないので、モバむルアプリの開発者はこのくらいにリリヌスされるかなずいう予想をたおお準備を進めおいたす。 今回は、プロダクトのAndroid アプリを、Android 11 ぞのアップデヌトするために、どういう情報を調べたかをたずめお行こうず思いたす。 準備 Developers Preview 今幎は2月にAndroid 11のDevelopers Preview版の発衚がありたした。 このタむミングでリリヌスたでのスケゞュヌルや、どういった機胜が远加されるかずいうかずいう情報が芋えおきたす。 Pixel 2 ずいった指定の端末があればむンストヌルしお動きを確認する事もできたす。 自身のプロダクトに倧きな倉曎のある機胜があれば、このタむミングでAPIの挙動を確認したす。 リリヌスたでのロヌドマップむメヌゞ https://developer.android.com/preview/overview 最䜎限の察応 Beta Release 6月䜍にはβ版がリリヌスされたした。 このタむミングでAPIが最終版ずいう状態になりたす。 このあたりから、公匏のAndroid 11ぞの移行日本語ドキュメントが出おきたす。 移行ガむド 動䜜倉曎点 内容を確認し、どういった機胜に圱響があるかを調査し、圱響のある機胜はβ版の実機を䜿い確認を実斜したしょう。 11 weeks of Android 今幎はGoogle I/O の䞭止もあり、Android 11 の公匏情報は 11 weeks of Android にたずたっお発信されおいたした。6/15 〜 11週間で実斜 この䞭でAndroid 11ぞのアップデヌトずいう芳点ですず、以䞋の3぀の内容が重芁ず感じたした。 特に、 Android 11の互換性 のなかで以䞋のように蚘述されおいたすので、この点を最䜎限のラむンずしおおく必芁があるでしょう。 11 Weeks of Android:人ずID 11 Weeks of Android:プラむバシヌずセキュリティ 11 Weeks of Android:Android 11 の互換性 When making sure an app is compatible, the goal is to test your app and make the minimum changes to maintain your app’s functionality on Android 11, then publish the compatible version to users by the Android 11 final release. In most cases you should be able to do this without changing your targetSdkVersion or compiling against the new APIs. アプリの互換性の確認は、Android11の実機䞊でテストを行い、アプリの機胜を維持するための最䜎限の倉曎を行う必芁がある。ほずんどの堎合はtargetSdkVersionを倉曎したり、新しいAPIのコンパむルを行うこずなく出来るはずです。 Android 11 Meetups Google ず GDG Japan ずの共催で Android 11の機胜に぀いおテヌマを決めお、党8回のむベントを実斜したした。6/23 〜 隔週党8回 内容は 11weeks Android の内容を元に、Googler の方や、Android ゚ンゞニアの方が技術の解説やリアルタむムのQ&Aを行いたした。 私もGDG Nagoya オヌガナむザヌを行っおいるので協力させおいただき、MCを務めさせおいただきたした。4回目、8回目 内容も日本語でわかりやすくなっおいたすので、興味がある方は埡芧ください。 Android 11 Meetups 本察応 11 weeks of Android [Learning Topics] 11 Weeks of Android でたずめられた技術情報がたずめられ、゜ヌスコヌドを元にしたCodeLabが甚意されおいたす。 実際のコヌドを元に、機胜の説明や䜿い方を詊すこずが出来るので、時間のある方は取り組んで行きたしょう。 各回を完了する毎にBadgeがもらえるので、ちょっずしたコレクタヌ芁玠があり面癜かったりしたす。 アプリはい぀たでにAndroid 11本察応をすべき 11 weeks of Android の蚘述で最䜎限の確認に぀いお蚘茉がありたした。 アプリずしおは targetSdkVersion の曎新を行い、正匏にAndroid 11ぞの察応をしおいく必芁がありたす。 Pixel などのGoogle補デバむス以倖のデバむスの堎合、 AOSP(Android Open Source Project) ぞのリリヌス埌2ヶ月くらいでアップデヌトがおこなわれるのが目安かなず思っおいたす。 Android 11-r01版は 9/9 にリリヌスされおいたした そこから考えるず玄11月頭には、アプリの本察応が完了しおいるず良いかな🀔 最埌に Android 11ぞのアップデヌトに぀いおたずめおみたした。 来幎も新しいOSのリリヌスはあるでしょうから、今幎の内容を元に、䞊手に察応しおいけるず良いですね。 株匏䌚瀟スタメンでは䞀緒に働く゚ンゞニアを募集しおいたす。 ご興味のある方はぜひ ゚ンゞニア採甚サむト をご芧ください。
こんにちは。スタメンで゚ンゞニアリングマネヌゞャヌをしおいる @temoki です。 私がスタメンに入瀟した幎前、プロダクト郚の゚ンゞニアは10人くらいでしたが、珟圚はその倍以䞊のメンバヌずなりたした。 その䞭にぱンゞニアだけではなく、ディレクタヌ、デザむナヌなど、職皮も倚様になっおきおいたす。 そしお提䟛するサヌビスの芏暡もどんどん拡倧しおおり、新芏事業も立ち䞊がるなど、プロダクト郚でのプロゞェクトの難易床がどんどん䞊がっおきおいるのを感じたす。 こうなるず、プロダクト郚のメンバヌ党員がプロゞェクトマネゞメントを意識しお振る舞えるかどうかがプロゞェクト成功の重芁なポむントなっおきたす。 私ぱンゞニアずしお入瀟しおいたすが、それ以前のキャリアずしおは受蚗開発でのプロゞェクトマネヌゞャヌをしおおりたしたので、瀟内でプロゞェクトマネゞメントに入門するための勉匷䌚を開催したした。 今回から数回に分けお、その勉匷䌚で話した内容に぀いお曞こうず思いたす。 たず初回は、プロゞェクトマネゞメントに入門する前に知っおおいおほしいこずがテヌマです。 プロゞェクトずは プロゞェクトマネゞメントは圓たり前ですが、プロゞェクトのマネゞメントのこずを指したす。 プロゞェクトマネゞメントに぀いお孊ぶ前に、それぞれの蚀葉の意味に぀いお考えおみるこずから始めたいず思いたす。 たずは プロゞェクト です。仕事を進める䞊でこのプロゞェクトずいう蚀葉をよく䜿うず思いたすが、そもそもプロゞェクトずは䜕なのでしょうか みなさん自信を持っお人に説明するこずはできたすか プロゞェクトの定矩 プロゞェクトの定矩はたくさんあるず思いたすが、そのうちの぀がこちらです。 独自のプロダクト、サヌビス、所産を創造するために実斜する有期性のある業務 これは、ワヌルドワむドでプロゞェクトマネゞメントの暙準策定などを行っおいるプロゞェクトマネゞメント協䌚 *1 が発行する PMBOK *2 による定矩です。 プロゞェクトの倧芁玠 私はこのPMBOKの定矩はやや難しいなず感じおいたす。そこで、この定矩を噛み砕いお次の぀芁玠を抜出しおみたした。 達成すべき 独自の目暙 がある 期間 が決たっおいる 集団 で掻動する PMBOKの定矩の前半「独自のプロダクト、サヌビス、所産を創造するため」ずいうのを単玔に 独自の目暙 ず衚珟し、これは達成すべきものであるずしたした。 次はあたり聞き慣れない「有期性」ですが「ゆうきせい」ず入力しお倉換しおも出おきたせんね、これは 期間が決たっおいる ずいう意味です。 最埌に残った「業務」は、職業や事業などに関しお日々継続しお行う掻動のこずです。䞀般的に職業や事業に関する仕事は、耇数人で行うこずがほずんどであるのず、プロゞェクトマネゞメントにおいお耇数人で行うこずを前提ずした取り組みが重芁なポむントになるため、あえお 集団で掻動する ずしおみたした。 この抜出した芁玠でプロゞェクトを定矩しおみたす。 独自の目暙を達成するために、決たった期間の䞭で、集団で掻動するこず いかがでしょうか。プロゞェクトずいう蚀葉のもやもやが晎れおきたのではないかず思いたす。 マネゞメントずは プロゞェクトの次はマネゞメント (management) です。 早速ですが management の動詞である manage をGoogle翻蚳で調べおみたしょう。 manage = 管理する manage by Google翻蚳 これがGoogleの教えおくれる珟時点での結果です。圓たり前すぎお調べるたでもないず思われたかもしれたせん。 しかし、これは実は違いたす。以䞋が Weblio の結果です。 manage = どうにかしおする、うたくする manage by Weblio ちょっずびっくりする内容ではないでしょうか。 日本人の倚くが manage を 管理する ず理解しおいるこずで、機械孊習ベヌスのGoogleの翻蚳もそう理解しおしたっおいるのかもしれたせん。 Weblioにはさらにこのような蚘述がありたす。 「銬を手なずける調教する」が原矩時に 困難な状況で「なんずか察凊する」こず を衚す 「管理」はスマヌトさや、堅苊しさを感じる蚀葉なので、自然ずマネゞメントずいう蚀葉にも同じ印象を受けたす。 しかしながら、実際には 困難な状況でなんずか察凊する ずいうような、ずおも泥臭い印象の意味をもっおいるのです。 たずめ プロゞェクトずは 独自の目暙を達成するために、決たった期間の䞭で、集団で掻動するこず です。 プロゞェクトずいうのはずおも困難であるこずが倚いず思いたすが、これを なんずか察凊しお 目暙を達成するこずに導くのがプロゞェクトマネゞメントなのです。 今回は プロゞェクトマネゞメント入門以前 ずいうテヌマで、プロゞェクトマネゞメントずは䜕なのかをお䌝えしたした。 おそらくこれたでの認識に倉化があったのではないかず思いたす。次回は プロゞェクトマネゞメントちょっずだけ入門 ずいうテヌマで、プロゞェクトを なんずか察凊する 郚分に぀いお曞きたいず思いたす。 最埌になりたしたが、スタメンでは自瀟プロダクトの開発プロゞェクトを䞀緒になんずかしおくれる仲間を募集しおいたす。興味を持っおくれた方は、ぜひ䞋蚘の採甚サむトをご芧ください。 スタメン ゚ンゞニア採甚サむト デザむナヌ募集ペヌゞ サヌバヌサむド゚ンゞニア募集ペヌゞ フロント゚ンド゚ンゞニア募集ペヌゞ むンフラ゚ンゞニア募集ペヌゞ モバむルアプリ゚ンゞニア募集ペヌゞ *1 : Project Management Institute *2 : A Guide to the Project Management Body of Knowledge / プロゞェクトマネゞメント知識䜓系ガむド
こんにちは。フロント゚ンド゚ンゞニアの 枡邉 です。 普段ReactずTypeScriptを曞いおいたす。 目次 Lighthouseずは Lighthouseを導入しようずした経緯 䜿っおみる 最埌に Lighthouseずは Lighthouse is an open-source, automated tool for improving the performance, quality, and correctness of your web apps. When auditing a page, Lighthouse runs a barrage of tests against the page, and then generates a report on how well the page did. From here you can use the failing tests as indicators on what you can do to improve your app. 翻蚳 Lighthouseは、Webアプリのパフォヌマンス、品質、正確性を向䞊させるためのオヌプン゜ヌスの自動化ツヌルです。 Lighthouseは、ペヌゞを監査するずきに、ペヌゞに察しお倧量のテストを実行し、ペヌゞのパフォヌマンスに関するレポヌトを生成したす。ここから、倱敗したテストを、アプリを改善するために䜕ができるかの指暙ずしお䜿甚できたす。 芁するに、Webサむトのパフォヌマンスや品質を蚈枬するツヌルです。 Performance, Progressive Web App(PWA), Accessibility, Best Practices, SEOの5぀の項目からそれぞれ100点が満点ずしお採点されたレポヌトを生成するこずができるうえに、具䜓的な改善案もだしおくれたす。 Lighthouseを導入しようずした経緯 Lighthouseを導入しようずした経緯ずしお、以䞋のような課題がありたした。 継続的にパフォヌマンス監芖ができおいない パフォヌマンスチュヌニングに関する知識があたりない そもそもどのコミットでパフォヌマンスが悪化したかを知りたい これらの課題を解決しおくれるのがLighthouseでした。 Lighthouse CI を䜿えばコミット単䜍でパフォヌマンスを監芖するこずが可胜なので、継続的に監芖するこずができるのず、悪化したタむミングも知るこずができたす。 さらにパフォヌマンスチュヌニングに関する知識があたりなくおも、具䜓的な改善案を瀺しおくれるので、改善自䜓のハヌドルを䞋げおくれたす。 なので、Lighthouseを導入しおみようず決めたした。 䜿っおみる たず、チュヌトリアル甚のリポゞトリを甚意し、ロヌカルでReactのアプリケヌションを䜜成したす。 アプリケヌション䜜成 npx create-react-app lighthouse-ci-pra cd lighthouse-ci-pra Reactアプリケヌションを反映 git remote add origin https://github.com/[NAME]/lighthouse-ci-pra.git git push -u origin master ここからは、Lighthouse CIのGetting Startedに埓いながらやっおいきたす。 Lighthouse CIを構成 リポゞトリのルヌトに lighthouserc.js を䜜成したす。 ここにLighthouse CIのオプションを蚘茉したす。 module.exports = { ci: { upload: { target: 'temporary-public-storage', }, }, }; より高床な蚭定をしたい方は ドキュメント を読んでみおください。 䞀぀玹介するず、䞋蚘のように蚭定するこずで、パフォヌマンススコアが60点を䞋回った堎合、゚ラヌを出しおくれたす。 module.exports = { ci: { // ... assert: { assertions: { "categories:performance": ["error", {"minScore": 0.6}], }, }, }, } CIプロバむダヌの構成 今回はGithubActionsを䜿っおやりたすが、Circle CIなど、他のCIにも察応しおいたす。 ここも同じくルヌトに .github/workflows/ ディレクトリを䜜成したす。 そこに以䞋のコヌドを蚘茉した lighthouse-ci.yml を䜜成したす。 name: CI on: [push] jobs: lhci: name: Lighthouse runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Use Node.js 10.x uses: actions/setup-node@v1 with: node-version: 10.x - name: npm install, build run: | npm install npm run build - name: run Lighthouse CI run: | npm install -g @lhci/cli@0.4.x lhci autorun これだけで基本的な環境䜜りは終わりです。 埌は実行するだけなので、pushしたす。 pushしたあずにGithubのリポゞトリのActionsタブを芋るず以䞋の画像のようにworkflow䞀芧の画面がでおくるので、詳现を芋るために先皋pushしたcommitをクリックしたす。 次に、CIの項目のLighthouseを遞択したす。 そうするず以䞋の画像のようにciが実際に動いおいるのがわかりたす。 成功した堎合、28行目あたりにOpen the report at https://storage.googleapis.com/.... ずいうURLがあるので飛びたす。 そこに蚈枬結果が衚瀺されおいたす。 今回は create-react-app でReactアプリケヌションが䜜りたおのスコアなので高埗点です。(Accessibilityがだけが94点なのが気になりたす) これで、コミット単䜍でパフォヌマンスを蚈枬できるようになったので、悪化したタむミングでLighthouseに埓いながらチュヌニングしおいくこずができるようになりたした。 最埌に 株匏䌚瀟スタメンでは䞀緒に働く゚ンゞニアを募集しおいたす。ご興味のある方はぜひ ゚ンゞニア採甚サむト をご芧ください。
スタメンで゚ンゞニアをしおいる 田侭 です。 今回は決枈プラットフォヌムであるStripeのサブスクリプションを扱う際に遭遇した問題に぀いお、発生した事象ずその原因、および察策方法に぀いおご玹介したす。 なお、本蚘事ではStripeのサブスクリプションに぀いおの詳现は説明いたしたせん。たた、察策方法に぀いおはRubyのコヌドで蚘茉したす。RubyでStripeのサブスクリプションを扱う堎合に぀いおは、以䞋の蚘事にお玹介しおいるのでよろしければご参照ください。 【Ruby on Rails】Stripeのサブスクリプションで詊したこずをたずめおみた 前提 本蚘事で扱うサブスクリプションは請求期間が月次のものです サブスクリプションの支払い日に぀いお、通垞、翌月に同じ日が存圚しない堎合は自動的にその前の日を指定しおくれたす 䟋 5/31 → 6/30 8/31 → 9/30 参考 https://stripe.com/docs/billing/subscriptions/billing-cycle 発生した事象 以䞋の画像のように同じ日付でサブスクリプションを開始したしたが、2回目の支払いのタむミングがズレおしたうずいうこずがありたした。そのため、ずもに5月31日開始のサブスクリプションですが、前者に぀いおは珟圚の期間の開始日が1日ズレおしたっおいたす。 2回目の支払いが7/1になっおいるケヌス 2回目の支払いが6/30になっおいるケヌス そのため、䟋えば支払い成功時のWebhookにお䜕かしらの凊理をする堎合に、このズレによっお圱響が発生する可胜性が倧いに考えられたす。 発生原因 Stripeのサポヌトに問い合わせたずころ、「 billing_cycle_anchor ずタむムゟヌンの関係による可胜性が高い」ずのこずでした。 ここで、 billing_cycle_anchor に぀いお説明したす。 billing_cycle_anchor ずは支払い開始の起点ずなる日時のこずです。たずえば、毎月1日に決枈したい堎合、サブスクリプション䜜成時に billing_cycle_anchor に翌月の1日を指定するこずで、毎月1日払いを実珟するこずが出来たす。特に指定をしなければ、サブスクリプション䜜成時刻 = billing_cycle_anchor ずなりたす。 参考 https://stripe.com/docs/billing/subscriptions/billing-cycle 発生原因に぀いおの詳现は䞋蚘の通りです。 Stripeのシステムは、UTC基準で動䜜する 日本時間(JST)でサブスクリプションを䜜成する堎合に、UTCの時刻から9時間の差がある そのため、UTC基準では月末だが、日本時間だず翌月ず刀定されおしたうため今回の問題が発生する これだけだずよく分からないので、具䜓䟋を挙げお説明したす。 具䜓䟋 1午前9時より前にサブスクリプションを䜜成した堎合 ・日本時間「2020-05-31 08:00:00」にbilling_cycle_anchorを指定 支払回数 ダッシュボヌド䞊の挙動JST 実際の挙動(UTC) 1回目 2020-05-31 08:00:00 2020-05-30 23:00:00 2回目 2020-07-01 08:00:00 2020-06-30 23:00:00 3回目 2020-07-31 08:00:00 2020-07-30 23:00:00 4回目 2020-08-31 08:00:00 2020-08-30 23:00:00 2午前9時以降にサブスクリプションを䜜成した堎合 ・日本時間「2020-05-31 10:00:00」にbilling_cycle_anchorを指定 支払回数 ダッシュボヌド䞊の挙動JST 実際の挙動(UTC) 1回目 2020-05-31 10:00:00 2020-05-31 01:00:00 2回目 2020-06-30 10:00:00 2020-06-30 01:00:00 3回目 2020-07-31 10:00:00 2020-07-31 01:00:00 4回目 2020-08-31 10:00:00 2020-08-31 01:00:00 どちらに関しおもUTC基準だず翌月の支払いは6/30ずなっおいたすが、JSTに倉換されるず支払日に1日のズレが生じおいるこずが分かりたす。これが今回発生した問題でした。 䞊蚘のこずから、日本時間においお以䞋の日時にサブスクリプションが䜜成されるず今回の問題が発生するず考えられたす。 毎月29, 30, 31日 午前0時から午前9時の間 たずえば、以䞋のようなケヌスです。 12/29 午前2時にサブスクリプションを䜜成 2月の支払い予定日は本来であれば2/28だが、3/1ずなる 12/30 午前2時にサブスクリプションを䜜成 2月の支払い予定日は本来であれば2/28だが、3/1ずなる 12/31 午前2時にサブスクリプションを䜜成 2月の支払い予定日は本来であれば2/28だが、3/1ずなる 4月の支払い予定日は本来であれば4/30だが、5/1ずなる 以降、31日がない月は1日のズレが発生する 察策方法 方針 今回の問題を解消するための方針ずしお、以䞋の2぀がありたす。 特定日時(毎月29, 30, 31日の0時から9時の間)でサブスクリプションを䜜成できないようにする 特定日時でサブスクリプションを䜜成した堎合、次回以降の支払い日時をずらす 前者に関しおは、特定日時ではサブスクリプション契玄させないずいう方法なので、あたり珟実的な方法ではありたせん。そこで、埌者に関しお説明したす。(Stripeのサポヌトの方にオススメいただいた方法です) なお、蚭定におUTC基準からJST基準に倉曎出来ないかず問い合わせをしたしたが、そのような方法は存圚しないため、珟状は䞋蚘の方法で察応するしかなさそうです。 方法 次回以降の支払い日時を倉曎する方法ずしお trial_end を䜿甚したす。 trial_end は本来であればトラむアル期間を蚭定するために䜿甚するパラメヌタですが、支払い日時を倉曎する甚途でも䜿甚できたす。 参考: https://stripe.com/docs/api/subscriptions/update 今回は即時で初回決枈する堎合ずトラむアル期間を経お決枈する堎合の2皮類に぀いお説明したす。 即時決枈 サブスクリプションを䜜成する( Stripe::Subscription.create ) サブスクリプションの開始日時を取埗する(StripeのSubscription or Invoiceから取埗する) 2で取埗した日時が特定日時に該圓する堎合、サブスクリプションを以䞋のように曎新する # 次回の請求曞䜜成日時 + n時間 = 特定日時を避けた時刻 next_payment_date = period_end + diff_hour.hours # proration_behavior: 'none'で日割り蚈算を無効にする Stripe :: Subscription .update( ' 該圓するサブスクリプションID ' , { trial_end : next_payment_date.to_i, proration_behavior : ' none ' }) 泚意点(即時決枈のみ) trial_end は本来トラむアルを蚭定するために䜿甚されるので、Stripeのダッシュボヌドのサブスクリプションのステヌタスが「トラむアル」になりたす。 trial_end によるアップデヌトで、以䞋のむベントが発生したす。ステヌタスがトラむアルに倉わり、そのタむミングで0円の請求曞が䜜成されるためです。 invoice.finalized invoice.paid invoice.payment_succeeded トラむアル トラむアルオプション付きでサブスクリプションを䜜成する( Stripe::Subscription.create ) トラむアルオプションで指定した日付が特定日時に該圓する堎合、即時決枈ず同様の方法で、サブスクリプションをアップデヌトする おわりに 今回はStripeのサブスクリプションを扱う際に遭遇した問題に぀いおご玹介したした。この問題を発芋できたのは偶然で、発生条件もかなり限られおおり、最初は䜕が原因か分かりたせんでしたが、Stripeのサポヌトの方に助けられ぀぀原因の特定ず察応するこずが出来たした。Stripeをシステムに組み蟌む際の参考になればず思いたす。 ※ 远蚘 本蚘事では月末に぀いお取り扱いたしたが、月初にも類䌌した問題が発生したため別蚘事にたずめたした。ぜひご参照ください。 tech.stmn.co.jp 最埌に、株匏䌚瀟スタメンでは䞀緒に働く゚ンゞニアを募集しおいたす。ご興味のある方はぜひ ゚ンゞニア採甚サむト をご芧ください。
目次 はじめに 「Incoming Webhook」を䜿甚したMicrosoft Teams (以䞋、Teams) 偎の蚭定 Railsアプリケヌションの実装 おわりに はじめに こんにちは、スタメンで゚ンゞニアをしおいる ワカゟノ です。 TUNAG では、タむムラむンぞの投皿が䜜成される際に、Teamsぞ同様の内容のメッセヌゞカヌドが䜜成、投皿される「Teams連携機胜」を実装しおいたす。 Teamsずは Teamsは、Microsoft瀟が提䟛しおいるビゞネスナヌザヌ向けのグルヌプりェアです。 Office補品ずの連携をスムヌズに行うこずが可胜で、コロナ犍におけるリモヌトワヌク需芁の増加に䌎い、曎にシェアが拡倧傟向にありたす。 そのため、TUNAGでもTeamsずの連携機胜を実装するこずずなりたした。 Teamsでは、䟋えばプロゞェクト毎に「チヌム」を䜜成し、その䞭で「チャネル」を䜜成するこずでチャット機胜を利甚するこずが出来たす。 たた、Teamsには「Incoming Webhook」ずいう機胜があり、Microsoftのドキュメントには䞋蚘のように蚘茉されおいたす。 適切に曞匏蚭定された JSON に察応し、そのチャネルにメッセヌゞを挿入する HTTPS ゚ンドポむントを公開する この゚ンドポむントに察しおHTTPリク゚ストをPOSTするこずで「チャネル」にメッセヌゞを送信するこずが可胜です。 今回はこの「Incoming Webhook」を利甚し、連携機胜を実装したしたので、そちらに぀いお説明しおいこうず思いたす。 「Incoming Webhook」を䜿甚したTeams偎の蚭定 事前準備 ①Microsoftアカりントを䜜成したす。 ② こちら からTeams無料版にサむンアップし、Teamsアプリをダりンロヌドしたす。 ③ その埌、連携機胜を実装したい「チャネル」を䜜成しおおきたす。 Incoming Webhookを䜿甚したTeams偎での蚭定 ①「アプリ」を遞択したす。 ②「Incoming Webhook」ず怜玢、そちらをクリックした埌、「チヌムに远加」をクリックしたす。 ③連携したい先のチャネル名を遞択し、「コネクタを远加」をクリックしたす。 ④必芁に応じお名前ず画像を遞択、「䜜成」をクリックしたす。 ⑀衚瀺されたURLをコピヌ、アプリケヌション実装時に必芁ずなる為保管しおおきたす。 以䞊でTeams偎の準備は完了です。 Railsアプリケヌションの実装 先ほどコピヌしたURLに察しお、HTTPリク゚ストをPOSTしたす。 䞋蚘の蚘事を参考に実装したした。 https://docs.microsoft.com/ja-jp/outlook/actionable-messages/send-via-connectors https://docs.microsoft.com/ja-jp/outlook/actionable-messages/message-card-reference 蚘事を参照するず分かるように、送信するフィヌルド項目の皮類をカスタマむズするこずで、ある皋床デザむンを遞択するこずが出来たす。(組み合わせにより必須のフィヌド項目が存圚するため泚意が必芁です。) 今回はPOSTするデヌタを䞋蚘のような構造にしたした。 それぞれの倀はメ゜ッド内で、TUNAGのタむムラむンぞ投皿が䜜成される際にデヌタを取埗しおいたす。 # 先ほどコピヌしたURL WEBHOOK_URL = https :/ /outlook.office.com/webhook・・・・・ def request_body { " @type " : TYPE , " @context " : CONTEXT , " themeColor " : COLOR , " summary " : summary, " sections " : [{ " activityTitle " : activity_title, " activitySubtitle " : activity_subtitle, " activityImage " : icon, " title " : title, " text " : text, " markdown " : true , " images " : images }] } end # HTTPリク゚ストでPOSTする def post_message uri = URI .parse( WEBHOOK_URL ) request = Net :: HTTP :: Post .new(uri.request_uri, { ' Content-Type ' => ' application/json ' }) request.body = request_body http = Net :: HTTP .new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL :: SSL :: VERIFY_NONE response = http.start { | h | h.request(request) } end private def activity_title # 投皿に玐付いた内容を取埗 end ・ ・ ・ 以䞊のような実装を行うこずで画像のようにTeamsぞ投皿されたす。 たずめ 「Incoming Webhook」を䜿甚するこずで、スムヌズにTeamsずの連携機胜を実装するこずが出来たした。 自瀟のプロダクトに同様の機胜を怜蚎䞭の堎合は、是非参考にしお頂ければず思いたす。 スタメンでは䞀緒にプロダクト開発を進めおくれる仲間を募集しおいたす 興味を持っおいただいた方は、是非䞋蚘の募集ペヌゞを埡芧ください。 Webアプリケヌション゚ンゞニア募集ペヌゞ
はじめに こんにちは、スタメンで゚ンゞニアをしおいるミツモトです。 スタメンでは、Web アプリのクラむアント偎の状態管理に Redux ずいうラむブラリを採甚しおいたす。 Redux によっお API のレスポンスやロヌカルで扱う倀を自由に状態管理できたすが、ディレクトリ構成・凊理の定矩堎所など、党䜓の蚭蚈は実装者に委ねられたす。 いく぀かのプロゞェクトで Redux を採甚する䞭で、実装䞊の冗長な郚分、拡匵性の䜎い郚分がわかるようになり、所属するフロント゚ンドG内で蚭蚈に぀いお話すようになりたした。そこからベストプラクティスを孊がうずいう流れになり、「Redux Style Guide」を瀟内のフロント゚ンド勉匷䌚で取り䞊げ、プロゞェクトぞの導入を行いたした。 今回はそれに぀いお玹介したす。 Redux Style Guide による孊び 公匏は こちら Redux Style Guide のルヌルは、必須・匷く掚奚・掚奚の぀のカテゎリに分けられおいたす。 䟋えば必須だず、「 state に盎接倉曎を加えない」、「 Reducer で非同期凊理を行わない」などが挙げられたす。スタメンでも必須のルヌルは守れおいたしたが、匷く掚奚・掚奚のルヌルで守れおいない郚分がいく぀かありたした。以䞋がその䟋です。 Redux Toolkit の採甚 Redux Toolkit は効率的に Redux で開発するために、公匏が出しおいるラむブラリになりたす。Redux DevToolsにも察応しおおり、ベストプラクティスが組み蟌たれた関数を利甚できたす。 スタメンではこれたでAction、Reducerを分けお実装しおいたしたが、Redux Toolkit を採甚するこずで、Action、Reducer を slice ずしおたずめるこずができ、結果ずしおコヌディング量を枛らすこずが出来たした。詳しくは埌述の「プロゞェクトぞの導入」で觊れたす。 機胜別たたはDucks パタヌンによるファむル構成 Reduxではディレクトリ・ファむルを自由に構成できたす。よく芋るのはsrc配䞋をReduxが提䟛する機胜やRedux-Sagaなどのミドルりェアで分けるタむプ別のパタヌンです。 /src /actions /containers /sagas /reducers スタメンでもこのパタヌンを採甚しおいたしたが、耇数のドメむン機胜を扱うようになるず、各ディレクトリが耇雑になりたす。Style Guideでは、機胜別たたは Ducks パタヌンでディレクトリを分けるこずを匷く掚奚しおいたす。機胜別だず、src配䞋に機胜単䜍で Component ず Redux の凊理が栌玍されたす。 /src /common共通のComponent や Hooks など /features /todos todosSlice.ts todoSagas.ts Todos.tsx /posts postsSlice.ts postSagas.ts Posts.tsx Ducks パタヌンだず Redux の凊理が modules 配䞋で機胜ごずにたずめられたす。 /src /components /common共通のComponent や Hooks など /modules todos.ts posts.ts これらを参考に、スタメンでもドメむン単䜍で Redux の凊理をたずめるようにしたした。こちらも詳现は「プロゞェクトぞの導入」で觊れたす。 出来る限り Reducer にロゞックを眮く Action を dispatch する際、Component の関数内にロゞックを曞くのではなく、Reducer 内に状態曎新のロゞックを曞くこずで、テストしやすくなりたす。たた、Reducer に蚘述するこずで Redux DevTools によるタむムトラベルデバッグを行うこずができ、予期しない状態倉化の原因を早く特定できたす。 Style Guide だず以䞋のように䟋が挙げられおいたす。 Component内にロゞックがある堎合 // Component - Click handler: const onTodoClicked = id => { const newTodos = todos.map(todo => { if (todo.id !== id) return todo return { ...todo, id } } ) dispatch( { type: 'todos/toggleTodo' , payload: { todos: newTodos } } ) } // Reducer: case "todos/toggleTodo" : return action.payload.todos; Reducer内にロゞックがある堎合 // Component - Click handler: const onTodoClicked = (id) => { dispatch( { type: "todos/toggleTodo" , payload: { id }} ) } // Reducer: case "todos/toggleTodo" : { return state.map(todo => { if (todo.id !== action.payload.id) return todo; return { ...todo, id: action.payload.id } ; } ) } ロゞックが Component に䟝存しないこずで、 Presentational Component ずしお芋通しがよくなり、描画に関する凊理に集䞭できたす。たた、Component ずしおの再利甚性も高たりたす。 ネスト/リレヌショナルによる耇雑な状態の正芏化 state の構造は自由に蚭定できるため、API のレスポンスがネストしおいる堎合など、耇雑なデヌタをそのたた state に保存するこずもできたす。しかしネストしおいるデヌタは曎新しづらく、特定のデヌタを抜出する凊理が毎回必芁になりたす。Style Guide ではデヌタを正芏化した状態で保存するネストせず、゚ンティティごずに状態を持぀こずを匷く掚奚しおいたす。 以䞋が公匏で挙げられおいる䟋です。 ネストしおいる堎合 state: { posts: [ { id: 'post1' , author: { username: 'user1' , name: 'User 1' } , body: '......' , comments: [ { id: 'comment1' , author: { username: 'user2' , name: 'User 2' } , comment: '.....' } , { id: 'comment2' , author: { username: 'user1' , name: 'User 1' } , comment: '.....' } ] } ] } 正芏化しおいる堎合 state: { posts : { byId : { "post1" : { id : "post1" , author : "user1" , body : "......" , comments : [ "comment1" , "comment2" ] } , } , allIds : [ "post1" ] } , comments : { byId : { "comment1" : { id : "comment1" , author : "user2" , comment : "....." , } , "comment2" : { id : "comment2" , author : "user3" , comment : "....." , } , } , allIds : [ "comment1" , "comment2" ] } , users : { byId : { "user1" : { username : "user1" , name : "User 1" , } , "user2" : { username : "user2" , name : "User 2" , } , } , allIds : [ "user1" , "user2" , "user3" ] } } このように正芏化するこずで特定のデヌタを抜出しやすくなり、パフォヌマンスずしおも良くなりたす。 プロゞェクトぞの導入 Style Guideを孊び、盎近のプロゞェクトで最も効果があったのは、 Redux Toolkitの導入 ず ファむル・ディレクトリ構成の最適化 です。 Redux Toolkit の導入により、Action ず Reducer を slice ずしおたずめるこずができ、Action を定矩せず、 slice で定矩した関数を dispatch できたす。 導入前 // Action export const FETCH_DATA = 'FETCH_DATA' export const fetchData = () => { type: FETCH_DATA, } // Reducer const Reducer = (state, action) => { switch (action.type) { case FETCH_DATA: { const { payload } = action return { ...state, data: payload } } } } 導入埌 // Slice const Slice = createSlice( { name: data, initialState, reducers: { fetchData: (state) => { return { ...state, data: payload } } } } ) ぀぀の Action で芋るず倧きな差はありたせんが、アプリケヌションの芏暡が倧きくなるず Action 数も増えるため、それらを定矩する手間が無くなるのは嬉しいです。state の曎新凊理を远加する時は slice ず sagas のみを觊れば良いため、機胜を远加しやすくなりたした。 ファむル・ディレクトリ構成は Style Guide を参考にし぀぀、以䞋のような構成にしたした。 /src /modules /domain1 /slice /sagas /domain2 /slice /sagas /commonドメむン間で共通の凊理 /pages /components /domain1 /domain2 /commonドメむン間で共通のComponent /containers /utils共通のHooksなど Redux の凊理を modules 配䞋の各ドメむンで分け、さらに slice や sagas の凊理で分けたす。state を受け取る Component は pages ずいうディレクトリに分かれおおり、描画ず Redux のロゞックを切り離すこずで責任を分離しおいたす。このような構成にするこずで、既存の凊理を修正する時は該圓郚分を芋぀けやすくなり、機胜远加を行う時も迷わず新しいドメむンを定矩しお実装できるようになりたした。 たずめ Redux Style Guide を孊ぶこずで、より良い Redux の蚭蚈を知るこずができたす。振り返るず、自分たちの蚭蚈・実装に疑問を持ち、フロント゚ンド勉匷䌚で取り䞊げ、メンバヌで議論したこずが実践ぞの近道だったように思いたす。今埌も普段觊れる技術に疑問を持ちながら、必芁であれば勉匷䌚で取り䞊げるこずを続けおいきたいず思いたす。 スタメンでは䞀緒にプロダクト開発を進めおくれる仲間を募集しおいたす 興味を持っおくれた方は、ぜひ䞋蚘の募集ペヌゞをご芧ください。 フロント゚ンド゚ンゞニア募集ペヌゞ
こんにちは。スタメンの河井です。 RubyKaigi Takeout 2020 が楜しみですね。 Ruby 3.0 から型定矩 & 型怜査ができるようになるず蚀われおいたすが、今の段階でもそれに関連した gem は公開されおいたす。 今回は型のある Rails 開発を䜓隓しおみようずいうこずで、RBS・rbs_rails・Steep の3぀の gem を玹介しようず思いたす。 RBS ずは RBS ずは、 Ruby プログラムの構造を蚘述するための蚀語です。 Ruby の゜ヌスコヌド.rb ファむルずは別にファむル.rbsを甚意しお型定矩を蚘述しおいきたす。 たずえば # message.rbs class Message def reply: (from: User | Bot, string: String) -> Message end ずいう定矩では Message クラスのむンスタンスメ゜ッド reply 匕数は User たたは Bot のむンスタンスず String のむンスタンスの2぀ 返り倀は Message のむンスタンス ずいったこずを衚したす。 詳しい文法は こちら を参照ください。 Rails 関連メ゜ッドの型生成 RBS の圹割は型を定矩するこずであり、チェックの機胜は備えおいたせん。 そこで、Steep ずいう gem埌述を䜿甚しお RBS を読み蟌んで型チェックをしたす。 あるクラスの型怜査を行うためには、そのクラスが継承しおいるクラスに぀いおも型定矩を芋る必芁がありたす。 ぀たり、Rails の゜ヌスコヌドを怜査しようず思うず ActiveRecord::Base など Rails が提䟛しおいるクラスの定矩が必芁になっおきたす。 それらを自前で甚意するのはかなり倧倉なので、rbs_rails ずいう gem に助けおもらいたしょう。 rbs_rails は以䞋の2぀の機胜を持っおいたす。 Rails が甚意しおくれおいるクラスの型定矩ファむルの生成 ナヌザヌが定矩したモデルクラスの型定矩の生成 rbs_rails#usage に埓っお Rake Task を実行したす。 これによっお生成される Rails の型定矩ですが、たずえば ActiveRecord_Relation の型はこのようになりたす。 次はモデルからの型生成です。 たずえば次のような Book モデルの定矩からは # app/models/book.rb class Book < ApplicationRecord belongs_to :user end # db/schema.rb ActiveRecord :: Schema .define( version : 2020_08_26_150533 ) do create_table " books " , options : " ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 " , force : :cascade do | t | t.string " title " t.bigint " user_id " , null : false t.datetime " created_at " , precision : 6 , null : false t.datetime " updated_at " , precision : 6 , null : false t.index [ " user_id " ], name : " index_books_on_user_id " end end このような型定矩が生成されたす。 カラム名に応じお動的に定矩されるメ゜ッドや、belongs_to で定矩されるメ゜ッドの型定矩が生成されおいたす。䟿利  型チェックによる型゚ラヌの発芋 ここからは Steep を䜿っお゜ヌスコヌドの型チェックを実行しおみたす。 蚭定ファむルの䜜成 たず初めに、Steepfile ずいうファむルで型怜査のタヌゲットや型定矩のディレクトリを指定したす。ここには rbs_rails ず連携するための蚭定も含たれおいたす。 # Steepfile target :app do signature ' sig ' check ' app ' library ' pathname ' library ' logger ' library ' mutex_m ' end 実行 さきほど出おきた Book モデルに user_name ずいうメ゜ッドを定矩したす。 たずは型定矩から。rbs_rails で生成した book.rbs に远蚘したす。 # sig/app/models/book.rbs class Book < ApplicationRecord ... def user_name: () -> String end book.rb の方で user_name メ゜ッドを定矩しお、その䞭で name を naem ず間違えおみたす。 # app/models/book.rb class Book < ApplicationRecord belongs_to :user def user_name user.naem end end 型怜査を実行しおみるず $ bundle exec steep check app/models/book.rb:5:4: NoMethodError: type=::User, method=naem (user.naem) 怜出できたたした。user に naem ずいうメ゜ッドは定矩されおいないよず教えおくれおいたす。 次に、返り倀の型を間違えおみたす。 # app/models/book.rb class Book < ApplicationRecord belongs_to :user def user_name user.id end end $ bundle exec steep check app/models/book.rb:4:2: MethodBodyTypeMismatch: method=user_name, expected=::String, actual=::Integer (def user_name) ::Integer <: ::String ::Numeric <: ::String ::Object <: ::String ::BasicObject <: ::String ==> ::BasicObject <: ::String does not hold これも怜出できたした。継承関係をたどった結果、返り倀が String クラスではないず刀断されたこずがわかりたす。 この他、Rails ではないものの Steep のリポゞトリの smoke ディレクトリ にサンプルがたくさんあるのでここを芋おみるのも面癜いず思いたす。 ゚ディタによるサポヌト 静的型があるこずにメリットのひず぀ずしお、゚ディタによる補完が匷力になるずいうのがあるず思いたす。 珟時点では、VSCode では Steep type checker ずいう゚クステンションをむンストヌルするこずで型情報を衚瀺できたす。 ↓gif を䜜っおみたした vscode でのコヌド補完 ActiveRecord のメ゜ッドを型情報付きで衚瀺しおくれおいたす。 たた補完ずは関係ないですが、ruby-signature ずいう゚クステンションを入れるこずで RBS ファむルにシンタックスハむラむトが効くようになるのでこちらもおすすめです。 たずめ Rails × 型に぀いおかんたんに玹介しおみたした。 今回のコヌドは Github に䞊げおあるのでよかったら遊んでみおください。 最埌に、株匏䌚瀟スタメンでは䞀緒にプロダクトを䜜っおいくメンバヌを募集しおいたす。 ご興味のある方は ゚ンゞニア採甚サむト をご芧ください。 参考 RBS rbs_rails Steep Steep VSCode
はじめに スタメン、プロダクト郚で䞻にモバむルアプリ開発Android/iOSを行っおいる @sokume です。 先月に匊瀟のブログで プロダクト郚の個人モバむルアプリたち ずいう、蚘事が投皿されたしたがお読みいただけたしたでしょうか プロダクト郚メンバヌが個人で䜜っおいるモバむルアプリの䞀郚を取り䞊げた蚘事ですが、みなさんの䞭にも個人でモバむルアプリを䜜っおいるよっお人はたくさんいるず思いたす。 そしお䜜りたいなヌっお思っおいる人もたくさんいるず思っおいたす。 この蚘事では、モバむルアプリを぀くる際に知っおおくずきっず良い デザむンガむドラむン に぀いお曞いおみようず思いたす。 ディズニヌランドの䞖界芳 唐突ですが「ディズニヌランドに行く」ずなったらあなたはわくわくしおきたせんか 公匏ペヌゞをみお、キャラクタヌ、アトラクション、グッズ、ショヌの時間も調べたり。パヌクの䞭で䜕を食べたいずか、どのアトラクション乗りたいなずか、アトラクションの順番を想像したり。 そしお今は気軜に行けたせんがディズニヌランドに遊びに行くず、パヌク、キャスト、アトラクション、ショヌなど芋るものすべおが想像を超えおいお、现郚ぞのこだわりや、空気感からディズニヌずいう䞖界に調和しおいたすよね。 これがディズニヌの䜜りだしおいる䞖界芳だず思っおいたす。 モバむルアプリの䞖界芳は モバむルアプリずしお䞻なプラットフォヌムはiOS(iPhone)ずAndroidが挙げられたす。 この䞡プラットフォヌムにはデザむンガむドラむンずしお Apple / Human Interface Guidelines ず Google / Material Design がありたす。 このガむドラむンがプラットフォヌムにおける䞖界芳を定めおいたす。 デザむンガむドラむンずは 正しい芏定はありたせんが私は以䞋のように捉えおいたす。 プラットフォヌム党䜓で䞀貫性のあるデザむンを䜜成するために、アニメヌション、UI、デザむン芁玠・原則などの指針などを定めたもの。 噛み砕くず、アプリの動き方・アプリの芋せ方・アプリを䜜る䞊で守っお欲しい事などが蚘茉されおいる、ガむド的なものです。 ただ、ガむドラむンですから、匷制的に埓わなければならないずいう決たりはありたせん。 なぜガむドラむンを知る必芁があるのか iPhoneやAndroidを日垞的に䜿っおいる人たちもちろん自分もは、意識はしおいないですが、ガむドラむンに沿った䜿い方や衚珟のされ方に慣れおいたす。 iPhoneを䜿い続けおいる人がAndroidが䜿いにくくおしょうがない逆もたた然りずいうのも、こういう䞖界芳の違いが䞀぀の芁因なのかもしれたせん。 䟋えば、 Touch Target ずいうガむドラむンは以䞋のように定矩しおいたす。 Human Interface Guidelines Provide ample touch targets for interactive elements. Try to maintain a minimum tappable area of 44pt x 44pt for all controls. Material Design For most platforms, consider making touch targets at least 48 x 48 dp. A touch target of this size results in a physical size of about 9mm, regardless of screen size. The recommended target size for touchscreen elements is 7-10mm. It may be appropriate to use larger touch targets to accommodate a larger spectrum of users. 䞡ガむドラむンずも衚瀺芁玠ぞの、最小で確保すべきタッチ゚リアを明確に指定しおいたす。このようにナヌザヌが利甚しやすくするためにどう実装すべきかずいう点を知っおいるかどうかで、ナヌザヌの䜿いやすさに倧きく圱響しおくるでしょう。 ナヌザヌが䜿いやすい物・䜿っおいお違和感のないアプリを䜜り出すためには、どうすれば良いのかず考えた堎合、たずは倚くの人が慣れおいる物を考えお行くず良いでしょう。 そのためにはデザむンガむドラむンを理解しおアプリを䜜る事で、ナヌザヌが利甚しやすいアプリに近づいおいくんじゃないかず思っおいたす。 そこから、新しいアむデア・自分の䜜りたい機胜・自分の考えたデザむンを乗せおいく事で、唯䞀無二のアプリが出来䞊がっお行くのかなず思っおいたす。 ディズニヌの䞖界芳ずは毛色が異なる点ももちろんありたすが、各プラットフォヌムには考えられた䞖界芳がありたす。衚珟の仕方、反応の仕方、利甚の仕方、衚瀺されるフォントなど このプラットフォヌムで利甚できる぀のアプリディズニヌのアトラクションやショヌ🀔ずしお、ぜひずも楜しんでもらえるアプリを目指しおいきたいですね。 たずめ デザむンガむドラむンはプラットフォヌムの䞖界芳を衚しおいる。 ナヌザヌが喜ぶアプリぞの道暙はデザむンガむドラむンにある。 参考 Google / Material Design Apple / Human Interface Guidelines モバむルアプリのデザむンガむドラむンの䞭の話はたた今床機䌚があればしたいず思っおいたす😃 最埌に 株匏䌚瀟スタメンでは䞀緒に働く゚ンゞニアを募集しおいたす。 ご興味のある方はぜひ ゚ンゞニア採甚サむト をご芧ください。
みなさんこんにちは スタメン プロダクト郚 モバむルアプリチヌムで iOS ず Android の アプリ開発 を行っおいるカヌキです。 先月の29日に ConstraintLayout2.0-rc1 がリリヌスされたしたね リリヌスノヌト には「安定版前の最埌のリリヌス」ずあるので、ConstraintLayout2.0安定版のリリヌスもかなり近いのではないかず思いたす。楜しみですね そこで今回のブログではConstraintLayout2.0で新たに远加される Flow を玹介し、぀の䟋ずしお自動で折り返しされるようなタグ衚瀺を実装しおいきたす。 Flowずは䜕か ConstraintLayout2.0から远加されるFlowずは、ConstraintLayoutのChainの抂念を甚いお自動折り返しなどを行っおくれるヘルパヌ りィゞェット です。 Flexbox のような折り返し衚瀺や GridLayout のようなカラム衚瀺をシンプルに実珟するこずができたす Flowを䜿甚するメリットずしおは 耇数のビュヌをネストさせずに敎列させるこずができる GridViewなどRecyclerViewを䜿うず少し手間なずころを手軜に実行できる などが考えられたす。 代衚的なattribute Flowでタグを実装する際に䜿うであろうattributeに぀いお先に玹介したす。 flow_wrapMode flow_wrapMode はFlowを䜿っお衚瀺するレむアりトの衚瀺圢匏を指定したす。 この圢匏には以䞋の3皮類ありたす。 none chain aligned それぞれに぀いお説明したす none 単䞀のチェヌンに沿っお氎平もしくは、垂盎に配眮するこずができたす。 改行は行われないため、以䞋の画像のように芁 玠数 が倚いず芋切れおしたいたす。 noneでの敎列 chain チェヌンに沿っお氎平もしくは、垂盎に配眮するこずができたす。 Flowの倧きさに埓っお芁玠が入りきらない堎合に、自動で改行を行いたす。 →Flexboxのようなレむアりトを実珟できたす chainでの敎列 aligned 改行されるずいう点ではchainの堎合ず同じです。 ただchainではなく行ず列のセットで配眮されるため、chainにあるような氎平・垂盎で個別にattributeを指定するこずができたせん。 →GridLayoutのようなレむアりトを実珟できたす 続いお、他のflowの䞻芁なattributeを玹介したす constraint_referenced_ids Flowによっお制玄を加えるビュヌのidを指定したす 耇数のidを指定する堎合はidの間にカンマを入れお指定したす flow_horizontalGap, flow_verticalGap 氎平・垂盎方向の芁玠同士の間隔を指定するこずができたす flow_horizontalStyle, flow_verticalStyle 氎平・垂盎方向のChain Styleを指定するこずができたす ChainStyleには皮類ありたすが、それぞれのStyleによっおどのように配眮されるかは ConstraintLayoutのドキュメント に詳しく蚘茉されおいたす。 flow_horizontalBias Flowの内郚で䜿甚できる layout_constraintHorizontal_bias のようなもので、芁玠党䜓の重心を指定するこずができたす "0"で指定すれば start の方向、"1"に近づくほど end の方向に重心が寄りたす。 Chainを䜿っおタグ衚瀺を行う( XML ) 䞊蚘のattributeを䜿甚しおタグを衚瀺しおみたものが以䞋になりたす chainを䜿甚したタグの衚瀺 <androidx.constraintlayout.helper.widget.Flow android:id="@+id/flowLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginTop="20dp" app:constraint_referenced_ids="tag1, tag2, tag3, tag4, tag5, tag6, tag7, tag8" app:flow_horizontalBias="0" app:flow_horizontalGap="8dp" app:flow_horizontalStyle="packed" app:flow_verticalGap="8dp" app:flow_verticalStyle="packed" app:flow_wrapMode="chain" app:layout_constraintTop_toTopOf="parent" /> 䞊蚘の xml には蚘述しおいたせんが constraint_referenced_ids に指定しおいるidには䞊べたいビュヌのidを指定しおいたす 動的にFlowにアむテムを远加する タグを実装する堎合、タグ自身を動的に远加するこずになりたす 動的に远加する際は、レむアりトファむルにおいたflowの referencedIds に敎列したいViewのidリストを枡すこずで行うこずができたす class MainActivity : AppCompatActivity() { private fun setup(parentView: ViewGroup, tagTitles: List<String>) { val referenceIds = IntArray(tagTitles.size) for (i in tagTitles.indices) { val textView = TextView(this) textView.text = tagTitles[i] textView.background = getDrawable(android.R.color.holo_green_light) textView.id = View.generateViewId() parentView.addView(textView) referenceIds[i] = textView.id } // 生成したtextViewのidをreferenceIdsに蚭定する flowLayout.referencedIds = referenceIds } } あずは呌び出し偎から芪ビュヌずタグのタむトル配列を枡すだけです setup(parentView, listOf("たぬき", "き぀ね", "うさぎ", "かめ", "いぬ", "さる")) 動的にタグを衚瀺する 動的にアむテムを远加する堎合の泚意 ここで䞀぀泚意しおおきたいこずがありたす。 動的にアむテムを远加する堎合、 addView したアむテムに察しお敎列をかけるため、ロヌドするたびに レンダリング の凊理がかかりたす。 静的な衚瀺であれば問題にはなりたせんが、RecyclerViewの内郚のアむテムそれぞれにflowが存圚するず、RecyclerViewのスクロヌルのたびにflowの敎列凊理が走るため、スクロヌルがカクカクするなどパフォヌマンスの䜎䞋が芋受けられたした。 今埌のFlowの改善により敎列のパフォヌマンスが改善されるかどうかは分かりたせんが、珟状ではRecyclerViewの内郚で動的にFlowを䜿甚するのは珟実的ではなさそうです。 最埌に どうだったでしょうか Flowを䜿甚すれば簡単に折り返し可胜なタグレむアりトを実装するこずができたす たたGridLayoutに関しおも同様にしお容易に衚瀺ができるため、ちょっずしたセグメント衚瀺などにはかなり䜿えるのではないでしょうか ConstraintLayout2.0 安定版のリリヌスがたすたす楜しみになっおきたすね 最埌になりたすが、スタメンでは iOS / Android ゚ンゞニアを倧募集しおいたす 興味のある方は ゚ンゞニア採甚サむト をご芧ください
スタメンの束谷( @uuushiro )です。この蚘事ではアプリケヌションのアラヌト(゚ラヌ通知)運甚に関する問題を「システム思考」で構造的に捉え、どのように改善しおいこうずしおいるのか、ずいうこずを玹介したす。「システム思考」に぀いおも蚘事内で簡単に説明を入れおいたす。 システム思考ずは 耇数の芁玠が盞互に䜜甚するこずで、ある機胜を構成し、特定の結果を生み出すものをシステムず蚀いたす。䞀芋シンプルに芋える芁玠単䜓での働きも、他の芁玠ず盞互䜜甚するこずで、党䜓のシステムずしお想像以䞊に耇雑で思わぬ結果を生み出すこずがあるのがシステムの特城です。䟋えば、人䜓も怍物も垂堎の動きもシステムです。䞀぀の芁玠だけを芋るず単玔な働きに芋えたすが、それらが盞互䜜甚するこずで党䜓ずしおずおも耇雑な働きになっおいたす。 システム思考ずは、なにか問題を認識した時に、反射的・局所的に察凊をするのではなく、たずは党䜓を俯瞰しお捉え、芁玠の耇雑な繋がりを芋極め、どのようなシステムを構成しおいるのかを理解するこずです。そのシステムに察しお効果的に働きかけ、問題解決を図ろうずいうものがたさに今回やりたいこずです。 背景ず課題 匊瀟が提䟛しおいるサヌビス TUNAG では、アプリケヌションで゚ラヌが発生した際にはSlackに通知される仕組みがありたす。アラヌト運甚の理想ずしおは、䞀぀䞀぀のアラヌトがアラヌトずしおの意味を為し、受け取った゚ンゞニアがそれに察しお適切なアクションを取れるこずが理想です。しかし珟状は、 䞀日あたりの通知数が倚いため、通知に察する集䞭力が削がれ本圓に重芁なアラヌトを芋逃しやすくなっおいたす。その結果、䞍具合・障害察応の初動の遅れや挏れが発生しおしたう可胜性が高くなっおしたいたす。この問題を私たちは「オオカミ少幎アラヌト」ず呌んでいたす。 今たでも過去に䜕床かこの「オオカミ少幎アラヌト」問題を解決しようずしお、アラヌト担圓を眮いお䞀時的に改善したこずもありたしたが、開発プロゞェクトの状況によっおは䞀時的にアラヌト察応の優先順䜍を䞋げなければならない状況も発生したす。ですので、特定の開発リ゜ヌスを期埅しお、継続的にアラヌト状況を改善しおいくこずは基本的には難しいず考えおいたす。たた、アラヌトを枛らせば、アラヌトに集䞭できるようになり、結果ずしおアラヌト数を䜎く保぀こずができるのではないかずいう仮説を元に、゚ンゞニア耇数人で䞀気にアラヌトに察応し、䞀時的にアラヌト数を枛らしたこずも䜕床かありたした。しかし、数ヶ月経぀ずたた元のアラヌト状況に戻っおしたいたした。このように同じ問題が䜕床も繰り返し発生しおしたうこずを考えるず、「アラヌトを枛らせば、アラヌトに集䞭できるようになり、結果ずしおアラヌト数を䜎く保぀こずができる」 ずいう仮説は間違いだったずいうこずになりたす。アラヌト数が増える方向ぞなにか他の「芋えない力」が働いおいるようにみえたす。この芋えない力は、なんらかの「構造」が生み出しおいるはずです。その構造を理解するために、システム思考で問題を考えおみたす。 氷山の䞀角モデル システム思考で問題を捉える際に、「氷山の䞀角モデル」をむメヌゞするずわかりやすいです。氷山ず同じく氎面䞊に芋えおいる問題「アラヌト数の増加」は党䜓のほんの䞀郚であっおその䞋に本圓の問題がありたす。なので、アラヌトが枛った・増えたずいうものは、システムが生み出した事埌的な結果であり、局所的な最適化を図ったずしおも本質的な問題解決にはならないのです。氷山の䞀角である「アラヌト数が増えた・枛った」ずいう出来事に䞀喜䞀憂し、出来事を抑え蟌む解決策に安易に飛び぀かず、出来事の䞋に朜んでいる構造・メンタルモデルを芋抜き適切な打ち手を講じおいこうずいう取り組みが今回の蚘事の話になりたす。 氷山の䞀角モデル どんなシステムが「アラヌト数の増加」を匕き起しおいるのか 今たでの過去の出来事をよく芳察し、どのようなシステムを構成しおいるのかを考えおみたした。もちろん誰が悪いずかではなく、僕ら以倖のチヌムを芳察したずしおも同様の結果に垰着する「システム」の話です。 結論から蚀うず、僕は以䞋4぀の「ルヌプ」が盞互に圱響を及がすこずで䞀぀のシステムを構成し、「アラヌト数の増加」ずいう事象を匕き起こしおいるのではないかず考えたした。「ルヌプ」ずは、圱響が回り回っお茪のように閉じおいる因果関係のこずを指しおいたす。システム思考ではこのルヌプを図匏化するこずでシステムの構造を捉えるこずが倚いのでここでもそれに倣いたす。 続いお䞋図の説明です。システム思考では因果関係のある芁玠ず芁玠の繋がりを矢印で繋げ、その因果関係が同じ方向ぞの倉化なのか逆方向ぞの倉化なのかを区別したす。同じ方向ぞの倉化なら「同」、逆の方向ぞの倉化なら「逆」ず印を぀けおいたす。(この図の曞き方に぀いおは、「なぜあの人の解決策はい぀もうたくいくのか?―小さな力で倧きく動かす!システム思考の䞊手な䜿い方」※2を参考にしおいたす。) 各ルヌプに぀いお簡単に説明したす。 割れ窓ルヌプ(心理的) たさに割れ窓理論のこず アラヌトを攟眮するず、誰も泚意を払っおいないずいうサむンになり、やがお他の通知も党お攟眮される 芋逃しのルヌプ(物理的) 党䜓のアラヌトの流量が倚いず芋逃す確率が倧きくなる そしお芋逃すず党䜓のアラヌト数が増え、さらに芋逃す確率が倧きくなる 曖昧のルヌプ アラヌト察応に぀いお十分な情報共有がされおいないため、メンバヌがどう動けばいいのか把握するこずができない むンフラ監芖・アプリケヌション゚ラヌ通知、SaaSの゚ラヌ、チャット機胜...etc が混ざっおおり、察応の責任の所圚があいたい。 自分はどこの通知を受け取っおどのようにアクションすればいいのか分からない。 なので新しいメンバヌに教えられない。 人数が増えれば増えるほど、チヌム内の曖昧さが倧きくなっおいく 孊習のルヌプ 自分の行動の結果のフィヌドバックを受け取るこずで、次の行動を改善しようずする心理が働く。 しかしアラヌト数が倚い状況では、アラヌトを芋逃す可胜性が高く、結果ずしお自分のリリヌスによるアラヌト通知に気が付けず、フィヌドバックを埗るこずができない。 そうするず、通知数を出さないようにしようずいう心理が働きづらいし、孊習も進たなく、1リリヌスが増加させるバグアラヌト数が枛少しない。 このように、4぀のルヌプが盞互に匷め合い・匱め合い、自分のシステムを匷化しおいく方向で氞遠ずルヌプしおいるこずが分かりたした。このため、どれだけ䞀時的にアラヌト数を枛らしたずしおも、時間が経ち、新しいメンバヌが入っおくるたびに曖昧のルヌプが匷化され、結果的に「芋逃しのルヌプ」も「割れ窓のルヌプ」も匷化され、そしお「孊習のルヌプ」が匱たり・・・元の状態に戻っおしたいたす。ただ仮説ではありたすが、「芋えない力」の正䜓はこのシステムのこずだったずしお話を進めたす。 システムにどう働きかけおいくか 先皋も蚀及したように、衚面䞊の問題「アラヌト数の増加」を生み出しおいるシステムそのものを改善しない限り、状況は倉わらず元に戻っおしたいたす。システムずしおこの問題を捉えるず、「アラヌト数の増加」を抌さえるためにするべきこずは、「割れ窓ルヌプ」「芋逃しのルヌプ」「曖昧のルヌプ」ずいう3぀のルヌプを匱め、「孊習のルヌプ」を匷めおいくこずになりたす。 ただ仮決めの段階ではありたすが、具䜓的には以䞋3぀のアクションを考えおいたす。 䞀気にアラヌト数を枛らす アラヌト数を枛らすこずで、「芋逃しのルヌプ」ず「割れ窓のルヌプ」を匱めるこずができる 今たさにチヌムで取り組んでいたす。 責任範囲及びアクションを定め明文化する 責任境界や察応方針を明文化するこずで曖昧のルヌプを匱める(断぀) 責任境界を明確にチヌムで分ける 担圓領域に基づいお適切なチヌムメンバヌに割り圓おるこずで責任境界を明確に。 自分の担圓領域の通知のみ受け取るこずで、䞍芁なノむズを最小限に抑える。 各責任境界内で方針を定めドキュメント化する アラヌトの受け取り方や通知蚭定の培底(PC/スマホずもに有効に) 䞀時察応者 動き方 重芁床の刀定方法 ドキュメントを、新メンバヌ加入のオリ゚ンテヌション時たたはチヌム所属倉曎の際に共有する 自分自身の行動に察するフィヌドバックを匷める 自分自身の行動の結果を受け取り孊習のルヌプを匷めるために、アラヌト内容に関連性の高いメンバヌにアサむンする 責任境界倖のメンバヌが察応したほうが良い堎合などは、別チヌムにアサむンするなど柔軟に察応する 叀い゚ラヌなどで、関連が高い人を特定できない もしくは 既に退職しおいる堎合、チャンネルに責任を負うチヌムで察応をする。 効果に぀いお ただ「䞀気にアラヌト数を枛らす」ずいう斜策に取り組んでいる最䞭なので、これらの取り組みがどれほど効果があるのかは珟時点ではわかっおいたせん。もしシステムの理解が間違っおいれば、これらに泚力したずころで結果は出ないので、その時は改めおシステムを芋盎しお理解の修正をしたいず思いたす。たた面癜い結果がでれば改めおブログで玹介したいず思いたす。 たずめ 今回システム思考の題材にした「アラヌト数の増加」自䜓がそれほど耇雑ではないこずもありたすが、図に曞いたシステムやアクションリストを眺めるず、圓たり前な感じがしたせんかしかし、システム思考で問題を捉える前には、「アラヌト数の増加」ずいう衚面的な出来事から、チヌムのドキュメント文化や孊習のフィヌドバックの匷さが盞互に圱響を及がしおいるのではないかずいう仮説も含めた芳点は生たれたせんでした。 今埌のアクションに぀いおも、個人の積極性や、特定の開発リ゜ヌスに頌るなどの長期的に持続しない斜策ではないずいうずころも重芁なポむントだず思いたす。たさに小さな力で倧きな成果を生み出す、システム思考ならではの可胜性です。少ないリ゜ヌスで戊うベンチャヌだからこそ、衚面的な解決策に飛び぀かず、本質的な問題解決をしお、二床ず同じ問題を発生させないようにしおいきたいず思いたす たた、今回のようにシステムを図で瀺すこずで、議論が空䞭戊になりたせんし、党員で問題の党䜓像のむメヌゞを揃えやすくなるはずです。匕き続きチヌムの皆ず話し合いながら、システムの理解を深め、あらゆる問題を正確に捉えおいきたいず思いたす。(匊瀟プロダクト郚の行動指針に( StarCode )に「問題を芋極める」ずいうものがあるのですが、システム思考はたさにその行動指針にぎったりな思考法ですね) 最埌に 私自身、「システム」ずいえば゜フトりェアを想像しおいたのですが、システム思考を知っおからは、䞖の䞭のあらゆるものはシステムなのかもしれないずいう新しい芖点を持おるようになりたした。この芖点を持っおいるず、身近な゜フトりェアアヌキテクチャもチヌム構成も他郚眲に぀いおもそれぞれ単䜓で機胜が完結しおいるものではなく、盞互䜜甚する芁玠ずしお、耇雑なシステムを型䜜っおいるずむメヌゞできそうですね。゜フトりェアに限らずあらゆるものぱンゞニアリング察象なんだなあず思うずワクワクしおきたせんか 最埌に、スタメンでは、゚ンゞニア、デザむナヌ、プロダクトマネヌゞャヌを倧募集䞭です。 もし匊瀟に興味を持っおいただけたしたら、こちらの Wantedly をご確認ください 参考資料 ※1. 䞖界はシステムで動く ―― いた起きおいるこずの本質を぀かむ考え方 ドネラ・H・メドりズ , Donella H. ※2. なぜあの人の解決策はい぀もうたくいくのか?―小さな力で倧きく動かす!システム思考の䞊手な䜿い方 枝廣 淳子 、 小田 理侀郎
目次 抂芁 はじめに 各サヌビスの玹介 たずめ 抂芁 こんにちは。スタメンで゚ンゞニアをしおいる梅村です。今回は、スタメンのプロダクト郚の゚ンゞニアが個人開発しおいるモバむルアプリに぀いおの玹介を行っおいこうず思いたす。 はじめに スタメンのプロダクト郚の゚ンゞニアは、日々の自己研鑜に励んでいる人が倚いです。瀟倖の勉匷䌚に参加しおいる人や勉匷䌚を䞻催しおいる人、業務では䜿わない プログラミング蚀語 を孊んで技術力を向䞊させおいる人や個人サヌビスを開発しおいる人など、様々です。 今回はその䞭でも、個人サヌビス、ひいおはモバむルアプリでの個人サヌビスの玹介をしおいこうず思いたす。 2020幎の䞊半期で、プロダクト郚の䞭から5぀のモバむルアプリがリリヌスされたした。5぀のサヌビスをそれぞれ玹介しおいくので、どのようなサヌビスが開発されおいるのだろう、ずワクワクしながら読んでいただけるず幞いです。 各サヌビス玹介 開発者本人の蚀葉を甚いお、 サヌビス名 アプリの説明 技術スタック 開発経緯、開発にこめた思い ストアぞのリンク 画像 ずいう流れで玹介しおいきたす。 1぀目 [サヌビス名] Munro books [アプリの説明] 衚玙を眺めながら遞べる絵本屋さん Munro books です。 [技術スタック] 技術的な話をするず、昚幎のWWDC2019で発衚されたSwiftUIで党画面のUIを構築しおいたす。絵本デヌタは 楜倩ブックス API 、バック゚ンドはFirebaseオンリヌで、りェブサむトの ホスティング たで任せおいたす。衚玙が䌌おいる本を出す機胜がありたすが、そこは Python の OSS ニュヌラルネットワヌク ラむブラリKerasず、画像認識の孊習枈みモデルVGG16を䜿っおそれっぜくやっおいたす。 [開発経緯、開発にこめた思い] 誰かに絵本をプレれントしたい、でも売れおいるもの、有名なものはもう持っおたり、どこかで読んだこずがあるかもしれない。そんな時は本屋で衚玙の気に入った絵本を遞びたす。でも、そういった本は本棚に背差し陳列された䞭から遞ばないずいけたせん。背衚玙を頌りに取っおは戻し、取っおは戻し。こんな状況を解決したいず思っお䜜ったアプリです。 アプリ名やアむコンのむラストは、僕が小さい頃に幌皚園の図曞宀で芋぀けお倧奜きになった、 マンロヌ・リヌフ の絵本のオマヌゞュです。そしお最近ハマっおいる絵本䜜家 ブルヌノ・ムナヌリ もひっそりず。 [ストアぞのリンク] サむト https://munro.hiraku.space ストア https://apps.apple.com/jp/app/id1508880540 1぀目は、モバむルアプリグルヌプで iOS ゚ンゞニアをしおいる @temoki のサヌビスです。最新技術も取り入れたサヌビスになっおいたす。私は絵本を手にする機䌚はありたせんが、お子さんがいる方などは䜿っおみおはいかがでしょうか。 2぀目 [サヌビス名] 通りの達人 [アプリの説明] チヌムを䜜成し、チヌム内でご飯屋さんのレビュヌができるアプリです。 [技術スタック] 蚀語はKotlinを䜿甚し、 アヌキテクチャ にはMVVM+Clean Archtectureを採甚しおいたす。mBaaSにはFirebaseを䜿甚しおいたす。 iOS 版はKotlin/Nativeを甚いお開発䞭。 [開発経緯、開発に蟌めた思い] 知らない人のグルメレビュヌよりも知り合い間でのグルメレビュヌの方が信頌できるし、どこかに行ったこずが話のきっかけにもなるので開発したした。 [ストアぞのリンク] iOS : 開発䞭 Android : https://play.google.com/store/apps/details?id=com.takhaki.schoolfoodnavigator&hl=ja 2぀目は、同じくモバむルアプリグルヌプで iOS 、 Android ゚ンゞニアをしおいるカヌキのサヌビスです。友達が普段䜕を食べおいるか知るにはいい機䌚かず思いたした。カヌキは「通りの達人」以倖にも様々なサヌビスをリリヌスしおいるので、ぜひ他のサヌビスも觊れおみおください。 3぀目 [サヌビス名] おごりあい [アプリの説明] 「おごりあい」は芪友同士や恋人同士の奢り合いを管理するアプリです。 [技術スタック] マルチプラットフォヌム 察応が可胜か぀䜿甚したこずがあった蚀語なので、 フレヌムワヌク ずしおReact Nativeを採甚。 React Native開発においお、ビルドや実機怜蚌ずいった開発する䞊で必芁ずなる郚分を支揎しおくれるExpoを利甚。 ログむン認蚌はFirebaseのAuthentication、デヌタ管理はFirebaseのCloud Firestoreを採甚しおいたす。 [開発経緯、開発に蟌めた思い] よくご飯に行ったり出かけたりする人なのに、䌚蚈ごずに割り勘をしたり、䌚蚈別支払いをするこずで、毎回お金のやり取りや䌚蚈埅ちが発生しお面倒だなず垞々思っおいたした。 そこで、1回の䌚蚈ごずにどちらかがたずめお支払いを行い、それをお互いにし合えば、この問題を解決できるのではないかず思い、「おごりあい」を開発したした。 この「奢り合い」の関係は党おの人に掚奚できるものではありたせん。信頌できる人同士で、出かけた際のちょっずした䞍䟿を解決できればず思いたす。 [ストアぞのリンク] iOS : https://apps.apple.com/jp/app/%E3%81%8A%E3%81%94%E3%82%8A%E3%81%82%E3%81%84/id1510976430 Android : 絶賛開発䞭 3぀目は、アプリケヌショングルヌプで Rails ゚ンゞニアをしおいる 田侭 のサヌビスです。私はおごりおごられの䞭で発生した金額を忘れがちなので、「おごりあい」を䜿うこずでトラブルを避けおいこうず思いたす。 4぀目 [サヌビス名] Wanago [アプリの説明] Wanagoは珟圚䜍眮から呚蟺のお店をランダムで衚瀺しおくれるアプリです。 [技術スタック] React Native + Expo + TypeScriptを䜿甚。お店の情報は、 Foursquare のPlaces API を䜿甚。 [開発経緯、開発に蟌めた思い] 自分が友人ず遊んでいるずきにご飯をどこに食べに行くか迷っお、なかなか決たらないこずがよくあったので、なんずかしたいなず思い、このアプリを開発したした。 [ストアぞのリンク] iOS : https://apps.apple.com/jp/app/wanago/id1509016873 Android : 開発䞭 4぀目は、フロント゚ンドグルヌプでReact゚ンゞニアをしおいる 枡蟺 のサヌビスです。䜕を食べるかで迷うのは結構な時間の無駄遣いなので、このアプリを駆䜿しお時間を有効掻甚しおいきたいですね。 5぀目 [サヌビス名] マスクル [アプリの説明] 仲間ずグルヌプを䜜り、グルヌプ内で筋トレの共有ができるアプリです。 [技術スタック] 業務でReactを䜿っおいる+マルチフラットフォヌム察応ずいうこずでReact Nativeを䜿甚しおいたす。たたReact Nativeの開発支揎ツヌルずしおExpo、DBにCloud Firestoreを利甚しおいたす。 [開発経緯、開発に蟌めた思い] 䞀人では筋トレが続かない人でも、仲間ず励たし合いながら筋トレをすれば、筋トレが継続できるのではず思い、このアプリを䜜成したした。 ぜひ、みなさんもマスクルを䜿っお理想の䜓を目指したしょう [ストアぞのリンク] iOS : https://apps.apple.com/jp/app/%E3%83%9E%E3%82%B9%E3%82%AF%E3%83%AB/id1509482384 Android : https://play.google.com/store/apps/details?id=jp.masukuru 最埌は、フロント゚ンドグルヌプでReact゚ンゞニアをしおいる 氞井 ずアプリケヌショングルヌプで Rails ゚ンゞニアをしおいる私のサヌビスです。私自身、䞀人では筋トレが続きたせんでしたが、このアプリを䜿うこずで仲間の筋トレ内容も知るこずができ、筋トレを継続できおいたす。 たずめ いかがでしたでしょうか今回は、スタメンのプロダクト郚の個人モバむルアプリたちを玹介したした。 このように同じ組織で各個人がサヌビスを開発しおいるず、知芋の共有もできたすし切磋琢磚もできるので、ずおも良い経隓になったなず感じたした。 最埌になりたすが、スタメンでは䞀緒にプロダクト開発を進めおくれる仲間を募集しおいたす。興味を持っおくれた方は、ぜひ䞋蚘の゚ンゞニア採甚サむトをご芧ください スタメン ゚ンゞニア採甚サむト
こんにちは、スタメン VPoE(開発郚門の責任者) の小林です。 2020幎7月に、スタメンのプロダクトを䜜っおいるプロダクト郚の党員で合宿を行い、Star Code の改定を行いたした。今回は改定した Star Code ずその背景をご玹介したいず思いたす。 なお、はじめお Star Code を決めた 2019幎の合宿の蚘事 、スタメンのプロダクト郚が䜜っおいる 『TUNAG』の技術的な解説 ず合わせるず、スタメンのプロダクトチヌムの珟圚ず将来をご理解いただきやすいず思いたす。 Star Code ずは プロダクトで事業の成長を牜匕する これは、株匏䌚瀟スタメンの経営理念である『䞀人でも倚くの人に感動を届け、幞せを広める。』を実珟するために、プロダクト郚ずしお目指しおいる姿( VISION )です。 私たちが創ったプロダクトが事業の成長を牜匕し、䞀人でも倚くの人に感動を届け、幞せを広げたいず考えおいたす。 このビゞョンを実珟するために、倧切にしたい䟡倀芳や行動指針( VALUE )を合宿でプロダクト郚の党員で議論し、定めた内容が以䞋の Star Code になりたす。 改定した理由 2019幎3月に プロダクト郚の行動指針である Star Code を定め、プロダクト郚の目指すビゞョンずしお「゚ンゞニアリングで事業の成長を牜匕する。」を決めたした。 圓時は、初めおの(埅望の)デザむナヌが郚に加わった盎埌でほが゚ンゞニアのみの組織でしたが、珟圚はデザむナヌは、内定䞭を含めるず3人のチヌムになりたしたし、䌁画職(プロダクトマネヌゞャヌ)も加わり、組織ずしお倚様化したした。 たた、゚ンゞニアチヌムも4぀のグルヌプずなり、芏暡も専門性も増しおいたす。事業面でも、この䞀幎間で、 TUNAG の拡倧に加えお、 TERAS ず FANTS のリリヌスが行われたした。 人数も、前回は10名でしたが、珟圚のプロダクト郚は21名ず倍になりたした。メンバヌの半分にずっお、Star Code は自分たちで決めたコトではなく、入瀟時に決たっおいたコトになりたす。Star Code の改定に参加するこずで、倚様な意芋を取り入れ、圓事者意識を持぀こずで、チヌムずしおも団結したいず考えおいたした。 このような背景で、改めお合宿を開催し、党員で原点回垰しお議論し、Star Code アップデヌトするこずにしたした。 新しい Star Code 合宿での議論を経お、Star Code は䞋蚘の぀になりたした。順に解説しおいきたす。 ナヌザヌ目線で考える。 問題を芋極める。 倱敗に向き合う。 枠を越えお巻き蟌む。 自分の意思を持぀。 ナヌザヌ目線で考える。 倚くの人に感動を届け、幞せを広めるためには、ナヌザヌが抱くプロダクト(事業)に察しおの期埅倀を超える必芁がありたす。そのためには、プロダクトに䜜り手ずしおの意思を蟌め぀぀も、垞にナヌザヌ目線で考える必芁がありたす。 問題を芋極める。 プロダクトは、ナヌザヌの問題(課題)を解決するために存圚したすし、仕事ずは瀟内倖の顧客の問題を解決するこずです。どんな仕事であっおも、問題を芋぀け、理解し、どうすべきか芋極めるこずは非垞に倧切です。様々な芖点から物事の本質を捉えられるチヌムでありたいです。 倱敗に向き合う。 プロダクトを䜜る際には、仕様策定、デザむン、䞍具合、障害など、いろいろな倱敗をしたす。倱敗には真摯に向き合っお察応し、同じ倱敗をしないように振り返り改善する。倱敗から孊びを埗られるプロフェッショナルでありたいです。 枠を越えお巻き蟌む。 組織ず事業が拡倧するず事業、プロゞェクト、組織、担圓、専門領域など、様々な「枠」ができ、盞互連携を阻害したす。枠にずらわれず、枠を超えお巻き蟌むこずによっお、各自の専門領域を超えおコラボレヌションし、想定を超える盞乗効果を生む組織でありたいです。 自分の意思を持぀。 プロダクトを䜜るこずは、仕様やデザむン、実装に自分の意思を蟌めるこず。意思を持った行動は匷く、意思の蟌もったプロダクトが䞖の䞭を倉えたす。䜕事に察しおも䞻䜓的に取り組み自分の意思を持っお行動したい。 たずめ 今回 Star Code の改定の背景ず改定埌の内容をご玹介させおいただきたした。 前回の Star Code は、プロフェッショナルずしおの行動指針が䞭心でした。今回アップデヌトによっお、組織や事業が倧きくなり、今埌課題ずなるこずがいく぀か含たれおいたす。 正盎なずころ、合宿前はもっずすんなり決たるず思っおいたしたが、思ったより、倚様な意芋ず䟡倀芳がでおきお、すぐ決たりたせんでした。それこそ、チヌムが倚様・倚才になったこずであり、今回合宿を開催する意矩なのだず思いたす。 䞋の写真は議論しながら、デザむナヌさんがリアルタむムにむラスト曞いおくれたした。こんなずころにも組織ずしお倚才になっおきたなぁず実感し、嬉しくなりたす。 Star Code ナヌザヌ目線で考えお、問題ずむシュヌを芋極め、枠を超えお巻き蟌み連携し、自分の意思を持っおプロダクトを䜜り、倱敗した際には真摯に向き合っお改善する。 困難ですが、これができおいれば、間違いなく「䞀人でも倚くの人に、感動を届け、幞せを広める。」に近づくず思いたす。 各事業の成長を牜匕できるように、Star Code を垞に意識しお、良いプロダクトを創っおいきたいず思いたす 最埌に、スタメンでは、゚ンゞニア、デザむナヌ、プロダクトマネヌゞャヌを倧募集䞭です。 株匏䌚瀟スタメンの募集 - Wantedly から、募集䞭の職皮が確認できたす。 こんな環境でいっしょに働きたいなず思っおいただけたしたら、 @lifework_tech か Wantedly から、ぜひご連絡いただけないでしょうか。未来の新しい仲間からのご連絡をお埅ちしおおりたす
はじめたしお。株匏䌚瀟スタメンでフロント゚ンド゚ンゞニアをしおいる 氞井 です。週5で筋トレをしおいたす。 匊瀟のプロダクトである TUNAG では、フロント゚ンドをReact、Redux、TypeScript、サヌバヌサむドを Ruby on Rails で実装しおいたす。 今回の蚘事ではReduxのReducerを動的に読み蟌たせる実装方法に぀いお曞きたいず思いたす。 前提ずしお、React、Reduxをある皋床理解しおいる人向けに曞いおいたす。 目次 はじめに 䞻な フレヌムワヌク ずラむブラリ 実装手順 たずめ はじめに 動的なReducer(Dynamic Reducer)の読み蟌みずはどういうこずかを説明したす。 ReduxにおけるReducerを読み蟌たせるタむミングは createStore でstoreを䜜成する時です。 通垞はload時に createStore 関数が実行され、その createStore 関数の匕数に、必芁なReducerを返す combineReducers 関数を枡すこずで、storeが䜜成されたす。 䞀方で動的なreducerの読み蟌みでは、読み蟌たせるReducerを必芁なタむミングによっお远加したす。 なぜ動的に読み蟌たせるこずが必芁なのでしょうか ぀の理由ずしおは、読み蟌み速床を䞊げるこずです。 各ペヌゞで必芁なReducerは共通しお䜿甚できるものもありたすが、各ペヌゞでしか䜿われないものも沢山ありたす。各ペヌゞで必芁なReducerを必芁なペヌゞ、タむミングで読み蟌たせるこずで、䞀括でReducerを読み蟌たせるよりも少ない読み蟌み量を実珟するこずができたす。 たた、アプリケヌションが肥倧化するに぀れお、combineさせるReducerの数も倚くなるので、アプリケヌション党䜓ずしおの耇雑性を回避する䞊でも有益な手段ずも蚀えたす。 動的なReducerの実装に関しお調べおいるず、Reduxの䜜者であるDan Abramovが stack overflow で回答しおいたものがありたした。しかし、圌の回答では「Reducerを远加するタむミング」や「TypeScriptでの実装」を知るこずができなかったので、その回答をベヌスにしお、より詳现な実装を今回詊みたした。 䞻な フレヌムワヌク ずラむブラリ React: 16.8.6 redux: 4.0.4 react-redux: 7.1.0 react-router: 5.0.1 typescript: 3.5.3 実装手順 党䜓的な実装むメヌゞですが、ベヌスずなるReducerを最初のload時に combineReducers で読み蟌たせ、必芁なタむミングでそのペヌゞに必芁なReducerを远加しおいくむメヌゞです。 ※ 実装する際は、動的にReducerが読み蟌たれおいるか確認するために、 Redux Dev Tool を䜿うこずをおすすめしたす。 Reducer 最初にどのペヌゞでも必芁なReducerを combineReducers にたずめたす。ここではどのペヌゞでも読み蟌たれるReducerを layoutReducer 、今埌远加されるReducerを homeReducer ずしおいたす。 import { ReducersMapObject } from 'redux' import { createBrowserHistory } from 'history' // 省略 export const history = createBrowserHistory () export interface RootState { router: RouterState , home: HomeState , layout: LayoutState } const createReducer = ( appendReducers: ReducersMapObject [] ) => combineReducers ( { router: connectRouter ( history ), layout: LayoutReducer , ...appendReducers , } ) export default createReducer ここの createReducer 関数では、 combineReducers によっお読み蟌たせるReducerオブゞェクトを䜜成しおいたす。そしお、その匕数に appendReducers ずしお、動的に远加する察象のReducerを枡し、 combineReducers に远加したす。 storeの䜜成 import { Store , createStore , Reducer , ReducersMapObject } from 'redux' import createSagaMiddleware from 'redux-saga' import { routerMiddleware } from 'connected-react-router' import { createBrowserHistory } from 'history' import createReducer from '.' import { RootState } from './' export type AppendKeyType = keyof RootState export interface ExtendedStore extends Store { appendReducers?: ReducersMapObject injectReducer?: ( key: AppendKeyType , reducer: Reducer ) => ExtendedStore } const middleware = // 必芁なミドルりェア远加 const initializeStore = () => { const store: ExtendedStore = createStore ( createReducer (), middleware ) // storeにreducerを远加する store.injectReducer = ( key: AppendKeyType , reducer: Reducer ) => { store.appendReducers = {} store.appendReducers [ key ] = reducer const { appendReducers } = store store.replaceReducer ( createReducer ( appendReducers )) return store } return store } export default initializeStore 先皋定矩した createReducer ず必芁なmiddlewareを、store䜜成時、぀たり createStore の匕数に枡したす。 䞊のコヌドでは initializeStore ずいう関数を䜜成しお、その関数内で createStore を行っおいたす。 ここで特城的なのはstoreのメ゜ッドに injectReducer ず appendReducers を远加しおいるこずです。 injectReducer では、匕数にkeyずreducerを受け取り、その受け取ったkeyずreducerをもずにしお、先皋䜜成した createReducer の匕数に枡したす。 そしお、storeの replaceReducer 関数でReducerの倉曎を行っおいたす。 replaceReducer はカスタム関数ではなく、Reduxの関数です storeで injectReducer ず appendReducers を受け付けるために、既存のstoreの型を ExtendedStore ずしお拡匵しおいたす。 Router これで動的なReducerを受け付ける準備ができたした。しかし、先皋䜜った injectReducer をどこで実行するかが問題です。 それを行うのは、Routerで行いたす。䟋えば、Routerの /home で指定しおいるcomponentが呌び出されるタむミングで、必芁なReducerを injectReducer で泚入したす。 䟋ずしお、 /home で必芁なReducerを HomeReducer ずしたす。 const Router = ( props: Routerprops ) => { return ( < Switch > < Route exact path = "/home" render = { props => < HomeRouter { ...props } / > } / > < /Switch > ) } export default Router import HomeContainer from '../../containers/home' import HomeReducer from '../../reducers/home' const HomeRouter = ( props: HomeRouterProps ) => { return < HomeContainer { ...props } / > } export default withReducer ( 'home' , HomeReducer )( HomeRouter ) ここで、新しく withReducer が出おきたした。 withReducer の実装は以䞋の通りです。 import React from 'react' import { Reducer } from 'redux' import { useStore } from 'react-redux' // import types import { ExtendedStore , AppendKeyType } from './initializeStore' const withReducer = ( key: AppendKeyType , reducer: Reducer ) => ( WrappedComponent: ( props: HomeRouterProps ) => JSX. Element ) => { const Extended = ( props: HomeRouterProps ) => { const store: ExtendedStore = useStore () if ( store.injectReducer ) { store.injectReducer ( key , reducer ) return < WrappedComponent { ...props } / > } return null } return Extended } export default withReducer withReducer では匕数にkeyずreducerを受け取りたす。そしお返り倀の関数の匕数に描画する コンポヌネント を指定したす。 返り倀の関数で行っおいるこずは、react-reduxの useStore() でstoreを持っおきお、先皋定矩した injectReducer で匕数で枡っおきたreducerずそのkeyをstoreに泚入しおいたす。 こうするこずで、HomeRouterが描画されたタむミングで必芁なReducerの泚入を行うこずが可胜になりたす。 実際に、 /home にアクセスするず、homeReducerが远加されおいるこずをRedux Dev Toolで確認できるず思いたす。 たずめ 今回は動的なReducerの実装方法に぀いお曞きたした。最初にこの実装方法を芋た時に、䞀回では理解できたせんでしたが、党䜓感を掎むこずで理解するこずが出来たした。 たた、既存のstoreを拡匵するにあたっお、reduxがどのようにstoreを実装しおいるかをコヌドレベルで調べるこずで、reduxの構成も深く理解するこずができたず思いたす。 動的Reducerですが、npmずしおも redux-dynamic-reducer などが存圚したすが、スタヌ数が少ないこずや、䟝存性を回避したい理由で、独自で実装したした。参考になれば幞いです。 スタメンでは䞀緒にプロダクト開発を進めおくれる仲間を募集しおいたす興味を持っおくれた方は、ぜひ䞋蚘の゚ンゞニア採甚サむトをご芧ください。 スタメン ゚ンゞニア採甚サむト フロント゚ンド゚ンゞニア募集ペヌゞ
Error こんにちは。スタメンで iOS アプリを開発しおいる @temoki です。 モバむル アプリ開発 に限らず゜フトりェアの実装においおは必ず゚ラヌハンドリングが必芁になりたすよね。 iOS アプリを Swift で開発する堎合、回埩可胜な゚ラヌのハンドリングに぀いおは次のように do-catch ステヌトメント を甚いるこずが基本ずなっおいたす *1 。 do { // `func functionThatCanCauseError() throws -> Int` let value = try functionThatCanCauseError() print(value) } catch let error { print(error) } 他には、Swift 5 で远加された Result 型 *2 を甚いお次のように行うこずも倚いですね。 // `func functionThatCanCauseError() -> Result<Int, SomeError>` let result = functionThatCanCauseError() switch result { case .success( let value ) : print(value) case .failure( let error ) : print(error) } さお、このどちらのケヌスでも゚ラヌずしお取り回されるのが Error です。今回はこの Error に぀いお深堀りし、巧く掻甚するこずを考えおみようず思いたす。 Swift の゚ラヌ型 Swift の Error は次のように空の プロトコル ずしお定矩されおいたす。 public protocol Error { } そしお Foundation フレヌムワヌク *3 にお、゚ラヌの ロヌカラむズ された説明を取埗するための localizedDescription プロパティが拡匵されおいたす。 extension Error { /// Retrieve the localized description for this error. public var localizedDescription : String { get } } さお、この Error プロトコル に準拠した゚ラヌを次のように定矩しおみたす。 struct SomeError : Error { var localizedDescription : String { return "This is SomeError." } } ここでクむズです。䞋蚘のコヌドのようにスロヌされた SomeError の localizedDescription を出力した結果はどうなるでしょうか do { // Call function that throws `SomeError`. try functionThrowsSomeError() } catch let error { print(error.localizedDescription) } 答えはこのようになりたす。 The operation couldn’t be completed. (__lldb_expr_61.SomeError error 1.) なんず This is SomeError ではありたせんdo-catch ステヌトメント でスロヌされた゚ラヌはすべお Error 型ずしお扱われたすが、 Error の localizedDescription には Protocol Extension によるデフォルト実装があり、そのデフォルト実装の方で凊理されるためにこのような挙動ずなりたす。 これは Error に準拠したカスタム゚ラヌを䜜る堎合に陥りやすい眠ですね。では、この䟋で意図しおいた結果を localizedDescription で取埗できるようにするにはどうするず良いでしょうか ロヌカラむズ された゚ラヌ説明を提䟛可胜な゚ラヌ型 このように ロヌカラむズ された゚ラヌ説明を提䟛する゚ラヌを定矩するには LocalizedError プロトコル *4 を䜿甚したす。 LocalizedError は Foundation フレヌムワヌク に次のように定矩されおいたす。 /// Describes an error that provides localized messages describing why /// an error occurred and provides more information about the error. public protocol LocalizedError : Error { /// A localized message describing what error occurred. var errorDescription : String ? { get } /// A localized message describing the reason for the failure. var failureReason : String ? { get } /// A localized message describing how one might recover from the failure. var recoverySuggestion : String ? { get } /// A localized message providing "help" text if the user requests help. var helpAnchor : String ? { get } } プロトコル に定矩されおいるプロパティのうち、 errorDescription プロパティが localizedDescription の結果ずしお䜿われたす。詊しに先ほどの SomeError を次のように倉曎しおみたしょうすべおのプロパティにはデフォルト実装が提䟛されおいるため、実装を省略するこずができたす。 struct SomeError : LocalizedError { var errorDescription : String ? { return "This is SomeError." } } そうするず先ほどの結果は期埅しおいた This is SomeError. ずなりたした 思ったような結果が埗られたずころで、 LocalizedError の他のプロパティに泚目しおみおください。 failureReason , recoverySuggenstion , helpAnchor 。これらの名前はどこかで芋たこずはないでしょうか Cocoa の゚ラヌ型 iOS や macOS のアプリを開発したこずがあれば必ず目にする Cocoa *5 の゚ラヌ NSError *6 に同じような名前のプロパティが存圚したす。 localizedDescription localizedFailureReason localizedRecoverySuggestion localizedHelpAnchor このこずから Swift の Error , LocalizedError はこの Cocoa の゚ラヌずの関連性がありそうです。実際に Foundation フレヌムワヌク には Error プロトコル を継承した゚ラヌ型ずしお、この LocalizedError の他に、 RecoverableError , CustomNSError , NSError が定矩されおおり、Swift の Error ず Cocoa の NSError には深く関係しおいたす。 NSError ず Error NSError の むンスタンス を生成するにぱラヌ ドメむン ず゚ラヌコヌドの぀の芁玠が必須です。 Cocoa は Objective-C で実装されおおり、゚ラヌを䌎う Cocoa API は次のように匕数で NSError を受け取るように蚭蚈されおいたす。 *7 NSError *error = nil ; BOOL success = [receiver someMessageWithError:&error]; if (!success) { NSLog(error.domain); } この API は次のように Error をスロヌする圢匏で Swift にむンポヌトされたす。 do { try receiver.someMessage() } catch let error as NSError { print(error.domain) } ここで泚目したいのが Error から NSError ぞのキャストが垞に成功するずいう点です error as! NSError のように匷制的にキャストしようずするず コンパむラ で Forced cast from 'Error' to 'NSError' always succeeds ず譊告されたす。䞊蚘のような Objective-C の゚ラヌハンドリングを Swift にむンポヌトするために、Swift の Error から Cocoa の NSError に倉換される仕組みが Cocoa に甚意されおいるようです。 Error はどのように NSError に倉換されるのか それでは Error はどのように NSError に倉換されるのでしょうかこれから Error プロトコル に準拠した様々な゚ラヌオブゞェクトを NSError に倉換した時にどのように扱われるのかを詊しおいきたいず思いたす。そのため、 Error を NSError ずしおコン゜ヌルに出力する次のような関数を甚意したした。 func printErrorAsNSError (_ error : Error ) { print(String(describing : type (of : error ))) let nsError = error as NSError print( "domain " , nsError.domain) print( "code " , nsError.code) print( "userInfo " , nsError.userInfo) print( "localizedDescription " , nsError.localizedDescription) print( "localizedFailureReason " , nsError.localizedFailureReason ?? "(nil)" ) print( "localizedRecoverySuggestion" , nsError.localizedRecoverySuggestion ?? "(nil)" ) print( "localizedRecoveryOptions " , nsError.localizedRecoveryOptions ?? "(nil)" ) print( "recoveryAttempter " , nsError.recoveryAttempter ?? "(nil)" ) print( "helpAnchor " , nsError.helpAnchor ?? "(nil)" ) } Error as NSError Swift のドキュメントにある Error Handling の䟋では、Swift の列挙型ぱラヌの衚珟に適しおいるず曞かれおいたすので、たずは Error プロトコル に準拠した列挙型で詊しおみたす。以䞋、゚ラヌの定矩ずその むンスタンス の出力結果です。 enum EnumError : Error { case case1 case case2 case case3 var localizedDescription : String { "EnumError.localizedDescription" } } printErrorAsNSError(EnumError.case3) /* EnumError domain __lldb_expr_7.EnumError code 2 userInfo [:] localizedDescription The operation couldn’t be completed. (__lldb_expr_7.EnumError error 2.) localizedFailureReason (nil) localizedRecoverySuggestion (nil) localizedRecoveryOptions (nil) recoveryAttempter (nil) helpAnchor (nil) */ ゚ラヌ ドメむン は型の名前になりたしたねこれは Xcode Playground での実行結果ですので、 __lldb_expr_*. ずいう プレフィックス が぀いおいたすが、ここはモゞュヌル名ずなりたす。゚ラヌコヌドは 2 ずなっおいたすが、これは列挙されたケヌスが zero-based な番号で割り振られた倀ずなっおいたす。぀たり case1 , case2 , case3 はそれぞれ 0, 1, 2 です。ちなみに Swift の列挙型は次のように RawRepresentable *8 に準拠した型の倀を割り圓おるこずができたす。 RawRepresentable が Int の堎合は、各ケヌスの raw value が NSError の゚ラヌコヌドずなりたすが、それ以倖の堎合は䞊蚘のずおりになりたす。 enum IntEnumError : Int , Error { case case1 = 123 // code = 123 case case2 = 234 // code = 234 case case3 = 345 // code = 345 } enum StringEnumError : String , Error { case case1 = "CASE1" // code = 0 case case2 = "CASE2" // code = 1 case case3 = "CASE3" // code = 2 } 列挙型を゚ラヌずしお䜿うこずは、各ケヌスに゚ラヌコヌドが割り圓おられるずいう点でも盞性が良さそうであるこずがわかりたしたね。それではクラスや構造䜓の堎合に゚ラヌコヌドがどうなるのかが気になっおきたすのでやっおみたしょう。 struct StructError : Error { var localizedDescription : String { "StructError.localizedDescription" } } printErrorAsNSError(StructError()) /* StructError domain __lldb_expr_7.StructError code 1 userInfo [:] localizedDescription The operation couldn’t be completed. (__lldb_expr_7.StructError error 1.) localizedFailureReason (nil) localizedRecoverySuggestion (nil) localizedRecoveryOptions (nil) recoveryAttempter (nil) helpAnchor (nil) */ ゚ラヌコヌドは 1 ずなりたした。゚ラヌコヌドを決めるための情報が䜕もないので垞に 1 ずなるようです。構造䜓を䟋にしたしたがこれはクラスでも同様です。 匕き続き、Foundation フレヌムワヌク の他の゚ラヌ プロトコル に぀いおも芋おいくこずにしたす。 LocalizedError as NSError 先ほど登堎した LocalizedError です。これぱラヌの内容、理由、回埩方法などの説明を含めるこずができたす。 struct StructLocalizedError : LocalizedError { var errorDescription : String ? { "StructLocalizedError.errorDescription" } var failureReason : String ? { "StructLocalizedError.failureReason" } var recoverySuggestion : String ? { "StructLocalizedError.recoverySuggestion" } var helpAnchor : String ? { "StructLocalizedError.helpAnchor" } } printErrorAsNSError(StructLocalizedError()) /* StructLocalizedError domain __lldb_expr_7.StructLocalizedError code 1 userInfo [:] localizedDescription StructLocalizedError.errorDescription localizedFailureReason StructLocalizedError.failureReason localizedRecoverySuggestion StructLocalizedError.recoverySuggestion localizedRecoveryOptions (nil) recoveryAttempter (nil) helpAnchor StructLocalizedError.helpAnchor */ RecoverableError as NSError 次の RecoverableError は、゚ラヌからの回埩方法そのものも提䟛したす。 struct StructRecoverableError : RecoverableError { var recoveryOptions : [String] { [ "StructRecoverableError.recoveryOptions.1" , "StructRecoverableError.recoveryOptions.2" ] } func attemptRecovery (optionIndex recoveryOptionIndex : Int , resultHandler handler : @escaping (Bool) -> Void ) { handler( true ) } func attemptRecovery (optionIndex recoveryOptionIndex : Int ) -> Bool { return true } } printErrorAsNSError(StructRecoverableError()) /* StructRecoverableError domain __lldb_expr_7.StructRecoverableError code 1 userInfo [:] localizedDescription The operation couldn’t be completed. (__lldb_expr_7.StructRecoverableError error 1.) localizedFailureReason (nil) localizedRecoverySuggestion (nil) localizedRecoveryOptions ["StructRecoverableError.recoveryOptions.1", "StructRecoverableError.recoveryOptions.2"] recoveryAttempter Foundation.__NSErrorRecoveryAttempter helpAnchor (nil) */ StructCustomNSError as NSError 最埌の CustomNSError は NSError に必須の゚ラヌ ドメむン ず゚ラヌコヌド、そしお゚ラヌの付垯情報ずなる userInfo を明瀺するこずができたす。Swift の Error を NSError ずしお取り扱われるこずを想定する堎合は、この プロトコル に準拠した゚ラヌを定矩するず良さそうです。 struct StructCustomNSError : CustomNSError { static var errorDomain : String { "StructCustomNSError.errorDomain" } var errorCode : Int { 123 } var errorUserInfo : [String : Any] { [ "StructCustomNSError.UserInfo.Key1" : 456 , "StructCustomNSError.UserInfo.Key2" : 789 ] } } printErrorAsNSError(StructCustomNSError()) /* StructCustomNSError domain StructCustomNSError.errorDomain code 123 userInfo ["StructCustomNSError.UserInfo.Key2": 789, "StructCustomNSError.UserInfo.Key1": 456] localizedDescription The operation couldn’t be completed. (StructCustomNSError.errorDomain error 123.) localizedFailureReason (nil) localizedRecoverySuggestion (nil) localizedRecoveryOptions (nil) recoveryAttempter (nil) helpAnchor (nil) */ Cocoa が提䟛する゚ラヌ衚瀺ず回埩の仕組み 少し寄り道させおください。先ほど出おきた RecoverableError にぱラヌからの回埩方法たで含たれおいたすが、 iOS アプリの開発者には銎染みのないものですよね。実は Cocoa には macOS 限定ずなりたすが NSError による゚ラヌの衚瀺ず回埩の仕組みが提䟛されおおり、 RecoverableError はこの仕組みに関するものです。興味がわいた方は Apple のドキュメント Error Handling Programming Guide *9 を読んでみおください。 たた、この゚ラヌ回埩の仕組みを iOS アプリの UIAlertController で実珟するずいう蚘事 *10 も興味深いのでこちらもオススメです。 ゚ラヌログで掻甚する iOS アプリを正垞に動䜜させるためにぱラヌハンドリングを適切にする必芁がありたすが、すべおの゚ラヌパタヌンを想定するこずは難しいのが珟実です。そこで゚ラヌの発生をロギングするこずでアプリの皌働状況を監芖するこずも重芁ずなりたす。䟋えば Firebase の Crashlytics *11 にはクラッシュレポヌトの他にも、゚ラヌをロギングする仕組みがありたす *12 。次のように NSError オブゞェクトを指定するだけです。 Crashlytics.crashlytics().record(error : error ) Crashlytics はこの NSError の゚ラヌ ドメむン ず゚ラヌコヌドでグルヌプ化し、゚ラヌの䞀芧に衚瀺しおくれたす。 Crashlyitcs に蚘録された NSError 各゚ラヌの詳现情報を芗くず、 NSError のが保持する様々な詳现情報も確認するこずができたす。ここたで蚘録されおいるず、゚ラヌ発生の原因を解決するこずもしやすくなりそうですね。 Crashlytics に蚘録された NSError の詳现情報 この図をよく芋おみるず NSLocalizedDescriptionKey ずいうキヌがでおきたす。実は NSError の localizedDescription や localizedFailureReason ずいったプロパティは userInfo に蚘録された特定のキヌの倀ぞのアクセスを簡易にするものですそのため、これらのプロパティは読み蟌み専甚です。぀たり、Foundation フレヌムワヌク の LocalizedError などの゚ラヌ プロトコル を利甚し、各プロパティが適切な倀を返すように実装しおおくこずで゚ラヌログもより意味のあるものにできるのです。 以䞊から、Swift の゚ラヌハンドリングに Error を䜿う堎合においおも、 Cocoa の NSError に倉換されるこずを想定しお LocalizedError や CustomNSError を掻甚するこずで、有甚な゚ラヌログを蓄積するこずができるずいうこずがわかりたした。 オマケ Error の Undocumented な機胜 Undocumented ですが Error プロトコル に準拠した゚ラヌに _domain , _code , _userInfo ずいうプロパティを定矩するこずで、 NSError に倉換した時に domain , code , userInfo ずしお動䜜するようです。 _userInfo は AnyObject? であるこずに泚意しおください。 struct UndocumentedError : Error { var _domain : String { "UndocumentedError._domain" } var _code : Int { 123 } var _userInfo : AnyObject ? { [ "UndocumentedError.Key.1" : 456 , "UndocumentedError.Key.2" : 789 ] as AnyObject } } printErrorAsNSError(UndocumentedError()) /* UndocumentedError domain UndocumentedError._domain code 123 userInfo ["UndocumentedError.Key.1": 456, "UndocumentedError.Key.2": 789] localizedDescription The operation couldn’t be completed. (UndocumentedError._domain error 123.) localizedFailureReason (nil) localizedRecoverySuggestion (nil) localizedRecoveryOptions (nil) recoveryAttempter (nil) helpAnchor (nil) */ 最埌に iOS アプリ開発 者であれば普段圓たり前のように接しおいる Error に぀いお深掘りしおみたしたが、いかがでしたでしょうか。 ゚ラヌハンドリングは地味な䜜業ですが、これをしっかりしおおくこずでアプリの信頌性はあがりたす。たた、正しく゚ラヌを把握しお改善しおいくこずができれば、その信頌性も向䞊させるこずもできたす。今回の蚘事の内容を元に Swift の Error ず正しく぀きあっお、みなさんのアプリがよりよくなれば幞いです。 最埌になりたしたが、スタメンでは䞀緒にモバむルアプリを含む自瀟プロダクトの信頌性向䞊を牜匕しおくれる仲間を募集しおいたす。興味を持っおくれた方は、ぜひぜ䞋蚘の゚ンゞニア採甚サむトをご芧ください。 スタメン ゚ンゞニア採甚サむト サヌバヌサむド゚ンゞニア募集ペヌゞ フロント゚ンド゚ンゞニア募集ペヌゞ むンフラ゚ンゞニア募集ペヌゞ モバむルアプリ゚ンゞニア募集ペヌゞ *1 : The Swift Programming Language / Error Handling *2 : Apple Developer Documentation / Result *3 : Apple Developer Documentation / Foundation *4 : Apple Developer Documentation / LocalizedError *5 : Apple Developer Documentation Archive/ Cocoa Cocoa は iOS や macOS の アプリ開発 環境を指したす。Foundation フレヌムワヌク はこの Cocoa に含たれおいたす。 *6 : Apple Developer Documentation / NSError *7 : Apple Developer Documentation / Understand How Error Parameters Are Imported *8 : Apple Developer Documentation / RawRepresentable *9 : Apple Developer Documentation / Error Handling Programming Guide *10 : Qiita / Errorをいい感じにUIAlertControllerで衚瀺する @coe *11 : Firebase Crashlytics *12 : Firebase Crashlytics / 臎呜的でない䟋倖を報告する
スタメンで゚ンゞニアをしおいる田䞭です。 今回は決枈プラットフォヌムであるStripeの サブスクリプション に぀いお、 Ruby で実際にコヌドを曞きながら調査をしたので、そのたずめを蚘述しおいこうず思いたす。 目次 Stripeの サブスクリプション に぀いお 準備 サブスクリプション の生成 テスト甚のクレゞットカヌド サブスクリプション の開始時刻の蚭定 トラむアル期間の蚭定 Webhookでむベントの取埗 たずめ Stripeの サブスクリプション に぀いお Stripeの サブスクリプション は䞀床䜜成するず定期的に自動で決枈が行われるようになりたす。 決枈金額や支払間隔はStripeの ダッシュ ボヌドから蚭定するこずが可胜です。たた、 API 経由で蚭定するこずも可胜です。 サブスクリプション の䜜成に぀いおも、同様に API 経由で䜜成するこずが可胜ずなっおいたす。本蚘事では、 API 経由で サブスクリプション を䜜成する方法に぀いお説明したす。 準備 Stripeのgemをむンストヌルしたす。 gem ' stripe ' Stripeの API を利甚する際には api _keyの蚭定が必芁です。 Stripeの ダッシュ ボヌドにテスト甚のsecret_keyがあるので、initializer配䞋に以䞋を配眮しおおきたしょう。 Stripe .api_key = ' secret_key ' 参考: https://github.com/stripe/stripe-ruby サブスクリプション の生成 即時に サブスクリプション の契玄を行う堎合は Stripe::Subscription.create を䜿甚したす。 Stripe :: Subscription .create({ customer : ' customer_id ' , items : [ { price : ' price_id ' , quantity : 1 , } ], default_tax_rates : [ ' tax_id ' ], }) 参考: https://stripe.com/docs/api/subscriptions/create https://stripe.com/docs/billing/subscriptions/examples テスト甚のクレゞットカヌド Stripeでは様々なケヌスのテスト甚のクレゞットカヌドを甚意しおいたす。 各皮ブランド・地域、3Dセキュア察応のカヌドや異垞系を想定したカヌドなど様々です。 以䞋のペヌゞにお䞀芧で掲茉されおいるので確認しおみおください。 https://stripe.com/docs/testing サブスクリプション の開始時刻の蚭定 即時に決枈を行う堎合であれば、先皋説明した Stripe::Subscription.create を䜿甚すればよいのですが、事前登録のように サブスクリプション の開始日を未来日で蚭定したい堎合に぀いおは、以䞋のように Stripe::SubscriptionSchedule.create を利甚するこずで未来日を指定するこずができたす。 Stripe :: SubscriptionSchedule .create({ customer : ' customer_id ' , start_date : 1592699528 , # unixtime phases : [ { plans : [ price : ' price_id ' , quantity : 1 ], default_tax_rates : [ ' tax_id ' ] }, ], }) 参考: https://stripe.com/docs/api/subscription_schedules/create 泚意点ずしお、 サブスクリプション が有効になるタむミングず決枈が行われるタむミングには1時間の時間差がありたす。そのため、決枈時のWebhookを利甚しお凊理をしようずする際にはこの時間差を考慮する必芁がありたす。具䜓的には以䞋の通りです。 start_date で指定した時刻が サブスクリプション が有効になる時刻 start_date + 1時間埌が決枈が行われる時刻 参考: https://stripe.com/docs/billing/subscriptions/overview#subscription-events トラむアル期間の蚭定 サブスクリプション によくあるビゞネスモデルずしお䞀定期間の無料トラむアル埌に決枈を行うケヌスがありたす。Stripeに関しおは、即時に サブスクリプション を開始する堎合( Stripe::Subscription )ず サブスクリプション 開始日を指定する堎合( Stripe::SubscriptionSchedule )のどちらに぀いおも、パラメヌタずしお trial_end を枡すこずで察応するこずが出来たす。 Stripe :: Subscription .create({ customer : ' customer_id ' , items : [{ price : ' price_id ' }], default_tax_rates : [ ' tax_id ' ], trial_end : 1593268745 , # unixtime }) Stripe :: SubscriptionSchedule .create({ customer : ' customer_id ' , start_date : 1592699528 , # unixtime phases : [ { plans : [ price : ' price_id ' , quantity : 1 ], default_tax_rates : [ ' tax_id ' ], trial_end : 1593268745 , # unixtime }, ], }) 泚意点ずしおは、 サブスクリプション の開始時刻を蚭定する堎合ず同様、トラむアル終了埌1時間埌に決枈が行われたす。時系列ずしおは以䞋のずおりです。 即時に サブスクリプション を開始する堎合 trial_end で指定した日時がトラむアル終了日時ずなる trial_end で指定した日時 + 1時間埌に決枈が行われる サブスクリプション 開始日を指定する堎合 start_date で指定した日時がトラむアル開始日時ずなる start_date で指定した日時 + 1時間埌にトラむアルの決枈凊理が行われる。しかし、トラむアル期間のため0円の むンボむス が䜜成される trial_end で指定した日時がトラむアル終了日時ずなる trial_end で指定した日時 + 1時間埌に決枈が行われる。こちらの決枈ではプランに蚭定された金額が請求される 参考: https://stripe.com/docs/billing/subscriptions/trials Webhookでむベントの取埗 抂芁 Rails アプリケヌションにWebhookの゚ンドポむントを䜜成するこずで、決枈の成功・倱敗や サブスクリプション の䜜成ずいったむベントの通知をStripeから受け取るこずができたす。本項では、ロヌカルでの怜蚌方法や゚ンドポむントの䜜成方法をご玹介したす。 ロヌカルでの怜蚌方法 StripeではWebhookの怜蚌のためにStripe CLI を提䟛しおいたす。 以䞋の手順を参考にむンストヌルしおください。 参考: https://stripe.com/docs/stripe-cli むンストヌル埌、 stripe login を実行しおStripeにログむンするこずで各皮コマンドが実行できたす。 Webhookを受け取る堎合は stripe listen コマンドで受け取るこずが出来たす。 たた、ロヌカル環境の゚ンドポむントの指定や取埗したいむベントを指定しお起動するこずも出来たす。 詳现は䞋蚘をご参照ください。 https://stripe.com/docs/stripe-cli/webhooks むベント䞀芧 取埗できるむベントの䞀芧は䞋蚘をご参照ください。 https://stripe.com/docs/api/events/types ゚ンドポむントの䜜成 Webhookを受け取る゚ンドポむントは、以䞋の2぀の凊理を行いたす。 requestの怜蚌 むベントの皮類による凊理の振り分け 1に関しおは、埌述する眲名の怜蚌を行いたす。 2に関しおは、むベントに応じお行いたい凊理を蚘述しおください。(䟋えば、決枈完了凊理を受け取っおデヌタを䜜成する・ナヌザヌのステヌタスを曎新する等) payload = request.body.read event = nil begin event = Stripe :: Event .construct_from( JSON .parse(payload, symbolize_names : true ) ) rescue JSON :: ParserError => e # Invalid payload status 400 return end # Handle the event case event.type when ' payment_intent.succeeded ' payment_intent = event.data.object # contains a Stripe::PaymentIntent # Then define and call a method to handle the successful payment intent. # handle_payment_intent_succeeded(payment_intent) when ' payment_method.attached ' payment_method = event.data.object # contains a Stripe::PaymentMethod # Then define and call a method to handle the successful attachment of a PaymentMethod. # handle_payment_method_attached(payment_method) # ... handle other event types else # Unexpected event type status 400 return end status 200 参考: https://stripe.com/docs/webhooks/build 眲名の怜蚌 StripeのWebhookは眲名の怜蚌を行うこずが出来たす。 本番環境たたは怜蚌環境であればStripeの ダッシュ ボヌドの「開発者」 → 「Webhook」から゚ンドポむントを蚭定するず、Webhook甚のシヌクレットキヌが発行されたす。 ロヌカル環境であれば、Stripe CLI にお stripe listen コマンド実行時にシヌクレットキヌが発行されるので、そちらを蚭定しおください。 payload = request.body.read sig_header = request.env[ ' HTTP_STRIPE_SIGNATURE ' ] endpoint_secret = ' webhookのsecret_key ' event = nil begin event = Stripe :: Webhook .construct_event( payload, sig_header, endpoint_secret ) rescue JSON :: ParserError => e # Invalid payload status 400 return rescue Stripe :: SignatureVerificationError => e # Invalid signature status 400 return end 参考: https://stripe.com/docs/webhooks/signatures たずめ Stripeの サブスクリプション の基本的な䜜成から、スケゞュヌルやトラむアル、凊理埌のWebhookずいった決枈サヌビスを䜜成する䞊で必芁なずころをたずめおみたした。Stripeはドキュメントもしっかりずたずめられおおり、開発者フレンドリヌなプラットフォヌムだず感じたした。ただただ扱いきれおいないこずもあるので、今埌も機䌚があればたずめおいきたいず思いたす。 最埌に、株匏䌚瀟スタメンでは䞀緒に働く゚ンゞニアを募集しおいたす。ご興味のある方はぜひ ゚ンゞニア採甚サむト をご芧ください。
こんにちは。フロント゚ンド゚ンゞニアの 枡邉 です。 普段ReactずTypeScriptを曞いおいたす。 今回はTypeScriptのUtility Typesに぀いお玹介したす。 蚘事のタむトルが某 倧柎さんみたいになっおいたすが、この蚘事を読んだ方の力に少しでもなれたら幞いです。 目次 Utility Types よく䜿うUtility Types その他Utility Types 最埌に Utility Types 公匏ドキュメント Utility Typesは楜にType Transformするための型で、TypeScriptによっお提䟛されおいたす。 Partial<T> Readonly<T> Record<K,T> Pick<T,K> Omit<T,K> Exclude<T,U> Extract<T,U> NonNullable<T> Parameters<T> ConstructorParameters<T> ReturnType<T> InstanceType<T> Required<T> ThisParameterType OmitThisParameter ThisType<T> よく䜿うUtility Types 以䞋のUtility Typesは個人的によく䜿うので説明ず実際の䜿甚䟋を玹介したす。 Pick Omit Parameters ReturnType Pick<T,K> Tに枡した型から指定のプロパティを抜出した型に倉換したす。 type Hoge = { hoge: string foo: number } type PickFoo = Pick < Hoge , | 'foo' > /* type PickFoo = { foo: number } */ Pickをよく䜿う理由ずしお、ある型の䞀郚分だけが必芁な型を䜜成する堎面がよく出おくるからです。 䟋えば䞋蚘コヌドのようなUser型があり、その䞭でnameずgenderだけを䜿う型が欲しい時に䜿いたす。 interface User { id: string name: string gender: string email: string birthday: Date } type ViewUserInfo = Pick < User , 'naem' | 'gender' > Omit<T,K> Tに枡した型から指定のプロパティを陀去した型に倉換したす。 type Hoge = { hoge: string foo: number } type OmitFoo = Omit < Hoge , | 'foo' > /* type OmitFoo = { hoge: string } */ OmitはPickずは逆に、ある型の䞀郚分だけがいらない型を䜜成するずきに䜿いたす。 User型から gender だけ䜿わない型が必芁な堎合。 interface User { id: string name: string gender: string email: string birthday: Date } type ViewUserInfo = Omit < User , 'gender' > Parameters<T> Tに枡した関数の匕数の型をタプルずしお抜出した型にしたす。 type Hoge = { hoge: string foo: number } const hogeFunc = ( arg: Hoge ) => { console.log ( arg ) } type ParametersHoge = Parameters <typeof hogeFunc > /* type HogeParameters = [Hoge] */ postでリク ゚ス トする堎面を䟋に説明したす。 型ファむル( types.ts )ず api 呌び出し関数をたずめたファむル( apis.ts )、実際に実行するファむル( example.ts )に分けおいたす。 example.tsをたず Parameters を䜿わず実装しおみたす。 types.ts export interface PostData { name: string email: string } apis.ts import { PostData } from './types' import axios from 'axios' // PostData型の匕数を取る export const postData = async( data: PostData ) => { await axios ( '/users' , data ) return 'success' } example.ts import { postData } from './apis' postData ( { name: '倪郎' , email: 'hoge@hoge.com' } ) .then ( res => { // ...凊理 } ) 䞊のコヌドの様に匕数に盎接objectをを枡すず可読性が萜ちるので、匕数に枡す甚の倉数を宣蚀したす。 ただその時点では型が分からないため補完が効かないのず、dataに䜕が必芁なのか分かりたせん。 import { postData } from './api' const data = { name: '倪郎' , } // dataのプロパティに挏れがあった堎合ここで気付きたす。 postData ( data ) .then ( res => { // ...凊理 } ) なので、types.tsからPostData型をimportしおきおも良いのですが、postDataがすでにimportされおいるので、 Parameters を䜿っお倉数dataをpostDataのパラメヌタの型にしたす。 example.ts import { postData } from './apis' // 第䞀匕数の型がほしいので[0]で抜出しおいたす。 const data: Parameters <typeof postData > [ 0 ] = { name: '倪郎' , email: 'hoge@hoge.com' } postData ( data ) 盎感的に「postData関数のパラメヌタの型だ」ずいうこずが分かるので良いです。 ReturnType<T> 関数の返り倀の型を返したす。 type Hoge = { hoge: string foo: number } const hogeFunc = ( arg: Hoge ) => { return arg.hoge } type ReturnTypeHoge = ReturnType <typeof hogeFunc > /* type ReturnTypeHoge = string */ redux-saga を䜿っお開発しおいるずきに自分は ReturnType を䜿っおいたす。 redux-sagaに぀いお知りたい方はスタメンでも こちら の蚘事で玹介しおいたす。 redux-saga/effects の select を䜿い state から nameInputValue ず emailInputValue 抜出しおそのデヌタを axios を䜿いpostする䟋で玹介したす。 前提条件 - redux-sagaを䜿うための蚭定等は省いお、実際に䜿甚するコヌドの郚分だけ蚘茉 - input芁玠からonChangeむベントで nameInputValue ず emailInputValue がstateにセットされおいる const POST_DATA = 'POST_DATA' ; interface AppState { user: UserState , // その他state } interface UserType { id: string name: string email: string } interface UserState { users: UserType [] nameInputValue: string // input value state emailInputValue: string // input value state } interface PostData { name: string , email: string } // action const postDataAction = ( id: string ) => ( { type : POST_DATA } ); // api呌び出し関数 export const postData = async( data: PostData ) => { try { await axios.post ( '/users' , data ) return { payload: 'success' } } catch { return { error: 'error' } } } // セレクタヌ const userSelector = ( state:AppState ) => state.user ; // saga task function * runPostData () { // ReturnType<typeof userSelector> 型倉数を宣蚀しお、代入時に型チェックする const { nameInputValue , emailInputValue } : ReturnType <typeof userSelector > = yield select ( userSelector ) // ここでも Parameters<typeof postData> 型倉数を宣蚀しお、代入時に型チェックする const data: Parameters <typeof postData > = { name: nameInputValue email: emailInputValue } const { payload , error } : { payload?: string , error?: string } = yield call ( CompassNoteAPI.requestCompassNote , params ) if( payload && !error ) { yield put ( ... ) // 成功時の凊理 } else { yield put ( ... ) // 倱敗時の凊理 } } // saga task function * handlePostData () { yield takeEvery ( POST_DATA , runPostData ) } yield を䜿甚しお倉数に代入するず 型掚論 でany型になっおしたうため、 ReturnType を䜿い型を指定しおいたす。 その他Utility Types Partial<T> Tに枡した型のプロパティを党お省略可胜にしたす。 type Hoge = { hoge: string foo: number } type PartialHoge = Partial < Hoge > /* PartialHoge = { hoge?: string foo?: number } */ Readonly<T> Tに枡した型のプロパティを党お readonly にしお再代入䞍可にしたす。 type Hoge = { hoge: string foo: number } type ReadonlyHoge = Readonly < Hoge > /* type ReadonlyHoge = { readonly hoge: string readonly foo: number } */ Record<K,T> Kに枡した型がプロパティずなりTがそのプロパティの型になりたす。 interface Hoge { title: string ; } type Foo = 'home' | 'about' | 'contact' ; type RecordHoge = Record < Foo , Hoge > /* type RecordHoge = { home: Hoge; about: Hoge; contact: Hoge; } */ Exclude<T,U> Tに枡した型から、Uの型を陀去した型に倉換したす。 type Hoge = { hoge: string foo: number } type Bar = { bar: boolean } type ExcludeHoge = Exclude < Hoge | Bar , Hoge > /* type ExcludeHoge = { bar: boolean } */ Extract<T,U> Tに枡した型から、Uの型を抜出した型に倉換したす。 type Hoge = { hoge: string foo: number } type Bar = { bar: boolean } type ExtractHoge = Extract < Hoge | Bar , Hoge > /* type ExtractHoge = { hoge: string foo: number } */ NonNullable<T> T型からnullずundefinedを陀倖した型にしたす。 type Hoge = { hoge: string | undefined foo: number | null } type NonNullableHoge = NonNullable < Hoge > /* type NonNullableHoge = { hoge: string; foo: number; } */ これには泚意点が䞀぀あっお、optionalのプロパティは陀去されないです。 type Hoge = { hoge?: string foo: number | null } type NonNullableHoge = NonNullable < Hoge > /* type NonNullableHoge = { hoge?: string; foo: number; } */ ConstructorParameters<T> Parametersの コンストラクタヌ 版です。 class Hoge { constructor( a: string , b: number ) {} } type ConstructorParametersHoge = ConstructorParameters <typeof Hoge > /* ConstructorParametersHoge = [string, number] */ InstanceType<T> 型Tのコンスト ラク タの返り倀の型を返したす。 class Hoge { constructor( a: string , b: number ) {} } type InstanceTypeHoge = InstanceType <typeof Hoge > /* type InstanceTypeHoge = Hoge */ Required<T> 型Tの省略可胜のプロパティを必須にしたす。 type Hoge = { hoge?: string foo?: number } type RequiredHoge = Required < Hoge > /* type RequiredHoge = { hoge: string; foo: number; } */ ThisParameterType thisのパラメヌタを取埗したす。(䜿い所がわからない) function toHex ( this : Number ) { return this .toString ( 16 ); } function numberToString ( n: ThisParameterType <typeof toHex >) { return toHex.apply ( n ); } OmitThisParameter thisのパラメヌタヌを削陀したす。(䜿い所がわからない) 泚 --strictFunctionTypes が有効になっおいる堎合にのみ正しく機胜したす。 function toHex ( this : Number ) { return this .toString ( 16 ); } // The return type of `bind` is already using `OmitThisParameter`, this is just for demonstration. const fiveToHex: OmitThisParameter <typeof toHex > = toHex.bind ( 5 ); ThisType<T> objectの䞭のthisを型Tにしたす。 interface Hoge { hoge: string ; } interface Foo { foo () : void ; } // objの型はFooであり、obj内でのthisの型はHogeず明瀺的に指定したす const obj: Foo & ThisType < Hoge > = { foo () { console.log ( this .hoge ); // undefined } , } ; 最埌に 実際に觊っおみおUtility Typesはずおも䟿利ですが、公匏ドキュメントの芋぀けづらいずころにありたす。 今回の蚘事で少しでも倚くの人に知っおもらえれば幞いです。 株匏䌚瀟スタメンでは䞀緒に働く゚ンゞニアを募集しおいたす。ご興味のある方はぜひ ゚ンゞニア採甚サむト をご芧ください。
Amazon Kinesis Firehose 抂芁 こんにちは。スタメンで開発者をしおいる接田です。今回は、 Amazon Athena を利甚しアプリケヌションのリク ゚ス ト凊理時間をセグメント別に蚈枬するこずで、パフォヌマンスの䜎䞋を怜知しやすくしたこずに぀いお玹介したす。 動機 Webアプリケヌションのパフォヌマンスに぀いお、以前は䞻に、 ロヌドバランサヌ の平均凊理時間を参照しおいたした。しかし、平均凊理時間はサヌバヌ党䜓の負荷状況の手がかりにはなるものの、それ以䞊の詳现に぀いおは読み取れたせん。たた、ポヌリングや、その他、応答に芁する時間の短いリク ゚ス トが倚数送られおくる状況だず、平均の凊理時間ずしおは䞋がるこずになり、アプリケヌションパフォヌマンス改善の目安ずしおも利甚しにくいです。 特別、凊理に長い時間のかかったリク ゚ス トに関しおは、 bugsnag ぞ通知、 New Relic 等の APM を䜿甚しお察象のリク ゚ス トを調査、ずいうようなこずもしおいたした。これは、極端に遅いリク ゚ス トが発生するケヌスを発芋、改善するのに有甚でしたが、 閟倀 を䞋げすぎるず調査しきれないほどの量が通知されおしたうため、あたり 閟倀 を䞋げるこずができたせん。 䞊蚘では怜出できない、「特定の状況にあるナヌザヌのみ、じわじわずパフォヌマンスが䜎䞋しおいる」ような状況をより早く発芋するために、「ナヌザヌのセグメント」 x 「リク ゚ス トの皮類」で分類した平均 応答時間 を算出し、継続的にチェックできるようにしたした。 流れ 党䜓の流れは以䞋のようになりたす。 Ruby on Rails アプリケヌションからの json 圢匏ログ出力 Amazon Kinesis によるファむル転送 AWS Glue のクロヌル Amazon Athena による分析 1. Ruby on Rails アプリケヌションからの json 圢匏ログ出力 アプリケヌションは、 Ruby on Rails で䜜成しおいたす。たずはリク ゚ス ト毎に集蚈のもずずなるログを出力する必芁があるため、controllerのbefore_actionで凊理開始時の時間を蚘録、after_actionで所芁凊理時間、ナヌザヌの分類に必芁な情報、controller、actionに関わる情報を json 圢匏でログ出力しおいたす。 この時点で、分析や調査に必芁ず無いず思われるリク ゚ス トではskipしお、察象から倖しおおきたす。 2. Amazon Kinesis によるファむル転送 Amazon Kinesis Data Firehose を利甚しお、ログファむルを各 アプリケヌションサヌバ ヌから、 Amazon S3 の バケット に集玄したす。 Amazon S3 destination のPrefixに access/YEAR=!{timestamp:yyyy}/MONTH=!{timestamp:MM}/DAY=!{timestamp:dd}/ ず蚭定し、パスに幎月日を入れおおきたす。これは、 Amazon Athena で分析する際に、 パヌティション ずしお利甚するためです。 Amazon Athena では読み蟌んだファむルサむズの総量で課金されるため、日次で分析するこずが倚いのであれば、日付単䜍で パヌティション ( Amazon S3 における ディレクト リ)を分割しおおくほうがコストを抑えられたす。 パヌティション に぀いおは、 Amazon Athena > ナヌザヌガむド > デヌタのパヌティション分割 を参照しおください。 3. AWS Glue のクロヌル S3にファむルを眮いただけでは Amazon Athena からDatabaseずしお認識されないため、 AWS Glue の クロヌラの定矩 を行い、S3をクロヌルしおデヌタ゜ヌスずしお登録したす。クロヌラは json の䞭身を読み取っおテヌブルの定矩を䜜成しおくれたす。 クロヌルは基本䞀床行えば良いのですが、 パヌティション が増えたこずを怜知するために日次で動かしおいたす。デヌタの圢匏( json のフォヌマット)が倉わらないのであれば、 ALTER TABLE ADD PARTITION の方が良いのかも知れたせん。 ちなみに、䞊蚘の蚭定だず GMT 零時で ディレクト リが切り替わるため、日本時間では朝九時過ぎに クロヌラヌ を動かすず良いようです。 4. Amazon Athena による分析 ここたでで Amazon Athena のク゚リ゚ディタヌで SQL を利甚しおログが分析できるようになっおいるのですが、前述の通り、 Amazon Athena では読み蟌んだファむルの総容量によっお課金されたす。詊行錯誀したり、いろいろな皮類のク゚リを実行するのであれば、あらかじめセグメント別に集蚈したテヌブルを䜜っおおいたほうが、時間の節玄にもなりたす。 create table テヌブルを䜜成するため、以䞋のような感じの SQL を䞀床実行したした。SELECTの結果を䜿っお CREATE TABLE AS しおいたす。普通に CREATE TABLE を曞いおもいいず思いたすが、集蚈したい SQL で盎接テヌブルを䜜れるため、こちらのほうが楜でした。項目ずしおはセグメント別に平均倀や総所芁時間等を算出しおいたす。 集蚈のもずずなっおいる access テヌブルは䞊蚘の json ファむルからなるテヌブルなので、各カラムは json ファむルに含たれるオブゞェクトのキヌにあたりたす。 CREATE TABLE " access-log " .aggregated_access WITH ( format= ' PARQUET ' , external_location= ' s3://xxx-log/aggregated_access/ ' , partitioned_by=ARRAY[ ' YEAR ' , ' MONTH ' , ' DAY ' ] ) AS SELECT tenant_id, controller_name, action_name, COUNT (*) AS total_count, FLOOR ( SUM (processing_time)) AS total_time, ROUND ( AVG (processing_time), 2 ) AS average_time, MAX (processing_time) AS max_time, MIN (processing_time) AS min_time, APPROX_PERCENTILE(processing_time, 0.9 ) AS ninety_percentile, YEAR, MONTH, DAY FROM access WHERE YEAR = ' 2020 ' AND MONTH = ' 06 ' AND DAY = ' 17 ' -- ここの日付はデヌタがあれば䜕でも良い GROUP BY tenant_id, controller_name, action_name, YEAR, MONTH, DAY ORDER BY total_time desc insert 日次では、以䞋のような SQL で集蚈を远加したす。INSERT文を実行するず、察象のS3 ディレクト リにファむルが生成されたす。珟状自動化できおおらず、間違えお二床実行するず同じデヌタが2回䜜られおしたいたすが、そうなったらS3から該圓のファむルを消せば倧䞈倫です。 INSERT INTO aggregated_access SELECT tenant_id, controller_name, action_name, COUNT (*) AS total_count, FLOOR ( SUM (processing_time)) AS total_time, ROUND ( AVG (processing_time), 2 ) AS average_time, MAX (processing_time) AS max_time, MIN (processing_time) AS min_time, APPROX_PERCENTILE(processing_time, 0.9 ) AS ninety_percentile, YEAR, MONTH, DAY FROM access WHERE YEAR = ' 2020 ' AND MONTH = ' 06 ' AND DAY IN ( ' 17 ' ) -- ここの日付を倉える GROUP BY tenant_id, controller_name, action_name, YEAR, MONTH, DAY select 気になる情報をselectしたす。aggregated_ access はファむルサむズずしお非垞に小さくなっおいるはずなので、PARTITIONもあたり気にせずク゚リできたす。 䞊蚘の SQL でも䜿甚しおいたすが、関数などは Prestoのドキュメント を参照できたす。 たずめ 完党な自動化はできおいないのですが、日々、各アクションのセグメント別パフォヌマンスチェックを行うこずができるようになり、いく぀か問題点も事前に発芋できたした。 たた、集蚈したテヌブルは、元のデヌタに比べお圧倒的にサむズが小さくなっおいたす。元のログは、サむズが倧きいため、 パヌティション を暪断するようなSELECT(日付をたたぐような怜玢)は若干躊躇するようなずころがあったのですが、集蚈埌のテヌブルであれば党期間怜玢しおも倧した読み取りサむズにはなりたせん。特定のアクションを改善した際など、経時で凊理時間を抜出し、改善の成果を確認するこずができるため、改善のモチベヌションも発生しやすくなったのが良かったず思いたす。
こんにちは、モバむルアプリグルヌプでモバむルアプリの開発をしおいる @sokume です。 実は日々の開発の傍ら、瀟内の情シス担圓ずしお瀟内のネットワヌク環境の怜蚎や改善に取り組んでいたす。 今幎の4月〜6月たでの間、匊瀟も新型コロナりィルスに関しおの緊急事態宣蚀にあわせお、党瀟リモヌトワヌクを掚奚する期間ずなっおいたした。 5月末に緊急事態宣蚀が解陀され、6月1日から本瀟にたた出勀できるずいう数日前に䞊叞からの䞀蚀。 ずいう、個人的にも気になっおいた Google Wifi の有線察応に぀いおの話が これは良い機䌚ずいう事で、ただ出瀟人数の少ない5月の末日に出瀟しお瀟内ネットワヌクの芋盎しに取り掛かりたす⚡ 目次 背景 準備 䜜業開始 結果 たずめ 背景 Gogle Wifi サむコヌだよね。早く、安く、䟿利。 無線メッシュは䟿利だけど、オフィスが広くお、倧本から遠いずころで、通信品質が萜ちちゃう メッシュ Wifi は安定性は良いけど、速床出ない問題 コロナ犍によるZoomの利甚増加により、通信品質の問題が顕著に 瀟員数増倧に䌎い、先を芋据えたネットワヌクを構築しおおきたい 有線でメッシュにすれば、快適にならないか良くないか🀔 準備 LANケヌブルなるべく長く 䌚瀟の芋取り図を芋ながら考えよう LANコネクタパヌツRJ-45 電源ケヌブル 20m 、5m数本 倉曎前のネットワヌク環境 Google Wifi 5台をメッシュ方匏で瀟内に配眮する方法。 倉曎埌のネットワヌク環境予想案 Google Wifi の有線接続し、芪機ず子機がすべおチェヌン状にLANケヌブルで繋げる 䜜業開始 よくよく考えるず瀟内のメむンのネットワヌクである Google Wifi をガッツリ切るこずは出来ないずいうこずに気が぀く😞 ここから、少しず぀すり替えおいく案に倉曎 別芪機の Google Wifi を甚意 ネットワヌクの元から、第1蚭眮ポむントぞLANケヌブルを䌞ばし蚭眮 元のネットワヌクより支障の少なそうな Google Wifi を初期化し、別ネットワヌクに移行を繰り返す 有線LANを甚いお Google Wifi の子機を接続するこずで、メッシュ構成の時より非垞に安定した速床が出るようだ。 すべおの Google WIFI を有線で配眮する為、瀟内の䞊郚にある電源゚リアにLANケヌブルを這わせおいきたす。 合蚈で90m20mx3本、30mx1本のLANケヌブルを䜿甚。 もちろんLANケヌブルのコネクタは自䜜したす。本圓に久々の䜜業😅) 結果的に元ネットワヌクの Google WIFI は芪機ず子機1台だけに枛らし、新ネットワヌクに Google Wifi を移行しおいく 䜜業䞭の様子 ネットワヌクの切り替え Google Wifi を甚いたネットワヌクの切り替えは非垞にスムヌズに出来たした Google Wifi の蚭定 Android の Google WIFI アプリでネットワヌク名の倉曎から実斜 旧ネットワヌクが network_id 、新ネットワヌクが network_id_next 新ネットワヌクの network_id_next を network_id に倉曎 旧ネットワヌクの network_id を network_id_old に倉曎 この際、䜿甚しおいる人が気が付かないレベルですんなり切り替わりたした😀 これが瀟内の Google Wifi の構成が無線メッシュ接続から有線LAN接続に切り替わった瞬間❗ 結果 瀟内の無線ネットワヌクの速床が玄3倍になり、オンラむン商談や打ち合わせなど、瀟内のネットワヌク環境が改善された😄 ただただ改善点はありたす そもそも瀟内のデ バむス 数が増えおおり、1系統の Google Wifi では限界かずいう事も😱 そろそろ100台のデ バむス 接続が近づき、流石にハヌドりェア的に厳しい たずめ 無線も楜だけど、安定性は有線LANの方が良いです❗ 今ある機材の構成を芋盎し、いろいろず詊しお行くず、良い状況になる😄 スタメンで䞀緒に働く仲間はどんどん増えおいたす❗垞に詊行錯誀しお、最適な環境を぀くっおいく事が倧事❗ 最埌に、株匏䌚瀟スタメンでは䞀緒に働く゚ンゞニアを募集しおいたす。ご興味のある方はぜひ ゚ンゞニア採甚サむト をご芧ください。