묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
beforeEach에 객체를 생성하면....
service에서 private final MemberRepository memberRepository = new MemoryMemberRepository();위 코드를 보면 리포지토리 안에 store가 static으로 되어있으니 당장은 상관없으나 혹시 모르니 다른 객체를 사용하지 않도록 private final MemberRepository memberRepository; public MemberService(MemberRepository memberRepository) { this.memberRepository = memberRepository; }위와 같이 service에서 리포지토리를 주입 받는다라고 했는데요 근데 테스트케이스에서@BeforeEach public void beforeEach() { memberRepository = new MemoryMemberRepository(); service = new MemberService(memberRepository); }BeforeEach에 이런식으로 코드를 작성하면 리포지토리를 주입 받기는 했지만 결국 케이스별로 다른 리포지토리 인스턴스를 주입 받은거 아닌가요?
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
sessionTest() 에러
세션생성 테스트쪽에서 뭔가 이상하네요강사님과 코드는 같은데 에러 쪽에서 null을 출력하네요 member 자체를 불러오면 DTO값 들이 호출이될탠데member랑 result랑 다른게 정상아닌가요 ?
-
해결됨스프링 핵심 원리 - 기본편
@SpringBootApplication가 붙은 CoreApplication를 실행할 때
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]@SpringBootApplication 내부에 @ComponentScan이 있기 때문에@SpringBootApplication가 붙은 CoreApplication을 실행하면 컴포넌트 스캔을 하게 되는 거 맞죠? 그런데 만약 @Service, @Controller, @Component @Autowired 등을 전혀 사용하지 않고오로지 설정 클래스의 @Configuration, @Bean을 통해 빈을 등록한다면 CoreApplication을 실행했을 때컴포넌트 스캔이 동작하긴 하지만, 컴포넌트 스캔을 통해 등록되는 빈은 없다고 보면 되나요? 즉, @Bean을 통해 등록되는 빈들은 있어도, 컴포넌트 스캔은 동작은 하되, 스캔을 통해 등록되는 빈은 이 경우엔 없다고 보면 되나요?
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
BindingResult에 관해
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]강의 잘 듣고 있습니다! 학습을 위해 예제 코드를 조금 응용해서 TODO 앱를 만들어보고 있는데 의도한 기능대로 작동이 안 됩니다. 강의 내용과는 조금 방향이 다르지만 BindingResult 관련 질문이기도 하고 하루종일 해봐도 모르겠어서 질문 올립니다. 강의 예제의 [회원 가입] 폼에서 이름을 비워두고 제출한 경우 @Valid 검증 오류가 발생 하여 return "members/createMemberForm" 되고, View에서 <p th:if="${#fields.hasErrors('name')}" th:errors="*{name}"><p>가 삽입되면서 MemberForm의 @NotEmpty 에러 메세지가 출력되는 기능을 조금 바꿔서 해 수정 기능에 적용해 보려고 했습니다. 의도한 기능은 이렇습니다. (코드는 밑에 있습니다)할 일(task) 수정을 누르면 @GetMapping("/tasks/{taskId}/edit")와 매핑된 메서드가 전달된 파라미터의 taskId로 할일 객체를 찾아 TaskForm(할일 Form 객체)에 값을 세팅합니다. 그리고 이 TaskForm 객체를 Model에 "form"이라는 이름으로 값을 담아 전달합니다.View(할일 수정 폼)에서 수정하고자 하는 할일의 현재 name 값, priority 값를 보여줍니다. (수정을 위해 현재 값을 표시해줌)수정 폼을 채우고 제출을 누르면 @PostMapping("/tasks/{taskId}/edit")에 매핑됩니다. 만약 검증에 오류가 있는 경우 return "task/editTaskForm";를 합니다. 즉 editTaskForm.html 문서를 엽니다. 문제 상황 :그런데 editTaskForm.html 파싱에 실패합니다. 해당 Task의 현재 값을 미리보기 해주려면 ${form.name}을 참고해야 하는데 form이 넘어가지 않아서 인 것 같습니다. 그래서 @SessionAttributes("form")로 세션에 저장해주었습니다(스프링 2.7입니다).2. 이제 파싱은 되고 해당 Task의 현재 값 미리보기도 됩니다. 그런데 여전히 <p> 태그가 생성되지 않아서 오류 메세지도 출력이 안 됩니다. BindingResult가 날아간 것 같습니다.3.방법을 바꿔서 View를 바로 여는 게 아니라 @GetMapping으로 리다이렉트를 해보았는데 역시 Model이 초기화되면서 BindingResult가 날아가는 것 같습니다 ㅠㅠ검색해보니 RedirectAttributes를 써야 한다는데 저는 강의와 똑같이 스프링 2.7을 쓰고 있는데 3.1 미만 버전에서는 GET 파라미터로 전달하는 것 외엔 방법이 없을까요? @Controller @RequiredArgsConstructor @SessionAttributes("form") public class TaskController { private final TaskService taskService; @ModelAttribute("form") public TaskForm getTaskFrom() { return new TaskForm(); } @GetMapping("/tasks/{taskId}/edit") public String editTaskForm(@PathVariable Long taskId, Model model) { Task task = taskService.findOne(taskId); TaskForm form = new TaskForm(); form.setName(task.getName()); form.setPriority(task.getPriority()); model.addAttribute("form", form); return "task/editTaskForm"; } @PostMapping("/tasks/{taskId}/edit") public String editTask(@PathVariable Long taskId, @Valid TaskForm form, BindingResult result, SessionStatus sessionStatus) { if (result.hasErrors()) { return "task/editTaskForm"; } taskService.editTask(taskId, form.getName(), form.getPriority()); sessionStatus.setComplete(); return "redirect:/tasks"; } } JPA 활용 2편까지도 들었고 MVC 1까지도 들었는데.. MVC 2의 BindingResult 강의를 듣고 다시 해보는게 좋을까요?하지만 너무 해결하고 싶습니다.........🥺 어느 부분을 체크해보면 좋을까요?
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
RestController 가 Id 를 인식한다는게 뭔지 잘 모르겠습니다.
[질문 내용]약 8분 10초 쯤에서 RestController 를 사용하지 않아 "ok"라는 문자열을 ID 로 인식 못한다고하셨는데 이때 ID 라는게 뭔가요? 그리고 또 ... ResponseBody 가 ResponseEntity 보다 우선순위인가요?? 강의 7분쯤에서 내용이 잘 이해가 안가서요..ResponseBody 가 HTTP 응답 값을 바로 HTML에 뿌려주기 때문에 Entity는 무시되는건가요..? 무시된다면 어떤 결과를 가지고 오는지가 궁금합니다.. -> 정정 : ResponseEntity 는 ResponseBody가 필요없다는 걸 알게 되었는데요. 이때 ResponseEntity에도 ResponseBody 가 적용되면, ResponseBody는 그냥 무시되고 ResponseEntity 동작 방식이 우선순위가 되어 작동하는건지 질문변경합니다.
-
해결됨스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
404오류 servlet
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용] servlet 들어가자마 다 404가 뜹니다..ㅜㅠ뭐가 문제일까여 유료버전 사용
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
오류가 떴는데 코드를 봐도 잘 모르겠네요..
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]이유를 잘 모르겠네요...
-
해결됨스프링 핵심 원리 - 고급편
@AfterReturning에서 질문있습니다.
안녕하세요 강사님!강사님께서 말씀해주신 AOP로 구현하면 좋은 기능을 실제 코드로 작성해봤습니다.로그 트레이스기능에 매서드 종료가 1초 5초 10초가 될때 로그 레벨을 높여서 로그를 찍는 기능입니다. @Aspect @Slf4j static class LogTraceSelf { ThreadLocal<TraceStatus> trace = new ThreadLocal<>(); private TraceStatus SynchronizedTrace(){ TraceStatus traceStatus = trace.get(); if (traceStatus == null) { trace.set(new TraceStatus()); traceStatus = trace.get(); } return traceStatus; } @Pointcut("@annotation(hello.aop.exam.annotation.Trace)") public void logTrace() {} @Before("logTrace()") public void doTrace(JoinPoint joinPoint) { TraceStatus traceStatus = SynchronizedTrace(); traceStatus.begin(joinPoint.getSignature().getName()); } @AfterReturning("logTrace()") public void doTraceSuccess(JoinPoint joinPoint) { TraceStatus traceStatus = SynchronizedTrace(); long gap = traceStatus.end(joinPoint.getSignature().getName()); } @AfterThrowing(value = "logTrace()", throwing = "ex") public void doTraceException(JoinPoint joinPoint, Exception ex) { TraceStatus traceStatus = SynchronizedTrace(); traceStatus.end(joinPoint.getSignature().getName(),ex); } }조인포인트 실행 권한이 필요없는 부가 기능이다보니@Around 사용을 하지 않고 나누어서 처리를 하다보니 불필요한 코드가 더 증가하게되었습니다.그리고 @Around와 다르게 어드바이스내 지역변수에 TraceStatus를 저장하고 사용할수 없기때문에ThreadLoacal에 공유할 데이터와 본인이 가지고 있어야하는 timestamp도 저장했습니다.@Slf4j public class TraceStatus { private final String id; private int depth = 0; private Deque<Long> timeStampHolder = new ArrayDeque<>(); private final int offsetInfoMs; private final int offsetWarningMs; private final int offsetErrorMs; }기능은 stack 자료구조를 활용해서 begin을 할때마다 timeStampHolder에 시작시간을 저장합니다.end를 호출하면 pop으로 자신이 넣은 시작 시간을 꺼내서 시간을 계산하고 로그를 출력합니다.기타 기능 메서드들은 강사님이 작성하신거와 유사해서 제외했습니다. 이제 소요된 시간이 1초, 5초, 10초가 걸릴 경우 info,warning,error로 출력하는 로그를 남기려고할때어드바이스가 조건을 가지고 추가 기능을 만든다.위 코드처럼 로그 트레이스 내에 저장하고 로그 트레이스 내부에서 출력하도록 한다.경고 알림용 트레이스를 따로 만들고 상태를 전달한다.제 생각은 트레이스에게 넘길경우 트레이스를 인터페이스로 만들어서 필요에 따라 다른 트레이스를 사용한다고 한다면 이 기능을 계속 구현해야 된다는 단점이 생길거 같습니다.저라면 어드바이스에서 소요시간을 트레이스에게 받아서 추가 로직을 수행하려고 할거같습니다. 강사님께서 코드를 작성하신다면어떤 포인트컷을 사용하셔서 구현하실건지,그리고 애노테이션에서 넘어온 값을 가지고 어드바이스에서 처리하게 하시는지아니면 로그 트레이스 내에서 처리하게 하실건지 그 이유가 궁금합니다.
-
미해결스프링 핵심 원리 - 기본편
코드를 똑같이 따라쳤는데 사용자 등록 빈 전체가 안 나와요
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]직접 등록한 빈에서 이렇게만 나옵니다.AppConfig 설정 정보의 모든 메서드들이 나오지 않고 클래스만 나오네요.name = appConfig object=hello.core.AppConfig@3cce5371
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
form 안에 form이 있을 경우 vo를 어떻게 사용해야 할까
<form th:action=@"{/board/create}" th:object="${board}"> <input type="text" th:field="*{name}"> <button type="button" th:onclick="boardSubmit">글작성</button> <form th:action=@"{/board/create2}" th:object="${notice}"> <input type="hidden" th:field="*{boardId}"> <input type="text" th:field="*{title}"> <button type="button" th:onclick="noticeSubmit">글작성</button>위에 적은 소스는 샘플로 간단하게 Vo 안에 하나의 필드들만 들어갔을 경우를 예로 작성한 내용입니다.현업에서 작업을 하다보면 글 작성 form 안에 여러개의 form 이 들어가야 하는 경우가 한번씩 발생이 됩니다. 이럴 경우에는 어떤식으로 구조를 잡아야 할지 궁금합니다.@Getter @Setter public class RequestBoard{ private String name; private RequestNotice Notice; @Getter @Setter private static class RequestNotice { private String notice } }위와같이 innerClass를 이용해서 작성을 했는데 잘 안됩니다. 구현하고자 하는 기능은.boardSubmit을 클릭했을 때는 board의 name값이 저장되고,noticeSubmit을 클릭했을때는 title이 저장이 되게 하고 싶습니다.javascript에서 값을 받아서 ajax로 넘겨서 저장을 할려고 하면 input 값들이 많이 있을 경우에는 해당 vo도 만들어야하고, script에서도 그 값들을 일일히 받아서 컨트롤러로 넘겨줘야 하는데.. 타임리프를 이용해서 할 수 있는 방법이 있을까요?
-
미해결스프링 시큐리티 OAuth2
Session id가 인가 코드와 access token을 교환하는 도중에 변경됩니다.
안녕하세요. 강의 잘 듣고 있습니다.제목에서처럼, session id가 access token을 교환하는 일련의 과정 중에 변경되어 다음과 같은 오류가 브라우저 상에서 보여집니다.해당 에러 메시지를 내뱉는 로직이 OAuth2LoginAuthenticationFilter 클래스의 attemptAuthentication 메소드에 있음을 확인하였습니다. 제가 찾은 원인은 OAuth2AuthorizationRequestRedirectFilter에서 AuthorizationRequest의 저장까지는 성공적인데, OAuth2LoginAuthenticationFilter에서 AuthorizationRequest를 Repository에서 꺼내오려고 시도할 때 바뀐 session id값 때문에 이전의 AuthorizationRequest 객체를 가져오지 못하고 null을 반환하는 것이었습니다. 이렇게 session id가 바뀌는 원인이 무엇인지 알려주시면 감사하겠습니다 ㅠㅠ
-
미해결스프링 핵심 원리 - 기본편
강의 관련 질문
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]안녕하세요. 강의를 들으며 헷갈리는 부분 질문 남깁니다. 회원을 가입하고 조회할 수 있다. 회원은 일반과 VIP 두 가지 등급이 있다.회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다. (미확정)먼저 회원은 가입하고 조회할 수 있는 기능이 있고 이를 역할과 구현으로 구분하기 위해 역할인 MemberService와 구현인 MemberServiceImpl을 생성했다고 이해했습니다. 그 다음부터 헷갈리기 시작하는데요. 여기서부터 질문입니다. MemberRepository 라는 interface를 생성했는데. 일단 저는 Repository를 현업에서 거의 사용한 적이 없어서 명칭부터가 익숙치 않았는데요. 제가 이해하기로는 주로 외부와의(DB) 통신을 담당하는 친구로 DAO와 유사한 역할을 하는 것으로 이해했습니다. 맞나요? 다만, 자체 DB를 구축할지 외부시스템과 연동할지 정해지지 않았기 때문에 Interface로 생성한거고, 만약 정해져 있다면 굳이 Interface로 생성하지 않아도 될 것 같은데 맞나요? 제가 현업에서 일하면서 Service와 DAO를 연결할때는 보통 같은 기능명칭을 사용했는데 여기서는 join을 save로, findMember를 findById로 사용하더라구요. 명칭을 다르게 하는 이유가있나요?
-
미해결스프링 핵심 원리 - 기본편
컴포넌트 이름을 같게 해도 에러가 뜨지 않아요 ㅠ (자동)
MemberServiceImpl, OrderServiceImpl 이름을둘다 service로 이름을 설정했는데AutoAppConfigTest 에서 돌려봐도 테스트가 통과하고에러가 뜨질 않네요왜 이럴까요? 영한님 강의 너무 잘 듣고 있습니다 좋은 강의 찍어주셔서 감사합니다!
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
JPA1편 -> JPA2편 -> 기본편 순으로 학습해도 될까요?
김영한 강사님 추천 로드맵 수강 순서를 보면 JPA1편 -> 기본편 -> JPA1편 복습 -> JPA2편으로 되어있는데요 제 시간 사정상 최대한 빨리 익혀야 하기도 하고, API 설계나 DTO가 너무 궁금해서 곧바로 JPA2편으로 학습하고 싶습니다.JPA1편에서 모르는 개념들(영속성 컨텍스트, 머지, 도메인 설계)이 많아서 우선 그냥 코드 따라하기 식으로 하고 있는데요JPA2편까지 따라하다가 기본편 다시 봐도 될까요?
-
해결됨스프링 DB 2편 - 데이터 접근 활용 기술
문서 오타 제보합니다!
스프링 트랜잭션 이해12페이지 메서드에 하나라도 있으면 있으면-> 메서드에 하나라도 있으면 감사합니다:)
-
해결됨스프링 핵심 원리 - 기본편
@Autowired 필드 명, @Qualifier, @Primary 강의에서 오류가 저는 BeanDefinitionTest에서 납니다ㅠ
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]선생님 저는 BasicScan 테스트가 아니라 엉뚱한 곳에서 오류가 납니다 ㅠorg.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderService' defined in class path resource [appConfig.xml]: Unsatisfied dependency expressed through constructor parameter 1: Ambiguous argument values for parameter of type [hello.core.discount.DiscountPolicy] - did you specify the correct bean references as arguments? 빈이 2개 일 경우 첫번째 방법으로 @Autowired 생성자에서 파라미터명을 rateDiscountPolicy로 바꾸는 방법을 따라했는데 왜 저런 오류가 날까요? AppConfig.xml<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="memberService" class="hello.core.member.MemberServiceImpl"> <constructor-arg name="memberRepository" ref="memberRepository" /> </bean> <bean id="memberRepository" class="hello.core.member.MemoryMemberRepository" /> <bean id="orderService" class="hello.core.order.OrderServiceImpl"> <constructor-arg name="memberRepository" ref="memberRepository" /> <constructor-arg name="discountPolicy" ref="discountPolicy" /> </bean> <bean id="discountPolicy" class="hello.core.discount.RateDiscountPolicy" /> </beans> OrderServiceImplpackage hello.core.order; import hello.core.discount.DiscountPolicy; import hello.core.discount.FixDiscountPolicy; import hello.core.discount.RateDiscountPolicy; import hello.core.member.Member; import hello.core.member.MemberRepository; import hello.core.member.MemoryMemberRepository; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component //@RequiredArgsConstructor 롬복 public class OrderServiceImpl implements OrderService{ private final MemberRepository memberRepository; private final DiscountPolicy discountPolicy; @Autowired public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy rateDiscountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = rateDiscountPolicy; } @Override public Order createOrder(Long memberId, String itemName, int itemPrice) { Member member = memberRepository.findById(memberId); int discountPrice = discountPolicy.discount(member, itemPrice); return new Order(memberId,itemName,itemPrice,discountPrice); // 최종 생성된 주문을 반환 } //테스트용 public MemberRepository getMemberRepository(){ return memberRepository; } } BeanDefinitionTestpackage hello.core.beandefinition; import hello.core.AppConfig; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; public class BeanDefinitionTest { //AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); GenericXmlApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml"); @Test @DisplayName("Bean 설정 메타정보 확인") void findApplicationBean(){ String[] beanDefinitionNames = ac.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName); if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){ System.out.println("beanDefinitionName = " + beanDefinitionName + "beanDefinition = " + beanDefinition); } } } } package hello.core.xml; import hello.core.member.MemberService; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; import static org.assertj.core.api.Assertions.assertThat; public class XmlAppContext { @Test void xmlAppContext(){ ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml"); MemberService memberService = ac.getBean("memberService", MemberService.class); assertThat(memberService).isInstanceOf(MemberService.class); } }
-
미해결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이 되는지 이해가 되지 않습니다
-
미해결스프링 핵심 원리 - 기본편
MemberRepository의 주소값이 똑같은이유가 싱글톤 때문인가요?
안녕하세요 강의 잘 듣고있습니다 다름이아니라 memberRepository1(), memberRepository2() 둘의 메서드의 value값의 주소가 사진처럼 똑같은데요 이게 싱글톤이라서 주소를 공유하기때문에 주소값이 똑같은건가요?
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
전 강의에서 들었던 내용인데 기억이 안나네요
로그인 페이지에서 아이디 혹은 비밀번호가 오류면아이디,비밀번호를 입력하는 페이지를 다시연결해주는데ID값은 남아있고 비밀번호폼데이터는 없어지는걸 볼수있어요 전 강의에서 이 부분을 공부했었는데 어떻게 하는지 기억이 안나네요비밀번호도 유지할려면 어떻게 해야되나요 ?
-
미해결스프링 핵심 원리 - 기본편
@Component로 자동 빈 등록시 생성자가 2개인 경우 생성자를 선택하여 Bean을 생성할 수 있나요?
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]안녕하세요.강의 내용중 궁금증이 있어 질문 올립니다.만약 @Component로 자동 빈 등록시 빈으로 등록될 class에 생성자가 2개인 경우생성자를 선택하여 Bean을 생성할 수 있나요?예를 들면 아래와 같습니다.@Componentpublic class MemberServiceImpl implements MemberService {private final MemberRepository memberRepository;@Autowiredpublic MemberServiceImpl(MemberRepository memberRepository) {this.memberRepository = memberRepository;}/* MemberRepository 객체를 인자로 2개 받는 생성자를 호출 가능한지? */@Autowiredpublic MemberServiceImpl(MemberRepository memberRepository1, MemberRepository memberRepository2) {this.memberRepository = memberRepository;}} getBean 으로 빈 조회를 할때MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class); MemberServiceImpl 클래스의 두번째 생성자(인자값 두개)를 호출하여 생성하는 방법이 있나요?@Bean 메서드를 만들어서 강제로 new MemberServiceImpl(MemberRepository memberRepository1, MemberRepository memberRepository2);위 방식으로 하면 될것 같은데 혹시 @Bean을 사용하지 않고 생성하는 다른 방법이 있을까요?