묻고 답해요
160만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Practical Testing: 실용적인 테스트 가이드
프레젠테이션 레이어 테스트 코드 작성시 코틀린 non-nullable
안녕하세요. 강의 잘 보고 있습니다. 요청 객체를 테스트(ex. 신규상품을 등록할때 타입은 필수 값이다.) 할 때에 코틀린을 사용하는 경우 productType은 non-nullable 입니다. data class ProductCreateRequest( @field:NotNull(message = "상품 타입을 선택해주세요.") val type: ProductType, @field:NotNull(message = "판매 상태를 선택해주세요.") val sellingStatus: ProductSellingStatus, @field:NotBlank(message = "상품명을 입력해주세요.") val name: String, @field:Positive(message = "상품 가격을 입력해주세요.") val price: Int, ) 이런 경우 enum 타입은 bean Validation에서 NotNull이나 NotBlank에 대한 테스트는 불가능한 상태인데요. 이렇게되면 테스트 가능한 것들만 하는게 맞는건가요?
-
미해결Practical Testing: 실용적인 테스트 가이드
안녕하세요 강사님 컨트롤러 테스트 질문입니다!
좋은 강의를 찍어주셔서 너무 감사합니다.덕분에 테스트에 접근하는 방법을 터득할 수 있었습니다!컨트롤러 테스트는 Validation 역할이 핵심이라고 이해했는데,@Valid 어노테이션을 부착한 DTO 검증만 강의를 통해 배웠습니다. 궁금한게 있습니다.시큐리티를 적용했을 때 저는 컨트롤러 메서드 매개변수에 Authentication을 이용해서 SecurityContext 값을 사용하는데,이러한 SecurityContext도 검증을 해야 되는지,PathVariable에 대한 검증도 필요한가요?Param값 검증도 필요한지 궁금합니다.페이징했을 때 페이징 결과도 검증을 해야 될까요?Validation 영역이 아니라고 판단되어지는데, 강사님의 의견이 궁금합니다! 감사합니다.
-
미해결실전! 스프링부트 상품-주문 API 개발로 알아보는 TDD
리뷰반영 강의에서 shortcut ctrl + esc 는 어떻게 설정하나요?
이 화면에서 단축키 ctrl + esc 만 누르면 시작 화면이 자꾸 떠서 단축키 등록이 안돼요..
-
미해결Practical Testing: 실용적인 테스트 가이드
Request Dto에서 생성자 관련...
강의에서 RequestDto를 Builder 패턴으로 생성자를 만들어주셨는데 그렇게 생성하신 이유가 있을까요?? 코드에서 확인해보면 이 생성자를 사용하지 않는걸로 확인이 되는데 .. 제가 추측하기로는 그 이후에 확장성을 위해서?,,, 라고 추측을 해봤는데 다른 이유가 있을까요????
-
미해결Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트
List<Domain> -> List<Response> 변환을 Controller에서 하는 게 맞나요?
Domain -> Response 변환 코드를 Domain에 정의해두고Controller에서 Domain 메서드를 호출해서 Response를 변환하는게 맞나는 건 이해했습니다. 근데 실제 API에 해당 내용을 적용하려고 보니 Domain 단 건 조회보다는 List<Domain>을 반환하는 경우가 훨씬 많았습니다. 따라서 List<Domain>을 List<Response>로 변환해야 하는데 해당 작업을 for문이나 Stream으로 Controller로 처리하려니 Controller 코드도 지저분해지고 Controller가 하는 역할에 부합하지 않게 되는 것 같습니다.List<>를 변환할 때는 어디서 하는게 올바른 것인지 질문드립니다!
-
미해결실전! 스프링부트 상품-주문 API 개발로 알아보는 TDD
POJO
강사님이 생각하시는 POJO란 어떤 방법론인가요?? 시중에 나와있는 설명으로는 용어가 잘 와닿지 않아서요!!
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
todoData.map 화살표 함수 질문
[JSX Key 속성 이해하기] 파트에서는 todoData에 map 메서드를 사용할 때 this.todoData.map((value) => ( <div style={this.getStsyle()} key={value.id}> <input type="checkbox" defaultChecked={false} /> {value.title} <button style={this.btnStyle}>X</button> </div> ))로 return 없이 사용하셨는데 전 기존에는 this.todoData.map((value) => { return ( <div style={this.getStsyle()} key={value.id}> <input type="checkbox" defaultChecked={false} /> {value.title} <button style={this.btnStyle}>X</button> </div> ) })이렇게 사용했습니다 둘다 정상 동작하는데 차이점이 뭔지 궁금합니다
-
미해결Practical Testing: 실용적인 테스트 가이드
생성자 검증
안녕하세요 우빈님!빌더 패턴 사용 시 생성자 검증을 어떻게 하는 것이 좋을까에 대한 질문을 드리고 싶습니다..!변경 가능성이 있는 도메인 검증을 진행 할 때 빌더 패턴이 적용 된 private 생성자에서 검증을 진행하나요?저 또한 정적 팩토리 메서드를 즐겨썼었는데 Builder를 사용하고나니 어느 위치에서 검증을 하는게 좋을까에 대한 의문이 생기더라구요.기본적으로는 보통 정적 팩토리 메서드에서 생성자 검증을 진행하게 되면 도메인에서 검증을 위한 private 메서드가 전부 static이 되어야하는데 이게 옳은가? 라는 의문이 들기도 하고그렇다고 private한 빌더 쪽에 생성자 검증을 하려고 하니 외부 세계에 영향을 받는 가령 클라이언트로 부터 입력받은 시간이 현재 시간 이전 일 수 없습니다. 라는 테스트를 수행해야 할 때 외부로부터 계속해서 LocalDateTime.now() 를 전파받아서 구현을 해야하는데 이 필드를 생성자에 추가하는 것도 아닌 것 같고..이러한 고민 속에서 결국 생성자에서 진행됐어야 할 검증을 서비스 로직에서 도메인의 검증 메서드를 따로 호출하였는데 서비스 로직에서 검증 메서드를 호출하는 것 또한 좋은 방법은 아닌 것 같다는 생각이 들었습니다.이러한 상황에서 우빈님은 보통 어떤 방식을 택하시는지 궁금합니다 ㅎㅎ
-
해결됨Practical Testing: 실용적인 테스트 가이드
null 검증
안녕하세요! 우빈님. 테스트 강의에서 많은 인사이트를 얻고 갑니다! 작은 고민이 하나 있는데요! 우빈님은 어떻게 생각하시는지 궁금하여 여쭈어봅니다!바로 도메인 객체에서의 null 검증인데요!BeanValidation을 통해서 Presentation에서 검증을 하더라도, 도메인 단에서 또 null 검증을 해주어야 하는가에 대한 질문입니다. 팀원들과 팀플을 하다보면 @NotNull 을 이용하여 Presentation 단에서 검증이 될텐데, 도메인에서도 null 검증을 해주어야 하는가!? 에 대한 질문을 많이 받습니다. 개인적으로 저는 Presentation 단에서 검증이 되더라도 실제로 객체가 생성될 때 까지의 일련의 과정들 속에서 객체에 온전한 값이 들어가지 않을 것 같아 null 체크 또한 해주는 것이 안정적인 코드를 만들어 줄 수 있다고 생각하는 편인데요! 이러한 작업들이 그래도 비용이 들어가는 측면이라, 개발 속도에 영향을 미쳐서 그런지 선호하지 않는 분들도 종종 만났던 것 같습니다! 혹시 우빈님 생각은 어떤지 의견을 한 번 여쭈어보고 싶습니다
-
미해결Practical Testing: 실용적인 테스트 가이드
현재 시간에 의존하는 코드
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. 안녕하세요. 테스트 강의를 보고 실천(?)하고 있는 개발자입니다!최근 현재 시간에 대한 테스트를 짜던 도중, 현재 시간을 모킹하는 방법이 있다는 걸 알게 됐습니다. 우빈님 강의에서는 현재시간을 파라미터로 받는 방식을 통해 테스트를 짜셨는데, 두 가지 방법에 대해 어떻게 생각하시는지 의견이 궁금합니다!
-
미해결Practical Testing: 실용적인 테스트 가이드
OrderRepositoryTest에서 발생한 에러
package sample.cafekiosk.spring.domain.order; @ActiveProfiles("test") @DataJpaTest class OrderRepositoryTest { @Autowired private OrderRepository orderRepository; @Autowired private ProductRepository productRepository; @DisplayName("특정 주문 상태에 따라 주문을 조회한다") @Test void findOrdersBy() { //given Product product1 = createProduct("아메리카노", 3000, HANDMADE, SELLING); Product product2 = createProduct("카페라떼", 4000, HANDMADE, SELLING); Product product3 = createProduct("카푸치노", 5000, HANDMADE, SELLING); List<Product> products = List.of(product1, product2, product3); LocalDateTime startTime = LocalDateTime.of(2023, 10, 19, 0, 0); LocalDateTime orderTime = LocalDateTime.of(2023, 10, 19, 10, 0); LocalDateTime endTime = LocalDateTime.of(2023, 10, 20, 0, 0); Order completedOrder = createOrder(orderTime, PAYMENT_COMPLETED, products); Order canceledOrder = createOrder(orderTime, CANCELED, products); // when List<Order> orders = orderRepository.findOrdersBy(startTime, endTime, PAYMENT_COMPLETED); // then assertThat(orders).hasSize(1) .extracting("id", "orderStatus", "totalPrice", "registeredDateTime") .containsExactlyInAnyOrder( tuple(1L, PAYMENT_COMPLETED, 12000, orderTime) ); } private Product createProduct(String name, int price, ProductType productType, ProductSellingStatus productSellingStatus) { Product product = Product.builder() .name(name) .price(price) .type(productType) .sellingStatus(productSellingStatus) .build(); return productRepository.save(product); } private Order createOrder(LocalDateTime now, OrderStatus orderStatus, List<Product> products) { Order order = Order.builder() .products(products) .orderStatus(orderStatus) .registeredDateTime(now) .build(); return orderRepository.save(order); } @DisplayName("찾고자 하는 시간 안에 있는 주문을 조회한다") @Test void findOrdersBy2() { //given Product product1 = createProduct("아메리카노", 3000, HANDMADE, SELLING); Product product2 = createProduct("카페라떼", 4000, HANDMADE, SELLING); Product product3 = createProduct("카푸치노", 5000, HANDMADE, SELLING); List<Product> products = List.of(product1, product2, product3); LocalDateTime startTime = LocalDateTime.of(2023, 10, 19, 0, 0); LocalDateTime orderTime = LocalDateTime.of(2023, 10, 19, 10, 0); LocalDateTime endTime = LocalDateTime.of(2023, 10, 20, 0, 0); LocalDateTime overTime = LocalDateTime.of(2023, 10, 20, 10, 0); Order completedOrder = createOrder(orderTime, PAYMENT_COMPLETED, products); Order overTimeOrder = createOrder(overTime, PAYMENT_COMPLETED, products); // when List<Order> orders = orderRepository.findOrdersBy(startTime, endTime, PAYMENT_COMPLETED); // then assertThat(orders).hasSize(1) .extracting("id", "orderStatus", "totalPrice", "registeredDateTime") .containsExactlyInAnyOrder( tuple(1L, PAYMENT_COMPLETED, 12000, orderTime) ); } }각각 Test 수행할 땐 정상적으로 잘 동작했습니다. 하지만, 같이 Test을 수행하는 경우 findOrdersBy()에서 아래와 같은 에러가 발생하고 있습니다2023-10-20 23:12:40.519 INFO 8704 --- [ main] o.s.t.c.transaction.TransactionContext : Began transaction (1) for test context [DefaultTestContext@1a6c1270 testClass = OrderRepositoryTest, testInstance = sample.cafekiosk.spring.domain.order.OrderRepositoryTest@2d114d27, testMethod = findOrdersBy@OrderRepositoryTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@18a136ac testClass = OrderRepositoryTest, locations = '{}', classes = '{class sample.cafekiosk.spring.CafeKioskApplication}', contextInitializerClasses = '[]', activeProfiles = '{test}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@560348e6, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@6f1c29b7, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@fb58afcf, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@7b7fdc8, [ImportsContextCustomizer@77d67cf3 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, eJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@27d5a580, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@40d10264]; rollback [true] Hibernate: insert into product (id, created_date_time, modified_date_time, name, price, product_number, selling_status, type) values (default, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into product (id, created_date_time, modified_date_time, name, price, product_number, selling_status, type) values (default, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into product (id, created_date_time, modified_date_time, name, price, product_number, selling_status, type) values (default, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into orders (id, created_date_time, modified_date_time, order_status, registered_date_time, total_price) values (default, ?, ?, ?, ?, ?) Hibernate: insert into order_product (id, created_date_time, modified_date_time, order_id, product_id) values (default, ?, ?, ?, ?) Hibernate: insert into order_product (id, created_date_time, modified_date_time, order_id, product_id) values (default, ?, ?, ?, ?) Hibernate: insert into order_product (id, created_date_time, modified_date_time, order_id, product_id) values (default, ?, ?, ?, ?) Hibernate: insert into orders (id, created_date_time, modified_date_time, order_status, registered_date_time, total_price) values (default, ?, ?, ?, ?, ?) Hibernate: insert into order_product (id, created_date_time, modified_date_time, order_id, product_id) values (default, ?, ?, ?, ?) Hibernate: insert into order_product (id, created_date_time, modified_date_time, order_id, product_id) values (default, ?, ?, ?, ?) Hibernate: insert into order_product (id, created_date_time, modified_date_time, order_id, product_id) values (default, ?, ?, ?, ?) Hibernate: select order0_.id as id1_2_, order0_.created_date_time as created_2_2_, order0_.modified_date_time as modified3_2_, order0_.order_status as order_st4_2_, order0_.registered_date_time as register5_2_, order0_.total_price as total_pr6_2_ from orders order0_ where order0_.registered_date_time>=? and order0_.registered_date_time<? and order0_.order_status=? 2023-10-20 23:12:40.621 INFO 8704 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext@1a6c1270 testClass = OrderRepositoryTest, testInstance = sample.cafekiosk.spring.domain.order.OrderRepositoryTest@2d114d27, testMethod = findOrdersBy@OrderRepositoryTest, testException = java.lang.AssertionError: [Extracted: id, orderStatus, totalPrice, registeredDateTime] Expecting actual: [(3L, PAYMENT_COMPLETED, 12000, 2023-10-19T10:00 (java.time.LocalDateTime))] to contain exactly in any order: [(1L, PAYMENT_COMPLETED, 12000, 2023-10-19T10:00 (java.time.LocalDateTime))] elements not found: [(1L, PAYMENT_COMPLETED, 12000, 2023-10-19T10:00 (java.time.LocalDateTime))] and elements not expected: [(3L, PAYMENT_COMPLETED, 12000, 2023-10-19T10:00 (java.time.LocalDateTime))] , mergedContextConfiguration = [MergedContextConfiguration@18a136ac testClass = OrderRepositoryTest, locations = '{}', classes = '{class sample.cafekiosk.spring.CafeKioskApplication}', contextInitializerClasses = '[]', activeProfiles = '{test}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = 왜 id가 3인가요?? 저는 DataJpaTest을 수행하면 각각의 Test 마다 rollBack이 수행되어 id가 당연히 1이라고 생각했었습니다. 왜 3이 되는지 이해가 되지 않습니다
-
미해결Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트
SystemUuidHolder를 테스트하는 경우
Interface를 이용하여 완충재를 두고 테스트를 할 때는 테스트를 위한 mock 구현체를 이용하여 final 메소드를 stub하는 것을 피한다는 것은 이해를 했는데요. 갑자기 드는 생각이 결국 프로젝트가 배포될 때는 SystemUuidHolder라는 구현체를 사용하게 되고 그러면 해당 클래스의 대한 테스트도 진행해야 하나요? 진행한다면 해당 클래스는 UUID를 사용하고 있으니 final 메소드를 stub하는 상황을 피할 수 없게 되는건가요?
-
미해결Practical Testing: 실용적인 테스트 가이드
정적 팩터리 메서드 사용 기준?
제목 그대로 언제 사용하시는지에 대해 얘기를 나눠보고 싶어 질문 남깁니다.제 경우는 영속성 계층에 새로운 객체가 추가될 때 (RDB에 레코드를 추가할 때) new 키워드를 사용하여 id 값을 생성자로 받지 않는 생성자를 열어두고,존재하는 도메인 엔티티 혹은 영속성 엔티티를 영속성 계층으로부터 불러올 때는 정적 팩터리 메서드를 사용하는데강의를 진행하시면서 습관적으로 생성자 대신 static 메서드를 통해 생성 하시더라구요, 강사님만의 명확한 기준이 있으신지 궁금합니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
ProductRepository에 @Repository 어노테이션을 붙인 이유
스프링 컨테이너가 뜰 때 Jpa의 Repository 인터페이스 하위 타입을 스캔하여 빈으로 등록하는 것으로 알고 있어서 ProductRepository가 자동으로 스캔이 될텐데 @Repository 어노테이션을 붙이신 이유가 궁금합니다
-
미해결Practical Testing: 실용적인 테스트 가이드
ProductNumberFactory 클래스는 어떤 Layer 객체로 봐야 할까요?
안녕하세요! 먼저 항상 좋은 강의 감사드립니다! 저도 작은 경험 이지만 개발을 하면서 서비스 단의 로직을 분리 (강의에서 말씀 해주신 것 처럼 책임을 분리할 정도)해야 하는 상황인 경우, 이렇게 하는게 맞는지는 모르겠으나, Point1. ServiceUtil 클래스를 정의하여 컴포넌트로 주입받아 사용하거나 (실무에선 이렇게 사용)Point2. 학습시에는 Facade 패턴을 이용하여 서로 다른 서비스들의 상위 퍼사드 객체를 만들어서 사용한 적이 있습니다.(물론 퍼사드 패턴의 경우, 두 로직이 완전히 다른 맥락인 경우에 사용하는 것 같습니다.. 강의 예제와 같이 결합도가 높은 경우가 아니라..)그런데, 강의에서는 Factory 객체를 만들어서 (마치 제가 ServiceUtil을 만들어서 사용하는 것 처럼 - 사실 이름만 다르지 같습니다)사용하시는 모습을 보여주셨는데, Q1. 강사님께서는 실무에서 서비스의 책임을 분리할 때 주로 이런식으로 Factory 클래스를 분리하여 사용하시는지 궁금하고, Q2.그렇게 Factory 클래스로 책임을 분리했을 때, 이 Factory 클래스는 Controller / Service/ Repository 그 어느것도 아니게 되는데, Spring WEB mvc 레이어 아키텍쳐 상으로 어떤 Layer의 어떤 입장의 객체로 인지하고 사용해야 하는지 여쭙고 싶습니다. 감사합니다.
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
PostDetailPage params 이 어디서 온건가요?
async function PostDetailPage({ params }: any) { const post = await getPost(params.id); return ( <div> <h1>Posts/{post.id}</h1> <div> <h3>{post.title}</h3> <p>{post.created}</p> </div> </div> ); }PostDetailPage 에서 params을 콘솔에 출력하면 id값이 나오는거는 주소에서 받아오는건가요?
-
미해결Practical Testing: 실용적인 테스트 가이드
private method 테스트문의
안녕하세요. 좋은 강의 잘 듣고 있습니다 ^^ 강의에서 private method 테스트를 해야되는 상황이라면객체를 분리해야되는 상황이라고 말씀을 주셨는데실무에서는 꼭 분리를 해야되는 상황이 아닐수도 있을텐데1)실무에서는 private method 에 대한 테스트는 거의 작성안하나요? 2)레거시 프로젝트를 맡았을때 public method 에 대한 테스트를 작성하기에는 많은 작업이 필요하다면 private method 라도 테스트 코드를 작성하는게 좋을거 같은데 이런 경우도 작성을 안하나요?3) private 함수를 변경하는경우 해당함수만 테스트하고 싶을거 같은데 이런경우는 어떻게하나요?
-
미해결Practical Testing: 실용적인 테스트 가이드
controller, service dto 분리에대해 질문드립니다.
포스, 키오스크, 이외 다른 주문 엔드포인트로부터 주문이 들어왔을때 똑같은 서비스를 사용할 경우 그 서비스에서 사용하는 서비스dto로 변환해주어야 하기 때문에 컨트롤러 dto와 서비스dto를 분리해주는게 좋다로 이해했는데 맞을까요?
-
미해결[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
강의에 있는 똑같은 ppt는 pdf로라도 구할 수 없는걸가요?
강의자료에 자료가 있습니다만, 가르쳐주시는 곳의 ppt 랑 똑같은거는 아니더라고요. 강의중에 쓰는 ppt가 복습할때 더 효율적일 거 같아서 볼려고 하는데 볼 수 없을까요?
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
netlify를 배포를 했는데 문제 생겼습니다
netlify를 배포 했는데 경고와 빌드 문제 인 것 같습니다 어떻게 하면 될까요?