package com.mycom.myapp.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mycom.myapp.dto.StudentDto;
import com.mycom.myapp.dto.StudentResultDto;
import com.mycom.myapp.service.StudentServiceCrud;
import lombok.RequiredArgsConstructor;
@RestController
@RequestMapping("/api/re") // /api/v1 <= 버전을 추가하는 경우도 있다.
@RequiredArgsConstructor
// ResponseEntity를 이용하는 코드는 브라우저와 spring 간의 응답 처리에 대해 직접 관여 O
// - spring에게 의뢰 처리
// - 기존 응답 객체를 ResponseEntity로 감싼다(wrapper)
// => ResponseEntity의 설정 값을 확인하고 HttpResponse 코드 변경
// - ResponseEntity 객체에 응답 코드 설정
// ResponseEntity를 통해서 작업 결과 추가로 전달 <= ResultDto와 의미 중복이 되는 문제
// 선택 1: ResponseEntity만 사용
// 선택 2: ~ResultDto만 사용
// 선택 3: 함께 사용, - ex. 프론트와 협의 정상, 비정상 구분, 비정상 중에 어떤 원인.. 등 복잡한 응답구조가 필요할때
public class StudentControllerCrudResponseEntity {
// StdentServiceCrud DI
private final StudentServiceCrud studentServiceCrud;
// 목록 get /students
@GetMapping("/students")
public ResponseEntity<StudentResultDto> listStudent(){
// return studentServiceCrud.listStudent();
StudentResultDto studentResultDto = studentServiceCrud.listStudent();
// return new ResponseEntity<StudentResultDto>(studentResultDto, HttpStatus.OK);
// return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
// return new ResponseEntity<StudentResultDto>(studentResultDto, HttpStatus.NOT_FOUND); // 비추 = 응답과 불일치하기에
// return new ResponseEntity<StudentResultDto>(studentResultDto, HttpStatus.INTERNAL_SERVER_ERROR);
// 200
// return ResponseEntity
// .status(HttpStatus.OK)
// .body(studentResultDto);
// return ResponseEntity
// .ok()
// .body(studentResultDto);
// 404
// return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
// return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); // Build the response entity with no body.
// 500
// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
return ResponseEntity.internalServerError().build();
}
// 상세 get /students/123
@GetMapping("/students/{id}")
public StudentResultDto detailStudent(@PathVariable("id") Integer id){
return studentServiceCrud.detailStudent(id);
}
// 등록 post /students
@PostMapping("/students")
public StudentResultDto insertStudent(StudentDto studentDto) {
return studentServiceCrud.insertStudent(studentDto);
}
// 수정
@PutMapping("/students/{id}")
public StudentResultDto updateStudent(@PathVariable("id") Integer id, StudentDto studentDto) {
// Parameter StudentDto 객체의 id 필드를 명시적으로 처리
// Parameter StudentDto 객체의 id 필드보다 PathVariable 로 넘어오는 id 를 우선시
studentDto.setId(id);
return studentServiceCrud.updateStudent(studentDto);
}
// 삭제
@DeleteMapping("/students/{id}")
public StudentResultDto deleteStudent(@PathVariable("id") Integer id) {
return studentServiceCrud.deleteStudent(id);
}
// 전체 건수
@GetMapping("/students/count")
public StudentResultDto countStudent() {
return studentServiceCrud.countStudent();
}
// 페이징
// REST API 에서 반드시 PathVariable 또는 RequestParam 을 써야 한다는 내용 X
// 우리는 PathVariable 사용.
@GetMapping("/students/page/{pageNumber}/{pageSize}")
public StudentResultDto listStudent(
@PathVariable("pageNumber") Integer pageNumber,
@PathVariable("pageSize") Integer pageSize) {
return studentServiceCrud.listStudent(pageNumber, pageSize);
}
}
얼마가 충분한가의 판단이 중요
중요한 코드는 비관적으로 작성 - 성능 이슈, 비용 증가이기에 필수적인 곳에만 사용
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Student Test</title>
</head>
<body>
<h1>Student.html</h1>
<hr>
<table>
<thead>
<tr><td>Id</td><td>Name</td><td>email</td><td>phone</td></tr>
</thead>
<tbody id="tbodyStudent"></tbody>
</table>
<hr>
<form>
<table>
<tbody>
<tr><td>Name</td><td><input type="text" name="name" id="inputName"></td></tr>
<tr><td>Email</td><td><input type="text" name="email" id="inputEmail"></td></tr>
<tr><td>Phone</td><td><input type="text" name="phone" id="inputPhone"></td></tr>
</tbody>
</table>
</form>
<div style="border: 1px; margin: auto; text-align: center">
<button id="btnInsert">등록</button>
<button id="btnUpdate">수정</button>
<button id="btnDelete">삭제</button>
<button id="btnClear">초기화</button>
</div>
<script>
let CURRENT_ID;
window.onload = function(){
listStudent();
// 이벤트 핸들러 등록
document.querySelector("#btnInsert").onclick = insertStudent;
document.querySelector("#btnUpdate").onclick = updateStudent;
document.querySelector("#btnDelete").onclick = deleteStudent;
document.querySelector("#btnClear").onclick = clear;
}
// ResponseEntity에 대응하는 코드로 변경
async function listStudent(){
let url = '/api/re/students';
// // 낙관적 대응
// try{
// let response = await fetch(url);
// console.log(response)
// let data = await response.json(); // 백엔드에서 body가 null로 리턴
// makeListHtml( data.list );
// // 위 코드 대신data.list 대신
// // let {list} = await response.json();
// // makeListHtml(list) 를 사용할 수도 있다.
// }catch(error){
// alert("학생 조회 과정 중 오류가 발생했습니다.")
// console.log(error)
// }
//비낙관적 대응
// respose의 응답 코드 확인
// ok 또는 status 항목으로 판단
try{
let response = await fetch(url);
console.log(response)
if(response.ok){
let data = await response.json(); // 백엔드에서 body가 null로 리턴 안 할수도 있다
makeListHtml( data.list );
} else{
// #1 . 일반화된 오류 출력
// alert("학생 조회 과정 중 오류 발생");
// #2. 세분화된 오류 출력
switch(response.status){
case 404: alert("학생 데이터를 찾을 수 없음"); break;
case 500: alert("학생 조회 과정 중 서버 오류 발생"); break;
}
}
}catch(error){
alert("학생 조회 과정 중 오류가 발생했습니다.")
console.log(error)
}
}
function makeListHtml(list){
let listHtml = ``;
list.forEach( el => {
let id = el.id;
let name = el.name;
let email = el.email;
let phone = el.phone;
listHtml += `<tr style="cursor: pointer;" data-id=${id}><td>${id}</td><td>${name}</td><td>${email}</td><td>${phone}</td></tr>`;
});
document.querySelector("#tbodyStudent").innerHTML = listHtml;
document.querySelectorAll("#tbodyStudent tr").forEach( el => {
el.onclick = function(){
let id = this.getAttribute("data-id");
detailStudent(id);
}
});
}
// studnetResultDto 대신 ResponseEntity의 status 코드와 studentDto 응답
async function detailStudent(id){
let url = '/api/re/students/' + id;
// try{
// let response = await fetch(url);
// console.log(response)
// let {dto} = await response.json(); // javascript es6 destruction 문법
// makeDetailHtml( dto );
// }catch(error){
// console.log("detail error")
// console.log(error)
// }
try{
let response = await fetch(url);
console.log(response)
if(response.ok){
let data = await response.json(); // 백엔드에서 body가 null로 리턴 안 할수도 있다
makeListHtml( data ); // studnetResultDto의 일부인 studnetDto가 바로 응답
} else{
// #1 . 일반화된 오류 출력
// alert("학생 조회 과정 중 오류 발생");
// #2. 세분화된 오류 출력
switch(response.status){
case 404: alert("학생 데이터를 찾을 수 없음"); break;
case 500: alert("학생 조회 과정 중 서버 오류 발생"); break;
}
}
}catch(error){
alert("학생 조회 과정 중 오류가 발생했습니다.")
console.log(error)
}
}
function makeDetailHtml(detail){
console.log(detail);
CURRENT_ID = detail.id;
document.querySelector("#inputName").value = detail.name;
document.querySelector("#inputEmail").value = detail.email;
document.querySelector("#inputPhone").value = detail.phone;
}
async function insertStudent(){
let url = '/api/students/';
let name = document.querySelector("#inputName").value;
let email = document.querySelector("#inputEmail").value;
let phone = document.querySelector("#inputPhone").value;
let urlParams = new URLSearchParams({
name, email, phone // javascript es6 shorthand property
})
let fetchOptions = {
method : "POST",
body: urlParams,
}
try{
let response = await fetch(url, fetchOptions)
let data = await response.json();
console.log(data);
listStudent();
clear();
}catch(error){ // BE 404 에러는 이곳에
console.log(error)
}
}
async function updateStudent(){
let url = '/api/students/' + CURRENT_ID;
let id = CURRENT_ID;
let name = document.querySelector("#inputName").value;
let email = document.querySelector("#inputEmail").value;
let phone = document.querySelector("#inputPhone").value;
let urlParams = new URLSearchParams({
id, name, email, phone
})
let fetchOptions = {
method : "PUT",
body: urlParams,
}
try{
let response = await fetch(url, fetchOptions)
let data = await response.json();
console.log(data);
listStudent();
clear();
}catch(error){
console.log(error)
}
}
async function deleteStudent(){
let url = '/api/students/' + CURRENT_ID;
let fetchOptions = {
method : "DELETE",
}
try{
let response = await fetch(url, fetchOptions)
let data = await response.json();
console.log(data);
listStudent();
clear();
}catch(error){
console.log(error)
}
}
function clear(){
document.querySelector("#inputName").value = "";
document.querySelector("#inputEmail").value = "";
document.querySelector("#inputPhone").value = "";
}
</script>
</body>
</html>
// ResultDto와 ResponseEntity 함께 사용 예시
// 작업 결과는 ResponseEntity의 status로, 데이터는 ResultDto => studentDto만, 결과는 제외
// 상세 get /students/123
@GetMapping("/students/{id}")
public ResponseEntity<StudentDto> detailStudent(@PathVariable("id") Integer id){
StudentResultDto studentResultDto = studentServiceCrud.deleteStudent(id);
String result = studentResultDto.getResult();
switch(result) {
case "success" : return ResponseEntity.ok().body(studentResultDto.getDto());
case "notfound" : return ResponseEntity.notFound().build();
case "fail" :
default : return ResponseEntity.internalServerError().build();
}
}
DR = 장애 발생 복구
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.html
https://docs.spring.io/spring-framework/reference/6.0/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.html
// @RequestBody 로 선언되지 않은 Dto 는 form data, query parameters, 그리고 URI path variables 로부터 일치하는 필드 자동 값 지정
// @RequestBody 로 선언된 Dto 는 제외
package com.mycom.myapp.controller;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mycom.myapp.dto.StudentDto;
import com.mycom.myapp.dto.StudentResultDto;
import com.mycom.myapp.service.StudentServiceCrud;
import lombok.RequiredArgsConstructor;
@RestController
@RequestMapping("/api/re") // /api/v1 <= 버전을 추가하는 경우도 있다.
@RequiredArgsConstructor
// ResponseEntity를 이용하는 코드는 브라우저와 spring 간의 응답 처리에 대해 직접 관여 O
// - spring에게 의뢰 처리
// - 기존 응답 객체를 ResponseEntity로 감싼다(wrapper)
// => ResponseEntity의 설정 값을 확인하고 HttpResponse 코드 변경
// - ResponseEntity 객체에 응답 코드 설정
// ResponseEntity를 통해서 작업 결과 추가로 전달 <= ResultDto와 의미 중복이 되는 문제
// 선택 1: ResponseEntity만 사용
// 선택 2: ~ResultDto만 사용
// 선택 3: 함께 사용, - ex. 프론트와 협의 정상, 비정상 구분, 비정상 중에 어떤 원인.. 등 복잡한 응답구조가 필요할때
public class StudentControllerCrudResponseEntity {
// StdentServiceCrud DI
private final StudentServiceCrud studentServiceCrud;
// 목록 get /students
@GetMapping("/students")
public ResponseEntity<StudentResultDto> listStudent(){
// return studentServiceCrud.listStudent();
StudentResultDto studentResultDto = studentServiceCrud.listStudent();
// return new ResponseEntity<StudentResultDto>(studentResultDto, HttpStatus.OK);
// return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
// return new ResponseEntity<StudentResultDto>(studentResultDto, HttpStatus.NOT_FOUND); // 비추 = 응답과 불일치하기에
// return new ResponseEntity<StudentResultDto>(studentResultDto, HttpStatus.INTERNAL_SERVER_ERROR);
// 200
// return ResponseEntity
// .status(HttpStatus.OK)
// .body(studentResultDto);
return ResponseEntity
.ok()
.body(studentResultDto);
// 404
// return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
// return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); // Build the response entity with no body.
// 500
// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
// return ResponseEntity.internalServerError().build();
}
// ResultDto와 ResponseEntity 함께 사용 예시
// 작업 결과는 ResponseEntity의 status로, 데이터는 ResultDto => studentDto만, 결과는 제외
// 상세 get /students/123
@GetMapping("/students/{id}")
public ResponseEntity<StudentDto> detailStudent(@PathVariable("id") Integer id){
StudentResultDto studentResultDto = studentServiceCrud.detailStudent(id);
String result = studentResultDto.getResult();
switch(result) {
case "success" : return ResponseEntity.ok().body(studentResultDto.getDto());
case "notfound" : return ResponseEntity.notFound().build();
case "fail" :
default : return ResponseEntity.internalServerError().build();
}
}
/////////////////////////////////////////////////////////////////
// 등록 post /students
@PostMapping("/students")
public ResponseEntity<String> insertStudent(StudentDto studentDto) {
StudentResultDto studentResultDto = studentServiceCrud.insertStudent(studentDto);
System.out.println(studentResultDto);
String result = studentResultDto.getResult();
System.out.println("r "+result);
switch(result) {
case "success" : return ResponseEntity.ok().build();
case "notfound" : return ResponseEntity.notFound().build();
case "fail" :
default : return ResponseEntity.internalServerError().build();
}
}
// 수정
@PutMapping("/students/{id}")
public ResponseEntity<StudentDto> updateStudent(@PathVariable("id") Integer id, @RequestBody StudentDto studentDto) {
// Parameter StudentDto 객체의 id 필드를 명시적으로 처리
// Parameter StudentDto 객체의 id 필드보다 PathVariable 로 넘어오는 id 를 우선시
studentDto.setId(id);
StudentResultDto studentResultDto = studentServiceCrud.updateStudent(studentDto);
System.out.println("update "+studentResultDto);
String result = studentResultDto.getResult();
System.out.println("r "+result);
switch(result) {
case "success" : return ResponseEntity.ok().body(studentResultDto.getDto());
case "notfound" : return ResponseEntity.notFound().build();
case "fail" :
default : return ResponseEntity.internalServerError().build();
}
}
// 삭제
@DeleteMapping("/students/{id}")
public ResponseEntity<StudentResultDto> deleteStudent(@PathVariable("id") Integer id) {
StudentResultDto studentResultDto = studentServiceCrud.deleteStudent(id);
return ResponseEntity
.ok()
.body(studentResultDto);
}
//////////////////////////////////////////////////////////////////////////
// 전체 건수
@GetMapping("/students/count")
public ResponseEntity<Long> countStudent() {
StudentResultDto studentResultDto = studentServiceCrud.countStudent();
String result = studentResultDto.getResult();
switch (result) {
// "success" 또는 "" 또는 StudentDto (id 포함한) 이 경우 Service Layer 리턴을 변경
case "success" : return ResponseEntity.ok().body(studentResultDto.getCount());
case "fail" :
default : return ResponseEntity.internalServerError().build();
}
}
// 페이징
// REST API 에서 반드시 PathVariable 또는 RequestParam 을 써야 한다는 내용 X
// 우리는 PathVariable 사용.
// 목록 전체를 가져오는 ResponseEntity<StudentResultDto> listStudent와 비교
@GetMapping("/students/page/{pageNumber}/{pageSize}")
public ResponseEntity<List<StudentDto>> listStudent(
@PathVariable("pageNumber") Integer pageNumber,
@PathVariable("pageSize") Integer pageSize) {
StudentResultDto studentResultDto = studentServiceCrud.listStudent(pageNumber, pageSize);
String result = studentResultDto.getResult();
switch (result) {
// "success" 또는 "" 또는 StudentDto (id 포함한) 이 경우 Service Layer 리턴을 변경
case "success" : return ResponseEntity.ok().body(studentResultDto.getList());
case "fail" :
default : return ResponseEntity.internalServerError().build();
}
}
}
==
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Student Test</title>
</head>
<body>
<h1>Student.html</h1>
<hr>
<table>
<thead>
<tr><td>Id</td><td>Name</td><td>email</td><td>phone</td></tr>
</thead>
<tbody id="tbodyStudent"></tbody>
</table>
<hr>
<form>
<table>
<tbody>
<tr><td>Name</td><td><input type="text" name="name" id="inputName"></td></tr>
<tr><td>Email</td><td><input type="text" name="email" id="inputEmail"></td></tr>
<tr><td>Phone</td><td><input type="text" name="phone" id="inputPhone"></td></tr>
</tbody>
</table>
</form>
<div style="border: 1px; margin: auto; text-align: center">
<button id="btnInsert">등록</button>
<button id="btnUpdate">수정</button>
<button id="btnDelete">삭제</button>
<button id="btnClear">초기화</button>
</div>
<script>
let CURRENT_ID;
window.onload = function(){
listStudent();
// 이벤트 핸들러 등록
document.querySelector("#btnInsert").onclick = insertStudent;
document.querySelector("#btnUpdate").onclick = updateStudent;
document.querySelector("#btnDelete").onclick = deleteStudent;
document.querySelector("#btnClear").onclick = clear;
}
// ResponseEntity에 대응하는 코드로 변경
async function listStudent(){
let url = '/api/re/students';
// // 낙관적 대응
// try{
// let response = await fetch(url);
// console.log(response)
// let data = await response.json(); // 백엔드에서 body가 null로 리턴
// makeListHtml( data.list );
// // 위 코드 대신data.list 대신
// // let {list} = await response.json();
// // makeListHtml(list) 를 사용할 수도 있다.
// }catch(error){
// alert("학생 조회 과정 중 오류가 발생했습니다.")
// console.log(error)
// }
//비낙관적 대응
// respose의 응답 코드 확인
// ok 또는 status 항목으로 판단
try{
let response = await fetch(url);
console.log(response)
if(response.ok){
let data = await response.json(); // 백엔드에서 body가 null로 리턴 안 할수도 있다
makeListHtml( data.list );
} else{
// #1 . 일반화된 오류 출력
// alert("학생 조회 과정 중 오류 발생");
// #2. 세분화된 오류 출력
switch(response.status){
case 404: alert("학생 데이터를 찾을 수 없음"); break;
case 500: alert("학생 조회 과정 중 서버 오류 발생"); break;
}
}
}catch(error){
alert("학생 조회 과정 중 오류가 발생했습니다.")
console.log(error)
}
}
function makeListHtml(list){
let listHtml = ``;
list.forEach( el => {
let id = el.id;
let name = el.name;
let email = el.email;
let phone = el.phone;
listHtml += `<tr style="cursor: pointer;" data-id=${id}><td>${id}</td><td>${name}</td><td>${email}</td><td>${phone}</td></tr>`;
});
document.querySelector("#tbodyStudent").innerHTML = listHtml;
document.querySelectorAll("#tbodyStudent tr").forEach( el => {
el.onclick = function(){
let id = this.getAttribute("data-id");
detailStudent(id);
}
});
}
// studnetResultDto 대신 ResponseEntity의 status 코드와 studentDto 응답
async function detailStudent(id){
let url = '/api/re/students/' + id;
// try{
// let response = await fetch(url);
// console.log(response)
// let {dto} = await response.json(); // javascript es6 destruction 문법
// makeDetailHtml( dto );
// }catch(error){
// console.log("detail error")
// console.log(error)
// }
try{
let response = await fetch(url);
console.log(response)
if(response.ok){
let data = await response.json(); // 백엔드에서 body가 null로 리턴 안 할수도 있다
makeDetailHtml( data ); // studnetResultDto의 일부인 studnetDto가 바로 응답
} else{
// #1 . 일반화된 오류 출력
// alert("학생 조회 과정 중 오류 발생");
// #2. 세분화된 오류 출력
switch(response.status){
case 404: alert("학생 데이터를 찾을 수 없음"); break;
case 500: alert("학생 조회 과정 중 서버 오류 발생"); break;
}
}
}catch(error){
alert("학생 조회 과정 중 오류가 발생했습니다.")
console.log(error)
}
}
function makeDetailHtml(detail){
console.log(detail);
CURRENT_ID = detail.id;
document.querySelector("#inputName").value = detail.name;
document.querySelector("#inputEmail").value = detail.email;
document.querySelector("#inputPhone").value = detail.phone;
}
async function insertStudent(){
let url = '/api/re/students';
let name = document.querySelector("#inputName").value;
let email = document.querySelector("#inputEmail").value;
let phone = document.querySelector("#inputPhone").value;
let urlParams = new URLSearchParams({
name, email, phone // javascript es6 shorthand property
})
let fetchOptions = {
method : "POST",
body: urlParams,
}
try{
let response = await fetch(url, fetchOptions);
console.log(response)
if(response.ok){
// let data = await response.json();
listStudent();
clear();
} else{
switch(response.status){
case 404: alert("학생 데이터를 넣을 수 없음"); break;
case 500: alert("학생 삽입 과정 중 서버 오류 발생"); break;
}
}
}catch(error){
alert("학생 삽입 과정 중 오류가 발생했습니다.")
console.log(error)
}
}
async function updateStudent(){
let url = '/api/re/students/' + CURRENT_ID;
let id = CURRENT_ID;
let name = document.querySelector("#inputName").value;
let email = document.querySelector("#inputEmail").value;
let phone = document.querySelector("#inputPhone").value;
// let urlParams = new URLSearchParams({
// id, name, email, phone
// })
let jsonParams = JSON.stringify( {id, name, email, phone} );
let fetchOptions = {
method : "PUT",
headers: {
"Content-Type" : "application/json"
},
body: jsonParams,
}
try{
let response = await fetch(url, fetchOptions);
console.log(response)
if(response.ok){
// let data = await response.json();
listStudent();
clear();
} else{
switch(response.status){
case 404: alert("학생 데이터를 수정할 수 없음"); break;
case 500: alert("학생 수정 과정 중 서버 오류 발생"); break;
}
}
}catch(error){
alert("학생 수정 과정 중 오류가 발생했습니다.")
console.log(error)
}
}
async function deleteStudent(){
let url = '/api/re/students/' + CURRENT_ID;
let fetchOptions = {
method : "DELETE",
}
try{
let response = await fetch(url, fetchOptions);
console.log(response)
if(response.ok){
let data = await response.json();
listStudent();
clear();
} else{
switch(response.status){
case 404: alert("학생 데이터를 삭제할 수 없음"); break;
case 500: alert("학생 삭제 과정 중 서버 오류 발생"); break;
}
}
}catch(error){
alert("학생 삭제 과정 중 오류가 발생했습니다.")
console.log(error)
}
}
function clear(){
document.querySelector("#inputName").value = "";
document.querySelector("#inputEmail").value = "";
document.querySelector("#inputPhone").value = "";
}
</script>
</body>
</html>
비즈니스 로직이 아니라 사용자 매뉴얼+테스트
문서도 무시해서는 안됨. 설계 문서를 보고 개발을 해야하기 때문. 실제 고객과 할때는 서명이 들어가기 때문
https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui
http://localhost:8080/swagger-ui/index.html#
package com.mycom.myapp.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
@Configuration
public class SwaggerConfig {
@Bean
OpenAPI openApi() {
return new OpenAPI()
.components(new Components())
.info(apiInfo());
}
private Info apiInfo() {
return new Info()
.title("학생 관리 API")
.description("REST API로 구현된 학생 관리 기능을 테스트합니다.")
.version("v0.9");
}
}
Controller에 @Tag(name="",description="") 추가
각 함수에는 @Operation(summary="", description="") 추가, deprecated, hidden도 존재
인터페이스로 처리도 가능
package com.mycom.myapp.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import com.mycom.myapp.dto.StudentDto;
import com.mycom.myapp.dto.StudentResultDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@Tag(name="json student CRUD REST API",description="json요청을 통해 student의 CRUD기능을 REST API로 제공합니다")
public interface StudentControllerCrudJsonRequestSwagger {
// 목록 get /students
@Operation(summary="학생 목록", description="전체 학생 목록 조회")
@GetMapping("/students")
StudentResultDto listStudent();
// 상세 get /students/123
@Operation(summary="학생 상세", description="개별 학생 1명 조회")
@GetMapping("/students/{id}")
StudentResultDto detailStudent(@PathVariable("id") Integer id);
// 등록 post /students
@Operation(summary="학생 등록", description="json 요청을 통해 신규 학생 1명 등록")
@PostMapping("/students")
StudentResultDto insertStudent(StudentDto studentDto);
}
http://localhost:8080/v3/api-docs를 postman에 import를 해서 바로 실행해볼 수 있다.


path대신 tag로 수정하면 아래와 같이 좀 더 보기 편하게 된다
