본문 바로가기
Java/Spring

Spring) Ajax - jsonView빈을 통해 응답

by 박채니 2022. 9. 5.
안녕하세요, 코린이의 코딩 학습기 채니 입니다.
개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.

 

Ajax 

jsonView 빈을 통해 응답

 

memberEnroll.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<jsp:include page="/WEB-INF/views/common/header.jsp">
	<jsp:param value="회원등록" name="title"/>
</jsp:include>
<link rel="stylesheet" href="${ pageContext.request.contextPath }/resources/css/member.css" />

<div id="enroll-container" class="mx-auto text-center">
	<form name="memberEnrollFrm" action="" method="POST">
		<table class="mx-auto">
			<tr>
				<th>아이디</th>
				<td>
			        <div id="memberId-container">
			            <input type="text" class="form-control" placeholder="아이디(4글자이상)" name="memberId" id="memberId" required>
			            <span class="guide ok">이 아이디는 사용가능합니다.</span>
			            <span class="guide error">이 아이디는 사용할 수 없습니다.</span>
			            <input type="hidden" id="idValid" value="0"/> <!-- 사용불가한 아이디인 경우 0 | 사용가능한 아이디인 경우 1 -->
			        </div>
				</td>
			</tr>
			<tr>
				<th>패스워드</th>
				<td>
					<input type="password" class="form-control" name="password" id="password" required>
				</td>
			</tr>
			<tr>
				<th>패스워드확인</th>
				<td>	
					<input type="password" class="form-control" id="passwordCheck" required>
				</td>
			</tr>  
			<tr>
				<th>이름</th>
				<td>	
					<input type="text" class="form-control" name="name" id="name" required>
				</td>
			</tr>
			<tr>
				<th>생년월일</th>
				<td>	
					<input type="date" class="form-control" name="birthday" id="birthday"/>
				</td>
			</tr> 
			<tr>
				<th>이메일</th>
				<td>	
					<input type="email" class="form-control" placeholder="abc@xyz.com" name="email" id="email">
				</td>
			</tr>
			<tr>
				<th>휴대폰</th>
				<td>	
					<input type="tel" class="form-control" placeholder="(-없이)01012345678" name="phone" id="phone" maxlength="11" required>
				</td>
			</tr>
			<tr>
				<th>주소</th>
				<td>	
					<input type="text" class="form-control" placeholder="" name="address" id="address">
				</td>
			</tr>
			<tr>
				<th>성별 </th>
				<td>
					<div class="form-check form-check-inline">
						<input type="radio" class="form-check-input" name="gender" id="gender0" value="M">
						<label  class="form-check-label" for="gender0">남</label>&nbsp;
						<input type="radio" class="form-check-input" name="gender" id="gender1" value="F">
						<label  class="form-check-label" for="gender1">여</label>
					</div>
				</td>
			</tr>
			<tr>
				<th>취미 </th>
				<td>
					<div class="form-check form-check-inline">
						<input type="checkbox" class="form-check-input" name="hobby" id="hobby0" value="운동"><label class="form-check-label" for="hobby0">운동</label>&nbsp;
						<input type="checkbox" class="form-check-input" name="hobby" id="hobby1" value="등산"><label class="form-check-label" for="hobby1">등산</label>&nbsp;
						<input type="checkbox" class="form-check-input" name="hobby" id="hobby2" value="독서"><label class="form-check-label" for="hobby2">독서</label>&nbsp;
						<input type="checkbox" class="form-check-input" name="hobby" id="hobby3" value="게임"><label class="form-check-label" for="hobby3">게임</label>&nbsp;
						<input type="checkbox" class="form-check-input" name="hobby" id="hobby4" value="여행"><label class="form-check-label" for="hobby4">여행</label>&nbsp;
					 </div>
				</td>
			</tr>
		</table>
		<input type="submit" value="가입" >
		<input type="reset" value="취소">
	</form>
</div>
<script>
document.querySelector("#memberId").addEventListener('keyup', (e) => {
	const {value : memberId} = e.target;
	
	if(memberId.length < 4) {
		return;
	}
	
	$.ajax({
		url : "${pageContext.request.contextPath}/member/checkIdDuplicate.do",
		data : {memberId},
		success(response) {
			console.log(response); // js object
		},
		error(jqxhr, statusText, err) {
			console.log(jqxhr, statusText, err);
		}
	});
});
</script>
<jsp:include page="/WEB-INF/views/common/footer.jsp"/>

 

Controller

MemberController

 

jsonView

- model에 담긴 속성을 json 문자열로 반환하여 응답메세지 body에 출력

- BeanNameViewResolver를 통해서 viewName에 해당하는 빈을 찾음

@GetMapping("/checkIdDuplicate.do")
public String checkIdDuplicate(@RequestParam String memberId, Model model) {
    Member member = memberService.selectOneMember(memberId);
    boolean available = member == null;

    model.addAttribute("memberId", memberId);
    model.addAttribute("available", available);

    return "jsonView";
}

이렇게 되면 InternalResourceViewResolver에 의해서 '/WEB-INF/views/jsonView.jsp'를 찾을 것입니다.

이를 위해 jsonView 빈 등록 + Resolver를 추가해줍니다.

 

pom.xml

#12. jsonView 의존

<!-- #12 jsonView 의존 -->
<dependency>
    <groupId>net.sf.json-lib</groupId>
    <artifactId>json-lib-ext-spring</artifactId>
    <version>1.0.2</version>
</dependency>

 

servlet-context.xml

#12.1 jsonView 빈 등록

#12.2 BeanNameViewResolver : viewName에 해당하는 빈을 view로 연결하는 resolver

...
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".jsp" />
</beans:bean>
...
...

<!-- #12.1 jsonView 빈 등록 -->
<beans:bean id="jsonView" class="net.sf.json.spring.web.servlet.view.JsonView">
    <beans:property name="contentType" value="application/json; charset=utf-8"/>
</beans:bean>

<!-- #12.2 BeanNameViewResolver viewName에 해당하는 빈을 view로 연결하는 resolver -->
<beans:bean id="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver">
    <beans:property name="order" value="1" />
</beans:bean>

BeanNameViewResolver는 넘겨받은 viewName에 해당하는 빈이 등록 되어있는 지 판단 후 만일 존재한다면 해당 view로 연결해줍니다.

또한 우선순위를 정해줄 수 있는데 (name="order" value = ?), BeanNameViewResolver가 1순위로 지정되어 있어 먼저 검사를 한 후 존재한다면 BeanNameViewResolver에서 처리, 그렇지 않다면 InternalResourceViewResolver가 처리합니다.

 

Controller에서 'jsonView'를 넘겨주었고, jsonView라는 id 값을 가진 빈이 (JsonView) 등록 되어있기 때문 BeanNameViewResolver가 실행 되어 view로 연결시켜주는 것이죠.

 

만일 "member/memberEnroll"이라는 viewName이 넘어오게 된다면, 최우선순위인 BeanNameViewResolver가 넘어온 viewName에 해당하는 빈이 있는 지 찾게 됩니다.

존재하지 않기 때문에 InternalResourceViewResolver가 처리해주는 것이죠.

 

contentType 설정을 통해 인코딩 설정!

jquery가 json 문자열을 받은 후 parsing하여 success 함수 호출! (js object)

응답이 잘 넘어오는 것을 확인할 수 있습니다.

(jsonView - model에 담긴 속성을 json 문자열로 반환하여 응답메세지 body에 작성!!)

json 문자열로 반환

 

 

후처리

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<jsp:include page="/WEB-INF/views/common/header.jsp">
	<jsp:param value="회원등록" name="title"/>
</jsp:include>
<link rel="stylesheet" href="${ pageContext.request.contextPath }/resources/css/member.css" />

<div id="enroll-container" class="mx-auto text-center">
	<form name="memberEnrollFrm" action="" method="POST">
		<table class="mx-auto">
			<tr>
				<th>아이디</th>
				<td>
			        <div id="memberId-container">
			            <input type="text" class="form-control" placeholder="아이디(4글자이상)" name="memberId" id="memberId" required>
			            <span class="guide ok">이 아이디는 사용가능합니다.</span>
			            <span class="guide error">이 아이디는 사용할 수 없습니다.</span>
			            <input type="hidden" id="idValid" value="0"/> <!-- 사용불가한 아이디인 경우 0 | 사용가능한 아이디인 경우 1 -->
			        </div>
				</td>
			</tr>
			<tr>
				<th>패스워드</th>
				<td>
					<input type="password" class="form-control" name="password" id="password" required>
				</td>
			</tr>
			<tr>
				<th>패스워드확인</th>
				<td>	
					<input type="password" class="form-control" id="passwordCheck" required>
				</td>
			</tr>  
			<tr>
				<th>이름</th>
				<td>	
					<input type="text" class="form-control" name="name" id="name" required>
				</td>
			</tr>
			<tr>
				<th>생년월일</th>
				<td>	
					<input type="date" class="form-control" name="birthday" id="birthday"/>
				</td>
			</tr> 
			<tr>
				<th>이메일</th>
				<td>	
					<input type="email" class="form-control" placeholder="abc@xyz.com" name="email" id="email">
				</td>
			</tr>
			<tr>
				<th>휴대폰</th>
				<td>	
					<input type="tel" class="form-control" placeholder="(-없이)01012345678" name="phone" id="phone" maxlength="11" required>
				</td>
			</tr>
			<tr>
				<th>주소</th>
				<td>	
					<input type="text" class="form-control" placeholder="" name="address" id="address">
				</td>
			</tr>
			<tr>
				<th>성별 </th>
				<td>
					<div class="form-check form-check-inline">
						<input type="radio" class="form-check-input" name="gender" id="gender0" value="M">
						<label  class="form-check-label" for="gender0">남</label>&nbsp;
						<input type="radio" class="form-check-input" name="gender" id="gender1" value="F">
						<label  class="form-check-label" for="gender1">여</label>
					</div>
				</td>
			</tr>
			<tr>
				<th>취미 </th>
				<td>
					<div class="form-check form-check-inline">
						<input type="checkbox" class="form-check-input" name="hobby" id="hobby0" value="운동"><label class="form-check-label" for="hobby0">운동</label>&nbsp;
						<input type="checkbox" class="form-check-input" name="hobby" id="hobby1" value="등산"><label class="form-check-label" for="hobby1">등산</label>&nbsp;
						<input type="checkbox" class="form-check-input" name="hobby" id="hobby2" value="독서"><label class="form-check-label" for="hobby2">독서</label>&nbsp;
						<input type="checkbox" class="form-check-input" name="hobby" id="hobby3" value="게임"><label class="form-check-label" for="hobby3">게임</label>&nbsp;
						<input type="checkbox" class="form-check-input" name="hobby" id="hobby4" value="여행"><label class="form-check-label" for="hobby4">여행</label>&nbsp;
					 </div>
				</td>
			</tr>
		</table>
		<input type="submit" value="가입" >
		<input type="reset" value="취소">
	</form>
</div>
<script>
const ok = document.querySelector(".guide.ok");
const error = document.querySelector(".guide.error");
const idValid = document.querySelector("#idValid");

document.querySelector("#memberId").addEventListener('keyup', (e) => {
	const {value : memberId} = e.target;
	
	if(memberId.length < 4) {
		ok.style.display = "none";
		error.style.display = "none";
		idValid.value = "0";
		return;
	}
	
	$.ajax({
		url : "${pageContext.request.contextPath}/member/checkIdDuplicate.do",
		data : {memberId},
		success(response) {
			const {available} = response
			
			if(available) {
				ok.style.display = "inline";
				error.style.display = "none";
				idValid.value = "1";
			} else {
				ok.style.display = "none";
				error.style.display = "inline";
				idValid.value = "0";
			}
		},
		error(jqxhr, statusText, err) {
			console.log(jqxhr, statusText, err);
		}
	});
});

document.memberEnrollFrm.addEventListener('submit', (e) => {
	if(idValid.value == "0") {
		e.preventDefault();
		alert("유효한 아이디를 입력해주세요.");
		return;
	}
});
</script>
<jsp:include page="/WEB-INF/views/common/footer.jsp"/>