프로젝트

9. 회원가입 / 로그인기능 만들기(1)

알렉스 페레이라 2023. 10. 26. 15:31

웹 포탈의 가장 기본인 회원가입 / 로그인기능을 만드려고 한다.

 

다음의 4단계를 거쳐 구성할 계획이다.

  1. 화면 설계 개념 정리
  2. DB스키마 설계
  3. 화면단 구축
  4. back단 구축

 

우선 화면을 설계해 보자면, 상단메뉴의 우측에 

  1. 로그인이 되어있을시 - 로그아웃버튼만
  2. 로그인이 되어있지 않을시 - 로그인버튼, 회원가입 버튼을 보여주려 한다.
  3. 로그인 및 회원가입 창은 전부 bootstrap의 modal을 통해 보여줄것이고
  4. 로그인여부는 axios를 통해 back단에 요청할 계획이다.
  5. 사용자 테이블도 구성하여 간단히 회원가입 시킬 예정 (+ 더 가능하다면 네이버 회원가입도)

 


우선, 상단메뉴 컴포넌트인 NavBar.vue에 버튼을 그린다.

 

isLogin이라는 변수를 통해 분기렌더링 할것.

        <form class="d-flex" role="search">
            <div v-if="isLogin">
                <button class="btn btn-outline-success" type="button">{{msgSearch}}</button>
            </div>
            <div v-else>
                <button class="btn btn-outline-success" type="button">{{msgSearch}}</button>
            </div>
            <!--
            <input class="form-control me-2" type="search" v-bind:placeholder="msgSearchPlaceHolder" aria-label="Search">
            <button class="btn btn-outline-success" type="submit">{{msgSearch}}</button>
            -->
        </form>

로그인이 되어있지 않을시,

 

로그인 변수를 true로 강제변경한다.

 

로그인이 되어있을시.

 

이제 isLogin변수값을 서버단에서 가져와야 하는데. 해당 작업은 회원가입이 끝나고 처리하도록 하자.

 


회원가입

 

회원가입창은 bootstrap의 form태그와 bootstrap을 이용해 만들 예정이다.

 

그러기 위해선 또 DB 스키마를 작성해야 하는데, 우선 생각한 컬럼은 다음과 같다.

 

  • ID(키값)
  • EMAIL(로그인 ID)
  • NAME(사용자 이름)
  • PASSWORD(비밀번호)
  • PHONE(핸드폰번호)
  • ZIPCODE(우편번호)
  • ADDRESS1(주소1)
  • ADDRESS2(주소2)

우선 위와같이 회원가입에 필요한 데이터를 생성하고, 추후 필요한 컬럼은 추가할 예정이다.

쿼리는 다음과 같다.

DROP TABLE USER;

CREATE TABLE USER(
	ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
	EMAIL VARCHAR(30) NOT NULL,
	NAME VARCHAR(30) NOT NULL,
	PASSWORD VARCHAR(100) NOT NULL,
	PHONE VARCHAR(30) NOT NULL,
	ZIPCODE VARCHAR(10),
	ADDRESS1 VARCHAR(100),
	ADDRESS2 VARCHAR(100)
);

 

성공. 이제 해당 컬럼에맞게 화면단 FORM을 완성해준다.

 

ㅋㅋㅋㅋ... 조악해서 조금 부끄럽다.

 

여기에 추가할 기능은

  • 이메일인증
  • 비밀번호 암호화
  • 카카오 주소API연동

정도가 있겠다.

 

소스는 다음과 같다.

<template>
    <nav class="navbar navbar-expand-lg bg-light">
        <div class="container-fluid">
            <a class="navbar-brand" href="index.html">
                <img src="../../assets/alex.png" alt="Logo" width="30" height="30" class="d-inline-block align-text-top">
                {{msgLogo}}
            </a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                <li class="nav-item">
                <a class="nav-link active" aria-current="page" href="board.html">{{msgBoard}}</a>
                </li>
                <li class="nav-item dropdown">
                <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                    {{msgOtherMenu}}
                </a>
                <ul class="dropdown-menu">
                    <li><a class="dropdown-item" href="#">{{msgGuestBook}}</a></li>
                    <li><a class="dropdown-item" v-bind:href="blogUrl">{{msgGoToBlog}}</a></li>
                    <li><hr class="dropdown-divider"></li>
                    <li><a class="dropdown-item" href="#">Something else here</a></li>
                </ul>
                </li>
            </ul>
            <form class="d-flex" role="search">
                <div v-if="isLogin">
                    <button class="btn btn-outline-success" type="button">{{msgLogOut}}</button>
                </div>
                <div v-else>
                    <button class="btn btn-outline-success" type="button">{{msgLogIn}}</button>
                    <button class="btn btn-outline-success" type="button" @click="SignInOpen">{{msgSignIn}}</button>
                </div>
            </form>
            </div>
        </div>

        <!-- SignIn Modal-->
        <div class="signin-modal-wrap" v-show="signinModalCheck" @click="SignInOpen">
            <div class="signin-modal-container" @click.stop="">
                
                <form>
                    <table class="table">
                        <tbody>
                            <!-- email -->
                            <tr>
                                <th><label for="email">{{ msgEmail }}</label></th>
                                <td>
                                    <div class="div7"><input id="email" class="form-control" type="email"></div>
                                    <div class="div3" style="padding-left:10px;"><button class="btn btn-outline-success" style="margin-bottom:5px;">{{msgEmailCheck}}</button></div>
                                </td>
                            </tr>

                            <!-- name -->
                            <tr>
                                <th><label for="name">{{ msgName }}</label></th>
                                <td>
                                    <div class="div10"><input id="name" class="form-control" type="text"></div>
                                </td>
                            </tr>

                            <!-- password -->
                            <tr>
                                <th><label for="password">{{ msgPassword }}</label></th>
                                <td>
                                    <div class="div10"><input id="password" class="form-control" type="password"></div>
                                </td>
                            </tr>

                            <!-- phone -->
                            <tr>
                                <th><label for="phone1">{{ msgPhone }}</label></th>
                                <td>
                                    <div class="div3"><input id="phone1" class="form-control" type="text" maxlength="4"></div>
                                    -
                                    <div class="div3"><input id="phone2" class="form-control" type="text" maxlength="4"></div>
                                    -
                                    <div class="div3"><input id="phone3" class="form-control" type="text" maxlength="4"></div>
                                </td>
                            </tr>

                            <!-- address -->
                            <tr>
                                <th rowspan="2"><label for="zipcode">{{ msgAddress }}</label></th>
                                <td>
                                    <div class="div3"><input id="zipcode" class="form-control" type="text"></div>
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    <div class="div5"><input id="address1" class="form-control" type="text"></div>
                                    <div class="div5"><input id="address2" class="form-control" type="text"></div>
                                </td>
                            </tr>
                        </tbody>
                        </table>
                </form>
                                    
                <div class="modal-btn">
                <button class="btn btn-outline-success" @click="SignInOpen">{{msgClose}}</button>
                <button class="btn btn-outline-success" @click="SignInOpen">{{msgSignIn}}</button>
                </div>
            </div>
        </div>
    </nav>
</template>

<script>
    export default {
        data(){
            return {
                msgLogo : '알렉스 페레이라의 격투 블로그',
                msgSearchPlaceHolder : '검색어를 입력하세요.',
                msgSearch : 'search',
                msgBoard : '자유게시판',
                msgGuestBook : '방명록',
                msgGoToBlog : '블로그로 이동',
                msgOtherMenu : '부가메뉴',
                
                blogUrl : 'https://leonjk3.tistory.com/',

                isLogin : false,
                signinModalCheck : false,
                msgLogOut : '로그아웃',
                msgLogIn : '로그인',
                msgSignIn : '회원가입',
                msgClose : '닫기',

                msgEmail : '메일주소',
                msgEmailCheck : '이메일인증',
                msgName : '이름',
                msgPassword : '비밀번호',
                msgPhone : '핸드폰번호',
                msgAddress : '주소'
            }
        },
        methods:{
            SignInOpen(){
                this.signinModalCheck = !this.signinModalCheck;
            }
        }
    }
</script>

<style lang="scss" scoped>
@import '../../assets/common.css';

//modal
.signin-modal-wrap {position: fixed; left: 0; top: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.4); z-index:999;}
.signin-modal-container {position: relative; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 550px; background: #fff; border-radius: 10px; padding: 20px; box-sizing: border-box;}

//signIn
.table tbody>tr>td>div{display:inline-block;}
.table tbody>tr>th{width:20%;}
.table tbody>tr>th>label{padding-top:5px;}
.table > label{padding-top:5px;}

</style>