Old Branch

스프링 부트(Spring Boot) Ajax통신 Example

woolbro 2019. 9. 17. 11:19
반응형

안녕하세요, 오랜만에 포스팅 하는 것 같습니다 :)

 

한달여 동안 일이있어 포스팅이 여러번 미루어졌네요 ㅜㅡㅜ

 

이번 포스팅은, 스프링 부트(Spring Boot)와 Ajax통신을 사용해서 나타내는 예제입니다.

 

이번 포스팅도 마찬가지로 소스코드는 Github에 올려놓았습니다!

 

소스코드 --> 링크

 


1. Ajax??

  기존의 웹 어플리케이션은, html의 폼의 내용을 작성해서 이를 웹 서버로 제출(submit) 하면, 웹 서버는 요청된 내용에 따라서 데이터를 가공하여 새로운 웹 페이지를  되돌려줍니다.

  이 때에, html을 작성하여 제출한 내용과 웹 서버가 작성하여 결과를 돌려준 내용 중 다수가 중복이 되는 경우가 많습니다 (대부분...). 결과적으로 중복되는 코드를 전송 함으로써 자원의 낭비가 됩니다. 

 

  반면에 Ajax 애플리케이션은 필요한 데이터만을 웹서버에 요청해서 받은 후 클라이언트에서 데이터에 대한 처리를 할 수 있습니다.  웹서버에서 처리되던 데이터 처리의 일부분이 클라이언트 쪽에서 처리 되기 때문에 웹 브라우저와 웹 서버 사이에 교환되는 데이터량과 웹서버의 데이터 처리량이 줄어듭니다. 때문에 애플리케이션의 응답성이 좋아지게 됩니다. 

 

아래의 그림은, 기존의 html/css를 주고받는 경우와 Ajax통신으로 데이터를 주고 받는 그림의 예입니다. 

 

By DanielSHaischt, via Wikimedia Commons - https://commons.wikimedia.org/wiki/File%3AAjax-vergleich.svg, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=29724785

 

2. Project Structure - 프로젝트 구조

이번 포스팅의 프로젝트 구조는 아래와 같습니다.

project Structure

3. Project Dependency 설정 ( pom.xml 수정)

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.8.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.woolbro</groupId>
	<artifactId>woolbro_springAjax</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>woolbro_springAjax</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web-services</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>jquery</artifactId>
			<version>2.2.4</version>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>bootstrap</artifactId>
			<version>3.3.7</version>
		</dependency>
		
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

 

4. 이외의 소스코드

4.1 Spring Controller (REST API) - SearchController.java

package com.woolbro.controller;

import java.util.List;
import java.util.stream.Collectors;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.woolbro.model.AjaxResponseBody;
import com.woolbro.model.SearchCriteria;
import com.woolbro.model.User;
import com.woolbro.services.UserService;

@RestController
public class SearchController {

	UserService userService;

	@Autowired
	public void setUserService(UserService userService) {
		this.userService = userService;
	}
	
	@PostMapping("/api/search")
	public ResponseEntity<?> getSearchResultViaAjax(@Valid @RequestBody SearchCriteria search,Errors errors){
		AjaxResponseBody result = new AjaxResponseBody();
		
		if(errors.hasErrors()) {
			result.setMsg(errors.getAllErrors().stream().map(x->x.getDefaultMessage()).collect(Collectors.joining(",")));
			return ResponseEntity.badRequest().body(result);
		}
		List<User> users = userService.findByUserNameOrEmail(search.getUsername());
		if(users.isEmpty()) {
			result.setMsg("사용자가 존재하지 않습니다!");
		}else {
			result.setMsg("성공입니다!");
		}
		result.setResult(users);
		
		return ResponseEntity.ok(result);
	}

}

 

4.2. IndexController.java

package com.woolbro.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {
	
	private final Logger logger = LoggerFactory.getLogger(IndexController.class);
	
	@GetMapping("/")
	public String index() {
		logger.info("IndexController, GetMapping('/')");
		return "ajax";
	}
}

 

4.3. AjaxResponseBody.java

package com.woolbro.model;

import java.util.List;

public class AjaxResponseBody {

	String msg;
	List<User> result;

	//getter, setter 
	
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	public List<User> getResult() {
		return result;
	}
	public void setResult(List<User> result) {
		this.result = result;
	}
	
	
}

 

4.4. SearchCriteria.java

package com.woolbro.model;

import javax.validation.constraints.NotBlank;

public class SearchCriteria {
	
	@NotBlank(message = "이름은 빈공간으로 둘 수 없습니닷!")
	String username;
	
	public String getUsername() {
		return username;
	}
	
	public void setUsername(String username) {
		this.username = username;
	}
}

 

4.5. User.java

package com.woolbro.model;

public class User {
	
	String username;
	String password;
	String email;
	
	public User(String username, String password, String email) {
		this.username = username;
		this.password = password;
		this.email = email;
	}

	//getter setter
	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}
	
	
	
}

 

4.6. UserService.java

작성한 User에 woolbro, yunani,bjkang 을 넣어보았습니다.

User의 존재여부에 따라서 Ajax응답이 달라지겠죠~?

package com.woolbro.services;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;

import org.springframework.stereotype.Service;

import com.woolbro.model.User;

@Service
public class UserService {
	
	private List<User> users;
	
	public List<User> findByUserNameOrEmail(String username){
		List<User> result = users.stream().filter(x -> x.getUsername().equalsIgnoreCase(username)).collect(Collectors.toList());
		return result;
	}
	
	@PostConstruct
	private void iniDataForTesting() {
		users = new ArrayList<User>();
		
		User user1 = new User("woolbro","qwer111","woolbro@tistory.com");
		User user2 = new User("yunani","qwer222","yunani@tistory.com");
		User user3 = new User("bjkang","qwer333","bjkang@tistory.com");
		
		users.add(user1);
		users.add(user2);
		users.add(user3);
		
	}
}

 

4.7. ajax.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
<title>Spring Boot Ajax 예</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<link rel="stylesheet" type="text/css"
	href="webjars/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>

	<nav class="navbar navbar-inverse">
		<div class="container">
			<div class="navbar-header">
				<a class="navbar-brand" href="https://woolbro.tistory.com">Woolbro.com</a>
			</div>
		</div>
	</nav>

	<div class="container" style="min-height: 500px">

		<div class="starter-template">
			<h1>Spring Boot AJAX 예</h1>

			<div id="feedback"></div>

			<form class="form-horizontal" id="search-form">
				<div class="form-group form-group-lg">
					<label class="col-sm-2 control-label">Username</label>
					<div class="col-sm-10">
						<input type="text" class="form-control" id="username" />
					</div>
				</div>

				<div class="form-group">
					<div class="col-sm-offset-2 col-sm-10">
						<button type="submit" id="bth-search"
							class="btn btn-primary btn-lg">Search</button>
					</div>
				</div>
			</form>

		</div>

	</div>

	<div class="container">
		<footer>
			<p>
				© <a href="https://woolbro.tistory.com">Woolbro.com</a> 2019
			</p>
		</footer>
	</div>

	<script type="text/javascript" src="webjars/jquery/2.2.4/jquery.min.js"></script>

	<script type="text/javascript">
$(document).ready(function () {

    $("#search-form").submit(function (event) {

        event.preventDefault();

        fire_ajax_submit();

    });

});

function fire_ajax_submit() {

    var search = {}
    search["username"] = $("#username").val();
    //search["email"] = $("#email").val();

    $("#btn-search").prop("disabled", true);

    $.ajax({
        type: "POST",
        contentType: "application/json",
        url: "/api/search",
        data: JSON.stringify(search),
        dataType: 'json',
        cache: false,
        timeout: 600000,
        success: function (data) { 

            var json = "<h4>Ajax Response</h4><pre>"
                + JSON.stringify(data, null, 4) + "</pre>";
            $('#feedback').html(json);

            console.log("SUCCESS : ", data);
            $("#btn-search").prop("disabled", false);

        },
        error: function (e) {

            var json = "<h4>Ajax Response</h4><pre>"
                + e.responseText + "</pre>";
            $('#feedback').html(json);

            console.log("ERROR : ", e);
            $("#btn-search").prop("disabled", false);

        }
    });

}
</script>

</body>
</html>

 

4.8. logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>
                %d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n
            </Pattern>
        </layout>
    </appender>

    <logger name="org.springframework.web" level="error" additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>

    <logger name="com.woolbro" level="debug" additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>

    <root level="error">
        <appender-ref ref="STDOUT"/>
    </root>

</configuration>

 

4.9. Application.properties 

application.properties 파일을 사용해서, UTF-8 인코딩을 해 주었습니다.

#UTF - 8 ENCODING
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true

 

5.결과화면

기본화면

 

검색 성공

 

검색 실패