241105 RESTful (GET/POST)

241105 에이콘 아카데미 수업을 기반하여 작성되었음을 알립니다.

RESTful

AJAX를 운영하고 있다면 RESTful을 필수적으로 알아야 한다.

RESTful 하나의 프로그래밍 기법이다. 웹의 장점을 최대한 활용할 수 있는 아키텍처, 하나의 스타일인 것이다.

모바일, 웹 프로그래밍, 클라우드와 소통할 수 있는 아키텍처

하지만 REST를 사용했다 하여 모두가 RESTful 한 것은 아니다.  REST API의 설계 규칙을 올바르게 지킨 시스템을 RESTful 하다고 할 수 있다.

클라이언트가 요청할 때 GET/POST 방식만 써왔다. 서버도 마찬가지로 GET으로 요청이 왔을 때 GET 방식으로 응답, POST 방식으로 요청이 왔을 때 POST 방식으로 응답해왔다.

GET / POST 방식으로 모든 CRUD가 가능하긴하다.

RESTful 시스템에서는 모든 것이 자원이다. 자원은 URI(Uniform Resource Identifier)를 통해 식별된다.

예를 들어, 사용자의 정보를 얻으려면 /users라는 URI를 사용할 수 있다.

내가 작업하려는 것을 URI에 적어준다. /users, /products, /buser ...

REST는 자원(Resource), 행위(Verb), 표현(Representations) 세 가지 구성요소를 가지고 있다.

자원은 접근 대상을 의미, HTTP URI이다.

행위는 메소드를 의미, HTTP Method (GET(select/조회), POST(insert/생성), DELETE(delete/삭제), PUT, PATCH (update/수정,기존에 데이터가 없다면 추가로도 쓸 수 있다.))

// PUT은 리소스의 모든 것을 업데이트, PATCH는 리소스의 일부를 업데이트

표현은 메세지를 의미, HTTP Message PayLoad( 전송되는 데이터 )
표현의 주요 개념자원은 여러 가지 형식으로 표현될 수 있다.
가장 일반적인 형식은 JSON(JavaScript Object Notation)과 XML(Extensible Markup Language)이다.
예를 들어, 사용자 자원은 JSON 또는 XML 형식으로 표현될 수 있다.

서버는 응답할 때 Content-Type 헤더를 사용하여 표현의 형식을 명시한다.
예: Content-Type: application/json은 응답이 JSON 형식임을 나타낸다.

클라이언트는 요청할 때 Accept 헤더를 사용하여 원하는 표현 형식을 지정할 수 있다.
예: Accept: application/json은 클라이언트가 JSON 형식의 응답을 원함을 나타낸다.

서버는 클라이언트의 요청에 따라 자원을 적절한 형식으로 변환하여 응답할 수 있다.
자원 자체는 변하지 않지만, 표현 형식은 클라이언트의 요구에 맞게 변경다.

예를 들어 "상품명이 진라면인 상품을 생산한다."
자원 : 상품
행위 : POST
메세지 : 상품명이 진라면
HTTP POST(메소드), https://localhost/products (URI) 이런식으로 날아가게 된다.
{"name" : "진라면", "price" : "5000"}(메세지)
<form method="post" action="/products"></form> 이런 식으로 해주고 submit해주면 된다.

URI 예시:
GET /users: 모든 사용자 목록을 가져온다.
GET /users/{id}: 특정 ID를 가진 사용자의 정보를 가져온다. 패스배리어블로 받아준다., ?로 넘어오는 데이터는 리퀘스트 파람으로 받음
POST /users: 새로운 사용자를 생성한다.
PUT /users/{id}: 특정 ID를 가진 사용자의 정보를 업데이트한다. 기존에 데이터가 없다면 추가,
DELETE /users/{id}: 특정 ID를 가진 사용자를 삭제한다.

server 측에서는 각 요청명에 맞게 매핑된다. @get, @post, @put, @patch, @delete

URI 설계 시 주의할 점
 동사보다는 명사를! 대문자 보다는 가급적 소문자를! 사용하자.

언더바(_)보다 하이픈(-)은 URI 가독성을 높이는데 사용하자. Path에 띄어쓰기가 들어가는 경우 %20이 들어가 가독성이 떨어진다. 한글보다는 영문이 좋음 아스키코드 문자가 좋다.

확장자를 사용하지 말자.

마지막에 슬래시 (/)를 포함하지 않는다.

행위를 포함하지 않는다.

REST의 장점 : Open API를 제공하기 쉽다.

REST의 단점 : 사용할 수 있는 Method가 제한적이다. 하지만 CRUD 만 사용하게 될 것이기에 큰 문제가 없다.

REST API 디자인 가이드 : https://bcho.tistory.com/914를 읽어보면서 정확하게 이해하고 설명할 수 있어야 

보통, 리액트나 뷰로 많이 쓴다.

위의 REST의 특징은 실습 없이는 잘 이해되지 않는다.


Postman tool 사용

Postman 툴을 이용해 요청 처리 (API 테스트) 확인, 서버 사이드 개발자들은 유용하게 사용 가능!

클라이언트가 없어도 바로 확인 가능 AJAX 사용 시 반드시 필요한 툴이다.

Tabbed Postman - REST Client 확장프로그램 추가


RESTful 실습 1 (GET)

의존성 추가

Spring Boot DevTools / Spring Web

application.properties

spring.application.name=sprweb33restful

server.port=80

Controller

package pack.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController // @Controller + @ResponseBody 두 개를 합친 것
public class GetController {
	
	@GetMapping(value = "/hello") // 행위 GET
	public String abc() {
		System.out.println("요청 1 접수");
		return "Hello 안녕하세요"; // 메세지
	}
	
	@GetMapping(value = "/hello/{info}") // 함께 넘어오는 데이터가 있다.
	public String abc2(@PathVariable("info")String info) {
		System.out.println("요청 2 접수 : " + info);
		return "반환 값 : " + info;
	}
	
	@GetMapping(value = "/world") // /world?변수=값&변수=값
	public String abc2(@RequestParam("name")String irum,
			@RequestParam("age")String nai) {
		System.out.println("요청 3 접수 : ");
		return "반환 값 : " + irum + " " + nai;
	}
}

test1

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
function func1(){
	//alert("aa"); // fetch then then catch 세트!
	fetch("/hello",{method:"GET"})
	.then(response => {
		if(!response.ok){
			throw new Error("response 오류");
		}
		return response.text();
	})
	.then(data => {
		document.querySelector("#result").innerText = data;
	})
	.catch(error => {
		document.querySelector("#result").innerText = "err : " + error; 
	});
}

function func2(){
	const info = document.querySelector("#infoInfut").value;
	
	fetch(`/hello/${info}`,{method:"GET"}) // 자바스크립트에서 `` 사용하면 ${} 사용 가능, 자주 사용해주면 편함
	.then(response => {
		if(!response.ok){
			throw new Error("response 오류2");
		}
		return response.text();
	})
	.then(data => {
		document.querySelector("#result").innerText = data;
	})
	.catch(error => {
		document.querySelector("#result").innerText = "err2 : " + error; 
	});
}

function func3(){
	const name = document.querySelector("#name").value;
	const age = document.querySelector("#age").value;
	// 인코딩해서 넘겨주기
	const url = `/world?name=${encodeURIComponent(name)}&age=${encodeURIComponent(age)}`;
	console.log(url);
	fetch(url,{method:"GET"})
	.then(response => {
		if(!response.ok){
			throw new Error("response 오류3");
		}
		return response.text();
	})
	.then(data => {
		document.querySelector("#result").innerText = data;
	})
	.catch(error => {
		document.querySelector("#result").innerText = "err3 : " + error; 
	});
}
</script>
</head>
<body>
<h2>RESTfull API TEST</h2>
<button onclick="func1()">GET 요청1</button>
<br/>
<input type="text" id="infoInfut" placeholder="info 입력">
<button onclick="func2()">GET 요청2</button>
<br/>
<input type="text" id="name">
<input type="text" id="age">
<button onclick="func3()">GET 요청3</button>
<hr/>
<div id="result"></div>
</body>
</html>

결과

사용자 개발 도구에서 각각의 상태를 확인할 수 있다.

Postman 결과

url과 함께 넘어오는 데이터

파라미터로 넘어오는 데이터


RESTful 실습 2 (POST)

Controller

package pack.controller;

import java.util.Map;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PostController {
	@PostMapping(value = "/hipost")
	public String post1() {
		System.out.println("post 요청 접수 1");
		return "post 요청1 결과";
	}
	
	@PostMapping(value = "/hiform") // form tag로 전송한 자료를 수신
	public String post2(@RequestParam("name")String name,
			@RequestParam("addr")String addr) {
		System.out.println("post 요청 접수 2 : 파라미터 값으로 insert 진행한다고 생각해야 함");
		return name + " " + addr;
	}
	
	@PostMapping(value = "/hiform2") // json 형식으로 전송한 자료를 수신
	// 전달 받을 데이터 예시 {name:name, addr:addr}
	public String postJson(@RequestBody Map<String, String> postData) {
		String name = postData.get("name");
		String addr = postData.get("addr");
		System.out.println("post 요청 접수 3 : json 형식으로 insert 진행한다고 생각해야 함");
		return "이름 : " + name + ", 주소 : " + addr;
	}
	
	@PostMapping(value = "/hiform3") // 폼빈 형식으로 전송한 자료를 수신
	public String postJson2(PostDataBean postData) {
		String name = postData.getName();
		String addr = postData.getAddr();
		System.out.println("post 요청 접수 4");
		return "이름은 " + name + ", 주소는 " + addr;
	}
}

test2

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
function func1() {
	fetch("/hipost",{
		method:"POST"
	})
	.then(response => {
		if(!response.ok){
			throw new Error("response 오류");
		}
		return response.text();
	})
	.then(data => {
		document.querySelector("#result").innerText = data;
	})
	.catch(error => {
		document.querySelector("#result").innerText = "err : " + error; 
	});
}
function func2() {
	const name = document.querySelector("#name").value;
	const addr = document.querySelector("#addr").value;
	fetch("/hiform",{
		method:"POST",
		headers:{
			"Content-Type":"application/x-www-form-urlencoded"
		},
		body:`name=${encodeURIComponent(name)}&addr=${encodeURIComponent(addr)}`
		//body:`name=${name}&addr=${addr}` 굳이 안줘도 안 깨짐
	})
	.then(response => {
		if(!response.ok){
			throw new Error("response 오류2");
		}
		return response.text();
	})
	.then(data => {
		document.querySelector("#result").innerText = data;
	})
	.catch(error => {
		document.querySelector("#result").innerText = "err2 : " + error; 
	});
}
function func3(event) {
	event.preventDefault();
	
	const formData = new FormData(document.querySelector("#frm"));
	//console.log(formData);
	fetch("/hiform",{
		method:"POST",
		body:formData
	})
	.then(response => {
		if(!response.ok){
			throw new Error("response 오류3");
		}
		return response.text();
	})
	.then(data => {
		document.querySelector("#result").innerText = data;
	})
	.catch(error => {
		document.querySelector("#result").innerText = "err3 : " + error; 
	});
}
function func4(event) {
	event.preventDefault();
	
	const formData = new FormData(document.querySelector("#frm2"));
	//console.log(formData);
	fetch("/hiform3",{
		method:"POST",
		body:formData
	})
	.then(response => {
		if(!response.ok){
			throw new Error("response 오류4");
		}
		return response.text();
	})
	.then(data => {
		document.querySelector("#result").innerText = data;
	})
	.catch(error => {
		document.querySelector("#result").innerText = "err4 : " + error; 
	});
}
function func5(event) {
	event.preventDefault();
	
	const name = document.querySelector("#name2").value;
	const addr = document.querySelector("#addr2").value;
	const jsondata = JSON.stringify({name:name, addr:addr}); // JSON 형식을 문자열로 변환
	//console.log(jsondata);
	fetch("/hiform2",{
		method:"POST",
		headers:{
			"Content-Type":"application/json"
		},
		//body:jsondata
		body:JSON.stringify({name:name, addr:addr})
	})
	.then(response => {
		if(!response.ok){
			throw new Error("response 오류4");
		}
		return response.text();
	})
	.then(data => {
		document.querySelector("#result").innerText = data;
	})
	.catch(error => {
		document.querySelector("#result").innerText = "err4 : " + error; 
	});
}
</script>
</head>
<body>
<h2>RESTfull API TEST : POST</h2>
<button onclick="func1()">POST 요청1</button>
<br/>
<input type="text" id="name">
<input type="text" id="addr">
<button onclick="func2()">POST 요청2 (form X)</button>
<br/>
<form id="frm" onsubmit="func3(event)">
  <input type="text" name="name" value="라라라">
  <input type="text" name="addr" value="성동구 성수동">
  <button type="submit">POST 요청3 (form 전송)</button>
</form>
<form id="frm2" onsubmit="func4(event)">
  <input type="text" name="name" value="타타타">
  <input type="text" name="addr" value="성동구 왕십리">
  <button type="submit">POST 요청4 (form 전송)</button>
</form>
<form id="frm3" onsubmit="func5(event)">
  <input type="text" id="name2" value="나나나">
  <input type="text" id="addr2" value="성동구 행당동">
  <button type="submit">POST 요청5 (json 전송)</button>
</form>
<hr/>
<div id="result"></div>
</body>
</html>

에러

🚨 Failed to load resource: the server responded with a status of 400 ()

POST 방식 form tag로 넘길 때 form 태그 안에는 id를 주고 input에는 name을 주어야 함!
<form id="frm" onsubmit="func3(event)">
  <input type="text" id="name" value="라라라">
  <input type="text" id="addr" value="성동구 성수동">
  <button type="submit">POST 요청3(form 전송)</button>
</form>
                             ▼
<form id="frm" onsubmit="func3(event)">
  <input type="text" name="name" value="라라라">
  <input type="text" name="addr" value="성동구 성수동">
  <button type="submit">POST 요청3(form 전송)</button>
</form>

결과

Postman 결과

 

form tag로 전송한 자료를 수신

JSON 형식으로 전송한 자료를 수신, Headers에 Content-Type:apllication/json 적어주기

폼빈 형식으로 전송한 자료를 수신

'Study > Acorn' 카테고리의 다른 글

241107 RESTful / AOP  (3) 2024.11.07
241106 RESTful (CRUD/문제)  (1) 2024.11.07
241105 RESTful (PUT/DELETE/문제)  (0) 2024.11.05
241104 AJAX (DB연동/문제)  (0) 2024.11.04
241101 AJAX (jQuery/fetch then/AXIOS)  (0) 2024.11.04