[211224] Json-simple & 로그인 중복 체크
Ajax란 Asynchronous JavaScript and XML의 약자입니다. Ajax는 빠르게 동작하는 동적인 웹 페이지를 만들기 위한 개발 기법의 하나입니다. Ajax는 웹 페이지 전체를 다시 로딩하지 않고도, 웹 페이지의 일부분만을 갱신할 수 있습니다.
- 비동기 통신을 사용하여 웹 서버에 메시지를 보내기 위해 사용
- 서버에 데이터를 보낼 때 JSON, XML 등의 형식으로 보내고 응답 받는다.
- 비동기 통신은 요청과 응답이 서로 분리되어 동작하는 형태를 지칭
-> 요청 후 응답이 오지 않아도 사용자는 브라우저에서 지속적으로 작업을 진행할 수 있다.
- 웹 페이지의 전환 없이 데이터를 갱신하거나 화면 효과를 부여하는 등의 처리를 할 수 있다.
(ex 쇼핑몰 스크롤 아래로 내릴때마다 화면 갱신 없이 품목 리스트가 계속 나오는 것, 깃허브에서 생성할 레포지터리 이름 입력하면 사용 가능한 이름인지 검사)
- Ajax 를 사용해도 HTTP 통신이다.
메인 페이지에서 Ajax 사용하기
<script type="text/javascript" src="/static/js/jquery-3.6.0.min.js"></script>
<div>
<h1>Ajax 샘플</h1>
<p>JQuery 라이브러리 사용 - Ajax에 대한 편의성을 제공</p>
<button type="button" onclick="sendAjax();">누르면 Ajax 전송</button>
<script type="text/javascript">
function sendAjax(){
console.log("Ajax를 시작 합니다.");
$.ajax({
url: "", // Ajax 처리를 수행할 서버 URL 주소 (Controller에 맵핑된 주소)
type: "", // get/post
data: {}, // 전송할 데이터의 키: "값" (키는 쌍따옴표 필요 x 값은 문자열일 경우 쌍따옴표 필요)
dataType: "", // 응답 데이터의 형식(json/xml ... 우리는 json 할 것)
success: "", // 응답이 왔을 때 동작할 함수명을 지정 또는 직접 함수 작성(콜백함수)
error: "" // 응답에 에러가 있을 때 ...
});
}
</script>
</div>
1. index.jsp (메인 페이지) 에 작성
<div>
<h1>Ajax 샘플</h1>
<p>JQuery 라이브러리 사용 - Ajax에 대한 편의성을 제공</p>
<button type="button" onclick="sendAjax();">누르면 Ajax 전송</button>
<script type="text/javascript">
function sendAjax(){
console.log("Ajax를 시작 합니다.");
$.ajax({
url: "./ajax/sample", // Ajax 처리를 수행할 서버 URL 주소 (Controller에 맵핑된 주소)
type: "get", // get/post
data: {
key: "value",
x: 10,
y: 20
}, // 전송할 데이터의 키: "값" (키는 쌍따옴표 필요 x 값은 문자열일 경우 쌍따옴표 필요)
dataType: "json", // 응답 데이터의 형식(json/xml)
success: function() {
console.log("서버로부터의 응답이 왔습니다.")
} // 응답이 왔을 때 동작할 함수명을 지정 또는 직접 함수 작성(콜백함수)
//error: "" // 응답에 에러가 있을 때 ...
});
}
</script>
</div>
2. 메인 페이지 실행 /localhost
3. Ajax 전송(button onclick)
F12 개발자모드 - 네트워크 - 헤더, Payload 에서 보낸 Ajax 확인할 수 있음
404 에러 뜨므로
받아올 컨트롤러 생성하여 값 확인
반환될 형식 = json -> 다 문자열로 변환해서 보내주면 됨
json 형식 예시
키 : 문자열 (원래)
값 : 불린, 문자열, 숫자, 객체, 배열[]
키 : 값 의 값 안에 또 키 : 값 있을 수 있음
문자열 직접 만들기
중괄호로 시작 { 중괄호로 끝나야 } 한다.
간단하게 라이브러리 사용하기
json-simple-1.1.1.jar 파일 설치
https://repo1.maven.org/maven2/com/googlecode/json-simple/json-simple/1.1.1/
Central Repository: com/googlecode/json-simple/json-simple/1.1.1
repo1.maven.org
간편하게 작성
package com.web.ajax;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.simple.JSONObject;
@WebServlet("/ajax/sample")
public class AjaxSampleController extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("key: " + request.getParameter("key"));
System.out.println("x: " + request.getParameter("x"));
System.out.println("y: " + request.getParameter("y"));
int x = Integer.parseInt(request.getParameter("x"));
int y = Integer.parseInt(request.getParameter("y"));
//로직
//로직의 결과를 반환 -> json 형식으로 만들기
JSONObject json = new JSONObject();
json.put("xy", x+y);
response.getWriter().println(json.toString()); //쓰기를 통해 응답하겠다. 문자열로 작성한 키 xy와 더하기 값 전송되어 success함수 (?)
response.getWriter().flush();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}
버튼 하나 더 생성해서 post도 테스트
한글 데이터는 깨지므로 인코딩 한다.
json 한글 인코딩
response.setContentType("application/json; charset=utf-8");
또는 인코딩 필터 작성한 것 수정 (요청 영역에 둘 다 넣는다. 왜?) - 현재는 수정하지 않고 JSONFilter로 분리
package com.web.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
//전 서블릿(컨트롤러)에 대해 이 필터를 거치도록 해야 함
@WebFilter("/*") //모든 요청에 대해 처리 annotation
public class EncodingFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 요청 필터 작성 영역
request.setCharacterEncoding("utf-8"); //클라이언트 -> 서버로 보내는 문자
String reqURI = ((HttpServletRequest) request).getRequestURI();
if(reqURI.contains("ajax")) {
response.setContentType("application/json; charset=utf-8");
} else {
response.setContentType("text/html; charset=utf-8"); // 서버 -> 클라이언트
}
System.out.println("-------------encordingfilter--------");
chain.doFilter(request, response); //기준
// 응답 필터 작성 영역
}
}
이유
-> response.setContentType("text/html; charset=utf-8"); // 서버 -> 클라이언트
는 response 메세지(json.toString())를 utf-8 로 바꾸어주는게 아니라 애초에 전송(response)문자열을 생성할 때 utf - 8로 만들겠다는 말
따라서 request 필터 작성 영역부분에 response.setContentType("text/html; charset=utf-8"); 를 해주지 않았다면
이미 toString 했을 때 utf - 8 이 아님 이미 다른걸로 문자열이 만들어졌다. 그리고 보내는 과정 중에
do filter 이후에 다른 응답필터로 이동할 건데 그때 가서 response.setContentType utf-8 해봤자 소용이 없기 때문.
그래서 문자열 만들어서(toString()) 응답!하기 전에 rresponse.setContentType utf-8 해두면 원하는 대로 utf 8 로 만들어진다.
실습 문제 1
JSONFilter를 생성하여 /ajax URL 주소가 있는 경우 아래와 같은 컨텐츠 타입이 설정 되도록 한다.
-> EncodingFilter 수정한 것은 원래대로 되돌리고 JSONFilter 로 분리시켰음
자습 풀이
JSONFilter.java
package com.web.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
@WebFilter("/ajax/*")
public class JSONFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setContentType("application/json; charset=utf-8");
System.out.println("-------------jsonfilter-------");
chain.doFilter(request, response);
}
}
실습 문제 2
자습 풀이
join.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.util.*"
import="com.web.account.model.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입</title>
<script type="text/javascript" src="/static/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="/static/js/join.js"></script>
<script type="text/javascript" src="/static/js/joinCheckId.js"></script>
<link type="text/css" rel="stylesheet" href="/static/css/join.css">
</head>
<body>
<%
AccountDTO initData = new AccountDTO();
String error = "";
if(request.getAttribute("init") != null) {
//반드시 다운캐스팅 ㄱ (아마오브젝트로 업캐스팅된상태여서[11/18에 설명해줌])
initData = (AccountDTO) request.getAttribute("init");
}
if(request.getAttribute("error") != null) {
error = (String) request.getAttribute("error");
}
%>
<h2>회원가입 하기</h2>
<form id="join_form" action="./join" method="post">
<div>
<label>계정명</label>
<input type="text" id="user_id" name="username" placeholder="계정 이름" value= "<%=initData.getUsername() %>" required>
<input type="hidden" id="id_button" value="">
</div>
<div>
<label>패스워드</label>
<input type="password" name="password" placeholder="패스워드" required>
</div>
<div>
<label>패스워드 확인</label>
<input type="password" name="password_check" placeholder="패스워드 확인" required>
</div>
<input type="email" name="email" placeholder="이메일 주소" value = "<%=initData.getEmail() %>" required>
<button type="submit">전송</button>
</form>
</body>
</html>
JoinIdCheckController.java
package com.web.ajax;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.simple.JSONObject;
import com.web.account.model.AccountDTO;
import com.web.account.model.AccountService;
@WebServlet("/ajax/joinIdCheck")
public class JoinIdCheckController extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String want = request.getParameter("want"); //키로 받음
System.out.println("want|" + want + "|");
//로직 //회원가입 아이디 중복체크
AccountDTO dto = new AccountDTO(want);
AccountService service = new AccountService();
JSONObject json = new JSONObject();
if(service.findAccount(dto) != null) { //이미 있는 계정이라면
//로직의 결과를 반환 -> json 형식으로 만들기
//response.setContentType("application/json; charset=utf-8"); 인코딩 필터를 수정
json.put("alert", "동일한 이름의 계정이 이미 존재합니다");
} else {
json.put("alert", "사용 가능한 계정입니다.");
};
response.getWriter().println(json.toString()); //쓰기를 통해 응답하겠다. 문자열로 작성한 키 xy와 더하기 값 전송되어 success함수 (?)
response.getWriter().flush();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}
AccountDAO.java
dao.selectAccount() 메서드 활용
package com.web.account.model;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.db.conn.MybatisConnect;
import com.db.conn.OracleConnect;
public class AccountDAO {
private MybatisConnect mc;
private SqlSession sess;
public AccountDAO() {
this.mc = new MybatisConnect();
this.sess = this.mc.getSession();
}
public AccountDTO selectAccount(AccountDTO dto) {
AccountDTO data = this.sess.selectOne("AccountMapper.selectAccount", dto);
return data;
}
public List<AccountDTO> selectChoiceAccount(List<Integer> list) {
List<AccountDTO> datas = this.sess.selectList("AccountMapper.selectChoiceAccount", list);
return datas;
}
//세션에 인서트, 업데이트, 딜리트 메서드 있으므로 사용하면 됨
//유저계정 추가
public boolean insert(AccountDTO dto) {
int res = this.sess.insert("AccountMapper.insertAccount", dto);
return res == 1 ? true : false;
}
//계정정보 수정
public boolean updateAccountInfo(AccountDTO dto) {
int res = this.sess.update("AccountMapper.updateAccountInfo", dto);
return res == 1 ? true : false;
}
//계정비번 수정
public boolean updateAccountPassword(AccountDTO dto) {
int res = this.sess.update("AccountMapper.updateAccountPassword", dto);
return res == 1 ? true : false;
}
//회원 탈퇴
public boolean deleteAccount(AccountDTO dto) {
int res = this.sess.delete("AccountMapper.deleteAccount", dto);
return res == 1 ? true : false;
}
public AccountDTO selectLoginAccount(String username, String password){
AccountDTO dto = new AccountDTO();
dto.setUsername(username);
dto.setPassword(password);
//객체에 담아서 파라미터로 account.xml로 보낸다.
AccountDTO data = this.sess.selectOne("AccountMapper.selectAccount", dto); //파라메터 하나밖에 못 싣음. DTO 객체로 주로 보낸다. 이때 account.xml 파라메터타입 해당 dto로 지정해야 함(com.web.---)
//account.xml의 mapper namespace & select id
System.out.println("AccountDAO.selectLoginAccount() ->" + data); //확인용(/login 페이지 가서 로그인 했을 때 유저정보 나와야 로그인 정상)
return data;
}
public void commit() {
mc.commit();
}
public void rollback() {
mc.rollback();
}
public void close() {
mc.close();
}
}
AccountDTO 생성자 오버로드 했음 public AccountDTO(String username) { //회원가입 아이디 중복체크 this.username = username; } |
account.xml
mybatis 연결 객체로 db 불러 옴
<select id="selectAccount" parameterType="account" resultType="account"> 활용
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 넘어오는 파라메터(AccountDTO select() - username)의 타입을 지정해주어야 한다. -->
<mapper namespace="AccountMapper">
<!--AccountDAO.selectAccount() -->
<select id="selectAccount" parameterType="account" resultType="account"> <!--(원)com.web.account.model.AccountDTO -> mybatis-config.xml 에서 <typeAliases>로 별칭부여해서 사용 -->
SELECT * FROM ACCOUNTS
<where> <!-- 가장 처음에 오는 AND 나 OR을 알아서 where로 변경 -->
<if test="id != 0"> <!-- test에 #{id} 이렇게 쓰지 않아도 됨 -->
AND ID = #{id}
</if>
<if test="username != null">
AND USERNAME = #{username}
</if>
<if test="password != null">
AND PASSWORD = #{password}
</if>
</where>
</select>
<select id="selectChoiceAccount" parameterType="list" resultType="account">
SELECT * FROM ACCOUNTS
<where>
ID in
<foreach item="n" collection="list" open="(" separator="," close=")">
#{n}
</foreach>
</where>
</select>
<!-- 유저 추가용, 리턴타입 기본적으로 int -->
<insert id="insertAccount" parameterType="account">
INSERT INTO ACCOUNTS VALUES(
ACCOUNTS_SEQ.NEXTVAL
, #{username}
, #{password}
, #{email}
, #{joinDate}
)
</insert>
<update id="updateAccountInfo" parameterType="account">
UPDATE ACCOUNTS
SET EMAIL = #{email}
WHERE ID = #{id}
</update>
<update id="updateAccountPassword" parameterType="account">
UPDATE ACCOUNTS
SET EMAIL = #{password}
WHERE ID = #{id}
</update>
<delete id="deleteAccount" parameterType="account">
DELETE FROM ACCOUNTS
WHERE ID = #{id}
</delete>
</mapper>
joinIdCheck.js
window.onload = function() {
$('#user_id').on('input', function(){
//초기화?
//$('#id_button').prop("type","hidden");
console.log($(this).val()); //실시간 입력 받은 것 사용. f2 shift 특수키 감지x
var input = $(this).val();
if(input == ''){
console.log("입력값 없는 상태");
$('#id_button').prop("type","hidden");
} else if(input.indexOf(" ") == -1){
$.ajax({
url: "./ajax/joinIdCheck", // Ajax 처리를 수행할 서버 URL 주소 (Controller에 맵핑된 주소)
type: "get", // get/post
data: {
want: input
}, // 전송할 데이터의 키: "값" (키는 쌍따옴표 필요 x 값은 문자열일 경우 쌍따옴표 필요)
dataType: "json", // 응답 데이터의 형식(json/xml)
success: function(data) {
console.log(data.alert);
$('#id_button').prop({"type":"button", "value":data.alert}); //배열로 한번에 지정
//$('#id_button').prop("value", data.alert);
} // 응답이 왔을 때 동작할 함수명을 지정 또는 직접 함수 작성(콜백함수)
//error: "" // 응답에 에러가 있을 때 ...
});
} else if(input.indexOf(" ") != -1) {
$('#id_button').prop("type","button");
$('#id_button').prop("value", "공백을 포함할 수 없습니다");
}
});
}
function idCheck(alert){
};
function sendPost(){
console.log("Post로 보내기");
$.ajax({
url: "./ajax/sample", // Ajax 처리를 수행할 서버 URL 주소 (Controller에 맵핑된 주소)
type: "post", // get/post
data: {
key: "post한글데이터",
x: 10,
y: 20
}, // 전송할 데이터의 키: "값" (키는 쌍따옴표 필요 x 값은 문자열일 경우 쌍따옴표 필요)
dataType: "json", // 응답 데이터의 형식(json/xml)
success: function(data) {
console.log(data.xy)
console.log(data.msg)
} // 응답이 왔을 때 동작할 함수명을 지정 또는 직접 함수 작성(콜백함수)
//error: "" // 응답에 에러가 있을 때 ...
});
}
결과
js 에서는 contains 함수를 쓸 수 없으므로 공백을 거르고 싶다면 indexOf(" ") == -1 로 조건 설정해 주어야 한다.
제이쿼리로 type, value 값을 지정하고 싶다면
$('#id_button').style("type","button")가 아닌 $('#id_button').prop("type","button")을 사용하여야 한다.
배열로 한번에 지정
$('#id_button').prop({"type":"button", "value":data.alert});
.attr() : HTML의속성을 취급
.prop() : javaScript프로퍼티를 취급
궁금!
왜 빨리 지우면 버튼 안 사라지고 천천히 지우면 잘 사라지는 가?
이유 -> 비동기 통신이므로 요청과 응답이 분리. 응답 하지 않아도 요청은 계속해서 보내지므로
개선 -> 따라서 다음과 같이 조건식을 이용. 이전에 변수로 설정 된 input이 아닌 현재 시간의 사용자 입력값을 기준으로 걸러서 버튼이 동작하도록 만들어야 한다.
if($('#user_id').val() == '') if($('#user_id').val().indexOf(' ') !== -1) |
개선된 코드
joinIdCheck.js
window.onload = function() {
$('#user_id').on('input', function(){
var input = $(this).val();
$.ajax({
url: "./ajax/joinIdCheck", // Ajax 처리를 수행할 서버 URL 주소 (Controller에 맵핑된 주소)
type: "get", // get/post
data: {
want: input
}, // 전송할 데이터의 키: "값" (키는 쌍따옴표 필요 x 값은 문자열일 경우 쌍따옴표 필요)
dataType: "json", // 응답 데이터의 형식(json/xml)
success: function(data) {
if(data.state === "success"){
$('#id_button').prop({"type":"button", "value":data.msg});
}
if (data.state === "fail") {
$('#id_button').prop({"type":"button", "value":data.msg});
}
if($('#user_id').val() == ''){
$('#id_button').prop("type","hidden");
}
if($('#user_id').val().indexOf(' ') !== -1){
$('#id_button').prop({"type":"button", "value":"공백을 포함하실 수 없습니다."});
}
//$('#id_button').prop("value", data.alert);
} // 응답이 왔을 때 동작할 함수명을 지정 또는 직접 함수 작성(콜백함수)
//error: "" // 응답에 에러가 있을 때 ...
});
});
}