안녕하세요?? 해당 강의 12:30 무렵 return "redirect:/";을 이용해 초기 화면으로 되돌아 가는데, HomeController처럼 return "home"; 을 사용하면 안되는건가요?? 두 가지 모두 초기 화면으로 돌아가는것 같은데 어떤 차이인지 궁금합니다.
영한님 안녕하세요 :) 오래전에 공부 했던게 기억나지 않아서 예전에 봤던 강의를 돌고 돌다가 질문을 남기게 되었습니다. UPDATE는 더티체킹으로 변경하는 것에 대해서는 이해하고 적용을 하고 있는데, 효율적인 삭제 방법에 대해서는 감이 잡히지 않아서 이렇게 질문을 드립니다. 아래와 같은 간단한 상황을 예를 들면서 질문을 드리겠습니다. 위와 같은 도메인들이 있고, 각각은 모두 양방향 참조를 하고 있는 상황이라 가정 하겠습니다. 이 때 비즈니스 요구사항은 아래와 같습니다. 1. MEMBER 탈퇴 시에 해당 MEMBER에 연관된 POST도 모두 함께 삭제 되어야 한다. 2. 특정 CATEGORY 삭제 시 해당되는 POST는 남겨두고 CATEGORY만 삭제가 되어야 한다. 1번, 2번이 서로 조건이 상반 되는데요. 여기서 질문을 드리고 싶습니다. 질문 1) 1번 요구사항의 경우 orphanRemoval 혹은 Cascade를 통해 손쉽게 할 수 있을거 같습니다. Cascade를 적용 해놓았기 때문에 JpaRepository의 delete 메서드로 MEMBER를 지우면 해당되는 POST도 모두 지워질 듯 한데요. 이런 요구사항에서는 이렇게 지우는게 best practice가 맞는지 알고 싶습니다. 질문 2) Cascade All, orphanRemoval을 함께 사용하면 부모 엔티티와 자식 엔티티가 생명 주기를 함께 하면서 부모 엔티티가 자식 엔티티에 대한 모든 제어권을 가지는 것과 마찬가지라고 알고 있습니다. 그런데 앞선 질문1 처럼 삭제 할 때만 Cascade를 적용하고 싶다면 Cascade DELETE가 더 적합할 듯 한데요. 강의에서 DELETE는 많이 안쓴다고 언급 하셨습니다. DELETE를 잘 사용하지 않는 어떤 이유가 있는지 궁금 합니다! 질문 3) 위 요구사항에 의하면 MEMBER와는 다르게 CATEGORY는 부모 엔티티가 삭제 되어도 자식 엔티티인 POST를 그대로 남겨두어야 합니다. 이 때, CATEGORY의 POST List를 순회하면서 해당 POST에서 CATEGORY 와의 관계를 끊어주어야 할 듯 한데요. 반복문 또는 stream을 사용하면서 모든 관계를 끊어주어야 한다고 할 때, 성능상에 문제가 생기지는 않는지 궁금합니다. 예를 들어 CATEGORY가 10만개, 100만개의 POST와 연관관계를 가지고 있었다면, 이때는 어떤 방법으로 처리 하는지 알고 싶습니다. (JPQL로 직접 쿼리를 날리는 방법이 가장 먼저 떠오르는데, 무언가 JPA스러운(?) 우아한 방법이 있을거 같네요ㅎㅎ) 질문 4) 자식 엔티티를 가지지 않는 POST 엔티티를 삭제 할 때는 1. PostRepository의 delete를 직접 사용해서 지운다. 2. CATEGORY, MEMBER와의 관계를 null 처리 하고, 두 부모 엔티티 리스트에서 해당 POST를 지운다. 두 방법 모두 가능 가능할 듯 한데요. 어떻게 삭제를 하는 것이 best practice인지 알고 싶습니다. (+) 내용이 읽기 불편하실거 같아 개행을 많이 했는데도 게시판에 적용이 안되는 듯 하네요. 양해 부탁 드립니다ㅠㅠ 감사합니다.
안녕하세요 강사님, 질문 드리겠습니다. DTO의 필요성에 대한 부분으로 "엔티티가 변경됐을 때 변경된 사항이 API 스펙에 적용되지 않아 API가 제대로 동작하지 않을 수 있다."라고 말씀해주셨는데요. 이러한 문제가 발생하는 V1에서는 받을 때는 Member, 돌려줄때는 CreateMemberResponse를 사용하고 있습니다. 그런데, 여기서 요청을 때, 응답을 줄 때 모두 Member를 사용해버린다면.. Member가 변경된다 하더라도 Response에서도 변경사항이 적용되기 때문에 문제가 없는 것 아닌가?? 하는 생각이 듭니다. 이 부분에 대한 추가설명을 부탁드리고 싶습니다. 감사합니다! ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ 이어지는 강의들을 듣다보니 자연스럽게 이해되었습니다. 이해한 바를 바탕으로 질문에 대한 자답을 해보자면.. ************************************************** DTO를 반드시 사용해야 하는 이유는 다음과 같다. 1. DTO를 사용하지 않을 경우 엔티티의 변경에 의해 API 스펙이 변경될 수 있다. -> 엔티티와 API가 일대일 대응의 관계를 가진다면 엔티티에 수정이 일어날 때마다 API 스펙을 일일히 변경해줘야한다. 이는 매우 번거로운 작업이며 컴파일 에러로 이를 감지할 수 없기 때문에 에러 원인을 찾기가 어렵다. (DTO를 사용하면 DTO -> 엔티티 과정에서 컴파일 에러가 발생되므로 엔티티의 변경사항을 반드시 파악할 수 있다) 2. 하나의 엔티티에 대해서 API는 여러 개가 존재할 수 있다. -> 각각의 API가 요구하는 엔티티에 대한 데이터는 모두 다를 확률이 높다. 이때, 그냥 엔티티의 모든 정보를 넘겨줘버린다면 필요없는 데이터까지 받긴 하지만 필요한 건 전부 받은 셈이니 기능 동작에는 문제가 없을 것이다. 하지만 PW처럼 보안상 감추어야 할 정보까지 모두 JSON으로 함께 넘어가기 때문에 보안 문제가 발생할 수 있다. 엔티티 측에서 컬럼에 @JsonIgnore를 사용해 JSON 전달을 막을 수는 있지만 이는 엔티티가 API 스펙에 의존성을 갖게 되므로 좋지 않다. 유지보수가 복잡해질 뿐 아니라 다른 API에 대해서는 그때그때 또 변경을 해줘야 하는 쓸 데 없는 번거로움이 발생한다. 3. 엔티티를 그대로 넘겨줄 경우, 엔티티가 가진 정보 외의 것들은 넘겨주지 못한다. -> DTO를 사용하면 엔티티의 정보 외에 추가적으로 필요한 정보도 함께 넘겨줄 수 있다. ************************************************** 이 정도가 될 것 같습니다만.. 제가 놓친 내용이 있을까요??
안녕하세요! HTTP강의듣고 이번에 JPA강의도 듣네요 명강의 감사합니다. 계속 고민하다 궁금한게 생겨서 여쭤봅니다. Post Reply 관계에서 N : 1 그래프 객체가 Reply에있는 상태에서 강의에서 공부한대로, 혼자공부한대로 최대한 단방향으로 설정하려합니다. 근데 항상 고민이 됬던 것은 Reply를 save할 때, 해당 Post를 갖고와서. 즉, url : post/1/reply - @PostMapping 으로 Reply를 만들고자할때 Service단에서 ( 여기서 코딩하는거라 오타가있을수도...) @Transactional public void saveReply(ReplyServiceDto serviceDto){ // 우선적으로 post : 1 을 불러와서 Post post = postRepository.find(serviceDto.getPostId); Reply reply = Reply.builder() .postId(post) .content(serviceDto.getContent) build(); replyRepository(reply); } 이런식으로 하면 항상 reply를 save할때 post를 갖고와야한다는 불편함이있습니다. 그리고 url또한 post/1/reply 가아닌 reply 로 할때는 어떻게해야하는지... 그래서 찾아본 결과 1. Reply 쪽에 private Long postId; 딱 이것만 설정해놓고 따로 Post는 설정안하는 방법이 있더라구요. 물론 이게 무결성에 안좋다는건 알고있습니다. 2. 반대로 그냥 Post post 부분을 referencedColumn , DynamicInsert를 통해 null 로 넣을수도있다는... ( 이부분을 계속 시도는해보지만 FK로 안되는중 ) 너무 길었습니다. 결론은 그겁니다. 어떻게 해야 DB에 가는 성능상의 이슈를 풀수있는가. reply를 저장할때마다 post를 꼭 불러와야하는가. 이것에 대한 문제입니다. 그리고 한 post에 여러 reply가 달린 프로젝트에선 양방향은 또 어떨지... 궁금합니다 항상 좋은강의 감사합니다.
안녕하세요. 김영한님:) 영한님의 JPA강의를 들으면서 실무에 바로바로 적용하는 중에 있는데요. 1. 엔티티에 setter를 사용 하지 않고 수정 및 데이터 저장시 entity내에 메소드를 만들어서 사용하는걸 권장해주셨는데, 큰 덩어리(?)에 entity에서 하나의 컬럼만 수정이 될 때도 말씀주신 메소드를 만들어서 사용 하는게 좋을까요? 2. prodQty = prodQty + 1 이렇게 바로 DB 컬럼만으로 update가 가능한것도 select한 것을 param으로 넘겨서 메소드로 만들어서 하는게 좋은걸까요? 개인적으로 repository내에 @Modifying을 이용해서 만드는게 더 가식적인고, 한개의 업데이트를 사용하기에도 더 편리해보인다는 라는 생각도 들어서요(물론 @Modifying은 벌크성 update에 주로 이용한다고 하셨지만...) JPA에 익숙하지 않는 습성(?)때문에 그렇게 느껴지는 부분일까요? 더티체킹이라는 JPA는 장점을 살리지 못한 생각일까요? 강의 들을때 이해가 퐉퐉! 되었는데 막상 실무에선 작은것도 많은 고민을 하게 되네요 ㅠㅠ 감사합니다.
1. id 값이getmapping으로 넘어오는 것도 관계가 궁급합니다. itemList.html에도 특정하게 id값을 넘겨달라 라고 보이는 문구도 없어보이는데 id값만 인자로 지정해서 넘어오는 것도 궁금합니다. 이어지는 부분이 없다고생각하는데 이런건 requestparam으로 받는게 맞지 않을까요? public String updateItemForm ( @PathVariable ( "itemId" ) Long id , Model model){ Book one = (Book) itemService .findOne(id) ; 선생님 이부분에서 items/createItemForm 에서 정보를 입력받으면 submit버튼을 누르는 순간 postmapping 으로 값들이 담겨져서 밑의 코드처럼 bookform 형식의 값이담긴 form이 넘어와지게 된다고 하셨는데 booform의 클래스의 필드이름들이 html(createItemForm )태그에 타임리프 문법에 있는 name, id, isbn 등등 필드 이름과 매칭이 돼서 넘어오는줄 알았는데 bookform클래스의 필드 이름이 다르게 바껴도 값이 넘어오더라고요.... 어떻게 매칭이 되고 어떤 관계가 있어서 bookform에 잘 맞춰서 넘어오게되는지 궁금합니다. @PostMapping ( "/items/new" ) public String create (BookForm form){ Book book = new Book() ; book.setName(form.getName()) ; book.setPrice(form.getPrice()) ; book.setStockQuantity(form.getStockQuantity()) ; book.setAuthor(form.getAuthor()) ; book.setIsbn(form.getIsbn()) ; itemService .saveItem(book) ; return "redirect:/" ; }
안녕하세요! 복습하다가 혼동되는 부분이 있어서 질문합니다! 15:57초 그림에서 질문입니다. Member member = em.getReference(Member.class, id); 이 시점에서 프록시 객체는 영속성 컨텍스트에 있는 건가요? 저 시점에 일단 프록시 객체가 영속성 컨텍스트에 있다가 getName이 호출되면 영속성 컨텍스트에 초기화 요청을 하게 되는 것으로 이해가 되는데 잘 이해한게 맞나요??
안녕하세요 ㅎㅎㅎ 항상 강의 잘 듣고 있습니다. 감사합니다. HTTP 강의 수강 이후에 RFC문서도 좀 보면서 웹에 빠져들고 있어서 Spring 공부도 하고 있습니다....! 다름이 아니라 순수 JDBC강좌 10분 20초경에 나오는 rs.next()의 의미가 무엇인지 잘 모르겠습니다 ㅠㅠ conn으로 연결을 실행하고 pstmt로 sql문을 전송 Generated ID KEY를 받아옴 rs로 Generated된 key값을 받아옴 까진 이해가 되는데 rs.next()가 의미하는 바가 무엇인지 모르겠습니다. 그래서 뒤에부터 값을 설정한다는게 어떻게 flow가 흘러가는건지 잘 이해가 안가는데 부가 설명을 부탁드려도 될까요 ㅠㅠ 추가로) getLong, getString, setLong ---- 등등의 메소드 이름에서 뒤에 붙는것들은 (Long, String...) DB에서 생성된 Column의 Attribute라고 봐도 될까요? 감사합니다.
안녕하세요. 기본적으로 게시판을 만들 경우 LOB를 사용하게 됩니다. 그럼 Lazy를 사용하게 되는데 경험상 어떤 구조가 좋은지 알고 싶습니다. 지금까지는 @ElementCollection을 만들어서 Unique를 주어서 one to one 처럼사용을 하였습니다. 자구 눈에 거슬려서 리팩토링을 하고 싶은데 하나의 테이블에서 @Basic를 사용하는 것이 좋은지 아니면 서브 테이블을 만들고 @OneToOne 관계를 사용하는것이 좋은지 노하우를 공유하고 싶습니다. @OneToOne에서는 단방향을 사용하고 싶고 서브 테이블의 PK 컬럼은 PK이면서 FK로 설정하고 싶습니다. (즉, 메인 테이블의 PK값을 사용) 그럼, 수고하세요.
안녕하세요. JPA 강의를 듣고 연관관계부분 실습해보다 질문드립니다. 다대다 관계는 실무에서 사용하지 않는게 좋다고 하셔서 직접 중간테이블을 만들고 카테고리테이블, 카테고리_아이템 테이블, 아이템 테이블을 만들어 아래와 같이 구현해보려 했습니다. 간단히 엔티티 코드를 요약해보았습니다. 카테고리를 눌렀을 때 해당 카테고리에 Item 리스트를 불러오고 각각의 Item들에서도 카테고리 정보를 노출하고싶은데 어떻게 해야 할지 감이 잡히질 않습니다. categoryItem의 Repository에서 쿼리문으로 조인하여 가져오는것은 매우 복잡할것같고 Item이나 Category의 엔티티 안에 메서드를 만들어서 가져와보려했는데 제 기본기가 부족해서 그런지 도저히 방법이 떠오르질 않네요. 어떤식으로 구현을 해야좋을지 대략적인 흐름을 간단히 조언해주시면 감사하겠습니다.
안녕하세요! 좋은 강의 정말 잘 듣고있습니다 감사합니다! 다름이 아니라, 며칠 전 영한님이 찍으신 HTTP 강의를 들었습니다 해당 강의에서 PUT 과 PATCH 사용의 차이점에 대해서도 설명해 주셨는데 결론적으로 Member 리소스의 이름만 변경하는 현재 로직상 PUT 요청보다 PATCH 요청이 HTTP 스팩을 잘 맞춘(?) 설계라고 생각되는데 맞을까요? 또 저는 이렇게 스팩을 정확하게 맞춘 경우에는 (엔티티 데이터 수정시 변경감지를 사용하는 것이 권장되지만) 수정을 할 때 PUT 요청으로 수정이 오는경우, detached 된 엔티티 데이터를 수정하고 merge를 사용하여 업데이트를 하는 방법도 고려할 수 있겠다고 생각했습니다. 그리고 동시에 또 스팩에 너무 발이 묶이는 것도 결국은 비효율적이라는 생각도 들면서 머리가 복잡해지네요 결론적으로, 실무에서는 PUT 과 PATCH 를 HTTP 스팩에 정확히 맞추어 사용하기 보다, PUT으로 통일하여 사용함으로써 개발의 효율을 높이는 경우가 많을까요? 구글링을 하다보니 그런 글들을 본 기억이 있는것 같은데 영한님의 생각도 궁금하네요! 다시 한번 좋은 강의 감사합니다! :)
좋은강의 감사드립니다! 처음으로 DB 를 사용함에 있어서 흥미를 가지게 되었네요!! 다름이 아니라 궁금한 점이 생겼는데 스프링 컨테이너가 MemberRepositoryImpl 을 사용하게 되는 것 같은데 컨테이너에 어떻게 해당 클래스가 빈으로 등록되는지 감이 잘 오지 않습니다. ㅠㅠ ****RepositryImpl 이라는 규약을 두고 해당 클래스를 빈으로 등록시켜주는 매커니즘일까요??
제가 이해한 바로는 Team team = new Team() ; team.setName( "TeamA" ) ; entityManager.persist(team) ; Member member = new Member() ; member.setUsername( "member1" ) ; member.setTeam(team) ; entityManager.persist(member) ; // team.getMembers().add(member); // entityManager.flush(); // entityManager.clear(); Team findTeam = entityManager.find(Team. class, team.getId()) ; List<Member> members = findTeam.getMembers() ; for (Member m : members) { System. out .println( "member = " + m.getUsername()) ; } (위의 코드는 강의에서 작성해주셨던 코드와 동일합니다) 제가 이해한 바로는 "flush, clear를 하지 않음으로써 그 상태의 영속성 컨텍스트에 등록된 Team과 Member 기준으로 members의 값을 가져오기 때문에 System. out .println( "member = " + m.getUsername()) ; 위 코드에 대한 출력이 되지 않는것으로 이해했습니다 ㅠㅠ!" 제가 이해한 바가 맞을까요 센세...? 이게 만약 맞다면 "추가적으로 " flush 와 clear를 하는 과정에서 "원래 commit 시점에 쿼리가 발생하여 1차 캐시 혹은 DB에 값을 적재하는것이 정상이지만 flush 를 사용하는 시점에 해당과정을 즉시 실행하여 데이터를 적재시키고 다음으로 clear를 함으로써 비영속상태로 만들어 getMembers(); 시에 JOIN쿼리를 발생시켜 members와 관련된 데이터를 반영시킨다고 이해하였습니다." 마지막으로 "getMembers를 하는 과정에서 Member들을 가져오는 join 쿼리를 날리는 과정은 무조건 비영속 상태이어야만 하는지가 궁금합니다." 질문이 많았죠 센세... 항상 넘나 재밌게 잘 보고 있습니다~!!! JPA를 실무에서 사용한적이 없어서... 이런저런 궁금한점이 참 많습니당... 항상 감사합니다!!!!!
자동 생성(@GeneratedValue)에서 DB를 ORACLE로 사용시 SEQUENCE로 안하고 AUTO로하면 방언에 따라 기본값이 자동 지정 되긴하는데 그럴경우 SEQUENCE 생성시 의미없는? 이름이 생성돼 DB 운영/관리 측면에서는 관리가 어려워 잘 사용하지 않을 것 같은데 어떤 경우에 사용하는지요? AUTO로하면 DB에 종속적이지 않은 장점이 있으나 일반적으로 운영하는 DB에 맞춰서 예를 들어 ORACLE일 경우 미리 스크립트로 MEMBER_SEQ를 생성하고 @SequenceGenerator로 맵핑해서 개발될것 같은데 맞을지요?
안녕하세요. 좋은 강의 덕분에 업무에 많은 도움이 되고 있습니다. 감사합니다. ^^ 다름이아니라, 강의 예제에 나온 repository에서는 보통 한 종류의 엔터티를 반환하는데요. 예를 들면 OrderRepository 에서는 반환값이 Order, List<Order> ItemRepository에서는 Item, List<Item>을 반환합니다. 하지만 OrderRepository에서 Item, List<Item>을 반환하는 경우는 없더군요. 그런데 여러 테이블간에 조인을 거쳐 최종 조회되는 엔터티가 해당 Repository의 엔터티가 아닌 경우, 이럴 때는 어떻게 하는 게 좋을까요? 예를 들면 엔터티 간의 관계가 아래와 같을 때 Order : OrderItem = 1 : N Item : OrderItem = 1 : N Item : ItemCategory = 1 : N Category : ItemCategory = 1 : N Order Id = 100인 Item의 List<Category>를 조회하고자 하는 경우 OrderRepository에서 각 엔터티들의 조인을 거쳐 List<Category>를 반환하는 게 좋을까요? 아니면 각각의 Repository에서 필요한 엔터티를 반환받아 최종적으로 List<Category>를 찾는 것이 좋은가요? 아니면 제 3의 별도의 Repository를 만드는 것이 좋을까요? 여러개의 테이블을 조인하여 한 번의 쿼리로 데이터를 조회하는 것이 더 성능상 이점이 있을 것 같은데요 반면 Repository에서 서로 다른 유형의 엔터티를 반환해도 유지보수에 문제가 없을지 걱정이 듭니다. 김영한님의 조언 부탁드립니다.
@Entity @DiscriminatorValue("B") @Getter @Setter public class Book extends Item { private String author; private String isbn; public static Book createBook(String name, int price, int stockQuantity, String author, String isbn) { Book book = new Book(); book.setName(name); book.setPrice(price); book.setStockQuantity(stockQuantity); book.setAuthor(author); book.setIsbn(isbn); return book; } } setter들을 최대한 줄이고자 위와 같이 정적 팩토리 메서드를 작성했습니다. 그런데 결국 Book 객체를 만들기 위해선 createBook안에 setter가 필요하게 되더라구요 그래서 setter을 닫으면서 정적 팩토리 메서드를 작성하기 위한 두가지 방법이 생각났는데요 첫 번째는 생성자를 protected으로 만들어놓고 정적 팩토리 메서드에서 setter가 아닌 생성자로 객체를 생성하는 방법이고 두 번째로 setter의 접근 권한을 private으로 설정하여 정적 팩토리 메서드 안에서만 setter을 사용하도록 허가하는 방법입니다. 두 방법 중에 무엇이 더 사용하기 적합한지, 아니면 그 외에 다른 방법엔 어떤 것이 있는지 궁금합니다. /// 추가 강의를 진행하다보니 두 방법 모두 setter의 부재로 변경 감지 방법을 사용하지 못한다는 문제점이 있는 것 같습니다.. 변경 감지 강의에서의 영한님 말씀처럼 setter 대신에 비즈니스 메서드를 만들어서 변경 감지 등의 로직에서 사용하고, 정적 팩토리 메서드에서는 첫 번째 방법처럼 생성자로 객체를 생성하는 방법을 사용하려고 하는데 옳은 방법일까요?
[개정판 2023-11-27] Spring Boot 3.x 를 이용한 RESTful Web Services 개발
@PostMapping("/users") public ResponseEntity<User> createUser(@RequestBody User user) { User savedUser = service.save(user); URI location = ServletUriComponentsBuilder.fromCurrentRequest() .path("/{id}") .buildAndExpand(savedUser.getId()) .toUri(); return ResponseEntity.created(location).build(); } 에서 URI 객체 location을 이용해 return 값을 아래와 같이 설정하는데요 return ResponseEntity.created(location).build() 질문1 uri를 만들때 사용한 ServletUriComponentsBuilder 클래스는 무슨 클래스인지 URI location 객체는 url 주소를 말하는게 맞나여? 질문2 ResponseEntity는 무슨 객체인지 뭘 created하는 건지 location인자는 왜 넘기는건지 뭘 build 해서 리턴하는건지 잘이해가 안가요 좀더 자세하게 설명해주시면 감사여 질문3 그리고 response body에 success: true 같은걸 응답하도록 하는게 맞지 않나여? 그러려면 어떻게 해야 되나여? 질문4 그리고 user 한명 입력인데 url을 왜 /users로 하신건가요? 질문5 그리고 dao 계층과 서비스 계층을 분리할수도 있는거죠?
안녕하세요 강사님. 테스트를 실행했을 때 강사님께서는 DDL문이 한 줄마다 줄바꿈되어 나오는데, 저는 한줄로 쭉 연결되어 나옵니다. 사실 별 건 아니지만 가독성이 떨어져서 찾아보기가 약간 힘들게 느껴져서 혹시 설정하는 방법이 있는지 여쭤봅니다 ㅠㅠ 현재 H2 버전을 1.4.199를 깔아 사용하고 있는데 이거때문일까요?