묻고 답해요
160만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨Spring Boot를 활용하여 채팅 플랫폼 만들어보기
MySQLConfig 관련 질문있습니다.
선생님께서 말씀을 조금 두루뭉술하게 해주셔서인지 ㅠㅠ 다들 궁금해 하시는거같은데 저또한 궁금한 부분들이 있습니다. JPA에서 처음부터 트랜잭션 매니저 구현체로 JpaTransactionManager를 사용하지 않고 auth쪽은 DataSourceTransactionManager를 사용한건 왜 그런건가요?AuthService#createUser()부분에서는 일반 JDBC 기반의 DataSourceTransactionManager를 사용하셨더라구요.이러한 경우 영속성 컨텍스트에서 Entity들의 생명주기까지는 관리가 안되는걸로 알고 있습니다. Lazy Loading이나 Dirty Check 등..채팅관련 트랜잭션 설정할때에는 구체적으로 어떤 메커니즘에 의해 어떤 문제가 발생할 수 있는지 명확히 알려주실 수 있을까요? 제가 몇 번 돌려서 듣긴 했는데 느낌이 STOMP 통신 특성상 발생되는 이슈라고 이해는 한 것 같은데 그게 구체적으로 어떠 어떠한 이유로 오류가 발생한다?혹은 할수도 있다? 는것인지 이해를 못했습니다. 확실히 무조건 오류가 발생을 한다는걸까요? 그리고 그 원인은 무엇일까요?단순히 JPA를 이용한 비즈니스 로직을 작성할때는 JpaTransactionManager가 관리하는 트랜잭션 내에서 작성하자. 그리고 그러기 위해서 createChatTransacationMansger를 사용하자. 라고 이해해도 될까요?JpaRepository 구현체인 SimpleJpaRepository에는 기본적으로 save()에는 트랜잭션이 걸려 있지만 저장 이후에 Entity 조작하는 로직이 있다면 동작하지 않게 되니까요.. 혹시 일반적인 JPA 환경과 별개로 STOMP를 통한 웹소켓 통신 환경에서의 JPA 트랜잭션 처리에 대해 특이사항이 있었던거라면 자세히 설명좀 부탁드려도 될까요? 예를 들어 일반 MVC 프레임웍의 http 통신 환경일때는 스레드 또한 톰캣의 스레드를 사용하지만 STOMP는 환경이 달라서 뭔가 신경써야 할게 있다든지..? 감사합니다.
-
해결됨Practical Testing: 실용적인 테스트 가이드
stubbing을 해줘야 하는 이유
안녕하세요https://inf.run/QwLTR위의 링크와 연계되는 질문입니다. 복습을 하다 보니 언제 when()으로 stubbing 을 해 줘야할까에 대해 헷갈려져서, 저의 사고 흐름을 정리해봤습니다컨트롤러에서는 서비스 단 하위로는 모킹을 한다.모킹하는 객체들은 테스트에서 주 관심사가 아니며, 항상 잘 동작한다고 가정한다.모킹해오는 객체들은 stubbing을 해주지 않는다면 기본 값들을 반환하는 정책을 따른다.위 질문 링크의 "판매 상품을 조회한다." 테스트에서는 모킹 객체인 productService의 메소드가 어떤 리턴값을 반환하든 presentation 레이어에서 검증하고 싶은 내용이 아니기 때문에, 검증을 수행하지 않았고, productService.getSellingProducts()에 대한 stubbing도 굳이 필요 없다."메일 전송 테스트" 에서 mailSendClient.sendEmail()에 대한 stubbing이 필요한 이유모킹 객체인 mailSendClient.sendMail()이 기본 값 반환 정책이 false이어서, 테스트 하고자 하는 mailService.sendMail()에 영향을 미치기 때문이다. (여기서 기본 값 반환 정책이 true였다면 굳이 stubbing 해주지 않아도 된다 -> "메일 전송 테스트"에서 검증하고자 하는 대상이 아니기 때문에)이런식으로 테스트에 검증하고자 하는 대상에 집중하다 보면 모든 동작을 제대로 stubbing했다고 보장하기 어려워 진다 -> Mockist의 치명적인 단점 제가 정리해 본 내용이 적절한지 알려주시면 감사합니다.테스트의 세계는 정말 헷갈리네요..
-
미해결Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)
MSA 개발 배포 방식에 대한 선택과 방향성
안녕하세요! 강의를 마친 후, 여러 가지 궁금한 점이 생겼습니다.지금까지 로컬,및 도커에서 사용한 config-service, apigateway-service, discovery-service(유레카 서버) 의 역할이 Kubernetes 환경에서 대체될 수 있다는 것을 배웠습니다. 구체적으로는:유레카 대체: 각 Pod의 DNS 이름을 사용하여 서비스 간 통신API Gateway 대체: Ingress 리소스를 사용하여 외부 요청을 라우팅(강의엔 없음) Config 서버 대체: RabbitMQ를 통한 busrefresh로 설정 정보를 업데이트하는 대신, Kubernetes의 ConfigMap을 사용하여 설정을 동적으로 업데이트이렇게 Kubernetes에서 많은 기능을 간편하게 처리할 수 있다는 점에 대해 배우면서, 이전의 복잡한 서비스 구조가 생각보다 간단하게 대체될 수 있다는 점에서 약간 허무한 느낌도 듭니다.강의를 마친 후의 궁금증은, 실제 MSA 개발 시에 Kubernetes, Docker, AWS를 사용하는 다양한 배포 방식 중 어떤 선택을 해야 할지에 대한 부분입니다.EC2에 Docker 사용하여 배포EC2에 Kubernetes 사용하여 배포AWS의 EKS, ECS, API Gateway를 사용하는 방법 등 여러가지 방법이 있을텐데요 이와 같은 다양한 옵션 중에서 어떤 방식을 선택 해야하는지 공부의 방향성을 제시해주시면 감사하겠습니다.좋은 강의 감사합니다!
-
해결됨Spring Boot와 React로 배우는 초간단 REST API 게시판 만들기
중괄호 Tab to Exit 설정
js파일에서 html 작성할 때 속성의 중괄호 부분이 tab키를 눌러도 빠져나가지지 않네요.다른 괄호나 따옴표는 나가지는데 따로 설정을 하신건가요?
-
미해결Practical Testing: 실용적인 테스트 가이드
테스트코드 범위
안녕하세요 선생님 질문이 생겨서 글 남깁니다.! 선생님이 생각하시는 가장 이상적인 테스트 코드 범위는 어디 까지인가요??예를들어 controller,service,repository가 있다고 가정하고 선생님 강의처럼 각각 레이어별로 테코를 짜고 service 쪽도 repo를 mock처리하여 단위테스트까지도 진행 하여야하나요?제 질문을 정리하자면 controller,service,repository 각각 단위테스트 작성후 service + repo로 통합테스트 하는게 옳은 이상적인 범위인가 궁금합니다감사합니다
-
미해결Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)
마지막강의 k8s catalog 404 오류
serivce 오타네여
-
해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
실전 예제1 persistence.xml 에러
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]실전 예제1 들으면서 강의따라 persistence 복붙했는데 에러가 납니다. ex1-hellojpa에서는 에러가 안나는데요. <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"> <persistence-unit name="hello"> <properties> <!-- 필수 속성 --> <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/> <property name="jakarta.persistence.jdbc.user" value="sa"/> <property name="jakarta.persistence.jdbc.password" value="123"/> <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <!-- 옵션 --> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> <property name="hibernate.use_sql_comments" value="true"/> <property name="hibernate.jdbc.batch_size" value="10"/> <!-- 강의에서는 아래 auto 을 value= create, 주석 처리해놓으심--> <property name="hibernate.hbm2ddl.auto" value="create" /> </properties> </persistence-unit> </persistence에러 화면입니다.
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
값 타입, 임베디드 타입
[질문 템플릿]1. 강의 내용과 관련된 질문인가요?네2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 네3. 질문 잘하기 메뉴얼을 읽어보셨나요? 네안녕하세요, 임베디드 타입에 대해 수강하던 중 별 것 아닐 수도 있는 것에 대해 제가 생각한 것이 맞나 확인 차 여쭤보고 싶어 질문드리게 되었습니다. 임베디드 타입 강의 중간부터 값타입이라는 말이 많이 나오는데, 임베디드 타입 = 값타입으로 이해를 하는게 맞는 것일까요 ?
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
최악이라 하셨던 jsonignore
여기 강의 섹션2.API개발 기본-회원조회 강의에서는 @jsonIgnore 사용은 최악이라고 하셧거든요. 이유도 납득갔습니다. 그런데,섹션4.API개발 고급-간단주문 조회 V1에서는 최악이라고 하셨던 @jsonIgnore를 쓰시는데 . 어떻게 받아들이면될까요??최악이라고 하셨는데 ,나중에 사용하셔서 학습자입장에서 띠용했습니다.
-
미해결자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
User import문 자동으로 추가하는 키
8강에서 import문 자동으로 추가하는 키가 뭘까요?
-
미해결자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
7강 패키지 관련
혹시 패키지를 dto랑 controller랑 나누는 이유가 뭔가요?
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
페치 조인 질문
[질문 템플릿]1. 강의 내용과 관련된 질문인가요 네2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 네3. 질문 잘하기 메뉴얼을 읽어보셨나요? 네 [질문 내용] 강의 내용대로 쿼리 결과보려고하는데 회원1,2 가져오고 회원3인 teamB를 영속성에 올리는 쿼리가 따로 안돌아요. 연속으로 회원1,2,3가져오는데 새 버전이라 그런건지 제가 설정을 잘못했는지 궁금합니다
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
프록시 초기화 부분에 대해서 이해가 되지않아 추가질문드립니다..
강의를 들어본 후, 다른 질문들도 참고를 해보았는데요. 프록시 초기화(프록시객체의 초기화) : 프록시 객체의 target필드에 실제 엔티티 객체의 참조를 설정하는것.1. <프록시 초기화 과정>em.getReference()를 하게되면, 프록시 객체가 영속성컨텍스트(1차캐시)에 저장된다. 프록시는 내부에 Member target;이라는 멤버변수(필드)를 가지고 있다.member.getName()을 호출한다.프록시가 아직 초기화되지않은 상태이므로, JPA는 영속성 컨텍스트에 초기화 요청을 한다. 영속성컨텍스트가 db조회를 해서 실제 엔티티객체를 생성하고 프록시 객체 내부의 target필드에 실제 엔티티객체의 참조를 설정한다.초기화된 필드를 통해 실제 객체의 메서드를 호출한다.---------------------------------------------------------------------2. em.getReference()를 하게되면, 프록시 객체가 영속성컨텍스트(1차캐시)에 저장된다. 초기화를 해도 실제 객체는 1차캐시에 저장되지않는다. 프록시 객체가 실제 객체의 참조를 가지고있기 때문에 프록시 객체를 통해 실제 객체를 사용할 수 있다. 즉 실제 객체가 1차캐시에 등록되는건 아니고 프록시 객체가 실제 객체의 참조를 가지고 있기 때문에 실제 객체가 1차 캐시에 등록되어 있는 것처럼 사용할 수 있다.==> 프록시 객체만 영속성컨텍스트에 저장되고 실제 엔티티객체는 영속성컨텍스트에 저장되지않는다.---------------------------------------------------------------------3. JPA는 동일한 트랜잭션안에서 동일한 PK에 대해 처음에 em.getReference()를 사용하면 프록시 객체를 반환하고, em.find()를 사용해도 프록시 객체를 반환한다. 이때 프록시 객체가 영속성컨텍스트(1차캐시)에 저장된다. 이후 em.find()를 사용해도 이미 1차캐시에 프록시가 존재하므로 프록시가 반환된다.반대의 경우 동일한 트랜잭션안에서 처음에 em.find()를 사용하면 실제 엔티티가 반환되고, em.getReference()를 사용해도 실제 엔티티를 반환한다. 이때 실제 엔티티가 영속성컨텍스트(1차캐시)에 저장된다. 이후 em.getReference()를 사용해도 이미 1차캐시에 실제 엔티티가 존재하므로 실제 엔티티가 반환된다. 위와 같이 이해하는게 맞을까요..?
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
remove() 정확한 흐름을 알고싶습니다
링크위 링크 속 질문에 대한 답변을 아래와 같이 이해하였습니다. em.remove(member)를 하는 순간에 member가 1차 캐시에서 제거되고동시에 delete 쿼리가 쓰기 지연 SQL 저장소에 저장commit()을 만나면 내부적으로 flush()를 호출하고 쓰기 지연 SQL 저장소에 있는 쿼리가 나간다고 이해했습니다. 그리고 아래와 같은 테스트를 했을 때 의문이 생겼습니다Member member1 = em.find(Member.class, 101L); em.remove(member1); Member member2 = em.find(Member.class, 101L); System.out.print(member2); // null tx.commit();위 테스트의 결과는처음 member1을 찾을 때 select 문 1번remove()로 인한 delete 문 1번이처럼 총 2번 발생했습니다 하지만 제 생각은remove()를 하면서 1차 캐시에서 지웠기 때문에두 번째 find() 시에는 쿼리를 날려야 되는거 아닌가요? 그리고 또 이해가 안되는 부분은 member2를 찍어보면 null 이 나옵니다.commit()을 하기 전, 즉 flush()를 통해remove()에 의해 만들어진 쓰기 지연 SQL 저장소에 저장된delete 문이 나가기 전인데 왜 null이 찍히는 걸까요? 정리하자면두번째 find()는 왜 안 날라가는지member2는 왜 null 인지별도로 궁금한 점쓰기 지연 SQL 저장소에 있는 쿼리들은 들어온 순서대로 나가나요?
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
초기화하면 1차캐시에
안녕하세요. em.getReference()를 통해 프록시 객체를 조회하면 1차캐시에 프록시객체가 저장되고, 이후에 getName()을하더라도 실제 엔티티객체는 1차캐시에 저장되지않고 프록시객체만 1차캐시에 저장되어있는건가요??
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
프록시 객체의 초기화 부분에서
안녕하세요. 강의 내용 15분쯤에 프록시 객체의 초기화 부분에서 설명해주신 부분이 잘 이해가 되지않아서 찾아보고 아래와 같이 정리해보았는데요. 이렇게 이해하는게 맞을까요? 프록시 초기화(프록시객체의 초기화) : 프록시 객체의 target필드에 실제 엔티티 객체의 참조를 설정하는것.em.getReference()를 하게되면, 프록시 객체가 영속성컨텍스트(1차캐시)에 저장된다. 프록시는 내부에 Member target;이라는 멤버변수(필드)를 가지고 있다.member.getName()을 호출해서 초기화 요청을 한다.JPA는 영속성 컨텍스트(1차캐시)에 실제 엔티티객체가 있는지 확인한다. 4-1. 있으면, JPA는 프록시 객체 내부의 target필드에 1차캐시에 있는 실제 엔티티객체의 참조를 설정한다.4-2 없으면, JPA는 db조회를 해서 실제 엔티티객체를 생성하고 프록시 객체 내부의 target필드에 실제 엔티티객체의 참조를 설정한다.초기화된 필드를 통해 실제 객체의 메서드를 호출한다.
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
h2 DB연결 오류
이 상황에서 연결 누르면 에러가 뜨는데 어떻게 해야될까요...
-
해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
데이터 조회 후 변경 시 쿼리 시점이 어떻게 되나요?
질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/)[질문 내용]선생님 질문이 있습니다.강의 22:00 쯤 변경 감지 듣다가 궁금증이 생겼습니다.질문은 영속 컨텍스트에 넣지 않은 상태로 DB에서 바로 가져올 땐 커밋 시점 보다 먼저 쿼리가 발생하나요? package hellojpa; import jakarta.persistence.*; import java.util.List; public class JpaMain { public static void main(String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); try { Member member = em.find(Member.class, 450L); member.setName("XXXX"); // em.persist(member); System.out.println("=================="); tx.commit(); } catch (Exception e) { tx.rollback(); } finally { em.close(); } emf.close(); } } 출력결과 Hibernate:selectm1_0.id,m1_0.namefromMember m1_0wherem1_0.id=?==================Hibernate:/* updatefor hellojpa.Member */update Membersetname=?whereid=?예상 출력 결과는 === select ...update ...이렇게 나올 줄 알았는데요 왜 이렇게 나오지 않을까요?
-
미해결코드로 배우는 React 19 with 스프링부트 API서버
목록(페이징)처리구현 - import package 문의
강의수강중에 Pageable import 관련하여아래와 같은 Pageable을 선택하니까오류가 나오더라구요...type unmatch 형태//import java.awt.print.Pageable; springboot를 사용할 때는아래와 같은 org.springframework의 형태가import 우선순위가 되는것이 맞는건가요?import org.springframework.data.domain.Pageable;
-
미해결
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing 오류
스프링부트에서 Thymeleaf를 이용해 회원가입 폼을 만들고 있는데 폼에 입력을 제대로 하면 의도한대로 회원가입이 잘 됩니다.하지만 폼에 아무것도 입력하지 않으면 유효성 검사에서 @NotBlank를 만나서 그에 맞는 에러 메시지를 출력해야 하는데 저렇게 오류가 뜨네요... 대체 이유가 뭘까요 ㅠㅠ 제 회원가입 코드는 다음과 같습니다. User.java (Entity)@Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor @Entity(name="users") //테이블 이름 public class User { @Id //기본키 = userid @GeneratedValue(strategy = GenerationType.IDENTITY) private long userid; @Column(unique = true) //아이디 중복 방지 private String username; private String password; @Column(unique = true) //이메일 중복 방지 private String email; } RegisterDTO.java (DTO)@Getter @Setter @Builder @AllArgsConstructor @NoArgsConstructor public class RegisterDTO { @NotBlank(message = "아이디를 입력하세요.") private String username; @NotBlank(message = "비밀번호를 입력하세요.") private String password; @NotBlank(message = "이메일을 입력하세요.") private String email; } UserController.java (컨트롤러)@Controller @RequiredArgsConstructor public class UserController { private final UserService UserService; @GetMapping("/register") // 유저 등록 창 불러오기 public String ShowRegister(Model model) { model.addAttribute("userDTO", new RegisterDTO()); return "register"; } @PostMapping("/register") // 유저 등록 public String Register(@Valid RegisterDTO userDTO, BindingResult result, Model model) { if (result.hasErrors()) { // 유효성 오류 발견 System.out.println("유효성 오류"); return "register"; } try { UserService.saveDTOUser(userDTO); // 유저 등록 } catch (IllegalArgumentException e) { // 중복된 사용자 발견 System.out.println("예외 처리"); model.addAttribute("error", e.getMessage()); return "register"; } return "redirect:/userlist"; } } UserService.java (서비스)@Service @RequiredArgsConstructor public class UserService { private final UserRepository userRepository; public void saveDTOUser(RegisterDTO userDTO) { if (userRepository.existsByUsername(userDTO.getUsername())) { throw new IllegalArgumentException("이미 등록된 아이디입니다."); } if (userRepository.existsByEmail(userDTO.getEmail())) { throw new IllegalArgumentException("이미 등록된 이메일입니다."); } User user = User.builder() .username(userDTO.getUsername()) .email(userDTO.getEmail()) .password(userDTO.getPassword()) .build(); userRepository.save(user); } } register.html<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Register User</title> </head> <body> <h1>Register User</h1> <form th:action="@{/register}" method="post" th:object="${userDTO}"> <label for="username">username:</label> <input type="text" id="username" name="username" th:field="*{username}" /> <div th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></div> <br> <label for="password">Password:</label> <input type="password" id="password" name="password" th:field="*{password}" /> <div th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></div> <br> <label for="email">email:</label> <input type="text" id="email" name="email" th:field="*{email}" /> <div th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></div> <br> <div th:if="${error}" class="error"> <p th:text="${error}"></p> </div> <button type="submit">Register</button> </form> <br> <a th:href="@{/userlist}">Back to User List</a> </body> </html> 컨트롤러에서 작성한 "유효성 오류", "예외 처리" 구문은 잘 나오더라구요. 그러면 유효성 검사는 잘 수행하는거 아닌가요?