안녕하세요, 오랜만에 포스팅 하는 것 같습니다 :)
한달여 동안 일이있어 포스팅이 여러번 미루어졌네요 ㅜㅡㅜ
이번 포스팅은, 스프링 부트(Spring Boot)와 Ajax통신을 사용해서 나타내는 예제입니다.
이번 포스팅도 마찬가지로 소스코드는 Github에 올려놓았습니다!
1. Ajax??
기존의 웹 어플리케이션은, html의 폼의 내용을 작성해서 이를 웹 서버로 제출(submit) 하면, 웹 서버는 요청된 내용에 따라서 데이터를 가공하여 새로운 웹 페이지를 되돌려줍니다.
이 때에, html을 작성하여 제출한 내용과 웹 서버가 작성하여 결과를 돌려준 내용 중 다수가 중복이 되는 경우가 많습니다 (대부분...). 결과적으로 중복되는 코드를 전송 함으로써 자원의 낭비가 됩니다.
반면에 Ajax 애플리케이션은 필요한 데이터만을 웹서버에 요청해서 받은 후 클라이언트에서 데이터에 대한 처리를 할 수 있습니다. 웹서버에서 처리되던 데이터 처리의 일부분이 클라이언트 쪽에서 처리 되기 때문에 웹 브라우저와 웹 서버 사이에 교환되는 데이터량과 웹서버의 데이터 처리량이 줄어듭니다. 때문에 애플리케이션의 응답성이 좋아지게 됩니다.
아래의 그림은, 기존의 html/css를 주고받는 경우와 Ajax통신으로 데이터를 주고 받는 그림의 예입니다.
2. 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.결과화면
기본화면
검색 성공
검색 실패
'Old Branch' 카테고리의 다른 글
Django - tweetme 소셜서비스 구현해보기 (1) - Django Setup (0) | 2019.09.30 |
---|---|
스프링 부트(Spring Boot) + 시큐리티 (Spring Security) 기본인증 (0) | 2019.09.19 |
장고(Django) 예제 / 북마크 프로젝트 - 4. 템플릿 확장과 꾸미기 (0) | 2019.07.25 |
장고(Django) 예제 / 북마크 프로젝트 - 3 . CRUD추가 (0) | 2019.07.24 |
장고(Django) 예제 / 북마크 프로젝트 - 2. 장고 뷰, url 연동하기 (0) | 2019.07.23 |