[Spring] 비동기통신

JH·2023년 5월 9일

Spring

목록 보기
6/9

1. TIL

HttpHeader, body
https://developer.mozilla.org/ko/docs/Web/HTTP/Messages

HttpStatus, Status 참조용
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status

produces : 클라이언트에게 반환하는 HTTP Header의 형식을 지정하는 파라미터 (클라이언트에 데이터를 전달할 때, 서버의 입장에서 데이터를 생산할 때)

consumes : 클라이언트에서 들어오는 HTTP Header의 형식을 지정하는 파라미터 (클라이언트로부터 데이터를 얻을 때, 서버의 입장에서 데이터를 소비할 때)

@ResponseBody : 자바 객체를 json 기반의 HTTP Body로 변환

@RequestBody : json 기반의 HTTP Body를 자바 객체로 변환

@CrossOrigin : CORS 요청 처리 방식 명시

@ExceptionHandler : 하나의 컨트롤러에 대해 예외 처리

  • 예외 처리시 Exception 객체를 servlet 컨테이너, web 컨테이너와 공유할 수 없기 때문에 되도록 spring 컨테이너에서 예외를 처리

A. 비동기처리

1. 비동기

동기 (Synchronous) : 요청 결과 응답 후, 다른 동작이 실행되는 방식

비동기 (Asynchronous) : 요청 결과 응답과는 별개로 다음 동작이 실행되는 방식

@RestController : 요청에 대한 응답을 지정한 content type으로 리턴하는 컨트롤러 (화면에 대한 전환이 없음)

  • RestController 를 사용하면 @ResponseBody 생략 가능

RestController의 기본 리턴 타입 : ResponseEntity<Resource>


2. callback, promise

콜백 함수 : 다른 함수의 파라미터로 넘겨주는 함수

  • 콜백함수가 실행되서 내부의 함수를 실행시켜주므로 실행 순서가 보장됨
function func() {
            console.log(1); 
            setTimeout(() => console.log(2), 0);
            console.log(3);
			// 1 3 2 순으로 출력
}

3. Promise

콜백 지옥을 해결하기 위해 Promise를 사용

  • 상태 : pending -> resolve() - fulfilled(정상적인 기능 수행 완료)
    • reject() - rejected (실패)
  • 생산자(Producer) - 소비자(Consumer) : then

// 생산자
const promise1 = new Promise((resolve, reject) => {
	setTimeout(() => {resolve('data1');}, 2000);
});
// 소비자
promise1.then(v => console.log(v));


B. XML

XMLHttpRequest : 웹 브라우저와 서버 간에 메소드가 데이터를 전송하는 객체 형태의 API

JSON (JavaScript Object Notation) : 데이터를 문자열의 형태로 주고 받도록 만들어진 내장객체

  • stringify : js객체 -> json 문자열
  • parse : json 문자열 -> js객체
function ajaxTest() {
  // step01 : 비동기 요청 객체 생성
  var xhr = new XMLHttpRequest();

  // step03 : 데이터 응답
  xhr.onreadystatechange = function() {
    if(this.readyState == 4 && this.status == 200) {
      document.write(this.responseText);}
    }
  // step02 : 서버로 데이터를 비동기로 요청
  xhr.open("GET", "https://jsonplaceholder.typicode.com/users");
  xhr.send();
}   
ajaxTest();

2. fetch

fetch : 비동기 통신을 제공하는 JS 내장 함수
기본 요청은 GET, 이외 POST, PUT, DELETE가 있음

fetch('url')
.then(response => response.json())
.then(data => console.log(data));

3. axios

axios : 비동기 통신을 위한 프로미스 기반의 라이브러리
요청 방식은 fetch와 같음, 기본적으로 json 객체 반환

axios.get(url)
.then(response => response.data)
.then(users => console.log(users[7]))



C. 비동기 통신 Ex)

1. Controller

@RestController : 요청에 대한 응답을 지정한 컨텐트 타입으로 리턴하는 컨트롤러 (화면에 대한 전환이 없음)

객체를 json 형식으로 전달하려면 converter가 필요함 (Jackson 라이브러리)

POSTMAN 이용함

@RestController
public class DeptRestController {
		// GET 요청
		// 객체를 Json으로 전달하려면 converter가 필요함 (Jackson)
		// Jackson으로 컨버팅을 하지만 서버에서 어느 데이터 타입을 생산하는지 알리기 위해 명시함
		@CrossOrigin(origins = {"*"})	// 모든 요청을 허용
		@RequestMapping(value = "/getTest2",
					method = RequestMethod.GET,
					produces = {MediaType.APPLICATION_JSON_VALUE})
//		@ResponseBody // RestController 를 사용하면 생략 가능
		public Dept getTest2() {
    		Dept dept = new Dept(50, "DEV", "IT");
			return dept;
		}
		
        // POST 요청1, MediaType JSON, 여러 개의 타입으로 받을 수 있음
		@RequestMapping(value = "/getTest3",
						method = RequestMethod.POST,
						consumes = {MediaType.APPLICATION_JSON_VALUE})
		@ResponseBody
		public String getTest3(@RequestBody Dept newDept) {
			return "success";
		}
		
        // POST 요청2 MediaType  URLENCODED_VALUE
		@RequestMapping(value = "/getTest3", 
						method = RequestMethod.POST,
						consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
		@ResponseBody
		public String getTest3ForForm(Dept newDept) {
			return "success";
		}
		
        // HTTP 통신 
		@CrossOrigin(origins = {"*"})
		@RequestMapping(value = "/api/dept/{deptno}", 
						method = RequestMethod.POST,
						produces = {MediaType.APPLICATION_JSON_VALUE})
		public Dept getDeptByDeptno(@PathVariable int deptno) {
			Dept dept = null;
			
			if(dept == null) {
				throw new NullPointerException("해당 부서가 존재하지 않습니다.");
			}
			return dept;
		}
		
		// RestController 임시 예외 처리
		@ExceptionHandler(value = {NullPointerException.class})
		public ResponseEntity<String> handleNullPointerException(Exception e) {
			return new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
		}
}

2. 통신으로 Input 제어

blur가 된다면 DB의 deptno 여부에 따라 중복 여부 체크

<span style="font-size:12pt;">
	<input id="deptno" type="text" name="deptno" size="30">
</span>
<br/>
<span id="deptnoMsg" style="font-size:8pt"></span>

<script type="text/javascript">
	const inputDeptno = document.getElementById('deptno');
	const deptMsg = document.getElementById('deptnoMsg');
	
	inputDeptno.addEventListener('blur', () => {
		axios.get('http://localhost:8082/api/dept/' + inputDeptno.value)
			.then(response => {
				if(response.data == '') {
					deptMsg.innerHTML = '새로운 부서 번호로 사용할 수 있습니다.';
				}
			})
			.catch(error => {
				deptMsg.innerHTML = error.response.data;
			})
	});
</script>

RestController

@RestController
@RequiredArgsConstructor	// 생성자 주입 방식, 안정성이 높음
public class DeptAPIController {
	
	final DeptService deptService; // 생성자 주입 방식, 안정성이 높음
	
	// deptno 검증
	@RequestMapping(value = "/api/dept/{deptno}", 
					method = RequestMethod.GET)
	@ResponseBody
	public Dept getAPIDeptByDeptno (@PathVariable int deptno) throws Exception {		
		Dept dept = deptService.getDeptByDeptno(deptno);
		
		if(dept != null) {
			throw new Exception("이미 존재하는 부서번호 입니다.");
		}
		return dept;
	}
	
    // 리턴 타입 ResponseEntity<?> 
	@ExceptionHandler(value = {Exception.class})
	public ResponseEntity<String> handleNullPointerException(Exception e) {
		return new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
	}
}



2. 에러

ERROR 415 : MediaType이 매치되지 않을 경우 발생 (json, urlEncoded, form-data)

CORS ERROR : 동일한 http://ip주소:포트 번호까지 동일해야만 요청한 결과값을 돌려주는 정책
이 정책을 모두 다 허용하기 위해서 backend 요청 메소드에 @CrossOrigin(origin = {"*"}) 을 추가



3. 보완 해야 할 것

xml에서 javascript를 사용할 떄 자동완성이 되지 않아 문법적인 실수가 정말 많았음, 자동 완성에 너무 의존하면 안될 것 같음


4. 느낀점

이제 정말 프론트에서 서버, 데이터베이스까지 연결할 수 있게되서 정말 기뻤다.

throw를 사용해서 중복값을 예외로 만들어 처리하는 것과
예외 처리를 왜 @ControllerAdvice, @ExceptionHandler 에서 해줘야하는 이유도 점차 알아가고 있다.

이제 정말 미니프로젝트에 대한 기본 지식은 갖춘 것 같다.

profile
잘해볼게요

0개의 댓글