https://getbootstrap.com/docs/5.1/components/spinners/

// movie.js actions 에 추가

// 사용자가 아직 검색이 끝나지 않았을 때 또 검색을 시도하면 return 시킨다.
if(state.loading) return
// 검색이 시작되면 기본메세지를 지워준다.
commit('updateState', {
	message: '',
  loading: true
})

// try catch 문에 finally 추가한다. 최종적으론 loading 이 끝나니까
finally {
commit('updateState', {
	loading: false
	})
}
// MovieList.vue
// no-result class 는 movies 의 결과에 따라서 생기고 사라진다.
// v-if 문으로 loading 중일 때 spinner 가 돌아가게 한다.
<template>
  <div class="container">
    <div
      :class="{ 'no-result' : !movies.length }"
      class="inner">
      <div
        v-if="loading"
        class="spinner-border text-primary"></div>
      <div
        v-if="message"
        class="message">
        {{ message }}
      </div>
      <div
        v-else
        class="movies">
        <MovieItem
          v-for="movie in movies"
          :key="movie.imdbID"
          :movie="movie" />
      </div>
    </div>
  </div>
</template>

export default {
  components: {
    MovieItem
  },
  computed: {
    movies() {
      // movie 모듈안에 movies
      // 반응성 데이터를 유지하기 위해 computed
      return this.$store.state.movie.movies
    },
    message() {
      return this.$store.state.movie.message
    },
    loading() {
      return this.$store.state.movie.loading
    }
  }
}
</script>

// 첫 화면이거나 검색에 실패 했을 때 회색화면에 텍스트를 띄운다.
<style lang="scss" scoped>
@import "~/scss/main";
.container {
  margin-top: 30px;
  .inner {
    background-color: $gray-200;
    padding: 10px 0;
    border-radius: 4px;
    text-align: center;
    &.no-result {
      padding: 70px 0;
    }
  }
  .message {
    color: $gray-400;
    font-size: 20px;
  }
  .movies {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
  }
}
</style>