autofocus, 로그인, 로그아웃, 마이페이지, 아이디 중복확인, 회원가입, store

keep_going·2022년 12월 22일
0

vue

목록 보기
4/13

서버주소 => http://1.234.5.158:23000/

회원가입 => /member101/insert.json => 키정보 {id:"a", pw:"b", name:"c", email:"a@a.com", age:13 }
아이디중복확인 => /member101/idcheck.json?id=a
로그인 => /member101/select.json => 키정보 {"id":"a", "pw":"a"}
회원정보수정 => /member101/update.json 토큰(headers에 auth키) + {"name":"변경이름", "age":1234}
암호변경 => /member101/updatepw.json 토큰(headers에 auth키) + {"pw":"aaa", "newpw":"bbb"}


조회(select) => await axios.get(url, {headers});
추가(insert) => await axios.post(url, body, {headers});
수정(update) => await axios.put(url, body, {headers});
삭제(delete) => await axios.delete(url, {headers:headers, data:body});


vue에서 화면상에 조작하고 싶으면 (templates에)
1. 스크립트에 변수를 미리 만든다.
2. 마지막에 리턴
3. 위에서 사용
결국 스크립트에서 만든걸 화면에 던져준것. 사용해!
뷰의 관점은 스크립트 중심!


로그인 수정_백엔드 연동, autofocus, vuex
// 파일명 : LoginPage.vue

<template>
    <div class="container">
        {{ user }}
        <h3>로그인</h3>
        <div>
            <label class="lbl">아이디</label>
            <input type="text" ref="userid" v-model="user.userid" />
        </div>

        <div>
            <label class="lbl">암호</label>
            <input type="password" ref="userpw" v-model="user.userpw" />
        </div>

        <div>
            <button @click="handleLogin()">로그인</button>
        </div>
    </div>
</template>

<script>
import axios from 'axios';
import { reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';

export default {
    setup () {
 
      	// 로그인 한 타이밍을 알려주기 위한 컴포넌트 공통 변수
        const store = useStore();

        // 페이지 이동
        const router = useRouter();

        // 공통변수 영역 상수 object로 만듬, reactive
        const user = reactive({
            userid : 'a1',
            userpw : 'a1'
        });

        // 레퍼런스 변수 생성(focus 시키기 위함). import 해야해!
        const userid = ref();
        const userpw = ref();

        // 함수들 영역
        const handleLogin = async () => {
            console.log('handleLogin');
            if(user.userid === ''){
                alert('아이디 입력하세요.');
                userid.value.focus();
                return false;
            }
            if(user.userpw === ''){
                alert('암호를 입력하세요.');
                userpw.value.focus();
                return false;
            }
		
            // 벡엔드 연동 위치-> 함수 안에 짜야한다!
          	// 여기 왔다는 말은 위쪽에서 if문이 수행된적 없다는 뜻. 즉, 필터하고자 한것을 다 통과했다!
          	// if문이 수행됐으면 return false로 종료되니까.
            const url = `/member101/select.json`;
            const headers = {"Content-Type" : "application/json"};
            const body = {
                id : user.userid, 
                pw : user.userpw,
            };

            const { data } = await axios.post(url, body, {headers});
            console.log(data);
            if(data.status === 200) {
                // 다른컴포넌트에서 로그인 검증 위해 저장(토큰값을 넣어주는것)
                sessionStorage.setItem("TOKEN", data.result);

                // 컴포넌트 공통 변수 값 변경(함수, 변경값)
                store.commit('setLogged', true);

                alert('로그인 성공했습니다.');
                router.push({path:'/'});
            }
            else {
                alert('아이디 또는 암호가 틀립니다.');
            }
        };

        // 리턴 영역( 변수, 함수 등 templates에 사용하는 모든것)
        return {
            user, //reactive
            handleLogin, //함수
            userid, //ref
            userpw //ref
        };

    }
}
</script>

<style lang="css" scoped>
    .container {
        width   : 600px;
        padding : 20px;
        border  : 2px solid #cccccc;
    }

    .lbl {
        display : inline-block;
        width   : 100px;
    }
</style>

// 파일명 : LogoutPage.vue

<template>
    <div>
	// 화면을 만들 필요는 없음. 로그아웃 할건지 알림창만 띄우면 되니까.
    </div>
</template>

<script>

import { onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';

export default {
    setup () {

        const store = useStore();
        const router = useRouter();

        // 페이지 로딩될때 바로 실행되도록
        onMounted(() => {
            if(confirm('로그아웃 할까요?')) {
                sessionStorage.removeItem("TOKEN");
                store.commit('setLogged', false);
              	// 로그아웃 됐다는걸 컴포넌트 공통 변수에 알려줌
            }
            router.push({path:'/'});
        });

        return {
        };
    }
}
</script>

<style lang="css" scoped>

</style>

회원가입 수정_백엔드 연동, autofocus, 아이디 중복확인

// 파일명 : JoinPage.vue

<template>
    <div class="container">
        <h3>회원 가입</h3>
        {{ user }}
        <div>
            <label class="lbl">아이디</label>
            <input type="text" ref="uid" v-model="user.userid" @keyup="handleIDCheck()" />
            <label class="lbl" v-html="user.idcheck"></label> 
            // html태그 작성한것 해석해서 출력
        </div>
        <div>
            <label class="lbl">암호</label>
            <input type="password" ref="upw" v-model="user.userpw" />
        </div>
        <div>
            <label class="lbl">암호 확인</label>
            <input type="password" ref="upw1" v-model="user.userpw1" />
        </div>
        <div>
            <label class="lbl">이름</label>
            <input type="text" ref="uname" v-model="user.username" />
        </div>
        <div>
            <label class="lbl">나이</label>
            <input type="number" ref="uage" v-model="user.userage" />
        </div>
        <div>
            <label class="lbl">이메일</label>
            <input type="text" ref="uemail" v-model="user.useremail" />
            <label>@</label>
            <select v-model="user.useremail1">
                <option>이메일 주소 선택</option>
                <option>google.com</option>
                <option>naver.com</option>
                <option>daum.net</option>
            </select>
        </div>
        <div>
            <button @click="handleJoin()">회원 가입</button>
        </div>
    </div>
</template>

<script>
import axios from 'axios';
import { reactive, ref } from 'vue';
import { useRouter } from 'vue-router';

export default {
    setup () {
        
        const router = useRouter();

        // 상태변수
        const user = reactive({
            userid      : '',
            userpw      : '',
            userpw1     : '',
            username    : '',
            userage     : '',
            useremail   : '',
            useremail1  : '이메일 주소 선택',
            
            idcheck     : '<label style="color:#000000">중복 확인</label>'
        });
        
        // 묶어서 표현하고 싶다...어떻게?
        const uid = ref();
        const upw = ref();
        const upw1 = ref();
        const uname = ref();
        const uage = ref();
        const uemail = ref();

        // 함수들
        const handleJoin = async() => {
            if(user.userid === '') {
                alert('아이디를 입력하세요.')
                uid.value.focus();
                return false;
            }
            if(user.userpw === '') {
                alert('암호를 입력하세요.')
                upw.value.focus();
                return false;
            }
            if(user.userpw1 === '') {
                alert('암호를 다시 입력하세요.')
                upw1.value.focus();
                return false;
            }
            if(user.userpw !== user.userpw1) {
                alert('암호를 확인 하세요.')
                upw1.value.focus();
                return false;
            }
            if(user.username === '') {
                alert('이름를 입력하세요.')
                uname.value.focus();
                return false;
            }
            if(user.userage === '') {
                alert('나이를 입력하세요.')
                uage.value.focus();
                return false;
            }
            if(user.useremail === '') {
                alert('이메일을 입력하세요.')
                uemail.value.focus();
                return false;
            }
            if(user.useremail1 === '이메일 주소 선택') {
                alert('이메일 주소를 입력하세요.')
                return false;
            }
            // 유효성 성공하면 백엔드 연동
            const url = `/member101/insert.json`;
            const headers = {"Content-Type":"application/json"};
            const body = {
                id      : user.userid,
                pw      : user.userpw,
                name    : user.username,
                email   : `${user.useremail}@${user.useremail1}`, 
              			// 변수와 문자를 동시에 입력하고 싶다면 이런 꼴로!
                age     : user.userage
            };
            const { data } = await axios.post(url, body, {headers}); 
            console.log(data);
            if(data.status === 200) {
                alert('회원 가입 성공!');
                router.push({path:'/'});            
            }
        }

        // 아이디를 입력할때 마다 호출되는 함수
        const handleIDCheck = async() => {
            console.log('handleIDCheck');

            // user.idcheck = '중복확인';  이렇게 걸면 else 두개를 생략 가능 하지만 중간중간(백엔드로 왔다갔다 하는 시간차) 중복확인이 떠서 거슬림...
            // 프론트에서 검증 다하고 백엔드로 날리는게 맞다. 아니면 백엔드에서 부하 걸려!
            if(user.userid.length > 0) { // 내용 입력
                const url=`/member101/idcheck.json?id=${user.userid}`;
                const headers={"Content-Type":"application/json"};
                const { data } = await axios.get(url, {headers});
                console.log(data);    
                if(data.status === 200) { // 백엔드 정상적인가?
                    if(data.result === 0) { // 사용 가능 위치
                        user.idcheck = '<label style="color:green">사용 가능</label>';
                      					// v-html을 사용했으므로 ''안의 태그가 해석되어서 출력
                    }
                    else if(data.result === 1) { // 사용 불가 위치
                        user.idcheck = '<label style="color:red">사용 불가</label>';
                    }
                }
                else { // 정상적이지 않을 경우 
                    user.idcheck = '<label style="color:#000000">중복 확인</label>';
                }                      
            }
            else { // 내용 입력하지 않음
                user.idcheck = '<label style="color:#000000">중복 확인</label>';
            } 
        };

        // 템플릿에서 사용하기 위한 리턴(변수, 함수 등..)
        return {
            user,
            handleJoin,
            uid,
            upw,
            upw1,
            uname,
            uage,
            uemail,
            handleIDCheck
        }
    }
}
</script>

<style lang="css" scoped>
    .container {
        width   : 600px;
        padding : 20px;
        border  : 2px solid #ffd7d7;
        margin  : 0px auto;
    }
    .lbl {
        display: inline-block;
        width: 100px;
        padding: 5px;
    }
</style>

====================================

  • 로그인 했을때는 마이페이지, 로그아웃 메뉴가 보이고 로그인 안했을때는 로그인, 회원가입 메뉴가 보이도록 하고 싶다.
  • 로그인 된 타이밍은 로그인 컴포넌트가 알수 있고(sessionStorage.setItem("TOKEN", data.result); 시점에) 메뉴를 바꾸는건 app.vue에서 할수 있는데 로그인/로그아웃 됐다는걸 어떻게 알려 줄수 있나?
  • 컴포넌트들 간에 실시간으로 reactive되는 변수가 필요하다!
  • redus, store, vuex 라이브러리 이용! 전부 같은것

-- 라이브러리 설치하기
CMD> npm i vuex --save

  • 서버 중지 후 설치하고 다시 서버 구동
  • 라우터 처럼 직접 만들어서 메인에 등록한 후 쓴다. src에서 stores 폴더를 만들어서 그 안에 index.js생성

// 파일명 : stores/index.js



import { createStore  } from 'vuex';

// LoginPage.vue 컴포넌트에서 로그인된 정보를 App.vue로 실시간으로 알려줘야 함
// LogoutPage.vue 컴포넌트에서 로그아웃된 정보를 App.vue로 실시간으로 알려줘야 함

// mutations에서 데이터를 바꾸고 state변수에 저장하면 getters에서 가져가는것!
const stores = createStore({
    // 컴포넌트별 공유하는 변수
  	// 저장소의 토큰값을 가져와서 true, false 나타냄
  	/* :를 찍으면 {} 내용을 변수설정한다고 보기 때문에 if, for과 같은 문법이 사용 불가능 하다.
    state : {
        logged  : false,
        counter : 10
    },
	*/
  	
  	// 새로고침하더라도 로그인 상태를 유지하고 싶다!
    state() {
        let tmp = false; // 기본값은 로그아웃
        if(sessionStorage.getItem("TOKEN") !== null){ // 토큰 값이 있으면
            tmp = true; // 로그인 상태로
        }
        return {
            logged  : tmp, // 로그인 or 아웃 상태를 기록
            counter : 10 // 이건 그냥 여러개 값이 들어간다고 보여주기 위한 예시. 실습에만 이용.
        }
    },

  
  
    // App.vue컴포넌트에서 사용 예정. 가져감!
    getters :{
        getLogged(state) {
            return state.logged;
        }
      	getCounter(state) {
            return state.counter;
        }
    },

    // 로그인, 로그아웃 컴포넌트에서 사용 예정. 변경됨!
    mutations :{
        setLogged(state, value){
            state.logged = value
        },
        setCounter(state, val) { // counter에 새로운 값을 줌.
            state.counter = val;
        },
        setCounterPlus(state) { // counter 값에 +1씩
            state.counter++;
            // state.counter = state.counter + 1; 와 같은 의미지만 너무 길어서 위의 방식으로 사용.
        },
        setCounterMinus(state) { // counter 값에 -1씩
            state.counter--;
        }
    }

});

export default stores;

// 파일명 : main.js

import { createApp } from 'vue'
import App from './App.vue'

// 라우터처럼 메인에 등록!
import routes from './routes/index';
import stores from './stores/index';

const app = createApp(App);

app.use(routes);
app.use(stores);

app.mount('#app');

// 파일명 : MyPage.vue

<template>
    <div>
        <h3>마이페이지</h3>
		// 토큰을 가지고 있을때만(로그인 상태) 나오는 페이지
        {{ state }}
        <hr />

        <button @click="state.menu=1">회원정보수정</button>
        <button @click="state.menu=2">비밀번호변경</button>

        <hr />

        <div v-show="state.menu === 1" v-if="state.user">
            <div>
                <input type="text" v-model="state.user.name" />
            </div>
            <div>
                <input type="text" v-model="state.user.age" />
            </div>
            <div>
                <button @click="handleUpdate()">정보수정</button>
            </div>
        </div>

        <div v-show="state.menu === 2">
            <div>
                <input type="password" v-model="state.user.oldpw" placeholder="기존암호" />
            </div>

            <div>
                <input type="password" v-model="state.user.newpw" placeholder="변경암호" />
            </div>

            <div>
                <input type="password" v-model="state.user.newpw1" placeholder="변경암호확인" />
            </div>

            <div>
                <button @click="handleUpdatePW()">비밀번호변경</button>
            </div>
        </div>
    </div>
</template>

<script>
import axios from 'axios';
import { reactive, onMounted } from 'vue';

export default {
    setup () {
        const state = reactive({
            menu    : 1,    // 메뉴
            token   : sessionStorage.getItem("TOKEN"), // 토큰
            user    : null, // 현재 사용자 정보 보관용
          	// null로 하면 if문 처리 해야 오류 안남. ''로 하면 if문 처리 안해도 오류 안남.
            oldpw   : null, // 이건 null이고 if문 처리 안해도 오류가 안난다..! 데이터 처리 시 순서가 나중인가...? 
            newpw   : null,
            newp1   : null,
        });

        const handleData = async() => {
            const url = `/member101/selectone.json`;
            const headers = {
                "Content-Type" : "application/json",
                "auth" : state.token
              	// headers에 토큰을 심음
            }
            const { data } = await axios.get(url, {headers});
            console.log(data);
            if(data.status === 200) {
                state.user = data.result;
            }
        };

        onMounted(() => {
            handleData();
        });

        const handleUpdate = async() => {
            const url = `/member101/update.json`;
            const headers = {
                "Content-Type" : "application/json",
                "auth" : state.token
              	// 이름하고 나이만 주면 어떤 사람건지 모름. 그래서 토큰을 같이 보내는것.
            };
            const body = {
                name    : state.user.name, 
                age     : state.user.age,
            };
            const { data } = await axios.put(url, body, {headers});
            console.log(data);
            if(data.status === 200){
                alert('정보수정완료');
                handleData();
              	// 정보 수정 후 새로고침 해준 것
            }
        };

        const handleUpdatePW = async() =>{
            const url = `/member101/updatepw.json`;
            const headers = {
                "Content-Type" : "application/json",
                "auth" : state.token
            };
            const body = {
                pw        : state.oldpw, 
                newpw     : state.newpw,
            };
            const { data } = await axios.put(url, body, {headers});
            console.log(data);
            if(data.status === 200){
                alert('비밀번호 변경 완료');
                state.menu = 2;
              	// handleData는 회원정보 불러오는 함수인데 거기엔 비밀번호가 포함 안되있잖어...
              	// 그래서 굳이 비밀번호 변경하고 다시 handleData를 작동시킬 이유가 없음.
            }
        };

        return {
            state,
            handleUpdate,
            handleUpdatePW
        };
    }
}
</script>

<style lang="css" scoped>

</style>

// 파일명 : StorePage.vue

<template>
    <div>
        <h3>store 실습</h3>
        <div>
            <input type="number" v-model="state.num" />
        </div>
        <button @click="handleFunc1()">입력한 값으로 변경하기</button>
        <button @click="handleFunc2()">기본 숫자에서 1증가 시키기</button>
        <button @click="handleFunc3()">기본 숫자에서 1감소 시키기</button>
    </div>
</template>

<script>
import { reactive, computed } from 'vue';
import { useStore } from 'vuex';

export default {
    setup () {
        const store = useStore();

        const state = reactive({
            num : 10,
          	counter : computed(() => store.getters.getCounter ),
            // 바뀐 값을 입력창에서 실시간으로 보여주고싶으면 App에서 가져와서 쓴것 처러 computed 해야함! import하는것 잊지마
        });

        const  handleFunc1 = () =>{
            store.commit('setCounter', state.num);
          	// state.num값으로 변경!
        };

        const  handleFunc2 = () =>{
            store.commit('setCounterPlus');
          	// 넣어줘야 할 값이 있는게 아님!
        };

        const  handleFunc3 = () =>{
            store.commit('setCounterMinus');
        };
        
        return { 
            state,
            handleFunc1,
            handleFunc2,
            handleFunc3
        };
    }
}
</script>

<style lang="css" scoped>

</style>

// 파일명 : App.vue의 script 부분만

<script>
import { reactive, computed } from 'vue';
import { useStore } from 'vuex';

export default {
  setup () {
    // 컴포넌트별 공용변수 사용하기 위해
    const store = useStore();

    const state = reactive({
      logged : computed(() => store.getters.getLogged ),
      counter : computed(() => store.getters.getCounter ),
      // counter 실습을 위해 추가함... 이게 없어도 작동 한다. App.vue에서 필요한게 아니니까
    })

    return {state}
  }
}
</script>
profile
keep going

2개의 댓글

comment-user-thumbnail
2022년 12월 22일
답글 달기
comment-user-thumbnail
2022년 12월 27일
답글 달기