안녕하세요 강사님, 질문 드리겠습니다. 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:/" ; }
1. 주 기억장치(=메모리)가 전원을 끄면 데이터가 사라지잖아요. 그래서 전원을 꺼도 데이터가 저장되는 보조기억장치로 보완한다는데 CPU(연산을 하는 부분)는 보조기억장치에 직접 접근할 수 없어서 주 기억장치랑만 왔다갔다한다는데 그러면 CPU는 전원이 켜져있을 때만 작동하고, 주 기억장치랑만 왔다갔다하므로 얘도 전원을 키면 주 기억장치 처럼 데이터가 싹 사라지나요?? 맞다면 전원이 꺼져있는 상태라면 운영체제나 그 외 데이터 이런것도 싹 없는 상태이고 전원을 킬때마다 보조기억장치에서 꺼내쓰는거겠네요? 맞나요? 2. 근데 강의 도중 메모리(=주 기억장치)가 저장을 하는 기능이라고 말씀하신 것 같은데 얘는 전원을 끄면 데이터가 싹 사라지잖아요...? 근데 보조 기억장치는 안사라지잖아요. 그럼 데이터를 저장하는 기능하는 것은 보조 기억장치 아닌가요? 그리고 또 정리하자면 저장하는 기능= 보조기억장치 주기억장치는 그냥 속도를 빠르게하는 역할 및 CPU에 정보를 전달하는 역할 인건가요? 3. RAM은 주 기억장치에 속한 장치인가요? 임의 접근이 가능한 메모리 라고 하셨는데 정확히 무엇을 접근하는건지 모르겠어요. 4. 그래픽카드는 그냥 계산량을 늘리는 역할을 하는건가요? 5. FDD와 자기테이프의 역할은 뭔가요? (그냥 어원만 알려주셔서..ㅠㅠ) 6. CPU에서 레지스터가 메모리처럼 저장을 하는 부분이라는 말인가요?.. 그니까 연산말고도 저장도 한다는 말인가요? 그 저장되는건 메모리에서 얻은 정보들? 죄송해요 처음 공부하는거라 궁금한 것이 산더미네요ㅠㅠ 뭔가 자세히 설명은 안해주신 것 같은데 지금 수준에서는 필요 없어서 그런건가요??
안녕하세요! 복습하다가 혼동되는 부분이 있어서 질문합니다! 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를 깔아 사용하고 있는데 이거때문일까요?
수정하려는 엔티티의 키가 10이라고 했을 때, 1. DB에는 아직 수정 전인 엔티티가 들어있음. 이건 영속. 2. Book book = new Book(); 한 후 BookForm의 정보들로 set해줌. 이건 그냥 함수 내에서 new로 만들었을 뿐이니 JPA가 관리하고 있지 않음. 하지만 이 book의 키값인 10은 디비에 저장되어있음. 그래서 이건 준영속. (즉, 10이란 키값을 갖는 엔티티에 대해 영속 엔티티와 준영속 엔티티가 동시에 존재하는 상황.) 3. 여기서 준영속 엔티티 book의 key값으로 검색하여 영속 엔티티 findItem을 가져오고 값을 덮어씌움. 4. 모든 작업 이후에도 book은 여전히 준영속 엔티티이므로 더이상 사용하지 않는 것이 좋음. + 5. 결국 더티체킹 메서드를 직접 만들든, em.merge()를 사용하든 내부적으로는 전부 더티체킹을 사용하여 update하는 것임. (null 업데이트 문제는 제쳐두고) 이런 흐름이 맞나요? merge의 과정보다도 merge 실행 전에 같은 키값의 영속,준영속이 동시에 존재하는 부분이 맞는지가 더 모르겠고 궁금하네요. 감사합니다! ㅡㅡㅡㅡㅡㅡㅡㅡ 19:53에서 맨 윗줄에 '재'한적이다 오타 제보드립니다. ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ (추가질문) setter를 경계하라는 말씀을 다시 한 번 해주셨는데요. "change()처럼 엔티티에 변경 메서드를 따로 만들면 추적이 쉽다" 라는 말씀에서 이해가 좀 안 가는 부분이 있습니다. setter로 변경을 하더라도 그 setter명으로 역추적하면 변경지점이 어디인지 알 수 있는 것 아닌가요? 혹시 여러 엔티티에서 같은 이름의 멤버변수를 가지는 경우엔 setter이름도 같아져서 찾기 어렵다는 말씀이신가요?