21.03.03 lamplight서비스 프로젝트(글쓰기,상세페이지,로그인 ~ 회원가입까지)

2021. 3. 3. 22:59Vue.js/Spring & Vue APP 프로젝트(프론트엔드)

#NOTE

<post 전송방식>
1.form 방식
 일반 form 형식
2.axios json 방식
 json 형식
:to와 to의 차이??
:이 있으면 자바스크립트 사용 가능

ex)
to="article/list"
:to="'article/list?boardId' + article.boardId"
protected _handleResponse(axiosResponse:AxiosResponse) : AxiosResponse {

    // 로그인 정보 체크
    // axiosResponse?.data?.resultCode == "F-A" || axiosResponse?.data?.resultCode == "F-B" (초보 버전)
    // axiosResponse?.data?.resultCode == "F-A" => 로그인 정보가 아예 없는 경우
    // axiosResponse?.data?.resultCode == "F-B" => 로그인 정보가 틀린 경우

    // axiosResponse?.data?.resultCode == "F-A" || axiosResponse?.data?.resultCode == "F-B" (초보 버전)
    //["F-A", "F-B"].includes(axiosResponse?.data?.resultCode) (고급 버전)
    
    /* 로그인 정보 체크 후 로그인 화면으로 이동 */
    if (["F-A", "F-B"].includes(axiosResponse?.data?.resultCode)) {
      alert('로그인 후 이용해주세요.');
      location.replace('/member/login');
    }

    return axiosResponse;
  }
<컴포넌트>

현재 앱 컴포넌트와 페이지별 컴포넌트로 구성
페이지 컴포넌트는 현재 라우터(주소)에 의해 자동적으로 선택되고 있음
<state: 상태>

페이지 글과 같은 상태는 전역적으로 필요하지 않음
하지만 로그인 정보의 경우 전역적으로 필요함 
이를 위해 전역상태를 구축해야함

#주요소스코드

<main.ts>

import { createApp, reactive, computed } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'

// 앱 컴포넌트 불러오기
import App from './App.vue'

// 전역 CSS 불러오기
import './index.css'

// 전역 컴포넌트 불러오기
import * as Util from './utils/'; // utils파일로 부터 가져오는 모든 것(*)은 Util로 치환
import TitleBar from './components/TitleBar.vue';
import FormRow from './components/FormRow.vue';

// 각 페이지 불러오기
import HomeMainPage from './pages/HomeMainPage.vue'
import ArticleListPage from './pages/ArticleListPage.vue'
import ArticleWritePage from './pages/ArticleWritePage.vue'
import ArticleDetailPage from './pages/ArticleDetailPage.vue'
import MemberLoginPage from './pages/MemberLoginPage.vue'
import MemberJoinPage from './pages/MemberJoinPage.vue'

// 전역state 만들기
/// localStorage에서 로그인 정보 가져오기
const authKey = localStorage.getItem("authKey")
const loginedMemberId  = Util.toIntOrNull(localStorage.getItem("loginedMemberId"))
const loginedMemberName = localStorage.getItem("loginedMemberName")
const loginedMemberNickname  = localStorage.getItem("loginedMemberNickname")

/*state => 상태
페이지 글과 같은 state는 전역적으로 필요하지 않음
하지만 로그인 정보의 경우 전역적으로 필요함 
이를 위해 전역state(=> globalShare)를 구축해야함*/
const globalShare:any = reactive({
  //loginedMember:{},  //loginedMember:{}는 비어있는 상태
  //로그인 정보 채우기
  loginedMember:{
    authKey,
    id:loginedMemberId,
    name:loginedMemberName,
    nickname:loginedMemberNickname,
  },
  //globalShare.loginedMember가 비어있지 않는지를 computed로 자동 체크
  //비어있지 않다면(===false) isLogined
  //isLogined: computed(() => Util.isEmptyObject(globalShare.loginedMember) === false)  
  isLogined: computed(() => globalShare.loginedMember.id !== null ),
  
  //로그아웃
  logout: () => { 
      localStorage.removeItem("authKey");
      localStorage.removeItem("loginedMemberId");
      localStorage.removeItem("loginedMemberName");
      localStorage.removeItem("loginedMemberNickname");

      location.replace('/member/login');
  }
});

/*테스트용
setTimeout(() => {
  globalShare.loginedMember.id = 1;
}, 5000);
*/

// MainApi 불러오기
import { MainApi } from './apis/'

// MainApi 객체 생성
const mainApi = new MainApi();

// 라우팅 정보 설정(구성)
const routes = [
  { 
    path: '/', 
    component: HomeMainPage 
  },
  { 
    path: '/article/list', 
    component: ArticleListPage, 
    //주소에 바로 접근하지 않게끔하고
    //props로 들어오는 경로로 접근하도록??
    props: (route:any) => ({ boardId: Util.toIntOrUnd(route.query.boardId), globalShare })
  },
  { 
    path: '/article/write', 
    component: ArticleWritePage, 
    props: (route:any) => ({ boardId: Util.toIntOrUnd(route.query.boardId), globalShare })
  },
  { 
    path: '/article/detail', 
    component: ArticleDetailPage, 
    props: (route:any) => ({ id: Util.toIntOrUnd(route.query.id), globalShare })
  },
  {
    path: '/member/login',
    component: MemberLoginPage,
    props: (route:any) => ({globalShare})
  },
  {
    path: '/member/join',
    component: MemberJoinPage,
    props: (route:any) => ({globalShare})
  }

    

];

// 라우팅 정보를 가져오는 라우터 생성
const router = createRouter({
  //Provide the history implementation to use. We are using the hash history for simplicity here.
  history: createWebHistory(),
  //routes : routes 이름이 똑같으면 아래처럼 축약 가능
  routes
});


//앱 생성
//createApp(App).mount('#app') 이것을 풀어보면 아래와 같음
const app = createApp(App, {
  globalShare
});

//앱에 전역 라이브러리 등록
app.config.globalProperties.$mainApi = mainApi;
app.config.globalProperties.$router = router;

//앱에 전역 컴포넌트 적용
//모든 곳에서 컴포넌트를 불러올수 있게 해줌
app.component('TitleBar',TitleBar)
app.component('FormRow',FormRow)

//앱에 라우터 적용
app.use(router)

//앱 표시
app.mount('#app');

<apis/index.ts>

import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import {IArticle} from '../types'

// API 원형
abstract class HttpClient {
  protected readonly instance: AxiosInstance;

  public constructor(instance: AxiosInstance) {
    this.instance = instance;

    this._initializeRequestInterceptor();
    this._initializeResponseInterceptor();
  }

  private _initializeRequestInterceptor() {
    this.instance.interceptors.request.use(
      this._handleRequest,
      this._handleError,
    );
  };

  private _initializeResponseInterceptor() {
    this.instance.interceptors.response.use(
      this._handleResponse,
      this._handleError,
    );
  };

  protected _handleRequest(config:AxiosRequestConfig) : AxiosRequestConfig {
    return config;
  }

  protected _handleResponse(axiosResponse:AxiosResponse) : AxiosResponse {
    return axiosResponse;
  }

  protected _handleError(error: AxiosError) {
    if (error.response) {
      // 요청이 이루어졌으며 서버가 2xx의 범위를 벗어나는 상태 코드로 응답했습니다.
      alert('요청을 처리하는 중에 오류가 발생하였습니다.');
    }
    else if (error.request) {
      // 요청이 이루어 졌으나 응답을 받지 못했습니다.
      // `error.request`는 브라우저의 XMLHttpRequest 인스턴스 또는
      // Node.js의 http.ClientRequest 인스턴스입니다.
      alert('서버 또는 네트워크의 상태가 좋지 않습니다.');
    }
    else {
      // 오류를 발생시킨 요청을 설정하는 중에 문제가 발생했습니다.
      console.log('Error', error.message);
    }

    return Promise.reject(error);
  };

  // POST전송으로 전송하기 위한 로직
  public postByForm<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R> {
    const params = new URLSearchParams();

    for ( let key in data ) {
      params.append(key, data[key]);
    }

    config =  {} as AxiosRequestConfig;

    config.headers = {
      'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
      'Accept': '*/*'
    };

    return this.instance.post(url, params, config);
  }


}

// 응답타입1
interface Base__IResponseBodyType1 {
  resultCode:string;
  msg:string;
  fail:boolean;
  success:boolean;
}

// /usr/article/list 의 응답 타입
export interface MainApi__article_list__IResponseBody extends Base__IResponseBodyType1 {
  body:{
    articles: IArticle[]
  };
}

// /usr/article/detail 의 응답 타입
export interface MainApi__article_detail__IResponseBody extends Base__IResponseBodyType1 {
  body:{
    article: IArticle
  };
}

// /usr/article/doAdd 의 응답 타입
export interface MainApi__article_doWrite__IResponseBody extends Base__IResponseBodyType1 {
  body:{
    id: number
  };
}

// /usr/member/authKey 의 응답 타입
export interface MainApi__member_authKey__IResponseBody extends Base__IResponseBodyType1 {
  body:{
    authKey : string,
    id : number,
    name : string,
    nickname : string
  };
}

// /usr/member/doJoin 의 응답 타입
export interface MainApi__member_doJoin__IResponseBody extends Base__IResponseBodyType1 {
  body:{
    id: number
  };
}


// http://localhost:8024/usr/ 와의 통신장치
export class MainApi extends HttpClient {
  public constructor() {
    super(
      axios.create({
        baseURL:'http://localhost:8024/usr/',
      })
    );
  }

  protected _handleRequest(config:AxiosRequestConfig) {
    config.params = {};
    config.params.authKey = localStorage.getItem("authKey");
    return config;
  };

  protected _handleResponse(axiosResponse:AxiosResponse) : AxiosResponse {

    // 로그인 정보 체크
    // axiosResponse?.data?.resultCode == "F-A" || axiosResponse?.data?.resultCode == "F-B" (초보 버전)
    // axiosResponse?.data?.resultCode == "F-A" => 로그인 정보가 아예 없는 경우
    // axiosResponse?.data?.resultCode == "F-B" => 로그인 정보가 틀린 경우

    // axiosResponse?.data?.resultCode == "F-A" || axiosResponse?.data?.resultCode == "F-B" (초보 버전)
    //["F-A", "F-B"].includes(axiosResponse?.data?.resultCode) (고급 버전)
    
    /* 로그인 정보 체크 후 로그인 화면으로 이동 */
    if (["F-A", "F-B"].includes(axiosResponse?.data?.resultCode)) {
      alert('로그인 후 이용해주세요.');

      //비정상 로그인인 경우 localStorage 초기화
      localStorage.removeItem("authKey");
      localStorage.removeItem("loginedMemberId");
      localStorage.removeItem("loginedMemberName");
      localStorage.removeItem("loginedMemberNickname");

      location.replace('/member/login');
    }

    return axiosResponse;
  }

  // http://localhost:8024/usr/article/list?boardId=? 를 요청하고 응답을 받아오는 함수
  public article_list(boardId: number) {
    return this.instance.get<MainApi__article_list__IResponseBody>(`/article/list?boardId=${boardId}`);
  }

  // http://localhost:8024/usr/detail/id=? 를 요청하고 응답을 받아오는 함수
  public article_detail(id: number) {
    return this.instance.get<MainApi__article_detail__IResponseBody>(`/article/detail?id=${id}`);
  }

  // http://localhost:8024/usr/article/doAdd/boardId=?&title=?&body=? 를 요청하고 응답을 받아오는 함수
  // postByForm: post 전송을 스프링이 이해할 수 있는 form형식으로 전송시켜주는 함수?
  public article_doWrite(boardId:number, title: string, body: string) {
    return this.postByForm<MainApi__article_doWrite__IResponseBody>(
      `/article/doAdd`, {
        boardId,
        title,
        body
      }
    );
  }

  // http://localhost:8024/usr/member/authKey/loginId=?&loginPw=? 를 요청하고 응답을 받아오는 함수
  // postByForm: post 전송을 스프링이 이해할 수 있는 form형식으로 전송시켜주는 함수?
  public member_authKey(loginId: string, loginPw: string) {
      return this.instance.get<MainApi__member_authKey__IResponseBody>(`/member/authKey?loginId=${loginId}&loginPw=${loginPw}`);
    }

  // http://localhost:8024/usr/member/doJoin/loginId=?&loginPw=?...... 를 요청하고 응답을 받아오는 함수
  public member_doJoin(loginId:string, loginPw:string, name:string, nickname:string, cellphoneNo:string, email:string) {
    return this.postByForm<MainApi__member_doJoin__IResponseBody>(
      `/member/doJoin`, {
        loginId,
        loginPw,
        name,
        nickname,
        cellphoneNo,
        email
      }
    );
  }

} 

<App.vue>

<!-- vue를 하나의 jsp라고 생각하면 됨 -->

<template>


  <header class="header-bar h-20 bg-black text-white">
    <div class="container mx-auto flex h-full">
      <!-- router-link는 a와 같다고 보면 됨 -->
      <!-- to는 href -->
      <router-link to="/" class="h-full flex items-center px-2">
        <img class="block w-10" src="./assets/logo.png" alt="">
      </router-link>

      <div class="flex-grow"></div>

      <nav class="header-bar__menu-box-1 overflow-x-auto">
        <ul class="flex h-full">
          <li>
            <router-link to="/" class="h-full flex items-center font-bold px-4 hover:bg-white hover:text-black whitespace-nowrap">
              홈
            </router-link>
          </li>
          <li v-if="globalShare.isLogined == false">
            <router-link to="/member/login" class="h-full flex items-center font-bold px-4 hover:bg-white hover:text-black whitespace-nowrap">
              로그인
            </router-link>
          </li>
          <li v-if="globalShare.isLogined == false">
            <router-link to="/member/join" class="h-full flex items-center font-bold px-4 hover:bg-white hover:text-black whitespace-nowrap">
              회원가입
            </router-link>
          </li>
          <li v-if="globalShare.isLogined">
            <a v-on:click="globalShare.logout" class="cursor-pointer h-full flex items-center font-bold px-4 hover:bg-white hover:text-black whitespace-nowrap">
              로그아웃
            </a>
          </li>
          <li v-if="globalShare.isLogined">
            <router-link to="/article/write" class="h-full flex items-center font-bold px-4 hover:bg-white hover:text-black whitespace-nowrap">
              글쓰기
            </router-link>
          </li>
          <li>
            <router-link to="/article/list?boardId=1" class="h-full flex items-center font-bold px-4 hover:bg-white hover:text-black whitespace-nowrap">
              공지사항 게시판
            </router-link>
          </li>
          <li>
            <router-link to="/article/list?boardId=2" class="h-full flex items-center font-bold px-4 hover:bg-white hover:text-black whitespace-nowrap">
              자유게시판
            </router-link>
          </li>
        </ul>
      </nav>
    </div>
  </header>

    
   <main>
    <!-- 이것을 해주어야 다른 페이지를 불러올 수 있음 -->
    <router-view></router-view>
  </main>

</template>

<script lang="ts">
import { defineComponent, computed } from 'vue'


export default defineComponent({
  name: 'App',
  props: {
    globalShare: {
      type: Object,
      required: true
    },
  },
  setup() {
    
  }

})
</script>

<style lang="postcss">
.btn-type-1 {
  @apply py-2 px-4 text-white transition ease-in duration-200 text-center text-base font-semibold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 rounded-lg cursor-pointer;
}

.btn-primary {
  @apply py-2 px-4 bg-blue-600 hover:bg-blue-700 focus:ring-blue-500 focus:ring-offset-blue-200 btn-type-1;
}
.btn-secondary {
  @apply py-2 px-4 bg-gray-600 hover:bg-gray-700 focus:ring-gray-500 focus:ring-offset-gray-200 btn-type-1;
}
.btn-success {
  @apply py-2 px-4 bg-green-600 hover:bg-green-700 focus:ring-green-500 focus:ring-offset-green-200 btn-type-1;
}
.btn-danger {
  @apply py-2 px-4 bg-red-600 hover:bg-red-700 focus:ring-red-500 focus:ring-offset-red-200 btn-type-1;
}
.btn-warning {
  @apply py-2 px-4 bg-yellow-600 hover:bg-yellow-700 focus:ring-yellow-500 focus:ring-offset-yellow-200 btn-type-1;
}
.btn-info {
  @apply py-2 px-4 bg-purple-600 hover:bg-purple-700 focus:ring-purple-500 focus:ring-offset-purple-200 btn-type-1;
}
.btn-link {
  @apply underline text-blue-500 hover:text-red-500 cursor-pointer;
}


/* css 선택자 */
/* .btns 자식들 중에 class로 'btn-'를 갖고 있는 엘리먼트의 다음 엘리먼트 */
.btns > [class*="btn-"] + [class*="btn-"] {
  margin-left: theme('spacing.2');
}
</style> 

<ArticleWritePage.vue>

<template>

  <TitleBar>글쓰기</TitleBar>

  <section class="section section-article-write-form-box px-2">
    <div class="container mx-auto">
      <div class="px-6 py-6 bg-white rounded-lg shadow-md">
        <form v-if="globalShare.isLogined" v-on:submit.prevent="checkAndWriteArticle">
          <FormRow title="게시판">
            <select class="form-row-select" ref="newArticleBoardIdElRef">
              <option value="1">NOTICE</option>
              <option value="2">FREE</option>
            </select>
          </FormRow>
          <FormRow title="제목">
            <input ref="newArticleTitleElRef" class="form-row-input" type="text" placeholder="제목을 입력해주세요.">
          </FormRow>
          <FormRow title="내용">
            <textarea ref="newArticleBodyElRef" class="form-row-input" placeholder="내용을 입력해주세요."></textarea>
          </FormRow>
          <FormRow title="작성">
            <div class="btns">
              <input type="submit" value="작성" class="btn-primary" />
            </div>
          </FormRow>
        </form>
        <div v-else>
          <router-link class="btn-link" to="/member/login">로그인</router-link> 후 이용해주세요.
        </div>
      </div>
    </div>
  </section>


</template>

<script lang="ts">
import { defineComponent, reactive, ref, getCurrentInstance, onMounted } from 'vue'
import { MainApi } from '../apis/'
import { Router } from 'vue-router'


export default defineComponent({
  name: 'ArticleWritePage',

  props: {
    globalShare: {
      type: Object,
      required: true
    },
    boardId: {
      type: Number,
      required: true,
      default:1
    }
  },

  setup(props){
    const router:Router = getCurrentInstance()?.appContext.config.globalProperties.$router;
    const mainApi:MainApi = getCurrentInstance()?.appContext.config.globalProperties.$mainApi;

    const newArticleBoardIdElRef = ref<HTMLInputElement>();
    const newArticleTitleElRef = ref<HTMLInputElement>();
    const newArticleBodyElRef = ref<HTMLInputElement>();

    //boardId 파라미터 값에 따라 게시판 선택 옵션 값(newArticleBoardIdElRef.value.value)이 바뀜
    onMounted(() => {
        if(newArticleBoardIdElRef.value == null){
        return;
      }
        newArticleBoardIdElRef.value.value = props.boardId + "";
      });

    /* 공백 체크 */
    function checkAndWriteArticle(){

      if(newArticleBoardIdElRef.value == null){
        return;
      }

      const newArticleBoardIdEl = newArticleBoardIdElRef.value;


      //일반적으로 안해도 되지만 typescript에서는 해야됨
      if(newArticleTitleElRef.value == null){
        return;
      }

      const newArticleTitleEl = newArticleTitleElRef.value;
      newArticleTitleEl.value = newArticleTitleEl.value.trim();

      if(newArticleTitleEl.value.length == 0){
        alert('제목을 입력해 주세요.')
        newArticleTitleEl.focus();
        return;
      }

      if(newArticleBodyElRef.value == null){
        return;
      }

      const newArticleBodyEl = newArticleBodyElRef.value;
      newArticleBodyEl.value = newArticleBodyEl.value.trim();

      if(newArticleBodyEl.value.length == 0){
        alert('내용을 입력해 주세요.')
        newArticleBodyEl.focus();
        return;
      }

      // 글작성 함수로 보내기
      writeArticle(parseInt(newArticleBoardIdEl.value), newArticleTitleEl.value, newArticleBodyEl.value);

    }

    //typescript에서는 title:string, body:string 이런식으로 type을 적어주어야 한다
      function writeArticle(boardId:number, title:string, body:string){
       
        mainApi.article_doWrite(boardId, title, body)
        .then(axiosResponse => {
          alert(axiosResponse.data.msg);
          
          // 로그인이 fail 상태이면 리턴
          if ( axiosResponse.data.fail ) {
            return;
          }

          //authKey가 있는 상태에서 가능
          const newArticleId = axiosResponse.data.body.id;
          //alert(newArticleId + "번 게시물 등록 완료!!");

          router.replace("detail?id=" + newArticleId);
        });
      }

    return{
      newArticleBoardIdElRef,
      newArticleTitleElRef,
      newArticleBodyElRef,
      checkAndWriteArticle
      
    }

  }
  
})
</script>

<style scoped>

</style>

<MemberLoginPage.vue>

<template>
  <TitleBar>로그인</TitleBar>

  <section class="section section-member-login-form px-2">
    <div class="container mx-auto">
      <div class="px-6 py-6 bg-white rounded-lg shadow-md">
        <form v-if="globalShare.isLogined == false" v-on:submit.prevent="checkAndLogin">
          <FormRow title="로그인아이디">
            <input ref="loginIdElRef" class="form-row-input" type="text" placeholder="아이디를 입력해주세요.">
          </FormRow>
          <FormRow title="로그인비밀번호">
            <input ref="loginPwElRef" class="form-row-input" type="password" placeholder="로그인비밀번호를 입력해주세요.">
          </FormRow>
          <FormRow title="로그인">
            <div class="btns">
              <input type="submit" value="로그인" class="btn-primary" />
            </div>
          </FormRow>
        </form>
        <div v-else>
          이미 로그인 되었습니다. <route-link class="btn-link" to="/">홈</route-link> 으로 이동
        </div>
      </div>
    </div>
  </section>
</template>

<script lang="ts">
import { defineComponent, ref, getCurrentInstance, onMounted } from 'vue'
import { IArticle } from '../types/'
import { MainApi } from '../apis/'
import { useRoute } from 'vue-router'
import { Router } from 'vue-router'

export default defineComponent({
  name: 'MemberLoginPage',
  props: {
    globalShare: {
      type: Object,
      required: true
    },
  },
  setup(props) {
    const route = useRoute();
    const router:Router = getCurrentInstance()?.appContext.config.globalProperties.$router;
    const mainApi:MainApi = getCurrentInstance()?.appContext.config.globalProperties.$mainApi;
    const loginIdElRef = ref<HTMLInputElement>();
    const loginPwElRef = ref<HTMLInputElement>();


    
    onMounted(() => {
      // 만약, route로 들어온 쿼리의 loginId가 null이 아니면
      if ( route.query.loginId != null ) {
        if ( loginIdElRef.value == null ) {
          return;
        }
        if ( loginPwElRef.value == null ) {
          return;
        }

        // loginIdElRef 값에 route.query.loginId 정보 담기
        loginIdElRef.value.value = route.query.loginId as any;
        // loginPwElRef에 포커스
        loginPwElRef.value.focus();
      }
    })


    function checkAndLogin() {
      if ( loginIdElRef.value == null ) {
        return;
      }

      const loginIdEl = loginIdElRef.value;
      loginIdEl.value = loginIdEl.value.trim();

      if ( loginIdEl.value.length == 0 ) {
        alert('아이디를 입력해주세요.');
        loginIdEl.focus();
        return;
      }
      if ( loginPwElRef.value == null ) {
        return;
      }

      const loginPwEl = loginPwElRef.value;
      loginPwEl.value = loginPwEl.value.trim();

      if ( loginPwEl.value.length == 0 ) {
        alert('비밀번호를 입력해주세요.');
        loginPwEl.focus();
        return;
      }

      //로그인
      login(loginIdEl.value, loginPwEl.value);
    
    }
    function login(loginId:string, loginPw:string) {
      mainApi.member_authKey(loginId, loginPw)
        .then(axiosResponse => {
          alert(axiosResponse.data.msg);
          
          // 로그인이 fail 상태이면 리턴
          if ( axiosResponse.data.fail ) {
            return;
          }

          const authKey = axiosResponse.data.body.authKey;
          const loginedMemberId = axiosResponse.data.body.id;
          const loginedMemberName = axiosResponse.data.body.name;
          const loginedMemberNickname = axiosResponse.data.body.nickname;

          localStorage.setItem("authKey", authKey);
          localStorage.setItem("loginedMemberId", loginedMemberId + "");
          localStorage.setItem("loginedMemberName", loginedMemberName);
          localStorage.setItem("loginedMemberNickname", loginedMemberNickname);
          
          props.globalShare.loginedMember = {
            authKey,
            id:loginedMemberId,
            name:loginedMemberName,
            nickname:loginedMemberNickname,
          };

          //alert(axiosResponse.data.msg);

          router.replace('/')
        });
    }
    return {
      checkAndLogin,
      loginIdElRef,
      loginPwElRef
    }
  }
})
</script>

<style scoped>
</style> 

<MemberJoinPage.vue>

<template>
  <TitleBar>회원가입</TitleBar>

  <section class="section section-member-join-form px-2">
    <div class="container mx-auto">
      <div class="px-6 py-6 bg-white rounded-lg shadow-md">
        <form v-if="globalShare.isLogined == false" v-on:submit.prevent="checkAndJoin">
          <FormRow title="아이디">
            <input ref="loginIdElRef" class="form-row-input" type="text" placeholder="아이디를 입력해주세요.">
          </FormRow>
          <FormRow title="비밀번호">
            <input ref="loginPwElRef" class="form-row-input" type="password" placeholder="비밀번호를 입력해주세요.">
          </FormRow>
          <FormRow title="비밀번호 확인">
            <input ref="loginPwConfirmElRef" class="form-row-input" type="password" placeholder="비밀번호 확인을 해주세요.">
          </FormRow>
          <FormRow title="이름">
            <input ref="nameElRef" class="form-row-input" type="text" placeholder="이름을 입력해주세요.">
          </FormRow>
          <FormRow title="닉네임">
            <input ref="nicknameElRef" class="form-row-input" type="text" placeholder="닉네임을 입력해주세요.">
          </FormRow>
          <FormRow title="전화번호">
            <input ref="cellphoneNoElRef" class="form-row-input" type="tel" placeholder="전화번호를 입력해주세요.">
          </FormRow>
          <FormRow title="이메일">
            <input ref="emailElRef" class="form-row-input" type="email" placeholder="이메일을 입력해주세요.">
          </FormRow>
          <FormRow title="가입">
            <div class="btns">
              <input type="submit" value="가입" class="btn-primary" />
            </div>
          </FormRow>
        </form>
        <div v-else>
          이미 로그인 상태입니다. <route-link class="btn-link" to="/">홈</route-link> 으로 이동
        </div>
      </div>
    </div>
  </section>
</template>

<script lang="ts">
import { defineComponent, ref, reactive, getCurrentInstance, onMounted } from 'vue'
import { MainApi } from '../apis/'
import { Router } from 'vue-router';

export default defineComponent({
  name: 'MemberJoinPage',
  props: {
    globalShare: {
      type: Object,
      required: true
    },
  },
  setup(props) {
    const router:Router = getCurrentInstance()?.appContext.config.globalProperties.$router;
    const mainApi:MainApi = getCurrentInstance()?.appContext.config.globalProperties.$mainApi;
    const loginIdElRef = ref<HTMLInputElement>();
    const loginPwElRef = ref<HTMLInputElement>();
    const loginPwConfirmElRef = ref<HTMLInputElement>();
    const nameElRef = ref<HTMLInputElement>();
    const nicknameElRef = ref<HTMLInputElement>();
    const cellphoneNoElRef = ref<HTMLInputElement>();
    const emailElRef = ref<HTMLInputElement>();
    
   
    function checkAndJoin() {
       // 아이디 체크
      if ( loginIdElRef.value == null ) {
        return;
      }
      
      const loginIdEl = loginIdElRef.value;
      loginIdEl.value = loginIdEl.value.trim();
      
      if ( loginIdEl.value.length == 0 ) {
        alert('아이디를 입력해주세요.');
        loginIdEl.focus();
        return;
      }
      // 비번 체크
      if ( loginPwElRef.value == null ) {
        return;
      }

      const loginPwEl = loginPwElRef.value;
      loginPwEl.value = loginPwEl.value.trim();
      
      if ( loginPwEl.value.length == 0 ) {
        alert('비밀번호를 입력해주세요.');
        loginPwEl.focus();
        return;
      }
      
      // 비번확인 체크
      if ( loginPwConfirmElRef.value == null ) {
        return;
      }

      const loginPwConfirmEl = loginPwConfirmElRef.value;
      
      if ( loginPwEl.value != loginPwConfirmEl.value ) {
        alert('로그인 비번이 일치하지 않습니다.');
        loginPwConfirmEl.focus();
        return;
      }

      // 이름 체크
      if ( nameElRef.value == null ) {
        return;
      }

      const nameEl = nameElRef.value;
      nameEl.value = nameEl.value.trim();

      if ( nameEl.value.length == 0 ) {
        alert('이름을 입력해주세요.');
        nameEl.focus();
        return;
      }

      // 닉네임 체크
      if ( nicknameElRef.value == null ) {
        return;
      }

      const nicknameEl = nicknameElRef.value;
      nicknameEl.value = nicknameEl.value.trim();
      
      if ( nicknameEl.value.length == 0 ) {
        alert('닉네임을 입력해주세요.');
        nicknameEl.focus();
        return;
      }
      
      // 전화번호 체크
      if ( cellphoneNoElRef.value == null ) {
        return;
      }

      const cellphoneNoEl = cellphoneNoElRef.value;
      cellphoneNoEl.value = cellphoneNoEl.value.trim();
      
      if ( cellphoneNoEl.value.length == 0 ) {
        alert('전화번호를 입력해주세요.');
        cellphoneNoEl.focus();
        return;
      }

      // 이메일 체크
      if ( emailElRef.value == null ) {
        return;
      }
      
      const emailEl = emailElRef.value;
      emailEl.value = emailEl.value.trim();
      
      if ( emailEl.value.length == 0 ) {
        alert('이메일을 입력해주세요.');
        emailEl.focus();
        return;
      }
      
      //회원가입
      join(loginIdEl.value, loginPwEl.value, nameEl.value, nicknameEl.value, cellphoneNoEl.value, emailEl.value);
    }
    function join(loginId:string, loginPw:string, name:string, nickname:string, cellphoneNo:string, email:string) {
      mainApi.member_doJoin(loginId, loginPw, name, nickname, cellphoneNo, email)
        .then(axiosResponse => {
          
          alert(axiosResponse.data.msg);
          if ( axiosResponse.data.fail ) {
            return;
          }
          
          router.replace('/member/login?loginId=' + loginId)
        });
    }
    return {
      checkAndJoin,
      loginIdElRef,
      loginPwElRef,
      loginPwConfirmElRef,
      nameElRef,
      nicknameElRef,
      cellphoneNoElRef,
      emailElRef,
    }
  }
})
</script>

<style scoped>
</style>