Pro Yearly is on sale from $80 to $50! »

小鳥さんとアイドルのパーソナルカラーを検索できるWebアプリを100%Kotlinで作る

7c3b3366947123ba6772698b09edf4e2?s=47 subroh_0508
September 26, 2020

 小鳥さんとアイドルのパーソナルカラーを検索できるWebアプリを100%Kotlinで作る

IM@S ENGINEERS ON@IR!!!! 2020のトーク資料です

https://imas.connpass.com/event/186165/

7c3b3366947123ba6772698b09edf4e2?s=128

subroh_0508

September 26, 2020
Tweet

Transcript

  1. খௗ͞ΜͱΞΠυϧͷύʔιφϧΧϥʔΛ ݕࡧͰ͖Δ8FCΞϓϦΛ ,PUMJOͰ࡞Δ *.!4&/(*/&&340/!*3 ʹ͜͠Γ͞ͿΖʙ!TVCSPI@

  2. "CPVU.F 2 ʹ͜͠Γ͞ͿΖʙ ✦גࣜձࣾ#FBS5BJM  "OESPJE8FC  ܦඅਫ਼ࢉ4BB4αʔϏεͷ։ൃ ✦୲౰ ŧŽŕ

  3. "CPVU.F 3 ʹ͜͠Γ͞ͿΖʙ ✦גࣜձࣾ#FBS5BJM  "OESPJE8FC  ܦඅਫ਼ࢉ4BB4αʔϏεͷ։ൃ ✦୲౰ ŧŽŕ

    ܦඅਫ਼ࢉ͕ۤखͳ࿀৊Λ ٹ͏ϓϩμΫτΛ࡞ͬͯ·͢
  4. "CPVU.F ˒,PUMJO͕େ޷͖ʂ  ,POUSJCVUPSʹͳͬͨ 4 ˞,PUMJOͷ$POUSJCVUPSͷ͜ͱ γϟχOEʹʜߦ͘͸ͣͩͬͨʜ

  5. "CPVU.F ˒,PUMJO͕େ޷͖ʂ  ,POUSJCVUPSʹͳͬͨ 5 ˞,PUMJOͷ$POUSJCVUPSͷ͜ͱ γϟχOEʹʜߦ͘͸ͣͩͬͨʜ ͱ͞Μ͸ಉҰͷଘࡏ ࣗ໌ 

    ˣ Λߏ੒͢Δ໿ߦΛॻ͍ͨ ˣ ͞ΜΛߏ੒͢Δ໿ߦ͸๻͕ॻ͍ͨʂʂʂ
  6. ໨࣍ 㾎,PUMJO੡8FCΞϓϦʮ$0-03.!45&3ʯͷ঺հ 㾎࣮૷ղઆ ,PUMJO+4ͷྑ͍ͱ͜ΖɾͭΒ͍ͱ͜Ζ 㾎,PUMJO+4ͷࠓޙͷల๬ 6

  7. ໨࣍ 㾎,PUMJO੡8FCΞϓϦʮ$0-03.!45&3ʯͷ঺հ 㾎࣮૷ղઆ ,PUMJO+4ͷྑ͍ͱ͜ΖɾͭΒ͍ͱ͜Ζ 㾎,PUMJO+4ͷࠓޙͷల๬ 7 ,PUMJO+4Ͱͷ8FCΞϓϦ࣮૷ গ͠Ͱ΋ڵຯΛ࣋ͬͯ͘ΕΕ͹ʜ

  8. ໨࣍ 㾎,PUMJO੡8FCΞϓϦʮ$0-03.!45&3ʯͷ঺հ 㾎࣮૷ղઆ ,PUMJO+4ͷྑ͍ͱ͜ΖɾͭΒ͍ͱ͜Ζ 㾎,PUMJO+4ͷࠓޙͷల๬ 8

  9. $0-03.!45&3 ֓ཁ  ΞΠυϧΛݕࡧ ΠϝʔδΧϥʔΛϓϨϏϡʔͰ͖ΔΞϓϦ  8FCΞϓϦ൛͕طʹϦϦʔεࡁʂ  "OESPJEJ04൛։ൃத 

    ϨϙδτϦͷ,PUMJO཰ 9 63-JNBTDPMPSNBTUFSXFCBQQ (JU)VCTVCSPIDPMPSNBTUFS ˞΄΅খௗ͞ΜͱಉҰͷଘࡏ
  10. $0-03.!45&3 ػೳ  ϓϨϏϡʔϞʔυ  ݕࡧͨ͠ΞΠυϧͷΠϝʔδΧϥʔͷҰཡදࣔ  ோΊΔʂʂʂָ͍͠ʂʂʂ  ϖϯϥΠτɾέϛΧϧϥΠτͷ৭֬ೝ

     8FCαΠτɾΞϓϦ౳ͷΧϥʔςʔϚݕ౼ 10
  11. $0-03.!45&3 ػೳ  ϓϨϏϡʔϞʔυ  ݕࡧͨ͠ΞΠυϧͷΠϝʔδΧϥʔͷҰཡදࣔ  ோΊΔʂʂʂָ͍͠ʂʂʂ  ϖϯϥΠτɾέϛΧϧϥΠτͷ৭֬ೝ

     8FCαΠτɾΞϓϦ౳ͷΧϥʔςʔϚݕ౼ 11
  12. $0-03.!45&3 ػೳ  ϓϨϏϡʔϞʔυ  ݕࡧͨ͠ΞΠυϧͷΠϝʔδΧϥʔͷҰཡදࣔ  ோΊΔʂʂʂָ͍͠ʂʂʂ  ϖϯϥΠτɾέϛΧϧϥΠτͷ৭֬ೝ

     8FCαΠτɾΞϓϦ౳ͷΧϥʔςʔϚݕ౼ 12 എܠ৭౮৫   ΞΫηϯτΧϥʔ  ਅ೫ ''#"%  ΞΫηϯτΧϥʔ  Ί͙Δ ''&  㱤͜ͷεϥΠυ͸࣮࣭Πϧϛωʔγϣϯελʔζ
  13. $0-03.!45&3 ػೳ  ϖϯϥΠτϞʔυ  બ୒ͨ͠ΞΠυϧͷΠϝʔδΧϥʔΛશը໘දࣔ 13 બ୒ λοϓ

  14. $0-03.!45&3 ػೳ  ϖϯϥΠτϞʔυ  બ୒ͨ͠ΞΠυϧͷΠϝʔδΧϥʔΛશը໘දࣔ 14 બ୒ λοϓ ͋ͳͨͷεϚϗ͕

    ͦͷ৔ͰϖϯϥΠτʹૣมΘΓʂ ΞχΫϥɾ%+όʔͰʮϖϯϥ๨Εͨʯ ͜Μͳ൵ܶͱ͸΋͏͓͞Β͹ʂ
  15. $0-03.!45&3 ػೳ  ͦͷଞ  ͔ͳΓਅ໘໨ʹ ϨεϙϯγϒରԠ  μʔΫςʔϚରԠ 

    ଟݴޠରԠ ೔ຊޠPSӳޠ 15 μʔΫςʔϚ μʔΫςʔϚ ӳޠදࣔ ͜͜·Ͱਅ໘໨ʹ࣮૷ͨ͠,PUMJO+4੡ΞϓϦ ଞʹଘࡏ͠ͳ͍ͷͰ͸ʜ ˞ଞʹଘࡏͨ͠Βࢀߟʹ͍ͨ͠ͷͰڭ͑ͯཉ͍͠ ੾࣮
  16. ໨࣍ 㾎,PUMJO੡8FCΞϓϦʮ$0-03.!45&3ʯͷ঺հ 㾎࣮૷ղઆ ,PUMJO+4ͷྑ͍ͱ͜ΖɾͭΒ͍ͱ͜Ζ 㾎,PUMJO+4ͷࠓޙͷల๬ 16

  17. ࣮૷ղઆ,PUMJO+4ͱ͸ʜʁ ,PUMJO.VMUJQMBUGPSN .11   ,PUMJO੡ͷ91MBUϑϨʔϜϫʔΫ  ʮϩδοΫͷڞ௨Խʯ΁ͷϑΥʔΧε͕ಛ௃  +7./BUJWF+4ίʔυͷग़ྗ͕Մೳ

    17 +4ίʔυͷग़ྗˠ,PUMJO+4 ϥΠϒϥϦ͸,PUMJO੡ +4੡ͷϞϊ͕ར༻Մʂ ˞,PUMJO੡ϥΠϒϥϦ͸ཁ.11ରԠ 3FGFSFODFLPUMJOMBOHPSHEPDTSFGFSFODFNVMUJQMBUGPSNIUNM
  18. ࣮૷ղઆ$0-03.!45&3 ར༻ϥΠϒϥϦ,PUMJO  ,UPS)UUQΫϥΠΞϯτ  LPUMJOYTFSJBMJ[FS+40/γϦΞϥΠβσγϦΞϥΠβ  ,PUMJO$PSPVUJOFTඇಉظॲཧ  ,PJO%FQFOEFODZ*OKFDUJPOϥΠϒϥϦ

     LPUMJOXSBQQFST3FBDUɾTUZMFEDPNQPOFOU౳ͷ,PUMJOϥούʔ  ,PUMJO.BUFSJBM6*.BUFSJBM6*ͷ,PUMJOϥούʔ 18 (JU)VCTVCSPILPUMJONBUFSJBMVJ 4UBSΛ͘ΕΔͱتͼ·͢ʜ
  19. ࣮૷ղઆ$0-03.!45&3 ར༻ϥΠϒϥϦ+4  3FBDUϝΠϯϑϨʔϜϫʔΫ  .BUFSJBM6*6*ϑϨʔϜϫʔΫ  TUZMFEDPNQPOFOUT$44JO+4ϥΠϒϥϦ  3FBDU3PVUFSϧʔςΟϯάϥΠϒϥϦ

     SFBDUJOFYUݴޠϦιʔε੾Γସ͑  XFCQBDLDEOQMVHJOIUNMXFCQBDLQMVHJOόϯυϧαΠζ࡟ݮ 19
  20. ࣮૷ղઆ$0-03.!45&3 ,PUMJO+4੡8FCΞϓϦΛϦϦʔε͢Δ·Ͱ  ֤छϥΠϒϥϦΛ࢖͍ɺΠΠײ͡ʹ6* ϩδοΫΛ࣮૷͢Δ  ,PUMJOίʔυΛ+BWBTDSJQUίʔυʹม׵͢Δ  (SBEMFͷ୲౰Օॴɺ,PUMJOϥΠϒϥϦͷґଘੑղܾ 

    +BWBTDSJQUͷίʔυΛͭͷόϯυϧϑΝΠϧʹ·ͱΊΔ  8FCQBDLͷ୲౰Օॴɺ+4Ϟδϡʔϧͷґଘੑղܾ NJOJGZ 20 ϦϦʔε ˞$0-03.!45&3͸ ɹ'JSFCBTF)PTUJOHΛར༻
  21. ࣮૷ղઆ$0-03.!45&3 ϩδοΫ෦෼  TIBSFEϞδϡʔϧ಺ʹ.11ରԠͷܗࣜͰ࣮૷  8FC"OESPJEJ04Ͱ࢖͍ճͤΔΑ͏ʹ 21 㾎DPNQPOFOUTˠ%*ؔ࿈ͷϝιουɾΫϥε 㾎JOGSBBQJˠ"1*ͷΤϯυϙΠϯτఆٛ 㾎JOGSBRVFSZˠJN!TQBSRMͷΫΤϦఆٛ

    㾎JOGSBSFQPTJUPSZˠϏδωεϩδοΫͷهड़ $36%ͱର  㾎NPEFMˠΤϯςΟςΟɾ஋ΦϒδΣΫτͷఆٛ 㾎VUJMJUJFTˠศརؔ਺ͷஔ͖৔ॴ JN!TQBSRM࢖ͬͯ·͢ʂ ͋Γ͕αϯΩϡʔʂ
  22. ࣮૷ղઆ$0-03.!45&3 ίϯϙʔωϯτ࣮૷  "UPNJD%FTJHOͬΆ͍σΟϨΫτϦߏ੒  ঢ়ଶ؅ཧ͸DPOUBJOFSTʹू໿ 22

  23. ࣮૷ղઆ$0-03.!45&3 ίϯϙʔωϯτ࣮૷  "UPNJD%FTJHOͬΆ͍σΟϨΫτϦߏ੒  ঢ়ଶ؅ཧ͸DPOUBJOFSTʹू໿ 23

  24. ࣮૷ղઆ$0-03.!45&3 ίϯϙʔωϯτ࣮૷  "UPNJD%FTJHOͬΆ͍σΟϨΫτϦߏ੒  ঢ়ଶ؅ཧ͸DPOUBJOFSTʹू໿ 24

  25. ࣮૷ղઆ$0-03.!45&3 ίϯϙʔωϯτ࣮૷  ͭͭͷίϯϙʔωϯτ͸'VODUJPOBM$PNQPOFOUͱͯ͠ఆٛ 25 val chipComponent = functionalComponent<ChipProps> {

    props -> val classes = useStyles() chip { attrs { classes(classes.root) label { +props.label } color = ChipColor.primary variant = if (props.isChecked) ChipVariant.default else ChipVariant.outlined onClickFunction = { props.onClick?.invoke(it) } } } } const ChipComponent = props => { const classes = useStyles(); return ( <Chip classes={ classes.root } label={ props.label } color='primary' variant={ props.isChecked ? 'default' : 'outlined' } onClick={ e => props.onClick(e) } /> ); } ྫ$IJQίϯϙʔωϯτ +49ͬΆ͞Λ࢒ͨ͠จ๏Ͱ ίϯϙʔωϯτఆ͕ٛͰ͖Δʂ ˞͜͜਺೥ͷ3FBDUͰओྲྀͷίϯϙʔωϯτఆٛ
  26. ࣮૷ղઆ$0-03.!45&3 ঢ়ଶ؅ཧ 26 private val IdolSearchContainerImpl = functionalComponent<RProps> { val

    controller = useContext(IdolSearchControllerContext) val (uiModel, dispatch) = useReducer(reducer, UiModel.Search.INITIALIZED) fun onChangeIdolName(filters: Filters, name: String?) = dispatch(actions(...)) fun onSuccess(items: List<IdolColor>) = dispatch(actions(...)) fun onFailure(e: Throwable) = dispatch(actions(...)) fun IdolSearchController.search(filters: Filters = Filters.Empty) = launch { runCatching { fetchItems(filters) } .onSuccess(::onSuccess) .onFailure(::onFailure) } useEffectDidMount { controller.search() } useDebounceEffect(uiModel.filters, 500) { controller.search(it) } ... } ྫΞΠυϧݕࡧը໘ͷ$POUBJOFS$PNQPOFOU
  27. ࣮૷ղઆ$0-03.!45&3 ঢ়ଶ؅ཧ 27 private val IdolSearchContainerImpl = functionalComponent<RProps> { val

    controller = useContext(IdolSearchControllerContext) val (uiModel, dispatch) = useReducer(reducer, UiModel.Search.INITIALIZED) fun onChangeIdolName(filters: Filters, name: String?) = dispatch(actions(...)) fun onSuccess(items: List<IdolColor>) = dispatch(actions(...)) fun onFailure(e: Throwable) = dispatch(actions(...)) fun IdolSearchController.search(filters: Filters = Filters.Empty) = launch { runCatching { fetchItems(filters) } .onSuccess(::onSuccess) .onFailure(::onFailure) } useEffectDidMount { controller.search() } useDebounceEffect(uiModel.filters, 500) { controller.search(it) } ... } ྫΞΠυϧݕࡧը໘ͷ$POUBJOFS$PNQPOFOU 3FBDU)PPLTʹΑΔ 'MVYΞʔΩςΫνϟͰঢ়ଶ؅ཧΛ࣮૷
  28. ࣮૷ղઆ$0-03.!45&3 ঢ়ଶ؅ཧ 28 private val IdolSearchContainerImpl = functionalComponent<RProps> { val

    controller = useContext(IdolSearchControllerContext) val (uiModel, dispatch) = useReducer(reducer, UiModel.Search.INITIALIZED) fun onChangeIdolName(filters: Filters, name: String?) = dispatch(actions(...)) fun onSuccess(items: List<IdolColor>) = dispatch(actions(...)) fun onFailure(e: Throwable) = dispatch(actions(...)) fun IdolSearchController.search(filters: Filters = Filters.Empty) = launch { runCatching { fetchItems(filters) } .onSuccess(::onSuccess) .onFailure(::onFailure) } useEffectDidMount { controller.search() } useDebounceEffect(uiModel.filters, 500) { controller.search(it) } ... } ྫΞΠυϧݕࡧը໘ͷ$POUBJOFS$PNQPOFOU EJTQBUDIͰ ετΞͷߋ৽ΠϕϯτΛൃՐ
  29. ࣮૷ղઆ$0-03.!45&3 ঢ়ଶ؅ཧ 29 private val IdolSearchContainerImpl = functionalComponent<RProps> { val

    controller = useContext(IdolSearchControllerContext) val (uiModel, dispatch) = useReducer(reducer, UiModel.Search.INITIALIZED) fun onChangeIdolName(filters: Filters, name: String?) = dispatch(actions(...)) fun onSuccess(items: List<IdolColor>) = dispatch(actions(...)) fun onFailure(e: Throwable) = dispatch(actions(...)) fun IdolSearchController.search(filters: Filters = Filters.Empty) = launch { runCatching { fetchItems(filters) } .onSuccess(::onSuccess) .onFailure(::onFailure) } useEffectDidMount { controller.search() } useDebounceEffect(uiModel.filters, 500) { controller.search(it) } ... } ྫΞΠυϧݕࡧը໘ͷ$POUBJOFS$PNQPOFOU ,PUMJO$PSPVUJOFTΛར༻ͨ͠ඇಉظॲཧ JN!TQBSRM΁ͷϦΫΤετૹ৴
  30. ,PUMJO+4ͷྑ͍ͱ͜Ζ 㾎ϩδοΫ෦෼Λ"OESPJEJ04ͱڞ௨ԽͰ͖Δʂ  ʮ8FCͰϓϩμΫταΠΫϧΛճͨ͠ޙͷΞϓϦ࣮૷ʯΛૉૣ࣮͘ݱ 㾎+4ͷϥΠϒϥϦࢿ࢈Λྲྀ༻Ͱ͖Δʂ  +4ͰͰ͖Δ͜ͱ͸େମͰ͖Δ μʔΫςʔϚɺଟݴޠԽɺ'$ɺ3FBDU)PPLTFUD  㾎+49ʹ͍ۙίʔυͷݟͨ໨

    ,PUMJOͷ๛෋ͳදݱྗͷཱ྆ʂ  ʮ+7.ͷܕγεςϜʯͷੈք؍ͰϑϩϯτΤϯυ࣮૷͕Ͱ͖Δ 30 5ZQF4DSJQUͷܕγεςϜɺಠಛ͡Όͳ͍͔ͬ͢ʜʁ +7.ͷຽͳͷͰ+7.ͬΆ͍ܕγεςϜ͕͍͍ͳʜ
  31. ,PUMJO+4ͷͭΒ͍ͱ͜Ζ 㾎όϯυϧϑΝΠϧ͕େ͖͘ͳΓ͕ͪ  &4ඇ४ڌͷ+4ίʔυ͕ੜ੒͞ΕΔ  ͭ·Γ8FCQBDLͷ5SFF4IBLJOH͕ޮ͔ͳ͍ 㾎7VFɾ"OHVMBSͷެࣜϥούʔ͕ͳ͍  +FU#SBJOT͕༻ҙ͍ͯ͠Δͷ͸3FBDUͷϥούʔͷΈ 㾎,PUMJO+4ରԠϥΠϒϥϦ͕গͳ͍

    +4Ϟδϡʔϧͷར༻ʹͻͱखؒ  +4ϞδϡʔϧΛ࢖͏৔߹ɺϒϦοδͷίʔυ͕ඞਢ 31 ˞ຊ൪Ϗϧυ࣌ʹະ࢖༻ϝιου΍ίʔυͷ࠷దԽΛࣗಈͰߦ͏ػೳ ࠷ऴతʹެ։͢ΔϑΝΠϧ͕େ͖͍ ύϑΥʔϚϯεɾॳճϩʔυ଎౓ʹӨڹ ˞+4ͷඪ४ن֨ɺੜ+4ͱগ͠จ๏͕ҟͳΔ
  32. ໨࣍ 㾎,PUMJO੡8FCΞϓϦʮ$0-03.!45&3ʯͷ঺հ 㾎࣮૷ղઆ ,PUMJO+4ͷྑ͍ͱ͜ΖɾͭΒ͍ͱ͜Ζ 㾎,PUMJO+4ͷࠓޙͷల๬ 32

  33. +FU#SBJOTɺׂͱຊؾʜʂ ˒݄ϦϦʔεɺ,PUMJOͷ৽ػೳ  /FX*3#BDLFOE,PUMJO+4ίϯύΠϧ࣌ͷதؒදݱ͕৽͘͠ͳͬͨ  όϯυϧαΠζ͕൒෼ʹʂ ˒&4ରԠ  &4ίʔυΛੜ੒͢ΔػೳΛ࣮૷༧ఆ 

    8FCQBDLͷ5SFF4IBLJOH͕ޮ͘Α͏ʹʂ ͳΔ͔΋ 33 ࠷େͷऑ఺Λ࠷༏ઌͰ௵ͦ͏ͱ͍ͯ͠Δʂະདྷ΁ͷر๬͕Έ͑Δʂ
  34. +FU#SBJOTɺׂͱຊؾʜʂ ˒ਐԽ͢Δ,PUMJO.VMUJQMBUGPSN  ,PUMJO/BUJWFͰͷTVTQFOEؔ਺ͷαϙʔτ  ,PUMJO.VMUJQMBUGPSN.PCJMFͷBMQIB൛ϦϦʔε  ౳ͷ*%&ͰϞόΠϧ޲͚.11։ൃΛαϙʔτ͢ΔϓϥάΠϯ 34 ˞,PUMJO$PSPVUJOFT͕࢖͍΍͘͢ͳͬͨ

    ,PUMJO+4΋.11ͷҰһʂ .11͕੝Γ্͕Ε͹,PUMJO+4ͷվળεϐʔυ΋্͕Δʂ ͸ͣ 
  35. ,PUMJO+4ɺࢼͯ͠Έͨ͘ͳͬͨΒʜʁ ˒ެࣜͷνϡʔτϦΞϧ͕͋Δͧʂʂʂ  #VJMEJOH8FC"QQMJDBUJPOTXJUI3FBDUBOE,PUMJO+4  #VJMEJOHB'VMM4UBDL8FC"QQXJUI,PUMJO.VMUJQMBUGPSN 35 63-QMBZLPUMJOMBOHPSHIBOETPO 'VMM4UBDL8FC"QQXJUI,PUMJO.VMUJQMBUGPSN@*OUSPEVDUJPO 63-QMBZLPUMJOMBOHPSHIBOETPO

    #VJMEJOH8FC"QQMJDBUJPOTXJUI3FBDUBOE,PUMJO+4@*OUSPEVDUJPO ڈ೥͸ݸ΋ͳ͔ͬͨ ಥવݸ΋௥Ճ͞ΕͯϚδͰ͏Ε͍͠
  36. 㾎,PUMJO཰ʹ͍ۙɺύʔιφϧΧϥʔݕࡧ8FCΞϓϦΛϦϦʔεͨ͠  ࠓޙػೳ௥Ճ༧ఆ "OESPJEJ04൛ؤுͬͯ࡞Γ·͢ 㾎,PUMJO+4ɺ࣮༻ஈ֊ʹٸ଎ʹ͍͍ۙͮͯΔʂ  ·ͩ·ͩ͠ΜͲ͍Օॴ͸਺͋ΕͲɺਐԽͷૣ͞ʹر๬͕࣋ͯΔ  +7.ͱ8FCϑϩϯτɺ྆ํߦ͖དྷͰ͖ͯͱͯ΋ָ͍͠ 

    ར༻ऀগͳׂ͍ʹɺ৘ใ͕੔උ͞Εͭͭ͋Δˠ$POUSJCVUFνϟϯεʂ ·ͱΊ 36 "OESPJEɾαʔόʔαΠυ͚ͩ͡Όͳ͍,PUMJOͷ࣮ྗ Έͳ͞Μ΋ମݧͯ͠Έͯ͸ʜʂ
  37. 37 )BWFBOJDF,PUMJO XJUI,PUPSJ