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

Spring Boot - REST API

by 코도꼬마 2023. 5. 31.

Rest API(application Programming Interface)

  • REST API : 웹 상에서 URL을 통해 원하는 정보를 얻어오거나 특정한 요청을 하는 것(사용 설명서도 포함)
  • Rest API는 Client와 Server 사이에 일어나지만 Server와 Server 사이에도 일어남

 

 

WebClient

  •  Client의 요청을 Server에서 받아 다른 Server에 요청 후 응답 값을 client에 다시 전달

 

  • pom.xml
    • Maven Repository에서 webflux 복붙
    • project 생성 시 library추가해도 됨
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-webflux -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</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>INDEX PAGE</h3>
	<input type="text" id="msg"/>
	<button onclick="getSend()">GET SEND</button>
	<br/><br/>
	<input type="number" id="cnt"/>
	<button onclick="postSend()">POST SEND</button>
	<p><button onclick="fluxTest()">FLUX TEST</button></p>
</body>
<script>

	function getSend(){
		
		$.ajax({
			url:'/get/send/'+$('#msg').val(),
			type:'get',
			dataType:'json',
			success:function(data){
				console.log(data);
			},
			error:function(e){
				console.log(e);
			}
		});
		
	}
	
	// 숫자 전송 -> 숫자만큼 리스트를 가져옴
	// 헤더에 값을 넣음(보안이 필요한 값은 헤더에 많이 넣음)
	function postSend(){
		
		$.ajax({
			url:'/post/send/'+$('#cnt').val(),
			type:'post',
			dataType:'json',
			beforeSend:function(header){
				console.log(header);
				header.setRequestHeader("Authorization","ASD123");
			},
			success:function(data){
				console.log(data);
			},
			error:function(e){
				console.log(e);
			}
		});
			
	}
		
	function fluxTest(){
		
		$.ajax({
			url:'/get/fluxTest',
			type:'get',
			dataType:'json',
			success:function(data){
				console.log(data);
			},
			error:function(e){
				console.log(e);
			}
		});
		
	}
	
</script>
</html>

 

  • SendController
package kr.co.gudi.controller;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

import kr.co.gudi.service.ApiService;

@RestController
public class SendController {
	
	Logger logger = LoggerFactory.getLogger(getClass());

	private final ApiService service;
	
	public SendController(ApiService service) {
		this.service = service;
	}
	
	@GetMapping(value = "/get/send/{msg}")
	public HashMap<String, String> getSend(@PathVariable String msg){
		logger.info("getSend : "+msg);
		
		return service.getSend(msg);
	}
	
	@PostMapping(value = "/post/send/{cnt}")
	public ArrayList<HashMap<String, Object>> postSend(
			@PathVariable String cnt, @RequestHeader HashMap<String, String> header){
		logger.info("header : "+header);
		return service.postSend(cnt,header.get("authorization"));
	}
	
	@GetMapping(value = "/get/fluxTest")
	public List<HashMap<String, Object>> fluxTest(){
		return service.fluxTest();
	}
	
}

 

  • ApiService

@Service
public class ApiService {

	Logger logger = LoggerFactory.getLogger(getClass());
	
	/* WebClient는 Spring 5.0부터 지원
	 * Httpconnection -> RestTemplate -> WebClient(webflux)
	 * WebClient : non-blocking 방식을 지원하며 속도가 빠름
	 */

	public HashMap<String, String> getSend(String msg) {
		// 1. 전송 URL 설정(특정한 주소로 요청을 보내겠다)
        // 지금은 하나의 서버로 했지만 다른 서버로 보낼 수도 있음
		WebClient client = WebClient.create("http://localhost");
		
		// 2. 전송방식 선택 // 3. 추가할 URL 설정
		Mono<HashMap> mono = client.get().uri("/return/"+msg)
	
		// retrieve() : body 값만 가져옴
		// exchange() : body+header+status 등도 가져옴(사용지양-너무 많은 값을 가져옴(뭉탱이로))
		// 4. 전송(어떻게 보낼것인지, 무엇을 받을 것인지에 따라 다름)
        .retrieve() 
		// 5. 받아올 방식 설정(형태, 처리방식에 따라 다름)
        .bodyToMono(HashMap.class); 
		// Mono, Flux : 데이터를 받아올 때 데이터를 알맹이 같이 감싼 상태로 뭉탱이로 떨어짐 
		//데이터를 감싸서 가져오는 껍데기 같은 느낌(future 객체와 비슷)
		//혼자서는 아무것도 할 수 없기 때문에 아래와 같이 꺼내서 사용
		
		// bodyToMono : 데이터가 0~1개 처리될 경우(동기식)
		// bodyToFlux : 데이터가 한번에 여러개 처리될 경우(비동기식)
		HashMap<String, String> resp = mono.block();
		logger.info("resp : "+resp);
		
		return resp;
	}

	public ArrayList<HashMap<String, Object>> postSend(String cnt, String key) {
		
		// get에서는 url에 파라메터를 보냈지만
		// post에서는 바디에 보내야 함
		
		WebClient client = WebClient.create("http://localhost");
		
		// post에서는 파라메터를 uri에 붙일 수 없어서 바디에 넣어서 보내야하기 때문에 다음과 같이 파라메터를 넣음
		FormInserter<String> form = BodyInserters.fromFormData("cnt",cnt);
		form.with("name","lee"); // 추가로 파라메터를 넣고 싶을 경우
		
		Mono<ArrayList> mono = client.post().uri("/listReturn")
		.header("headerName", key)
		.body(form)
		.retrieve().bodyToMono(ArrayList.class);
		
		// block()은 사용이 편리하지만 사용을 권고하지 않음(동기방식이라 효율성이 떨어짐)
		// ex) 선착순으로 하나씩 뽑으면 일시적으로 몰리는 현상이 발생함
		//ArrayList<HashMap<String, Object>> resp = mono.block();
		
		// 비동기로 받아놓고 -> 줄을 세워서 -> 하나씩 가져옴
		ArrayList<HashMap<String, Object>> resp = mono.flux().toStream().findFirst().get();
		
		logger.info("resp : "+resp);
		
		return resp;
	}

	public List<HashMap<String, Object>> fluxTest() {
		
		WebClient client = WebClient.create("http://localhost");
		
		// json 형태로 파라메터 보내기
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("age", 22);
		params.put("name", "lee");
		params.put("married", false);
		params.put("scores", new int[] {30,40,50,60,70,80,90,100});
		
		// json 형태로 데이터를 보낼때 bodyValue를 사용하면 됨
		// 받을때는 @RequestBody로 받아야함
		List<HashMap<String, Object>> list = client.post().uri("fluxReturn")
				.bodyValue(params).retrieve()
				.bodyToFlux(HashMap.class).toStream()
				.collect(Collectors.toList()); //list 형태로 하나씩 수집해서 가져옴(list 형태로만 반환)
		
		return list;
	}
	
	
}

 

  • ReceiveController
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.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

@RestController
@CrossOrigin(value = "*") // 외부에서 어떤 요청이 올지 모르기 때문에 설정
public class ReceiveController {
	
	Logger logger = LoggerFactory.getLogger(getClass());
	
	//요청온 메세지를 그대로 돌려줌
	@GetMapping(value = "/return/{msg}")
	public HashMap<String, String> getReturn(@PathVariable String msg){
		logger.info("다른 서버로 부터 받은 메시지 : "+msg);
		
		HashMap<String, String> result = new HashMap<String, String>();
		result.put("your_msg", msg);
		return result;
	}
	
	@PostMapping(value = "/listReturn")
	public ArrayList<HashMap<String, Object>> postReturn(
			int cnt, @RequestHeader HashMap<String, String> header){
		
		logger.info("receive : "+cnt);
		logger.info("receive key : "+header.get("authorization"));
		
		ArrayList<HashMap<String, Object>> list = new ArrayList<HashMap<String,Object>>();
		HashMap<String, Object> map = null;
		
		for (int i = 0; i < cnt; i++) {
			map = new HashMap<String, Object>();
			map.put("no", i);
			map.put("name", "lee");
			map.put("salary", i*10000000);
			list.add(map);
		}
		
		return list;
	}
	
	@PostMapping(value = "/fluxReturn")
	public ArrayList<HashMap<String, Object>> fluxReturn(@RequestBody HashMap<String, Object> params){
		
		logger.info("params : "+params);
		
		ArrayList<HashMap<String, Object>> list = new ArrayList<HashMap<String,Object>>();
		HashMap<String, Object> map = null;
		
		for (int i = 0; i < 10; i++) {
			map = new HashMap<String, Object>();
			map.put("no", i);
			map.put("name", "lee");
			map.put("salary", i*10000000);
			list.add(map);
		}
		
		return list;
	}

}

 

 

 

ApiServer

 

  • index.html
<html>
    <head>
        <meta charset="UTF-8">
        <title>J-QUERY</title>
        <link rel="icon" href="../img/chrome.png">
        <script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
        <style>
            ul{
                list-style-type: none;
            }
            li{
                height: 30;
                width: 300;
                border: 1px solid lightgray;
                padding: 10;
                font-size: 13;
                margin-top: 5;
                cursor: pointer;
            }
            a{
                text-decoration: none;
                color: black;
            }
            a:visited{
                text-decoration: none;
                color: gray;
            }
            .centerName{
                font-weight: 600;
            }
            .address{
                color: gray;
            }
        </style>
    </head>
    <body>
       <ul id="list"></ul>
    </body>
    <script>

        //var url = 'https://api.odcloud.kr/api/15077586/v1/centers';
        //var key = '3CtbWp0yQsbTWajEO8gjZvITGmrmVWB0yh3fjbjhSKoFhCIYZ7tiI1ueDjJvAIX85vWdWiznj7ejkuFbEcKmKw==';
        
        $.ajax({
            url:'apiCall.ajax',
            type:'get',
            data:{
                page:1,
                perPage:10
            },
            dataType:'json',
            success:function(obj){
                console.log(obj);
                var content = '';
                obj.data.forEach(item => {
                    content += '<li>';
                    content += '<a href="https://www.google.com/maps/place/'+item.address+'">';
                    content += '<div class="centerName">'+item.centerName+'</div>';
                    content += '<div class="address">'+item.address+'</div>';
                    content += '</a>';
                    content += '</li>';
                });
                $('#list').html(content);
            },
            error:function(e){
                console.log(e);
            }
        });

    </script>
</html>

 

  • ApiController
package kr.co.gudi.controller;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import kr.co.gudi.service.ApiService;

@RestController
public class ApiController {
	
	@Autowired ApiService service;

	Logger logger = LoggerFactory.getLogger(getClass());
	
	@GetMapping(value = "/apiCall.ajax")
	public Map<String, Object> apiList(String page, String perPage){
		logger.info(page+"/"+perPage);		
		return service.getList(page,perPage);
	}
	
}

 

  • ApiService
package kr.co.gudi.service;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;

import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder.EncoderMode;
import reactor.core.publisher.Mono;

@Service
public class ApiService {

	Logger logger = LoggerFactory.getLogger(getClass());

	public Map<String, Object> getList(String page, String perPage) {
		
		String url = "https://api.odcloud.kr/api/15077586/v1/centers";
		// encoding key 사용
		String key = "3CtbWp0yQsbTWajEO8gjZvITGmrmVWB0yh3fjbjhSKoFhCIYZ7tiI1ueDjJvAIX85vWdWiznj7ejkuFbEcKmKw%3D%3D";
		//WebClient에서는 특수문자가 있는 경우 따로 인코딩 과정을 거쳐야함
		DefaultUriBuilderFactory fac = new DefaultUriBuilderFactory(url);
		fac.setEncodingMode(EncodingMode.VALUES_ONLY);
		
		WebClient client = WebClient.builder().uriBuilderFactory(fac).baseUrl(url).build();
		Mono<HashMap> mono = client.get().uri("?serviceKey="+key+"&page="+page+"&perPage="+perPage).retrieve().bodyToMono(HashMap.class);
		Map<String, Object> resp = mono.flux().toStream().findFirst().get();
		logger.info("resp : "+resp);
		return resp;
	}
	
}