Vue.js 3 & NodeJS/Vue 3

Vue CLI 로그인 처리 3(공통 헤더 추가, 게시판 컴포넌트 수정) - Vue CLI Axios Add Common Header, Edit Board Component

carrotweb 2021. 10. 3. 00:06
728x90
반응형

전역 엑시오시 기본값(Global Axios Defaults)에 공통(Commone) 헤더(Header) 추가

C:\workspaces\nodeserver\testvue\src\store\modules 폴더에서 login.js 파일을 오픈합니다.

actions의 doLogin() 메서드에서 axios의 모든 요청에 적용되게 전역 기본값으로 헤더를 추가합니다.

헤더의 키는 "Access-Token"이고 값은 Login 후 응답받은 "accessToken"입니다.

// 로그인합니다.
async doLogin({ commit }, memberInfo) {
	let result = false;
	let resultErr = null;
	try {
		let res = await axios.post("http://localhost:9000/members/login", memberInfo);
		if (res.data.success == true) {
			console.log("로그인되었습니다.");
			commit('setMmemberId', memberInfo.id);
			commit('setAccessToken', res.data.accessToken);
			axios.defaults.headers.common['Access-Token'] = res.data.accessToken; --> 추가
			result = true;
		} else {
			console.log("로그인되지 않았습니다.");
			let err = new Error("Request failed with status code 401");
			err.response = {data:{"success":false, "errormessage":"로그인되지 않았습니다."}};
			resultErr = err;
		}
	} catch(err) {
		if (!err.response) {
			err.response = {data:{"success":false, "errormessage":err.message}};
		}
		resultErr = err;
	}
	return new Promise((resolve, reject) => {
		if (result) {
			resolve();
		} else {
			reject(resultErr);
		}
	});
}

actions의 doLogout() 메서드에서 모든 요청에 적용된 전역 "Access-Token" 헤더를 삭제합니다.

// 로그아웃합니다.
doLogout({commit}) {
	commit('reset');
	delete axios.defaults.headers.common['Access-Token']; --> 추가
}

 

 

 

게시물 등록 컴포넌트 수정하기

1. C:\workspaces\nodeserver\testvue\src\views 폴더에서 BoardWrite.vue 파일을 오픈합니다.

<template>
	<div class="board">
		<h1>This is an board write page</h1>
		<table>
			<colgroup>
				<col style="width:18.5%">
				<col style="width:auto">
			</colgroup>
			<tbody>
				<tr>
					<th scope="row">작성자</th>
					<td><input type="text" placeholder="아이디를 입력하세요." ref="writerInput" v-model.trim="writer"></td>
				</tr>
				<tr>
					<th scope="row">제목</th>
					<td><input type="text" placeholder="제목을 입력하세요." ref="subjectInput" v-model.trim="subject"></td>
				</tr>
				<tr>
					<th scope="row">내용</th>
					<td><textarea rows="10" placeholder="내용을 입력하세요." ref="contentTextarea" v-model.trim="content"></textarea></td>
				</tr>
			</tbody>
		</table>
		<div class="buttons">
			<div class="right">
				<button class="button blue" @click="boardSaveClick">등록</button>
				<button class="button" @click="boardCancelClick">취소</button>
			</div>
		</div>
	</div>
</template>

<script>
export default {
	name : 'BoardWrite',
	data : function() {
		return {
			writer : '',
			subject : '',
			content : ''
		};
	},
	methods : {
		boardSaveClick() {
			if (this.writer == "") {
				alert("작성자를 입력하세요.");
				this.$refs.writerInput.focus();
				return;
			} else if (this.subject == "") {
				alert("제목을 입력하세요.");
				this.$refs.subjectInput.focus();
				return;
			} else if (this.content == "") {
				alert("내용을 입력하세요.");
				this.$refs.contentTextarea.focus();
				return;
			}

			var result = confirm("등록하시겠습니까?");
			if (result) {
				let boardItem = { writer : this.writer, subject : this.subject, content : this.content };
				this.axios.post("http://localhost:9000/boards", boardItem).then((res)=>{
					console.log(res);
					if (res.data.success == true) {
						alert("등록되었습니다.");
						this.$router.push({name : 'BoardList'});
					} else {
						alert("등록되지 않았습니다.");
					}
				}).catch((err) => {
					console.log(err);
					alert("등록되지 않았습니다.");
				});
			}
		},
		boardCancelClick() {
			this.$router.go(-1);
		}
	},
	mounted() {
		this.$refs.writerInput.focus();
	}
};
</script>

1. <template>의 작성자 입력란 대신 출력으로 수정합니다.

2. <script>의 data에서 writer를 Login 된 아이디로 수정합니다.

3. <script>의 methods에서 boardSaveClick() 메서드에 있는 witer 검증을 삭제하고 POST로 전달 데이터(boardItem)에서 writer를 삭제합니다.

4. <script>의 mounted() 메서드에서 제목 입력란으로 포커스가 가도록 수정합니다.

<template>
	<div class="board">
		<h1>This is an board write page</h1>
		<table>
			<colgroup>
				<col style="width:18.5%">
				<col style="width:auto">
			</colgroup>
			<tbody>
				<tr>
					<th scope="row">작성자</th>
					<td>{{ writer }}</td>
				</tr>
				<tr>
					<th scope="row">제목</th>
					<td><input type="text" placeholder="제목을 입력하세요." ref="subjectInput" v-model.trim="subject"></td>
				</tr>
				<tr>
					<th scope="row">내용</th>
					<td><textarea rows="10" placeholder="내용을 입력하세요." ref="contentTextarea" v-model.trim="content"></textarea></td>
				</tr>
			</tbody>
		</table>
		<div class="buttons">
			<div class="right">
				<button class="button blue" @click="boardSaveClick">등록</button>
				<button class="button" @click="boardCancelClick">취소</button>
			</div>
		</div>
	</div>
</template>

<script>
export default {
	name : 'BoardWrite',
	data : function() {
		return {
			writer : this.$store.state.loginStore.memberId,
			subject : '',
			content : ''
		};
	},
	methods : {
		boardSaveClick() {
			if (this.subject == "") {
				alert("제목을 입력하세요.");
				this.$refs.subjectInput.focus();
				return;
			} else if (this.content == "") {
				alert("내용을 입력하세요.");
				this.$refs.contentTextarea.focus();
				return;
			}

			var result = confirm("등록하시겠습니까?");
			if (result) {
				let boardItem = { subject : this.subject, content : this.content };
				this.axios.post("http://localhost:9000/boards", boardItem).then((res)=>{
					console.log(res);
					if (res.data.success == true) {
						alert("등록되었습니다.");
						this.$router.push({name : 'BoardList'});
					} else {
						alert("등록되지 않았습니다.");
					}
				}).catch((err) => {
					console.log(err);
					alert("등록되지 않았습니다.");
				});
			}
		},
		boardCancelClick() {
			this.$router.go(-1);
		}
	},
	mounted() {
		this.$refs.subjectInput.focus();
	}
};
</script>

 

2. 콘솔을 실행하고 Vue 프로젝트가 있는 C:\workspaces\nodeserver\testvue 폴더로 이동합니다. 그리고 콘솔에서 npm run 명령어를 실행합니다.

npm run serve

 

3. 웹 브라우저에서 "http://localhost:8080/board"를 입력합니다. 그리고 쓰기 버튼을 클릭합니다. Login 페이지에서 아이이와 패스워드를 입력하면 게시물 작성 페이지로 이동됩니다. 위에서 수정한 것처럼 페이지가 수정되어 나옵니다.

등록 테스트를 위해 제목에 "테스트 제목4", 내용에 "테스트 내용4"를 입력하고 등록 버튼을 누르면 에러가 발생합니다.

에러의 원인은 서버에서 Access-Control-Allow-Headers로 요청 헤더 필드인 access-token을 허용하지 않았기 때문입니다.

Access to XMLHttpRequest at 'http://localhost:9000/boards' from origin 'http://localhost:8080' has been blocked by CORS policy: Request header field access-token is not allowed by Access-Control-Allow-Headers in preflight response.

Node.js 레스트 API 서버에서 HTTP Request의 헤더(header)에 "access-token"이 포함되어 요청될 수 있도록 추가해야 합니다.

반응형

 

Node.js 레스트 API 서버 수정하기

C:\workspaces\nodeserver\testrestapi 폴더에서 index.js 파일을 오픈합니다.

HTTP Request의 헤더(header)에서 "access-token"이 포함되어 요청을 보낼 수 있게 추가합니다.

res.header('Access-Control-Allow-Headers', 'content-type, access-token');

수정 후 Node.js 레스트 API 서버를 다시 실행합니다.

아직 로그아웃을 처리하지 않았기 때문에 Vue를 다시 실행합니다.

4. 위의 3번을 다시 수행합니다. 그러면 정상적을 게시물이 등록되고 게시물 리스트로 이동하는 것을 확인할 수 있습니다.

 

 

게시물 뷰 컴포넌트 수정하기

 

C:\workspaces\nodeserver\testvue\src\views 폴더에 BoardView.vue 파일을 오픈하여 <script>에 computed를 Login 정보와 게시물 작성자를 비교하여 수정할 수 있는지 확인하는 메서드를 추가합니다.

computed : {
	isEditable() {
		var result = false;
		var isLogin = this.$store.getters['loginStore/isLogin'];
		if (isLogin) {
			const writer = this.$store.state.loginStore.memberId;
			if (writer == this.boardItem.writer) {
				result = true;
			}
		}
		return result;
	}
}

그리고 수정 / 삭제 버튼에 Vue 조건부 렌더링(v-if)을 추가합니다.

<button class="button" v-if="isEditable" @click="boardEditClick">수정</button>
<button class="button" v-if="isEditable" @click="boardDeleteClick">삭제</button>

 

 

게시물 수정 컴포넌트 수정하기

C:\workspaces\nodeserver\testvue\src\views 폴더에서 BoardEdit.vue 파일을 오픈하여 <script>의 methods에서 boardUpdateClick() 메서드에 있는 PUT로 전달 데이터(boardItem)에서 writer를 삭제합니다.

async boardUpdateClick() {
	if (this.boardItem.subject == "") {
		alert("제목을 입력하세요.");
		this.$refs.subjectInput.focus();
		return;
	} else if (this.boardItem.content == "") {
		alert("내용을 입력하세요.");
		this.$refs.contentTextarea.focus();
		return;
	}
	var result = confirm("수정하시겠습니까?");
	if (result) {
		let boardItem = { subject : this.boardItem.subject, content : this.boardItem.content };
		try {				
			let res = await this.axios.put("http://localhost:9000/boards/" + this.$route.query.boardNo, boardItem);
			console.log(res.data.success);
			if (res.data.success == true) {
				alert("수정되었습니다.");
				this.$router.push({name : 'BoardList'});
			} else {
				alert("수정되지 않았습니다.");
			}
		} catch(err) {
			console.log(err);
			alert("수정되지 않았습니다.");
		}
	}
}

 

 

로그인/로그아웃 추가하기

1. C:\workspaces\nodeserver\testvue\src 폴더에서 App.vue 파일을 오픈하여 <template>에 Vue 조건부 렌더링(v-if)으로 Login 상태를 확인하여 Login과 Logout이 나타나게 추가합니다.

<template>
	<div class="header">
		<div class="topline">
			<div class="headmenu">
				<div v-if="isLogin">
					{{this.$store.state.loginStore.memberId}}님, 안녕하세요.
					<span @click="Logout()">로그아웃</span>
				</div>
				<div v-else>
					<router-link :to="{ name: 'Login', query: { returnUrl: '/' }}" v-show="isLogin == false">로그인</router-link>
				</div>
			</div>
		</div>
		<div id="nav">
			<router-link to="/">Home</router-link> |
			<router-link to="/about">About</router-link> |
			<router-link :to="{name: 'BoardList'}">Board</router-link> |
			<router-link :to="{name: 'Score'}">Score</router-link>
		</div>
	</div>
	<router-view/>
</template>

그리고 <script>를 추가합니다.

<script>
export default {
	name : 'App',
	methods : {
		Logout() {
			this.$store.dispatch("loginStore/doLogout");
			this.$router.push('/');
		}
	},
	computed : {
		isLogin() {
			return this.$store.getters['loginStore/isLogin'];
		}
	}
};
</script>

그리고 <style>에 추가합니다.

.header { position:relative; }
.header .topline { position:relative; height:30px; margin:0 10px; }
.header .topline .headmenu { position:absolute; right:0; top:4px; }
.header .topline .headmenu a { font-weight:bold; color:#2c3e50; }
.header .topline .headmenu span { font-weight:bold; color:#2c3e50; cursor:pointer; text-decoration:underline; }

 

2. 웹 브라우저에서 "http://localhost:8080/"를 입력합니다. 상단에 로그인이 나타납니다.

로그인을 클릭하여 로그인을 진행합니다.

로그인이 성공하면 상단에 환영 메시지와 로그아웃이 나타납니다.

로그아웃을 클릭합니다.

728x90
반응형