안녕하세요 토비님 제가 프로젝트를 진행하면서 도저히 모르는 부분이 있습니다. 강의와 관련이 없는 질문이지만, 간절한 마음으로 질문해봅니다 Service 계층은 상태검증과 행위 검증에 대한 고민이 있습니다. 특히, 객체의 책임과 테스트 범위에 대한 관점에 대한 차이 때문에 고민이 있습니다. 우선, 코드를 보여드리겠습니다. CommunityCommandService.updateCommunity는 커뮤니티의 소개란과 해시태그를 업데이트하는 부분입니다. //CommunityCommandService.java public void updateCommunity(Long userId, Long communityId, String description, List<String> newTags) { Community community = communityRepository.findCommunityById(communityId); memberQueryService.getManager(userId, communityId); community.updateCommunity(description, newTags); } 해당 코드는 communityId로 community를 가져오고, userId / communityId로 요청한 유저가 메니저인지 확인합니다. 그 후, community 객체에게 update를 위임합니다. 그러면 community 객체는 내부 상태값을 변경합니다. 여기서, 상태 검증인지 행위 검증인지에 따라 테스트가 달라집니다. public class CommunityCommandServiceTest { @Test void 상태검증_테스트() { Community community = new Community("dummy Intro", List.of("dummy tag")); given(communityRepository.findById(any)).willReturn(community); communityCommandService.updateCommunity(1L, "new intro", List.of("new tag")); assertThat(community.getIntroduce).isEqualTo("new intro"); assertThat(community.getTags).containsExactly("new tag"); } @Test void 행위검증_테스트() { Community community = mock(Community.class); given(communityRepository.findById(any)).willReturn(community); communityCommandService.updateCommunity(1L, "new intro", List.of("new tag")); then(community).should(times(1)).update("new intro", List.of("new tag")) } } 상태검증_테스트 의 검증 부분을 보면, 위임한 결과에 대해서 테스트를 진행하고 있습니다. Community 클래스의 update를 또 테스트하는 것 같은 느낌이 있습니다. 즉, 서비스 계층의 테스트 영역을 넘어서는 것인지 의문입니다. 반면, 행위검증_테스트 는 community.update가 호출하면서 위임했는지에 대해서만 테스트합니다. 하지만, 내부 로직에 하드코딩 되어있는 듯 합니다. 사실 저는 상태검증을 더 선호합니다. 하지만 상태 검증이 객체지향스러운지 잘 모르겠습니다. 어느정도 감수해야하는 것 일까요? 정리하자면, 상태검증 CommunityCommandService.updateCommunity로 변경된 상태를 테스트 개인적으로 선호하는 방식. 하지만, 상태를 테스트하기 때문에 객체지향의 관점에서 맞는지 확신이 없다. 테스트코드는 이 부분을 감수하는 것인지? 행위검증 협력한 객체의 행위에 대한 테스트 객체지향의 관점에서 위임이 잘 이루어졌는지 테스트하는게 자연스럽다고 생각 질문하자면, 서비스계층에서 Community.update()로 커뮤니티 내부 값에 대한 변경을 요청하였습니다. 객체지향에서는 객체들이 서로 책임을 위임하며 상호작용하는 것이기 때문에 Service 계층에서는 위임이 되었는지 호출 여부만 판단하는게 적절한지, 아니면, Service 계층에서 위임한 그 결과 Community의 내부값을 바꾼게 적절한 테스트인지.. 만약 이 방법이 맞다면 객체지향스럽다고 말할 수 있는지..? 어떤 방식이 적절한지 잘 모르겠습니다. 이론적인 부분과 실제 테스트에 대한 괴리때문에 발생하는 문제 같습니다. 긴 질문 읽어주셔서 감사드립니다!
main/resources/application.yml 을 따라가게 되는 건가요 ? 자바에서는 디비를 사용하고 싶고, test에서만 메모리 DB를 사용하고 싶다면 아무런 내용이 없는 application.yml를 test/resources 아래 생성만 하면 되는 건가요 ?
Binder.get(evn).bind("",bean.getClass()); Binder.get(evn).bindOrCreate("",bean.getClass()); 해당 클래스 타입으로 Environment 빈 정보를 읽어서 값을 바인딩해주고 null이 될경우 새 빈 오브젝트로 만들어준다고 하셨는데 1.여기서 바인딩 결과가 null이 될경우라는게 무슨 의 미인지 잘 이해가 안갑니다. 그리고 저희가 만든 @MyConfigurationProperties 어노테이션에는 @Component가 메타어노테이션이 들어있고 실제 스프링이 사용하는 @ConfigurationProperties 에는 @Component가 없더라구요 @EnableConfigurationProperties에서는 직접 레지스트리에 저장하는거같은데 제가 이해한게 맞을까요?
//==연관관계 메서드==// 가 주석으로 달린 메서드들이 연관관계에 있는 객체에 값을 넣어줄 때 반대편에도 자동으로 값을 넣어주는 역할을 하는 메서드가 맞나요? 이렇게 따로 메서드를 구현해주는 이유가 있나요? CASCADE 옵션을 사용하면 어차피 알아서 다 해주지 않나요 ?? 기본편에서 설명해주셨다고 했는데 어떤 강의를 다시 수강해보면 될까요 ? 지연로딩에서 ***ToMany의 디폴트는 Lazy고, ***ToOne의 디폴트는 Eager인 이유가 ***ToMany같은 경우 하나만 불러와도 기본적으로 수많은 값이 따라오기 때문에 스프링 측에서 Lazy로 해놓았고, (Member의 Order을 조회하면 수많은 Order이 자동으로 따라오기 때문에 디폴트가 Lazy) ***ToOne의 경우 객체 하나만 조회하면 쿼리문을 하나밖에 더 날리지 않기 때문에 Eager로 해놓았지만, 실무에서는 테이블 전체를 조회할 때가 있기 때문에 디폴트값인 Eager를 쓰지 않고 Lazy를 사용한다 이게 맞을까요? (Order하나만 조회하면 Member 1개만 따라오기때문에 디폴트가 Eager지만, select o from Order o 와 같은 쿼리문으로 Order를 전체 조회했을 때는 Order에 따른 멤버가 전부 따라오므로 그냥 Lazy 옵션을 준다) 이렇게 이해했는데 맞을까요?
기본편에서 관계에 따른 연관관계 설정하는 방법은 공부했는데, 위와 같은 기능 목록을 보고 기능목록을 보고 위와 같이 어떤 도메인을 써야 할지, 도메인끼리 1:N인지, N:N인지, 1:1인지 단방향인지, 양방향인지(실무에서는 N:N과 양방향은 지양하라고 하셨지만) 등 도메인 모델과 테이블 설계가 어렵다면 무엇을 참고하는 것이 좋을까요 ? ( 영한님이 분석해 놓은 회원 엔티티와 회원 테이블을 보고 코드를 작성할 순 있는데 스스로 회원 엔티티와 테이블을 분석하기가 너무 어렵습니다.) Category 클래스 안에서 스스로 일대다 관계를 맺고 있는데 이것은 무엇을 위해 만든건가요 ?
@MyAutoConfiguration class SampleBeanConfigClass { @Value("${name}") String name; @Bean SampleBean sampleBean(){ System.out.println("sampleBean name = "+name); } } @Configuration class UserConfigClass { @Value("${name}") String name; public UserConfigClass(){ System.out.println("UserConfigClass name = "+name); } @Bean public UserConfigBean userConfigBean(){ System.out.println("UserConfigBean name = "+name); return new UserConfigBean(); } } //application.properties name = spring sampleBean name = ${name} UserConfigClass name = null UserConfigBean name = spring 아직 PropertySourcesPlaceholderConfigurer는 MyAutoConfiguration으로 등록하지 않은 상태입니다. 그런데 @Value의 위치에 따라 null, spring, ${name} 이렇게 각각 나오더라구요 이렇게 value의 위치마다 다르게 작동하는 이유가 있을까요..?
3:58초 경에 findMember.setAge(20)으로 결과가 바뀌면 영속성 컨텍스트에 관리가 되는 거고, 안바뀌면 관리가 되지 않는다라는데 이유가 뭔가요 ? JPA가 DB의 데이터에 접근하고 수정하고 작성,삭제 할 수 있다 = 영속성 컨텍스트가 관리한다 같은 말일까요 ?
@OneToMany @JoinColumn(name = "MEMBER_ID") private Order order; @OneToMany @JoinColumn(name = "MEMBER_ID") private List<Order> orders; 두개가 무슨 차이가 있는지 궁금합니다. 일대다 관계라면 컬렉션을 안써도 되지 않나요?
public static void main(String[] args) { Integer a = Integer.valueOf(10); Integer b = a; a = 20; System.out.println("a = " + a); System.out.println("b = " + b); } =======출력값 ======== a = 20 b = 10 이렇게 하면 값이 변경되는데 PPT에서 변경X라는 말이 무슨 말일까요 ?
Member와 Team을 예로 생각했을 때 즉시로딩 -> Member만 필요해도 Team과 쿼리문이 같이 나감 지연로딩 -> Member만 불러오고, Team은 프록시 객체로 불러와서, Team이 필요할 때 영속성 컨텍스트에 연결을 요청하고 요청 쿼리문이 나가는 것이 맞나요 ? Member.getTeam().getName()을 1번 수행한다고 했을 때 즉시로딩 (Member + Team 쿼리문 같이)의 성능이 지연로딩 ( Member 쿼리문 따로, Team 쿼리문 따로)보다는 좋지만 강의에서 나온 여러가지 문제로 웬만해서는 지연로딩을 사용하는 것이 더 좋다. 이것이 맞나요?
<BookForm> @Getter @Setter public class BookForm { private Long id; private String name; private int price; private int stockQuantity; private String author; private String isbn; } <Controller> @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:/"; } <Service> @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class ItemService { private final ItemRepository itemRepository; public void saveItem(Item item){ itemRepository.save(item); } public Item findOne(Long itemId){ return itemRepository.findOne(itemId); } public List<Item> findAll(){ return itemRepository.findAll(); } } <Repository> @Repository @RequiredArgsConstructor @Slf4j public class ItemRepository { private final EntityManager em; public void save(Item item){ if(item.getId() == null){ log.info("item={}", item); em.persist(item); //신규등록 }else{ em.merge(item); } } public Item findOne(Long id){ return em.find(Item.class, id); } public List<Item> findAll(){ return em.createQuery("select i from Item i", Item.class) .getResultList(); } } <Log> 2023-04-21 15:08:16.380 INFO 7536 --- [nio-8080-exec-2] j.jpashop2.repository.ItemRepository : item=Book(author=qwe, isbn=1234) <Test> 로그에 찍히는거 보면 값이 다 전달이 되고 테스트 코드에서도 성공으로 떠서 저장이 되야 될거 같은데 h2 db에 가면 값이 저장이 안되어 있네요 아예 디비랑 연동이 안되나 싶었는데 멤버는 정상적으로 회원가입이 되는데 아이템만 저장이 안되네요 뭐가 문제일까요... ddl-auto 는 create로 되어있구요 h2 db 버전은 1.4.2 입니다
@ComponentScan을 하게되면 해당 패키지 이하로 컴포넌트가 어노테이션으로 있으면 빈으로 등록해주고 @Import를 하면 @Configuration class를 넣으면 해당 구정 정보 클래스가 빈 등록이 되는데 제가 생각한 @ComponentScan과 @Import차이는 컴포넌트 스캔은 패키지나 클래스를 기점으로 쭉 클래스를 찾아 돌아다녀야하지만 자동 구성빈에서 사용되는 많은 빈들을 다 컴포넌트 스캔으로 사용하는거보다 필요한 구성 정보 클래스만 스프링 부트가 @Conditional로 판단해서 빈으로 등록할수 있다보니 컴포넌트 스캔보다 더 빠르고 구성 정보끼리 관계를 알수 있다고 생각이 되더라구요 제가 이해한게 맞는지 아니면 잘못 이해하고 있는지 추가로 설명해주실수 있으신가요..?
ORDERS와 ORDER_ITEM의 관계는 하나의 주문 안에, 주문된 여러가지 아이템(book,album,movie등)이 있을 수 있기 때문에 1:N인 것 이 맞나요 ? OrderItem과 Item의 관계가 잘 이해 가지 않아서 찾아보다가 https://www.inflearn.com/course/lecture?courseSlug=ORM-JPA-Basic&unitId=21699&category=questionDetail&tab=community&q=805804 이 답변을 보고 헷갈리는 부분이 있는데요. Book을 n번 주문할 수 있기 때문에 1:N이라고 하셨는데 이 말은 즉, Book이라는 아이템 안에서도 주문된 여러가지 책(일본 책, 독일 책, 한국책) 이 있을 수 있기 때문에 1:N이라는 말일까요? 어떤 Member가 어떤 주문을 했는지 Order를 참고하는 것과 어떤 Order가 무엇을 구매했는지 Order_Item을 참고하는 것이 같은 맥락이라고 생각하는데 전자는 잘못된 설계고 후자는 괜찮은 설계인 이유는 무엇일까요?
public class Team2 { @Id @GeneratedValue @Column(name = "TEAM_ID") private int id; private String name; @OneToMany @JoinColumn(name = "MEMBER_ID") private List<Member> members = new ArrayList<>(); } public class Member2 { @Id @GeneratedValue() private Long id; @Column(name = "name") private String username; @ManyToOne(mappedBy = "members") private Team team; } 강의를 듣다가 외래키를 Team에 두면 안되는 것인가? 하고 혼자 코드를 작성해보았는데요. @ManyToOne은 mappedBy옵션을 지원하지 않는 걸로 봐서 1:N에서 N쪽이 외래키가 있어야하고, 연관관계의 주인이 되어야하는 것 같은데 연관관계의 주인이 되는 외래키는 어떤 방식으로 설정해주나요 ?
안녕하세요. 토비님 덕분에 좋은 강의를 잘 보고 있습니다! 강의와는 다른 내용인데. 토비님은 REST API를 개발하실때 다양한 쿼리스트링 요청이 들어올때 값의 유무 체크를 어떻게 하시는지 궁금합니다. if문으로 null을 각각 확인하고 있긴 한데.. 코드도 길어지고 가독성도 안좋아 질문드립니다 ㅠㅠ public class User { private String name; private String phone; private String email; } @GetMapping("/v1") public void list(@ModelAttribute User user) { } :8080/v1?name=apple :8080/v1?name=apple&phone=01012345678 :8080/v1?phone=01012345678
안녕하세요 선생님 다름이 아니라 제가 작성한 코드가 정답처리가 되었는데 이 코드는 정답이 되면 안되는 코드가 아닌가 해서 여쭤보고 싶어서 질문글 남깁니다. 예를들면 첫번째 문제에서는 CBA 순서로 무조건 짜야하지만 제 코드에서 str1 은 꼭 순서를 지켜야 하는 값이고 str2는 C의 순서를 알 수 없는데 정답처리가 되어서 제가 작성한 코드가 제대로 정답이 맞는건지 궁금해서 질문글을 남깁니다!
둘 다 null을 허가하지 않는 다는 의미로 쓰이는 것 같은데 어떤 차이가 있는 건가요 ? @EnumType 애노테이션을 제거하고 멤버를 추가해도 @EnumType.Ordinal 옵션을 적용 한 것처럼 Integer로 컬럼을 만들고 user = 0, admin=1 으로 잘 저장하던데 @EnumType은 어떤 역할을 하는 것인가요? EnumType.String이 필수로 써야할만큼 EnuType.Ordinal은 안좋은 옵션같은데 Default가 String이 아니라 Ordinal인 거는 따로 무슨 이유가 있는 건가요 ?