GET /members[{}, {}, {}]POST /members{ }➜ GET 과 POST 는 요청 경로는 같지만 요청 메소드가 다르기에
@GetMapping & @PostMapping 어노테이션으로 구분이 가능하다
GET /members/1{ }DELETE /members/1{ }PUT /members/1{ }PATCH /members/1{ }➜ 위에 4개도 요청 경로는 같지만 요청 메소드로 구분이 가능하다
RESTful API의 요청–응답 흐름
RESTful 방식으로 요청을 보내고JSON 응답을 반환
컨트롤러에서 리턴한 List 나 dto 를 JSON 문자열로 변경해서 응답
JSON 변환 방식
1. List : [ ] 로 변경
2. Map or dto : { } 로 변경
3. List<dto> : [{ },{ },{ }...] 형식으로 변경
{ } 가 구성될 수도 있다{
"num" : 1,
"name" : "kim",
"hobby" : ["xxx", "yyy"],
"info" : { }
}
create 메소드 생성@PostMapping("/members")
public MemberDto create(MemberDto dto) {
return memberService.addMember(dto);
}
addMember & deleteMember 메소드 수정public MemberDto addMember(MemberDto dto);
public MemberDto deleteMember(int num);
@Transactional
@Override
public MemberDto addMember(MemberDto dto) {
// insert or update 된 entity 를 리턴해준다
Member m = memberRepo.save(dto.toEntity());
// 방금 추가한 회원의 정보를 리턴해준다
return MemberDto.toDto(m);
}
@Transactional
@Override
public MemberDto deleteMember(int num) {
// 만일 삭제할 Entity 가 존재하지 않으면
if(!memberRepo.existsById(num)) {
throw new IllegalArgumentException("삭제할 회원이 존재하지 않습니다 num = "+num);
}
Member m = memberRepo.findById(num).get();
// 번호를 이용해서 삭제 (실패 시 예외가 발생하지 않는다)
memberRepo.deleteById(num);
return MemberDto.toDto(m);
}
detail 메소드 추가@GetMapping("/members/{num}")
public MemberDto detail(@PathVariable int num) {
return memberService.getMember(num);
}
delete 메소드 추가@DeleteMapping("/members/{num}")
public MemberDto delete(@PathVariable int num) {
return memberService.deleteMember(num);
}
update 메소드 추가@PutMapping("/members/{num}")
public MemberDto update(@PathVariable int num, MemberDto dto) {
// 수정할 회원의 번호를 dto 에 넣어준다
dto.setNum(num);
memberService.updateMember(dto);
return dto;
}
<form method="post">
<input type="text" name="name" />
<input type="text" name="addr" />
</form>
Content Type = application/x-www-form-urlencoded 형식으로 전송된다
이런 형식의 전송은 input 요소에 입력한 내용이 query 문자열 로 변경되어서
요청의 body 에 들어간다
body 에 포함되는 query 문자열 : name=kimgura&addr=seoul
react 에서는 form 자체를 전송하지 않고 fetch() 혹은 axios 등 javascript 로 페이지 전환없이 전송할 내용을 json 문자열로 만들어서 전송한다
그런 경우, Content-Type = application/json 이고 요청의 body 에 {"name":"kimgura", "addr":"seoul"} 형식의 json 문자열을 포함시켜서 전송
json 문자열을 전송하면 서버에서 request.getParameter() 로 추출 X
➜ 추출 방법이 다르다
spring boot 에서는 @RequestBody 어노테이션을 이용하면 쉽게 추출할 수 있다
create & update 메소드에 @RequestBody 어노테이션 추가// 회원 추가 요청 처리
@PostMapping("/members")
public MemberDto create(@RequestBody MemberDto dto) {
return memberService.addMember(dto);
}
@PutMapping("/members/{num}")
public MemberDto update(@PathVariable int num, @RequestBody MemberDto dto) {
// 수정할 회원의 번호를 dto 에 넣어준다
dto.setNum(num);
memberService.updateMember(dto);
return dto;
}
spring09.config 패키지 생성
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.6.0</version>
</dependency>
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI customOpenAPI() {
Info info = new Info()
.title("API 문서입니다")
.version("v1")
.description("API 공부를 시작합시다");
return new OpenAPI().info(info);
}
}
http://localhost:9000/swagger-ui/index.html 로 링크 가기

API 그룹화
Tag 어노테이션 추가@Tag(name = "Members", description = "회원 관리 API") // swagger ui 에서 제공할 정보
API 설명 추가
list 메소드에 @Operation 어노테이션 추가@Operation(summary = "회원 목록 조회", description = "전체 회원을 페이징 없이 반환합니다")

DTO 문서화
@Schema 어노테이션 추가@Schema(name="MemberDto", description = "회원 정보 DTO")

배열의 구조를 분해해서 변수나 상수한테 할당하는 문법
const [state, setState] = useState([]);
fetch 함수 추가data : 회원 목록이 들어 있는 배열fetch("/api/v1/members")
.then(res=>res.json())
.then(data=>{
setMembers(data);
});
key 를 generate 해서 넣어줘야 한다<tbody>
{members.map(item=><tr key={item.num}>
<td>{item.num}</td>
<td>{item.name}</td>
<td>{item.addr}</td>
</tr>)}
</tbody>
import React, { useState } from 'react';
function App4() {
// 회원 목록을 상태값으로 관리하기
const [members, setMembers] = useState([]);
return (
<div className='container'>
<button onClick={()=>{
fetch("/api/v1/members")
.then(res=>res.json())
.then(data=>{
// data 는 회원 목록이 들어 있는 배열
/// spring boot 서버로부터 받아온 데이터로 상태값을 변경하면 ui 가 업데이트된다
setMembers(data);
});
}}>받아오기</button>
<h1>회원 목록</h1>
<table>
<thead>
<tr>
<th>번호</th>
<th>이름</th>
<th>주소</th>
</tr>
</thead>
<tbody>
{members.map(item=><tr key={item.num}>
<td>{item.num}</td>
<td>{item.name}</td>
<td>{item.addr}</td>
</tr>)}
</tbody>
</table>
</div>
);
}
export default App4;
App4.jsx 에 useEffect 코드 추가
useXXX() 형식을 react 에서 자주 사용하는 데 이걸 hook 이라고 부른다hook : react 에서 사용하는 유틸리티라고 생각하면 된다useEffect(()=>{}, [])component(App4 component) 가 준비가 되었을 때 최초 1번 호출된다useEffect(()=>{
// table 에 출력할 회원 목록을 받아와서 state 를 변경 시킨다
}, []);
fetch 함수 useEffect 안으로 이동useEffect(()=>{
// table 에 출력할 회원 목록을 받아와서 state 를 변경 시킨다
fetch("/api/v1/members")
.then(res=>res.json())
.then(data=>{
setMembers(data);
});
}, []);
useEffect() 실행 시점 정리

➜ 빈 배열 : 비어두었다면 컴포넌트가 호출되는 최초의 한 번에만 실행
<h1>회원 추가 양식</h1>
<form action="/api/v1/members" method="post">
<input type="text" name="name" placeholder='이름 입력'/>
<input type="text" name="addr" placeholder='주소 입력' />
<button type="submit">추가</button>
</form>
onSubmit 이벤트 코드 추가<h1>회원 추가 양식</h1>
<form action="/api/v1/members" method="post" onSubmit={(e) => {
// 폼제출 막기
e.preventDefault();
// 폼에 입력한 내용을 담고 있는 FormData 객체를 얻어낸다
const formData = new FormData(e.target);
// 폼에 입력한 내용을 object 로 변환
const obj = Object.fromEntries(formData);
// object 를 JSON 문자열로 변환
const json = JSON.stringify(obj);
// fetch() 함수를 이용해서 전송하기
fetch("/api/v1/members", {
method: "post", // 요청 method
headers: { "Content-Type": "application/json" }, // 요청 header
body: json // 요청 body
})
.then(res => res.json())
.then(data => {
// data 는 방금 추가한 회원 정보
console.log(data);
});
}}>
<input type="text" name="name" placeholder='이름 입력' />
<input type="text" name="addr" placeholder='주소 입력' />
<button type="submit">추가</button>
</form>
getMembers 함수 생성 후 그 안에 fetch 함수 이동useEffect(() => {
getMembers();
}, []);
// 회원 목록을 요청해서 상태값을 변경해주는 함수
const getMembers = () => {
// table 에 출력할 회원 목록을 받아와서 state 를 변경 시킨다
fetch("/api/v1/members")
.then(res => res.json())
.then(data => {
setMembers(data);
getMembers();
});
};
e.target 코드 수정<form action="/api/v1/members" method="post" onSubmit={(e) => {
// 폼제출 막기
e.preventDefault();
// 이벤트가 일어난 그 요소(form)
const form = e.target;
// 폼에 입력한 내용을 담고 있는 FormData 객체를 얻어낸다
const formData = new FormData(form);
// 폼에 입력한 내용을 object 로 변환
const obj = Object.fromEntries(formData);
// object 를 JSON 문자열로 변환
const json = JSON.stringify(obj);
// fetch() 함수를 이용해서 전송하기
fetch(form.action, {
method: form.method, // 요청 method
headers: { "Content-Type": "application/json" }, // 요청 header
body: json // 요청 body
})
.then(res => res.json())
.then(data => {
// data 는 방금 추가한 회원 정보
console.log(data);
getMembers();
});
}}>
<th>삭제</th>
<td>
<button>x</button>
</td>
const handleDelete = (num) => {
const isDelete = confirm(num + " 번 회원을 삭제 하시겠습니까?");
if (isDelete) {
fetch(`/api/v1/members/${num}`, {
method: "delete"
})
.then(res => res.json())
.then(data => {
// 삭제 성공 시 여기가 호출된다
getMembers();
});
}
};
<button onClick={()=>{handleDelete(item.num)}}>x</button>
<th>수정</th>
<td>
<button>Edit</button>
</td>
class = classNamefor = htmlFor<div>
<h1><span>x</span> 번 회원의 수정 양식</h1>
<form action="">
<div>
<label htmlFor="name">이름</label>
<input type="text" name="name" id="name" value="x" />
</div>
<div>
<label htmlFor="addr">주소</label>
<input type="text" name="addr" id="addr" value="x" />
</div>
<button type="submit">수정 확인</button>
<button type="reset">취소</button>
</form>
</div>
true & false 상태값으로 관리{true && <p>true 입니다</p>}
{false && <p>false 입니다</p>}
const [editing, setEditing] = useState(false);
{editing && (
<div>
<h1><span>x</span> 번 회원의 수정 양식</h1>
<form action="">
<div>
<label htmlFor="name">이름</label>
<input type="text" name="name" id="name" value="x" />
</div>
<div>
<label htmlFor="addr">주소</label>
<input type="text" name="addr" id="addr" value="x" />
</div>
<button type="submit">수정 확인</button>
<button type="reset">취소</button>
</form>
</div>
)}
<button onClick={()=>{
setEditing(true);
}}>Edit</button>
const [member, setMember] = useState({num:0, name:"", addr:""});
{editing && (
<div>
<h1><span>{member.num}</span> 번 회원의 수정 양식</h1>
<form action="">
<div>
<label htmlFor="name">이름</label>
<input type="text" name="name" id="name" value={member.name} />
</div>
<div>
<label htmlFor="addr">주소</label>
<input type="text" name="addr" id="addr" value={member.addr} />
</div>
<button type="submit">수정 확인</button>
<button type="reset">취소</button>
</form>
</div>
)}
fetch(`/api/v1/members/${item.num}`)
.then(res=>res.json())
.then(data=>setMember(data));
change 이벤트 발생 시 현재 입력한 내용으로 상태값을 변경해야 한다const handleChange = (e) => {
};
text 박스에 onChange 함수 추가<input onChange={handleChange} type="text" name="name" id="name" value={member.name}/>
<input onChange={handleChange} type="text" name="addr" id="addr" value={member.addr}/>
// 이벤트가 일어난 요소의 name 속성의 값 "name" or "addr"
const name = e.target.name;
// 이벤트가 일어난 요소에 입력한 값 "입력한 문자열"
const value = e.target.value;
// 상태값 변경하기
setMember({
...member, // 기존의 object 안에 있는 값을 펼쳐놓고
[name]:value // 수정한 내용의 value 만 덮어쓰기 한다
});
key 값으로 결정 C:\Users\USER>node
> const m1 = {num:1, name:"kim"};
undefined
> const m2 = {["num"]:1, ["name"]:"kim"};
undefined
> const a = "num";
undefined
> const b = "name";
undefined
> const m3 = {[a]:1, [b]:"kim"};
undefined
> m1
{ num: 1, name: 'kim' }
> m2
{ num: 1, name: 'kim' }
> m3
{ num: 1, name: 'kim' }
>
action & onSubmit 코드 추가<form onSubmit={handleUpdateSubmit} action={`/api/v1/members/${member.num}`}>
submit 이벤트가 일어났을 때 실행할 함수 추가const handleUpdateSubmit = (e) => {
// 폼 전송 막기
e.preventDefault();
const form = e.target; // 수정 form 요소의 참조값
fetch(form.action, {
method: "put", // put 방식
headers: { "Content-Type": "application/json" }, // json 문자열을 보낸다고 알림
body: JSON.stringify(member) // 상태값으로 관리하는 member object 를 json 문자열로 변경
})
.then(res => res.json())
.then(data => {
alert(`${data.num} 번 회원의 정보 수정 완료`);
setEditing(false);
setMember({
num: 0, name: "", addr: ""
});
getMembers();
})
}
<button type="reset" onClick={()=>{
setEditing(false);}}>취소</button>
React 상태 변경과 렌더링 흐름

실습 숙제 Book 테이블로도 API 해보기