종우의 삶 (전체 공개)

Market 프로젝트 심화 - Vue를 이용한 프론트 페이지 작성 본문

개발/Spring

Market 프로젝트 심화 - Vue를 이용한 프론트 페이지 작성

jonggae 2024. 4. 15. 17:15

개요

 

서버를 어느정도 구현하였으니 다음 단계의 경험을 해보기 위해 웹 페이지를 만들어보기로 한다.

 

한번 사용해본적이 있는 Vue.js를 이용하여 도메인과 맞는 페이지들을 구현하려 한다.

 

회원가입 / 로그인

상품, 장바구니, 주문 도메인과 관련된 페이지(+@)가 되겠다.

 

처음엔 타임리프를 사용하여 간단하게 서버쪽에 함께 만들려했는데, 이게 더 복잡한 길이었다.

백엔드 서버와 프론트엔드 서버를 분리하여 두개의 프로젝트로 구성하기로 한다.

 

인텔리제이에서도 Vue프로젝트를 만들 수 있으므로 적당히 만들어준 다음

터미널에서 node와 vue,  vue cli까지 설치하여 환경을 만든다. 서버 포트 번호를 다르게 하면 로컬에서 스프링 서버와 vue 서버를 동시에 운영할 수 있다.

 


Vue는 어찌되었든 JS를 기반으로 한 프론트엔드 프레임워크이므로 내가 주로 활용했던 Spring과는 약간 차이가 있을 수 있다. 그렇기에 AI의 도움을 많이 받기는 하였지만 어느정도 사용법을 알아야 AI의 도움도 받을 수 있기에 그것들을 정리해보자.

 

Vue도 결국 html로 페이지의 뼈대를 만들고, Css코드로 스타일을 완성하며, Script로 페이지에서 사용할 로직을 만든다. 최소한 스타일 까지는 아니어도 내가 만든 서버 API의 기능들을 프론트 페이지에서 구현하고자 하는것이 목표이다.

 

Vue에는 ComponentView, Router가 존재한다. 

라우터는 현실세계의 터미널 마냥 말 그대로 이곳저곳으로 정보를 보내주는 역할을 한다.

페이지들이 있어도 라우터에 설정해놓지 않으면 웹사이트는 페이지를 보여주지 못한다. 

 

컴포넌트는 실제로 페이지를 구성하는 레이아웃을 만들어주는데, 몇개의 덩어리로 재사용이 가능한 부분은 주로 '컴포넌트'에, 그 컴포넌트들을 이용하거나 전체적인 화면을 띄우는 '뷰'로 나눌 수 있겠다.

 

객체지향적인 느낌이 나는 것 같기는 한데, 엄청 와닿는 설명은 아닌 듯 하다. 

이러한 프레임워크의 특징까지는 아직 깊이 공부하지 못했으므로 컴포넌트와 뷰를 나누는 기준이 아직은 모호하다. 

다양한 프레임 덩어리와 레이아웃등을 마음대로 사용할 수 있게 되면 명확한 분리를 할 수 있을듯 하다.

 

페이지 상단에 표시될 '내비게이션 바' 같은 것은 컴포넌트로 분리하고, 나머지를 '뷰'로 만들어 다양한 페이지들을 완성해보자.

 


 

가장 최소한의 프론트 페이지 이므로, 디자인이나 UI UX는 크게 고려하지 않았다. 

 

1. 홈 화면

URL에 접근하면 가장 먼저 만나는 페이지이다. 배포시에도 메인 홈페이지가 될 것이다.

상단에 있는 메뉴 바가 바로 컴포넌트에 속하는 '내비게이션 바' 이다. 

<template>
    <nav>
        <router-link to="/" class="nav-item">홈</router-link>
        <router-link to="/login" class="nav-item">로그인</router-link>
        <router-link to="/register" class="nav-item">회원가입</router-link>
        <router-link to="/products" class="nav-item">상품</router-link>
        <router-link to="/orders" class="nav-item">주문</router-link>
        <router-link to="/cart" class="nav-item">장바구니</router-link>
        <router-link to="/user-profile" class="nav-item">내 정보</router-link>
    </nav>
</template>

 

링크 주소를 걸어주면 해당 페이지로 전환을 할 수 있겠다. 

저기 보이는 '/products' 따위의 주소들은 router 디렉토리 안의 index.js에서 관리를 해주어야 한다.

 

import {createRouter, createWebHistory} from 'vue-router';
import HomePage from '@/views/HomePage.vue';
import LoginPage from "@/views/LoginPage.vue";
import MainPage from "@/views/MainPage.vue";
import RegisterPage from "@/views/RegisterPage.vue";
import UserProfile from "@/views/UserProfile.vue";
import ProductList from "@/views/ProductList.vue";
import AddProduct from "@/views/AddProduct.vue";
import ProductDetail from "@/views/ProductDetail.vue";
import ProductEdit from "@/views/ProductEdit.vue";
import CartPage from "@/views/CartPage.vue";
import OrderPage from "@/views/OrderPage.vue";
// LoginPage.vue, RegisterPage.vue, MainPage.vue 등 추가 컴포넌트를 여기에 임포트

const routes = [

    {
        path: '/',
        name: 'Home',
        component: HomePage
    },

    {
        path: '/login',
        name: 'Login',
        component: LoginPage
    },
    
    // 이하 생략//

 

각 view 페이지들을 구성하여 임포트해주고 (HomePage.vue 같은) 경로를 잘 적어준다.

그리고 실제 라우팅을 해줄 경로를 하단에 지정해주면 전체 사이트에서 사용할 수 있다. 

 

2. 회원가입 / 로그인 (JWT 인증)

역시나 회원 가입은 간단하다. 

적당히 html로 입력 폼을 만들어주고 회원가입 버튼을 누르면 서버와 연결된 DB에 해당 정보가 입력된다.

<template>
    <div>
        <h1>회원가입</h1>
        <form @submit.prevent="submitForm">
            <div>
                <label for="customerName">이름:</label>
                <input type="text" id="customerName" v-model="formData.customerName" required>
            </div>
            <div>
                <label for="password">비밀번호:</label>
                <input type="password" id="password" v-model="formData.password" required>
            </div>
            <div>
                <label for="email">이메일:</label>
                <input type="email" id="email" v-model="formData.email" required>
            </div>
            <div>
                <label for="phoneNumber">전화번호:</label>
                <input type="tel" id="phoneNumber" v-model="formData.phoneNumber" required>
            </div>
            <button type="submit">회원가입</button>
        </form>
    </div>
</template>

 

<script>
export default {
    name: 'RegisterPage',
    data() {
        return {
            formData: {
                customerName: '',
                email: '',
                phoneNumber: '',
                password: ''
            }
        };
    },
    methods: {
        submitForm() {
            fetch('/api/customer/register', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(this.formData)
            })
                .then(response => {
                    if (response.ok) {
                        alert('회원가입이 완료되었습니다. 로그인 페이지로 이동합니다.');
                        this.$router.push('/login');
                    } else {
                        response.json().then(data => {
                            throw new Error(data.message);
                        });
                    }
                })
                .catch(error => {
                    alert('회원가입 중 문제가 발생했습니다: ' + error.message);
                    console.error(error);
                });
        }
    }
};
</script>

 

이렇게 하나의 스크립트만으로 회원가입이 완료된다.

서버 API가 정상적으로 잘 작동한다면 vue를 이용한 페이지 구성은 그렇게 어려운 일은 아니었다. 

애초에 UI UX를 어떻게 만들고 (크게 고려 안함) , 어떤 스크립트를 사용해야하는지 찾아서 넣는 일이었다.

 

이후 로그인도 비슷하게 진행된다.

 

로그인의 경우 JWT를 관리해주어야 한다.

JWT를 기준으로 웹사이트 안에서 회원의 권한이나 정보를 관리해주기 때문에 어딘가에는 이 토큰이 저장되어 있어야한다.

쿠키와 세션은 이 상황에서 고려하지 않았다. 우선 로컬스토리지에 로그인 시 발급된 JWT를 저장해주는 로직을 설정해준다.

 

Spring 서버에서 LoginSuccessHandler를 통해 로그인이 성공하면 사용자의 정보를 담은 authentication 객체가 포함된 JWT가 생성된다. 

@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
    private final TokenProvider tokenProvider;

    public LoginSuccessHandler(TokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        String jwt = tokenProvider.createToken(authentication);
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(jwt);
        response.setStatus(HttpServletResponse.SC_OK);
        }

 

 

생성된 Jwt는 vue.js 애플리케이션이 추출할 수 있는 형태로 클라이언트에게 전송된다. 사용자는 웹사이트에서 로컬스토리지에 저장된 jwt를 기준으로 여러 접근을 할 수 있는 것이다. 

토큰을 파싱하여 내 정보, 권한을 확인하거나 유저별로 다른 장바구니, 주문 목록을 조회하는 등 여러 곳에 쓰인다.

 

하지만 직접 로컬스토리지에 jwt가 저장되는 것은 바람직 하지 않다. jwt는 보여지지 않는것이 보안상 안전하므로 서버쪽에서 redis를 사용하여 저장하는 방식으로 변경하려 한다.

 

현재 (로컬스토리지 저장)로서는 로그인 기능까지 잘 구현이 된다. 로그인 한 사용자별로 권한이 나뉘어져있고

각 사용자들의 정보도 다르게 표시된다.

 

 

일반회원  CJW와 관리자 계정 ADMIN의 차이를 볼 수 있다.

 

권한 설정을 통해 각 도메인별 접근 가능한 부분을 제어할 수 있다. 

 

관리자 계정은 상품을 직접 등록, 수정, 삭제할 수 있는 식이다. (일반 회원은 조회, 장바구니, 주문 추가만 가능)

 

서버 API가 웹페이지에서 동작하는 것을 확인해 볼 수 있었다.

 

다른 도메인인 상품, 장바구니, 주문(★)도 어느정도 구성을 했다.

 

주문은 특히 고려할 것이 많기때문에 따로 정리해보도록 한다.

 

이렇게 Vue.js를 이용한 프론트페이지 구성을 시작하였다. 배포까지 필요한 부분들을 만들어 실제로 배포를 시도해보자!

Comments