티스토리 뷰

그 외/기타

Nuxt3 WebSocket example #2

이안_ian 2024. 7. 29. 13:42




반응형

안녕하세요!!

저번에 nuxt로 채팅을 간단하게 만들어봤는데요

사실 통신은 되지만 채팅이라고 하기에 너무 빈약한것 같아서..

조금씩 생각나는대로 develop을 해보려고 합니다.

 

오늘은 간략하게 로그인도 넣어보고 화면도 수정되면 좋을것 같아서

vuetify로 화면도 적용시킬까 합니다.

Pinia 설치

pinia를 설치하는 이유는 로그인하고 상태유지를 위해서입니다.

npm install @pinia/nuxt

authStore

최상위에서 디렉토리와 파일을 stores/authStore.ts를 생성해줍시다.

import { defineStore } from 'pinia'
import { useFetch } from "@vueuse/core";

export const useAuthStore = defineStore('authStore', () => {
    interface User {
        id: string,
        name: string,
    }

    const userDetail = ref<User | null>()

    function clear() {
        userDetail.value = null;
    }

    async function login(id: string, password: string) {
        const {data, error} = await useFetch("/api/user/login", {
            method: "POST",
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify({id, password})
        })

        if (typeof data.value === "string") {
            const result = JSON.parse(data.value)

            if (result.code === "S" && result.data != null) {

                userDetail.value = {
                    id: result.data.id,
                    name: result.data.name
                }
            } else {
                clear();
            }
        }
    }

    return {userDetail, clear, login}
})

api 설정과 관련해서는 이전 포스팅을 참고해주시고

model/user.ts에는 제가 login쪽을 올렸는데 controller랑 [...].ts 쪽엔 빠져있는데 작업된 부분 참고해서

그 부분만 추가해주시면 될것 같습니다.

테스트

요런식으로 데이터를 잘 가져오는것 같습니다.

이제 화면쪽 구성하러 가볼까요?

Vuetify 설치 & 셋팅

npm i -D vuetify vite-plugin-vuetify
npm i @mdi/font
//nuxt.config.ts

import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
export default defineNuxtConfig({
  //...
  build: {
    transpile: ['vuetify'],
  },
  modules: [
    (_options, nuxt) => {
      nuxt.hooks.hook('vite:extendConfig', (config) => {
        // @ts-expect-error
        config.plugins.push(vuetify({ autoImport: true }))
      })
    },
    //...
  ],
  vite: {
    vue: {
      template: {
        transformAssetUrls,
      },
    },
  },
})
// /plugins/vuetify.ts 디렉토리 및 파일 생성

// import this after install `@mdi/font` package
import '@mdi/font/css/materialdesignicons.css'

import 'vuetify/styles'
import { createVuetify } from 'vuetify'

export default defineNuxtPlugin((app) => {
  const vuetify = createVuetify({
    // ... your configuration
  })
  app.vueApp.use(vuetify)
})
// app.vue 수정

<template>
  <div>
    <v-app>
      <NuxtPage />
    </v-app>
  </div>
</template>

Login.vue 추가

vuetify에서 제공하는 적당한 로그인UI를 가져와서 아까 만들어둔 authStore을 적용시켜보겠습니다.

<template>
  <div>
    <v-sheet class="bg-deep-purple pa-12" rounded>
      <v-card class="mx-auto px-6 py-8" max-width="344">
        <v-form
            @submit.prevent="login"
        >
          <v-text-field
              v-model="id"
              class="mb-2"
              label="id"
              clearable
          ></v-text-field>

          <v-text-field
              v-model="password"
              label="Password"
              type="password"
              placeholder="Enter your password"
              clearable
          ></v-text-field>

          <br>

          <v-btn
              color="success"
              size="large"
              type="submit"
              variant="elevated"
              block
          >
            Sign In
          </v-btn>
        </v-form>
      </v-card>
    </v-sheet>

  </div>
</template>

<script setup lang="ts">
import { useAuthStore } from "@/stores/authStore";

const authStore = useAuthStore()
const id = ref<string>('');
const password = ref<string>('');
const idInput = ref<HTMLInputElement | null>(null);

async function login() {
  if (id.value && password.value) {
    await authStore.login(id.value, password.value)

    if (authStore.userDetail) {
      alert("로그인되었습니다. " + authStore.userDetail.name + "님 환영합니다.")
      navigateTo('/')
    } else {
      id.value = ''
      password.value = ''
      alert("사용자 정보가 일치하지 않습니다.")

      // id로 포커스 이동
      idInput.value?.focus();
    }
  }
}

</script>

<style scoped></style>

그럴듯한 로그인 화면이 나왔네요!

그런데 vuetify 추가하고 나니 index 페이지가 살짝 이상해서 여기도 메시지 입력창에 맞게 약간 수정해볼게요

<script setup lang="ts">

import { useWebSocket } from '@vueuse/core'

const localUrl = 'ws://localhost:3000/api/websocket';
const { status, data, send, open, close } = useWebSocket(localUrl)

const message = ref('')
const history = ref<string[]>([])

watch(data, (receiveMsg: string) => {
  history.value.push(`server: ${receiveMsg}`)
})

function sendMessage() {
  if (message.value == null || message.value === '') {
    return false
  }

  history.value.push(`client : ${message.value}`)
  send(message.value);

  clearMessage();

}

function clearMessage() {
  message.value = ''
}

</script>

<template>
  <div>
    <h1>WebSocket - let's go</h1>
    <div>
      <p v-for="entry in history">{{ entry }}</p>
    </div>
      <v-form @submit.prevent="sendMessage">
        <v-container>
          <v-row>
            <v-col cols="12">
              <v-text-field
                  v-model="message"
                  append-icon="mdi-send"
                  clear-icon="mdi-close-circle"
                  label="Message"
                  type="text"
                  variant="filled"
                  clearable
                  @click:append="sendMessage"
                  @click:clear="clearMessage"
              ></v-text-field>
            </v-col>
          </v-row>
        </v-container>
      </v-form>
  </div>
</template>

 

이제 로그인이 안되면 채팅을 할 수 없게 수정하고 로그인이 되었다면 내 이름이 나오도록 수정해보겠습니다.

// index.vue

<script setup lang="ts">
...

const authStore = useAuthStore()
if (!authStore.userDetail) {
  navigateTo('/Login')
}

function sendMessage() {
  if (message.value == null || message.value === '') {
    return false
  }

  history.value.push(`${authStore.userDetail?.name}: ${message.value}`)
  send(message.value);

  clearMessage();
}

...
</script>

이렇게 내 이름이 나오도록 수정은 했지만... 상대방의 이름이 나오지 않는게 아쉽다!!!!

또한 로그인도 뒤로가기로 갔다가 다시 진입하면 유지가되지만 새로고침을 하면 데이터가 초기화 되는듯하다..

문서가 너무 길어지는듯 하여 이런 이슈들은 추후에 수정해보도록 하겠다.

반응형
댓글
반응형
최근에 달린 댓글
글 보관함
Total
Today
Yesterday
최근에 올라온 글
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30