83. Working with State

npm create vue@latest


이 강의에서는 모달을 토글하기 위해 상태를 사용하는 방법을 시작할 것입니다.
이미 템플릿을 컴포넌트로 분리하는 작업을 마쳤습니다.
이 작업으로 애플리케이션의 혼란을 줄였습니다.
다음 단계는 모달을 토글하는 것입니다.
접근 방법은 다음과 같습니다.
참 또는 거짓이 될 수 있는 값을 저장해야 합니다.
모달의 가시성은 이 값에 의존할 것입니다.
즉, 이 값을 추적해야 합니다.
값을 토글할 방법이 필요합니다.
값을 컴포넌트의 데이터 속성으로 저장할 수 있지만, 상태에 저장하는 것이 더 나은 접근 방법이라고 생각합니다.
이렇게 하면 다른 컴포넌트도 값을 토글할 수 있습니다.
Pinia는 이런 상황을 위해 만들어졌습니다.
모든 컴포넌트에서 사용할 수 있는 데이터를 포함할 수 있습니다.
데이터에 대한 업데이트는 모든 컴포넌트에 반영됩니다.
이 중앙 집중식 상태는 모달의 플래그 값을 저장하기에 이상적입니다.

먼저, 'stores' 디렉토리에서 값을 생성할 것입니다.
'modal.js'라는 파일을 생성합니다.
파일 상단에서 패키지로부터 'defineStore' 함수를 가져옵니다.
'defineStore' 함수는 새로운 저장소를 만드는 데 도움을 줄 것입니다.
저장소를 데이터가 저장된 객체로 생각할 수 있습니다.
저장소는 파일에서 내보내져야 합니다.
그렇지 않으면 컴포넌트가 데이터에 접근할 수 없습니다.
기본 이름공간에서 'defineStore' 함수를 내보냅니다.
이 함수는 두 개의 인자를 가집니다.

첫 번째 인자는 저장소의 ID입니다.
여러 저장소를 가질 수 있기 때문에 정상입니다.
기술적으로 전체 애플리케이션에 대해 하나의 저장소만 만들 수 도 있습니다.
그러나 상태를 여러 저장소로 분할하여 관리하기 쉽게 하는 것이 좋은 관행으로 간주됩니다.
ID는 저장소를 식별하는 데 사용됩니다.
ID를 'Modal'로 설정합시다.
다음으로 객체를 전달합니다.
이 객체에서 상태와 상호작용하는 다양한 속성을 추가할 수 있습니다.
함수를 정의하기 전에, 먼저 'state' 속성을 추가합시다.
이 속성의 값은 객체를 반환하는 화살표 함수입니다.
'state' 객체 안에 'isOpen'이라는 속성을 false로 초기값을 설정합니다.
'state' 객체는 전역적으로 다른 컴포넌트에 사용할 데이터를 추가할 곳입니다.

데이터가 준비되었습니다.
컴포넌트에서 이를 어떻게 접근하는지 배워봅시다.
편집기에서 헤더 컴포넌트 파일을 엽니다.
헤더 컴포넌트에는 네비게이션 메뉴가 있습니다.
이것은 Tailwind 클래스가 있는 순서가 없는 목록입니다.
메뉴 위에는 'Primary navigation'이라는 주석이 있습니다.
'로그인 및 등록'이라고 표시된 링크에 주목하겠습니다.
사용자가 이 링크를 클릭하면 모달을 표시하려고 합니다.
모달의 모습은 'isOpen' 상태 속성의 값에 따라 달라집니다.
참이면 모달이 보이고, 거짓이면 숨겨집니다.
본질적으로, 이 링크를 클릭하면 값이 토글되어야 합니다.
앵커 요소에 이벤트 리스너를 연결하겠습니다.
'Click' 이벤트와 'Prevent' 수정자를 사용하여 'toggleModal' 함수를 호출할 것입니다.
이 함수에는 아무것도 전달하지 않을 것입니다.
함수가 존재하지 않기 때문에 다음에 정의하겠습니다.

스크립트 블록 아래에서 'header'로 이름을 지정하고 'methods' 객체를 생성합니다.
스크립트 블록이 없다면 추가하세요.
'methods' 객체 안에서 'toggleModal' 함수를 정의합니다.
이 함수는 상태에서 생성한 불리언 값을 토글하는 역할을 할 것입니다.
그렇다면 상태에 어떻게 접근할까요?
Pinia는 스토어를 컴포넌트에 병합하여 상태에 접근할 수 있는 함수를 제공합니다.
스크립트 블록 상단에서 Pinia 패키지로부터 'mapStores' 함수를 가져옵니다.
이 함수는 스토어를 컴포넌트에 병합하여 상태에 접근할 수 있게 해줍니다.
이 함수를 실행하기 전에 스토어가 필요합니다.
'useModalStore'라는 이름으로 스토어를 가져옵니다.
스토어에 대한 특정 명명 규칙을 따르는 것이 일반적입니다.
이름은 항상 'use'라는 접두사와 'store'라는 접미사를 붙여야 합니다.
이 명명 규칙은 개발자들이 컴포넌트에서 스토어를 사용하고 있다는 것을 이해하는 데 도움이 됩니다.
선택 사항이지만 권장됩니다.
이 강의에서는 이 관행을 따를 것입니다.
컴포넌트 옵션에서 'computed' 객체를 추가하고 이 객체에서 'mapStores' 함수를 확산 연산자와 함께 호출합니다.
이 함수는 스토어를 받습니다.
'useModalStore' 객체를 전달하겠습니다.

'toggleModal' 함수 안에서 이제 'this.modalStore' 객체를 통해 스토어에 접근할 수 있습니다.
'mapStores' 함수는 ID의 이름으로 객체를 생성합니다.
우리의 경우, 스토어의 ID는 'modal'로 설정되었습니다.
ID의 이름은 'modal.js' 파일에서 확인할 수 있습니다.
이름에 'store'라는 단어가 추가됩니다.
따라서 'user'라는 스토어가 있다면 객체는 'userStore'라고 불릴 것입니다.
함수 내부에서 현재 값을 그 반대로 설정하려고 합니다.
이것은 불리언 값을 토글하는 간단한 방법입니다.
값이 토글되는지 확인하기 위해 로그를 남기려고 합니다.

브라우저에서 개발자 도구를 열고 콘솔 패널로 전환합니다.
그런 다음 '로그인 및 등록' 링크를 여러 번 클릭해보겠습니다.
이렇게 하면 값이 변경되는 것을 볼 수 있습니다.
상태를 업데이트하는 방법을 찾았습니다.
스토어 객체를 통해 상태에 접근할 수 있으며, 원한다면 값을 변경할 수도 있습니다.
훌륭합니다. 우리는 첫 번째 상태 속성을 성공적으로 생성했습니다.

다음 강의에서는 이 상태를 모달에 적용하기 시작할 것입니다.

src/App.vue
<script setup lang="ts">
  import AppHeader from '@/components/AppHeader.vue'
  import AppAuth from '@/components/AppAuth.vue'
</script>

<template>
  <AppHeader />
  <!-- Introduction -->
  <section class="mb-8 py-20 text-white text-center relative">
    <div
        class="absolute inset-0 w-full h-full bg-contain introduction-bg"
        style="background-image: url('assets/img/header.png')"
    ></div>
    <div class="container mx-auto">
      <div class="text-white main-header-content">
        <h1 class="font-bold text-5xl mb-5">Listen to Great Music!</h1>
        <p class="w-full md:w-8/12 mx-auto">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus et dolor mollis, congue
          augue non, venenatis elit. Nunc justo eros, suscipit ac aliquet imperdiet, venenatis et
          sapien. Duis sed magna pulvinar, fringilla lorem eget, ullamcorper urna.
        </p>
      </div>
    </div>

    <img
        class="relative block mx-auto mt-5 -mb-20 w-auto max-w-full"
        src="/assets/img/introduction-music.png"
    />
  </section>

  <!-- Main Content -->
  <section class="container mx-auto">
    <div class="bg-white rounded border border-gray-200 relative flex flex-col">
      <div class="px-6 pt-6 pb-5 font-bold border-b border-gray-200">
        <span class="card-title">Songs</span>
        <!-- Icon -->
        <i class="fa fa-headphones-alt float-right text-green-400 text-xl"></i>
      </div>
      <!-- Playlist -->
      <ol id="playlist">
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
        <li
            class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
        >
          <div>
            <a href="#" class="font-bold block text-gray-600">Song Title</a>
            <span class="text-gray-500 text-sm">Artist Name</span>
          </div>

          <div class="text-gray-600 text-lg">
            <span class="comments">
              <i class="fa fa-comments text-gray-600"></i>
              15
            </span>
          </div>
        </li>
      </ol>
      <!-- .. end Playlist -->
    </div>
  </section>

  <!-- Player -->
  <div class="fixed bottom-0 left-0 bg-white px-4 py-2 w-full">
    <!-- Track Info -->
    <div class="text-center">
      <span class="song-title font-bold">Song Title</span> by
      <span class="song-artist">Artist</span>
    </div>
    <div class="flex flex-nowrap gap-4 items-center">
      <!-- Play/Pause Button -->
      <button type="button">
        <i class="fa fa-play text-gray-500 text-xl"></i>
      </button>
      <!-- Current Position -->
      <div class="player-currenttime">00:00</div>
      <!-- Scrub Container  -->
      <div class="w-full h-2 rounded bg-gray-200 relative cursor-pointer">
        <!-- Player Ball -->
        <span class="absolute -top-2.5 -ml-2.5 text-gray-800 text-lg" style="left: 50%">
          <i class="fas fa-circle"></i>
        </span>
        <!-- Player Progress Bar-->
        <span
            class="block h-2 rounded bg-gradient-to-r from-green-500 to-green-400"
            style="width: 50%"
        ></span>
      </div>
      <!-- Duration -->
      <div class="player-duration">03:06</div>
    </div>
  </div>

  <AppAuth />
</template>