당니의 개발자 스토리

상품 수정 본문

스프링/실전! 스프링 부트와 JPA 활용1

상품 수정

clainy 2024. 5. 15. 16:13

상품 수정

자 이번 시간은 상품 수정입니다. 이게 엄청 중요합니다. 상품등록이나 조회는 되게 간단했는데, 사실 수정이 좀 복잡해요. 그리고 이거를 또 JPA에서 어떤 방법으로 수정하는 게 옳을까, 또 변경 감지랑 병합(merge)이란 두 가지 방법이 있단 말이에요. 둘 중에 뭘 쓰는게 좀 더 정석적인 방법이지 등등을 좀 디테일하게 설명을 드릴 거에요.

그래서 이번 시간은 진짜 집중을 해서 들으셔야 됩니다.

이거를 제가 jpa 하시는 분들 많이 보면 데이터 수정하는 거를 jpa가 하라는 guide 대로 하는 게 아니라, 본인의 관성에 의해서 merge(병합)를 쓴다고 하는데요. 사실 jpa에서 guide 해주는 거는 변경 감지를 쓰라는 게 이제 베스트 practice 거든요.

자 그거와 관련해서 이제 쭉 엮어서 말씀드리겠습니다.

먼저 상품 수정을 해봐야 되겠죠.

수정을 딱 누르면,

URL이 이렇게 됩니다. 보시면 items/1/edit로 가죠. 그럼 이제 컨트롤러에다가 items/1/edit를 만들어줘야 됩니다.

일단 @GetMapping 해서 아까 url을 붙여넣기 하면 되는데

이 부분은 변경될 수 있죠. 그래서 @PathVariable 이라는 걸 써야됩니다. form에서 먼저 수정하고 나서, 수정이 돼야 되니까 form을 먼저 짭니다.

@PathVariable 을 넣어서 위에 itemId랑 이거를 맵핑시켜줍니다.

그 다음에 long의 itemId 놓고, 또 데이터를 View에 넘겨야 되니까 Model 객체를 받아줍니다.

그리고 나서, itemService에서 findOne 해가지고 itemId를 넣으면, 지금 반환되는게 Item 타입 이거든요. 근데 저는 예제를 심플하게 하기 위해서 그냥 책만 무조건 가져온다고 가정을 할 거에요.

그래서 이렇게 반환 타입과 캐스팅을 해줍니다. 사실 캐스팅을 하고 이런 게 좋지는 않은데, 여기서는 예제를 단순하게 위해서 그냥 바로 캐스팅해서 썼습니다.

그 다음에 BookForm을 만들어야 겠죠. 무슨 얘기냐면 업데이트 하는데 Book 엔티티를 보내는 게 아니라, 이 BookForm 이라는 걸 보낼 거예요. 그리고 나서,

이렇게 set까지 해줍니다.

그 다음에 model.addAttribute 해서 form을 담아주고 return 해서 updateItemForm 화면으로 넘겨줍니다. 일단 지금 코드를 보시면, set이 너무 많잖아요.

그래서 인텔리제이에 무슨 기능이 있냐면 BookForm으로 가셔가지고 option을 빠르게 두 번 누르고 방향키를 하면,

이렇게 라인 셀렉트가 되거든요. 그래서 다시 돌아와서,

이런 식으로 한번에 수정하는 거죠.

자 뭐 이렇게 하는 것도 있고 인텔리제이에 플러그인이 있다는 것 같은데 궁금하신 분들은 한번 검색해 보세요.

자 어쨌든 이 폼에 데이터가 넘어간단 말이에요.

그럼 updateItemForm.html을 만들겠습니다. 그리고 일단 복붙 먼저 해놓고 한번 보여드릴게요.

특별한 게 없습니다. 이 form에 있는 데이터를 그냥 뿌리는 겁니다. 사실 update는 create랑 좀 다른 게 처음부터 데이터가 있는 거죠.

서버를 다시 껐다가 띄우겠습니다.

일단 DB가 데이터를 기존의 데이터를 다 날리기 때문에 상품 등록에 들어가셔서,

데이터를 먼저 등록해놓겠습니다.

Submit 누르면 상품목록으로 이동합니다. 그리고 이 수정 버튼을 누르면,

Get 방식으로 items/1/edit으로 이동해서 데이터들이 그대로 나오는 걸 확인하실 수 있죠.

그리고 다시 Submit을 누르면 Post 방식으로 다시 items/1/edit으로 이동하지만, 지금은 @PostMapping을 안 했으니까 에러가 나는게 맞습니다.

지금 보면 Request method 'POST' is not supported 라고 나오죠?

자 이제 @PostMapping을 만들겠습니다.

여기서 이 form 객체가 다시 넘어오는 겁니다.

@PostMapping에서는 @PathVariable이 없어도 돼요. 값을 쓸 것도 아니고 어차피 form에서 오거든요.

그래서 이렇게 적으면 됩니다. 그런데 이 form 앞에다가

@ModelAttribute 라는 어노테이션을 넣어주셔야 돼요. 이게 뭐냐면,

화면에서 object 해서 form을 잡으셨잖아요.

그래서 @ModelAttribute는 사용자가 요청시 전달하는 값을 오브젝트 형태로 매핑해주는 어노테이션입니다.

아무튼 그 다음에 어떻게 합니까? 이제 form을 book으로 바꿔야 겠죠.

set을 또 해야하는데, BookForm으로 가셔서

option을 두 번 누르시고, option을 누른 상태로 이동하면 스마트하게 이동이 가능하거든요. 그리고 shift 누른 상태로 복사를 할 수 있어요.

option + shift + <- 방향키로 복사를 깔끔하게 해줍니다.

그리고 다시 BookForm으로 가셔서

cmd + shift + U 하셔서 Upper-case(대문자)를 해줄 수 있습니다.

그 다음 form.get 하고 붙여넣기 하고 또 대문자로 바꾸면,

이렇게 끝납니다.

그리고 나서, saveItem으로 book을 다시 저장하시면 되겠죠. 지금 수정한 걸 업데이트 하는 거니까요. 그리고 return 하고 items로 가겠습니다. edit 하고 나서 다시 리스트로 가는 거죠.

자 이제 서버를 다시 띄우고 다시 상품이 초기화 되니까 상품 등록부터 하겠습니다.

이렇게 작성한 뒤 Submit 하고,

목록에서 수정을 누른 다음 수정이 되는지 확인해보겠습니다.

이렇게 바꾸고 Submit을 하면,

변경이 되어있죠.

자 이렇게 해서 상품 수정까지 완료를 해봤습니다. 근데 메커니즘이 참고로 실무에서는 너무 당연한 얘기일 수도 있는데,

이 Id를 되게 조심하셔야 돼요.

누군가 이 Id를 임의적으로 조작 할 수 있거든요.

그래서 수정하고 Submit할 때 화면 form에서 넘어오는데, 이 id가 조작되어서 넘어오면 다른 사람 데이터가 수정이 되어버려요. 당연하죠. id가 내 것이 아닌 다른 사람 걸로 넘어오면 다른 사람 게 수정되잖아요.

그래서 보안상 생각보다 취약점이 많아요. 그래서 서비스 계층이 뒷단에서든, 앞단에서든 이 유저가 이 item에 대해서 권한이 있는지, 없는지를 체크해주는 로직이 서버에 있어야 돼요.

아니면은 좀 더 복잡한 방법인데 업데이트 할 객체 자체를 뭔가 세션에 담아두고 풀어내는 방법도 있고요. 뭐 여러가지 방법이 있는데 요즘 세션 객체를 잘 안 써요.

아무튼 그렇고요. 이제부터 진짜 중요한 걸 말씀드릴거예요.

지금 itemService에서 saveItem을 하잖아요. 그럼 Book Entity가 넘어가죠. ItemService로 가볼게요.

saveItem을 호출하면 트랜잭션이 걸린 상태로, ItemRepository의 save를 또 호출합니다.

그래서 ItemRepository로 들어가면 이걸 잘 보세요.

지금 수정에서는 item의 id가 null이 아니죠. 원래는 null이면 새로운 object니까 persist를 하고 null이 아니면 else문을 타서 em.merge가 호출됩니다.

그러니까 수정에서의 item은 db에서 수정할 목적으로 불러온 놈이란 말이에요. 그러니까 id가 이미 있는 거죠.

아무튼 이 Merge의 정체에 대해서는 다음 시간에 명확하게 설명을 해드리겠습니다. 원래는 제가 이 Merge를 잘 설명을 안 해요. 왜냐면 사실 쓸 일이 실무에서 거의 없어요. 그리고 이런 화면에서 막 이렇게 엔티티가 넘어오고 해야 이 Merge를 '아 이렇게 쓰는 구나' 하고 알거든요.

자 아무튼 그래서 다음 시간에는 이 Merge에 대해서 잘 말씀드리고 jpa 에서 권장하는, 데이터를 수정하는 방법이 뭔지도 코드를 가지고 말씀드리겠습니다.

'스프링 > 실전! 스프링 부트와 JPA 활용1' 카테고리의 다른 글

상품 주문  (0) 2024.05.19
변경 감지와 병합(merge)  (0) 2024.05.16
상품 목록  (0) 2024.05.14
상품 등록  (0) 2024.05.14
회원 목록 조회  (0) 2024.05.14