1005 | REST Api (get, post, put) , @PathVariable , @RequestBody, SQL에 null조건 걸기
✅ REST API개념 (RestController라는 이름을 갖는 이유)
- REST
- RESTful
"Representational State Transfer”
요청할때 갖고 싶은 자원을 표현할 수 있는 방법이 필요함 (표현을 해야 전달해줌. 전달 == transfer)
자원은 정적인 상태 (== representational state). 자원의 현재상태를 갖다준다.
REST의 representation이란 무엇인가
사실 서버가 보내준 것은 리소스가 아니다. 다음과 같은 HTTP GET 요청을 서버에 보내서 GET Host: example.org Accept: text/plain, text/html; q=0.9 *; q=0.1 Accept-Language: en, ko; q=0.9, *; q=0.1 “hello”라는 메시지를
blog.npcode.com
A건물에 가서 2층에가서 C층에 있는 F자리 자원을 달라고 한다 ▶ 큰 범위에서 작은범위로 좁혀들어감
👉 http://.../A/2/C/F
👉 서버 or api /menus/3
👉 표현하고자 하는 요소들이 행위를 표현하면 안됨.
#️⃣ CRUD 외의 자료에 접근하는 경우
✔️ url 부터 만듦
서버 or api /menus/3/korName/업데이트내용 ▶ put요청
(메뉴들중 3번의 속성으로 접근)
✔️좋아요 데이터에 접근하기
서버 or api /menus/3/like/true (on, 1 등... 상태값 업데이트) ▶ post요청 (데이터를 새로 추가하는것이므로)
👉 따라서 REST를 만들땐 url을 꼼꼼히 따져서 만들어야 한다
✅ api를 활용하여 세부 정보에 접근하기
#️⃣ 메뉴 중 16번에 접근하기
//사용자에 대한 명이 들어가지않고 온전히 엔티티에 대한 내용만 들어간다
@RestController("apiMenuController") // Rest를 반환한다는 의미이므로 responsebody를 설정할 필요가 없다
@RequestMapping("/api/menus")
public class MenuController {
@Autowired
private MenuService service;
// @ResponseBody
@GetMapping //목록을 제공하는 api
public List<Menu> list(){
//menus경로로 데이터를 요청했으므로 getmapping에는 경로를 잡을 필요가 없음
//responsebody로 객체의 목록을 반환시킨다
List<Menu> list = service.getList();
return list;
}
// 16번 메뉴를 제공하는 api
@GetMapping("16") // /api/menus/16 경로를 변수화 할 수 있는가. 가 나중의 문제.
public Menu detail() {
Menu menu = service.getById(16L);
return menu;
}
}
👉 아이디를 어떻게 변수화할것인가가 관건.
#️⃣ {id}로 변수화 시키고 @PathVariable 로 경로임을 인지시키기
@RequestMapping("/api/menus")
public class MenuController {
@Autowired
private MenuService service;
// 16번 메뉴를 제공하는 api
@GetMapping("{id}") // /api/menus/16 경로를 변수화 할 수 있는가. 가 나중의 문제.
//{id}는 어노테이션일뿐, 받아서 활용하는 과정이 필요하다 menus/ 다음에 오는걸 id라는 변수로 받겠다는 표시
//소괄호 안에 들어가는건 쿼리스트링이나 post데이터여야하기 때문에 @PathVariable을 붙여줘야 경로로 인지한다
public Menu detail(@PathVariable long id) {
Menu menu = service.getById(id);
return menu;
}
//--------------------------------
@GetMapping("{id}/kor-name/{name}") // 경로에 여러가지가 들어갈 때, (속성접근)
public Menu detail(
@PathVariable long id,
@PathVariable String name) {
Menu menu = service.getById(id);
return menu;
}
👉 자바스크립트로 진행할때는 form이 꼭 필요하지않다. 자스 자체가 모든 태그에 접근할 수 있기 때문
✅ 메뉴를 등록하는 POST api
//MenuController.java
//메뉴를 등록하는 API 사용자가 전달한 데이터를 등록. 그리고 등록하자마자 반환받기를 원함.
@PostMapping
public Menu reg(Menu menu){
// service.add(menu); //메뉴추가를 한 후 다른 함수로 접근해도 되지만 요즘은 하나로 처리
Menu newMenu = service.add(menu);
return newMenu;
}
//Interface MenuService
Menu add(Menu menu); //menu를 받아서 db에 추가한 menu를 반환받는 api
//class MenuServiceImp
@Override
public Menu add(Menu menu) {
repository.save(menu);
Menu newMenu = repository.last(); // 마지막 저장값을 조회해서 반환
return newMenu;
}
//MenuRepository
void save(Menu menu); // insert를 save로 많이 표현한다
Menu last();
// Mapper.xml
<select id="last" resultType="Menu">
select * from menu order by id desc
limit 1
</select>
<insert id="save" parameterType="Menu">
insert into menu
(kor_name,eng_name, price, img ,hit, member_Id)
values
(#{korName},#{engName},#{price},#{img},#{hit},#{memberId})
</insert>
#️⃣ api를 테스트할 수 있는 postman
Postman API Platform | Sign Up for Free
Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.
www.postman.com
* postman은 application으로도 제공된다 (PWAS형태)
🤔🤔 PWAS란?
프로그레시브 웹 앱 소개 - 프로그레시브 웹 앱 | MDN
이 문서는 프로그레시브 웹 앱(PWA)의 소개입니다. PWA가 무엇이고 일반 웹 앱에 어떤 이점을 가져다주는지 설명합니다.
developer.mozilla.org
#️⃣ POST 전송해보기
- 자바스크립트가 post처리하기. (html을 쓰지않으므로 form태그도 필요없음)
- 자바스크립트 스타일의 객체방식으로 전송한다 ▶ URL이 아닌 JSON
🔥 HTTP 관련 참고할 수 있는 사이트
- data tracker에서 http검색
IETF Datatracker
Datatracker The IETF Datatracker is the day-to-day front-end to the IETF database for people who work on IETF standards. It contains data about the documents, working groups, meetings, agendas, minutes, presentations, and more, of the IETF. The primary pub
datatracker.ietf.org
RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1
HTTP has been in use by the World-Wide Web global information initiative since 1990. This specification defines the protocol referred to as "HTTP/1.1", and is an update to RFC 2068. [STANDARDS-TRACK]
datatracker.ietf.org
- JSON으로 보내기위해 파일을 row로 설정하고 타입도 JSON으로 설정.
- 자바스크립트에선 알아서 키값을 "쌍따옴표"로 만들어주지만 여기선 직접 감싸줘야함. (키에 따옴표 없으면 에러뜸!)
전송했을때 컨트롤러에 있는 @PostMapping은 JSON을 덩어리로 인식한다.
👉 통으로 오는 놈을 처리할 수 있는 어노테이션을 써주기. (@RequestBody)
@RequestBody
이 어노테이션이 붙은 파라미터에는 http요청의 본문(body)이 그대로 전달된다.
//메뉴를 등록하는 API 사용자가 전달한 데이터를 등록. 그리고 등록하자마자 반환받기를 원함.
@PostMapping
public Menu reg(@RequestBody Menu menu){ // 바디에 넣은 매개변수를 그대로 전달
System.out.println(menu);
Menu newMenu = service.add(menu);
System.out.println("newMenu"+newMenu);
return newMenu;
}
(*이전에 사용했던 responseBody와의 차이점)
@ResponseBody (*html의 body 내용을 그대로 웹에 띄웠었음)
자바객체를 HTTP요청의 바디내용으로 매핑하여 클라이언트로 전송한다.
@ResponseBody 가 붙은 파라미터가 있으면 HTTP요청의 미디어타입과 파라미터의 타입을 먼저 확인한다.
✅ 메뉴의 속성을 수정하는 PUT api
👉 전송할때도 JSON으로 전송한다
//메뉴를 수정하는 api 수정해서 메뉴를 반환한다. //api/menus/16만 수정한다.
//단 메뉴에 id가 담겨있으므로 id파라미터 전달 X. 리파지에서 getId로 뽑아서 사용
@PutMapping
public Menu modify(@RequestBody Menu menu) {
Menu amended = service.modify(menu);
return amended;
}
//interface service--------------------
Menu modify(Menu menu);
//serviceImp--------------------
@Override
public Menu modify(Menu menu) {
repository.update(menu);
Menu amendedM = repository.findById(menu.getId());
return amendedM;
}
//MenuRepository--------------------
void update(Menu menu);
-- repository에 쿼리문 준비
<update id="update" parameterType="Menu">
UPDATE `menu`
SET
`kor_name` = #{korName},
`eng_name` = #{engName},
`price` = #{price},
`img` = #{img},
`reg_date` = #{regDate},
`hit` = #{hit},
`member_id` = #{memberId}
WHERE `id` = #{id}
</update>
👉 이때 수정하고싶어서 전달한 값 외에 나머지값들은 모두 0 아니면 null로 채워지게 된다. ▶기존값들을 유지하고싶음!
🔥해결법
- 각 속성별로 수정하는 함수를 만든다
- null을 가지고 있는지 체크 ▶ 쿼리값이 동적으로 변경되게 만든다 (동적쿼리) -- 단 사용하는 dbms가 기능을 지원해야함
mybatis – MyBatis 3 | Dynamic SQL
Dynamic SQL One of the most powerful features of MyBatis has always been its Dynamic SQL capabilities. If you have any experience with JDBC or any similar framework, you understand how painful it is to conditionally concatenate strings of SQL together, mak
mybatis.org
<update id="update" parameterType="Menu">
UPDATE `menu`
SET --null이 아닐때 해당 속성을 업데이트한다는 동적쿼리
<if test="korName != null">`kor_name`=#{korName},</if>
<if test="engName != null">`eng_name`=#{engName},</if>
<if test="price != null">`price`=#{price},</if>
<if test="img != null">`img`=#{img},</if>
<if test="regDate != null">`reg_date`=#{regDate},</if>
<if test="hit">`hit`=#{hit},</if>
<if test="memberId != null">`member_id`=#{memberId},</if>
WHERE `id` = #{id}
</update>
필요에 따라 특정 블록만 남겨지는데, 한 블럭의 끝에 콤마(,)가 남아있으므로 오류의 원인이 될 수 있다.
👉 <SET>태그로 블럭들을 감싸주면 콤마를 알아서 삭제시켜줌.
<update id="update" parameterType="Menu">
UPDATE `menu`
<set>
<if test="korName != null">`kor_name`=#{korName},</if>
<if test="engName != null">`eng_name`=#{engName},</if>
<if test="price != null">`price`=#{price},</if>
<if test="img != null">`img`=#{img},</if>
<if test="regDate != null">`reg_date`=#{regDate},</if>
<if test="hit">`hit`=#{hit},</if>
<if test="memberId != null">`member_id`=#{memberId},</if>
</set>
WHERE `id` = #{id}
</update>
외래키인 member_id는 전달되지않았으나 null은 아니므로 0으로 업데이트하고자 함. 하지만 제약조건에 따라 0이 들어갈 수 없어서 에러가 남 == null체크와 0체크를 해야하는데 mybatis에서 0체크 기능은 따로 넣어줘야함
<update id="update" parameterType="Menu">
UPDATE `menu`
<set>
<if test="korName != null">`kor_name`=#{korName},</if>
<if test="engName != null">`eng_name`=#{engName},</if>
<if test="!(price == null or price==0)">`price`=#{price},</if>
<if test="img != null">`img`=#{img},</if>
<if test="regDate != null">`reg_date`=#{regDate},</if>
<if test="hit">`hit`=#{hit},</if>
<if test="memberId != null and memberId!=0">`member_id`=#{memberId},</if>
</set>
WHERE `id` = #{id}
</update>
👉 기본값으로 null이 들어갈수있도록 함으로써 sql에서의 값체크를 줄일 수 있음
(널이 아닐때만 동작하므로 값이 없는것을 모두 널로 통일. 기본형의 0체크를 안해도됨)
🔥 최종
public class Menu { //entity변경
//null이 가능한 타입으로 맞춰준다
private Long id;
private String korName;
private String engName;
private Integer price;
private Boolean isSmall;
private String img;
private String description;
private Date regDate;
private Integer hit;
private Long memberId; //참조키
<update id="update" parameterType="Menu">
UPDATE `menu`
<set>
<if test="korName != null">`kor_name`=#{korName},</if>
<if test="engName != null">`eng_name`=#{engName},</if>
<if test="price != null">`price`=#{price},</if>
<if test="img != null">`img`=#{img},</if>
<if test="regDate != null">`reg_date`=#{regDate},</if>
<if test="hit">`hit`=#{hit},</if>
<if test="memberId != null">`member_id`=#{memberId},</if>
</set>
WHERE `id` = #{id}
</update>
🤔entity를 wrapper class로 처리하면 성능에 문제가 있지않을까?
👉 JS나 파이썬 모두 래퍼타입. 요즘에는 크게 문제두지않고 참조형을 사용한다.