추가적인 리팩토링 해보기 PaymentService 에서 exRate 계산을 Payment 가 직접 하도록 할 수 있음 exRateProvider 를 Payment 안으로 넣어주면됨 시간계산은 Clock 을 Payment 안으로 넣어줘도 됨 추가 리팩토링을 진행하면 Payment 의 prepare 메서드의 내부 로직은 한 줄이면 끝남 강의 중 말씀 해주신 위의 설명을 토대로 PaymentService, Payment 클래스를 리팩토링하고 PaymentTest 클래스를 수정 해 보았습니다. PaymentService의 prepare 메서드 @Component public class PaymentService { private final ExRateProvider exRateProvider; private final Clock clock; public PaymentService(ExRateProvider exRateProvider, Clock clock) { this.exRateProvider = exRateProvider; this.clock = clock; } public Payment prepare(Long orderId, String currency, BigDecimal foreginCurrencyAmount) throws IOException { return Payment.createPrepared(orderId, currency, foreginCurrencyAmount, this.exRateProvider, this.clock); } } Payment의 createdPrepared 메서드 public static Payment createPrepared(Long orderId, String currency, BigDecimal foreginCurrencyAmount, ExRateProvider exRateProvider, Clock clock) throws IOException { BigDecimal exRate = exRateProvider.getExRate(currency); BigDecimal convertedAmount = foreginCurrencyAmount.multiply(exRate); LocalDateTime validUntil = LocalDateTime.now(clock).plusMinutes(30); return new Payment(orderId, currency, foreginCurrencyAmount, exRate, convertedAmount, validUntil); } PaymentTest class PaymentTest { @Test void createPrepared() throws IOException { Clock clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); ExRateProviderStub exRateProvider = new ExRateProviderStub(valueOf(1_000)); Payment payment = Payment.createPrepared( 1L, "USD", BigDecimal.TEN, exRateProvider, clock ); Assertions.assertThat(payment.getConvertedAmount()).isEqualByComparingTo(valueOf(10_000)); Assertions.assertThat(payment.getValidUntil()).isEqualTo(LocalDateTime.now(clock).plusMinutes(30)); } @Test void isValid() throws IOException { Clock clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); ExRateProviderStub exRateProvider = new ExRateProviderStub(valueOf(1_000)); Payment payment = Payment.createPrepared( 1L, "USD", BigDecimal.TEN, exRateProvider, clock ); Assertions.assertThat(payment.isValid(clock)).isTrue(); Assertions.assertThat( payment.isValid(Clock.offset(clock, Duration.of(30, ChronoUnit.MINUTES)))).isFalse(); } } 질문 제가 진행한 리팩토링이 올바르게 행하였는지 궁금합니다. Test를 진행할 때 exRateProvider 를 사용하기 위해서 기존에 작성하였던 ExRateProviderStub 오브젝트를 생성하여 테스트를 진행해 주는 것이 맞는지 궁금합니다. 처음에 ExRateProviderStub exRateProvider = null; 을 사용해 봤더니 java.lang.NullPointerException: Cannot invoke "tobyspring.hellospring.payment.ExRateProvider.getExRate(String)" because "exRateProvider" is null 이와 같은 에러가 발생하였습니다. 제가 ExRateProvider 객체가 null로 설정해서 발생한 에러인걸 이해 하고, ExRateProvider 를 구현한 객체가 필요하기 때문에 ExRateProviderStub 오브젝트를 생성하여 exRate 를 넣어 주었습니다. 3. ExRateProviderStub 오브젝트를 생성하여 exRate (적용환율)을 넣고 생성하는 이유는 실제 api가 아닌 일부 기능을 테스트하기 위해서 저희가 직접 적용환율을 적용해보고 외화금액과 곱해서 계산된게 맞는지 테스트하는 목적이다. 라고 제가 이해하였는데 올바르게 이해한 것인지 궁금합니다. PS. 제가 아직 배운 내용을 완전히 소화하지 못한 부분이 있을 수 있어, 질문에 대한 설명이 부족할 수 있습니다. 혹시 잘못된 부분이나 추가적인 조언이 있다면 피드백 부탁드립니다. 감사합니다.
리액트 쿼리를 사용하면 결국 client side fetching이 됩니다. nextjs13부터 server side fetching이 가능해지고 suspense를 활용한 data streaming은 데이터펫칭에서 새로운 패러다임을 제시했다고 생각합니다. 굳이 리액트 쿼리를 사용할 이유가 있을까요?
쿠폰이 발급되고 바로 사용하려는 유저가 있다면 어떻게해야하나요? 실무에서 보통의 경우 어떻게 처리하는지 궁금한데요. 예를 들어서, 쿠폰이 생성되려면 시간이 다소 소요될 수 있다는 안내 문구와 같은 정책으로 안고가는지.. 아니면 다른 좋은 방법이 있는지 궁금합니다. 강의 내용 흐름 레디스를 통해서 싱글 스레드로 쿠폰 개수 확인 제한 범위 안이라면 클라이언트에게 쿠폰 발급 성공 응답 비동기로 처리량을 조절하면서 쿠폰 데이터를 저장 실제 유저가 아직 쿠폰 발급이 안되었는데, 쿠폰 사용 시도 이 경우 어떻게 대응하는지 궁금합니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 혹시 중급1에서 완벽히 이해가 안됬다고 느끼면 중급1편을 다시 재수강하고 중급2로 넘어가는게 좋을까요? 아니면 우선 순차적으로 모두 수강후에 재수강하는것이 좋을까요?
On-Demand ISR 사전 렌더링 방식을 보면서 현재 대부분의 SSR로 구현 되어 있는 어플리케이션을 좀 더 최적화 할 수 있겠다는 생각을 했습니다! 여기서 질문은 API Router를 생성해서 revalidate할 경로를 작성해서 해당 경로의 페이지를 On-Demand ISR 처리되도록 구현해주셨는데요..! 실제로 비지니스 로직을 작성할 때는 많은 페이지들이 존재하는데, 이럴때는 API Router에서 "/" 인덱스 페이지 경로를 하드코딩한 부분을 동적으로 받을 수 있게 처리해서 백엔드 서버와 통신 시, 정상응답을 받았을 때, 해당 API Router를 호출하게 하면 되는걸까요..?! 추가로, 페이징 기능 같은 페이지가 있는 경우에는 어떤식으로 On-Demand ISR을 적용하는지도 궁금합니다..!! 바쁘시겠지만 답변 해주시면 감사하겠습니다 😀 🚨 아래의 가이드라인을 꼭 읽고 질문을 올려주시기 바랍니다 🚨 질문 하시기 전에 꼭 확인해주세요 - 질문 전 구글에 먼저 검색해보세요 (답변을 기다리는 시간을 아낄 수 있습니다) - 코드에 오타가 없는지 면밀히 체크해보세요 (Date와 Data를 많이 헷갈리십니다) - 이전에 올린 질문에 달린 답변들에 꼭 반응해주세요 (질문에 대한 답변만 받으시고 쌩 가시면 속상해요 😢 ) 질문 하실때 꼭 확인하세요 - 제목만 보고도 무슨 문제가 있는지 대충 알 수 있도록 자세한 제목을 정해주세요 (단순 단어 X) - 질문의 배경정보를 제공해주세요 (이 문제가 언제 어떻게 발생했고 어디까지 시도해보셨는지) - 문제를 재현하도록 코드샌드박스나 깃허브 링크로 전달해주세요 (프로젝트 코드에서 문제가 발생할 경우) - 답변이 달렸다면 꼭 확인하고 반응을 남겨주세요 - 강의의 몇 분 몇 초 관련 질문인지 알려주세요! - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.
authentication-server는 어떤역활인가요? 강의 마지막에 볼때..클라이언트가 GateWay-server에 요청할경우 config-server 와 Eureka-server를 보내고 아이템서비스와 히스토리 서비스에 데이터 전달하는데 authentication-server는 아이템서비스로 봐야하는지 아니면 별도의 서버인지 궁금합니다. 14장 pdf파일에 두번째 그림보고 궁금해서 문의드립니다. 제가 느끼기엔 item-microservice와 유사한 기능이라 생각됩니다만.. 제가 이해하는게 맞을까요?
레이아웃 컴포넌트의 경우 여러 페이지에 공통적으로 들어가야 하는 것이기 때문에, _app.tsx에 넣어야 하는 것은 이해했습니다. 그리고 모든 페이지에 들어가는 레이아웃이 아닌 특정 페이지만 들어가는 레이아웃의 경우에 정환님 같은 경우에는 getLayout이라는 메서드를 해당 레이아웃을 필요로 하는 페이지 컴포넌트 함수에 메서드로 주시고 _app.tsx에서는 그 메서드를 불러오게끔 하셨는데요. 제가 머리가 나빠서인지는 모르겠지만, 뭔가 이런 메서드로 레이아웃을 관리하니까 이해하기 어렵고 뭔가 직관적으로 다가오는 느낌이 없어서 힘들더라구요.. 그래서 그냥 router와 if문을 이용해서 특정 페이지의 경우에만 searchable 레이아웃이 나오게 하고, 아닌 경우에는 그냥 페이지를 리턴하도록 하는 게 저한테는 뭔가 더 직관적..? 으로 다가오는데 이런 식으로 사용해도 되는지 궁금해서 여쭈어봅니다.. (코드는 밑에 같이 써놓았습니다!) export default function App({ Component, pageProps }: AppProps) { const router = useRouter(); const isSearchableLayoutPage = router.pathname === '/' || router.pathname === '/search'; return ( <GlobalLayout> {isSearchableLayoutPage ? ( <SearchableLayout> <Component {...pageProps} /> </SearchableLayout> ) : ( <Component {...pageProps} /> )} </GlobalLayout> ); }
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] https://drive.google.com/drive/folders/1jHYkBAHDuUoSr5hjn0Kp5CzTAWLem3Zu?usp=drive_link 코드를 쭉 작성해봤는데 마지막에 submit할 때 whiteError페이지가 나옵니다. gpt에 해결책을 물어봤을 때 에러 로그를 보면, Thymeleaf 템플릿을 처리하는 동안 문제가 발생한 것을 알 수 있습니다. 특히, item.orderItems[0].item.name 라는 표현식에서 문제가 발생했습니다. 에러의 원인 에러 메시지 중에서 가장 중요한 부분은 다음과 같습니다: Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1025E: The collection has '0' elements, index '0' is invalid 이 메시지는 item.orderItems[0] 에서 인덱스를 사용하여 컬렉션의 첫 번째 요소에 접근하려고 했지만, orderItems 컬렉션이 비어 있어서 인덱스 0 이 유효하지 않다는 것을 의미합니다. 즉, orderItems 컬렉션이 비어있거나 null일 때 이 표현식을 평가하려다가 오류가 발생한 것입니다. 해결 방법 컬렉션이 비어있을 경우에 대한 처리 : Thymeleaf 템플릿에서 컬렉션이 비어있는 경우를 대비하여 조건문을 추가할 수 있습니다. 예를 들어, orderItems 가 비어있지 않은 경우에만 접근하도록 할 수 있습니다. <div th:if="${item.orderItems.size() > 0}"> <span th:text="${item.orderItems[0].item.name}">Item Name</span> </div> 안전한 접근을 위한 기본값 제공 : orderItems 컬렉션이 비어있을 때 안전하게 처리하기 위해, th:text 속성에서 기본값을 제공할 수 있습니다. <span th:text="${item.orderItems.size() > 0 ? item.orderItems[0].item.name : 'No items'}">Item Name</span> 서버 측에서 데이터 처리 : 서버 코드에서 orderItems 가 비어있는 경우에 대해 적절히 처리하도록 할 수도 있습니다. 예를 들어, orderItems 가 비어있다면 기본값을 설정하거나, 비어있음을 표시하는 별도의 로직을 추가할 수 있습니다. 템플릿 구조 개선 : 템플릿 구조를 개선하여, orderItems 가 비어있을 때는 특정 HTML 요소를 렌더링하지 않도록 할 수 있습니다. 결론 에러의 원인은 item.orderItems[0].item.name 에서, orderItems 컬렉션이 비어있을 때 인덱스 0 에 접근하려고 했기 때문입니다. 이를 해결하려면 위의 방법 중 하나를 사용하여, orderItems 가 비어있는 경우를 안전하게 처리해야 합니다. 템플릿에서 Thymeleaf의 조건문이나 기본값 제공 기능을 사용하여 이 문제를 해결할 수 있습니다. 이런식으로 나왔고 h2데이터베이스의 order자체에 값이 잘 저장된 것은 확인했는데 무엇이 문제인지 잘 모르겠어서 질문드립니다
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? 예 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예 3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예 [질문 내용] 테스트 application.properties 와 메인의 application.properties 의 설정으로 테스트와 실제 어플의 db 동작을 분리 할수 있는데 만약에 테스트에서도 테스트 패키지 혹은 테스트 별로 db를 다르게 사용하려면 어떻게 해야하나요? 단위 테스트 할 기능중에 db종속적인 부분이 있어서 질문 드려요!
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 왠만하면 단방향으로 다 끝낼 수 있는데 그러면 양방향은 언제 쓰는게 좋은 건가요??
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 양방향은 필요할 때만 사용하고 단방향만으로도 끝낼 수 있게 설계하시라고 하셨는데 그러면 단방향일때는 외래키가 있는 entity에만 private Ex ex; 같이 넣고 위에 어노테이션을 넣어야 하나요? 만약 넣는 다면 @ManyToOne 넣고 뭘 설정을 또 해야하나요?
안녕하세요. 실무에서 시간이 오래소요되는 3개의 delete쿼리를 excutor service를 사용하여 멀티스레드형식으로 1개의 스레드가 1개의 쿼리를 수행하도록 구현해보고 싶습니다. 그런데 멀티스레드를 사용했을때 트랜잭션은 보통 어떻게 처리하시나요.. ? 저는 @transactional 을 선언해서 사용하고 있는데 이는 스레드1개당 트랙잭션이 생성되는거라 제가 의도한데로 롤백이 제대로 안될거라고 하더라구요.. 3개의 쿼리가 모두 성공했을때 커밋되고 그렇지않으면 롤백시키고싶은데 이부분에 대한 지식도 혹시 알수있을까요? 추가로 혹시 해당 질문 관련 강의를 개설해놓으신게 있다면 알려주시면 감사하겠습니다!
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예) [질문 내용] < 사전 설명 > Rectangle 객체에서 hashCode()와 equals() 둘중 equals()만 오버라이드 하였습니다. 이후 public class RectangleTest { public static void main(String[] args) { Set<Rectangle> rectangleSet = new HashSet<>(); for (int i = 0; i < 13; i++) { rectangleSet.add(new Rectangle(20, 20)); } System.out.println(rectangleSet.size()); for (Rectangle rectangle : rectangleSet) { System.out.println("rectangle = " + rectangle); } } } 위와 같은 코드를 작성했을때 System.out.println(rectangleSet.size()); 출력 결과가 13이 나오고 for문의 출력 결과도 같은 결과가 13번 나옵니다. < 질문 > hashCode()를 오버라이드 하지 않음으로써 해시인덱스가 랜덤으로 배정되는 것으로 알고있습니다. 그래서 논리적으로 같은 객체가 들어오더라도 다른 해시인덱스에 할당 되는것이 문제가 됩니다. 하지만 제가 작성한 코드와 같이 많은 for문을 돌게 될면 새로 만들어진 Rectangle 객체가 언젠가 최소 한번쯤은 해시인덱스에서 중복된 객체를 만나게되고, 중복되는 해시 인덱스의 버킷에서 equals() 메서드가 실행되어 저장이 취소될것이라고 예상했습니다. 그래서 rectangleSet.size()는 13 미만이 될거라 생각했지만 아니었습니다. 오버라이드된 equals() 메서드가 실행되지 않는 이유가 무엇일까요?
즉시로딩 관계에서 조회를 할때 어떤 경우는 연관관계에 있는 데이터들을 join으로 한번에 끌고오는 경우가 있고 어떤때는 N + 1 문제가 발생하는 경우가 있었습니다. 그래서 아래와 같이 테스트를 해봤는데요 우선 member와 team의 관계를 즉시로딩으로 설정했습니다. 그리고 member을 한번만 저장하든 여러번 저장하든 N+ 1 문제가 발생했습니다. 그래서 즉시로딩 관계에서 어떤 경우에 연관관계에 있는 데이터들을 한번에 조인해서 가져오는지 궁금합니다. Team team = new Team(); team.setName("team1"); em.persist(team); Team team2 = new Team(); team2.setName("team1"); em.persist(team2); Member member = new Member(); member.setName("hi1"); member.setTeam(team); em.persist(member); Member member2 = new Member(); member2.setName("hi2"); member2.setTeam(team2); em.persist(member2); Member member3 = new Member(); member3.setName("hi3"); member3.setTeam(team2); em.persist(member3); em.flush(); em.clear(); List<Member> members = em.createQuery("select m from Member m", Member.class) .getResultList(); tx.commit();