GET/POST - Ajax

oYJo·2025년 3월 13일

Spring

목록 보기
18/29
post-thumbnail

1️⃣ GET방식 ajax

이메일 중복 검사 기능

✔️webapp(resources(js(signUp))) → signUp.js

...
// 정규 표현식을 이용해서 유효한 형식인지 판별
// 1) 정규 표현식 객체 생성
const regEx = /^[A-Za-z\d\-\_]{4,}@[가-힣\w\-\_]+(\.\w+){1,3}$/;

// 2) 입력 받은 이메일과 정규식 일치 여부 판별
if(regEx.test(memberEmail.value)){ // 유효한 경우

  /****************************************************************/
  /* fetch() API를 이용한 ajax(비동기 통신) */

  // GET 방식 ajax 요청(파라미터는 쿼리스트링으로!)
  fetch("/dupCheck/email?email=" + memberEmail.value)

    .then( response => response.text()) // 응답 객체 -> 파싱(parsing, 데이터 형태 변환)

    .then( count => {
    // count : 중복되면 1, 중복 아니면 0
    if(count == 0){
      emailMessage.innerText = "사용 가능한 이메일입니다.";
      emailMessage.classList.add("confirm");
      emailMessage.classList.remove("error");

      checkObj.memberEmail = true; // 유효 O

    } else {
      emailMessage.innerText = "이미 사용 중인 이메일입니다.";
      emailMessage.classList.add("error");
      emailMessage.classList.remove("confirm");

      checkObj.memberEmail = false; // 유효 X
    }
  }) // 파싱한 데이터를 이용해서 수행할 코드 작성

    .catch(err => console.log(err)) // 예외 처리

  /****************************************************************/

} else{ // 유효하지 않은 경우
  emailMessage.innerText = "이메일 형식이 유효하지 않습니다.";
  emailMessage.classList.add("error");
  emailMessage.classList.remove("confirm");

  checkObj.memberEmail = false; // 유효 X
}

})
...

✔️project(member(controller)) → AjaxController.java

...
	// 이메일 중복 검사
	@GetMapping("/dupCheck/email")
	@ResponseBody // HttpMessageConverter를 이용해
				  // JS에서 인식할 수 있는 형태(TEXT/JSON) 반환
				  // + 비동기 요청한 곳으로 돌아감
	public int checkEmail(String email) {
		return service.checkEmail(email);
	}

✔️project(member(model(service))) → AjaxServiceImpl.java

...
	/** 이메일 중복 검사
	 * @param email
	 * @return count
	 */
	int checkEmail(String email);

✔️project(member(model(service))) → AjaxService.java

...
	// 이메일 중복 검사
	@Override
	public int checkEmail(String email) {
		return dao.checkEmail(email);
	}

✔️project(member(model(dao))) → AjaxDAO.java

...
	/** 이메일 중복 검사
	 * @param email
	 * @return count
	 */
	public int checkEmail(String email) {
		return sqlSession.selectOne("ajaxMapper.checkEmail", email);
	}

✔️resources(mappers) → ajax-mapper-xml

...
<!-- 이메일 중복 검사 -->
<select id="checkEmail" resultType="_int">
  SELECT COUNT(*) FROM MEMBER
  WHERE MEMBER_EMAIL = #{email}
  AND MEMBER_DEL_FL = 'N'	
</select>

근데 값이 없는데도 중복된 닉네임이라고 뜬다
왜?
MappingJackson2HttpMessageConverter
근데 Jackson은 요청 DTO/Map, 응답 JSON만 해주는데

Jackson databind라는 아이는 좀 더 큰 아이
→ 기본 자료형까지 더 업그레이드 라이브러리

하여간, Jackson이 없어서 생기는 문제
라이브러리를 pom.xml에 추가한다

✔️pom.xml

...
<!-- Jackson-databind -->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.14.2</version>
</dependency>
...

닉네임 중복 검사 기능

✔️webapp(resources(js(signUp))) → signUp.js

...
// 정규 표현식으로 유효성 검사
const regEx = /^[가-힣\w\d]{2,10}$/;

if(regEx.test(memberNickname.value)){ // 유효

  fetch("/dupCheck/nickname?nickname=" + memberNickname.value)
    .then(resp => resp.text()) // 응답 객체를 text로 파싱(변환)
    .then(count => {

    if(count == 0){ // 중복이 아닌 경우

      nickMessage.innerText = "사용 가능한 닉네임입니다.";
      nickMessage.classList.add("confirm");
      nickMessage.classList.remove("error");

      checkObj.memberNickname = true;

    } else { // 중복인 경우

      nickMessage.innerText = "이미 사용 중인 닉네임입니다.";
      nickMessage.classList.add("error");
      nickMessage.classList.remove("confirm");

      checkObj.memberNickname = false;
    }

  })
    .catch(err => console.log(err));

} else{ // 무효
  nickMessage.innerText = "닉네임 형식이 유효하지 않습니다.";
  nickMessage.classList.add("error");
  nickMessage.classList.remove("confirm");

  checkObj.memberNickname = false;
}
})
  ...

✔️project(member(controller)) → AjaxController.java

...
	// 닉네임 중복 검사
	@GetMapping("/dupCheck/nickname")
	@ResponseBody
	public int checkNickname(String nickname) {
		return service.checkNickname(nickname);
	}

✔️project(member(model(service))) → AjaxService.java

...
	/** 닉네임 중복 검사
	 * @param nickname
	 * @return count
	 */
	int checkNickname(String nickname);

✔️project(member(model(service))) → AjaxServiceImpl.java

...
	// 닉네임 중복 검사
	@Override
	public int checkNickname(String nickname) {
		return dao.checkNickname(nickname);
	}

✔️project(member(model(dao))) → AjaxDAO.java

...
	/** 닉네임 중복 검사
	 * @param nickname
	 * @return count
	 */
	public int checkNickname(String nickname) {
		return sqlSession.selectOne("ajaxMapper.checkNickname", nickname);
	}

✔️resources(mappers) → ajax-mapper-xml

...
<!-- 닉네임 중복 검사 -->
<select id="checkNickname" resultType="_int">
  SELECT COUNT(*) FROM MEMBER
  WHERE MEMBER_NICKNAME = #{nickname}
  AND MEMBER_DEL_FL = 'N'	
</select>

@ResponseBody

💡 비동기통신을 할 때 클라이언트 → 서버로 요청
이때, 본문(Body)에 데이터를 담아 보내야함
이때 데이터 형식은 많은데, 대표적인 것이 JSON

@RequestBody를 통해 자바객체로 conversion
이때, HttpMessageConverter가 들어가 있다


2️⃣ POST방식 ajax

이메일 입력받아 일치하는 회원 정보 조회 기능

✔️webapp(WEB-INF(views(common))) → main.js

// fetch() API를 이용한 POST 방식 요청

// 이메일을 입력받아 일치하는 회원의 정보를 모두 조회
const inputEmail = document.getElementById("inputEmail");
const btn2 = document.getElementById("btn2");
const result2 = document.getElementById("result2");

btn2.addEventListener("click", ()=>{

  // POST 방식 비동기 요청

  // JSON.stringify() : JS 객체 -> JSON
  // JSON.parse()     : JSON -> JS 객체
  fetch("/selectMember", {
    method : "POST",
    headers : {"Content-Type" : "application/json"},
    body : JSON.stringify({"email" : inputEmail.value})
  })
    .then(resp => resp.json()) // 응답 객체를 매개변수로 얻어와 파싱
    .then(member => {
    console.log(member);

    // ul(#result2)의 내부 내용 모두 없애기
    result2.innerText = "";

    const li1 = document.createElement("li");
    li1.innerText = `회원 번호 : ${member.memberNo}`;

    const li2 = document.createElement("li");
    li2.innerText = `이메일 : ${member.memberEmail}`;

    const li3 = document.createElement("li");
    li3.innerText = `닉네임 : ${member.memberNickname}`;

    const li4 = document.createElement("li");
    li4.innerText = `전화번호 : ${member.memberTel}`;

    const li5 = document.createElement("li");
    li5.innerText = `주소 : ${member.memberAddress}`;

    const li6 = document.createElement("li");
    li6.innerText = `가입일 : ${member.enrollDate}`;

    result2.append(li1, li2, li3, li4, li5, li6);

  }) // 파싱한 데이터를 이용해서 비동기 처리 후 동작

    .catch(err => {
    console.log(err)
    result2.innerText = "일치하는 회원이 없습니다.";
  })
})
// JSON.stringify() : JS 객체 -> JSON
// JSON.parse()     : JSON -> JS 객체
fetch("/selectMember", {
  method : "POST",
  headers : {"Content-Type" : "application/json"},
  body : JSON.stringify({"email" : inputEmail.value})
    })

JSON : 자바스크립트 객체 표기 문자열
{”email” : inputEmail.value}
근데 이건 문자열이 아니잖아..!
그럼 ‘{”email” : inputEmail.value}’ 이렇게 쓰면 문자열이 되나?
여기서는 안된다

그럼 따로 메소드를 사용해서 바꿔준다

 // JSON.stringify() : JS 객체 -> JSON
 // JSON.parse()     : JSON -> JS 객체
 .then(resp => resp.json()) // 응답 객체를 매개변수로 얻어와 파싱

파싱한 것을 잡아서 뭘할까?
회원저보를 얻고 싶은거라 넣어줌

.then(member => {
  console.log(member);

✔️webapp(WEB-INF(views(common))) → main.jsp

...
<h3>이메일을 입력받아 일치하는 회원의 정보를 조회</h3>
email : <input type="text" id="inputEmail">
<button id="btn2">조회</button>
<ul id="result2">
</ul>
...


직접 뿌려주기

💡 템플릿 리터럴(Template literals)
내장된 표현식을 허용하는 문자열 리터럴로
표현식 / 문자열 삽입, 여러 줄 문자열, 문자열 형식화 등 다양한 기능을 제공한다


ES6부터 ``(백틱) 사용

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Template_literals

원래는 + 이런 식으로 사용해야 하는데, template literals을 이용하면 syntacitc sugar를 활용

이렇게 + 하지 않아도 `` 좀 더 깔끔하게 사용할 수 있다
여기까지는 생성만 된거고 마지막에 추가시켜줘야 한다

백틱을 사용하면 원래는 \n을 써줘야 했는데 그냥 원하는 대로 형태 그대로 반영된다.

💡 append : 뒤에 붙는 기능
prepend : 앞에 붙는 기능
html : 태그를 포함해서 가져온다
text : 태그를 제외하고 가져온다
empty : 요소 내용을 지운다, append prepend 이전에 사용
remove : 요서 자체를 지운다. 그래서 그 뒤로 jquery로 읽어낼 수 없다.

✔️project(member(controller)) → AjaxController.java

...
	// 이메일로 회원 정보 조회
	@PostMapping(value="/selectMember", produces = "application/json; charset=UTF-8")
	@ResponseBody // Java 데이터 -> JSON, TEXT로 변환 + 비동기 요청한 곳으로 응답
	public Member selectMember(@RequestBody Map<String, Object> paramMap) {
		
		// @RequestBody Map<String, Object> paramMap
		// -> 요청된 HTTP Body에 담긴 모든 데이터를 Map으로 반환
		
		String email = (String)paramMap.get("email");
		
		return service.selectMember(email);
	}
	@PostMapping("/selectMember")
	public Member selectMember(@RequestBody Map<String, Object>) {
		return null;
	}

Member 전달 할 파라미터가 너무 많다
→ Member로 가져오기
json으로 담아야 하기에 Map에 담겨준다
왜냐하면 Map은 key를 통해 value를 얻기 때문이다


1. @ResponseBody를 안 쓰면 forward 되니깐 다시 돌아갈 수 있게 어노테이션 써줘야 한다
@ResponseBody // Java 데이터 -> TEXT, JSON으로 변환 및 비동기 요청한 곳으로 응답


2. 결과에 한글이 있으니깐 utr-8처리를 해줘야 한다
@PostMapping(value="/selectMember", produces = "application/json; charset=UTF-8")
근데 이때 application/text가 아니라 application/json이다!

✔️project(member(model(service))) → AjaxService.java

	/** 이메일로 회원 정보 조회
	 * @param email
	 * @return member
	 */
	Member selectMember(String email);

✔️project(member(model(service))) → AjaxServiceImpl.java

...
	// 이메일로 회원 정보 조회
	@Override
	public Member selectMember(String email) {
		return dao.selectMember(email);
	}

✔️project(member(model(dao))) → AjaxDAO.java

...
	/** 이메일로 회원 정보 조회
	 * @param email
	 * @return member
	 */
	public Member selectMember(String email) {
		return sqlSession.selectOne("ajaxMapper.selectMember", email);
	}

✔️resources(mappers) → ajax-mapper-xml

	...
<resultMap type="Member" id="member_rm">

  <!-- DB의 기본 키(복합키라면 여러 개 작성) -->		
  <id property="memberNo" column="MEMBER_NO"/>

  <!-- DB의 일반 컬럼들 -->
  <result property="memberEmail" column="MEMBER_EMAIL"/>
  <result property="memberPw" column="MEMBER_PW"/>
  <result property="memberNickname" column="MEMBER_NICKNAME"/>
  <result property="memberTel" column="MEMBER_TEL"/>
  <result property="memberAddress" column="MEMBER_ADDR"/>
  <result property="profileImage" column="PROFILE_IMG"/>
  <result property="enrollDate" column="ENROLL_DATE"/>
  <result property="memberDeleteFlag" column="MEMBER_DEL_FL"/>
  <result property="authority" column="AUTHORITY"/>

</resultMap>

<!-- resultMap은 언제 사용?
  조회 결과 컬럼명과 DTO의 필드명이 다를 때 사용
  -->
<!-- 이메일로 회원 정보 조회 -->
<select id="selectMember" resultMap="member_rm">
  SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICKNAME, MEMBER_TEL,
  NVL(MEMBER_ADDR, '미작성') MEMBER_ADDR,
  TO_CHAR(ENROLL_DATE, 'YYYY"년" MM"월" DD"일" HH24"시" MI"분" SS"초"') ENROLL_DATE
  FROM MEMBER
  WHERE MEMBER_EMAIL = #{email}
  AND MEMBER_DEL_FL = 'N'
</select>

이메일이 일부라도 일치하는 모든 회원 조회 기능

✔️webapp(WEB-INF(views(common))) → main.jsp

...
<h3>이메일이 일부라도 일치하는 모든 회원 조회</h3>
검색어 : <input type="text" id="input">
<button id="btn3">조회</button>

<table border="1" style="border-collapse: collapse;">
  <thead>
    <tr>
      <th>회원번호</th>
      <th>이메일</th>
      <th>닉네임</th>
    </tr>
  </thead>

  <tbody id="result3">
    <tr>
      <td>1</td>
      <td>user01@kh.or.kr</td>
      <td>유저일</td>
    </tr>
  </tbody>
</table>
...

✔️webapp(WEB-INF(views(common))) → main.js

...
// 이메일이 일부라도 일치하는 모든 회원 조회
const input = document.getElementById("input");
const btn3 = document.getElementById("btn3");
const result3 = document.getElementById("result3");

btn3.addEventListener("click", ()=>{

  fetch("/selectMemberList",{
    method : "POST",
    headers: {"Content-Type" : "application/text"}, // 문자열 하나를 파라미터로 전달
    body : input.value // 보내질 문자열 하나
  })

    .then(resp => resp.json())

    .then(memberList => {
    console.log(memberList);

    result3.innerHTML = "";

    if(memberList.length == 0){
      result3.innerHTML = "조회 결과가 없습니다";
      return;
    }

    // 향상된 for문으로 memberList 순차 접근
    for(let member of memberList){

      // tr, td 만들어서 result3에 추가
      const tr = document.createElement("tr");

      const td1 = document.createElement("td"); 
      td1.innerText = member.memberNo;

      const td2 = document.createElement("td");    
      td2.innerText = member.memberEmail;

      const td3 = document.createElement("td");    
      td3.innerText = member.memberNickname;

      // 1) tr의 자식으로 td1, td2, td3 추가
      tr.append(td1, td2, td3);

      // 2) result3의 자식으로 tr 추가
      result3.append(tr);
    }
  })

    .catch(err => {
    console.log(err)
  })
})

✔️project(member(controller)) → AjaxController.java

...
	// 이메일이 일부라도 일치하는 모든 회원 조회
	@PostMapping(value="/selectMemberList", produces="application/json; charset=UTF-8")
	@ResponseBody
	public List<Member> selectMemberList(@RequestBody String input) {

		return service.selectMemberList(input);
	}

List : 순서가 있는 데이터 집합 저장하는 데이터 구조
인덱스를 통해 접근할 수 있고 중복 값 허용한다


Map : key-value를 저장하는 데이터 구조
순서가 없고 값에 대한 직접 접근은 키를 통해 이루어진다

✔️project(member(model(service))) → AjaxService.java

...
	/** 이메일이 일부라도 일치하는 모든 회원 조회
	 * @param input
	 * @return memberList
	 */
	List<Member> selectMemberList(String input);

✔️project(member(model(service))) → AjaxServiceImpl.java

...
	// 이메일이 일부라도 일치하는 모든 회원 조회
	@Override
	public List<Member> selectMemberList(String input) {
		return dao.selectMemberList(input);
	}

✔️project(member(model(dao))) → AjaxDAO.java

	/** 이메일이 일부라도 일치하는 모든 회원 조회
	 * @param input
	 * @return memberList
	 */
	public List<Member> selectMemberList(String input) {
		return sqlSession.selectList("ajaxMapper.selectMemberList", input);
	}

✔️resources(mappers) → ajax-mapper-xml

...
<!-- 이메일이 일부라도 일치하는 모든 회원 조회 -->
<select id="selectMemberList" resultMap="member_rm">
  SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICKNAME
  FROM MEMBER
  WHERE MEMBER_DEL_FL = 'N'
  AND MEMBER_EMAIL LIKE '%${input}%'
  ORDER BY MEMBER_NO
</select>

💡 이때 ${input}을 사용하는 이유
#{}으로 작성하면 '%' 값 '%' 형태로 SQL이 정상적인 형태가 아니게 되기 때문에
'값'을 감싸고 있는 '' 제거하기 위해서 쓴 것이다.

profile
Hello! My Name is oYJo

0개의 댓글