Wowgineer Notes

WowTechエンジニアの徒然開発日記

Wowgineer Notes

〜新たな"Wow"を生み出す〜

初めてVue + VuexでNewsAPIを使ったアプリ作ってみた

Web開発チームの山本です。

普段はReactで開発をしているのですが、Vueには触れたことがなかったので、簡単なアプリを作成してみました。今回は、ニュース記事を表示できるアプリについて紹介します。

 

 

アプリ概要

今回は、News APIを使ってカテゴリーごとに記事を表示させていきます。ただVueに集中したかったので、フロントのみを実装しています。

 

またVuexも使ってみたかったので、ブックマーク機能を追加しました。ブックマークした記事は、ブックマークのページでまとめて表示することもできます。

 

f:id:yamamoto5555:20210129113252p:plain

 

開発環境

私の開発環境は次のようになっています。

  • MacBookPro
  • Node.js v12.16.2
  • npm 6.14.4

準備

Vue CLIをインストール

Vue CLIは、Vue.jsを迅速に開発するためのフルシステムです。ReactでいうCreate React Appに近いかなと思います。Vue CLIは、基本的に必要なことをやってくれるので、とても助かります。ありがたや。

$npm install -g @vue/cli
$vue --version
@vue/cli 4.5.8

 

プロジェクトの作成

news-appというプロジェクトを作成します。

$vue create news-app
$cd news-app
//サーバーを起動 $npm run serve

 ブラウザでhttp://localhost:8081/ を開くと、サンプルが表示さます。サンプル表示させただけですが、表示できると嬉しいですね!

 

f:id:yamamoto5555:20210108111443p:plain 

Vue用のUIライブラリ

今回UIライブラリにVue Materialを使用します。雰囲気Google Newsぽくなるかなと思ったからです(なって欲しい)。

$npm install vue-material --save

 

公式ドキュメントで必要なコンポーネントだけをimportすることが推奨されていたので、main.jsで使いたいコンポーネントだけをimportしていきます。

import Vue from 'vue'
import App from './App.vue'
import { MdButton, MdCard, MdLayout, MdApp, MdList, MdToolbar, MdDrawer, MdIcon, MdContent } from 'vue-material/dist/components'
import 'vue-material/dist/vue-material.min.css'
import 'vue-material/dist/theme/default.css'

Vue.config.productionTip = false

Vue.use(Vuex)
Vue.use(MdButton)
Vue.use(MdCard)
Vue.use(MdLayout)
Vue.use(MdApp)
Vue.use(MdList)
Vue.use(MdToolbar)
Vue.use(MdDrawer)
Vue.use(MdIcon)
Vue.use(MdContent)

News APIのアカウント登録

表示させるニュースは、NewsAPIを使っていきます。アカウントを作成すると、API keyを取得できます。NewsAPIは、カテゴリーごとや言語ごとにニュースを取得できるので便利でした。

Vueでコンポーネント作成

Vue Materialを使用して、Headerを作成しましょう。Vue MaterialのComponentsの中のAppを使いました。

src/components/App.vue

<template>
  <div id="app">
    <div class="page-container">
      <md-app md-mode="reveal">
        <md-app-toolbar class="md-primary">
          <md-button class="md-icon-button">
            <md-icon>menu</md-icon>
          </md-button>
          <span class="md-title">News App</span>
        </md-app-toolbar>
      </md-app>
    </div>
  </div>
</template>
<script> export default { name: 'App', } </script>
<style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } </style>

上記のコードをかくと、http://localhost:8081/に下のような画像のようにHeaderが表示されます。

f:id:yamamoto5555:20210120110856p:plain

同じように下記のコンポーネントを作成していきます。

  • NewsItem.vue:ニュースを表示するカードのコンポーネント
  • Content.vue:複数のカードを表示されるコンポーネント

src/components/NewsItem.vue

<template>
    <md-card>
      <md-card-area>
          <md-card-media>
          </md-card-media>
          <md-card-header>
            <div class="md-title">Article Title</div>
          </md-card-header>

          <md-card-content>
            Content.........
          </md-card-content>
        </md-card-area>
        <md-card-actions md-alignment="space-between">
          <md-button class="md-icon-button" >
            <md-icon>bookmark</md-icon>
          </md-button>
          <md-button class="md-primary">Read More</md-button>
        </md-card-actions>
    </md-card>
</template>

 Vue MaterialにCardというコンポーネントがあるので、それを使用してカードを作成します。

 

src/components/Content.vue

<template>
  <div class="md-layout md-gutter md-alignment-center">
    <div v-for="n in 8" class="md-layout-item" :key="n">
<NewsItem :article="n"/>
</div> </div> </template> <script> import NewsItem from "./NewsItem.vue" export default { components: { NewsItem }, } </script>



 NewsItem.vueをContent.vueで呼び出し複数のカードを表示していきます。

作成した Content.vueをApp.vueで読み込んでみましょう。

src/components/App.vue

<template>
  <div id="app">
    <div class="page-container">
      <md-app md-mode="reveal">
        <md-app-toolbar class="md-primary">
          <md-button class="md-icon-button">
            <md-icon>menu</md-icon>
          </md-button>
          <span class="md-title">News App</span>
        </md-app-toolbar>
        <md-app-content>
          <Content />
        </md-app-content>
      </md-app>
    </div>
  </div>
</template>
<script>

export default {
import Content from './components/Content.vue'
  name: 'App',
components: {
    Content
  },
}
</script>

 

そうすると、カードを複数表示するコンポーネントが完成します。UIライブラリを使用すれば、そんなに時間がかからず作成できるはずです。

f:id:yamamoto5555:20210120114053p:plain



 

Vuexの作成

 次に、登録したNewsAPIを使用して、ニュースのデータを取得し表示します。さらに、次の3つのことを実現させたいです。

  • カテゴリーごとに記事を表示させる
  • 記事をブックマークできる
  • ブックマークされた記事は一覧で表示できるようにする

上記のことを実現するために考えなくてはいけないのは、記事とブックマークされた記事のデータ管理です。ただこれをVueで行うと、データの受け渡しをpropsで行うことになり、複雑化してしまいます。。。今回は、Vuexを使用してデータ一元化を行ないます。

 

Vuexとは

ReactでいうReduxのように、Vue.js アプリケーションのための状態管理のライブラリです。詳しいことは、公式ドキュメントに書かれています。

 

Vuexの導入

Vuexをインストールしましょう。

$npm install vuex --save

インストールしたら、main.jsを編集しVuexの設定をします。

import Vue from 'vue'
import Vuex from 'vuex'
import App from './App.vue'
import store from './store'


Vue.config.productionTip = false

Vue.use(Vuex)


new Vue({
  render: h => h(App),
  store,
}).$mount('#app')

「import store from './store'」と記載していますが、storeのディレクトリは作成していなかったので、作っていきます。

src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import news from './modules/news'

Vue.use(Vuex)

const debug = process.env.NODE_ENV !== 'production'

export default new Vuex.Store({
  modules: {
    news,
  },
  strict: debug,
})

 src/store/modules/news.js

// initial state
const state = () => ({
})

// getters
const getters = {}

// actions
const actions = {
}

// mutations
const mutations = {
}

export default {
  namespaced: true,
  state,
  getters,
  actions,
  mutations
}

 

stateの作成

stateは、アプリケーション全体のデータを管理するところです。今回のアプリでは、3つのデータを保存していきます。

  • カテゴリーごとに表示するニュースのデータ
  • 選択しているカテゴリー状態
  • ブックマークしているデータ

newsDataはカテゴリーごとにデータを取り出しやすくしたかったので、keyをカテゴリー名にしています。

 src/store/modules/news.js

const state = () => ({
    newsData: {
        "technology":[],
        "entertainment":[],
        "business":[],
        "science":[],
    },
    activeCategory: "business",
    bookmarkData:[]
})

 

NewsAPIでデータ取得

保存するニュースのデータをNewsAPIから取得します。指定したカテゴリーのデータを取得したかったので、引数でカテゴリー名を渡しています。APIからデータ取得するときに、axiosを使用しました。詳しい使い方は、ドキュメントをご覧ください。

import axios from 'axios' 

const key = NewsAPIの登録後に渡されるAPI key export const changeCategory = async (category) => { const res = await axios.get('https://newsapi.org/v2/top-headlines?country=jp&category='+category+'&apiKey='+ key) const articles = res.data.articles; return articles }
actionの作成

actionでAPIの実行をしましょう。初回のみデータ取得したいので、既にカテゴリーのデータがある場合には、更新せずにreturn falseしています。また、本当はサーバー側でidの割り振ると思うのですが、今回はフロントだけで処理を行なっているため、article_id_listというのを使って、idを割り振っています。ニュースデータは固定で20件ずつ取るようにしているので、article_id_listのstart_indexを20ごとに決めています。

import { changeCategory } from '../../../api/news.js'

const article_id_list = {business: 0, entertainment: 20, technology: 40, science: 60} const actions = { async getArticles({commit, state}){ const category = state.activeCategory if(state.newsData[category].length ){ return false }else{ const new_articles = await changeCategory(category) const article_data = new_articles.map((article, index)=>{ const start_index = article_id_list[category] article.id = index + start_index article.bookmark = false return article }) commit('setArticles', {data: article_data, category: category}) return new_articles } } } 

カテゴリーの変更をするactionを追加しましょう。

import { changeCategory } from '../../../api/news.js'

const article_id_list = {business: 0, entertainment: 20, technology: 40, science: 60} const actions = { async getArticles({commit, state}){ const category = state.activeCategory if(state.newsData[category].length ){ return false }else{ const new_articles = await changeCategory(category) const article_data = new_articles.map((article, index)=>{ const start_index = article_id_list[category] article.id = index + start_index article.bookmark = false return article }) console.log("article_data", article_data) commit('setArticles', {data: article_data, category: category}) return new_articles } },
  //カテゴリーの変更をするaction追加 updateCategory({commit, state}, category){ if(state.activeCategory !== category){ commit('setActiveCategory', category) } } }

 

mutationの作成

 mutationでデータの変更をしていきます。

const mutations = {
//取得した記事のデータを保存 setArticles(state, data){ const news_data = data.data const news_category = data.category state.newsData[news_category] = news_data },
  //選択中のカテゴリーを保存 setActiveCategory(state, category){ state.activeCategory = category } }

 記事のデータは、全てを渡すのではなく、選択しているカテゴリーのデータだけを渡すようにgettersの中に書いていきます。

const getters = {
    getArticlesByCategory: (state) => {
        const category = state.activeCategory
        return state.newsData[category]
    }
}

機能の実装

ニュースの表示

App.vueで記事のデータを取得します。createdの中で、getArticlesのアクションを実行しましょう。mapGettersを使って、Component内でstoreに保存した記事のデータを取り出します。そして、Content Componentにarticlesを渡していきます。 

src/components/App.vue

<template>
  <div id="app">
    <div class="page-container">
      <md-app md-mode="reveal">
        <md-app-toolbar class="md-primary">
          <md-button class="md-icon-button">
            <md-icon>menu</md-icon>
          </md-button>
          <span class="md-title">News App</span>
        </md-app-toolbar>
        <md-app-content >
          <Content :articles="articles"/>
        </md-app-content>
      </md-app>
    </div>
  </div>
</template>

<script>
import Content from './components/Content.vue'
import { mapState, mapGetters } from 'vuex'

export default {
  name: 'App',
  components: {
    Content
  },
    computed: {
      ...mapState({
        activeCategory: state => state.news.activeCategory,
      }),
      ...mapGetters('news', {
        articles: 'getArticlesByCategory'
      })
       
    },
    created () {
       this.$store.dispatch('news/getArticles')
  },
}
</script>

src/components/Content.vue

<template>
  <div class="md-layout md-gutter md-alignment-center">
    <div v-for="article in articles" class="md-layout-item" :key="article.title">
      <NewsItem :article="article"/>
    </div>
  </div>
</template>

<script>
import  NewsItem  from "./NewsItem.vue"

export default {
  components: {
    NewsItem
  },
  props: {
      articles: Array
  }
}

</script>

  

記事によっては、画像データがないのもあったので、画像がない場合は、no_image.jpgを表示するようにしています。

src/components/NewsItem.vue

<template>
    <md-card :id="article.id">
      <md-card-area>
          <md-card-media>
            <img :src="article.urlToImage" :alt="article.title" @error="noImage">
          </md-card-media>

          <md-card-header>
            <div class="md-title">{{article.title}}</div>
          </md-card-header>

          <md-card-content>
            {{article.description}}
          </md-card-content>
        </md-card-area>
        <md-card-actions md-alignment="space-between">
          <md-button  v-bind:href="article.url" class="md-primary">Read More</md-button>
        </md-card-actions>
    </md-card>
</template>

<script>
  import AssetsImage from "@/assets/no_image.jpg";  
  export default {
    name: 'NewsItem',
    props: {
      article: Object
    },
    data(){
      return {
        assetImage: AssetsImage
      }
    },
    methods: {
      noImage(element){
        element.target.src = this.assetImage
      }
    }
  }
</script>

 これで最初の画面に、ニュースを表示できるようになりました。

f:id:yamamoto5555:20210129113631p:plain


カテゴリー選択

ニュースデータの取得と表示はできたので、カテゴリーを選択して表示できるようにします。まずは、Drawerの左上のアイコンをクリックした時に表示/非表示できるようにします。これもVue MaterialのDrawerのコンポーネントを使っていきます。SideMenu.vueの中に、Drawer内に表示するものを書いていきます。

 

src/components/SideMenu.vue

<template>
    <div>
        <md-toolbar class="md-transparent" md-elevation="0">Category</md-toolbar>
        <md-list>
        <md-list-item class="item-active">
            <md-icon>business</md-icon>
            <span id="business" class="md-list-item-text" @click="setResource('business')">Business</span>
        </md-list-item>

        <md-list-item>
            <md-icon>music_note</md-icon>
            <span id="entertainment" class="md-list-item-text" @click="setResource('entertainment')">Entertainment</span>
        </md-list-item>

        <md-list-item>
            <md-icon>emoji_objects</md-icon>
            <span id="technology" class="md-list-item-text" @click="setResource('technology')">Technology</span>
        </md-list-item>

        <md-list-item>
            <md-icon>science</md-icon>
            <span id="science" class="md-list-item-text" @click="setResource('science')">Science</span>
        </md-list-item>
        <md-list-item>
            <md-icon>bookmarks</md-icon>
            <span id="bookmark" class="md-list-item-text" @click="setResource('bookmark')">Bookmark</span>
        </md-list-item>
        </md-list>
    </div>
</template>

<script>
    export default {
        name: 'SideMenu',
        props:{
            setResource: Function
        },

}
</script>

<style>
.md-list-item{
    cursor: pointer;
}

.md-list .item-text-active{
    color:#448aff;
}
</style>

SideMenu.vueをApp.vueから呼び出します。このDrawerの表示/表示のデータは、storeに入れずに、menuVisibleとしてlocalで管理します。そして、setResourceというメソッドで、カテゴリーを変更していきます。

 

 src/components/App.vue

<template>
  <div id="app">
    <div class="page-container">
      <md-app md-mode="reveal">
        <md-app-toolbar class="md-primary">
          <md-button class="md-icon-button" @click="menuVisible = !menuVisible">
            <md-icon>menu</md-icon>
          </md-button>
          <span class="md-title">News App</span>
        </md-app-toolbar>
        <md-app-drawer :md-active.sync="menuVisible"  >
           <SideMenu  :setResource = "setResource" />
        </md-app-drawer>
        <md-app-content>
     <Content :articles="articles"/>
    </md-app-content> </md-app> </div> </div> </template> <script> import Content from './components/Content.vue' import SideMenu from './components/SideMenu.vue' import { mapState, mapGetters } from 'vuex' export default { name: 'App', components: { SideMenu, Content }, data() { return { menuVisible: false, } }, ・




methods: {
setResource(category){
  this.$store.dispatch('news/updateCategory', category)
  this.menuVisible = false
}
}
} </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } .md-drawer { width: 230px; max-width: calc(100vw - 125px); } .md-content{ height: calc(100vh - 64px) } </style>

 

左上のアイコンをクリックすると、下のようにDrawerが表示されるようになりました。各カテゴリーをクリックしても、カテゴリー毎のニュースも表示されるようになっていると思います。

f:id:yamamoto5555:20210129195241p:plain

 

ニュースのブックマーク

actionとmutationの追加からしていきます。

  src/store/modules/news.js

const actions = {
・




  //ブックマークデータの更新のactionを追加 updateBookmark({commit}, value){ commit('updateBookmark', value) } }

  src/store/modules/news.js

const mutations = {





  //ブックマークした記事の保存または削除 updateBookmark(state, id){ let category = state.activeCategory
//既にブックマークされているか const bookmark_index = state.bookmarkData.findIndex((item) => item.id == id)
      if(category !== null && bookmark_index !== -1){ category = state.bookmarkData[bookmark_index].category }
const article_index = state.newsData[category].findIndex((item) => item.id == id) const bookmark = state.newsData[category][article_index].bookmark
if(article_index != null){
//newsDataのbookmarkをtrue・falseを変更 state.newsData[category][article_index].bookmark = !bookmark //ブックマークされていなかったら、bookmarkDataにデータを追加 if(bookmark_index === -1){ let copy_data = Object.assign(state.newsData[category][article_index]) copy_data.category = category state.bookmarkData.push(copy_data) }else{
         //既にある場合は、bookmarkDataから削除 state.bookmarkData.splice(bookmark_index, 1) } } } }

 mutationsのupdateBookmarkで、bookmarkDataとnewsDataの更新をしていきます。

 

activeCategoryがbookmarkの時は、bookmarkDataを渡すようにgettersを変更します。

  src/store/modules/news.js

const getters = {
    getArticlesByCategory: (state) => {
       const category = state.activeCategory
    let data
    if(category === "bookmark"){
     data = state.bookmarkData
    }else{
     data = state.newsData[category]
    }
     return data; } }

 

次に、クリックイベントを追加するために、コンポーネントの変更を行います。

src/components/NewsItem.vue

<template>
    <md-card :id="article.id">
      <md-card-area>
          <md-card-media>
            <img :src="article.urlToImage" :alt="article.title" @error="noImage">
          </md-card-media>

          <md-card-header>
            <div class="md-title">{{article.title}}</div>
          </md-card-header>

          <md-card-content>
            {{article.description}}
          </md-card-content>
        </md-card-area>
        <md-card-actions md-alignment="space-between">
          <md-button class="md-icon-button" @click="onChangeBookmark(article.id)">
            <md-icon v-if="article.bookmark">bookmark</md-icon>
            <md-icon v-else>bookmark_border</md-icon>
          </md-button>
          <md-button  v-bind:href="article.url" class="md-primary">Read More</md-button>
        </md-card-actions>
    </md-card>
</template>

<script>
  import AssetsImage from "@/assets/no_image.jpg";  
  export default {
    name: 'NewsItem',
    props: {
      article: Object
    },
    data(){
      return {
        assetImage: AssetsImage
      }
    },
    methods: {
      onChangeBookmark(id){
        this.$store.dispatch('news/updateBookmark', id)
      },
      noImage(element){
        element.target.src = this.assetImage
      }
    }
  }
</script>

ブックマークのアイコンをクリックすると、onChangeBookmarkの関数から、先ほど追加したupdateBookmarkのアクションが実行されます。

 

これで、ブックマークの機能が完成しました!

f:id:yamamoto5555:20210201115440g:plain

 

まとめ

初めてVueとVuexを使ってアプリを作りましたが、初心者でもReactに比べて時間をかけずに作成することができました。まだまだこのアプリの改善したいところもありますが、短時間で動くアプリを作るのを目標に作ってみました。これから、Vueを使っていく上で、様々な疑問が出てくると思うので、その時はまた別の記事でまとめたいです。

 

 

我々ワウテック技術開発部はこのような環境で開発をしています。
興味を持った方がいらっしゃればいつでもご連絡下さい。

f:id:wowtech-dev:20190313163146j:plain

業務未経験で入社して3ヶ月経って感じた事

f:id:miura1213:20201216213358j:plain

こんにちは。WowDesk担当の三浦です。
今回が初投稿となります。よろしくお願いいたします。
私が当社に入社して3ヶ月が経ちましたので所感を書いていこうと思います。
ワウテックでエンジニアになりたいと思う方の参考になればと思います。

簡単な経歴

前職では、自社サービスの保守・運用・導入がメインのIT企業に約4年いました。
その後、本格的に開発に携わりたいという気持ちが強くなりエンジニアになる道を目指し、プログラミングの学習を開始しました。
そして、当社と縁があり実務未経験として入社することになります。
 

入社後の業務内容

当社のクラウド受付サービス「WowDesk」のWeb開発を担当することになりました。
最初は簡易的な機能開発や、React-Reduxの導入を実施しました。

www.wowdesk.jp

入社して感じた事

私が入社して感じた事を、苦戦した事とよかった事の2つの視点で記載していきます。 

<苦戦した事>

  1. 既存のコードが読めない
    これは知識不足で、コードの意味が全くわかりませんでした。実務未経験の最初の壁だと思います。例えば、私は基本JavaScriptを記述しているのですが、非同期処理が未知の世界でした。
    (今でも記述はしますが、100%分かるかと言われると怪しいです・・・)
    しかし、この辺りは先輩方が本当に優しくて、しっかりとフォローした頂きなんとか乗り越えていくことができました。

  2. 開発だけでなく、自社サービスの発展も視野に入れる
    開発作業だけでなく、チーム内でどういったニーズがある機能を追加するか等を議論していきます。ここの部分に関しては、前職にはなかった思考だったので今でも苦戦しています。
    単純に機能追加をするだけでなく、お客様の本質的な課題解決につながる機能をしっかりと議論していく必要があります。

  3. リモートワークのコミュニケーション
    私が入社した時には、コロナウイルスの真っ只中でした。ほとんどのメンバーがリモートワークを実施していました。
    一番苦戦したことは、コードに関する質問が気軽にできないということでした。併せて上記の知識不足の為、全く前に進めませんでした。そこで「Chrome リモートデスクトップ」を利用し、質問等がしやすくなりました。また、先輩のデバッグの仕方など直で確認できるので、取り入れて正解だったなと思います。

    support.google.com

     

<よかった事>

  1. 自由な働き方
    まずなんといっても私服!前職では毎日スーツを来ており、最初は私服がなじめませんでした。(田舎出身という事もあり、みんなおしゃれに見える・・・)
    リモートワークも早めに導入しており、ベンチャー企業らしさを感じる一面です。

  2. 開発環境が良い
    開発メンバーの椅子が最高です。個人では買うことがない椅子で業務を行うことできます。椅子だけではなく、開発PCのスペック等、エンジニアが開発する上で良い環境で業務を行うことができます。

  3. 開発メンバーが優しい ※最重要
    開発メンバーには本当に恵まれているなと感じています。実務未経験者の私でも丁寧にフォローしていただけています。
    ただ、何でも教えていただく訳でもなく、自分で考えるべき部分はしっかり指導していただけます。
    当社に入社や検討される方がいらしたらこの部分は、安心して頂ければと思います。
まとめ

ワウテックでエンジニアになりたいという方の参考になればと思っております。
また、技術的な投稿も今後は投稿していきますので、よろしくお願いいたします。

 

参考リンク

 

 

我々ワウテック技術開発部はこのような環境で開発をしています。
興味を持った方がいらっしゃればいつでもご連絡下さい。

f:id:wowtech-dev:20190313163146j:plain

 

 

Reduxのパフォーマンス改善でImmutable Dataの概念にぶち当たった話

f:id:yamamoto5555:20200821113553j:plain

Web開発チームの山本です。

以前の記事でReduxのStore設計について書いた通り、Reduxの道へと踏み出したのですが、その後パフォーマンス面での課題にぶち当たったので、今回はReduxでパフォーマンス改善に取り組んだことについて紹介します。

背景

お客様によりWowTalkを使っていただくため社内の浸透をサポートする集計機能を実装しました(詳しくはこちら)。その中の最終アクティブでは、アカウントに所属するユーザーの最終アクティブを表示しています。この最終アクティブの画面の表示が、ユーザーの多いアカウントでは遅いという問題がありました。最終アクティブ画面以外にも影響が出ていたため、早急に改善する必要がありました。

f:id:yamamoto5555:20201006105315p:plain

 

課題

今回は、4000人以上のアカウントで最終アクティブのページの読み込みが遅いのが、顕著に現れたので、Google Dev Toolsを使用し計測してみました。計測方法としては、MacBook Proを使用し、4000人ほどのアカウントで試しました。最終的には、数万人のアカウントも使用できるようにすることが今回のゴールです。

 

・計測対象の操作

サイドバーから最終アクティブをクリックするが、表示の反応が遅れるのと、最終アクティブのリストが表示されるのが遅いです。

f:id:yamamoto5555:20201013103743g:plain

 

・合計のレンダリング時間

f:id:yamamoto5555:20201013101046p:plain

 

・APIのリクエストとレスポンス時間

f:id:yamamoto5555:20201013101108p:plain

 

Reduxの仕組み

Reactで安否確認機能というサービスも開発していますが、同じ規模のアカウントでユーザーの読み込みに時間に、これほど時間がかかることはなかったため、今回はRedux起因の問題であると予想を立てました。

 

redux-loggerで出力しているLogでは、明らかにリストとして表示しているユーザー情報をStoreに更新した時の処理に時間がかかっていました。デバックしてみると、4000人のユーザー情報でループ処理が実行されていました。調べてみたところ、Reduxのimmutabilityに関係していました。

 

ReduxとImmutable Data

Redux と React-Redux は、どちらもをshallow equality checkingを採用しています。shallow equality checking(またはreference equality)とは、2つの異なる変数が同じオブジェクトを参照しているかどうかをチェックします。 また、shallow equality checkingとよく比較されるのが、deep equality checking です。deep equality checkingは、2つのオブジェクトのプロパティのすべての値をチェックします。Immutable Dataに関しては、Reduxのドキュメントにも詳しく記載されています。

今回は、このshallow quality checkingによって、Storeに4000人のユーザー情報を入れる場合、それの差分を確認するためにループ処理が実行され、時間がかかっているのではと考えました。

 

改善内容

Storeに入れてしまうと、shallow quality checkingで時間がかかってしまうため、ユーザー情報は、Storeに入れないで、グローバル変数の中に入れることにしました。local stateに入れるのは、UIなどの管理は向いているかなと思ったのですが、今回のユーザー情報は、最初に取得するだけで、そのあとに更新などは必要ないため、グローバル変数に格納することになりました。

redux.js.org

 

また、その他の改善点としては、最終アクティブの画面ではソート機能やフィルター機能があるため、毎回数万規模で部門のフィルターで時間がかかってしまうため、最初の20件のみを表示するように変更しました。

 

改善結果

上記の改善施策やその他細々した修正をした結果、2秒かからないくらいになりました。

f:id:yamamoto5555:20201030104344p:plain

 

まとめ

Reduxのshallow equality checkingによって、データ量が多いものをStoreで管理しようとするとかなり時間がかかることが分かりました。サーバーの負荷を減らすためにも、今回はフロント側での処理で対応しました。

数万人までのアカウントで利用できるようにするのが目標でしたが、今後10万人近いアカウントにも対応が必要になる可能性もあります。その時は今のやり方では限界がくると思うので、再度修正が必要です(またその時考える)。

Reduxのパフォーマンス改善をする中でも、React部分の無駄な再レンダリングなど多々あり、まだまだ改善の余地がありそうです。これからも継続的に、改善していきたいです!

 

補足:

今回が初めてのパフォーマンス改善だったので、チューニングの基礎などが書かれていたWebフロントエンド ハイパフォーマンス チューニングという本を読みました。Reduxのパフォーマンス改善には直接関係していませんが、いろいろなテクニックが書かれており、今後にも開発にも役立ちそうでした。

www.amazon.co.jp

 

参考リンク 

 

 

我々ワウテック技術開発部はこのような環境で開発をしています。
興味を持った方がいらっしゃればいつでもご連絡下さい。

f:id:wowtech-dev:20190313163146j:plain

絵文字リアクションの開発裏話

f:id:yamamoto5555:20200911113722j:plain


Web開発チームの山本です。

WowTalkに絵文字リアクション機能がリリースされ、時間が少し立ってしまったのですが、自分の中でも思い出深い機能でもあるので、今回は絵文字リアクション機能の開発裏話を紹介します。

 

絵文字リアクション機能とは?

絵文字リアクション機能は、トーク上に投稿されたメッセージに対して12種類の絵文字で反応(リアクション)することができる機能です。メッセージに対してリアクションできるため、スタンプよりもカジュアルに使うことができます。詳しくは、こちらから!

 

なぜ絵文字リアクション機能を追加したのか?

日頃から多くのメッセージをWowTalkで行なっているため、スタンプがたくさん送信された際に、メッセージが流れてしまうという課題を感じていました。また、サービスコンセプトの1つでもある「感情」と絵文字リアクションとの親和性も高く、機能を追加する後押しとなりました。

 

開発で苦労したこと

機能開発時に大変だったことは、仕様の決定でした。リアクションの種類はもちろんのこと、何個まで選択できるか、リアクションした人を見せるかなど、想像以上に決めることが多く、技術部内でも何度も話し合いをしました。またWowTalkのヘビーユーザーである社内の人の声も聞きたかったので、共有のアンケート機能を使用して、リアクションの種類について社内からも意見を募っていました。面白かったのは、技術部内ではポジティブなリアクションだけでいいのではと話していたのですが、意外と社内の人からはリアクションの感情の幅があった方が良いと言う意見があったことです。確かに現在使用する中で、喜び以外の感情のリアクションを使用することがよくあるなと思っています。

 

個人的には、リアクションの追加・削除・更新などで起こるリアクション数の変更を適切に反映させることに、とても苦労しました。ただ追加するのに1リアクション増やす、削除するごとに1リアクション減らすでは、重複などのデータの矛盾が生じるため、メッセージが持つID(下のコードでいうと"messageId1"と"messageId2"のこと)をkeyにすることで、リアクションにアクセスしやすいようにしました。今回の実装で苦手に感じていた配列やオブジェクトの使い方にだいぶ慣れた気がします。

 

{
    messageId1: {
        clapping : {
            users :  [ user1,  user2, user3 ]
        },
        heart : {
            users: [ user2, user4 ]
        },    
    },
    messageId2: {
       rose : {
            users :  [ user4,  user5, user6 ]
        }
    }
}

 

 

リリース前のハプニング?!

テストも落ち着き、そろそろリリースだとなった時に、絵文字の入れ替えが発生しました。もともとは割れたハートと泣いている顔文字を入れる予定でしたが、上からのお達しで変更することになりました。そこで新しく入れる絵文字になったのが、👌と🌹です。バラは、もともと技術部でメンテナンス時などでお疲れ様という意味を込めて、バラのスタンプを送りあっていました。そのお疲れ様の意味を込めて、今回の絵文字に追加しました。皆さんも、ぜひ相手を労う時にたくさんバラを送ってください!

 

まとめ

嬉しいことに、社内からも「絵文字リアクション便利!」というポジティブな声を聞くと、頑張って開発して良かったなと思います。まだまだ改善の余地もあるとは思いますが、ユーザーの方にドンドン使っていただけると嬉しいです。これからも更にサービスを進化させられるよう、頑張っていきます!

 

 

我々ワウテック技術開発部はこのような環境で開発をしています。
興味を持った方がいらっしゃればいつでもご連絡下さい。

f:id:wowtech-dev:20190313163146j:plain

ReduxのStore設計で気をつけたこと

                 f:id:yamamoto5555:20200821112754j:plain


 

 Web開発チームのフロントを担当している山本です。

今年リリースをした集計機能でReduxを導入しました。集計機能とは、お客様によりWowTalkを使っていただくために、社内の浸透をサポートする機能です(詳しくはこちら)。Redux導入にあたり一番頭を悩ませたのが、Store設計でした。初めての設計ということもあり、いろいろと試行錯誤したので、今回はReduxのStore設計について紹介します。 

 

公式のReduxのStore設計

Reduxのドキュメントでも、複数のデータを取り扱う時の3つのポイントが書かれています。

  • Domain data: アプリケーションが表示、使用、または変更する必要のあるデータ

  例)サーバーから取得するデータ

  • App state: アプリケーション独自の動作データ 

  例)データの選択状態やリクエスト中の状態

  • UI state: 現在のUIの表示方法を表すデータ

  例)モーダルが表示か非表示か

 

この3つの分け方は、データを取り扱う時に、明確な指針としてとても助かりました。基本的には上記のことを元に、今回の集計機能のStoreも構成しています。

 

 他のプロジェクトのStoreは?

具体的な例も参考にしたかったので、react-redux-realworld-example-appというプロジェクトを見つけました。このプロジェクトでは、Twitterのように自分で簡単なメッセージを投稿できるようになっています。reducersを見てみると、editorやProfileといったパーツごとにreducerを分けており、フラットに整理されていました(プロジェクトの大きさによるかもしれませんが)。またcommon.jsでは、ユーザー情報など共通で管理を行っているデータをまとめています。

 

正直、いろいろな具体的例を参考にしたかったのですが、その当時はなかなか見つからず。。。(私の探し方が悪かったのかな)。今は、 React Developer ToolsでStoreの中を覗けることを知ったのですが、早く知りたかったです!React Developer Toolsの使い方なども書いてあり、こちらの記事がとても参考になりました。私もこれからいろいろなStoreの中を覗いてみたいと思います。

  

集計機能のStore構成

今回の集計機能では、ページごとに表示するデータが異なるので、パーツではなくページごとにStoreを構成しました。それは、今後のことを踏まえても、ページ数をかなり増やすことは考えていなかったこともあり、ページごとにStoreを作ることに踏み切ることが出来ました。 

-cache
    |_common
-page
    |_common
    |_activeUser
    |_lastActive
-ui
    |_common
    |_activeUser
    |_lastActive

 

上記が考えたStore構成です。基本はドキュメントにあった3つのポイントから構成し、さらにページごとに分けています。ページ関係なく共通で使うものはcommonに入れて管理しています。今のところ、実装する中ではどこでデータを管理しているのかが見つけやすいです。

 

まとめ

今回はStoreの設計をしたのですが、どれが正解かは分からない部分が多かったです。集計機能では、ページごとに分けて良かったとは思っていますが、サービスによっては、やはりパーツごとに分けたほうがいいものもあり、一概にはどれがいいとは言えないと思いました。また、これからさらに集計機能を進化させたいので、その中で新たな壁にぶつかりそうな気がしています(既に壁にぶつかったので、その内また違う記事で書きます)。まだまだRedux初心者なので、積極的に他のサービスからも学んで行きたいです!

  

参考URL:

React Redux Real World Examples 〜先人から学ぶReact Reduxの知恵〜

redux-ecosystem-links/apps-and-examples.md

 

我々ワウテック技術開発部はこのような環境で開発をしています。
興味を持った方がいらっしゃればいつでもご連絡下さい。

f:id:wowtech-dev:20190313163146j:plain

 

カラーユニバーサルデザイン認証を取得した時の開発話

f:id:yamamoto5555:20140914125312j:plain

  

Web開発チームのフロントを担当している山本です。

 

昨年ワウテックは、カラーユニバーサルデザイン(CUD)認証を取得しました。(詳しくはこちらから)より良いサービスを提供できるよう奮闘する中で、CUD認証の取得は新機能とは少し違う新たな取り組みでした。今回、なぜCUDマーク取得を行ったのか、どのような変更があったのかについてご紹介したいと思います。

 

 

CUDとは 

CUD認証とは、特定非営利活動(NPO)法人カラーユニバーサルデザイン機構が発行を行う、“製品が多くの人にわかりやすい配色であることを保証する第三者認証の証”です。(※1)

人それぞれ色の伝わり方が異なるため、正しく情報が伝わるよう色の使い方に配慮しようという取り組みです。私が赤色に見えていても黒っぽく見えてしまう人もいるため、みんなにとって、より分かりやすい配色を選択していきます。

 

なぜCUD認証を取得したのか

弊社では「言語」「距離・時間」「個性」「温度・感情」「経験」という5つの視点からサービスの開発や新機能の追加を行なっています。その中の「個性」から色の伝わり方が違うことで生じるコミュニケーションの課題解決に取り組むことにしました。もともと、私が入社前の面接時より、代表の瀬沼からカラーユニバーサルの話を聞いており、会社としても取り組んでいきたいことだと前々から私も認識していました。

 

変更点

では、実際にCUD認証を取得するにあたっての変更した箇所をいくつかご紹介したいと思います。

 

1. 赤文字からオレンジへ

ワウトークで使用していた赤が暗めで黒と同じように見えてしまうため、「注意を集める、強調する、違いを表現できていない」という指摘がありました。改善案としては、明るいオレンジよりの色に変更することでした。ただ赤文字はあらゆる所で使っていたため、全てを変更するのがとても大変でした。ただ色を変更すればいいという訳ではなく、動的に色を変更しているところもあるため(例えば、クリックすると色が変化するなど)、見落としがないようにと神経を使った気がします。

f:id:yamamoto5555:20200311094711j:plain

 

2.グラフの色

 共有機能では、アンケートを取ることができ、その結果はグラフで表示されます。そのアンケートのグラフで使用している色が差が少ないということでいくつか変更しました。それぞれの項目に白縁をつけたり、グラフの項目をクリックすると情報が表示されるなど、もともと対応していたところもあり、全ての色を変更する必要はありませんでした。ただ僅かな色の変更なので、私も変更するときに色の差異を認識することが難しかったです。

 

f:id:yamamoto5555:20200311095241p:plain

 

まとめ

正直CUD認証を取得にあたって、「色の変更が多くなるのでは」、「現在の製品とかなり違う色を提案されるのでは」と不安もありました。嬉しいことに、ワウトークの強みでもあるシンプルさが使用している色にも出ており、改善箇所の指摘も多くなかった気がします。意外と文字の色をオレンジに変更しても、私の中では違和感もあまりなく、今では馴染んでいるように感じます。

 

今回のCUD認証の取得から、色自体を認識できないというのではなく、背景色との色の差が少ないため分かりずらさが生じたり、強調するなどの色の役割ができていなかったことを知りました。デザイン性だけではなく、新たな色に関する視点を意識するようになったので、今後の開発にも活かしていきます!

 

 

我々ワウテック技術開発部はこのような環境で開発をしています。
興味を持った方がいらっしゃればいつでもご連絡下さい。

f:id:wowtech-dev:20190313163146j:plain

 

駆け出しエンジニア1年目で学んだ3つのこと

f:id:yamamoto5555:20200203135617j:plain

 

Web開発チームのフロントを担当している山本です。 

最近では技術開発部の仲間も着々と増え、再来年入社の新卒採用まで始まりました。基本的に技術開発部は実務経験のある方が主なのですが、私は実務未経験からの採用でした。エンジニアを目指している人やエンジニアとして働き始めた人たちがエンジニアとして働くイメージが膨らむよう、エンジニア1年目で得た3つの学びを紹介したいと思います。

 

 

学び1: ReactとReduxの技術面での挑戦

Reactでの開発時にデータ管理の複雑さが増していき、Reduxを取り入れようという話になりました。それからWeb開発チームでも定期的にRedux勉強会を開催されるようになりました。毎回テーマを決め、各自で事前に予習をし、勉強会でお互いに学んだことを共有するスタイルで行っています。Reduxを勉強する中で、Reactの復習にもなり、初期の頃より理解できることが増えてきたのを実感できたのも嬉しかったです。

 

学び2: 機能(サービス)リリースするまでの見積もりの大切さ

機能追加などを行う中で、現在の自分の力を考慮し、実装にかかる時間を正確に見積もることが大切だとよく上司から言われます。無限に時間があればいいのですが、そういうわけにはいきません。機能実装の時に必要な作業を書き出し、それに対してかかる作業時間を見積もるようにしています。数時間で終わるだろうと思っていても実際に取り掛かると手間取り、見積もりと差が出ることがよくありました。また作業時間の見積もりは正確だとしても他の作業が発生することもあるため、それも事前に想定しながら見積もることが必要です。見積もり時間が短いからいいというわけではなく、自分の力量を知るために自分と向き合う時間でもあると思っています。

今はInstaganttでスケジュール管理をしており、タスク状況を視覚的に把握できるので便利です。他にもオススメのツールがあれば教えて欲しいです。

 

学び3: 仕様の決定からデザインについて考えるようになる

初めて機能追加する時に、仕様の可能性が無限大だということに気がつきました。ユーザーが使う時のことを考えながら、開発期間内でできることを決めていく必要があります。既存の機能のことも考慮しないといけないため、色々なケースを想定する必要があり、何度も話し合いが必要なこともあります。

 

また私の場合デザイナーにデザイン依頼する前に、自分が実現したいものを伝えるため、ワイヤーフレームを作成します。大まかな配置も伝えたいので、色々と調べたり部内メンバーからもフィードバックをもらうことがあります。ワイヤーフレーム自体は、HTMLやCSSでコードを書きながら調整したり、既にあるコンテンツをつなげながら作っています。ワイヤフレームを作るようになり、他のサイトのレイアウトなども意識するようになりました。

 

まとめ

以前のエンジニアに対して、コーディングだけが仕事内容だというイメージがあったのですが、仕様決めからスケジューリングなどコードを書く上で必要な作業があることも知りました。また未経験でも勉強すればできるようになることも増えると実感し、今後も学び続けたいと思います。 

昨年は目の前のことで精一杯でしたが、今年はES6などの基礎部分を固めたり、もう少しバックエンドのことにも挑戦したりすることで、自分でできることの幅を増やしたいです。

 

 

我々ワウテック技術開発部はこのような環境で開発をしています。
興味を持った方がいらっしゃればいつでもご連絡下さい。

f:id:wowtech-dev:20190313163146j:plain