본문 바로가기
코딩도전기/Spring Boot

String Boot - Restful Service

by 코도꼬마 2023. 5. 30.

Restful Service(Representational State Transfer)

  • 클라이언트와 서버 요소를 엄격하게 구분함
  • 하나의 서버로 다양한 플랫폼의 클라이언트 대응 가능
  • url 형태로 요청을 명료화할 수 있음
  • URI :  ex) /detail/java/15
  • 상황이나 요청 데이터에 따라 요청방식을 선택
  • Restful 서비스의 반환 값은 일반적으로 XML과 JSON을 활용(JSON이 가장 대중적)

 

json 형태의 문자열을 HashMap으로 변환(jackson-databind 필요)

  • pom.xml
    • jackson-databind 추가(버전 상관 없음 - boot에서 알아서 적용해줌)
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

 

  • index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
</head>
<body>
	<h3>@RestController : @ResponseBody를 사용하지 않아도 됨</h3>
	<ul>
		<li>/rest/list.ajax<button onclick="sendAjax('list.ajax')">list 호출</button></li>
		<li>/rest/map.ajax<button onclick="sendAjax('map.ajax')">map 호출</button></li>
		<li>/rest/object.ajax<button onclick="sendAjax('object.ajax')">object 호출</button></li>
	</ul>
	
	<h3>복잡한 JSON 전송</h3>
	<p>복잡한 json 형태의 전송은 기존과 다른 방식<button onclick="complex()">복잡한 JSON</button></p>
	
	<h3>서버에서 문자열을 json 형태로 변환할 수 있음</h3>
	<p>hashmap 형태 : <button onclick="sendAjax('strMap.ajax')">map</button></p>
	<p>UserInfo 형태 : <button onclick="sendAjax('strObject.ajax')">UserInfo</button></p>
	
</body>
<script>

	function complex(){
		var arr = [1,2,3,4,5];
		var obj = {};
		
		obj.name="json";
		obj.num = arr;
		
		var params = {"values":obj};
		console.log(params);
		
		$.ajax({
			url : 'rest/complex.ajax',
			type:'POST', // post 방식이어야 함
			data:JSON.stringify(params), // json을 문자열화 해야함
			dataType:'json',
			contentType:'application/json; charset=utf-8;', // contentType이 json이라고 명시해줘야함
			success:function(data){
				console.log(data);
			},error:function(e){
				console.log(e);
			}
			
		});
		
	}

	function sendAjax(uri){
		
		$.ajax({
			type:'get',
			url:'rest/'+uri,
			dataType:'json',
			success:function(data){
				console.log(data);				
			},
			error:function(e){
				console.log(e);
			}
		});
	}
</script>
</html>

 

  • ApiController
    • restful에서는 기본적으로 아래 method를 사용
    • GET : 특정 데이터 조회 요청할 때 사용
    • POST : 특정 데이터의 입력을 요청할 때 사용
    • DELETE : 특정 데이터의 삭제를 요청할 때 사용
    • PUT : 특정 데이터의 수정을 요청할 때 사용
    • PATCH : 특정 데이터의 수정(일부)를 요청할 때 사용
      • 사용방법
      • @xxxMapping(value="/")
      • @RequestMapping(value="/" method={RequestMethod.xxx}) : 여러가지 방식을 받을 경우
package kr.co.gudi.controller;

import java.util.ArrayList;
import java.util.HashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import kr.co.gudi.dto.UserInfo;

// restController는 앞에 /rest가 붙은 요청만 받기로 함(규칙)
@RestController
@RequestMapping(value = "/rest")
public class ApiController {
	
	Logger logger = LoggerFactory.getLogger(getClass());
	
	@RequestMapping(value = "/list.ajax")
	public ArrayList<String> list(){
		logger.info("list 형태로 반환");
		
		ArrayList<String> list = new ArrayList<String>();
		list.add("하나");
		list.add("둘");
		list.add("셋");
		
		return list;		
	}
	
	@GetMapping(value="/map.ajax")
	public HashMap<String, Object> map(){
		logger.info("map 형태로 반환");
		
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("msg", "hello");
		map.put("age", 33);
		map.put("married", false);
		
		return map;
	}
	
	@GetMapping(value = "/object.ajax")
	public UserInfo object() {
		logger.info("UserInfo 형태로 반환");
		
		UserInfo info = new UserInfo();
		info.setId("tester");
		info.setAge(55);
		info.setName("Lee");
		info.setPromotion(true);
		
		return info;
	}
	
	@PostMapping(value = "/complex.ajax")
	public HashMap<String, Object> complex(@RequestBody HashMap<String, Object> params){
		logger.info("params : {}",params);
		// params : {values={name=json, num=[1, 2, 3, 4, 5]}}
		
		HashMap<String, Object> vmap = (HashMap<String, Object>) params.get("values");
		logger.info("vmap : "+vmap);
		
		String name = (String) vmap.get("name");
		logger.info("name : "+name);
		
		ArrayList<Integer> list = (ArrayList<Integer>) vmap.get("num");
		logger.info("list : "+list);
		
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("success", true);
		
		return map;
	}
	
	@GetMapping(value = "strMap.ajax")
	public HashMap<String, Object> strMap() throws Exception{
		
		// json 형태의 문자열을 HashMap으로 변환(jackson-databind 필요)
		// json 형태로 입력할 때는 "" 사용
		String json = "{\"no\":1, \"msg\":\"HashMap 변환 완료\", \"name\":\"홍길동\"}";
		
		ObjectMapper mapper = new ObjectMapper(); 
		// 여기 있는 데이터를, 이 형태로 만들어줘 
		// 타입체크가 강력함(다만 사용하기 귀찮음) 
		// HashMap<String, Object> map = mapper.readValue(json, new TypeReference<HashMap<String, Object>>() {});
		
		// 사용 자체가 편리한 방법
		HashMap<String, Object> map = mapper.readValue(json, HashMap.class);
		return map;
	}
	
	@GetMapping(value = "/strObject.ajax")
	public UserInfo strObject() throws Exception {
		
		String json = "{\"id\":\"json ID\", \"name\":\"홍길동\", \"age\":44,\"promotion\":false}";
		
		ObjectMapper mapper = new ObjectMapper();
		
		UserInfo info = mapper.readValue(json, UserInfo.class);
		
		return info;
	}


}

 

  • UserInfo(dto)
package kr.co.gudi.dto;

public class UserInfo {
	
	private String id;
	private String name;
	private int age;
	private boolean promotion;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public boolean isPromotion() {
		return promotion;
	}
	public void setPromotion(boolean promotion) {
		this.promotion = promotion;
	}

}

 

 

 

실시간 데이터 변경/조회

  • Restful은 url 경로를 활용하여 데이터를 가져오는 방식이므로 url 경로 자체를 파라메터처럼 변수화 시킬 수 있음

 

  • index.html

 

 

  • TeamCotroller
    • 필드 주입 : @Autowired TeamService service; (사용지양됨)
      • 순환참조를 조기에 발견할 수 없음(순환참조 : 서로를 참조하는 것으로 작동되지 않을 수 있음)
      • Controller -> Service -> DAO 와 같이 일방향 구조는 순환참조가 발생하지 않아 사용 가능
      • 서비스끼리 서로 참조하는 경우 문제가 발생할 수 있음(MemberService와 MyPageService가 서로를 호출할 경우)
      • 서비스 실행 후 실제 문제 발생(stack over flow) 시 까지 알 수 없음
    • 생성자 주입
      • final을 쓸 수 있으므로 불변성 보장
      • 객체 간 순환참조 방지
      • 순환참조 발생 시 빌드 전에 미리 알 수 있음(컴파일 에러 발생 - 순수자바이기 때문에 컴파일 시 확인 가능)
private final TeamService service;
	
public TeamCotroller(TeamService service) { //생성자 주입
    this.service = service;
}

 

 

  • TeamService

 

 

  • TeamDAO

 

 

  • team_mapper