비동기 처리
비동기 처리는 서버와 클라이언트 간의 통신을 비동기적으로 처리하여, 사용자가 요청을 보낸 후 페이지가 새로고침되지 않고도 결과를 받을 수 있는 기술이다. 여기서는 비동기 처리에서 반환되는 데이터 형태에 대해 알아볼 것이다.
1. String(Text) 반환
String 형식의 데이터는 주로 간단한 결과나 상태를 반환할 때 많이 사용된다. 보통 특정 작업의 성공 여부나 단순한 메시지를 전달할 때 적합하다. 비동기 통신으로 서버에 요청을 보내고, 응답으로 텍스트 값을 받아 처리할 수 있다. 대표적인 예시는 아래와 같다.
- 아이디 중복 검사: 회원가입 시 사용자가 입력한 아이디가 이미 존재하는지를 확인할 때, 서버에서 '사용 가능', '사용 불가'와 같은 텍스트를 반환해준다.
- 좋아요 기능: 사용자가 게시물에 '좋아요'를 누르면, 서버에 좋아요 상태를 저장하고 '좋아요 완료', '좋아요 취소'와 같은 상태를 문자열로 반환해 처리한다.
- on/off 상태 전환: 스위치 형태의 버튼으로 어떤 기능을 켜거나 끌 때, 'on' 또는 'off'라는 텍스트가 서버에서 반환되어 UI에서 상태를 업데이트한다.
- True/False, Yes/No: Boolean 값처럼 참/거짓을 판단할 수 있는 상황에서 't/f', 'y/n'과 같은 단순한 값을 반환하여 로직을 처리한다.
2. Object(Object) 반환
Object 형식의 데이터는 주로 복잡한 데이터 구조를 담고 있을 때 사용된다. 보통 리스트나 맵(Map), 데이터 전송 객체(DTO), 또는 JSON 형식으로 반환되어 클라이언트 측에서 다양한 데이터를 한 번에 처리할 수 있게 한다.
- 리스트(List): 서버에서 여러 개의 데이터를 한 번에 전달할 때 사용한다. 예를 들어, 특정 조건에 맞는 여러 개의 상품 목록이나 댓글 목록을 반환할 때 리스트 형태로 응답을 받을 수 있다.
- 맵(Map): 장바구니와 같은 기능에서 자주 사용된다. 특정 키(key)에 해당하는 값(value)을 저장해두고, 이를 통해 데이터 조회나 변경을 효율적으로 처리할 수 있다.
- DTO(데이터 전송 객체): 데이터베이스의 테이블과 대응되는 데이터 객체로, 특정 엔티티의 데이터를 클라이언트와 서버 간에 주고받을 때 사용된다. 예를 들어, 사용자 정보나 상품 정보를 담은 DTO를 비동기적으로 전달받을 수 있다.
- JSON ★★: 가장 흔하게 사용되는 비동기 처리 응답 형식 중 하나로, 서버에서 전달하는 데이터를 구조화하여 사용할 수 있다. JSON 형식은 자바스크립트에서 쉽게 객체로 변환하여 데이터에 접근하고 처리할 수 있어 많이 사용된다.
이처럼 비동기 처리에서는 단순한 텍스트 데이터부터 복잡한 객체 구조까지 다양한 형태의 데이터를 반환받아 클라이언트에서 원하는 대로 처리할 수 있다. 상황에 맞는 데이터 형식을 선택하여 효율적으로 서버와의 통신을 구성하는 것이 중요하다.
비동기 처리와 Spring 연동
이번에는 비동기 처리(Ajax)를 사용해서 아이디 중복 검사를 구현하는 방법을 정리하려고 한다. 원래는 서블릿(Servlet)을 이용해서 비동기 처리를 했는데, 이제 Spring 프레임워크를 사용하면 일반 Controller에서도 간편하게 비동기 처리를 할 수 있다.
우선, 그전에 회원가입 join.jsp 코드는 아래와 같다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입 페이지</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="js/checkID.js"></script>
<link rel="stylesheet" type="text/css" href="css/join.css">
</head>
<body>
<div class="container">
<form action="join.do" method="POST">
<h2>회원가입</h2>
<table>
<tr>
<td><label for="name">이름</label></td>
<td><input type="text" id="name" name="name" required></td>
</tr>
<tr>
<td><label for="mid">아이디</label></td>
<td>
<input type="text" id="mid" name="mid" required>
<!-- 비동기 처리 보여줄 result -->
<span id="result"></span>
</td>
</tr>
<tr>
<td><label for="password">비밀번호</label></td>
<td><input type="password" id="password" name="password" required></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="회원가입">
</td>
</tr>
</table>
</form>
</div>
<div style="text-align: center; margin-top: 20px;">
<a href="login.do">로그인 페이지로 이동</a>
</div>
</body>
</html>

JavaScript에서 아이디 값을 가져오고 Ajax 요청하기 ( Spring Text 반환)
우선, js 폴더에 checkID.js 파일을 생성하고, 아래와 같이 작성한다. 이 코드는 사용자가 아이디 입력란에 값을 입력한 후, 아이디가 변경될 때마다 해당 값을 가져와서 로그로 확인하는 코드다.
$(document).ready(function(){
$('#mid').on('change', function(){
var mid = $(this).val(); // 아이디 입력란의 값을 가져오기
console.log('['+mid+']'); // 로그
});
});
이 코드를 실행하면 아이디 값이 변경될 때마다 콘솔에 아이디가 출력된다.
이제 이 값이 실제로 존재하는지를 검사하기 위해 Ajax 요청을 추가한다.
아래는 아이디 값이 있을 때만 서버에 Ajax 요청을 보내고, 값이 없을 경우 결과를 지워주는 코드다.
$(document).ready(function(){
$('#mid').on('change', function(){
var mid = $(this).val();
console.log('['+mid+']');
if(mid){ // 값이 있을 때만 Ajax 요청을 보낸다
$.ajax({
url : 'checkMID.do', // 서버로 요청을 보낼 URL
type : 'POST', // 요청 방식
data : { mid : mid }, // 서버로 보낼 데이터 (아이디)
success : function(data){
console.log('['+data+']'); // 서버에서 받은 응답을 콘솔에 출력
if(data == 'true'){ // 아이디가 존재하면
$('#result').text('DB에 존재하는 아이디입니다.').css('color','red');
} else { // 아이디가 없으면
$('#result').text('DB에 없는 아이디입니다.').css('color','blue');
}
}
});
} else {
$('#result').text(''); // 값이 없을 때 결과를 공백으로 표시
}
});
});
여기서 url : 'checkMID.do' 부분은 Spring에서 처리할 경로를 나타낸다.
이제 이 Ajax 요청을 처리할 Spring Controller를 작성한다.
Spring Controller 작성하기
Spring에서는 비동기 요청을 처리하는 것도 매우 간단하다.
먼저 Controller에서 checkMID.do 요청을 처리하는 메서드를 만든다.
이 메서드는 Ajax 요청으로 전달된 아이디를 검사한 후, 결과를 문자열로 반환한다.
@Controller
public class CheckController {
@Autowired
private MemberService memberService; // MemberService 주입
@RequestMapping(value="/checkMID.do", method=RequestMethod.POST)
public String check(MemberDTO memberDTO) {
System.out.println("비동기 처리 로그"); // 요청이 잘 들어왔는지 로그 출력
memberDTO = memberService.selectOne(memberDTO); // 아이디 검사
String result = "false"; // 기본값은 false로 설정
if(memberDTO != null) { // 아이디가 존재하면
result = "true";
}
return result; // 결과 반환
}
}
이 코드를 보면, checkMID.do 요청이 들어오면 MemberService를 통해 해당 아이디가 존재하는지 확인하고, 결과에 따라 true 또는 false를 반환한다.
Ajax 요청과 Controller 연동
이제 JavaScript 코드와 Spring Controller가 연동되었다.
사용자가 아이디를 입력하고 변경할 때마다 Ajax 요청이 발생하고, Spring Controller는 해당 요청을 처리해서 결과를 반환한다.
위 코드처럼 작성을 하면, DB에 존재하는 경우 (== 이미 가입한 아이디라면)

존재한다는 문구가 보이고, 반대의 경우( == 사용 가능한 아이디라면)

위와 같이 출력되는 것을 볼 수 있다.

로그도 콘솔창에 잘 찍히는 것을 볼 수 있다.
로그 이해 +
MemberDAO.java
package com.yn.app.biz.member;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.yn.app.biz.common.JDBCUtil;
@Repository
public class MemberDAO {
private final String SELECTONE_IDCHECK = "SELECT MID FROM MEMBER WHERE MID=? ";
private final String SELECTONE_LOGIN = "SELECT MID, PASSWORD FROM MEMBER WHERE MID=? AND PASSWORD=?";
private final String INSERT = "INSERT INTO MEMBER(NAME,MID, PASSWORD) VALUES (?,?,?)";
public List<MemberDTO> selectAll(MemberDTO memberDTO) {
return null;
}
public MemberDTO selectOne(MemberDTO memberDTO) {
System.out.println("MemberDTO selectOne 시작");
MemberDTO data=null;
Connection conn=JDBCUtil.connect();
PreparedStatement pstmt;
try {
//아이디 중복검사
if(memberDTO.getCondition().equals("SELECTONE_IDCHECK")) {
pstmt = conn.prepareStatement(SELECTONE_IDCHECK);
pstmt.setString(1, memberDTO.getMid());
System.out.println("SELECTONE_IDCHECK pstmt 준비 완료");
ResultSet rs=pstmt.executeQuery();
if(rs.next()) {
data=new MemberDTO();
data.setMid(rs.getString("MID"));
}
}
// 로그인
else if(memberDTO.getCondition().equals("SELECTONE_LOGIN")) {
pstmt = conn.prepareStatement(SELECTONE_LOGIN);
pstmt.setString(1, memberDTO.getMid());
pstmt.setString(2, memberDTO.getPassword());
System.out.println("SELECTONE_LOGIN pstmt 준비 완료");
ResultSet rs=pstmt.executeQuery();
if(rs.next()) {
data=new MemberDTO();
data.setMid(rs.getString("MID"));
data.setPassword(rs.getString("PASSWORD"));
}
}
else if(memberDTO.getCondition() == null) {
System.out.println("condition null");
return null;
}
else {
System.out.println("condition 틀림");
return null;
}
} catch (SQLException e) {
System.err.println("MemberDAO selectOne sql문 실패");
e.printStackTrace();
return null;
}
System.out.println("memberDAO.selectOne 맞는 데이터 존재함");
return data;
}
public boolean insert(MemberDTO memberDTO) {
System.out.println("MemberDAO insert 시작");
Connection conn=JDBCUtil.connect();
PreparedStatement pstmt = null;
try {
// 회원가입
if(memberDTO.getCondition().equals("INSERT")) {
pstmt = conn.prepareStatement(INSERT);
pstmt.setString(1, memberDTO.getName());
pstmt.setString(2, memberDTO.getMid());
pstmt.setString(3, memberDTO.getPassword());
System.out.println("INSERT pstmt 준비 완료");
}
else if(memberDTO.getCondition() == null) {
System.out.println("condition null");
return false;
}
else {
System.out.println("condition 틀림");
return false;
}
int rs = pstmt.executeUpdate();
if(rs<=0) {
System.err.println("MemberDAO insert 실패");
return false;
}
} catch (SQLException e) {
System.out.println("MemberDAO insert sql문 실패");
e.printStackTrace();
return false;
} finally {
JDBCUtil.disconnect(pstmt, conn);
}
System.out.println("MemberDAO insert문 성공");
return true;
}
public boolean update(MemberDTO memberDTO) {
return false;
}
public boolean delete(MemberDTO memberDTO) {
return false;
}
}
Spring의 비동기 처리 방식
Spring에서는 비동기 응답을 처리할 때, 두 가지 방법을 사용할 수 있다.
- @ResponseBody 사용: 문자열이나 JSON 데이터를 반환할 때 사용한다. 이 방법을 사용하면 ViewResolver를 거치지 않고 바로 데이터를 응답한다.
- @RestController 사용: 스프링 부트(Spring Boot)에서 많이 사용되는 방식으로, @Controller와 @ResponseBody를 결합한 어노테이션이다. 데이터를 직접 반환할 때 유용하다.
두 방식 모두 간단하게 비동기 처리에 활용할 수 있다.
JSON 응답처리 - Object 반환
비동기 처리에서 Object 타입의 데이터를 반환하고 싶다면, JSON 형식을 사용하는 것이 가장 안전하다.
앞서 말했듯이, Spring에서는 @RequestBody와 @ResponseBody를 사용해서 JSON 데이터를 주고받을 수 있다.
아래는 js 코드이다.
$(document).ready(function(){
$('#mid').on('change', function(){
var mid = $(this).val();
console.log('['+mid+']');
if(mid){
$.ajax({
url : 'check.do',
type : 'POST',
contentType : 'application/json', // 요청 데이터 형식 지정
data : JSON.stringify({ mid : mid }), // JSON 형식으로 데이터 전송
dataType : 'json', // 응답 데이터 형식을 JSON으로 지정
success : function(data){
console.log('['+data+']');
if(data == 'true'){ // 아이디가 존재하면
$('#result').text('DB에 존재하는 아이디입니다.').css('color','red');
} else { // 아이디가 없으면
$('#result').text('DB에 없는 아이디입니다.').css('color','blue');
}
}
});
} else {
$('#result').text('');
}
});
});
위와 같이 JSON 형식으로 데이터를 주고받을 때는, 서버에서도 @RequestBody 어노테이션을 사용해서 요청 데이터를 처리할 수 있다.
@RestController
public class CheckController {
@Autowired
private MemberService memberService;
//데이터 여러개 받기
@RequestMapping(value="/check.do", method=RequestMethod.POST)
public @ResponseBody List<String> check(@RequestBody MemberDTO memberDTO) {
//@RequestBody 어노테이션을 붙임으로써,
//주고받는 data 형식이 json임을 알 수 있다.
System.out.println("비동기 처리 로그"); // 잘 도착 했는지 로그
memberDTO = memberService.selectOne(memberDTO);
List<String> result = new ArrayList<String>(); // 변수 선언했다고 그닥 무겁지도 않고, 메서드 내에서 선언 >> 메서드 종료시 사라짐. 메모리 ㄱㅊ
result.add("apple");
result.add("banana");
return result;
}
}
위 코드는 임의로 작성한 값이라, 보여지는건 없지만, 추가 실습을 통해 게시판 필터 검색을 구현할 예정이다.
'Spring' 카테고리의 다른 글
[Spring] AOP와 어노테이션 (0) | 2024.10.16 |
---|---|
[Spring] AOP 관점지향 프로그래밍 (1) | 2024.10.15 |
[Spring] Tomcat Server 기동 시 Listener와 Spring IoC Container의 초기화 과정 (0) | 2024.10.10 |
[Spring] 2-Layered 아키텍처 정리 (0) | 2024.10.10 |
[Spring] 스테레오타입 어노테이션과 @RequestMapping (0) | 2024.10.09 |