ããã«ã¡ã¯ãããã³ããšã³ããšã³ãžãã¢ã® æž¡é ã§ãã æ®æ®µ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ã®æ¹ãè¯ãã§ãâïž ä»ããæ©æã®æ§æãèŠçŽãããããããšè©ŠããŠè¡ããšãè¯ãç¶æ³ã«ãªãð ã¹ã¿ã¡ã³ã§äžç·ã«åã仲éã¯ã©ãã©ãå¢ããŠããŸãâïžåžžã«è©Šè¡é¯èª€ããŠãæé©ãªç°å¢ãã€ãã£ãŠããäºã倧äºâïž æåŸã«ãæ ªåŒäŒç€Ÿã¹ã¿ã¡ã³ã§ã¯äžç·ã«åããšã³ãžãã¢ãåéããŠããŸãããèå³ã®ããæ¹ã¯ãã² ãšã³ãžãã¢æ¡çšãµã€ã ãã芧ãã ããã
Recoilå
¥é ããã«ã¡ã¯ãããã³ããšã³ããšã³ãžãã¢ã® æž¡é ã§ãã æè¿ããã³ããšã³ãçéã§çãäžãã£ãŠããRecoilã«ã€ããŠåŠã³ãŸããã æ¬èšäºã¯èªåã®Recoilå
¥éã®ã€ãã§ã«èšäºã«ããã®ã§ãåçŽè
åãã«ãªã£ãŠããŸãã ç®æ¬¡ Recoilãšã¯ 䜿ã£ãŠã¿ã API Referenceãèªã åèãµã€ã Recoilãšã¯ Fecebookãæ°ããçºè¡šããã®Reactã®ç¶æ
管çã©ã€ãã©ãªã§ãã å
¬åŒããã¥ã¡ã³ã 䜿ã£ãŠã¿ã äœãããã£ãããããåãããªã人ãããããç¥ããªãã®ã§èªåã®åŠç¿æé ã玹ä»ãã€ã€å®éã«è§Šã£ãŠãããŸãã å
¬åŒããã¥ã¡ã³ãã®Getting Started & BasicTutorialããã Recoilã«ã€ããŠæžãããŠããèšäºãèªã å
¬åŒããã¥ã¡ã³ãã®API Referenceãèªã ã¿ãããªæãã§åŠç¿ããŸããã Getting Startedã§ã¯å
¥åããæåãåºåããã®ãšãã®å
¥åãããæåæ°ã«ãŠã³ããåºåããã¢ããªãäœã£ãŠãããŸãã ãã®èšäºã§ãGetting Startedãäžç·ã«ãã£ãŠãããŸãã ãŸãã¯ã¢ããªã±ãŒã·ã§ã³äœæã»ç§»å npx create - react - app my - app cd my - app recoil install npm install recoil or yarn add recoil RecoilRoot recoilã䜿ãã«ã¯ã«ãŒãã³ã³ããŒãã³ãã RecoilRoot ã§ããã App.js import React from 'react' ; import { RecoilRoot , } from 'recoil' ; function App () { return ( <RecoilRoot > <CharacterCounter / > < / RecoilRoot> ) ; } CharacterCounterã§ã¯å
¥åãã©ãŒã ãšå
¥åãããæåãåºåãã TextInput ãšå
¥åãããæåæ°ãåºåãã CharacterCount ãåŒã³åºããŠããŸãã CharacterCounter.js import React from 'react' ; import TextInput from './TextInput' ; import CharacterCount from './CharacterCount' ; function CharacterCounter () { return ( <div > <TextInput / > <CharacterCount / > < / div> ) ; } export default CharacterCounter Recoilã§ã¯è€æ°ã³ã³ããŒãã³ãã§å
±æãããã¹ããŒãã¯atomãšåŒã°ããŸãã ä»åã¯TextInputã§æ±ãstateãCharacterCountã§ã䜿ãããã®ã§ãatom颿°ãçšããŠäœããŸãã atomã®å€ãèªã¿åãã³ã³ããŒãã³ããæé»çã«ãµãã¹ã¯ã©ã€ããããã®ã§ãatomã«æŽæ°ãå
¥ããšãã®atomã䜿çšããŠããã³ã³ããŒãã³ãå
šãŠã«åã¬ã³ããªã³ã°ãèµ°ããŸãã export const textState = atom ({ key : 'textState' , default : '' }) atomãäœãã«ã¯key(äžæã®ID)ãšããã©ã«ãå€ãèšå®ããŸãã æŽã«èªã¿åãæžã蟌ã¿ãããå Žåã¯ã useRecoilState ã䜿ããŸãã useRecoilState ã¯useStateã¿ãããªæèŠã§äœ¿ããŸãã ããã©ã«ãå€ãuseStateãšéããå
çšå®çŸ©ããatomãåŒæ°ã«åãåããŸãã TextInput.js import React from 'react' ; import { atom , useRecoilState } from 'recoil' ; export const textState = atom ({ key : 'textState' , default : '' }) function TextInput () { const [ text , setText ] = useRecoilState ( textState ) ; const onChange = ( event ) => { setText ( event . target . value ) ; } ; return ( <div > <input type = "text" value = { text } onChange = { onChange } / > <br / > Echo: { text } < / div> ) ; } export default TextInput CharacterCountã§ã¯å
çšå®çŸ©ãã textState ã䜿ã£ãŠãã®æåæ°ãã¬ã³ããŒããŸãã atomã®å€ããèšç®ããå¥ã®å€ãšããŠåºãã«ã¯ selector 颿°ã䜿ããŸãã const characterCountState = selector ({ key : 'characterCountState' , get : ({ get }) => { const text = get ( textState ) // getã®åŒæ°ã«stateãæž¡ãã return text . length } }) Recoilã§ã¯ atom ãš selector ãåãããŠstateãšåŒã³ãŸãã ã©ãããããšããšèšããšãå
çšäœ¿çšããuseRecoilStateã¯atomã ãã§ã¯ãªãselectorã«å¯ŸããŠã䜿ããŸãã atomãšselectorã¯ã°ããŒãã«ãªå€ãæäŸãããšããç¹ã§å
±éããŠããŸãã éããšããŠã¯ãatomã¯èªèº«ãå€ãæã£ãŠãããselectorã¯atomããç®åºãããå€ãšãããšããã§ãã atomãšåæ§ã« useRecoilState ã䜿ãå€ãåãåºããŸãã ã§ãããä»åå¿
èŠãªã®ã¯å€ã®èªã¿åãã ããªã®ã§ãã®å Žåã«ã¯ useRecoilValue ã䜿ããŸãã éã«æžã蟌ã¿ã ããããå Žåã«ã¯ useSetRecoilState ã䜿ããŸãã äžèšïŒã€ã®hooksããŸãšãããš const hogeState = atom ({ key : 'hogeState' , default : '' }) // useRecoilState const [ hoge , setHoge ] = useRecoilState ( hogeState ) // useRecoilValue const hoge = useRecoilValue ( hogeState ) // useSetRecoilState const setHoge = useSetRecoilState ( hogeState ) // useRecoilStateã§å€ã ãåãå Žå const [ hoge ] = useRecoilState ( hogeState ) // useRecoilStateã§æŽæ°é¢æ°ã ãåãå Žå const [ , setHoge ] = useRecoilState ( hogeState ) ä»å㯠useRecoilValue ã䜿ã£ãŠå€ãèªã¿åããŸãã CharacterCount.js import React from 'react' ; import { selector , useRecoilValue } from 'recoil' ; import { textState } from './TextInput' const characterCountState = selector ({ key : 'characterCountState' , get : ({ get }) => { const text = get ( textState ) return text . length } }) function CharacterCount () { const count = useRecoilValue ( characterCountState ) return ( <p > character count: { count } < / p> ) ; } export default CharacterCount ããã§Getting Startedã®ã¢ããªã¯å®æã§ãã API Referenceãèªã Getting Startedã ããã£ãŠãåºæ¬çãªããšããããããªãã®ã§ãGetting StartedãçµãããAPI Referenceãèªã¿ãŸããã ãã®èšäºã§ã¯Recoilã§åºç€çãªæŠå¿µã® atom ãš selector ã玹ä»ããŸãã atom() atom(options) options key : atomãèå¥ããäžæã®æåå ã¢ããªã±ãŒã·ã§ã³å
šäœã§ä»ã®atom,selectorã«å¯ŸããŠäžæã®ã§ããå¿
èŠããã default : atomã®åæå€ selector() selector(options) options key : atomãšåããselectorãèå¥ããäžæã®æåå get : åŒæ°ããgetãåãåãget颿°(ããããããã) get : get颿°ããåãåã£ãgetã«stateãæž¡ãããšã§ãã®stateå€ãçšããããšãã§ããã getã«ããããŠããstateã¯æé»çã«ãªã¹ãã«è¿œå ãããã®ã§stateãæŽæ°å
¥ããšselectorã¯åè©äŸ¡ãããŸã set? : ãã®ããããã£ãèšå®ããããšæžèŸŒã¿å¯èœãªselectorã«ãªããŸããgetãšsetããã©ã¡ãŒã¿ãšããŠãªããžã§ã¯ããæž¡ã颿° get : set颿°ããåãåãgetã¯æž¡ããatom/selectorã«ã»ã¬ã¯ã¿ãŒããµãã¹ã¯ã©ã€ãããªã set : ä»ã®atom/selectorã«æžã蟌ãããã®set颿° setã¯Referenceèªãã ã ãã ãšã¡ãããšçè§£ã§ããªãã£ãã®ã§ç°¡æãªã³ãŒããèŠãªãã説æããŸãã æµããå
ã«èšèŒããŠããã®ã§ã³ãŒããšç
§ããåãããªããèªãã§ã¿ãŠãã ããã sCount åæå€0 setSCount(sCount + 1) å®è¡ set颿°ã§åãåã£ãä»ã®atomãæžã蟌ãããã®(ä»åã®å Žåã¯nomalCountã«æžã蟌ã)set颿°ãšãnewValue( 1 )ãåãåã atomã«æžã蟌ãããã®set颿°ã«nomalCountãæž¡ããnewValueã10åã«ããŠè¿ã nomalCountãæŽæ°ãããã®ã§specialCountã®getãå®è¡ããnomalCountã®å€ãè¿ã sCountãæŽæ°ãããæç» import React from 'react' import { atom , selector , useRecoilState } from 'recoil' ; const nomalCount = atom ({ key : 'nomalCount' , default : 0 , }) ; const specialCount = selector ({ key : 'specialCount' , get : ({ get }) => get ( nomalCount ) , set : ({ set } , newValue ) => set ( nomalCount , newValue * 10 ) , }) ; export default function SpecialCounter () { const [ sCount , setSCount ] = useRecoilState ( specialCount ) ; const addSCount = () => setSCount ( sCount + 1 ) ; return ( <div > <p > specialCount: { sCount } < / p> <button onClick={addSCOunt}>add special< / button > < / div> ) ; } åèãµã€ã https://blog.uhy.ooo/entry/2020-05-16/recoil-first-impression/ ãŸãšã æ®æ®µèªåã¯Reduxã䜿ã£ãŠç¶æ
管çãããŠããŸããReduxãšRecoilã§ã¯ãstateã®æã¡æ¹ãç°ãªãããšããããããŸããã Reduxã§ã¯ãããžãã¯ãç»é¢ããšã«reducerãæã¡ã combineReducers ã䜿ã£ãŠäžã€ã®reducerã«ãŸãšããŸãã stateã䜿ãå Žå㯠useSelector ã䜿ã£ãŠstoreã®stateããèªåãå¿
èŠãªããŒã¿ãåãåºããŸãã äžæ¹Recoilã¯ãããã屿çã«stateããã¡ãstateãå©çšããã³ã³ããŒãã³ãéã§ã®ã¿å
±æããŸãã ä»ã«ãRecoilã§ã¯Reduxã¿ããã«storeãäœã£ãŠreduceräœã£ãŠactionäœã£ãŠã¿ãããªäœæ¥ããªããŠãããã«åãæãããã®ãå人çã«ã¯å¬ããã£ãã§ãã æåŸã«ãæ ªåŒäŒç€Ÿã¹ã¿ã¡ã³ã§ã¯äžç·ã«åããšã³ãžãã¢ãåéããŠããŸãããèå³ã®ããæ¹ã¯ãã² ãšã³ãžãã¢æ¡çšãµã€ã ãã芧ãã ããã
ããã«ã¡ã¯ãã¹ã¿ã¡ã³ã§äž»ã«ããã¯ãšã³ãã®éçºãæ
åœããŠããæ²³äºã§ãã ä»å㯠Firebase Cloud MessagingïŒä»¥äž FCMïŒãå©çšããããã·ã¥éç¥ã®äžæ¬éä¿¡ã«ã€ããŠæžããŠã¿ãŸãã èæ¯ å®ã¯ä»¥åã«ã FCM ãå©çšããéç¥ã®èšäº ãæžããŠããŠãããã§ã¯åã ãã€ã¹ ãžã®éç¥1åã«ã€ã1å FCM ãžãªã¯ ãšã¹ ããããæ¹æ³ã玹ä»ããŸããã ããããµãŒãã¹ãæ¡å€§ããŠãããšéç¥å
ã®ã ãã€ã¹ ãå¢ããåå¥ã«ãªã¯ ãšã¹ ããããŠããããšã«ããå¹çã®æªããç®ç«ã£ãŠããŸãã ããã§è§£æ±ºçãšãªãã®ãè€æ°ã ãã€ã¹ ãžã®äžæ¬éç¥ã§ãã è€æ°ã®ãªã¯ ãšã¹ ããäžåã§ãŸãšããŠéä¿¡ããããšã§ããã©ãŒãã³ã¹ã®åäžãæåŸ
ã§ããŸãã è€æ°ã ãã€ã¹ éç¥ã®ä»æ§ã«ã€ããŠ è€æ°ã ãã€ã¹ éç¥ã®å
¬åŒããã¥ã¡ã³ã㯠ãã¡ã ã§ãã 以åã®ããã°èšäºã§ãè§Šããããã«ã Ruby ã«ã¯ Firebase å
¬åŒã® SDK ã¯ãããŸããã éå
¬åŒã® Gem ããããŸãããããã§ã¯ããã¥ã¡ã³ãã® REST ã®é
ç®ãèŠãŠèªåã§å®è£
ããŠã¿ãŸãã ãªã¯ ãšã¹ ã ãŸãã¯ãªã¯ ãšã¹ ãã®äžèº«ãèŠãŠã¿ãŸãããã --subrequest_boundary Content - Type : application / http Content - Transfer - Encoding : binary Authorization : Bearer ya29 .ElqKBGN2Ri_Uz...HnS_uNreA POST / v1 / projects / myproject - b5ae1 / messages : send Content - Type : application / json accept : application / json { " message ": { " token ":" bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1... ", " notification ": { " title ":" FCM Message ", " body ":" This is an FCM notification message! " } } } ... --subrequest_boundary Content - Type : application / http Content - Transfer - Encoding : binary Authorization : Bearer ya29 .ElqKBGN2Ri_Uz...HnS_uNreA POST / v1 / projects / myproject - b5ae1 / messages : send Content - Type : application / json accept : application / json { " message ": { " token ":" cR1rjyj4_Kc:APA91bGusqbypSuMdsh7jSNrW4nzsM... ", " notification ": { " title ":" FCM Message ", " body ":" This is an FCM notification message! " } } } --subrequest_boundary-- FCM ã®ããã¥ã¡ã³ãã§ã¯ãã®åœ¢åŒã«ã€ããŠç¹ã«è§£èª¬ããŠããŸãããããã㯠"--åºåãæåå" ãšãã圢åŒã®æååã§å§ãŸã åã
ã®ãªã¯ ãšã¹ ãå
容ãæååãšã㊠"--åºåãæåå" ã§é£çµã æåŸã "--åºåãæåå--" ã§éãã 1ã€ã®æååïŒããã¹ããã¡ã€ã«ïŒ ã§ããæ¹è¡ã¯ãã¹ãŠ CRLFïŒ \r\n ïŒ ã䜿ããŸãã åºåãæåå ã«ã¯ãªã¯ ãšã¹ ãéä¿¡æã® boundary=" " ã§ä»»æã®æååã䜿çšã§ããŸãã ãã㯠mulitpart/mixed ãšãã圢åŒã§ã詳ãã説æã¯ RFC 2046 ã«è²ããŸãã ãªããåã
ã®ãªã¯ ãšã¹ ãã«å«ããå
容ãšããŠã¯åå¥éç¥ã®ãšããšåããã®ãªã®ã§ããã¡ãã«ã€ããŠã®è©³çŽ°ã¯ éå»ã®èšäº ãèŠãŠãã ããã ã¬ã¹ãã³ã¹ ã¬ã¹ãã³ã¹ãåãããã«ãåã
ã®ãªã¯ ãšã¹ ãã®ã¬ã¹ãã³ã¹ã1ã€ã®æååã«ãªã£ããã®ãè¿ã£ãŠããŸãã --batch_nDhMX4IzFTDLsCJ3kHH7v_44ua- aJT6q Content - Type : application / http Content - ID : response - HTTP /1.1 200 OK Content - Type : application / json; charset = UTF -8 Vary : Origin Vary : X - Origin Vary : Referer { " name ": " projects/35006771263/messages/0:1570471792141125%43c11b7043c11b70 " } ... --batch_nDhMX4IzFTDLsCJ3kHH7v_44ua- aJT6q Content - Type : application / http Content - ID : response - HTTP /1.1 200 OK Content - Type : application / json; charset = UTF -8 Vary : Origin Vary : X - Origin Vary : Referer { " name ": " projects/35006771263/messages/0:1570471792141696%43c11b7043c11b70 " } --batch_nDhMX4IzFTDLsCJ3kHH7v_44ua- aJT6q -- ãã®ãããã¬ã¹ãã³ã¹ã«ã¯ããããªã¯ ãšã¹ ãã«å«ãããªã¯ ãšã¹ ããšåãé çªã§å¯Ÿå¿ããã¬ã¹ãã³ã¹ãå«ãŸããŠããŸãã å
çšè§ŠããŸããã§ãããã Content-ID: foo ãšãããã£ãŒã«ããåã
ã®ãªã¯ ãšã¹ ãã«å
¥ããŠãããšã¬ã¹ãã³ã¹ã« Content-ID: response-foo ãšãã圢ã§å
¥ã£ãŠããŸãã åãªã¯ ãšã¹ ãã«ã€ããŠããŒã¯ãªæååã䜿çšããããšã§ãªã¯ ãšã¹ ãã«å¯Ÿå¿ãããã®ãã©ããã確èªã§ããŸãã æ³šæç¹ãšããŠãããããªã¯ ãšã¹ ãèªäœã®æåŠãšåã
ã®ãªã¯ ãšã¹ ãã®æåŠã¯ç¬ç«ããŠãããšããããšãæããããŸãã äŸãã°ããããã¬ã¹ãã³ã¹èªäœã¯æ£åžžã«åäœããã®ã§ ã¹ããŒã¿ã¹ã³ãŒã 㯠200ããã ããããªã¯ ãšã¹ ãã®éç¥å
ã®ã ãã€ã¹ ãååšãããã®ãªã¯ ãšã¹ ãã«å¯Ÿããã¬ã¹ãã³ã¹ã³ãŒã㯠404 ãšãªã£ãŠããããšããç¶æ³ãããããŸãã ãã®ããåã
ã®ã¬ã¹ãã³ã¹ã«åå²ããŠãªã¯ ãšã¹ ãã®æåŠã確èªããå¿
èŠããããŸãã Ruby ã§ã®å®è£
äžéã仿§ã«ã€ããŠã¯ææ¡ã§ããã®ã§ãããã Ruby ã§å®è£
ããŠã¿ãŸãã HTTP ãªã¯ ãšã¹ ãã«ã¯ Ruby æšæºã® Net::HTTP ã䜿ããŸãã ãªã¯ ãšã¹ ãããã£ã®äœæ ãŸãã¯ãªã¯ ãšã¹ ãããã£ãäœæããŸãã äžã§è§Šãã圢åŒã«æ²¿ã£ãŠã以äžã®ããã«1è¡ãã€æååãè¶³ããŠãããŸãã boundary = " subrequest_boundary " # åºåãæåå buffer = "" tokens.each do | token | buffer += " -- #{ boundary }\r\n" buffer += " Content-Type: application/http \r\n" buffer += " Content-Transfer-Encoding: binary \r\n" buffer += " Content-Id: #{ token }\r\n" buffer += " Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA \r\n" buffer += "\r\n" buffer += " POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send \r\n" buffer += " Content-Type: application/json \r\n" buffer += " accept: application/json \r\n" buffer += "\r\n" body = { message : { token : token, notification : { title : " FCM Message " , body : " This is an FCM notification message to device 0! " } } } buffer += body.to_json buffer += "\r\n" end buffer += " -- #{ boundary } -- \r\n" åºåãæååã«ã¯å
¬åŒããã¥ã¡ã³ãã«åŸã£ãŠ subrequest_boundary ãšããŠããŸãã Content_id ã«ã¯ã ãã€ã¹ ã®èå¥ ããŒã¯ ã³ãå
¥ããŠã¿ãŸããã ãªãå¶éãšããŠã1åã®ãªã¯ ãšã¹ ãã«ã¯æå€§500åãŸã§ ããŒã¯ ã³ãå«ããããšãã§ããŸãã ããããªã¯ ãšã¹ ãã®å®è¡ curl ã§æžãããŠããããããªã¯ ãšã¹ ãã Net::HTTP ã®åœ¢åŒã«å€æããŸãã require ' net/http ' require ' uri ' uri = URI .parse( " https://fcm.googleapis.com/batch " ) request = Net :: HTTP :: Post .new(uri) request.content_type = " multipart/mixed; boundary=\" #{ boundary } \" " request.body = buffer # <= ããã§ãã£ãäœã£ãæååãå
¥ãã req_options = { use_ssl : uri.scheme == " https " , } response = Net :: HTTP .start(uri.hostname, uri.port, req_options) do | http | http.request(request) end 倿ã«ã¯ curl-to-ruby ã䜿ããŸããã content_type ã§åºåãæåãæå®ããrequest body ã«å
ã»ã©äœæããããããªã¯ ãšã¹ ãããã£ãå
¥ããŸãã ã¬ã¹ãã³ã¹ããã£ã®è§£æ ããããªã¯ ãšã¹ ãã®ã¬ã¹ãã³ã¹ã¯ãªã¯ ãšã¹ ãæã«æå®ããŠãæååã§åºåãããŠããŠããã€ããããªã¯ ãšã¹ ããšåãé çªã§ã¬ã¹ãã³ã¹ãå«ãã§ããŸãã ãŸãã¯åºåãæååã䜿ã£ãŠã¬ã¹ãã³ã¹ãåå²ããŸãã responses = response.body.split(boundary)[ 1 .. -2 ] ã¬ã¹ãã³ã¹ã¯ --åºåãæå ã§å§ãŸã --åºåãæåå-- ã§çµããããšãæãåºããšãåå²ããé
å㯠[ '' , res1, res2, ... , ' -- ' ] ãšãªãã®ã§å
é ãšæ«å°Ÿã®èŠçŽ ã¯é€ããŠãããŸãã åã
ã®èŠçŽ ã¯ä»¥äžã®ãããªæååã§ãã Content - Type : application / http Content - ID : response - HTTP /1.1 200 OK Content - Type : application / json; charset = UTF -8 Vary : Origin Vary : X - Origin Vary : Referer { " name ": " projects/35006771263/messages/0:1570471792141696%43c11b7043c11b70 " } ããã§ æ£èŠè¡šçŸ ã§ ã¹ããŒã¿ã¹ã³ãŒã ãš content_id ãæãåºããŠã¿ãŸãã code = res.match( / HTTP \/ 1 \. 1 (\d{3})/ )[ 1 ] token = res.match( / Content-ID: response- (.*)\r\n/ )[ 1 ] ããšã¯ãããã®æ
å ±ãå
ã«ãªãã©ã€ãªãããŸãããã ãŸãšã FCM ãå©çšããŠè€æ°ã ãã€ã¹ ã«äžæ¬ã§éç¥ããæ¹æ³ã玹ä»ããŸããã äžæ¬ã§éä¿¡ããããšã§ãªã¯ ãšã¹ ãæ°ã倧å¹
ã«æžããããšãã§ããŸãã ä»ã®ãšããäžæ¬éä¿¡ãå©çšããããšã«ããé
ä¿¡ã®é
å»¶ãæããŠããªãã®ã§ãããã©ãŒãã³ã¹ã«ãæ©ã¿ã®æ¹ã¯ãã²è©ŠããŠã¿ãŠãã ããã æåŸã«ãæ ªåŒäŒç€Ÿã¹ã¿ã¡ã³ã§ã¯äžç·ã«åããšã³ãžãã¢ãåéããŠããŸãã ãèå³ã®ããæ¹ã¯ãã² ãšã³ãžãã¢æ¡çšãµã€ã ã Wantedly ãã芧ãã ããã Photo by Jamie Street on Unsplash
ã¯ããã« ããã«ã¡ã¯ãã¹ã¿ã¡ã³ ãšã³ãžãã¢ã®ããã¢ãã§ãã æ®æ®µã¯åŒç€Ÿã®ãããã¯ãã§ããã TUNAG ããã TERAS ãã®éçºãè¡ã£ãŠããããã©ã€ããŒãã§ã¯ ã¿ããã€Web ( @misokatsu_web )ãšããã°ã«ãŒãã§æŽ»åããŠããŸãã ã¿ãã ã€ãŠã§ãã®æŽ»åãšããŠã5/30ïŒåïŒã«ã ã¿ããã€ç€Ÿ ãšã³ãžãã¢ã»ãã¶ã€ã㌠転è·äŒè° ãã®éå¬ãäºå®ããŠããããã®ã€ãã³ããµã€ãã Gatsby JSã§äœæããŸããã ä»åã®èšäºã¯ããã®ããšã«ã€ããŠã玹ä»ãããŠããã ããŸãã ã¿ãã ã€ãŠã§ããšã¯ïŒ ã¿ãã ã€ãŠã§ãã¯åå€å±ãæ ç¹ãšããŠããšã³ãžãã¢ããã¶ã€ããŒãªã©ã ITæ¥çã«é¢ãã人å士ã®äº€æµã®å ŽãæäŸãããããä»å¹Žã®ïŒæããæŽ»åããŠããŸãã 䌌ãå¢éã®ä»²éãæ¢ããããçžè«ã§ããå人ãäœã£ãã ITãšããããŒã¯ãŒãã§ç¹ãããã³ãã¥ããã£ã§ããããããŸã§ã«ãªã³ã©ã€ã³ã®åº§è«äŒãªã©ãè¡ã£ãŠããŸããã äŸãã° ãããã¯ããããžã¡ã³ã ãããŠãã人ããã瀟å
ã ãšçžè«ã§ããçžæãããªã...ãããšããå Žåã«ã ã¿ãã ã€ãŠã§ããéããŠãä»ç€Ÿã®åããããªåœ¹å²ãæã€äººãšæ°è»œã«è©±ãããããšãã§ããŸãã æŽ»åå
容ã¯éå¶ã¡ã³ããŒã§éæè©±ãããŠããããäºãã«å°ã£ãŠããããšãæã¡å¯ã£ãŠã€ãã³ãã®äŒç»ãããŠããŸãããããªäžã§ãã©ã®ã¡ã³ããŒããæ¡çšãé 匵ãããããšããæãããããŸããã ã€ãã³ãã®çµç·¯ æ¡çšãé 匵ããããšããæãããããåå€å±ã«æ ç¹ãããäŒæ¥ããã€ãŸã£ãŠããªã³ã©ã€ã³ã®æ¡çšã€ãã³ããã§ããªããïŒããšããæ¡ãçãŸããŸãããããããåã¡ã³ããŒãåå€å±ã®ITç³»äŒæ¥ããèªãããèšïŒç€Ÿã®åå äŒæ¥ãéãŸããã ã¿ããã€ç€Ÿ ãšã³ãžãã¢ã»ãã¶ã€ã㌠転è·äŒè° ããéå¬ããããšã«ãªããŸããã 5/30ã®éå¬ã«åããŠãã¡ã³ããŒã§æºåãé²ããŠããŸãã åå è
ãžã®åç¥ãè¡ãããã«ã€ãã³ããµã€ããäœãããšæãã å®è£
å·¥æ° ãæžããã ãã³ãã¬ãŒãã®ããªãšãŒã·ã§ã³ãè±å¯ 现ãããšããã¯èªåã§ã«ã¹ã¿ãã€ãºã§ãã ãšãã芳ç¹ãããGatsbyJSã§ãµã€ããäœãããšã«ããŸããã GatsbyJSãšã¯ïŒ GatsbyJS å
¬åŒ ReactãããŒã¹ã«ãããé«éãªãŠã§ããµã€ãã»ã¢ããªãã€ãã ãªãŒãã³ãœãŒã¹ ãã¬ãŒã ã¯ãŒã¯ ã Markdown ã«ãã ããã¥ã¡ã³ããŒã·ã§ã³ ãæçš¿ã API ãªã©ã®ããŒã¿ãœãŒã¹ãGraphQLã§ç®¡çããã³ãŒãåå²ã»ç»åã®æé©åã»é
å»¶èªã¿èŸŒã¿ãªã©ã®ããã©ãŒãã³ã¹æé©åãããŠãµã€ããé«éåããŸãã GatsbyJSã¯å°å
¥ãç°¡åã§ã $ npm install -g gatsby-cli ãå®è¡ããããšã§å°å
¥ã§ããŸããïŒ npm ãå
¥ã£ãŠããªãå Žåã¯å
ã«ãã€ã³ã¹ããŒã«ããŠãã ãããïŒ å®è£
ä»å㯠gatsby-starter-solidstate ãšãããã³ãã¬ãŒããçšããŠãµã€ããäœæããŸããã ãŸãã¯ãã³ãã¬ãŒããããŒã¹ã«ããã¶ã€ã³ã®çŽ æ¡ã Adobe XDã§äœããŸãã ãã¶ã€ã³ãã§ããããããŒã«ã«éçºç°å¢ãæ§ç¯ããŸãã gatsby -starter-solidstateã® GitHub ããŒãžã«åŸãã以äžã®ã³ãã³ããå®è¡ããŸãã $ gatsby new <ãµã€ãå> https://github.com/anubhavsrivastava/gatsby-starter-solidstate $ cd <察象ãã£ã¬ã¯ããª> $ yarn install $ gatsby develop ããã ãã§ãããŒã«ã«éçºç°å¢ã§ãã³ãã¬ãŒãã®ãµã€ããç«ã¡äžããããšãã§ããŸããïŒæ¥œãããããïŒ ããããã€ãã³ããµã€ãçšã«ã«ã¹ã¿ãã€ãºãããã®ã§ãç¬èªã®ã¹ã¿ã€ãªã³ã°ããããããstyled-componentsã®ã©ã€ãã©ãªãå
¥ããŸããå ããŠãæ¬çªç°å¢ã§styled-componentsãåäœãããã gatsby -plugin-styled-componentsãå
¥ããŸãã $ yarn add styled-components $ yarn add -D gatsby-plugin-styled-components gatsby -plugin-styled-componentsãé©çšããããããããžã§ã¯ãã®ã«ãŒãã«ãã gatsby -config.jsã® ãã©ã°ã€ã³ ã«è¿œå ããŸãã module.exports = { plugins: [ 'gatsby-plugin-styled-components' ] }; ããšã¯ã«ã¹ã¿ãã€ãºãããéšåãReact, styled-componentsã§å®è£
ããŠãããŸãã ããã〠GatsbyJSã§åºæ¥ããµã€ãã ãã¹ãã£ã³ã° ããæ¹æ³ã¯ãNetlifyã» AWS Amplifyãªã©ãããããããŸããïŒåèïŒ https://www.gatsbyjs.org/docs/deploying-and-hosting ïŒ ä»åã¯è©ŠããŠã¿ãããšæã£ãŠãã Firebase Hosting ã§ãµã€ãããããã€ããŸããã ãŸã㯠Google ã¢ã«ãŠã³ãã®çšæãã Firebase ã³ã³ãœãŒã«ãžãã°ã€ã³ & ãããžã§ã¯ãã®äœæãè¡ããŸããå ããŠãfirebase CLI ãã€ã³ã¹ããŒã«ããŸãã $ npm install -g firebase-tools 次ã«firebaseã§ãã°ã€ã³ããFirebase ãããžã§ã¯ããåæåããŸãã $ firebase login $ firebase init initãããšãå©çšããfirebaseã®æ©èœã»å¯Ÿè±¡ã®firebaseãããžã§ã¯ãçãèãããã®ã§éžæããŸãã ãããŠæåŸã«ä»¥äžã®ã³ãã³ããå®è¡ããŸãã $ firebase deploy ããã ãã§ãµã€ãããããã€ããããšãã§ããŸãã 宿ãããã®ã ãã¡ã ãããã« ä»åã¯GatsbyJSãçšããŠã€ãã³ããµã€ããäœæããŸãããGatsbyJSãå©çšããã° å·¥æ° ãããããèªåã®æãæããµã€ããäœãããšãã§ããŸãããŸãããã³ãã¬ãŒãã®çš®é¡ãããããããããšã§ãã€ãã³ããµã€ãã®ãã¶ã€ã³ãèããæã®åèã«ãªããŸãããèå³ãããæ¹ã¯ãã²ã詊ããã ããã ã¹ã¿ã¡ã³ã§ã¯äžç·ã«éçºããŠããããšã³ãžãã¢ãåéããŠããŸãïŒèå³ãæã£ãŠãããæ¹ã¯ããã² ãšã³ãžãã¢æ¡çšãµã€ã ãã芧ãã ããã
æŠèŠ ã¯ãããŸããŠãã¹ã¿ã¡ã³ã§ããã³ããšã³ããšã³ãžãã¢ãããŠãã @0906koki ã§ããçãã¬ãé±5ã§ããçèãšã³ãžãã¢ã§ãã æ®æ®µã¯ReactãšTypeScriptã§éçºãããŠããŸããããµãŒããŒãµã€ãã®RailsãAPIãäœæããã¿ã€ãã³ã°ã§è§ŠããŸãã åŒç€Ÿã®ãããã¯ãã§ãã TUNAG ã§ã¯ãããã³ããšã³ããReactãReduxã§å®è£
ããŠãããReduxã«redux-sagaãå°å
¥ããŠããŸããä»åã¯ããã®redux-sagaã«ã€ããŠæžããããšæããŸãã â» æ¬èšäºã¯ReactãšReduxãããçšåºŠçè§£ããŠãã人åããšãªã£ãŠãããŸãã çµç·¯ Reduxãè§Šã£ãŠãããšãéåæåŠçãªã©ã®å¯äœçšããredux-thunkãredux-sagaãªã©ã®middlewareãåãæã€ããšãå€ããšæããŸãããŸããReduxã䜿ããã«ãReactã©ã€ããµã€ã¯ã«å
ã§éåæåŠçãèšè¿°ããããšããããšæããŸãã Reduxæãç¡ãã«é¢ããããéåæåŠçã«ãã£ãŠã³ãŒããè€éåããã±ãŒã¹ãå€ããéåæåŠçãã·ã³ãã«ã«ä¿ã€ããšã¯Reactã®å®è£
ã«ãããŠã¯éèŠãªèª²é¡ã§ãããããã課é¡ã«å¯ŸããŠãredux-sagaã¯ä»ã®ææ³ã«æ¯ã¹ãŠãã³ãŒããã·ã³ãã«ã«ä¿ã€ããšãã§ããã®ã§ãä»åã¯æ¹ããŠredux-sagaã«ã€ããŠåŠç¿ããŠã¿ãããšæããŸããã äž»ãªãã¬ãŒã ã¯ãŒã¯ãšã©ã€ãã©ãª reactïŒ16.3.2ïŒ react-reduxïŒ7.1.0ïŒ redux-sagaïŒ1.0.5ïŒ redux-sagaã®åºæ¬ redux-sagaã¯ãreduxã®åæçãªãããŒã®äžã«ãéåæåŠçãªã©ã®å¯äœçšãç°¡åã«çµã¿èŸŒããããã«ããã©ã€ãã©ãªã§ãããŸããredux-sagaã§ã¯ãç¬èªã®ã¿ã¹ã¯ãšããæŠå¿µã«ãã£ãŠãå¯äœçšã䞊åã§èµ°ãããããšãã§ããŸãã ããããå¯äœçšã®åŠçããåæçãªreduxãããŒã®ã©ãã§è¡ãããšãããšãmiddlewareãæ
åœããŸãã 倧ãŸããªreduxã®æµãã«ãredux-sagaãå
¥ãããšãããªæãã«ãªããŸãã ã¯ãªãã¯ãªã©ã®ãŠãŒã¶ãŒã€ãã³ãã«ãããActionãDispatchãã ãã®Actionãredux-sagaãæ€ç¥ããŠãã¿ã¹ã¯ãå®è¡ ãã®ã¿ã¹ã¯å
ã§ã®éåæåŠçãæåããå Žåãæåã®çµæãéç¥ããActionãDispatchãã éåžžã¯ãActionãDispatch â Reducerã§stateãæŽæ°ã®æµããªã®ã§ãããredux-sagaã䜿çšãããšãç¹å®ã®ActionãDispatchãããã¿ã€ãã³ã°ã§ããã®ã¢ã¯ã·ã§ã³ã«å¯Ÿå¿ãããžã§ãã¬ãŒã¿é¢æ°ãå®è¡ããŸããæŽã«ããã®é¢æ°ã®äžã§API callãè¡ããActionãDispatchããããšã§ãAPIã§åãåã£ãããŒã¿ãããšã«ãstateã«æŽæ°ããããããšãã§ããŸãã redux-sagaãæäŸããã¿ã¹ã¯å®è¡ã³ãã³ã redux-sagaã§ã¯ãã¿ã¹ã¯ãå®è¡ããã³ãã³ããã redux-saga/effects ã®importã«ãã£ãŠäœ¿çšã§ããŸããäž»ãªã³ãã³ãã¯ä»¥äžã®éãã§ãã putïŒActionãDispatchããïŒ takeïŒæå®ã®Actionã®Dispatchã®ç£èŠãè¡ãïŒ takeEveryïŒåãActionãè€æ°åæ¥ãå Žåã«ã䞊åã§åŠçãè¡ãïŒ takeLatestïŒåŠçããã£ã³ã»ã«ããæ°ããåŠçãè¡ãïŒ callïŒéåæåŠçãåŒã³åºããŠãçµäºãåŸ
ã€ïŒ forkïŒã¿ã¹ã¯ã®å®è¡ãè¡ãïŒ selectïŒstoreããstateãåãåºãïŒ ããããã³ãã³ããããžã§ãã¬ãŒã¿ãŒé¢æ°ã®äžã§äœ¿çšããŠãåæçã«èšè¿°ããããšãå¯èœã«ããŸãã redux-sagaã®ã¡ãªãã reduxã®äžã§éåæåŠçãªã©ã®å¯äœçšãåãæã€ã©ã€ãã©ãªãšããŠãredux-thunkããããããredux-sagaãšå¯Ÿæ¯ãããã·ãŒã³ãå€ãèŠåããããŸãã redux-sagaã§ã¯ãredux-thunkãšæ¯èŒããŠãã©ããã£ãã¡ãªããããããã§ããããïŒ redux-sagaãå°å
¥ããããšã®ã¡ãªãããšããŠã¯ãäž»ã«ä»¥äžã®ããšãæãããããšæããŸãã redux-thunkã¯Action Createrã«åŠçãèšè¿°ããäžæ¹ãredux-sagaã¯sagaã³ã³ããŒãã³ãã«èšè¿°ã§ãããããè€éåãã«ãã ãã¹ããæžãããã Async/Awaitãªã©ã®éåæåŠçã®ãã¹ããæ·±ããªããã«æžã redux-sagaãreduxã®äžã§äœ¿çšãã åºæ¬çãªredux-sagaã®äœ¿ãæ¹ãåãã£ãæã§ãå®éã«reduxã®äžã§redux-sagaã䜿çšããŠã¿ãããšæããŸãããŸãã¯ãredux-sagaãã€ã³ã¹ããŒã«ããŸãã npm install --save redux-saga ã¿ã¹ã¯ãäœæ ä»åã¯ãããŒã¿ããµãŒããŒãããã§ããããŠããŠãæåãããæåã®ã¢ã¯ã·ã§ã³ãDispatchã倱æããã倱æã®ã¢ã¯ã·ã§ã³ãDispatchããåŠçãSagaå
ã§æžããŠãããããšæããŸãã Sagaã§äœ¿çšããã¢ã¯ã·ã§ã³ãšããŠãããŒã¿ã®ãªã¯ãšã¹ããè¡ã REQUEST_FETCH_DATA ãããŒã¿ååŸãæåããå Žåã® SUCCESS_FETCH_DATA ãããŒã¿ååŸã倱æããå Žåã® FAILURE_FETCH_DATA ãå®çŸ©ããŸãã ãã®æ¬¡ã«ã src/sagas ã®ãã£ã¬ã¯ããªãäœæããé
äžã« sample.js ãã¡ã€ã«ãäœæããŸãããã®ãã¡ã€ã«ã«ä»¥äžã®ããã«èšè¿°ããŸãã import { call , select , fork , take , takeEvery , put } from 'redux-saga/effects' import fetchData from '../apis' import { REQUEST_FETCH_DATA , SUCCESS_FETCH_DATA , FAILURE_FETCH_DATA } from '../actions/actionTypes' import { successFetchData , failureFetchData } from '../actions' function* runRequestFetchData () { const { text } = yield select (( state ) => state ) const { payload , error } = yield call ( fetchData , text ) if ( payload && ! error ) { yield put ( successFetchData ( payload )) } else { yield put ( failureFetchData ( error )) } } ããã§ã¯ä»¥äžã®ãããªåŠçãè¡ã£ãŠããŸãã select ã§storeã«ãã text ã®stateãååŸ ãã®stateãfetchDataã®åŒæ°ã«å
¥ããŠã call ã«ãã£ãŠéåæåŠç ååŸãæåããŠããã°ã successFetchData ã¢ã¯ã·ã§ã³ãDispatch ååŸã«å€±æããŠããã°ã failureFetchData ã¢ã¯ã·ã§ã³ãDispatch äžèšã®ããã« runRequestFetchData ãšããã¿ã¹ã¯ãçšæããŸãããããã®ã¿ã¹ã¯ã¯ã©ããã£ãŠå®è¡ãããã®ã§ããããïŒ ãã®ã¿ã¹ã¯ã¯ REQUEST_FETCH_DATA ã¢ã¯ã·ã§ã³ãDispatchãããã¿ã€ãã³ã°ã§å®è¡ãããã®ã§ãActionãç£èŠããããã«åŠçãèšè¿°ããªããã°ãããŸãããç£èŠããã«ã¯ä»¥äžã®ããã«ã¿ã¹ã¯ã远å ããŸãã // çç¥ function* handleRequestFetchData () { yield takeEvery ( REQUEST_FETCH_DATA , runRequestFetchData ) } ããã ãã§ãActionãDispatchãããã¿ã€ãã³ã°ã§ã runRequestFetchData ãå®è¡ããããšãã§ããŸããç°¡åã§ããã â»äžŠåã§éåæåŠçãèµ°ãããå Žå éåæåŠçã䞊åã§å®è¡ããŠãããŒã¿ãååŸãããå Žåã¯ã以äžã®ããã«æžããŸãã const { firstData , secondData } = yield all ([ call ( fetchData1 , text1 ) , call ( fetchData2 , text2 ) ]) call all ã䜿çšãã䞊ååŠçã®å Žåã Promise.all ãšåæ§ã«ããã¹ãŠã®åŠçãresolveããããŸã§ããã©ã¡ãããrejectããããŸã§ãå€ãè¿ããŸããã redux-sagaã§ã®ãã¹ã redux-sagaã§ã¯ããã¹ãã®æžãããããã¡ãªãããšããŠæããŸããããã©ããã£ãŠãã¹ããæžãã®ã§ããããïŒ ããã€ãredux-sagaã®ãã¹ãçšã©ã€ãã©ãªã¯ååšããŸãããä»åã¯äžçªã¹ã¿ãŒæ°ã®å€ã redux-saga-test-plan ã䜿ã£ãUnitãã¹ãããå
çšã®ã³ãŒãã亀ããŠç޹ä»ããŸãã import { expextSaga } from 'redux-saga-test-plan' describe ( 'runRequestFetchData' , () => { test ( 'success' , () => { const successResponse = { payload : 'some data' } ; return expectSaga ( runRequestFetchData ) . provide ([ [ call . fn ( fetchData , 'text' ) , successResponse ] ]) . put ({ type : 'SUCCESS_FETCH_DATA' , payload : { payload : successResponse . payload } }) . run () ; }) ; test ( 'failure' , () => { const failureResponse = { error : 'ãšã©ãŒ' } ; return expectSaga ( runRequestFetchData ) . provide ([ [ call . fn ( fetchData , 'text' ) , failureResponse ] ]) . put ({ type : 'FAILURE_FETCH_DATA' , error : failureResponse . error }) . run () }) }) äžèšã®äŸã§ã¯ã runRequestFetchData ã¿ã¹ã¯ãå®è¡ãããšãã«ã put ã§æçµçã«è¿åŽãããããŒã¿ãšäžèŽããŠããããã¹ãããŠããŸãã redux-saga-test-planã䜿ã£ããã¹ãã§ã¯ãcallã§åŒã³åºãfetch颿°ãšããã®é¢æ°ãå®è¡ããå Žåã®è¿åŽããŒã¿ãProviderã䜿çšããããšã§ãäºãã»ããããŠããããšãã§ããŸããäžèšã®äŸã§ã¯ãæåããå Žåãšå€±æããå Žåã®ãªã¹ãã³ã¹ããŒã¿ãã successResponse ã failureResponse ã«ã»ããããŠããŸãã ãã®ããŒã¿ãå
ã«ã FAILURE_FETCH_DATA ã¢ã¯ã·ã§ã³ãš FAILURE_FETCH_DATA ã¢ã¯ã·ã§ã³ãDispatchããŠããŸãã storeã®æ¥ç¶ ããã§ã¯ãæåŸã«reduxãšredux-sagaãã€ãªãããå®è£
ãããããšæããŸãã以äžã®ããã«ãã¹ãŠã®SagaãéçŽããrootSagaãã¡ã€ã«ãäœæããrootSaga颿°ã§ã¿ã¹ã¯ããŸãšããŸãã // çç¥ export default function* sampleSaga () { yield fork ( handleRequestFetchData ) } import { fork } from 'redux-saga/effects' import sampleSaga from './sampleSaga' export default function* rootSaga () { yield fork ( sampleSaga ) } rootSagaãå®çŸ©ãããã®ã§ããã®rootSagaãstoreã«æ¥ç¶ããŠãã¿ã¹ã¯ãå®è¡ã§ããããã«ããŸãã import { createStore , applyMiddleware } from 'redux' import createSagaMiddleware from 'redux-saga' // sagaMiddlewareãäœæ const sagaMiddleware = createSagaMiddleware () // ã¹ãã¢ãäœæ const store = createStore ( reducer , applyMiddleware ( sagaMiddleware , someMiddleware )) 以äžã§ãSagaã®äžé£ã®å®è£
ã¯å®æããŸããããšãŠãåŠçã®æµãã远ãã®ãç°¡åã ãšæããŸãã redux-sagaã䜿çšãããšãredux-thunkã®ã³ãŒã«ããã¯å°çããReactã©ã€ããµã€ã¯ã«å
ã§ã®éåæåŠçã®ç
©éæ§ããéæŸãããã®ã§ãæ¯é䜿ã£ãŠã¿ãŠãã ããã æåŸã« ä»åã®èšäºã§ã¯redux-sagaã«ã€ããŠæžããŸãããredux-sagaã¯ç¹åŸŽçãªæžãæ¹ãªã®ã§ããšã£ã€ãã«ããéšåã¯ãããŸãããä»åã®èšäºã§å°ãã§ãredux-sagaã«è©³ãããªããã幞ãã§ãã ã¹ã¿ã¡ã³ã§ã¯äžç·ã«ãããã¯ãéçºãé²ããŠããã仲éãåéããŠããŸãïŒèå³ãæã£ãŠãããæ¹ã¯ããã²äžèšã®ãšã³ãžãã¢æ¡çšãµã€ããã芧ãã ããã ã¹ã¿ã¡ã³ ãšã³ãžãã¢æ¡çšãµã€ã ããã³ããšã³ããšã³ãžãã¢åéããŒãž
ã¯ããã« 20åã®æ°åã§å
¥ç€Ÿããã¢ãã€ã«ã¢ããªããŒã ã®ã«ãŒãã§ãïŒ ã¹ã¿ã¡ã³ã§ã¯æšä»ã® æ°åã³ãããŠã€ã«ã¹ ã®ææç¶æ³ãéã¿ãŠ4æããååãªã¢ãŒãã§æ¥åãè¡ãªã£ãŠããŸãïŒ ãªã¢ãŒãã§ã®æ¥åã§ã¯ZOOMã䜿çšãã ã€ã³ã¿ãŒã³ çãå«ããå
š17åãåãéšå±ã§ã«ã¡ã©ããªã³ããã€ã¯ããã¥ãŒãã®ç¶æ
ã§æ¥åãè¡ã£ãŠããŸãïŒ æ®æ®µã®zoomã§ã®æ§å ä»åã¯ã¹ã¿ã¡ã³ãããã¯ãéšã§ã®ãªã¢ãŒãã¯ãŒã¯ã®åãçµã¿ãã玹ä»ããããšæããŸãïŒ ãããã¯ãéšã®åãçµã¿ ãªã¢ãŒãã§ã®å€åãè¡ã£ãåœåããªãã£ã¹ã§ã®æ¥åãšæ¯èŒã㊠éè«ããªããªã ã¡ãã£ãšãã質åããã¥ãã ãªã©ã®ãã¡ãªããããããã¯ãéšå
šäœãšããŠæããŠããŸããïŒ ã©ããã³ãã¥ãã±ãŒã·ã§ã³ã«é¢ããããšã§ããããã®æ¥å
¥ç€Ÿããã°ããã®åã®ãããªæ°äººãšã³ãžãã¢ã«ã¯ã³ãã¥ãã±ãŒã·ã§ã³äžè¶³ãçç£é«ã«é¢ãã£ãŠããŸãïŒ ããã§ã¹ã¿ã¡ã³ã®ãããã¯ãéšã§ã¯ã³ãã¥ãã±ãŒã·ã§ã³äžè¶³ã«ãããçç£é«ã®æžå°ã鲿¢ããã¹ã以äžã®ãããªæœçãè¡ã£ãŠããŸãïŒ ããã€ã¿ã€ã ãªãã£ã¹ã«ãããšãã¯äœæ°ãªãäŒè©±ãæ¯æãã«ãªã£ãŠããŸããããå®¶ã§äžäººã§ãããšéè«ã¯çãŸããŸããïŒããã§ãæŒã®15æã«ã¯å
šå¡ãã¥ãŒããå€ããŠ15åã§çšåºŠã®ããã€ã¿ã€ã ãèšããŠããŸãïŒ ããã€ã¿ã€ã ã§ã¯ã¿ããªã§ãèåãé£ã¹ãªããéè«ãããŠããŸãïŒ å
šå¡ã§ãäºãã®ããŒãã£ã«èæ¯ãããããªããéããããšãããã°ãã©ããã²ãã³æ¹åŒã§è©±ãããã¡ã³ããŒãæåããŠã°ã«ãŒãããšã« Remo ã®éšå±ã«åãããŠéè«ãè¡ãããšããããŸã(ä»ã®ãšãã1çµãæç«ããŠããªãã®ã§ããâŠ) ããã€ã¿ã€ã ã«Remoã䜿çšããæã®æ§å 宿ã«ããã€ã¿ã€ã ãããããšã§ãèªå®
ã§ã®å€åã«ãã¡ãªããªãçãŸããŠãããšæããŸãïŒ ããŒã ããšã«é³å£°ãåžžæé話 ããã¯ãããã¯ãéšå
šäœã§è¡ã£ãŠããããšã§ã¯ãããŸããããã¢ããªã±ãŒã·ã§ã³ããŒã ã§ã¯ Discord ãããã³ããšã³ãããŒã ã§ã¯ whereby ã䜿çšããŠåžžæäŒè©±ãã§ããããã«ããŠããŸãïŒåžžæé³å£°ãã€ãªãã§ããããšã§ãããŒã å
ã§ã®è³ªåãå
±æäºé
ãè¿
éã«è¡ãããšãã§ããŠããŸãïŒ ãããã¯ãéšå
ã«ã¯åãå«ãå
¥ç€ŸããŠéããªãã¡ã³ããŒãå€ãããããã€ã§ãæ°å
Œããªã質åããããç°å¢ãé³å£°é話ã®åžžææ¥ç¶ã§å®çŸããŠããŸãïŒ ãªã³ã©ã€ã³ã§ã®ç€Ÿå
ååŒ·äŒ ãããã¯ãéšå
ã®å匷äŒããªã³ã©ã€ã³ã§éå¬ããŠããŸãïŒ ãŸã 仿ã®å匷äŒããå§ããã°ããã§ãåå¿ãåããã¥ãããªã©ãæ¹åç¹ããããŸãããªã³ã©ã€ã³ã§ãéåžžéã宿œããããšãã§ããŸããïŒ ç€Ÿå
ãªã³ã©ã€ã³å匷äŒã®æ§å èªçæ¥äŒããªã³ã©ã€ã³ã§ ã¹ã¿ã¡ã³ã§ã¯ã¡ã³ããŒã®èªçæ¥ã®ãæŒã«èªçæ¥äŒãéããŠããã®ã§ãããä»åã¯ãªã³ã©ã€ã³ã§èªçæ¥äŒãéå¬ããŸããïŒ ä»åã®èªçæ¥äŒã§ã¯Remoã䜿çšããŠãèªçæ¥ä»¥å€ã®ã¡ã³ããŒãïŒäººãã€ã®ã°ã«ãŒãã«ãªã£ãŠé çªã«èªçæ¥ã®ã¡ã³ããŒãç¥ããŸããïŒ Remoã®ãã¯ã€ãããŒãæ©èœã䜿çšããããããã®ã°ã«ãŒãããšã«ã€ã©ã¹ããåçã»ã¡ãã»ãŒãžã§ãã³ã¬ãŒã·ã§ã³ãè¡ããŸããïŒ ã°ã«ãŒãããšã«ããŒãã«ãåæ§ãåºãŠããŠé¢çœãã£ãã§ãïŒ èªçæ¥äŒã®Remoã§ã®æ§å èªçæ¥äŒã®Remoã§ã®æ§åâ¡ æåŸã« ãŸãäŒç€Ÿã§ã®åãçµã¿ãšããŠã¹ã¿ã¡ã³ã«ã¯ ãã¬ãµ ã«ã ãšããŠãªã³ã©ã€ã³é£²ã¿äŒãæšå¥šããå¶åºŠããããŸãïŒãªã¢ãŒãã¯ãŒã¯ã§ãã³ãã¥ãã±ãŒã·ã§ã³ã掻æ§åããããã«å®æœãããŠããŸãïŒ ãªã³ã©ã€ã³ã®é£²ã¿äŒãªã®ã§æ±äº¬ã»å€§éªæ¯ç€Ÿã®ã¡ã³ããŒãšã飲ã¿äŒã楜ããããšãã§ããæ®æ®µä»¥äžã«ä»æ¯ç€Ÿã®ã¡ã³ããŒãšã®è·é¢ãè¿ãæããŠããŸãïŒ ãã®ããã«ã¹ã¿ã¡ã³ã§ã¯ãªã¢ãŒãã§ã®å€åãäœåãªããããäžã§ããªã¢ãŒãã ããã§ããããšãèŠã€ããæœçãšããŠè¡ã£ãŠããŸãïŒ ãã®ç¶æ³ããã€ãŸã§ç¶ããã¯åãããŸãããããªã¢ãŒãã§ã®å€åãæ¥œãã¿ãªããã¹ã¿ã¡ã³ããã宿œããŠãããããšæããŸãïŒ
ããã«ã¡ã¯ãã¹ã¿ã¡ã³ã§ãã£ã¬ã¯ã¿ãŒãããŠãã @nicka0001 ã§ãïŒ æ®æ®µã¯äž»ã« TUNAG ã®æ¹åæœçã®äŒç»ãèšèšããããžã§ã¯ããããžã¡ã³ããè¡ãªã£ãŠããŸãã ä»åã¯ããµãŒãã¹ãéçšããŠããã«ããã£ãŠã®æŠç¥çãªæ©èœäŒç»ã«ã€ããŠæžããŠãããŸãã æŠç¥ã®å¿
èŠæ§ 仿§ãUIãæ±ºå®ããããšã¯æ£è§£ããªãããšãŠãé£è§£ã§æããã®ã§ãã ã·ã³ãã«ã«äŸ¿å©ã»äžäŸ¿/䜿ããããã»äœ¿ãã«ããã§ã¯ãªããªã決ããããªãè²ããªèŠçŽ ãåãã§ããŸãã ã ãããããåžžã«èãã«èããéããŠæåãå°œãããããã®ã§ããããŸãã ãã®ããã«ã¯ã æŠç¥ãç«ãŠã ã¹ããŒã¯ãã«ã㌠ããããžã§ã¯ãã¡ã³ããŒã«èªãã仿§ãUIãæŠç¥ãããŒã¹ã«è°è«ãããªãªãŒã¹ããããæŠç¥ãããšã«è©äŸ¡ãè¡ããæåãçªãè©°ããŠããã ãããªå¥å
šã§æŽ»çºãª PDCAãµã€ã¯ã« ãåãããã«ãæŠç¥ã¯å¿
èŠã§ãããšèããæŠç¥ã®çå®ãè¡ãããã«ãUXãã¶ã€ã³ã®ãããŒãäžéšæŽ»çšããªããäŒç»ãè¡ãªã£ãŠããŸãã ãªãŒã³éçºãè¡ãçµç¹ã«ãããŠããMVPâ»1 ãè©äŸ¡ããäžã§éèŠãªææšã«ãªãããšæããŸãã æŠç¥ãç«ãŠãããã®æ
å ±åé äºæ¥æŠç¥ãèãããããã¯ãã®æ©èœæ¡ãç«ãŠããããã客æ§ã®äžçªè¿ãã§æ¥åãè¡ãªã£ãŠãããŠãããã«ã¹ã¿ããŒãµã¯ã»ã¹â»2ã« ãã¢ãª ã³ã°ããŸãã çŸç¶ã®ããŒãºãšãããæºããçºã«è¡ãªã£ãŠããäœæ¥(ã¿ã¹ã¯) ã¿ã¹ã¯ã®è©³çް( 5W1H ) ãã®ã¿ã¹ã¯ãå¿
èŠãªèæ¯ çã
ã§ããéãå€å²ã«è©³çްã«ã ãã¢ãª ã³ã°ããŸãã 䞻芳ãçµéšåã§ãªãããŠãŒã¶ãŒã«ã€ããŠã®äºå®ãæããã«ããããšã§ããã广çãªã¢ ã€ã㢠ãåºãããšãã§ããŸãã äœããããã®æ
å ±ã¯ãæææ±ºå®ãèªåã®ã¢ ã€ã㢠ãèªãããã§ãšãŠãé Œãã«ãªããŸãã æ
å ±ã®åæãšã¢ ã€ã㢠ã®çºæ£ ããŠããŠãŒã¶ãŒã«ã€ããŠã®äºå®æ
å ±ãéãŸã£ããšããã§ãèŠä»¶ã«èœãšã蟌ãã§ãããŸãã ãã¢ãª ã³ã°ãè¡ã£ãã¿ã¹ã¯ã¯æ³å®ãããŒã«èœãšã蟌㿠æäœéãå®çšæäœéæ©èœ(MVP)ãç»é¢é·ç§»ã®åºæºãšããŠå©çšãã ããŒãºãç°å¢ãèæ¯ã¯KAæ³â»3ã§äŸ¡å€æœåºãã æ©èœã®äžã®äžã€äžã€ã®UIæ¡ã仿§æ¡ãšãã®æ
å ±åªå
床ãå²ãåºãçºã«å©çšããŸãã ããã§ãç»é¢é·ç§»ãšã³ã³ã»ããã¬ãã«ã®MVPæ¡(UIã仿§æ¡)ãããããã® ãã¢ãª ã³ã°å
容ã«çµã³ã€ãã圢ã§çå®ã§ããŠããããšæããŸãã ããã«ãããç»é¢é·ç§»ãããUIã仿§ã«è³ãäžã€äžã€ã«æŠç¥ãçµã³ã€ãã圢ã«ãªãããããããç®çãéæã§ããŠããããšãã£ãã詳现ãªè©äŸ¡ãè¡ãããšãå¯èœã«ãªããŸãã ãŠãŒã¶ãŒã®æåŸ
ãè¶
ããããã®ä»®èª¬èšå® MVPæ¡ãæããã«ãªã£ããšããããã å床ãŠãŒã¶ãŒã®ããŒãºãèæ¯ãç°å¢ãšå床åãåãã 仮説ãç«ãŠãŠããšã§ããã¹ã ãŒãºãã€ãæ©èœãäžãã䟡å€ããããå¢å¹
ããã㢠ã€ã㢠ãåºããŠããããšã§ããŠãŒã¶ãŒã®æåŸ
ãè¶
ãããããã¯ãã®æäŸãå¯èœã«ãªããšèããŠããŸãã ãŠãŒã¶ãŒã®åããç°å¢ãªã©ã®äºå®æ
å ±ããã©ãã ããè¿·ããè¡åãå
åãã仮説ãç«ãŠãããããšããèœåã¯ããã®ã¥ãããè¡ã人éã«æ±ããããŠããã®ã§ã¯ãªãããšæããŸãã ãŸãšã ãããã¯ãã®çްéšã®äžã€äžã€ã«ã€ããŠãç±éãæã£ãŠèªããæŠç¥ãããŒã äžäžžã§éæããŠãããŸãããã ã¹ã¿ã¡ã³ã§ã¯ããŠãŒã¶ãŒã«å¿ããæ°ã«å
¥ã£ãŠãããããããã¯ããç®æããæé©ãªæŠç¥ãå
±ã«èããéæããŠãããããã¯ãéçºã¡ã³ããŒãåéããŠããŸãïŒèå³ãæã£ãŠãããæ¹ã¯äžèšã®æ¡çšãµã€ããã芧ãã ããïŒ ã¹ã¿ã¡ã³ ãšã³ãžãã¢æ¡çšãµã€ã ãµãŒããŒãµã€ããšã³ãžãã¢åéããŒãž ããã³ããšã³ããšã³ãžãã¢åéããŒãž ã€ã³ãã©ãšã³ãžãã¢åéããŒãž ã¢ãã€ã«ã¢ããªãšã³ãžãã¢åéããŒãž åèèšäº â»1 MVP Wikipedia å®çšæå°éã®è£œå â»2 ã«ã¹ã¿ããŒãµã¯ã»ã¹ ãã¯ã·ã«ãã¬ãžã³ ã«ã¹ã¿ããŒãµã¯ã»ã¹ãšã¯ | é¡§å®¢ã®æåäœéšã¥ããã§æ³šç®ãCSãšã®éãã»å®çŸ©ã»äºäŸè§£èª¬ â»3 KAæ³ UX.txt KAæ³ èŠåºãç»å : Photo by JESHOOTS.COM on Unsplash
ããã«ã¡ã¯ãããã³ããšã³ããšã³ãžãã¢ã® æž¡é ã§ãã ã¹ã¿ã¡ã³ã§éçºããŠãããµãŒãã¹ã®æ°æ©èœã¯ãReact v16.8ã§è¿œå ããã hooks çã䜿ã£ãŠéçºããŠããŸãã ãã®äžã§ãæ¬èšäºã§ã¯useEffectã«ã€ããŠè§ŠããŠããããšæããŸãã ç®æ¬¡ hooksãšã¯ useEffectã®ç¬¬äºåŒæ°ã®äŸåãªã¹ããæ£ç¢ºã«æž¡ã åèãµã€ã hooksãšã¯ ãã㯠(hook) 㯠React 16.8 ã§è¿œå ãããæ°æ©èœã§ããstate ãªã©ã® React ã®æ©èœããã¯ã©ã¹ãæžããã«äœ¿ããããã«ãªããŸãã React å
¬åŒããã¥ã¡ã³ã useEffect useEffectã¯ç¬¬1åŒæ°ã«renderæ¯ã«å®è¡ãã颿°ãã第2åŒæ°ã¯renderæã«useEffectå
ã®é¢æ°ãå®è¡ãããå€å®ããå€ãé
åã§æž¡ããŸãã 以äžã³ãŒãã®ããã«ç¬¬2åŒæ°ã«äœãæž¡ããªããšrenderæ¯ã«å®è¡ãããŸãã const Counter = () => { const [ count, setCount ] = useState(0); const [ count2, setCount2 ] = useState(1); useEffect(() => { document .title = `You clicked $ { count } times`; } ); return ( <div> <span>You clicked { count } times</span> <button onClick= { () => setCount(count + 1) } >Click me</button> <span>You clicked { count2 } times</span> <button onClick= { () => setCount2(count2 + 1) } >count2 Click me</button> </div> ); } countãå€ãã£ãæéå®ã§å®è¡ãããå Žåã¯ç¬¬2åŒæ°ã«countãæž¡ãã ããã§count以å€ã®stateãæŽæ°ãããŠããuseEffectãå®è¡ãããããšã¯ãªããªããŸãã const Counter = () => { const [ count, setCount ] = useState(0); const [ count2, setCount2 ] = useState(1); useEffect(() => { document .title = `You clicked $ { count } times`; } , [ count ] ); // countãå€ãã£ããå®è¡ããã return ( <div> <span>You clicked { count } times</span> <button onClick= { () => setCount(count + 1) } >Click me</button> <span>You clicked { count2 } times</span> <button onClick= { () => setCount2(count2 + 1) } >count2 Click me</button> </div> ); } // åæ render const Counter = () => { // ... // åærender useEffect(() => { document .title = `You clicked $ { 0 } times`; } , [ count ] ); // ... } // `<button onClick={() => setCount(count + 1)}>Click me</button>`ãã¯ãªãã¯ããããšãCounter颿°ãåŒã³åºããã const Counter = () => { // ... // årenderã§countãå€ãã£ãŠããã®ã§useEffectå
ã®é¢æ°ãå®è¡ããã useEffect(() => { document .title = `You clicked $ { 1 } times`; } , [ count ] ); // ... } // `<button onClick={() => setCount2(count2 + 1)}>count2 Click me</button>`ãã¯ãªãã¯ããããšãå床Counter颿°ãåŒã³åºããã const Counter = () => { // ... // count2ãå€ãã£ãããcountã¯å€ãã£ãŠããªãã®ã§useEffectå
ã®é¢æ°ã¯å®è¡ãããªã useEffect(() => { document .title = `You clicked $ { 1 } times`; } , [ count ] ); // .. } useEffectã®ç¬¬2åŒæ°ã®äŸåãªã¹ããæ£ç¢ºã«æž¡ã 第2åŒæ°ã«ç©ºé
åãæž¡ããŠãããã¿ãŒã³ã¯èŠæ³šæã§ãã å
¬åŒããã¥ã¡ã³ãåŒçš 颿°ãäŸåã®ãªã¹ãããå®å
šã«çç¥ã§ããã®ã¯ããã®é¢æ°ïŒãããã¯ãã®é¢æ°ããåŒã°ãã颿°ïŒã propsãstateãªãããããããæŽŸçããå€ã®ããããå«ãã§ããªãå Žåã®ã¿ã§ãã äŸãã°ã以äžã³ãŒãã«ã¯ãã°ããããŸãã const dataPage = ( { dataId } ) => { const [ data, setData ] = useState( null ); const getUrl = () => { return `http: //api/data/${dataId}` } const fetchData = async () => { const url = getUrl() const response = await fetch(url); const json = await response.json(); setData(json); } const testFunc = () => { // åŠç } const testFunc2 = () => { // åŠç2 } const testFunc3 = () => { // åŠç3 } useEffect(() => { fetchData(); } , [] ); // ... } äžã®ã³ãŒãã®å Žå dataId ã«å€æŽããã£ãå Žåã§ã fetchData ã¯å®è¡ããããå€ã data ã衚瀺ããç¶ããŠããŸãã ãã®ããã«ãpropsãstateãåç
§ããŠãã颿°ãuseEffectã®å€ã§äœæããããšãpropsãšstateãå®éã«æ±ãããŠããããææ¡ããã«ã¯ãã¢ããªã±ãŒã·ã§ã³ãè¥å€§åããã«é£ãé£ãããªãããã°ãçã¿ãããããŠããŸãã const dataPage = ( { dataId } ) => { const [ data, setData ] = useState( null ); const getUrl = () => { return `http: //api/data/${dataId}` // propsã§æž¡ã£ãŠããdataIdã䜿çšããŠãã } const fetchData = async () => { const url = getUrl() const response = await fetch(url); const json = await response.json(); setData(json); } const testFunc = () => { // åŠç } const testFunc2 = () => { // åŠç2 } const testFunc3 = () => { // åŠç3 } useEffect(() => { fetchData(); // å®è¡ } , [] ); // 空é
åãæž¡ããŠãããããdataIdã倿ŽãããŠãå€ãdataã®ãŸãŸè¡šç€ºãããŠããŸã // ... } ãªã®ã§ãäŸåãªã¹ãã«æ£ç¢ºã«æž¡ãæ¹æ³ãšããŠã®åºæ¬useEffectå
ã§å®è¡ãã颿°ã¯useEffectå
ã§å®£èšããã®ãšãuseEffectå
ã§äœ¿ãããŠããå€ã¯ãã¹ãŠèšè¿°ããããšã§ãã ãªã®ã§å®éã«å
çšã®ã³ãŒããä¿®æ£ããŸãã const dataPage = ( { dataId } ) => { const [ data, setData ] = useState( null ); const testFunc = () => { // åŠç } const testFunc2 = () => { // åŠç2 } const testFunc3 = () => { // åŠç3 } useEffect(() => { const getUrl = () => { return `http: //api/data/${dataId}` // propsã§æž¡ã£ãŠããdataIdã䜿çšããŠãã } const fetchData = async () => { const url = getUrl() const response = await fetch(url); const json = await response.json(); setData(json); } fetchData(); // å®è¡ } , [ dataId ] ); // ... } ããã«ããuseEffectå
ã§ãã䜿ãããŠããªãã®ã§ã仮㫠dataId ã䜿ããªããªã£ããšããŠãæ°ã¥ãããããªããŸãã useEffectå
ã«é¢æ°ãç§»åã§ããªãå Žå ãã®å Žåã®éžæè¢ãšããŠã ã³ã³ããŒãã³ã ã®å€éšã«ãã®é¢æ°ãç§»åã§ããªãããèããå¿
èŠããããŸãã äŸãã°ã testFunc ãå€éšã«ç§»åããããšã«ããpropsãstateãåç
§ããŠããªãããšãä¿èšŒããããããäŸåãªã¹ãã«å«ããªããŠãè¯ããªãã®ã§ã空é
åãæž¡ããŠãå®å
šã§ãã const testFunc = () => { console.log( 'test' ) } const dataPage = ( { dataId } ) => { const [ data, setData ] = useState( null ); // ... useEffect(() => { testFunc() } , [] ); // ... } propsãstateãåç
§ããŠããŠå€éšã«ç§»åã§ããªãå Žåã®è§£æ±ºçãšã㊠useCallback ã䜿çšããæ¹æ³ããããŸãã äŸãã°ã芪㮠ã³ã³ããŒãã³ã ããèªèº«ã®stateã倿Žãããã¢ã¯ã·ã§ã³ãå ã³ã³ããŒãã³ã ã«æž¡ããŠãå ã³ã³ããŒãã³ã ã§çºç«ãããïŒãªã©ãªã©ã useCallbackã䜿ã useCallbackãšã¯... 颿°èªäœã®äŸåãå€ãããªãéã颿°ãå€åããªãããšãä¿èšŒã§ãã(ã¡ã¢åãããã³ãŒã«ããã¯ãè¿ã) 以äžã®ããã«Parent ã³ã³ããŒãã³ã ã§èªèº«ã®propsãåç
§ããŠãã颿°ã ã³ã³ããŒãã³ã ã«æž¡ãããŠå®è¡ããå Žå const Parent = ( { id } ) => { const [ name, setName ] = useState( '' ); const hogeFunc = () => { console.log(id) } retrun ( //... <Child hogeFunc= { hogeFunc } /> //... ) } const Child = ( { hogeFunc } ) => { useEffect(() => { hogeFunc() } , [ hogeFunc ] ) //... } renderå
ã§ã¢ããŒé¢æ°ãå©çšãããšåžžã«æ°èŠã®é¢æ°ãªããžã§ã¯ããäœæããŠããŸããŸãã ãã®ããããã®é¢æ°ãå ã³ã³ããŒãã³ã ã«æž¡ããå Žårenderæ¯ã«ååæž¡ã£ãŠãã颿°ãšéã颿°ãšããŠæ±ãããŠããŸããŸãã ãªã®ã§äžã®ãããªã³ãŒãã ãšstateã® name ãæŽæ°ãããŠãParent ã³ã³ããŒãã³ã ãåæç»ããããã³ã«ãChildã«æž¡ã hogeFunc ã¯æ°èŠã®é¢æ°ãšããŠæž¡ããŠããŸãã®ã§ãuseEffectå
ã¯æ¯åå®è¡ãããŠããŸãã ãã®ãããªãšãã«useCallbackã®åºçªã§ãã useCallbackã¯ãæå³çã«åã颿°ããè¿ããã©ãããå€å¥ããŠãåãå€ãè¿ã颿°ãè¿ãã¹ããªãæ°èŠã®ã¢ããŒåŒãæšãŠãŠãåã«æž¡ããåãçµæãè¿ã颿°ã§ãã const memoizedCallback = useCallback(() => { doSomething(a, b); } , [ a, b ] ) // useCallbackã®ç¬¬2åŒæ°ãuseEffectã®ç¬¬2åŒæ°ãšåãèãæ¹; å®éã«useCallbackã䜿ã£ãŠå
çšã®ã³ãŒããä¿®æ£ããŸãã const Parent = ( { id } ) => { const [ name, setName ] = useState( '' ); // hogeFuncã¯idã«å€æŽãåã£ãå Žåã®ã¿æ°èŠé¢æ°ãšããŠäœãããã const hogeFunc = useCallback( () => { console.log(id) } , [ id ] ) retrun ( //... <Child hogeFunc= { hogeFunc } /> //... ) } const Child = ( { hogeFunc } ) => { useEffect(() => { hogeFunc() } , [ hogeFunc ] ) // Parentã³ã³ããŒãã³ãã®idãå€ãã£ãå Žåã®ã¿hogeFuncãæ°èŠé¢æ°ãšããŠäœæãããã®ã§ãåæç»ã®åºŠã«å®è¡ãããããšã¯ãªããªã //... } useCallbackã䜿ãåžžã«æ°èŠã®é¢æ°ãªããžã§ã¯ããäœæããªããªãããšã«ããå©ç¹ãä»ã«ããããŸãã Child ã³ã³ããŒãã³ã ãReact.memo()ãããŠããå Žåã§ãã React.memo()ã¯propsã®å€æŽããã§ãã¯ããŠãåãçµæã®å Žåã¯åæç»ãã¹ãããããŠã倿Žããã£ãå Žåã®ã¿åæç»ãããã®ã§ãç¡é§ãªã¬ã³ããŒãæžããããšãã§ããŸãã const Parent = ( { id } ) => { const [ name, setName ] = useState( '' ); const hogeFunc = useCallback(() => { console.log(id) } , [ id ] ) return ( //... <Child hogeFunc= { hogeFunc } /> //... ) } const Child = React.memo(( { hogeFunc } ) => { // hogeFuncã«å€æŽããªãéãChildã³ã³ããŒãã³ãã¯åæç»ãããªãã useEffect(() => { hogeFunc() } , [ hogeFunc ] ) //... } ) React.memo()ã®è©³çްã¯ä»åçãã®ã§ å
¬åŒããã¥ã¡ã³ã ã«ãŠãé¡ãããŸãã åèãµã€ã useEffectå®å
šã¬ã€ã ãããèªããšuseEffectãã¹ã¿ãŒã«ãªãããšæããŸãã å°ãé·ãã§ãããå®å
šã«çè§£ãããæ¹ã«ã¯ããããã§ãã ãŸãšã æè¿ã¯hooksã®ãæè»œãããFunctionalComponentã§æžãããšãå€ãã§ããããã¡ããšçè§£ããäžã§æžããŠãããããšèšäºããŸãšããŠããæã«æ¹ããŠæããŸããã æåŸã«ãæ ªåŒäŒç€Ÿã¹ã¿ã¡ã³ã§ã¯äžç·ã«åããšã³ãžãã¢ãåéããŠããŸãããèå³ã®ããæ¹ã¯ãã² ãšã³ãžãã¢æ¡çšãµã€ã ãã芧ãã ããã
ããã«ã¡ã¯ãã¹ã¿ã¡ã³ã§ããã¯ãšã³ãã®ãšã³ãžãã¢ãããŠããæ²³äºã§ãã ä»å¹Žã®3æã« AWS èªå®ãœãªã¥ãŒã·ã§ã³ã¢ãŒããã¯ã-ã¢ãœã·ãšã€ãïŒSAA-C01ïŒãåéšããåæ Œããã®ã§å匷ããå
容ãåããŠè¯ãã£ãããšã«ã€ããŠæ¯ãè¿ããããŠã¿ãŸãã â»çŸåšã¯ SAA-C02 ãææ°ã§ãæ¬èšäºã§ç޹ä»ããåç»ã暡詊ãã¢ããããŒããããŠå¯Ÿå¿æžã¿ã§ã åéšã®çç± AWS ã®åºæ¬çãªãµãŒãã¹ãäœç³»çã«çè§£ããããã§ãã ã¹ã¿ã¡ã³ã«æ°åå
¥ç€ŸããŠä»¥æ¥ Rails ã React ãçšããéçºãçµéšããŸãããã AWS ã¯ããŸãè§Šãæ©äŒããããŸããã§ããã ãã®åŸããã¯ãšã³ãã°ã«ãŒãã®æå±ã«ãªããTUNAG ã®æ©èœéçºã«å ã㊠ã¯ã©ãŠã ã€ã³ãã©ã®éçšãããã©ãŒãã³ã¹ç£èŠæ¥åã«ãµããããšã«ãªããŸããã æ®æ®µã®æ¥åã§ AWS é¢é£ã®äŒè©±ãããªãå¢ããŸããããå
容ãããããªãããšãå€ãã£ãã§ãã ãã¡ãããã®éœåºŠãµãŒãã¹ã«ã€ããŠèããã調ã¹ãããããã®ã®ãæççãªç¥èãéããã®ã§ã¯ãªããªãå
æ¬çãªçè§£ãé£ãããšæããŠããŸããã ããã§ãŸãã¯è©ŠéšãåããŠåºãåºç€ç¥èãã€ããããšèããŸããã åéšæã®çµéš åéšã決ãããšãã®ãšã³ãžãã¢ãšããŠã®çµéšã¯ ãŠã§ããšã³ãžãã¢æŽ2幎匱 Rails ãReact ãªã©ã¢ããªã±ãŒã·ã§ã³ã®éçºçµéšãã¡ã€ã³ AWS 㯠ETL åºç€ã®æ¹åã§å°ãè§Šã£ãããšãããïŒCloudFormationãLambdaãGlueã Kinesis ãªã©ïŒ AWS èªå®ä»¥å€ãå«ãè³æ Œè©Šéšã®çµéšãªã ãšãã£ãæãã§ããèªå®è©Šéšã«ããåºãç¯å²ã®çµéšã¯ã»ãŒãããŸããã§ããã å匷ããããš äœã䜿ã£ãŠã©ããªããšãå匷ããã®ãã玹ä»ããŸãã 䜿ã£ãã®ã¯å€§ãã3ã€ã§ã詊éšå¯Ÿçæ¬ãåç»ãæš¡æ¬è©Šéšã§ãã 詊éšå¯Ÿçæ¬ è©Šéšå¯Ÿçæ¬ã¯ã SBã¯ãªãšã€ãã£ã ã® AWS èªå®è³æ Œè©Šéšããã¹ãã䜿ããŸããã SAA åããšãåºç€ã¬ãã«ã® ã¯ã©ãŠã ã ã©ã¯ ãã£ã·ã§ããŒïŒCPïŒã®2åã§ãã www.sbcr.jp www.sbcr.jp ãŸã㯠CP ã®å¯Ÿçæ¬ãäžéãèªã¿ãŸããããæã£ãããç°¡åã ã£ãã®ã§ããã« SAA ã®æ¹ã«åãæ¿ããŸããã CP ã®è©Šéšãåããäºå®ã¯ãªããŠããåºç€ã®åºç€ãæããäžã§ SAA ã«å¿
èŠãªç¥èãç©ã¿äžããããã®ã§èªãã§æã¯ãªããšæããŸãã ã©ã¡ãã®æ¬ã«ã€ããŠãå
容ã现ããèŠããããšãããã©ããªå
容ã«ã€ããŠçè§£ããã°è¯ãã®ããææ¡ããããã«ãã£ãšèªã¿ãŸããã åç»ææ æ¬ã ãã ãšã€ã¡ãŒãžãæ¹§ãã¥ããããšã¯ãããã¹ãŠã®ãµãŒãã¹ãè§Šãã»ã©æéã¯ãããããªããšããçç±ããåç»ææã䜿ãããšã«ããŸããã ä»åã¯ããŸããŸãã€ãŒãã§èŠããã Whizlabs ãéžã³ãŸããã å®ãã£ãã®ãšæéããªãã£ãã®ã§ç¹ã«èª¿æ»ãæ¯èŒãªã©ã¯ããŠããªãã§ãã www.whizlabs.com ãã¡ããæåã¯å
šå®¹ãææ¡ããç®çã§1.5ã2åéã§æµããŸããã åãµãŒãã¹ã®äœ¿ãæ¹ãåç»ã§ãããã®ã§å®éã«è§ŠããªããŠããªããšãªãæè§Šãã€ããããšãã§ããŸãã ãããã«ããéšåã«é¢ããŠã¯åç»ã«æ²¿ã£ãŠæãåãããªããçè§£ãæ·±ããŸããã æš¡æ¬è©Šéš å
¬åŒã®æš¡æ¬è©Šéšã¯æ¬çªããé£æåºŠãäœããšèããŠããã®ã§äœ¿ããããããããããããŠãã Udemy ã®ä»¥äžã®ãã®ã䜿ããŸããã ãSAA-02çãAWS èªå®ãœãªã¥ãŒã·ã§ã³ã¢ãŒããã¯ã ã¢ãœã·ãšã€ãæš¡æ¬è©Šéšåé¡éïŒ6åå390åïŒ åãåããã®ã¯ C01 ã®ãšãã ã£ãã®ã§5ååïŒ6åç®ã¯ C02 ã®å¯ŸçãšããŠè¿œå ãããïŒã§ãäžé£æåºŠ1ã€ãé«é£æåºŠ4ã€ã®æ§æã§ããã é çªãšããŠã¯é«é£æåºŠã®4ã€ãå
ã«ããªããåéšã®åæ¥ã«äžé£æåºŠã®è©Šéšåé¡ãè§£ããŸããã 詊éšåŸã«é åå¥ã§åŸç¹çãåºããŠãããããšãšãåèšåã®åéžæè¢ã«ã€ããŠè§£èª¬ãã€ããŠããã®ãéåžžã«å匷ã«ãªããŸãã ãã®è§£èª¬ãèªã¿ãªãããã©ãããã°æ£è§£ã®éžæè¢ã«ãã©ãçããã®ããèããŸããã ç¥ããªãç¥èãããã°æ¬ãåç»ã«ãã©ã£ãŠåŸ©ç¿ããŸãã æ¬ãåç»ã§æµãçšåºŠã«çããçç±ã¯ããã§ãå®éã®çŸå Žã«è¿ãã·ãŒã³ã®äžã§ã©ãããããŒãºããã£ãŠãã®æ©èœãããã®ãïŒãç¥ã£ãäžã§ç¥èãã€ããã»ããå¹ççãã€å®è·µçã§ãã ç¥èãšããŠã¯ç¥ã£ãŠããã®ã«ééãããšãããšã㯠Well-Architected ãã¬ãŒã ã¯ãŒã¯ ã«æ²¿ã£ãŠèããããŠãããã©ããã確èªããŸããã ãããç¹°ãè¿ãããšã§èªç¶ãš Well-Architected ãã¬ãŒã ã¯ãŒã¯ ã«ã€ããŠãçè§£ãæ·±ãŸããŸããã åéšã®å¹æ äžçªå®æããã®ã¯ããã¯ãæ®æ®µã®æ¥åã§ã®äŒè©±ãè³æã§ AWS é¢é£ã®è©±é¡ãåºãŠãããšãã«äœã«ã€ããŠã®ãµãŒãã¹ãªã®ãããªãããã«ã€ããŠè©±é¡ã«ããã®ããçè§£ã§ããããã«ãªã£ãããšã§ãã ãŸããããããªãããšããã£ãŠããã£ããã¢ãããŸã§ã®ã¹ããŒããéããªããŸããã 以äžã¯ã¹ã¿ã¡ã³ã®äºæ¥ïŒTUNAGæ¬äœãTUNAGã®ETLåºç€ãTERASïŒã§äœ¿ã£ãŠãããµãŒãã¹ã§ãã ãããã®äžã§è©Šéšæ¬çªã詊éšå匷ãéããŠèŠãªãã£ãã®ã¯ Glue ã ãã§ããïŒGlue ãèŠèœãšããŠãããä»ã®ç·Žç¿åé¡ãªã©ã§åºãŠãããããå¯èœæ§ã¯ãããŸããïŒ ä»ã®ç¹ãšããŠãèšèšã®ãã¹ãã ã©ã¯ ãã£ã¹ã«ã€ããŠãçè§£ãæ·±ãŸã£ãã®ã§ãæ¢åã®èšèšã«ã€ããŠãæ¹åãã¹ããšããã倿ã§ããããã«ãªããŸããã æ¬çª æ¬çªã®è©Šéšã¯ Udemy ã®è¬åº§ã§ç·Žç¿ããéãã®åœ¢åŒã ã£ãã®ã§ç¹ã«æžæãããšã¯ãªããé£æåºŠã«ã€ããŠã Udemy ã®é«é£æåºŠè©Šéšãšæ¯ã¹ããšç°¡åã§ãµã¯ãµã¯è§£ããŠããããšãã§ããŸããã ãã®ç¹ã§ã暡詊ã§èšç·ŽããŠããããšã¯éèŠã ãšæããŸãã ãŸãšã AWS SAA åéšã®æ¯ãè¿ããããŸããã å人çã«æã£ãŠãã以äžã®å¹æãããåéšããŠããã£ãã§ãããåããããªèª²é¡ãæããŠããæ¹ã«ããããããããã§ãã
ããã«ã¡ã¯ãã¹ã¿ã¡ã³ã§ã¢ãã€ã« ã¢ããªéçº ãããŠãã @temoki ã§ãã ã¹ã¿ã¡ã³ã«å
¥ç€Ÿããåœæã¯ç§äžäººã§ã¢ãã€ã« ã¢ããªéçº ãããŠããŸãããããããããŸã§ãã®äžå¹Žåã§ä»²éãå¢ããçµç¹äžãã¢ãã€ã«ã¢ããªã°ã«ãŒããçºè¶³ããã«ãŸã§è³ããŸããããã®ãããæè¿ã¯ããŒã ã§äžç·ã«ãªã£ãŠéçºãé²ããŠãããåã³ãæãã€ã€ãè€æ°äººã§ãéçºãã¹ã ãŒãºã«è¡ãããã®ç°å¢ã¥ãããé²ããŠããŸãã ãã®äžã€ãšããŠããã®ãã³ã¢ãã€ã« ã¢ããªéçº ã«ãããCI/CDãµãŒãã¹ãšã㊠Bitrise ãæ¡çšããããããã®èæ¯ãçç±ã«ã€ããŠãäŒãããããšæããŸãã èæ¯ãšç®ç CI/CDãµãŒãã¹ãå°å
¥ãæ€èšãã¯ãããç®çã¯ãã®äžã€ã§ãã ãªãªãŒã¹ã®é »åºŠãäžããã ç§ãããªããŠããããã€ã§ããããã«ããã ãŠããããã¹ã ãå®çãããã ããããã®ç®çã«ã€ããŠã®èæ¯ã¯æ¬¡ã®ãšããã§ãã ãªãªãŒã¹ã®é »åºŠãäžããã ã¹ãã¢ãžã®ãªãªãŒã¹ã¯ã ãããæã«äžåãšããããŒã¹ã§è¡ã£ãŠããŸããããã¡ã³ããŒãå¢ããããšã§éçºã¹ããŒããäžãããäžåã®ãªãªãŒã¹ã§ã®å€æŽéã倧ãããªã£ãããšããªãªãŒã¹ãžã®äžå®ã«ã€ãªãã£ãŠããŸããããã®ããã瀟å
ãžã®ãã¹ãé
ä¿¡ã®é »åºŠããããããã¹ãã¢ãžã®ãªãªãŒã¹ãããå°ãããããé »ç¹ã«è¡ãå¿
èŠããããŸãã ãŸãããã£ããéçºã¯å®äºããŠããæ©èœãããã®ã«ãŠãŒã¶ãŒãžã®æäŸãé
ããªã£ãŠããŸã£ãããéã«é
ããªããªãããã«çŽè¿ã®ãªãªãŒã¹äºå®æ¥ã§ã®æ©èœã®è©°ã蟌ã¿ãšããããšãèµ·ããããã«ãªã£ãŠããŸããã ç§ãããªããŠããããã€ã§ããããã«ããã ãããã€äœæ¥èªäœã¯ fastlane ã Gradle ã«ããã»ãŒèªååããŠãããŸããããã¢ããªçœ²åã®ããã®ç§å¿æ
å ±ã®ç®¡çã®èгç¹ããããã®äœæ¥ã¯ç§ã®éçºæ©ã®ã¿ã§è¡ãããã«ããŠããŸããããããããã®åæ°ãå¢ããŠãããšãå®è¡ã®æéãå®è¡ã®ããã«éçºæ©ã®ãªãœãŒã¹ãåãããããšã§ãèªåã®äœæ¥ã«åœ±é¿ãã§ãŠããŸããã ãããããããŒã ã§ã®éçºãšãªã£ãä»ãç§ãããããã€ãè¡ããªããšããããšèªäœãåé¡ã§ãã ãŠããããã¹ã ãå®çãããã ãªãªãŒã¹ãžã®äžå®ãæžããããã« ãŠããããã¹ã ã®æåãå®çãããŠãããããšæã£ãŠããäžãæ¹ä¿®ã«ããæ¢åã® ãŠããããã¹ã ãå£ããŠããããšã«æ°ã¥ãã®ãé
ãããšããããšãèµ·ããŠããŸããŸããã幞ãã«ã ãŠããããã¹ã ã®ãã«ããéããªããªã£ãŠããã ãã§ããã¹ãèªäœã§ãšã©ãŒãèµ·ãããããªããšã§ã¯ãããŸããã§ãããããã¯ãå®çãããããã«ã¯åžžã«ãã¹ããå®è¡ãããç°å¢ãå¿
èŠã ãšåèªèããŸããã åè£ã®éžå®ãšæ€èšŒ iOS ã¢ããªã®ãã«ããè¡ãã macOS ããµããŒãããŠããããšãæ¡ä»¶ã«åè£ãçµããŸããã以äžãåè£ã«æãã£ããµãŒãã¹ãšãã®çç±ã§ãã GitHub Actions : macOS ã®ç¡ææ ããã Bitrise : ã¢ãã€ã«ã¢ããªã«åŒ·ããšè©å€ Circle CI : TUNAG ãŠã§ãã¢ããªã±ãŒã·ã§ã³ ã®éçºã§äœ¿çšäž ãããããŠã§ããµã€ãäžã§æ©èœè¡šãæ¯èŒããŠã¿ãŠã倧ããªå·®ã¯ãªãããããŸãå®çŸããããšæã£ãŠããæ¬¡ã®ãããªã¯ãŒã¯ãããŒãäœæããŠæ¯èŒããŠã¿ãããšã«ããŸããã GitHub ãªããžã㪠ã®ç¹å®ã®ãã©ã³ããžã®ããã·ã¥ãããªã¬ãŒã«ãã¢ããªããã«ãã㊠Firebase App Distribution ã§ç€Ÿå
é
ä¿¡ãã æåã®ããªã¬ãŒã§ãã¢ããªããã«ãããŠã¹ãã¢ãžã¢ããããŒããã GitHub ã® Pull Request ãããªã¬ãŒã«ã ãŠããããã¹ã ãå®è¡ãã 以éã«èšèŒããæ¯èŒçµæã¯2020幎3ææç¹ã®ãã®ã§ã ãœãŒã¹ã³ãŒã ã GitHub ã®ãã©ã€ã㌠ããªããž ããªã«ããåæã®ãã®ãšãªããŸãã GitHub Actions GitHub Actions 㯠GitHub Universe 2018 ã§çºè¡šããã2019幎æ«ã«æ£åŒãªãªãŒã¹ãããã°ããã®ã GitHub 補CI/CDãµãŒãã¹ã§ãããªãªãŒã¹ã®ãã¥ãŒã¹ãèããæã¯ã¢ãã€ã«ã¢ããªåãã®ãµããŒãã¯å
ã«ãªãã ãããšæã£ãŠããã®ã§ããããã©ãããã©ãŒã ãšã㊠macOS ãå«ãŸãã iOS ã Android ã®ãã«ãããŒã«ããµããŒãããããªã©ãè¯ãæå³ã§è£åãããã®ã§æ³šç®ããŠããŸããã ãããã macOS ã§ã ç¡ææ ïŒ GitHub Freeãã©ã³ã®å Žåã¯2000åãProãã©ã³ã®å Žåã¯3000åã®å©çšæéïŒããããããæåã«è©ŠããŠã¿ãã®ã¯ GitHub Actions ã«ããããšã«ããŸããã 以äžã«ãã®çµæã ââ³Ã ã§ãŸãšããŸããã â : macOS ã§ãç¡æã§å©çšã§ããæ ããã â : ã¢ãã€ã«ã¢ããªåãã«å¿
èŠãªç°å¢ãå
å®ããŠãã Software installed on GitHub-hosted runners â : ã¢ãã€ã«ã¢ããªåãã®ã¢ã¯ã·ã§ã³ãæ¯èŒçæã£ãŠããå°è±¡ â³ : ã¢ã¯ã·ã§ã³ã¯ YAML 圢åŒã®ã³ãŒãã§ã®ã¿å®çŸ©ã§ãã βçã®æã¯ Visual Editor ããã£ããæ£åŒãªãªãŒã¹æã«å»æ¢ãããããã§ã â³ : ãã«ãã«å¿
èŠãªç§å¿æ
å ±ã¯ GitHub ã® Secrets ã«æååãšããŠé
眮ã§ãã iOS ã®ããããžã§ãã³ã°ãã¡ã€ã«ããã³ãŒã眲åçšèšŒææžãªã©ãæ±ãã«ã¯äžæéããã à : macOS ç°å¢ãé«é¡ Linux ã®10åã§å©çšæéãæ¶è²»ããããã Proãã©ã³ã®å Žåã¯å®è³ª300åããç¡æã§å©çšã§ããªã à : æåã§ãã«ããéå§ããå
¬åŒã®æ¹æ³ããªãã®ã§ã¢ã¯ã·ã§ã³æ§ç¯ã®ãã©ã€ïŒãšã©ãŒãããã«ãã GitHub API ã§ å€éšã€ãã³ã repository_dispatch ãèµ·ããããšã§ä»£çšã§ãããé¢åããã®ããªã¬ãŒã§ã¯Carthageã®ãã£ãã·ã¥ãå©çšã§ããªããªã©ã®é£ç¹ãã äžéãã®ãã©ã€ã¢ã«ãçµããŠã¿ãŠãÃã«æããéšåãéçšäžã®ããã¯ãšãªããããªããšãããããŸãããç¹ã«ã iOS ã«ãã㊠ipa ãã¡ã€ã«ãåºåãããŸã§ã«ã¯ïŒCarthageã®ãã£ãã·ã¥ãå©çšã§ãããšããŠãïŒ20åãããã¯ãããçŸç¶ããããå©çšæéïŒïŒè²»çšïŒãæ°ã«ããªããéçšããããšã«ãªãããã§ãã Bitrise 次㯠Bitrise ã§ããBitrise ã¯ã¢ãã€ã«ã¢ããªåãã®CI/CDãµãŒãã¹ã謳ã£ãŠãããæ¥æ¬éå¬ã®ã¢ãã€ã«ã¢ããªåãã®ã€ãã³ãã«ãããåºå±ãããŠããããæ¬åœã®ãµãŒãã¹ã§ããBitriseã§ãFreeãã©ã³ã§ macOS ãå©çšå¯èœã§ãããïŒãã«ãããã10åãšããå¶çŽããããçŸç¶ã®ã¢ããªã® ipa ãã¡ã€ã«åºåãŸã§ã詊ãã«ã¯æéãè¶³ããªãããšãããã£ãŠããããã GitHub Actionsã®åŸã«è©Šãããšã«ãªããŸããã ä»åãæ€èšŒã®ããã« Developerãã©ã³ ãå¥çŽããŠããŸãããã®ãã©ã³ã§ã¯ãäžåã®ãã«ãããã45åå¶éããããŸããããã«ãåæ°ã«é¢ãããå®é¡ã§ãããããå©çšæéãæ°ã«ããã«è©Šãããšãã§ããŸããã以äžããBitriseã®çµæã§ãã â³ : Freeãã©ã³ã§ã¯1ãã«ãããã10åã®å¶éãããã iOS ã¢ããªã®ãã«ãã詊ãã«ã¯å³ãã â : å®é¡ã§ãããããå©çšæéãæ°ã«ããå¿
èŠããªã â : ã¢ãã€ã«ã¢ããªåãã«å¿
èŠãªç°å¢ãå
å®ããŠãã â : ãªããžããª æ§æãèªåã§ãã§ãã¯ããŠã¯ãŒã¯ãããŒãããçšåºŠèªåã§æ§ç¯ããŠããããããåæã»ããã¢ãããæ¥œ â : UIãããããããïŒãããŠããããïŒ â : ã¯ãŒã¯ãããŒãããžã¥ã¢ã«ã§ã YAML 圢åŒã®ã³ãŒãã§ãå®çŸ©ã§ãã â : ã¢ãã€ã«ã¢ããªåãã®ã¢ã¯ã·ã§ã³ãè±å¯ã«ãã â : ãã«ãã«å¿
èŠãªç§å¿æ
å ±ã¯ã¢ããªã±ãŒã·ã§ã³ããšã«å®çŸ©ã§ãããã€ã¢ãã€ã«ã¢ããªãšã®èŠªåæ§ãé«ã iOS ã®ããããžã§ãã³ã°ãã¡ã€ã«ããã³ãŒã眲åçšèšŒææžãèšå®ããããã®å°çšã®æ©èœããã â : å°ã£ãæã¯ ä»®æ³ãã·ã³ ã« ssh ã ãªã¢ãŒããã¹ã¯ããã ã§ãã°ã€ã³ããŠç¶æ³ã確èªããããšãã§ãã Bitriseã®UIã¯ãããã§ããããð è©å€ãé«ãã®ãçŽåŸã®ã¯ãªãªãã£ã§ããã GitHub Actionsã®æã«ã¯è©Šè¡é¯èª€ããªããã»ããã¢ããããŠããŸããããBitriseã§ã¯ãšãŠãç°¡åã«ã»ããã¢ãããçµããããšãã§ããŸããããŸããå®é¡ãªã®ã§ãã®åŸãã¢ããªã®ç€Ÿå
é
ä¿¡ã ãŠããããã¹ã ãã©ãã©ãå®è¡ã§ããå©çšæéãå
šãæ°ã«ããªããŠããã®ãçŽ æŽãããã§ãã Circle CI TUNAG ãŠã§ãã¢ããªã±ãŒã·ã§ã³ ã®éçºã§ãã§ã«ç€Ÿå
ã§å©çšäžã® Circle CI ãåè£ã«æããŸããããã¢ãã€ã«ã¢ããªåãã«æ³å®ããŠããå®è¡æéãé »åºŠãèãããšããã®éçºæ¥åã«ãè²»çšã«ã倧ããªåœ±é¿ãäžããããšããããšãããããŸããããããŠäœããBitriseãæ³å以äžã«åªç§ã ã£ãããšãããCircle CIã®æ€èšŒã¯ãããBitriseãç¶ç¶ããŠããããšã«æ±ºããŸããã ãããã« æåŸã«ãŸãšããšãªããŸãããäžèšã§èšãã° ãBitriseã¯ãããïŒã ã§ããæ¥åäžã®éžå®ãšãªãããæ©èœé¢ã§ã®æ¯èŒãçé¢ç®ã«è¡ããŸããããBitriseã䜿ã£ãŠãããã¡ã«ããã®ããžã¥ã¢ã«ãã¶ã€ã³ã®ãããããæäœæ§ã®è¯ãã现ããéšåã§ã®ãŠãŒã¶ãŒãžã®é
æ
®ãæããããçŽç²ã«Bitriseãšãããããã¯ããå¿ããæ°ã«å
¥ã£ãŠããŸããŸããã Continuous happiness âš ãããŠãåŒç€Ÿã®åµæ¥äºæ¥TUNAGããšã³ããŠãŒã¶ãŒã«åãããã«å¿ããæ°ã«å
¥ã£ãŠãããããããªãããã¯ãã«è²ãŠãŠãããããšåŒ·ãæããŸããããã®ããã«ã¯ããã«åŒ·ãéçºåãå¿
èŠãšãªããŸãã®ã§ãã¹ã¿ã¡ã³ã§ã¯äžç·ã«ãããã¯ãéçºãé²ããŠããã仲éãåéããŠããŸãïŒèå³ãæã£ãŠãããæ¹ã¯ããã²ãã²äžèšã®ãšã³ãžãã¢æ¡çšãµã€ããã芧ãã ããã ã¹ã¿ã¡ã³ ãšã³ãžãã¢æ¡çšãµã€ã ãµãŒããŒãµã€ããšã³ãžãã¢åéããŒãž ããã³ããšã³ããšã³ãžãã¢åéããŒãž ã€ã³ãã©ãšã³ãžãã¢åéããŒãž ã¢ãã€ã«ã¢ããªãšã³ãžãã¢åéããŒãž
ã¹ã¿ã¡ã³ã®ãã¶ã€ããŒã® @kiyoshifuwa ã§ãã çŸåšã åŒç€Ÿã®ãã¶ã€ããŒã¯ç€Ÿå¡ã®ããã1åãåŠçã¢ã«ãã€ããã1åã®èš2åã§ãã äž»ã«TUNAGãªã©åäºæ¥ã®ã¢ããªã±ãŒã·ã§ã³ã®ãã¶ã€ã³ãè¡ã£ãŠããŸãããå®ã¯ç€Ÿå
ã®ãããããã瀟å
ã®ãã¶ã€ããŒãäœã£ãŠããŸãïŒ ä»åã®èšäºã§ã¯ã瀟å
æ¥åã®äžéšãã玹ä»ããŸãã 瀟å€ã®æ¹ã«åã㊠åçš®Webãµã€ã èŠæš¡ã®å€§ãããã®ã¯å€éšååäŒç€Ÿæ§ã«ãé¡ãããããšããããŸãããå°èŠæš¡ã§ããã°ç€Ÿå
ã§ãã¶ã€ã³ã»å®è£
ãè¡ããŸããæšå¹Žã¯TUNAGãã©ã³ããµã€ãã®ãããããŒãžããšã³ãžãã¢æ¡çšãµã€ãã ã€ã³ã¿ãŒã³ã·ãã åéãµã€ãã®ãã¶ã€ã³ãããŸããã æ¡å
ããã« ã¹ã¿ã¡ã³ã® ã»ã ããŒã¹ããŒã¹ã§ã¯æ§ã
ãªã€ãã³ããå匷äŒãè¡ãããŸããæ¥ç€Ÿãããã客æ§ããæ¡å
ããããã«ã Wi-Fi ãšãææŽãã®ããã«ãäœããŸããïŒ ç€Ÿå€å ± 瀟å¡ã®ãå®¶æã«ã瀟é·ã®æ³ãã瀟å¡ã®æŽ»èºãã€ãã³ãã®æ§åãªã©ãç¥ã£ãŠããã ãããã«äœã£ãŠããŸãããã®ãããªååã®ãã¶ã€ã³ã¯ãã¹ã¿ã¡ã³ã«å
¥ããŸã§ãã£ãããšããããŸããã§ãããäžããå匷ããŠã詊è¡é¯èª€ããªããäœãäžããŠããŸãïŒ âŒæ°ã¡ã³ããŒãåæ ç¹ã®ãªãã£ã¹ã玹ä»ããã³ã³ãã³ãã§ãïŒ ã¹ã¿ã¡ã³ç€Ÿå¡ã«åã㊠è¡åæéãã¹ã¿ãŒ ã¹ã¿ã¡ã³ã«ã¯6ã€ã®è¡åæéããããŸããæ°ã¡ã³ããŒã§ãããã«éŠŽæã¿æµžéããããã«ãã¿ããªãå¿ããã«æèã§ããããã«ãããããã®æéã«ãã£ãããžã¥ã¢ã«ãäœãããã¹ã¿ãŒã«ããŸããã âŒãã¡ããåå€å±æ¬ç€Ÿã ãã§ãªãæ±äº¬ã倧éªãªãã£ã¹ã«ã æ²ç€º ãããŠããŸãïŒÂ âŒEnjoy Togetherã®äººã
ã®äžã«ãå®ã¯ãããžã¡ã³ãã¡ã³ããŒãããŸãããã£ããéã³å¿ãå ããŠããŸãïŒ ã¹ã¿ã¡ã³ã¢ã¯ãŒãåè³è
ãã¹ã¿ãŒ æããšã«æã掻èºããã¡ã³ããŒã衚地ãããã¹ã¿ã¡ã³ã¢ã¯ãŒãããåè³ããã¡ã³ããŒãç§°ãããã¹ã¿ãŒã§ãïŒãã¡ãããã¡ããå
šæ ç¹ã« æ²ç€º ãããŸãïŒåžžã«ç®ã«å
¥ãäœçœ®ã« æ²ç€º ãããŠããã®ã§ãæ¥ã
ãæ¬¡ã¯èªåãåè³ã§ããããé 匵ããð¥ããšæããŸãã âŒ2019幎3rdããªãªã æ°äººè³ãåè³ãããããã§ããããã§ãšãïŒ ä»è·çš®ã¡ã³ããŒã®ãæäŒã 瀟å
ååŒ·äŒ ã»ãŒã«ã¹ããšã³ãžãã¢ã«ããã¶ã€ã³ãããŒã«ã®ç¥èã圹ç«ã€ããšããããŸãããããããããã¹ã©ã€ãã®äœãæ¹ãããã Keynote ãå¹çããäœ¿ãæ¹æ³ããªã©ãã³ãã¶ã€ããŒåãã®ç€Ÿå
å匷äŒãèªäž»çã«è¡ã£ãŠããŸãïŒ ç»å£è³æãã³ãã¬ãŒã ã¹ã¿ã¡ã³ã«ã¯è·çš®ãåããèªäž»çã«å€éšçºä¿¡ããã人ããšãŠãå€ãã§ããè³æäœããæ¥œã«ããããããŸãä»ç€Ÿã®æ¹ã«ã¹ã¿ã¡ã³ã®è³æã ãšèªèããŠãããããã«ãã³ãã¬ãŒããäœããŸããïŒæã£ããã瀟å
ããåé¿ããã£ãã®ã§ãäœã£ãŠããã£ããªããšæããŸããâºïž å®ã¯ãããªãã®ããã¶ã€ã³ããŠããŸã 瀟å
ã€ãã³ããçãäžããããã®ãã¶ã€ã³ãäœã£ãŠããŸãïŒ ã¹ ã¿ã 㯠瀟å
暪æåã®çµç¹æŽ»æ§åPJããŒã ãã¹ ã¿ã ã¯ãæŽä»£ã¡ã³ããŒã玹ä»ããç»åã§ãïŒæ®åœ±ã¯ç€Ÿå
ã§æ¬æ Œçã«è¡ããŸããã TUNAGçšã¹ã¿ã³ã ã¹ã¿ã¡ã³ã§ã¯ãèªç€Ÿã§éçºããŠãããTUNAGãã§ã³ãã¥ãã±ãŒã·ã§ã³ãåã£ãŠããŸããæçš¿ãžã®ã³ã¡ã³ãããã£ããã§ã®ãªã¢ã¯ã·ã§ã³ã«ãªãªãžãã«ã®ã¹ã¿ã³ãã䜿ãããšãã§ããŸãããããããããŸãããäžéšãã玹ä»ããŸãïŒ TUNAGçšçµç¹ç»å ãTUNAGãã§ã¯ãçµç¹ã®æ§æãèŠãããšãã§ããŸããåéšçœ²ãåæ§ãåºãŸããã®åçãã«ããŒç»åã«èšå®ããŠããŸãããããã¯ãéšã§ã¯ãã匷ããšã³ãžãã¢ã»ãã¶ã€ããŒã»ãã£ã¬ã¯ã¿ãŒããããŒãã«ããŒãã£ãœãç»åãç°¡æçã«äœããŸããïŒèæ¯ã®ççºã¯ããæããå å·¥ããŠã§ããŠããŸãã ããã㫠以äžãã¹ã¿ã¡ã³ã§ã®ç€Ÿå
æ¥åãã玹ä»ããŸããã å°å·ãªã©ãå
šç¶ç¥èã®ãªãäžæ
£ããªåéã§ãèªåã§èª¿ã¹ãªãããããããªãã®ã§ããªããªã倧å€ãªããšããããŸãïŒç¬ïŒ ã§ãæ¯åãã¹ã¿ã¡ã³ã®ã¿ããªãžãµãã©ã€ãºã仿ããŠãããããªæ°æã¡ã«ãªã£ãŠããããããŸãïŒãŸãããæ«é²ç®ããåŸã®åå¿ãèŠãã®ãå¬ããããããããæããŸããå°é£ãä¹ãè¶ããŠãã¿ããªã«æåãå±ããã®ã¯ãšãŠã楜ããã§ãðª ã¹ã¿ã¡ã³ã«ãè¶ãããã ãéã¯ãã²ããã¶ã€ã³ãã芧ã«ãªã£ãŠãã£ãŠãã ããïŒ
ããã«ã¡ã¯ãæŸè°·( @uuushiro )ã§ãã å
æ¥ã³ãŒãã¬ãŒããµã€ãã«ãŠã æ°çµå¶äœå¶ã®ãç¥ãã ãå
¬éãããŠããã ããŸãããã3æããåç· åœ¹CTOãåããŠããå°æ äžæš¹ãåžžååç· åœ¹VPoEã«å°±ä»»ãã å·è¡åœ¹å¡ CTOã«ã¯ç§ãæŸè°· å岿 ã就任ããŸãããCTO ãš VPoE ãåé¢ããããšã§ãå°æã¯VPoEãšããŠçµç¹ãšäºæ¥ã«ãç§ã¯CTOãšããŠæè¡ã«å°å¿µããçµç¹ã»äºæ¥ã»æè¡ã®æææ±ºå®ã®é床ãšç²ŸåºŠã®åäžãå®çŸããŠãããŸãã ä»åã®å°±ä»»ã«ããããŸããŠãç§èªèº«ã®æ¯ãè¿ããšããããã®æ±è² ãç°¡åã«è¿°ã¹ãããŠããã ããããšæããŸãã ãŸãç§ãšã¹ã¿ã¡ã³ãšã®é¢ããã§ãããã¹ã¿ã¡ã³ã嵿¥ãã2016幎ã«åŠç ã€ã³ã¿ãŒã³ ãšããŠå ãããç¿å¹Žæ£ç€Ÿå¡ãšããŠå
¥ç€ŸããŸãããå
¥ç€Ÿæã®èšäºã¯ ãã¡ã ã§ãããšã³ãžãã¢æªçµéšã§ ãã³ãã£ãŒ ã«é£ã³èŸŒã¿ãä»ãŸã§ã²ãããæ¥åã®äžã§çµéšãç©ãŸããŠããããŸãããå
¥ç€Ÿæã«ã¯èªåãCTOãšããŠåœ¹å²ãæ
ãããšã«ãªããšã¯æ³åãããŠããŸããã§ãããã嵿¥æã¯CTOå°æã®çŽäžã§åŠãã§ããã®ã§ãèªç¶ãšCTOãšãã圹å²ãæèããŠããã®ã確ãã§ãããã èªåãšããŠã®CTOã®ã€ã¡ãŒãžã¯æŒ ç¶ãšããŠããŠããCTOã«ãªãããããšèšã£ãŠã¯ã¿ãŠãããããããCTOã£ãŠãªãã ããïŒãããªããããŠãªã£ãŠãããã®ãªã®ãïŒããªã©ãªã©èªåèªçãç¶ããŠããŸããã ãããªäžãå
¥ç€Ÿ2幎ç®ã«ãã ã¯ãªãŒã ãšããŠæè¡çãªæææ±ºå®ã«é¢ããããã«ãªãã3幎ç®ã«ã¯ãããŒãžã£ãŒãšããŠéçºããŒã ã®éå¶ã«é¢ããããã«ãªããŸããããã ã¯ãªãŒã ãšããŠããããŒãžã£ãŒãšããŠãã©ããã£ããã¹ã¿ã¡ã³ã«è²¢ç®ã§ããããäžé·æã§ç§ãã§ããããšã¯äœãªã®ãããšããããšãèãç¶ããŠãããã®å»¶é·ç·äžã«CTOãšããã€ã¡ãŒãžãåŸã
ã«ã¯ã£ããããŠããŸããããŸã ãŸã ææ¢ãç¶æ
ã§ã¯ãããŸãããCTOãšããŠã¹ã¿ã¡ã³ã«è²¢ç®ã§ããå§¿ã«ç¢ºä¿¡ãæãŠãããã«ãèªåã®ããã¹ãCTOåãèãæããŠãããããšæããŸãã å®éã«å°±ä»»ããŠã¿ããšã倱æã誰ã®ããã«ãã§ããªãç·åŒµæãæææ±ºå®ã®åœ±é¿ã®å€§ãããçµå¶é£ãšããŠã®è²¬ä»»ã®éãã«ããã ãã 身ãåŒãç· ãŸãæãã§ããæ£çŽãåCTOã®å°æãã瀟å€ã®CTOã® ãããã©ã³ããŒ ã®æ¹ãã¡ãèŠãŠãããšåãCTOãšããŠçŠãããæããŸããããä»åŸã¯èªåã®éçå€ãçµç¹ã®éçå€ã«ãªããã¬ãã·ã£ãŒãæããŠããŸããä»ã¯ãŸã ãŸã çæ³ãšã¯çšé ãã§ããããšã«ããèªåã®æé·ãæ¢ããã«å°ãã§ãåé²ãææãåºããŠãããããšæããŸãã ä»åŸã®æè¡ç課é¡ãšããŠã¯ãäºæ¥ã®æé·ãå éããããããããªéçºã¹ããŒããšã¯ãªãªãã£ã®ç¶æãäºæ¥èп𡿡倧ã«åããã¹ã±ãŒã©ããªãã£ã®ç¢ºä¿ãå®å¿ããŠãµãŒãã¹ãå©çšããŠããã ããå
ç¢ãªã»ãã¥ãªãã£ãéé£ç¶ãªé²åãéããããã®æ°ããæè¡æè³ããªã©ãªã©æããã°ããªããããŸããããã¹ã¿ã¡ã³éçºããŒã ã®ããžã§ã³ããšã³ãžãã¢ãªã³ã°ã§äºæ¥ã®æé·ãçœåŒãããã«åŸ¹åºçã«åãåããããŒã ã§åé²ããŠãããããã«CTOãšããŠãªãŒãããŠãããããšæããŸãã çµç¹ãšããŠã¯ãã¹ã¿ã¡ã³ãšã³ãžãã¢ã®äŸ¡å€èг Star Code ã«ããéããåžžã«ãŠãŒã¶ãŒç®ç·ã§èããé害ããããžã§ã¯ãé
å»¶ãªã©ã®å€±æã«åãåãããäºãå¿åºŠããã«æ¬é³ã§èªãåããæ¥ã
ã®èªåã®ä»äºã«èªããæãŠãã»ã©ã®ããæèãæã¡ããããŠäœããããšã³ãžãã¢ãªã³ã°ã楜ããããããªãšã³ãžãã¢çµç¹ãCTOãšããŠçãšã€ãããããŠããããã§ãã 瀟å
å€åããã¹ã¿ã¡ã³ã®CTOãšããŠçŽåŸããŠããããå®åãã€ãå®çžŸãåºããããã«ãå
šåã§åœ¹å²ãå
šãããŠãããããšæããŸãã®ã§çæ§ãããããé¡ãããããŸãã æåŸã« ã¹ã¿ã¡ã³ãšã³ãžãã¢çµç¹ãšããŠã¯ã0â1 ãã㊠1â10 ã®ãã§ãŒãºãçµããããããã¯ãã®å
ã®ãã§ãŒãº10â100ã®ãã§ãŒãºã«ãªããŸãããããã¯ããè€æ°ã«ãªããTUNAG ã®å€§èŠæš¡åãšæ©èœæ¡åŒµã«åããŠäžå±€åŒ·åãé²ããŠããã¿ã€ãã³ã°ãªã®ã§ããšãŠãããããã®ããé¢çœãä»äºã«ãªããšæããŸããæ°ããäœå¶ã§ãå€ãã®ãã£ã¬ã³ãžã³ã°ãªæè¡ç課é¡ã«åãåãããŠãŒã¶ãŒã«äŸ¡å€ãå±ãããšã³ãžãã¢åéããŠããŸãïŒèå³ãæã£ãŠãããæ¹ã¯æ¯éãäžèšã®ãšã³ãžãã¢æ¡çšãµã€ããèŠãŠãã ããã ã¹ã¿ã¡ã³ ãšã³ãžãã¢æ¡çšãµã€ã ãµãŒããŒãµã€ããšã³ãžãã¢åéããŒãž ããã³ããšã³ããšã³ãžãã¢åéããŒãž ã€ã³ãã©ãšã³ãžãã¢åéããŒãž ã¢ãã€ã«ã¢ããªãšã³ãžãã¢åéããŒãž
ããã«ã¡ã¯ãã¹ã¿ã¡ã³ã§ããã¯ãšã³ãã®ãšã³ãžãã¢ãããŠããæ²³äºã§ãã ç§ã®æå±ããããã¯ãšã³ãããŒã ã§ã¯ãæ®æ®µãããµãŒãã¹ã®ããã©ãŒãã³ã¹ç£èŠãšãã¥ãŒãã³ã°ãç¶ç¶çã«è¡ã£ãŠããŸãã ä»åã¯ãããŒã¿ããŒã¹è² è·ã®ã¢ãã¿ãªã³ã°ã«äœ¿ã£ãŠãã Performance Insights ãšããããŒã«ã®æŽ»ç𿹿³ããŸãšããŠã¿ãŸãã Performance Insights ã«ã€ã㊠Performance InsightsïŒããã©ãŒãã³ã¹ã€ã³ãµã€ãïŒãšã¯ãAmazon RDS ã§æäŸãããŠããæ©èœã§ãDB ã®è² è·ã®ã¢ãã¿ãªã³ã°ãç°¡åã«ã§ããŸãã ãã£ãããšãŸãšãããšã ææšãã·ã³ãã«ãªã®ã§ DB ã®è² è·ç¶æ³ãçŽæçã«ææ¡ã§ãã SQL ã¯ãšãªã衚瀺ãããããåé¡ç®æã®ç¹å®ããããã ãªã¢ã«ã¿ã€ã æŽæ°ãªã®ã§å³åº§ã«è² è·å
容ãåæã§ãã ãšãã£ãã¡ãªããããããŸãã æå¹åã«ã€ããŠã¯ ãã¡ã ãåç
§ãã ããã ããã·ã¥ããŒãã®èŠæ¹ ããã·ã¥ããŒããèŠããããŠäŸ¿å©ãªã®ã§ãåºæ¬çã«ãã¡ãã䜿ããŸãã RDS ã³ã³ãœãŒã«ã®ãµã€ãããŒã«ãããããã©ãŒãã³ã¹ã€ã³ãµã€ããããã¢ã¯ã»ã¹ã§ããŸãã ç»é¢ã¯3ã€ã®ããŒãã«å¥ããŠããŠãäžãããã«ãŠã³ã¿ãŒã¡ããªã¯ã¹ãããŒã¿ããŒã¹ã®ããŒããSQL ã¹ããŒãã¡ã³ããã€ãžã§ã¹ããšãªã£ãŠããŸãã åºæ¬çã«ã¯ããŒã¿ããŒã¹ã®ããŒããšSQLã¹ããŒãã¡ã³ããã€ãžã§ã¹ãã䜿ãã®ã§ããã®2ã€ã«ã€ããŠè§£èª¬ããŸãã ããŒã¿ããŒã¹ã®ããŒã ããŒã¿ããŒã¹ã®è² è·ãæç³»åã§è¡šç€ºããŠããŸããè² è·ã¯å¹³åã¢ã¯ãã£ãã»ãã·ã§ã³ïŒAverage Active SessionãAASïŒãšããææšã§è¡šçŸãããŠããŸãã å¹³åã¢ã¯ãã£ãã»ãã·ã§ã³ãšã¯ãäžå®æéå
ã«åŠçäžãŸãã¯åŠçéå§ãåŸ
ã¡ã®ã»ãã·ã§ã³ã®æ°ã§ããã®æ°ãå€ãã»ã© DB ã«è² è·ãããã£ãŠãããšãããŸãã äžèšç»åã§ã¯ãã¹ã©ã€ã¹åºæºãšããé
ç®ãããåŸ
æ©ã ã«ãªã£ãŠããŸãã ã¹ã©ã€ã¹åºæºãšã¯ AAS ã®å
èš³ãäœåºæºã§è¡šç€ºããããšããèšå®é
ç®ã§ãã åŸ
æ©åºæºã§ã¯ãããŒã¿ããŒã¹ã®äžé£ã®åŠçã®ãã¡ã©ã®ã€ãã³ãã§ã©ã®ã€ãã³ãã§ã®åŸ
æ©æéãé·ããªã£ãŠããããç¥ãããšãã§ããŸãã åçš®ã€ãã³ãã®è©³çްã«ã€ããŠã¯ ãã¡ã ãåç
§ãã ããã ã°ã©ãã®äžéšã«ããç Žç·ã¯ä»èŠãŠãã RDS ã€ã³ã¹ã¿ã³ã¹ã®ä»®æ³ CPU æ°ã衚ããŠããŸãã CPU ã§ã®åŸ
æ©æ°ã ãã§ãã®ã©ã€ã³ãè¶
ãããšãCPU æ°ãäžè¶³ããŠãããšãããŸãã äžã®ç»åã§ã¯äžç®æãã®ã©ã€ã³ã«å°éããŠããŸãããåŸ
æ©ã®å
èš³ãèŠããš CPU ã®å²åã¯äœãã§ãã ãã®ãããCPU ãåå ãšãªã£ãŠè² è·ãé«ãŸã£ãŠããããã§ã¯ãªããšå€æã§ããŸãã æ¬¡ã«ãã¹ã©ã€ã¹åºæºã SQL ã«å€ããŠã¿ãŸãã SQL åºæºãéžæããããšã§ãåã¯ãšãªããšã«ã©ãã ãåŸ
æ©ããŠããããç¥ãããšãã§ããŸãã ãŒãããå
¥ããŠãããšããã«ã¯ SQL ã¯ãšãªã衚瀺ãããŠãããã©ã®ã¯ãšãªã«ãã£ãŠè² è·ãçºçããŠããã®ãïŒãå€å®ã§ããŸãã SQL ã¹ããŒãã¡ã³ããã€ãžã§ã¹ã SQL ã¹ããŒãã¡ã³ããã€ãžã§ã¹ãã§ã¯ã ç°¡ç¥åããã SQL ã¯ãšãªãè² è·ã®é«ããã®ããé ã«è¡šç€ºãããŠããŸãã ãã€ãžã§ã¹ãã§ã¯ãã©ã¡ãŒã¿ã name = ? ã®ããã«çœ®ãæããããŠãããåãã¯ãšãªã®åœ±é¿ããŸãšããå€ã«ãªã£ãŠããŸãã ããã«è©³çްãèŠãããšãã¯â·ãã¯ãªãã¯ããå®éã®ãã©ã¡ãŒã¿ãå
¥ã£ãåã
ã®ã¯ãšãªãèŠãããšãã§ããŸãã TUNAG ã§ã¯äŒæ¥ããšã«æè»ãªèšå®ãå¯èœãªãããäºæãã¬èšå®ã§è² è·ãé«ãŸãããšããããŸãã ãã©ã¡ãŒã¿ããã®ç¶æ
ã§ã¯ãšãªãèŠãŠäŒæ¥ãç¹å®ããåå ç©¶æãŸã§ã¹ããŒãã£ã«è¡ããããã«ãªããŸããã ç£èŠã®èšå® äžèšã¯è² è·ãããã£ãŠããã¿ã€ãã³ã°ãç¹å®ã§ããŠããåæã§è©±ãããŠããŸããã è² è·ãé«ãŸã£ãŠããããšèªäœã«æ°ã¥ãããã«ãããŒã¿ããŒã¹ã®ããŒããäžå®æ°ãè¶
ãããéç¥ãããããã«ããŠã¿ãŸãã èšå®å¯èœãªé
ç® CloudWatch Alarm ã§ããŒã¿ããŒã¹ã®ããŒããç£èŠããããšãã§ããã®ã§ãèšå®æé ã説æããŸãã ããŒã¿ããŒã¹ã®ããŒãã«é¢ããã¡ããªã¯ã¹ã¯3çš®é¡ãããŸãã DBLoad DBloadCPU DBLoadNonCPU DBLoadCPU ã¯åŸ
æ©èŠå ã CPU ã®ã¿ã«éå®ãããã®ãDBLoadNonCPU 㯠CPU 以å€ã®èŠå ã§åŸ
ã¡ãçºçããŠãããã®ãDBLoad ã¯ãã®åèšå€ãšãªã£ãŠããŸãã ããã¯ãšã³ãããŒã ã§ã¯æ¬¡ã®ãããªäœ¿ãåããããŠããŸãã DBLoadCPU: CPU æ°äžè¶³ã®æ€ç¥ DBLoadNonCPU: éå¹çãªã¯ãšãªçºè¡ã®æ€ç¥ïŒã€ã³ããã¯ã¹ãå¹ããŠããªããDB ãžã®åæã¢ã¯ã»ã¹æ°ãå€ããããªã©ïŒ èšå®æé ã©ã®ææšã䜿ãã«ããŠãèšå®ã¯åããªã®ã§ãDBLoad ã®ã¢ã©ãŒã ãèšå®ããŠã¿ãŸãã ãŸã㯠CloudWatch ã³ã³ãœãŒã«ã«ã¢ã¯ã»ã¹ãããµã€ãã¡ãã¥ãŒã®ãã¢ã©ãŒã ããéžæããã¢ã©ãŒã ã®äœæããšé²ã¿ãŸãã é²ãã å
ã®ç»é¢ã§ã"DBLoad" ã§æ€çŽ¢âãRDS > ããŒã¿ããŒã¹ã¡ããªã¯ã¹ããéžæâç£èŠããã DB ã€ã³ã¹ã¿ã³ã¹ã«ãã§ãã¯âã¡ããªã¯ã¹ãéžæãšé²ã¿ãŸãã ã¡ããªã¯ã¹ãååŸããééãéŸå€ã¯ãæ®æ®µè² è·ãé«ãŸããšãã® AAS ãåèã«æ±ºããŸãããã æåŸã«ãéç¥å
ãèšå®ããŠçµäºã§ãã ãŸãšã Performance Insights ã®ããã·ã¥ããŒãã®èŠæ¹ããã¢ã©ãŒãã®èšå®æ¹æ³ãŸã§ãã£ãããšãŸãšããŠã¿ãŸããã ããã䜿ãå§ããŠããåçã«ã¯ãšãªã®æ¹åãé²ãã ã®ã§ããŸã 䜿ã£ãŠããªããšããæ¹ã¯ãã²è§Šã£ãŠã¿ãŠãã ããã æåŸã«ãæ ªåŒäŒç€Ÿã¹ã¿ã¡ã³ã§ã¯äžç·ã«åã仲éãåéããŠããŸãã ãèå³ã®ããæ¹ã¯ãã²äžèšã®ããŒãžãã芧ãã ããïŒ ãšã³ãžãã¢æ¡çšãµã€ã ãšã³ãžãã¢åéããŒãžïŒWantedlyïŒ åèè³æ ããã©ãŒãã³ã¹ã€ã³ãµã€ãã䜿çšãã Performance Insights ã§ Amazon RDS for MySQL ããã¥ãŒãã³ã°ãã ã¢ã€ãã£ãã Photo by Chris Liverani on Unsplash
ã¹ã¿ã¡ã³ ãšã³ãžãã¢ã®æŸè°·( @uuushiro )ã§ãã ã¹ã¿ã¡ã³ã¯ã2020幎3æãã ãšã³ã²ãŒãžã¡ã³ã蚺æãµãŒãã¹ TERAS(ãã©ã¹) ã®æäŸãéå§ããŸãã嵿¥ãããã¯ãã§ãã TUNAG(ããã°) ã«ç¶ããŠ2ã€ç®ã®ãããã¯ãã«ãªããŸãïŒ TERASã¯ããšã³ã²ãŒãžã¡ã³ãçµå¶ãå®è¡ã§ãããµãŒãã¹ TUNAG ã®ããŠããŠãå
ã«ãããçµç¹ã®ãšã³ã²ãŒãžã¡ã³ããå¯èŠåããçµç¹èšºæãµãŒãã¹ã§ãããµãŒãã¹ã®æŠèŠã¯ä»¥äžãªã³ã¯ããã芧ãã ããïŒ ãšã³ã²ãŒãžã¡ã³ã蚺æãµãŒãã¹ãTERASãããªãªãŒã¹ ãã®èšäºã§ã¯ããšã³ã²ãŒãžã¡ã³ã蚺æãµãŒãã¹ TERAS ã®æè¡ ã¢ãŒããã¯ã㣠ã®ç޹ä»ãããŸãã TERAS 㯠SPA(React)ãš API ãµãŒã( Ruby on Rails )ã§äœãããWebã¢ããªã±ãŒã·ã§ã³ã§ãã ã¢ãŒããã¯ãã£ ã®æ§æ³ã»å®è£
ã«ããã£ãŠãéèŠèŠããç¹ã¯3ã€ã§ãã å®è£
ã»ä¿å®ã»éçšã³ã¹ãããªãã¹ãåæžããæ©èœéçºã«éäžã§ããåºç€ã«ããã ã»ãã¥ãªãã£ã»ã¹ã±ãŒã©ããªãã£ãªã©ã®éæ©èœèŠä»¶ã¯åæãããªãã¹ã劥åããããªã ã€ã³ãã©ãã³ãŒãåããŠç®¡çããã€ã³ãã©ç¥èã® åé·å ã®ããŒãã«ãäžãããããŸããåå©çšæ§ãããããã ç¹ã«è€éãªããšã¯ããŠããããçµæãšããŠã·ã³ãã«ãªæ§æã«ãªããŸããã 以äžã§ ã¢ãŒããã¯ã㣠ã®å
šäœå³ãšåå¥è©³çްã玹ä»ããŠãããŸãã TERAS ã¢ãŒããã¯ã㣠å
šäœå³ TERAS ã¢ãŒããã¯ã㣠ã®å
šäœå³ã¯ä»¥äžã«ãªããŸãã ãã£ãã以äžã®é
ç®ã«åããŠç޹ä»ããŠãããŸãã ãããã¯ãŒã¯ CDN WEBãµãŒã㌠ããã〠ç§å¿æ
å ±ç®¡ç ã€ã³ãã©ã®ã³ãŒãå ãããã¯ãŒã¯ ãããã¯ãŒã¯ä»¥äž3ã€ã®ãµããããã«åããŠããŸãã ã°ããŒãã«IP ãæãŠã°ãå€éšãããã¯ãŒã¯ãšéä¿¡ãã§ãããµãããã(Public Subnet) NATãä»ããŠã®ã¿å€éšãããã¯ãŒã¯ãšéä¿¡ãã§ãããµãããã(Protected Subnet) å€éšãããã¯ãŒã¯ãšéä¿¡ã¯ã§ããªããµãããã(Private Subnet) TERAS ã§ã¯ãApplication Load Balancer ã Public Subnet ã«é
眮ããECS/Fargateã¿ã¹ã¯ã Protected Subnet ã«é
眮ããããšã§ãApplication Load Balancer ããã®ã¿ã¢ã¯ã»ã¹ãåãä»ããããã«ããŠããŸãããŸããWEBãµãŒããŒãšããŠFargateã¿ã¹ã¯ãå©çšããŠããŸãããFargateã¿ã¹ã¯ã«å¯ŸããŠçŽæ¥Elastic IPãå²ãåœãŠãããšãã§ããªãã®ã§ãå€éšãžã®ã¢ãŠãããŠã³ãéä¿¡(ã¡ãŒã«é
ä¿¡ãµãŒãã¹ãžã® API ã¢ã¯ã»ã¹ãªã©)ãèš±å¯ããããã«ãNATã Public Subnet ã«é
眮ããŠããŸããRDSã¯ãå€éšéä¿¡ã®å¿
èŠæ§ããªãã®ã§ãPrivate Subnetã«é
眮ããŠããŸãããã®ããã«ããããã¯ãŒã¯ã®åœ¹å²æ¯ã«äœæããããšã§ãéå±€åããã¢ã¯ã»ã¹å¶åŸ¡ãè¡ãã»ãã¥ãªãã£ãåäžãããŠããŸãã CDN TERAS ã§ã¯ã CDN ãšã㊠Amazon CloudFront ãå©çšããŠããŸããå°å
¥çç±ã¯3ç¹ã§ãã ããã©ãŒãã³ã¹åäž ãšããžãµãŒããŒããã³ã³ãã³ããéä¿¡ãããåãã¬ã¹ãã³ã¹ãéã ãã ããªãªãžã³ãæ±äº¬ãªãŒãžã§ã³ãªã®ã§å®éã«ã¯ãããŸã§å·®ãã§ãªããããããªãã ã³ã¹ãåæž Amazon CloudFront ããããŒã¿ãéä¿¡ããã³ã¹ã㯠Amazon S3 ããéä¿¡ããã³ã¹ããããå®ã ã»ãã¥ãªã㣠AWS WAF ã®çµ±åãå¯èœã§ã XSS ã SQLã€ã³ãžã§ã¯ã·ã§ã³ ãªã©ã®äžè¬çãªWEBã¢ããªã±ãŒã·ã§ã³ã®æ»æãé²ãããšãã§ããã(2019幎11æã«æ°ãããªãªãŒã¹ããã AWS WAF V2 ã® Managed Rule ãå©çšããWAF V1ã«æ¯ã¹ãã«ãŒã«ã®èšå®ã倧å¹
ã«æ¥œã«ãªã£ãã) Amazon CloudFront ã¯ãäžçäžã«ããŒãããããã倧é㮠垯åå¹
ããããå€ãã® DDOSæ»æ ã軜æžã§ãã ãŸãã Amazon CloudFront ã«é¢ããŠãSPAã«ãããèŠä»¶ããã»ãã¥ãªãã£ã®èгç¹ã§å¯Ÿå¿ããäºé
ãããã®ã§ä»¥äžã§ç޹ä»ããŸãã ãã£ãã·ã¥ã³ã³ ãããŒã« Amazon CloudFront ã®ãã£ãã·ã¥ã³ã³ ãããŒã« æ©èœ(Behaviors)ãæŽ»çšãã Amazon S3 (SPAãµãŒããŒ)ãš Application Load Balancer ( API ãµãŒããŒ)ã®ãã«ããªãªãžã³æ§æã«ããŠããŸããã¯ã©ã€ã¢ã³ãããã®ãªã¯ ãšã¹ ããã¿ãŒã³ãå
ã«ããã£ãã·ã¥ããªã·ãŒããªãªãžã³ãžã®ã¢ã¯ã»ã¹ã«ãŒã«ãåå¥èšå®ããŠããŸããTERAS ã§ã¯ã /api ãšãããªã¯ ãšã¹ ããã¿ãŒã³ã®å Žåã¯ãApplication Load Balancerãžããã®ä»ã®ãªã¯ ãšã¹ ããã¿ãŒã³ã¯ Amazon S3 ãžæ¯ãåããããããã«èšå®ããŠããŸãã ãŸãã /api ã®ãããªåçã³ã³ãã³ãã«é¢ããŠã¯ããã£ãã·ã¥ãããªãããã« TTL ã0ã«èšå®ããŠããŸãã ã«ã¹ã¿ã ãšã©ãŒããŒãž React Routerãªã©SPAåŽã§ã«ãŒãã£ã³ã°ãããŠããå Žåããµã ãã£ã¬ã¯ã ãªå«ãURLã§ãã©ãŠã¶ããªããŒãããããšãS3ã®ãšã©ãŒããŒãžã衚瀺ãããŠããŸããŸããããã¯å¯Ÿè±¡ã® ãã£ã¬ã¯ã ãªãS3 ãã±ãã ã«ååšããªãããšãåå ã§ãã Amazon CloudFront ã® Error Pages ã«403ãŸãã¯404ãšã©ãŒã®å Žåã«ã¯ /index.html ãžè»¢éããSPAåŽã§ã«ãŒãã£ã³ã°ã®å¶åŸ¡ãè¡ããããã«ããŠããŸãã ãªãªãžã³ãµãŒããŒã®ä¿è· ã»ãã¥ãªãã£ã»ããã©ãŒãã³ã¹ã®èгç¹ã«ãããŠãå€éšããçŽæ¥ãªãªãžã³ãžã¢ã¯ã»ã¹ãããããšãé²ãããã§ãã Amazon S3 ã«ã¯ããããã¯ãããªãã¯ã¢ã¯ã»ã¹ãšãããããããã«ãŒã«ããåªå
ãããã¢ã¯ã»ã¹å¶åŸ¡ããªã·ãŒãããã©ã«ãã§æå¹åãããŠããŸããèšå®ãã¹ãªã©ããŒã¿ãæå³ããå
¬éãããããšãé²ãããã«ãåºæ¬ãããã¯ãããªãã¯ã¢ã¯ã»ã¹ã¯æå¹åã®ãŸãŸã§éçšã§ããããã«ãããã§ãã Amazon CloudFront ã® Origin Access Identity ã䜿ãããšã§ã ãããã¯ãããªãã¯ã¢ã¯ã»ã¹ãæå¹åãããŸãŸã Amazon S3 ãžã®ã¢ã¯ã»ã¹ã Amazon CloudFront ããã®ãªã¯ ãšã¹ ãã«éå®ããããšãã§ããŸãã ãŸããApplication Load Balancer ã«ã€ããŠãã Amazon CloudFront ã§ã«ã¹ã¿ã ãããã远å ããApplication Load BalanceråŽã§å€ãäžèŽããã°ãCloudFrontãã転éãããŠãããªã¯ ãšã¹ ããšã¿ãªãããªã¯ ãšã¹ ããèš±å¯ããããšãã§ããŸããããã«ãã£ãŠãApplication Load Balancer ã«ã€ããŠã Amazon CloudFront ããã®ãªã¯ ãšã¹ ãã«éå®ããããšãã§ããŸãã ã¯ãã¹ ãã¡ã€ã³ éä¿¡ãäžèŠ SPAãš API ãµãŒãã飿ºããã¢ããªã±ãŒã·ã§ã³ã ãã¹ãã£ã³ã° ããå ŽåãSPAãµãŒããŒãš API ãµãŒããŒãå¥ ãã¡ã€ã³ ã§ ãã¹ãã£ã³ã° ãããããšããããšæããŸããã Amazon CloudFront ã§ãã«ããªãªãžã³å¯Ÿå¿ããããšã§ãåäž ãã¡ã€ã³ ã§æ±ãããšãã§ãã API ãµãŒããŒåŽã«ãããCORS察å¿ãäžèŠã«ãªããŸããå®è£
ã»æ€èšŒ å·¥æ° ãæãã1ã€ã®èŠå ãšãªããŸããã WEBãµãŒã㌠AWS Fargateã以äžã®çç±ã§å°å
¥ããŸããã ãã¹ã ã€ã³ã¹ã¿ã³ã¹ ã®ç®¡çããè§£æŸ ãªãŒãã¹ã±ãŒã«ã®èšå®ãäžèŠ ã€ãŸããèªåã§ãµãŒããŒã管çããããšãªãã³ã³ãããå®è¡ã§ããŸããEC2ã«æ¯ã¹ãŠãã³ã¹ãã¯å²é«ã§ãã人ç å·¥æ° ã®åæžãèãããšå
ãåããã®ã§ã¯ãªãããšæããŸãã AWS Fargate ã«ã€ããŠã¯ã以äžã® AWS Startup ããã° ã倧å€åãããããã£ãã®ã§èå³ã®ããæ¹ã¯ãã¡ããã芧ãã ããã ã¹ã¿ãŒãã¢ããã®ããã®ã³ã³ããå
¥é â AWS Fargate ç·š ããã〠API ãµãŒããŒ( Amazon ECS) AWS CodePipeline ã§ AWS CodeBuild ãš AWS CodeDeploy ã飿ºããããã€ãããŠããŸãã以äžã®çç±ã§å°å
¥ããŸããã ãããã€ãµãŒããŒã®æ§ç¯ã»éçšã³ã¹ãåæž ãªãŒãã¹ã±ãŒã«ãšã®çžæ§ãè¯ã ECSãšã®çµ±åæ©èœãå
å®ããŠãã Blue Green Deployment Canary Deployment Linear Deployment AWS å
ã§ãããã€ãããŒãå®çµããããšã§ã AWS ã®èªèšŒæ
å ±ãªã©ãå€éšã«å
¬éããå¿
èŠããªãã»ãã¥ã¢ã« ãŸã ã AWS CodePipelineé¢é£ã®ãµãŒãã¹ã®å©çšçµéšãæµ
ãã®ã§èª²é¡ãèŠã€ãããããããŸããããã»ãã¥ãªãã£ã»ã¹ã±ãŒã©ããªãã£ã®èгç¹ãä» AWS ãµãŒãã¹ãšã®èŠªåæ§ã«ãããŠãè¯ãéžæãªã®ã§ã¯ãªãããšæã£ãŠããŸãã SPA( Amazon S3 ) AWS CodeBuild ã§ãã«ããããã®çæç©ã S3 sync ããŠã Amazon CloudFront ã® invalidate ãå®è¡ããã£ãã·ã¥ãã¯ãªã¢ããã ãã§ãã ç§å¿æ
å ±ç®¡ç System Manager ã® Parameter Store ç§å¿æ
å ±ãç»é²ããããšã§ã AWS CodeBuildã Amazon ECS ã§ ç°å¢å€æ° ã«æ©å¯æ
å ±ãæž¡ããŸãã Rails ã®credentialsæ©æ§ã䜿ããããæ¥œã§ããã«ãããããã€æãªã©å¿
èŠãªç§å¿æ
å ±ãåãããŠäžå
管çã§ããã®ã§ãšãŠã䟿å©ã§ã»ãã¥ã¢ã ãšæããŠããŸãã ã€ã³ãã©ã®ã³ãŒãå ãããã®æ§æã AWS CloudFormation ã§äœæããŠããŸãã AWS CodeDeploy ã®ECS Blue Green Deployment ã«ã€ããŠã¯ãæªã AWS CloudFormation 察å¿ããŠããªãããããããžã¡ã³ãã³ã³ãœãŒã«ããæåã§ç»é²ããŠããŸãã ãã® ã¢ãŒããã¯ã㣠ã¯ããããã¯ãåºæã®ç¹æ®ãªæ§æã§ã¯ãªãã®ã§ãä»åŸ WEBã¢ããªã±ãŒã·ã§ã³ã®åºç€æ§ç¯ã®éã«ãåå©çšãããããªã£ãŠããŸãããããŠãä»åŸæ°ãã TERAS ãžåå ããã¡ã³ããŒããã®èšäºãšã€ã³ãã©ã³ãŒããèŠãã°ãå
šäœåãææ¡ããããšãã§ãã®ã§ãã·ã¹ãã ç¥èã®ãã£ããã¢ãããéããªãã®ã§ã¯ãªãããšæåŸ
ããŠããŸãã ãŸãšã TERASã®å
šäœåãšåé
ç®ã«ã€ããŠç޹ä»ãããŠããã ããŸãããã€ã³ãã©æ§ç¯ã»éçšã³ã¹ããæãããã€ã»ãã¥ã¢ã§ã¹ã±ãŒã«ããã·ã¹ãã ãäœãéã®åèã«ãªãã°å¹žãã§ãããã®æ§æã¯ã·ã³ãã«ã§ãæ±çšæ§ãé«ãããã€ã³ãŒãåãããŠããã®ã§ãä»åŸãããã¯ããå¢ããéã«ããå²ãšç°¡åã«åå©çšãã§ããã®ã§ã¯ãªãããšæåŸ
ããŠããŸãã 3æã«ãªãªãŒã¹ããéçšãå§ãŸããŸããããŸã ãŸã 課é¡ã¯ãããããããŸãããŸãããããã¯ããè€æ°ã«ãªã£ãããšã§ãã»ãã¥ãªãã£ã»IAM管çã»ç£èŠäœå¶ãªã©ããããã¯ãããšã«ããªã·ãŒãä¹±ç«ããªãããã«ãæŠç¥ç«ãŠãŠæ±ºããŠããå¿
èŠããããªãšæããŠããŸãã æåŸã« TUNAGãTERASãæ°èŠãããã¯ããæšªæçã«èšèšã»æ¹åããŠãããSREãåéããŠããŸãïŒ ãããã¯ããè€æ°ã«ãªããTUNAG ã®å€§èŠæš¡åã«åããŠäžå±€ã€ã³ãã©é¢ã®åŒ·åãé²ããŠããã¿ã€ãã³ã°ãªã®ã§ããšãŠãããããã®ããé¢çœãä»äºã«ãªããšæããŸããèå³ãæã£ãŠãããæ¹ã¯æ¯éãäžèšã®ãšã³ãžãã¢æ¡çšãµã€ããèŠãŠãã ããïŒ ã¹ã¿ã¡ã³ ãšã³ãžãã¢æ¡çšãµã€ã ã€ã³ãã©ãšã³ãžãã¢åéããŒãž
çæ§ããããéããã§ãããã¹ã¿ã¡ã³ã§ã¢ãã€ã« ã¢ããªéçº ãæ
åœããŠãã @sokume ã§ãã 以åã TUNAG iOSã¢ããªã®æè¡çãªè§£èª¬ ã§ã @temoki ããçºä¿¡ããŠããŸãããèªãã§ããã ããŸããã§ãããããä»åã®èšäºã¯ Android çã®æè¡çãªè§£èª¬èšäºã§ãã èšèª Google I/O 2019ã«ãŠ Kotlin-First 宣èšããããåå®ãšãã« Android ã®ç¬¬1èšèªã¯ Kotlin ã«å€ãããŸããããã¡ããTUNAG Android ã¢ããªããã¹ãŠ Kotlin ã§æžãããŠããŸãã Kotlinãæ¥ã
æŽæ°ããŠããŸãã®ã§ãåžžã«æ°ããããŒãžã§ã³ã«è¿œåŸããŠãããéçºç°å¢ã«ããŠããŸããïŒèšè¿°æç¹ã§ã¯v1.3.61ã§ããïŒ ã¢ãŒããã¯ã㣠㌠Android Jetpack ã«å«ãŸããã Android Architecture Component ãããŒã¹ãšããæ§æããšã£ãŠããŸãã ãã®æ§æããã¹ãŠå©çšããã®ã§ãªããTUNAG Android ã¢ããªã«åããã圢ã§å©çšç®æã®éžå®ãããŠããŸãã ãµãŒãã¹ TUNAG 㯠AWS + Ruby on Rails ã§æ§ç¯ããŠããŸãããmBaaS (mobile backend as a service) ã® Firebase ãå©çšããŠä»¥äžã®å¯Ÿå¿ããããªã£ãŠãããŸãã - Cloud Firestore å©çšãã峿æ§ã®é«ããã£ããæ©èœ - Authentication ãå©çšãããŠãŒã¶ãŒèªèšŒ - Crashlyitics ãå©çšããã¯ã©ãã·ã¥ã¬ããŒãã£ã³ã° - Cloud Messaging ãå©çšããã¢ããªãžã®ããã·ã¥éç¥é
ä¿¡ - App Distribution ãå©çšããã¢ããªã®ããŒã¿é
ä¿¡ ïŒããã®ç¹ã¯ TUNAG iOS ãšäžç·ã®æ§æãšããŠããŸããïŒ ã©ã€ãã©ãª åè¿°ã® Android Jetpack ã Android KTX ã¯ãã¡ããã Material Components UI ã³ã³ããŒãã³ã ã©ã€ãã©ãªãå©çšãã Android ã«é©ããUIã®å®çŸãé²ããŠããŸãã HTTPã¯ã©ã€ã¢ã³ã㯠Retrofit2 ãš Okhttp3 ãå©çšããHTTPã¯ã©ã€ã¢ã³ããšããããŠã JSON ããŒãµãŒã¯ Moshi ãå©çšããŠããŸãã ç»åã®èªã¿èŸŒã¿ã«ãHTTPã¯ã©ã€ã¢ã³ããšããã㊠Picasso ãå©çšããŠããŸãã ïŒSquare 補ã©ã€ãã©ãªãå€ãã§ããïŒ Modelå±€ããã®ããŒã¿ã«ã€ããŠã¯ãªã¢ã¯ãã£ãã«åŠçããããã RxKotlin / RxAndroid / RxJava ãå©çšããŠããŸãã ReactiveExtensionãå©çšããŠããŸãããActivity/FragmentïŒViewå±€ïŒã ViewModel ïŒViewModelå±€ïŒã§ã¯ã©ã€ããµã€ã¯ã«å¯Ÿå¿ã³ã«ãŒãã³ã¹ã³ãŒããå©çšããã©ã€ããµã€ã¯ã«ãæèããåŠçã®å¶åŸ¡ãè¡ããå®è£
ãšããŠããŸãã ããŒã¿ãã¹ã iOS ã¢ããªãšåãããã¹ã¿ã¡ã³ã®ã¡ã³ããŒèªèº«ãTUNAGãå©çšããŠããŸãããã®ããã¢ããªã®æ°ããŒãžã§ã³ã¯åžžã«ç€Ÿå
ã¡ã³ããŒã§ã®ãã¹ãã宿œããŠããŸãã CIã®ä»çµã¿ã¯2019幎11æã«å
¬éããã Github Actions ã®å©çšãæ€èšããŠããŸãã ã¯ãŒã¯ãããŒãšãã圢ã§ã Github ãžã®ã¢ã¯ã·ã§ã³ãèšè¿°ãã Push ã PullRequset ãšãã£ããŠãŒã¶ãŒæäœãããªã¬ãŒã«ã ãã«ãå®è¡ ➡ é
ä¿¡ ãšãã£ãåŠçãç¶ç¶çã«å®æœããäºãã§ããŸãã 瀟å
ã¡ã³ããŒãžã® ããŒã¿ãã¹ã ã®é
ä¿¡ã¯åè¿°ã® Firebase App Distribution ãå©çšããŠé
ä¿¡ããŠããŸãã ãããã« TUNAG Android ã¢ããªã®æ§æã説æãããŠããã ããŸããã TUNAG iOS ã¢ããªãšã¯ãŸã£ããã®å¥ã®ãã®ãšããŠèãããåãæ§æã«ããããšã§åŸãããå©ç¹ãªã©ãèããæ§æã«ããŠããŸãã ãã¡ããã Android ã¢ããªãšããŠè¿œæ±ãã¹ãç¹ã¯åŠ¥åããŸããã ç±ã仲éãšäžç·ã«ãããè¯ããµãŒãã¹ãäžç·ã«äœããŸãããïŒ ä»²éãåéããŠããŸãïŒ ãŸãã¯ãã¹ã¿ã¡ã³ã®ãšã³ãžã ã¢ãµã€ ã stmn, inc. Engineers ãèŠãŠã¿ãŠãã ããïŒ
ããã«ã¡ã¯ïŒ ã¹ã¿ã¡ã³ã§ Rails ã¢ããªã®éçºãæ
åœããŠããã·ã¥ãŒã«( @shule517 )ã§ãïŒ Rails ã¢ããªã±ãŒã·ã§ã³ãå®å
šã«éçºããããã«ãèªåãã¹ãã¯ãšãŠã倧åã§ãããïŒ ã¹ã¿ã¡ã³ã§ã¯CircleCIã§ RSpec ãRuboCopã®å®è¡ãããå®å
šã確èªããŠæ¬çªæ©ãžãããã€ããŠããŸãã CircleCIã®é«éåãè¡ã£ãçµæãåéïŒçŽ20åâ10åïŒã«ããããšãã§ããŸããïŒãã®æã«å¯Ÿå¿ãããã€ã³ãã解説ããŸãã CircleCIã®ãã£ãã·ã¥ãæå¹æŽ»çšïŒ â ã¢ã»ããã㪠ã³ã³ãã€ã« çµæããã£ãã·ã¥ãã CIå
šäœã®åŠçæéã®ãã¡4åã®1ã¯ã¢ã»ããã㪠ã³ã³ãã€ã« ã«æéãããã£ãŠããŸãããã¢ã»ããã㪠ã³ã³ãã€ã« ã®çµæããã£ãã·ã¥ããããšã§5åã»ã©é«éåã§ããŸããã # ããªã³ã³ãã€ã«ã®ããŒãäœæ - run : name : create assets precompile cache key command : | # ããªã³ã³ãã€ã«å¯Ÿè±¡ã®ãã¡ã€ã«ã®ææ°ã³ãããçªå·ãããŒã«ãã git rev-parse $(git log --oneline -n 1 app/assets lib/assets vendor/assets Gemfile.lock | awk '{{print $1}}' ) > VERSION # ãã£ãã·ã¥ã埩垰 - restore_cache : keys : # åãããŒã·ã§ã³ãããã£ãã·ã¥ã埩垰ãç¡ããã°ãçŽè¿ã®ãã£ãã·ã¥ã埩垰ã - assets-precompile-cache-{{ checksum "VERSION" }} - assets-precompile-cache- # ããªã³ã³ãã€ã«ãå®è¡ - run : name : assets:precompile command : | current_revision=VERSION previous_revision=public/assets/VERSION # ããªã³ã³ãã€ã«å¯Ÿè±¡ã®ãã¡ã€ã«ã倿ŽãããŠããªããã°ã¹ããã if [ ! -e $previous_revision ] || ! diff $previous_revision $current_revision; then bundle exec rake assets:precompile cp -f $current_revision $previous_revision else echo "Skipped." fi # ãã£ãã·ã¥ãä¿å - save_cache : key : assets-precompile-cache-{{ checksum "VERSION" }} paths : # ããªã³ã³ãã€ã«çµæã®ä¿åå
ãæå® - public/assets - tmp/cache/assets â»å®éã«äœ¿çšããŠããèšå®ãã¡ã€ã«ãšã¯ç°ãªããŸãã ã¢ã»ããé¢é£ã®ãã¡ã€ã«ã倿ŽãããŠããªãå Žåã¯ã㪠ã³ã³ãã€ã« ãã¹ããããããããããã£ãã·ã¥ã®ããŒãã㪠ã³ã³ãã€ã« 察象ã®ãã¡ã€ã«ã®ææ°ã³ãããçªå·ã«ããŸããããŸããã¢ã»ããé¢é£ã®ãã¡ã€ã«ãä¿®æ£ããŠããå Žåããååãšã®å·®åã ããã㪠ã³ã³ãã€ã« ããããã«ãªãããããªãæéãç¯çŽã§ããŸãã ã㪠ã³ã³ãã€ã« ã®å¯Ÿè±¡ãã¡ã€ã«ãšã㊠Gemfile.lock ãå
¥ããŠããçç±ã¯ãGemãã€ã³ã¹ããŒã«ããæã«ã¢ã»ããé¢é£ã®ãã¡ã€ã«ã倿Žãããããšãããããã§ãã ã¢ã»ããã®å€æŽããªãå Žåãassets:precompileãã¹ãããããããšã§CIã®éåºŠãæ¹åãã ãåèã«ããŸããã â¡ Gemã®ã€ã³ã¹ããŒã«ããã£ãã·ã¥ãã CircleCIäžã§äœ¿ãGemãGemfileã«èšèŒããã€ã³ã¹ããŒã«ããGemããã£ãã·ã¥ããŸããRuboCopã®å®è¡ã«å¿
èŠãªGemããã£ãã·ã¥ãã3åã»ã©éããªããŸããïŒ group :test do # CircleCIã§RuboCopãå®è¡ããããã«å¿
èŠ # ä¿®æ£åã¯CircleCIã®ã¿ã¹ã¯ã§gem installããŠãã gem ' rubocop ' gem ' rubocop-select ' gem ' rubocop-checkstyle_formatter ' end # ãã£ãã·ã¥ã埩垰 - restore_cache : key : rails-gemfile-{{ checksum "Gemfile.lock" }} # Gemãã€ã³ã¹ããŒã« - run : bundle install --path vendor/bundle # ãã£ãã·ã¥ãä¿å - save_cache : key : rails-gemfile-{{ checksum "Gemfile.lock" }} paths : - vendor/bundle restore_cache / save_cache ã® keyã checksum "Gemfile.lock" ãšããããšã§ã Gemfile.lock ã®å
容ãäžèŽããæã ããã£ãã·ã¥ã埩垰ããŸããã€ãŸãã Gemfile ã倿Žããæã ãGemã®ã€ã³ã¹ããŒã«ãå®è¡ãããŸãã CircleCI - ãã£ãã·ã¥èšå®ã®äŸ ãåèã«ããŸããã ⢠ãœãŒã¹ã³ãŒã ã®ãã§ãã¯ã¢ãŠãããã£ãã·ã¥ãã ãœãŒã¹ã³ãŒã ããã§ãã¯ã¢ãŠãããåŸã®.gitãã©ã«ãããã£ãã·ã¥ããŸããããã§ååãšã®å·®åã ãããã§ãã¯ã¢ãŠãããã°ãããªãã30ç§ã»ã©éããªããŸããïŒ # ãã£ãã·ã¥ã埩垰 - restore_cache : keys : - source-v1-{{ .Branch }}-{{ .Revision }} - source-v1-{{ .Branch }}- - source-v1- # ãœãŒã¹ã³ãŒãããã§ãã¯ã¢ãŠã - checkout # ãã£ãã·ã¥ãä¿å - save_cache : key : source-v1-{{ .Branch }}-{{ .Revision }} paths : - ".git" restore_cache ã«æå®ãã keys ãè€æ°æå®ããŠãããšããã®äžããäžçªè¿ããã£ãã·ã¥ããŒã¿ã埩垰ããŠãããŸããä»åã®èšå®æ¹æ³ã ãšãåããã©ã³ãã®ãã£ãã·ã¥ãåªå
ããç¡ããã°çŽè¿ã®ãã£ãã·ã¥ã䜿çšããŸãã CircleCI - ãœãŒã¹ã³ãŒãã®ãã£ãã·ã¥ ãåèã«ããŸããã RSpec ããã©ã³ã¹ãã䞊åå®è¡ããïŒ â ããã«ãã㯠ã«ãªã£ãŠããã³ã³ãããèŠã€ãã CircleCIã®TimingãèŠããšã忣ããŠããåã³ã³ããã®å®è¡æéãèŠãããšãã§ããŸãã16åå°ã«å®äºããŠããã³ã³ããããããŸãããïŒçªé
ãã³ã³ããã«è¶³ãåŒã£åŒµãããŠããŸãå
šãŠãå®äºããã®ã¯22åã§ããåŠçãåçã«åå²ã§ããã°ã20å以å
ã«æžããããã§ããïŒé
ãã³ã³ããããé ã«åæ£ãæ€èšããŸãããã â¡ ããã«ãã㯠ã«ãªã£ãŠããspecãã¡ã€ã«ãèŠã€ãã RSpec ã®å®è¡ãã°ãä¿åããããšã§ãã³ã³ããããšã®åspecãã¡ã€ã«ã«ããã£ãè©³çŽ°ãªæéãèŠãããšãã§ããŸããã©ã®specãã¡ã€ã«ã ããã«ãã㯠ã«ãªã£ãŠããããèŠã€ããŸãã - run : name : bundle exec rspec command : bundle exec rspec --format RspecJunitFormatter --out test-results/rspec.xml - store_artifacts : path : test-results/rspec.xml ⢠RSpec ãé«éåãã ããã«ãã㯠ãè§£æ¶ãã ããã«ãã㯠ã®ã³ã³ããããã¹ããã¡ã€ã«ãç¹å®ãããã RSpec ã®é«éåãããŠãããŸãããïŒé«éåã§å¯Ÿå¿ããå
容ããã¡ãã§ãã äžèŠãªãã¹ãããŒã¿ãäœããªãããã«ãã äžèŠãªãã¹ãããŒã¿äœæã§ããã¹ãã®å®è¡ãé
ããªã£ãŠãã æªäœ¿çšãªãã¹ãããŒã¿ãäœã£ãŠããletãåé€ãã 䜿ãåããªããã¹ãããŒã¿ã®å Žåã¯ã`let!`â`let`ã«å€æŽãã E2Eãã¹ãã®itããŸãšãã 1ã€ã®itã§1ã€ã®expecã«ããã®ã¯ RSpec ãšããŠæ£ããããE2Eãã¹ãã§ã¯æéããããããã åãç»é¢ã確èªããå Žåã¯ãitããŸãšããããšã§é«éåãã itã®äžã§è€æ°ã®expectãå®è¡ããå Žåã¯ã`aggregate_failures`ã䜿ã RSpecã§ãã¹ãããŸãšããŠæ€èšŒããæ¹æ³ ãåèã«ããŠãã ããã E2Eãã¹ãã®sleepãåé€ããhave_xxxã§åŸ
〠ç»é¢æç»ãåŸ
ã€ããã«sleepãããŠããããããã¹ããé
ããªã£ãŠãã ãã¹ãã±ãŒã¹ã®æ° à sleepã®æéã ãç¡é§ãªåŸ
ã¡æéãçºçããŠããŸã sleepã®ä»£ããã«have_xxxãããã£ã䜿ããåŸ
ã¡æéãæå°éã«æãã 巚倧ãªspecãã¡ã€ã«ãåå²ãã RSpec ã®åæ£ã¯ãã¹ããã¡ã€ã«(*_spec.rb)ããšã«è¡ã£ãŠãã é«éåãè¡ã£ãŠãããã¹ãã±ãŒã¹ãå€ããã¹ããã¡ã€ã«ã¯ ããã«ãã㯠ã«ãªã£ãŠããŸã ãã®ããã巚倧ãªspecãã¡ã€ã«ãåå²ããåçã«åæ£ã§ããããã«ãã ãããã« CircleCIã®é«éåãè¡ããåŸ
ã¡æéãæžãããšã§éçºã®ã¹ãã¬ã¹ãããªãæžããŸããïŒã¿ãªãããCircleCIãé«éåããæã«ãåèã«ãªãã°ãšãŠãå¬ããã§ããåŸ
ã¡æéãæžãããŠãããæ¥œããå¹ççã«éçºãããŠãããŸãããïŒ ç±ã仲éãšäžç·ã«ãããè¯ããµãŒãã¹ãäžç·ã«äœããŸãããïŒ ä»²éãåéããŠããŸãïŒ ãŸãã¯ãã¹ã¿ã¡ã³ã®ãšã³ãžã ã¢ãµã€ ã stmn, inc. Engineers ãèŠãŠã¿ãŠãã ããïŒ
ããã«ã¡ã¯ãã¹ã¿ã¡ã³ã§ã¢ãã€ã« ã¢ããªéçº ãããŠãã @temoki ã§ãã ã¹ã¿ã¡ã³ã«ã¯ èªçç¥ ãšããå¶åºŠããããã¡ã³ããŒã®èªçæ¥ã«ã¯åãéšçœ²ã®ã¡ã³ããŒã§ãç¥ããããã®æ§åãåŒç€ŸãæäŸããŠãããµãŒãã¹ TUNAG ã§å
šç€Ÿã«å
±æããŠããŸããçŸåšã¢ã«ãã€ãã§ä»å¹Žã®æ¥ã«æ°åå
¥ç€Ÿãäºå®ããŠãããšã³ãžã ã¢ã¡ã³ã㌠ã®ããã«æšå¹Žã«å®æœããèªçç¥ã§ã¯ããããã¯ãéšãªãã§ã¯ã®æäœãã®ãç¥ããããã®ã§ããã®æ§åããäŒãããããšæããŸãã èšç» TUNAG ã«ã¯ã¡ã³ããŒã®èªçæ¥ãäºåã«ãç¥ããããæ©èœããããŸãããã®ãç¥ãããããªã¬ãŒã«æ¬äººãé€ããã¡ã³ããŒã§ç€Ÿå
ã®ãã£ããã°ã«ãŒããäœããããã§ã²ã£ãããšæºåãå§ãŸããŸãã ã¹ã¿ã¡ã³ã®äŒæ¥ç念㯠äžäººã§ãå€ãã®äººã«ãæåãå±ãã幞ããåºããã ã§ãããã®ç念ã«ã€ããŠåŒç€Ÿä»£è¡šã®å è€ã¯ãæåã¯çžæã®æåŸ
ãè¶
ããããšã§çãŸããããšããç§ãã¡ã«äŒããŸãããã®èªçç¥ã§ãæåŸ
ãè¶
ãããç¥ããã§ããããã«ããã®äººãæè¿è©±ããŠããããšããTUNAG ã®ãããã£ãŒã«ãæè¿ã®æçš¿ã®å
容ãªã©ã®æ
å ±ãéããªããããã¬ãŒã³ãã¯äœã«ãããïŒã©ããã£ãŠç¥ããïŒã®ã¢ ã€ã㢠ãåºããŠãããŸãã ä»åãç¥ãããã¡ã³ããŒã¯ Android ã¢ããªãšã³ãžãã¢ã§ãã圌ã®ãã¹ã¯ã«ã¯ããã€ãåãã£ã®ã¥ã¢ã眮ããŠãã£ãããå人ã§ã Android ã¢ããªããªãªãŒã¹ããŠããããšã Android ã¢ããªéçº ãžã®æããšãŠã匷ãã§ããããã§ã Android ãããŒãã«çã§ã¢ ã€ã㢠ãåºãåããæçµçã«ã¯æ¬¡ã®ããã«èšç»ããŸããã ãã¬ãŒã³ã㯠Boothã®ããã€ãåãã£ãã ãã®ãã¬ãŒã³ããããã€ãåããæž¡ãïŒïŒ ããŒã¹ããŒã±ãŒãã®ä»£ããã« Android ã®ãããŸã§ã®ããŒãžã§ã³ã®ã³ãŒãããŒã ãšãªã£ããèåãå
šãŠæãã æºå èšç»ãå
ã«ãåã¡ã³ããŒãæºåãé²ããŠãããŸãããã¬ãŒã³ãã¯çºæ³šããã ãã§ãããæ®ãã®ïŒã€ã®ã¢ ã€ã㢠ã倧å€ã§ãã ããã€ãåãã«ã¡ãããäœã ãã¬ãŒã³ããããã€ãåããæž¡ãããã«ãããã€ãåãªããããã«ã¡ãããäœãããšã«ããŸãããå
ãã¿ã¯ã³ãã©ïŒ ãã®ããã€ããããã«ã¡ãããèšèšæžä»ãã§èª°ã§ãæäœãå¯ ïŒã§ãã æ±æ¥ãã³ãº ã100åã·ã§ããããŸãã£ãŠææãéããæ¬äººãããªãæãçã£ãŠå¶äœãé²ããŠãããŸããã åçç¶ãšãã£ã¹ã¯ç¶ã®çºç ²ã¹ããŒããŒã«ãçµã¿åãããŠæŽåœ¢ ããã€ãåã®ç·è²ã«è¿ãæ°Žæ§ã¢ã¯ãªã«å¡æã§å¡ã£ãŠãã å¡ãã ã©ããªãããã«å
¥å¿µã«ãã§ã㯠ãã¶ã€ããŒã®æã§ç®ãå
¥ããŠåœãå¹ã蟌ã Android ã®ãããŸã§ã®ããŒãžã§ã³ã®ã³ãŒãããŒã ãšãªã£ããèå Android 㯠2009幎ã«ãªãªãŒã¹ãããããŒãžã§ã³ 1.5 ãããé æåãã¢ã«ãã¡ãããé ã§å§ãŸããèåã®ååãä»ããããŠããŸããæ¯å¹Ž Android ã®æ°ããŒãžã§ã³ã®ãªãªãŒã¹ææãè¿ã¥ããšã Android ã¢ããªéçº è
ã®éã§ã¯æ¬¡ã®ããŒãžã§ã³ã®ãèåã¯äœããäºæž¬ãã話é¡ã§çãäžãããŸããã ãã®ã³ãŒãããŒã ãæšå¹ŽãªãªãŒã¹ããã Android 10 ã§å»æ¢ãããããšãããããã®èªçç¥ã®ãç¥ãã®ã±ãŒãã®ä»£ããã«ããããã®ãèåãå
šãŠæããŠã¿ãããšããããšã«ãªããŸããã忥ã«ã¡ã³ããŒã§ããããªãåºãåã£ãŠãªããšãå
šãŠéããããšãã§ããŸããã åè (List of Android version names) Android ver. ãèå 1.5 C upcake ( ã«ãã ã±ãŒã) 1.6 D onut (ããŒãã) 2.0, 2.1 E clair (ãšã¯ã¬ã¢) 2.2 F royo ( ãããŒãºã³ãšãŒã°ã«ã ) 2.3 G ingerbread (ãžã³ãžã£ãŒãã¬ãã) 3.0, 3.1, 3.2 H oneycomb (ã¯ã¡ã¿ã€) 4.0 I ce Cream Sandwich (ã¢ã€ã¹ã¯ãªãŒã ã»ãµã³ãã€ãã) 4.1, 4.2, 4.3 J elly Bean (ãžã§ãªãŒã» ããŒã³ ) 4.4 K itKat ( ãããã«ãã ) 5.0, 5.1 L ollipop ( ããªããã ) 6.0 M arshmallow (ãã·ã¥ãã) 7.0, 7.1 N ougat (ãã¬ãŒ) 8.0, 8.1 O reo (ãªã¬ãª) 9.0 P ie (ãã€) åœæ¥ èªçç¥ã®åœæ¥ããªãã£ã¹ããã¢ã«çªç¶ããŒã¹ããŒãœã³ã°ãæµããŠã€ãã³ããå§ãŸããŸãããŸãã¯ããŒã¹ããŒã±ãŒãã®ä»£ããã® Android ã¹ã€ãŒãã®ç»å Žã§ãããããã㯠Cupcake ( Android 1.5) ã«ãããŠæ¶ããŠããããŸããïŒ æ¬äººãé©ããé ããªãæ§å ããããšäžŠãã Android ã¹ã€ãŒã ãããŠããããªããµãã©ã€ãºã ã¹ãã·ã£ ã«ã²ã¹ããšããŠããã€ãåïŒããã¶ã£ãã¡ã³ããŒïŒãç»å ŽïŒããã€ãåã«ãªããã£ãŠãã¬ãŒã³ããæž¡ããŸããã ããã€ãåã¯èžããåŸæ ã ãïŒãšããããªã§è¯éºïŒãªãã³ã¹ãæ«é²ããŠãããŸãããã I am Droid. æåŸã«æ¬äººããäžèšããã£ãŠèªçç¥ã¯çµäºã§ãããã®èªçç¥ãæ¬åœã«åãã§ãããŠãæ¥å¹Žããã®æ£åŒå
¥ç€Ÿã«åããŠã®ææ°èŸŒã¿ãç±ãèªã£ãŠãããŸããïŒãç¥ãããã¡ã³ããŒããæ¬äººã®ç¬é¡ãèŠãŠãšãŠãæºè¶³ã§ãã ãããã« ä»åã®ãšã³ãžãã¢ããã°ã§ã¯ãæ®æ®µã¯ããŸããäŒãã§ããŠããªããããã¯ãéšã®ããŒããã«ãªãšããããäŒãããŸãããããããã§ããã§ããããããããã¯ãéšã§ã¯æè¡ããããã¯ãã¯ãã¡ãããããŒã ã仲éã²ãšãã²ãšãã«çå£ã«åãåããªããéçºãé²ããŠããŸããåŒç€Ÿã§ã¯ãã®ããŒã ã®äžã§äžç·ã«åããŠããããšã³ãžãã¢ãåéããŠããŸãããèå³ã®ããæ¹ã¯ãã²ã¹ã¿ã¡ã³ã®ãšã³ãžã ã¢ãµã€ ã stmn, inc. Engineers ãã芧ãã ããã
ã¹ã¿ã¡ã³ã§ãšã³ãžãã¢ãããŠãã ç°äž ã§ãã çããã¯Jay Fieldsã®èã ãªãã¡ã¯ã¿ãªã³ã° ïŒ Ruby ãšãã£ã·ã§ã³ããšããæ¬ããåãã§ããããïŒ ãªãã¡ã¯ã¿ãªã³ã° ã«ãããæåãªæ¬ãšããŠãMartin Fowlerã®èã ãªãã¡ã¯ã¿ãªã³ã° ããšããæ¬ã¯ãåãã®æ¹ãå€ãããšæããŸãã ãã¡ãã¯åœæåºãç¥ãããŠãã Java (åç), JavaScript (第äºç)ã§ãµã³ãã«ã³ãŒããæžãããŠããŸããã ã ãªãã¡ã¯ã¿ãªã³ã° ïŒ Ruby ãšãã£ã·ã§ã³ãã¯åèã§ããã ãªãã¡ã¯ã¿ãªã³ã° ãã Ruby ã«å¯Ÿå¿ããããã®ã§ã Ruby ã®ç¹åŸŽã掻ããã ãªãã¡ã¯ã¿ãªã³ã° ã®æ¹æ³ãèŒã£ãŠãããäžåºŠã¯èªãã§ã¿ãããšæã£ãŠããŸãããããããªããããã¡ãã®æ¬ã¯çŸåšçµ¶çã®ããå
¥æããããšãé£ããã§ãã ä»åãçžããã£ãŠãã¡ãã®æ¬ããåãããæ©äŒããããŸããã®ã§ããã®å
容ã«ã€ããŠã玹ä»ããŸãã ãªãã¡ã¯ã¿ãªã³ã° ãããäžã§å€§åãªããš ããã€ãè¯ããªãšæã£ãæç« ããã£ãã®ã§åŒçšã®åœ¢ã§ã玹ä»ããŸãã ãªãã¡ã¯ã¿ãªã³ã° ã«ã€ã㊠ãªãã¡ã¯ã¿ãªã³ã° ãšã¯ãã³ãŒãã®å€ãããµããŸããå€ããã«ãå
éšæ§é ãæ¹è¯ããããã«ããŠããœãããŠã§ã¢ã·ã¹ãã ãå€ããŠããããã»ã¹ã§ããã ã ã³ã³ãã¥ãŒã¿ãçè§£ã§ããã³ãŒããªã誰ã§ãæžãããåªãã ããã°ã©ã ãæžãã®ã¯ã人éãçè§£ã§ããã³ãŒãã ã ã ããªãã ãªãã¡ã¯ã¿ãªã³ã° ãããŠããã®ã¯ãçå®ãçŸã远æ±ããããã§ã¯ãªããäžçãããããããããŠãã°ãã°ããšæºãåããŠããããã°ã©ã ã®æ¯é
æš©ãåã³æ¡ãããã«åªåããŠããã®ã§ããã ãã¹ãã«ã€ã㊠ãªãã¡ã¯ã¿ãªã³ã° ãå§ããåã«ããã£ãããšãããã¹ããå¿
èŠã ã ã ãªãã¡ã¯ã¿ãªã³ã° ã§ã¯ãã¹ããåœç¶±ã«ãªãã ãªãã¡ã¯ã¿ãªã³ã° ãæåãããã©ããã¯ãããã¹ããçšæã§ããŠãããã©ããã«ãã£ãŠå·Šå³ãããã ããã©ãŒãã³ã¹ãšã®å
Œãåã ãªãã¡ã¯ã¿ãªã³ã° ãããŠãããšãã¯ããããããããããšã«éäžãã¹ãã§ãããã©ãŒãã³ã¹ããããããšã¯ãã®åŸã§å¥ã®ä»äºãšããŠè¡ãã®ãæ£ããæ¹æ³ã§ããã 倧åãªããšã¯ ãµããŸããå€ããªã ãµããŸããä¿èšŒããããã«ãã¹ããæžã ããã©ãŒãã³ã¹ã¯ãŸãå¥ã®ã¿ã¹ã¯ãšããŠèãã ç§èªèº«ã¯ ãªãã¡ã¯ã¿ãªã³ã° ãšããã©ãŒãã³ã¹ã®åœ¹å²ã®éãã«ã€ããŠã¯ç¹ã«æèããªããšãããªããšæããŸããã ãªãã¡ã¯ã¿ãªã³ã° æã«ããã©ãŒãã³ã¹ã䜵ããŠèããŠããŸã£ãŠããŸããããããããã®ç®çãç°ãªãã®ã§æžãã¹ãã³ãŒãã¯éããã®ã§ãããŸã㯠ãªãã¡ã¯ã¿ãªã³ã° ã«ãã£ãŠåãããããããäžã§å¿
èŠã«å¿ããŠããã©ãŒãã³ã¹ãã¥ãŒãã³ã°ãšãã£ãæµããæèããŠããããã§ãã å
·äœçãªææ³ æ°å€ãã®æ¹æ³ã玹ä»ãããŠããŸãããããã§ã¯3ã€ã«çµã£ãŠç޹ä»ããŸãã ãµã³ãã€ããã¡ãœããã®æœåº ã»ãŒåãå
容ã®ã³ãŒãã®ã¡ãœããã2ã€ããããããã®éããã¡ãœããã®ã¡ããã©äžé ã®ã¿ã®å Žåã«ãéè€éšåãæœåºããŠãããã¯ä»ãã®ã¡ãœããã«ããã ãµã³ãã« å€æŽå def charge (amount, credit_card_number) begin connection = CreditCardServer .connect(...) connection.send(amount, credit_card_number) rescue IOError =&gt; e Logger .log " Could not submit order #{ @order_number } to the server: #{ e }" return nil ensure connection.close end end 倿ŽåŸ def charge (amount, credit_card_number) connect do | connection | connection.send(amount, credit_card_number) end end def connect begin connection = CreditCardServer .connect(...) yield connection rescue IOError =&gt; e Logger .log " Could not submit order #{ @order_number } to the server: #{ e }" return nil ensure connection.close end end ååŠçãšåŸåŠçãå¿
èŠãªã¡ãœããã§å
·äœçãªåŠçã®äžèº«ãå€ãããå Žåã«æŽ»çšã§ãããã ãšæããŸããããããã¯ãå©çšããæžãæ¹ã Ruby ã£ãœãããŸããã®æžãæ¹ãç§èªèº«ããŸã䜿ãããªããŠããªããšæã£ãã®ã§ã玹ä»ããŸããã æ¡ä»¶åå²ã®çµã¿æã Ruby ã§ã¯ nil ã¬ãŒããšããæžãæ¹ãããã®ã§ã äžé
æŒç®å ã§æžããã« Ruby ãããæžãæ¹ãããããšããããšã§ããããã®ä»ã«ã Ruby ã§æäŸãããŠããèšæ³ã«åãããšã§ããã·ã³ãã«ã«æžãããã ãšæããŸããã ãµã³ãã« å€æŽå paraneters = params ? params : [] 倿ŽåŸ parameters = params || [] ä»ã®èšèªãçµéšããŠãããšãã€ã Ruby ããããå¿ããæžãæ¹ãããŠããŸããã¡ãªã®ã§ãæ°ãã€ããªããšãããªããšæããŸããã æ¡ä»¶åå²ã®ãã¹ãããã¬ãŒãç¯ãž æ£åžžãªå®è¡çµè·¯ãåããã«ããæ¡ä»¶åå²ãæã€ã¡ãœãããããå Žåã«ããã¹ãŠã®ç¹æ®æ¡ä»¶ãã¬ãŒãç¯ã§åŠçããã ãµã³ãã« å€æŽå def pay_amount if @dead result = dead_amount else if @separated result = separated_amount else if @retired result = retired_amount else result = normal_amount end end end return result; end 倿ŽåŸ def pay_amount return dead_amount if @dead return separated_amount if @separated return retired_amount if @retired normal_amount end æ©æreturnãšåŒã°ããŠããæ¹æ³ã§ãããå
šãŠã®ã±ãŒã¹ã«ãŠé©çšããŠè¯ããšããããã§ã¯ãããŸããã ããšãã°ãæ¡ä»¶åå²ããããšãã«äž¡æ¹ãšãæ£åžžãªãµããŸãã§ããå Žåã¯ã¬ãŒãç¯ãžç§»åããŠã¯ãããŸããã ãããŸã§ãç°åžžç³»ïŒç¹æ®æ¡ä»¶ïŒã¯æ©æreturnããããšããèãã®ããšã§äžèšã®ãã㪠ãªãã¡ã¯ã¿ãªã³ã° ãè¡ãå¿
èŠãããã®ã ãšæããŸããã ãããã« ãªãã¡ã¯ã¿ãªã³ã° ãš Ruby ã«ãããå
·äœçãªæ¹æ³ã«ã€ããŠãã ãªãã¡ã¯ã¿ãªã³ã° ïŒ Ruby ãšãã£ã·ã§ã³ããããšã«ã玹ä»ããŸããã Ruby ããããæŽ»ãããæžãæ¹ãå€ãããåèã«ãªããŸãããä»åããã€ãã®æ¹æ³ãã玹ä»ããŸãããããã®ä»ã«ãæ§ã
ãªæ¹æ³ãèŒã£ãŠããŸããããããä»ã®ç§ã«ãã¹ãŠã®æ¹æ³ãå¿
èŠããšèšããããšãããšã¯èšãåããŸãããæ±ããœãããŠã§ã¢ã®èŠæš¡ã®å€§ãããç¶æ
ã«ãã£ãŠãäœãå¿
èŠãã¯å€ãã£ãŠãããã®ã ãšæããŸããå¿
èŠã«ãªãæ©äŒã¯ä»åŸå¿
ããããšæãã®ã§ããã®ãšãã«å床æã«ãšã£ãŠèªã¿è¿ããããšæããŸãã ãã ãåé ã«ããäŒããããšããããã¡ãã®æ¬ã¯çŸåšçµ¶çã®ããå
¥æããããšããšãŠãé£ããã§ãã 埩åãããã³ã ãšãããµã€ãã«ãŠãªã¯ ãšã¹ ãããããšããªã¯ ãšã¹ ãæ°ã«ãã£ãŠã¯åŸ©åãããããšããããããªã®ã§ããèå³ããæ¹ã¯ãªã¯ ãšã¹ ãããŠããã ãããšå¹žãã§ãã( ãã¡ã ããã©ãã) æ¬èšäºãå·çäžã«åŸ©åãããŸããïŒïŒïŒå
ã
ã®è²©å£²äŸ¡æ Œãããé«ãã§ã¯ãããŸããããããŸã§ãªããªãæã«å
¥ããªãã£ãããšãèãããšæ°åã§ãã®äŸ¡æ Œã¯ããªããè²·ãåŸã«æããŸãïŒïŒ(ç§ã¯æ³šæããŸããïŒïŒ) ãèå³ããæ¹ã¯ ãã¡ã ããã©ããïŒïŒïŒ æåŸã«ãªããŸãããæ ªåŒäŒç€Ÿã¹ã¿ã¡ã³ã§ã¯äžç·ã«åããšã³ãžãã¢ãåéããŠããŸãããèå³ã®ããæ¹ã¯ãã² ãšã³ãžãã¢æ¡çšãµã€ã ãã芧ãã ããã Photo by João Silas on Unsplash