묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
엔티티를 외부에 노출하면 안 된다는 의미
안녕하세요.엔티티를 파라미터로 받으면 안 되는 이유에 대해서는 말씀해주셔서 이해하였습니다.엔티티에 화면 검증 로직이 들어가는 문제엔티티를 변경하면 API 스펙이 변경되는 문제그리고 엔티티를 외부에 노출하면 안 된다고 말씀하셨습니다.엔티티에서 필요한 변수만 사용하거나, 추가 변수를 사용하고자 해서 DTO로 반환하는 걸로 추측했습니다. 그런데 뭔가 더 구체적이고 다양한 이유가 있을 것 같아 직접 설명을 듣고 싶어 질문 드립니다.감사합니다.
-
해결됨스프링 핵심 원리 - 고급편
데코레이터 패턴에서 Component 코드 질문 있습니다
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.아래와 같은 코드에서 MessageDecorator 클래스는 Component 인터페이스를 구현하면서 operation 메서드를 구체화한 것은 이해를 했습니다.근데 데코레이터 패턴에서 MessageDecorator 클래스가 Component 클래스를 구현하면서 Component 클래스를 필드로 갖는 이유가 무엇인지 잘 이해가 안됩니다 설명 부탁드립니다 package springHigh.advanced.pureproxy.decorator.code; import lombok.extern.slf4j.Slf4j; @Slf4j public class MessageDecorator implements Component{ private Component component; public MessageDecorator(Component component) { this.component = component; } @Override public String operation() { log.info("MessageDecorator 실행"); // data => *** data **** String result = component.operation(); String decoResult = "******" + result+"*******"; log.info("MessageDecorator 적용"); return decoResult; } }
-
미해결스프링 핵심 원리 - 기본편
CoreApplication 실행시 AppConfig, AutoAppConfig순서
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)예3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)예[질문 내용]CoreApplication 실행 시에 @SpringBootApplication어노테이션 안에 @ComponentScan 이 있어서 @Configuration이 써진 AppConfig와 AutoAppConfig가 스캔되는 대상인 것은 알겠습니다. CoreApplication 실행시 AppConfig의 로그가 찍히는데 1.AppConfig와 AutoAppConfig가 둘다 작동됐는데 AppConfig가 뒤에 작동 된건지 순서가 궁금합니다. 2.AutoAppConfig에서 필터로 AppConfig는 스캔되지 않게 해줬는데 이것과 무관하게 CoreApplication 의 어노테이션으로 인해 AppConfig파일도 스캔된건지 궁금합니다.@Configuration @ComponentScan( excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class)) public class AutoAppConfig { }CoreApplication 실행 시에 스프링 컨테이너를 설정 안해줬는데 왜 AppConfig의 로그가 도는지... 궁금합니다
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
7. 스프링 MVC - 웹 페이지 만들기 > 상품 목록 페이지 th:onclick
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]안녕하세요 김영한 강사님현재 진행중인 7. 스프링 MVC - 웹 페이지 만들기 강의 - 상품 목록 페이지 th:onclick 부분은 아래와 같이 작성되어 있고,<div class="row"> <div class="col"> <button class="btn btn-primary float-end" onclick="location.href='addForm.html'" th:onclick="|location.href='@{basic/items/add}'|" type="button">상품 등록 </button> </div> </div>컨트롤러의 상품 등록 부분도 예제와 동일하게 작성되어 있습니다.@Controller @RequestMapping("/basic/items") @RequiredArgsConstructor public class BasicItemController { private final ItemRepository itemRepository; ...(생략) @GetMapping("/add") public String addForm() { return "basic/addForm"; } }그런데 브라우저에서 등록 버튼을 누르면 /basic이 한 번 더 붙는데 이유가 궁금합니다.
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
gradlew clean build 시 예외가 발생합니다.
> Task :test FAILEDFAILURE: Build failed with an exception.* What went wrong:Execution failed for task ':test'.> There were failing tests. See the report at: file:///C:/Spring_kyh/%EC%8A%A4%ED%94%84%EB%A7%81%20%EB%B6%80%ED%8A%B8%EC%99%80%20JPA%20%ED%99%9C%EC%9A%A91/jpashop/build/reports/tests/test/index.html* Try:> Run with --scan to get full insights.BUILD FAILED in 4s8 actionable tasks: 8 executed 다른 질문들 찾아보다가 https://www.inflearn.com/community/questions/1251256/gradlew-%EB%B9%8C%EB%93%9C%EA%B0%80-%EA%B3%84%EC%86%8D-%EC%8B%A4%ED%8C%A8%EB%A1%9C-%EB%9C%B9%EB%8B%88%EB%8B%A4?focusComment=336001이 답변을 보고 gradle도 8.5로 바꿔봤는데 똑같은 오류가 발생해 질문드립니다. 환경변수도 제대로 설정했는데도 똑같은 증상이 발생합니다
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
체크예외와 언체크예외
체크예외와 언체크예외에 대해서 복구불가능한 문제와 언체크예외를 주로 사용해야하는 부분에 대해서 아래와 같이 이해했는데 이렇게 이해하면 될까요?? 1. 예외는 주로 복구 불가능한 이유가 예외가 발생하면 try~catch로 예외를 처리해서 정상흐름대로는 할순있지만, 예외가 발생한거에대해서 이를 복구해서 원했던결과대로 정상적으로 돌리는것은 불가능하기때문에 그런건가요?? + 애플리케이션내에서 코드를 통해서 복구할수는 없는건가요??체크예외에서는 예외가 발생하면 throws로 예외를 던져야하지만 이를 서비스나 컨트롤러에서는 try catch로 처리할순있어도 딱히 예외를 복구할순없다. 그래서 그냥 try~catch로 예외를 처리하지말고 예외를 발생시켜서 개발자가 로그를통해 예외를 인지하는게 낫다. 결국 그래서 throws를 할필요가없는 언체크예외가 낫다.
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
강의 콘솔처럼 rolled back 기록이 안 보이네요
@ExtendWith(SpringExtension.class) @SpringBootTest class MemberRepositoryTest { @Autowired MemberRepository memberRepository; @Test @Transactional public void testMember() throws Exception { //given Member member = new Member(); member.setUsername("memberA"); //when Long saveId = memberRepository.save(member); Member findMember = memberRepository.find(saveId); //then Assertions.assertThat(findMember.getId()).isEqualTo(member.getId()); Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername()); }2024-08-27T08:46:11.488+09:00 INFO 20440 --- [ Test worker] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2024-08-27T08:46:11.607+09:00 INFO 20440 --- [ Test worker] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:tcp://localhost/~/jpashop user=SA 2024-08-27T08:46:11.610+09:00 INFO 20440 --- [ Test worker] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2024-08-27T08:46:12.826+09:00 INFO 20440 --- [ Test worker] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) 2024-08-27T08:46:12.846+09:00 DEBUG 20440 --- [ Test worker] org.hibernate.SQL : drop table if exists member cascade 2024-08-27T08:46:12.853+09:00 DEBUG 20440 --- [ Test worker] org.hibernate.SQL : drop sequence if exists member_seq 2024-08-27T08:46:12.860+09:00 DEBUG 20440 --- [ Test worker] org.hibernate.SQL : create sequence member_seq start with 1 increment by 50 2024-08-27T08:46:12.866+09:00 DEBUG 20440 --- [ Test worker] org.hibernate.SQL : create table member ( id bigint not null, username varchar(255), primary key (id) ) 2024-08-27T08:46:12.872+09:00 INFO 20440 --- [ Test worker] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2024-08-27T08:46:13.228+09:00 WARN 20440 --- [ Test worker] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning 2024-08-27T08:46:13.272+09:00 INFO 20440 --- [ Test worker] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html] 2024-08-27T08:46:13.987+09:00 INFO 20440 --- [ Test worker] jpabook.jpashop.MemberRepositoryTest : Started MemberRepositoryTest in 6.47 seconds (process running for 8.794) WARNING: A Java agent has been loaded dynamically (C:\gradle\caches\modules-2\files-2.1\net.bytebuddy\byte-buddy-agent\1.14.19\154da3a65b4f4a909d3e5bdec55d1b2b4cbb6ce1\byte-buddy-agent-1.14.19.jar) WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information WARNING: Dynamic loading of agents will be disallowed by default in a future release 2024-08-27T08:46:14.774+09:00 DEBUG 20440 --- [ Test worker] org.hibernate.SQL : select next value for member_seq Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended 2024-08-27T08:46:14.914+09:00 INFO 20440 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2024-08-27T08:46:14.918+09:00 INFO 20440 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2024-08-27T08:46:14.933+09:00 INFO 20440 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. > Task :test BUILD SUCCESSFUL in 26s 4 actionable tasks: 1 executed, 3 up-to-date 오전 8:46:15: Execution finished ':test --tests "jpabook.jpashop.MemberRepositoryTest.testMember"'. Transactional 애노테이션을 붙였는데 롤백얘기가 안보이네요... JUnit5로 해서 차이가 있는걸까요?
-
미해결토비의 스프링 6 - 이해와 원리
예외 처리에 대한 질문
리포지토리 계층에서 모든 데이터 액세스 기술에서 발생하는 예외는 DataAccessException으로 랩핑되어서 던져진다고 말씀하셨는데 받는 쪽인 서비스 계층에서는 DataAccessException의 서브클래스 타입의 예외를 받게 될거 같은데요. 서비스 계층에서 받게 되는 예외가 예를 들어 DuplicateKeyException이라서 처리를 하게 된다면 서비스 계층은 특정 리포지토리의 예외에 종속적이게 될거 같습니다. 그래서 컨트롤러 단으로 예외를 넘겨 ExceptionHandler로 처리해주는 것이 좋은 방법일까?에 대해서 질문 드리고 싶습니다.근데 만약 서비스 계층에서 처리한다면,// @Service 안에 있는 있는 일부 코드 (회원가입 로직) public boolean saveCustJoinInfo(UserDto userDto) { String pwd1 = userDto.getPwd().split(",")[0]; String password = passwordEncoder.encode(pwd1); userDto.setPwd(password); try { userDao.insertUser(userDto); return true; } catch (DataAccessException e) { return false; } }이렇게 처리해도 문제가 없는가에 대해 궁금합니다. 예외 처리에 대한 여러 블로그들을 읽어봤는데 다수의 사람들이 사용자 정의 예외클래스를 정의해서 던지도록 한 이유도 궁금합니다.만약 이 방법이 좋지 않다면 어떤 계층에서 처리하면 특정 계층이 기술에 종속적이지 않게 예외를 처리할 수 있는지 조언해 주시면 감사하겠습니다. 토비님의 스프링 강의를 일주일 만에 완강했는데 너무 좋은 강의라 부트 강의도 구매하였습니다ㅎㅎ 아키텍처에 대한 강의도 고려하시고 있다고 하셨는데 기다리고 있겠습니다!!
-
해결됨스프링 핵심 원리 - 고급편
스프링 빈으로 수동으로 등록이 안됩니다
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용] 위와같이 설정했을 때, http://localhost:8080/v1/request-proxy?itemId=hello로 접근했는데 올바르게 컨트롤러가 인식이 안되는데 원인이 무엇인가요?
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
addItemV5 와 addItemV6 차이점
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]addItemV5 함수에서는public String addItemV5(Item item) { itemRepository.save(item); return "redirect:/basic/items/"+item.getId(); } 보시다시피 return 값을 줄 때 리포지토리에 저장한 item의 id를 가져오는 것이 아니라 파라미터로 받은 item의 id를 가져와서 반환했는데,public String addItemV6(Item item, RedirectAttributes redirectAttributes) { Item savedItem = itemRepository.save(item); redirectAttributes.addAttribute("itemId", savedItem.getId()); redirectAttributes.addAttribute("status", true); return "redirect:/basic/items/{itemId}"; }v6 에서는 savedItem으로 저장한 item 자체를 가져와서 id를 넣어주는데, 혹시 차이점이 있을까요?
-
미해결스프링 핵심 원리 - 기본편
안녕하십니까 질문 있습니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오) 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)예[질문 내용]강의 내 나오는 클래스 다이어그램 혹시 어떤 프로그램 사용해서 그리시나요? DrawIO는 아닌 것같은데 저도 선생님처럼 설계시 다이어그램을 그려보고 싶어서 문의드립니다.
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
JPA강의에서 Build and run using 을 Gradle로 사용하는 이유 알 수 있을까요?
다른 강의에선 작동 속도때문에 전부 인텔리제이로 바꿔서 진행했었는데 강의 교안을 보면 스프링부트 3.2 이상부터는 gradle로 하라고 나와있어 궁금하여 질문드립니다.
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
localhost:8080 Whitelabel Error Page 뜹니다...
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]안녕하세요, 해당 static 폴더에 index.html파일에 코드를 입력한후 서버 실행결과 whilelabel 에러가 뜹니다.해결에 도움을 주시면 감사하겠습니다.. 콘솔 내용:: Spring Boot :: (v3.3.3)2024-08-26T17:27:55.146+09:00 INFO 4895 --- [ main] h.hello_spring.HelloSpringApplication : Starting HelloSpringApplication using Java 17.0.12 with PID 4895 (/Users/kipyo/Documents/스프링/hello-spring/out/production/classes started by kipyo in /Users/kipyo/Documents/스프링/hello-spring)2024-08-26T17:27:55.148+09:00 INFO 4895 --- [ main] h.hello_spring.HelloSpringApplication : No active profile set, falling back to 1 default profile: "default"2024-08-26T17:27:55.573+09:00 INFO 4895 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)2024-08-26T17:27:55.580+09:00 INFO 4895 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]2024-08-26T17:27:55.580+09:00 INFO 4895 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.28]2024-08-26T17:27:55.606+09:00 INFO 4895 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext2024-08-26T17:27:55.607+09:00 INFO 4895 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 433 ms2024-08-26T17:27:55.743+09:00 WARN 4895 --- [ main] ion$DefaultTemplateResolverConfiguration : Cannot find template location: classpath:/templates/ (please add some templates, check your Thymeleaf configuration, or set spring.thymeleaf.check-template-location=false)2024-08-26T17:27:55.768+09:00 INFO 4895 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'2024-08-26T17:27:55.772+09:00 INFO 4895 --- [ main] h.hello_spring.HelloSpringApplication : Started HelloSpringApplication in 0.793 seconds (process running for 0.952)
-
해결됨실전! 스프링 데이터 JPA
강의 4분 28초에 대해 질문 있습니다.
"성능 최적화를 할텐대 어쨋든 이 데이터를 두 개를 가지고 있어야 돼요" 라는 부분이 있는데요 여기서 하나는 실제 엔티티 객체고 나머지 하나는 스냅샷으로 저장한 초기 상태의 엔티티 객체를 말하는건가요?
-
미해결실전! 스프링 데이터 JPA
단순 조인과 페치 조인에 대해 질문 있습니다.
첫번째 JPQLQuery("select m from Member m join m.team ") List<Member> findInnerJoin(); @Test public void test() { Team teamA = new Team("teamA"); Team teamB = new Team("teamB"); teamRepository.save(teamA); teamRepository.save(teamB); Member member1 = new Member(10, "member1", teamA); Member member2 = new Member(10, "member2", teamB); memberRepository.save(member1); memberRepository.save(member2); em.flush(); em.clear(); List<Member> members = memberRepository.findInnerJoin(); for (Member member : members) { System.out.println("member = " + member); System.out.println("member.getTeam().getClass() = " + member.getTeam().getClass()); System.out.println("member.getTeam().getName() = " + member.getTeam().getName()); } }실행결과Hibernate: select m1_0.member_id, m1_0.age, m1_0.team_id, m1_0.username from member m1_0 join team t1_0 on t1_0.team_id=m1_0.team_id member = Member(id=1, username=member1, age=10) member.getTeam().getClass() = class study.data_jpa.entity.Team$HibernateProxy$w0d2fXxq Hibernate: select t1_0.team_id, t1_0.name from team t1_0 where t1_0.team_id=? member.getTeam().getName() = teamA member = Member(id=2, username=member2, age=10) member.getTeam().getClass() = class study.data_jpa.entity.Team$HibernateProxy$w0d2fXxq Hibernate: select t1_0.team_id, t1_0.name from team t1_0 where t1_0.team_id=? member.getTeam().getName() = teamB첫번째 JPQL에서는 전형적인 N + 1 문제가 발생함을 알 수 있습니다. 그래서 첫번째 JPQL에서의 select문에 t만 추가를 해봤습니다.두번째 JPQL@Query("select m, t from Member m join m.team t") List<Member> findInnerJoin();실행결과Hibernate: select m1_0.member_id, m1_0.age, m1_0.team_id, m1_0.username, t1_0.team_id, t1_0.name from member m1_0 join team t1_0 on t1_0.team_id=m1_0.team_id member = Member(id=1, username=member1, age=10) member.getTeam().getClass() = class study.data_jpa.entity.Team member.getTeam().getName() = teamA member = Member(id=2, username=member2, age=10) member.getTeam().getClass() = class study.data_jpa.entity.Team member.getTeam().getName() = teamB위 실행 결과를 보고 페치 조인과 무슨 차이가 있을까 해서 페치 조인을 테스트 해봤습니다.세번째 JPQL@Query("select m from Member m join fetch m.team t") List<Member> findMemberFetchJoinTeam();@Test public void ManyToOneFetchJoin() { Team teamA = new Team("teamA"); Team teamB = new Team("teamB"); teamRepository.save(teamA); teamRepository.save(teamB); Member member1 = new Member(10, "member1", teamA); Member member2 = new Member(10, "member2", teamB); memberRepository.save(member1); memberRepository.save(member2); em.flush(); em.clear(); List<Member> members = memberRepository.findMemberFetchJoinTeam(); for (Member member : members) { System.out.println("member = " + member); System.out.println("member.getTeam().getClass() = " + member.getTeam().getClass()); System.out.println("member.getTeam().getName() = " + member.getTeam().getName()); } }실행결과Hibernate: select m1_0.member_id, m1_0.age, t1_0.team_id, t1_0.name, m1_0.username from member m1_0 join team t1_0 on t1_0.team_id=m1_0.team_id member = Member(id=1, username=member1, age=10) member.getTeam().getClass() = class study.data_jpa.entity.Team member.getTeam().getName() = teamA member = Member(id=2, username=member2, age=10) member.getTeam().getClass() = class study.data_jpa.entity.Team member.getTeam().getName() = teamB두번째 JPQL과 세번째 JPQL의 차이를 보면 두번째 JPQL의 select문에서 m1_0.team_id 도 조회하고 세번째 JPQL의 select문에서는 m1_0.team_id 을 조회하지 않습니다.두번째 JPQL과 세번째 JPQL이 정확히 어떤 차이가 있어 select문에서 조회하는 컬럼이 달라지는지 궁금합니다.
-
미해결토비의 스프링 6 - 이해와 원리
도메인 모델 아키텍처 패턴 추가 리팩토링
추가적인 리팩토링 해보기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); } PaymentTestclass 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. 제가 아직 배운 내용을 완전히 소화하지 못한 부분이 있을 수 있어, 질문에 대한 설명이 부족할 수 있습니다. 혹시 잘못된 부분이나 추가적인 조언이 있다면 피드백 부탁드립니다. 감사합니다.
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
다음으로에 나온 실전 REST API 강의
다음으로에 나온 실전 REST API 강의는 아직 나오지 않은 상태일까요? 아니면 다른 강의에 녹아있는걸까요
-
미해결스프링 시큐리티 OAuth2
시큐리티 완전정복(6.x 개정판) 쿠폰
안녕하세요!!수원님의 시큐리티 강의를 듣고 있습니다!!좋은 강의 해주셔서 감사합니다!다름이 아니라, 제가 메일을 늦게 확인하여서기존 수강생에게 제공되는 50%할인 쿠폰을 사용하지 못했는데,혹시 다시 쿠폰 발급이 가능한지 여쭤봐도 되겠습니까?!감사합니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
안녕하세요 스프링 시큐리티 테스트에 대한 질문이 있습니다.
스프링 시큐리티를 사용하고 시큐리티 설정안에서 아래와 같은 예외 핸들링을 해주었을때http.exceptionHandling(e -> e.authenticationEntryPoint((request, response, authException) -> {CustomResponseUtil.fail(response, "로그인을 진행해 주세요", HttpStatus.UNAUTHORIZED);}));http.exceptionHandling(e -> e.accessDeniedHandler((request, response, accessDeniedException) -> {CustomResponseUtil.fail(response, "권한이 없습니다", HttpStatus.FORBIDDEN);}));컨트롤러 테스트에서 @WebMvcTest(AccountController.class)class AccountControllerTest {테스트를 하면 예외 핸들링이 안되는거 같은데 맞을까요?이러한 해결방법으로@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)이렇게 사용하면될거같은데 1. @WebMvcTest(AccountController.class) 방식으로도 해결할수 있는 방법이 있을까?2. 어떤 방식을 더 추천하실까요?
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
복구 불가능한 예외
강의에서 설명하신 복구 불가능한 예외에 대해서 질문드립니다. 말씀하신 복구 불가능한 예외라는게 구체적으로 어떤것을 의미하는것인지 이해를 잘 못하겠습니다. 앞서 배운것처럼 해당 예외가 발생하면 애플리케이션 로직에서 try~catch를 통해 예외를 잡아서 처리하면 되는것 아닌가요??