보라코딩

Vue, 스프링부트 REST API (3) 본문

코딩/Vue

Vue, 스프링부트 REST API (3)

new 보라 2023. 9. 16. 21:57
vue router 추가

 

 

vue 터미널에 입력해서 라우터 설치

npm i vue-router

 

 

main.js
import {createApp} from 'vue'
import App from './App.vue'
import {createRouter, createWebHistory} from 'vue-router'
import Home from "@/pages/Home.vue";
import Login from "@/pages/Login.vue";


const routes = [
    {path: '/', component: Home},
    {path: '/login', component: Login}
]

const router = createRouter({
    history: createWebHistory(), routes: routes
})

createApp(App).use(router).mount('#app')

 

 

App.vue

RouterView로 변경!

<template>
  <Header/>
  <RouterView/>
  <Footer/>
</template>

<script>

import Header from "@/components/Header.vue";
import Footer from "@/components/Footer.vue";

export default {
  name: 'App',
  components: {
    Footer,
    Header
  }
}
</script>

<style>
.bd-placeholder-img {
  font-size: 1.125rem;
  text-anchor: middle;
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
}

@media (min-width: 768px) {
  .bd-placeholder-img-lg {
    font-size: 3.5rem;
  }
}

.b-example-divider {
  width: 100%;
  height: 3rem;
  background-color: rgba(0, 0, 0, .1);
  border: solid rgba(0, 0, 0, .15);
  border-width: 1px 0;
  box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15);
}

.b-example-vr {
  flex-shrink: 0;
  width: 1.5rem;
  height: 100vh;
}

.bi {
  vertical-align: -.125em;
  fill: currentColor;
}

.nav-scroller {
  position: relative;
  z-index: 2;
  height: 2.75rem;
  overflow-y: hidden;
}

.nav-scroller .nav {
  display: flex;
  flex-wrap: nowrap;
  padding-bottom: 1rem;
  margin-top: -1px;
  overflow-x: auto;
  text-align: center;
  white-space: nowrap;
  -webkit-overflow-scrolling: touch;
}

.btn-bd-primary {
  --bd-violet-bg: #712cf9;
  --bd-violet-rgb: 112.520718, 44.062154, 249.437846;

  --bs-btn-font-weight: 600;
  --bs-btn-color: var(--bs-white);
  --bs-btn-bg: var(--bd-violet-bg);
  --bs-btn-border-color: var(--bd-violet-bg);
  --bs-btn-hover-color: var(--bs-white);
  --bs-btn-hover-bg: #6528e0;
  --bs-btn-hover-border-color: #6528e0;
  --bs-btn-focus-shadow-rgb: var(--bd-violet-rgb);
  --bs-btn-active-color: var(--bs-btn-hover-color);
  --bs-btn-active-bg: #5a23c8;
  --bs-btn-active-border-color: #5a23c8;
}

.bd-mode-toggle {
  z-index: 1500;
}
</style>

 

 

 

 

Login.vue

부트스트랩으로 로그인화면 만들기

<template>
  <div class="form-signin w-100 m-auto">
    <form>
      <h1 class="h3 mb-3 fw-normal">Please sign in</h1>

      <div class="form-floating">
        <input type="email" class="form-control" id="floatingInput" placeholder="name@example.com">
        <label for="floatingInput">Email address</label>
      </div>
      <div class="form-floating">
        <input type="password" class="form-control" id="floatingPassword" placeholder="Password">
        <label for="floatingPassword">Password</label>
      </div>

      <div class="form-check text-start my-3">
        <input class="form-check-input" type="checkbox" value="remember-me" id="flexCheckDefault">
        <label class="form-check-label" for="flexCheckDefault">
          Remember me
        </label>
      </div>
      <button class="btn btn-primary w-100 py-2" type="submit">Sign in</button>
      <p class="mt-5 mb-3 text-body-secondary">&copy; 2017–2023</p>
    </form>
  </div>
</template>

<script>
export default {

}
</script>

<style scoped>
.form-signin {
  max-width: 330px;
  padding: 1rem;
}

.form-signin .form-floating:focus-within {
  z-index: 2;
}

.form-signin input[type="email"] {
  margin-bottom: -1px;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}

.form-signin input[type="password"] {
  margin-bottom: 10px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}
</style>

 

 

 


 

 

backend 로그인 기능 구현

 

 

DB에 members 테이블 생성

 

 

 

Member(entity)
package com.example.backend.entity;

import lombok.Getter;

import javax.persistence.*;

@Table(name="members")
@Entity
@Getter
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) //자동증가
    private int id;

    @Column(length = 50, nullable = false, unique = true)
    private String email;

    @Column(length = 100, nullable = false)
    private String password;
}

 

 

 

MemberRepository
package com.example.backend.repository;

import com.example.backend.entity.Item;
import com.example.backend.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberRepository extends JpaRepository<Member, Integer> {

    Member findByEmailAndPassword(String email, String password);
}

 

 

AccountController (로그인 컨트롤러)
package com.example.backend.controller;

import com.example.backend.entity.Item;
import com.example.backend.entity.Member;
import com.example.backend.repository.ItemRepository;
import com.example.backend.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@RestController
@CrossOrigin("http://localhost:3001/")
public class AccountController {

    @Autowired
    MemberRepository memberRepository;

    @PostMapping("/api/account/login")
    public int login(@RequestBody Map<String, String> params) {
        Member member = memberRepository.findByEmailAndPassword(params.get("email"), params.get("password"));

        if (member != null) {
            return member.getId();
        }

        return 0;
    }

}

return 0은 추후에 바꿔줌

throw new ResponseStatusException(HttpStatus.NOT_FOUND);

 

 

 

 

frontend 로그인

 

 

이미 화면은 만들어둔 상태이고

 

 

 

Card.vue

input email에 v-model로 바인

<input type="email" class="form-control" id="floatingInput" placeholder="name@example.com"
       v-model="state.form.email">

 

 

password도 바인딩

<input type="password" class="form-control" id="floatingPassword" placeholder="Password"
       v-model="state.form.password">

 

 

버튼에 submit 함수

<button class="btn btn-primary w-100 py-2" type="submit" @click="submit()">Sign in</button>

 

 

setup에 state 입력하고 submit 함수도 입력

export default {
  setup() {
    const state = reactive({
      form: {
        email: "",
        password: ""
      }
    })

    const submit = () => {
      axios.post("/api/account/login", state.form)
          .then((res) => {
            console.log(res);
            window.alert("로그인 완료!!");
          })
    }

    return {state, submit}
  }
}

 

 

 

DB에 데이터 임의로 넣어두고

http://localhost:3001/login 에서 로그인하면 로그인 제대로 되는 것 확인 가능

 

 

 

 

 


 

 

 

Vuex로 로그인 보완

 

 

Header.vue 수정
<li>
  <router-link to="/" class="text-white">메인화면</router-link>
</li>
<li>
  <router-link to="/login" class="text-white">로그인</router-link>
</li>

 

 

터미널에서 vuex 설치하기
npm install vuex@next --save

 

 

store.js 생성하기 (scripts 폴더 밑에)
import { createStore } from 'vuex'

// Create a new store instance.
const store = createStore({
    state () {
        return {
            account: {
                id : 0
            }
        }
    },
    mutations: {
        setAccount(state, payload){
            state.account.id = payload;
        }
    }
})

export default store;

 

 

 

main.js

사용하기 위해 main.js에 추가

 

import store from "@/scripts/store";
createApp(App).use(store).use(router).mount('#app')

 

 

 

Login.vue

submit 함수 내용을 변경

store 포함하게

const submit = () => {
  axios.post("/api/account/login", state.form)
      .then((res) => {
        store.commit('setAccount', res.data);
        window.alert("로그인 완료!!");
      })
}

 

 

 

Header.vue

v-if 사용해서 아이디가 없으면 로그인 뜨고

아니면 로그아웃 뜨게 변경

<li>
  <router-link to="/login" class="text-white" v-if="!$store.state.account.id">로그인</router-link>
  <router-link to="/login" class="text-white" v-else>로그아웃</router-link>
</li>

 

다시 로그아웃 부분 변경

<a to="/login" class="text-white" @click="logout()" v-else>로그아웃</a>

 

 

router.js (새로 생성)

기존 main.js에 있는 router를 따로 생성

import Home from "@/pages/Home.vue";
import Login from "@/pages/Login.vue";
import {createRouter, createWebHistory} from "vue-router";

const routes = [
    {path: '/', component: Home},
    {path: '/login', component: Login}
]

const router = createRouter({
    history: createWebHistory(), routes: routes
})

export default router;

 

 

 

Header.vue

import해서 router을 사용 가능

export default {
  name: 'Header',
  setup(){
    const logout = () => {
      store.commit('setAccount',0);
      router.push({path:"/"});
    }
    return {logout}
  }
}

 

 

 

Login.vue

session에 저장시킴!

router도 지정해줌!

catch로 예외처리도!

const submit = () => {
  axios.post("/api/account/login", state.form)
      .then((res) => {
        store.commit('setAccount', res.data);
        sessionStorage.setItem("id", res.data);
        router.push({path:"/"});
        window.alert("로그인 완료!!");
      })
      .catch(() => {
        window.alert("로그인 정보가 존재하지 않아요!")
      })
}

 

 

 

Header.vue

로그아웃시에는 session remove

export default {
  name: 'Header',
  setup(){
    const logout = () => {
      store.commit('setAccount',0);
      sessionStorage.removeItem("id");
      router.push({path:"/"});
    }
    return {logout}
  }
}

 

 

 

App.vue

sessionStorage로 id 받아와서

상태변경 해줌

export default {
  name: 'App',
  components: {
    Footer,
    Header
  },
  setup(){
    const id = sessionStorage.getItem("id");

    if (id){
      store.commit("setAccount", id);
    }
  }
}

 

'코딩 > Vue' 카테고리의 다른 글

Vue, 스프링부트 REST API (2)  (0) 2023.09.12
Vue, 스프링부트 REST API (1)  (0) 2023.09.10
vue 설치 및 기초  (0) 2023.08.29