130. Navigating with Links

npm create vue@latest


이번엔 애플리케이션 내에서 페이지 간에 탐색할 수 있는 링크를 만드는 방법을 배울 것입니다.
사용자가 주소 표시줄에 URL을 수동으로 입력하여 탐색하는 대신, 접근하길 원하는 페이지로의 링크를 제공할 것입니다.
우선 헤더의 메뉴에 있는 링크를 기능적으로 만들 것입니다.
헤더 컴포넌트 파일에서 작업을 진행할 것입니다.
템플릿 내부에는 순서가 없는 링크 목록이 있습니다.
링크가 클릭될 때 기본 동작을 중지시키고 싶습니다.
브라우저가 새로운 요청을 하여 페이지를 새로 고침하는 것을 원하지 않습니다.
대신, 라우터에서 설정한 경로 중 하나와 일치하는지 확인하고 싶습니다.
일치하는 경로가 있다면, 브라우저가 자산을 다시 로드하지 않고 사용자를 리디렉션하고 싶습니다.
라우터 라이브러리는 이 작업을 대신 처리해줄 컴포넌트를 제공합니다.
우리는 홈페이지로의 링크를 만들기 시작할 것입니다.
위쪽에는 'App Name'이라고 코멘트된 앵커 태그가 있습니다.
앵커 태그르 ㄹ사용하는 대신 'RouterLink'라고 불리는 컴포넌트로 이를 대체할 것입니다.
이 컴포넌트는 Vue 라우터 플러그인에 의해 등록된 전역 컴포넌트입니다.
RouterLink 컴포넌트는 앵커 태그를 만들 것입니다.
주된 차이점은 이 앵커 태그가 클릭 이벤트를 듣고 있다는 것입니다.
클릭이 감지되면 기본 동작이 오버라이드됩니다.
라우터가 브라우저 대신 리디렉션을 처리할 것입니다.
클래스 속성은 그대로 두어도 됩니다.
이것은 컴포넌트에 의해 생성된 앵커 요소에 적용될 것입니다.
우리는 ref 속성을 수정할 것입니다.
그리고 이름을 to로 변경할 것입니다.
이것은 리디렉션할 경로를 설정할 수 있는 속성입니다.
경로 앞에 슬래시를 추가할 수도 있습니다.
만약 슬래시를 추가한다면, 라우터는 경로를 도메인에 추가할 것입니다.
그렇지 않다면 현재 경로에 경로를 추가할 것입니다.
예를 들어, 이것을 about으로 설정한다고 가정해봅시다.
만약 현재 URLdl example.com이라면, 라우터는 example.com/about으로 리디렉션할 것입니다.
그러나 현재 URL이 example.com/music/test이라면, 라우터는 example.com/music/about으로 리디렉션할 것입니다.
이것이 당신이 원하는 동작이라면, 이렇게 설정하면 됩니다.

그러나 당신이 리디렉션하고자 하는 URL이 example.com/about이라면, 경로 앞에 /를 추가해야 합니다.
이것이 사실인지 테스트해봅시다.
이것이 홈 링크라고 하더라도 예시 목적으로 about 경로를 설정할 것입니다.
링크를 클릭하기 전에 브라우저에서 앱을 열고 개발자 도구에서 검사해봅시다.
RouterLink 컴포넌트는 앵커 요소로 대체될 것입니다.
ref 속성은 우리가 전달한 경로로 설정됩니다.
이것은 라우터 라이브러리가 URL에 상대적인 전체 경로를 필요로 하기 때문에 슬래시가 미리 추가될 수 있습니다.
클래스 속성에는 컴포넌트에 추가한 클래스들이 있습니다.
추가적인 클래스들이 추가되어 있는데, 우리는 링크를 스타일링하는 데 사용할 수 있습니다.
우리는 곧 그렇게 할 것이지만, 지금은 이것들을 무시할 것입니다.
라우터 라이브러리는 우리 앱에 CSS 코드를 로드하지 않습니다.
우리는 그것들에 대해 걱정할 필요가 없습니다.
마지막으로, 앵커 태그 안의 텍스트는 컴포넌트 안에 배치된 동일한 텍스트입니다.
어떤 면에서, Router Link 컴포넌트는 거의 앵커 요소와 같습니다.
주요 차이점은 리디렉션을 처리하기 위한 일부 이벤트 리스너를 앵커 요소에 연결할 것입니다.
링크를 클릭하여 테스트해 봅시다.
클릭한 후 거의 즉시, 메인 콘텐츠가 홈 컴포넌트에서 about 컴포넌트로 전환됩니다.
브라우저는 콘텐츠를 다시 로드할 필요가 없습니다.
Vue는 변경사항을 처리합니다.
이것을 Vue 개발자 도구로 전환하여 확인할 수 있습니다.
Rooter 히스토리 섹션에서.
우리는 경로 변경에 대한 로그를 찾을 것입니다.
이 로그는 컴포넌트들이 서로 교체되었음을 증명합니다.
이것은 예상대로 작동하지만, URL 바 내에서 이 구현과 관련된 문제가 있습니다.
저는 경로를 test/test로 변경할 것입니다.
만약 경로가 레코드 중 어느 것과도 일치하지 않으면, 라우터는 아무것도 렌더링하지 않을 것입니다.
이것은 우리가 원하는 바가 아닙니다.
우리는 404 페이지를 만들어 이 행동을 오버라이드할 수 있지만, 그것은 다른 강의에서 다룰 것입니다.
지금은 다시 링크를 클릭할 것입니다.
이번에는 라우터가 test/about으로 네비게이션할 것입니다.
이 행동은 우리가 원하는 것이 아닙니다.
우리는 사용자를 /about 경로로 리디렉션하고 싶습니다.
만약 슬래시를 추가하지 않으면, 경로는 현재 경로에 추가될 것입니다.
그것은 기본 도메인에 상대적인 경로를 기반으로 리디렉션되지 않을 것입니다.
우리는 경로 앞에 항상 슬래시를 추가함으로써 이 문제를 해결할 수 있습니다.
에디터로 돌아가서 'to' 속성에 경로 앞에 슬래시 문자를 추가해 업데이트합시다.
다시 테스트해 봅시다, 중첩된 경로에서 시작하여.
다시 링크를 클릭하겠습니다.
브라우저는 슬래시 문자를 추가함으로써 /about 경로로 우리를 데려갈 것입니다.
우리는 경로가 현재 경로가 아닌 도메인에 추가될 것이라는 것을 확신할 수 있습니다.
때때로 슬래시 문자를 추가하는 것을 잊을 수 있으므로 이것을 강조하고 싶었습니다.
저도 그랬습니다.
다음 강의에서는 우리 앱의 다른 페이지들에 대한 링크를 만드는 작업을 진행할 것입니다.

src/components/AppHeader.vue
<script setup lang="ts">
import useModal from '@/stores/modal'
import useUser from '@/stores/user'
import { storeToRefs } from 'pinia'

const userStore = useUser()
const { signOut } = userStore
const { userLoggedIn } = storeToRefs(userStore)

const modalStore = useModal()
const { isOpen } = storeToRefs(modalStore)

const toggleAuthModal = () => {
  isOpen.value = !isOpen.value
  console.log(isOpen.value)
}
</script>

<template>
  <!-- Header -->
  <header id="header" class="bg-gray-700">
    <nav class="container mx-auto flex justify-start items-center py-5 px-4">
      <!-- App Name -->
      <RouterLink class="text-white font-bold uppercase text-2xl mr-4" to="/about">
        Music
      </RouterLink>

      <div class="flex flex-grow items-center">
        <!-- Primary Navigation -->
        <ul class="flex flex-row mt-1">
          <!-- Navigation Links -->
          <li v-if="!userLoggedIn">
            <a class="px-2 text-white" href="#" @click.prevent="toggleAuthModal">
              Login / Register
            </a>
          </li>
          <template v-else>
            <li>
              <a class="px-2 text-white" href="#">Manage</a>
            </li>
            <li>
              <a class="px-2 text-white" href="#" @click.prevent="signOut">Logout</a>
            </li>
          </template>
        </ul>
      </div>
    </nav>
  </header>
</template>

<style scoped></style>