묻고 답해요
138만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결실전! 스프링 데이터 JPA
Unit Test 관련 질문입니다.
안녕하세요 영한님 ! 어제 h2 관련해서 질문을 드렸었는데요 좋은 답변 감사했습니다! 다름이 아니라 service unit test 를 하는 과정에서 repository 의 Pageable 을 mock 으로 주입해야 하는 상황이 왔습니다. 현재 junit 5 의 bdd 를 이용해서 테스트를 하고 있는데요, junit5의 bdd 역시 영한님은 잘 아시겠지만 given()을 이용해서 JpaRepository.findAll(Pageable pageable) 이 호출되면 willReturn() 으로 반환되는 반환 값을 가짜로 주입해야 하는 상황입니다. 하지만 여기서 어떻게 Page<Entity> pagedEntitied 를 주입해야 하는지 몰라 진행이 힘들더라구요 ㅠㅠ 수업의 방향과 상관 없는 내용이라 실례임을 인지하고 있지만 어떻게 해결해야 할지 도통 갈피를 못 잡아서 질문드려 봅니다.. import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class MemberServiceTest { private MemberService memberService; private final MemberRepository memberRepository = mock(MemberRepository.class); @BeforeEach void setUp() { ModelMapper modelMapper = new ModelMapper(); modelMapper.getConfiguration() .setFieldAccessLevel(Configuration.AccessLevel.PRIVATE) .setFieldMatchingEnabled(true); memberService = new MemberService(memberRepository, modelMapper); // 문제의 willReturn 반환 타입.. 현재는 null을 넣어놨습니다. given(memberRepository.findAll(any(PageRequest.class))).willReturn(null); } @Test @DisplayName("모든 사용자 조회") void getMembers() { // when PageRequest pageRequest = PageRequest.of(0, 5); // then Page<MemberResponseData> pagedData = memberService.getMembers(pageRequest); assertEquals(pagedData.getSize(), 5); } } @Entity @Getter @Builder @AllArgsConstructor @NoArgsConstructor public class Member extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String email; private String password; private String nickname; public static MemberResponseData fromEntity(Member member) { return MemberResponseData.builder() .email(member.getEmail()) .nickname(member.getNickname()) .build(); } } // Page<MemberResponseData>에 해당하는 MemberResponseData(DTO) @Getter @Builder @AllArgsConstructor @NoArgsConstructor public class MemberResponseData { private String email; private String nickname; } // MemberService @Service public class MemberService { private final MemberRepository memberRepository; private final ModelMapper modelMapper; public MemberService(MemberRepository memberRepository, ModelMapper modelMapper) { this.memberRepository = memberRepository; this.modelMapper = modelMapper; } /** * 모든 회원을 조회한다. * * @param pageable : 페이징 정보 * @return 모든 회원 */ public Page<MemberResponseData> getMembers(Pageable pageable) { return memberRepository.findAll(pageable).map(Member::fromEntity); } }
-
미해결Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)
UUID 사용 이유
안녕하세요! 아직 학생이라 모르는 부분이 많아 자주 질문을 드리네요 ㅎㅎ.. 항상 감사하게 생각하고 있습니다. 다름이 아니라 email 이 아니라 UUID를 이용해서 토큰을 만드는 이유에 대해서 궁금합니다. 검색을 해보니 UUID는 를 사용하는 이유는 중복되지 않게 하기 위함 이라고 이해하였습니다. 하지만 사용자 관련 서비스에서 회원가입 시에 email이 동일한 사용자는 회원가입이 안되게 한다면 중복성에 대한 문제는 해결할 수 있지 않을까? 그럼 users 테이블에 있는 id 컬럼과 userId 컬럼 중 하나는 사라져도 되겠다. 그럼 DB에 저장해야할 칼럼도 줄어들고, 더 좋지 않을까? 라고 생각을 하게 되었습니다. 하지만 강사님이 users 테이블에 id 컬럼과 userId 컬럼을 두면서 까지 UUID 를 쓰는 이유는 분명히 존재할 것 같아서 이렇게 질문 올려봅니다..!
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
회원가입 제출 이후 whiteLabel
안녕하세요 강사님. 강의 보며 많이 배우고 있습니다. 강의 내용대로 쭉 따라가며 진행하고 있는데 회원가입 부분에서 회원가입 제출 누르면 홈으로 리다이렉트가 안되고 403에러와 화이트라벨 페이지로 이동하게 됩니다. create 메서드에 로그를 찍어도 안찍히는걸 보니 해당 post 매핑이 잘못되었나 하여 준비해주신 자료에 있는 소스를 복사해 똑같이 붙여넣기해도 마찬가지네요... 혹시 원인이 무엇일까요..ㅠㅠ
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
NoSQL과 JPA 관련 질문입니다.
안녕하세요 영한님 매일매일 강의 정말 너무너무 잘 듣고 있습니다. 수업과 살짝 관계가 없을 수도 있는 질문일수도 있지만 궁금해서 여쭤봅니다 ... ^^ 수업에서 사용하시는 케이스는 전부 관계형 데이터 베이스를 사용하는 경우, JPA를 어떻게 잘 사용할지에 대한 내용인 것 같은데 혹시 NoSQL 계열의 데이터베이스를 사용할 경우에는 JPA를 사용하는 것이 비효율적인지 또는 사용을 못하는건지 같은 관점에서 궁금합니다 (찾아보니까 몽고DB랑 같이 사용하는 케이스가 있는 것 같긴하던데..잘 감이 안오더라구요) 답변 주시면 감사하겠습니다!!
-
미해결Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)
actuator 인증 관련
안녕하세요 선생님, actuator 관련해서 질문이 있습니다. 실습에서 actuator 관련해서 user-service의 WebSecurity에는 아래와 같은 코드를, http.authorizeRequests().antMatchers("/actuator/**").permitAll(); apigateway-service의 application.yml 파일에서는 아래와 같은 코드를 추가했습니다. - id: user-service uri: lb://USER-SERVICE predicates: - Path=/user-service/actuator/** - Method=GET, POST filters: - RemoveRequestHeader=Cookie - RewritePath=/user-service/(?<segment>.*), /$\{segment} 제가 궁금한 것은, actuator로 누구나 접근할 수 있게 permitAll() 설정을 해주었고, 인증 필터 또한 설정하지 않았는데 포스트맨으로 actuator 접근할 시 토큰이 필요한 이유에 대해 잘 모르겠습니다. (8분 15초 이후 관련)
-
해결됨실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
회원가입 시 비밀번호 입력에 대한 질문
안녕하세요 영한님 질문이 있습니다. 회원가입을 할 때 비밀번호를 입력하는 예제를 만들어 보려고 합니다. 회원가입 시에 비밀번호를 입력하고 비밀번호 확인을 입력하여 둘이 같으면 회원가입을 요청하도록 로직을 짜려고 하는데, 이 로직을 어느 계층에 넣어야 할지 고민이 됩니다. 비밀번호 확인이라는 칼럼을 멤버 클래스에 추가하지 않고 클라이언트에서 넘겨준 데이터를 확인만 하고 버리려고 하는데 그러면 MemberApiController에서 직접 처리를 해야하는 걸까요? 감사합니다.
-
미해결실전! Querydsl
queryDsl에서는 DATE 함수 지원 하지 않나요?
SELECT * FROM member AS m INNER JOIN history AS h ON (m.id = h.id) WHERE DATE(h.created_at) = DATE_ADD(CURDATE(), INTERVAL -1 DAY) 해당 쿼리문을 만들어야 하는데요... 지금 까지 제가 해본 것 입니다!!!! 함수 () { LocalDate minusDays = LocalDate.now().minusDays(1); DateTemplate<LocalDate> localDateDateTemplate = Expressions.dateTemplate(LocalDate.class, "DATE({0},'%Y-%m-%d')", subQEmrConsultationHistory.createdAt, ConstantImpl.create("%Y-%m-%d")); BooleanBuilder booleanBuilder = new BooleanBuilder(); booleanBuilder.and(localDateDateTemplate.eq(minusDays)); return queryFactory .from(qhistory) .join(qhistory.member) .where(booleanBuilder) } 이렇게 한번 해보았는데 당연히 안되었네요... 이방법이 아닌거 같은데요... 이 경우 어쩔수 없이 Mybatis로 가야하나요....
-
해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
고급매핑 중 MappedSuperclass 를 질문드립니다.
- 배경 1. @MappedSuperclass 예시 코드에는 create, update 정보만 있었지만, 저는 deleted (소프트 딜리트 플래그) 도 넣고 싶습니다. 2. deleted 는 검색이 필요하기에 객체 관계가 필요하고, 그렇기에 @MappedSuperclass 로 묶을 수 없다는 것을 인지했습니다. 3. 위 조건을 만족하기 위해서 @Entity 를 주면 해결이 되지만, 이 경우 매핑 정보 전달만을 위한 클래스가 테이블로 생성되어 버럽니다. - 질문 1. 현재는 뾰족한 방법을 찾지 못해 모든 테이블에 deleted 를 따로 만들어주고 있습니다.공통적으로 관리할 수 있는 방법이 있을까요?
-
미해결실전! Querydsl
영속성 컨텍스트 쿼리 질문 드립니다.
안녕하세요, 강의 잘 듣고 있습니다. 질문을 드리고 싶어서요. 영속성 컨텍스트에 엔티티A가 있다고 가정해 볼게요. EntityManager의 em.find()를 통해 엔티티A를 조회하면, EntityManager는 영속성 컨텍스트에 있는 엔티티A를 반환하고, 실제 DB에 select 쿼리는 날리지 않는걸로 알고 있어요. 그런데 queryFactory는 생성자 인자로 EntityManager를 받고 있음에도, 왜 해당 강의 7분 21초에서는 select 쿼리가 나가나요?? queryDSL은 EntityManager 차원의 find 실행이 아니고, JPQL 차원의 쿼리 실행이라, 무조건 쿼리가 나가는 건가요??
-
미해결실전! 스프링 데이터 JPA
스냅샷이란..?
선생님 강의 정말 잘 듣고있습니다!! 선생님이 QueryHint에서 readOnly를 켜주면 " 스냅샷을 안찍죠 . . . " 이러셨는데 이떄 스냅샷이 JPA 영속성컨텍스트(1차캐시) 를 의미하는 건가요?
-
미해결Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)
12 Factors
안녕하세요~ 좋은 강의 감사합니다~ 12 Factor내용을 보고싶어서 https://12factor.net/ko/ 사이트에 들어갔는데, 머릿말에서 소개하기를, SaaS앱을 만들기 위한 방법론이라고 소개하고 있습니다. 제가 인프라가 많이 부족하고 Spring Cloud가 어떤것인지 감도 안오지만, Image와 컨테이너를 사용하는 것으로 알고있고, 컨테이너를 사용해서 구축하는 아키텍는 IaaS로 알고있는데, 맞는 접근법인가요?
-
미해결Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)
2:50~3:50 부분
안녕하세요. 강의 감사합니다. 2:50~3:00 부분 컨티뉴 디플로이먼트에서 운영에 반영하기 전에 충분한 테스트를 거치고 그 다음에 반영한다는 말인가요? 그 후에 카나리나 배포 블루그린 배포처럼 문제가 있을 수 도 있지만 새버전에 문제가 발생하면 지속적으로 수정 업데이트 한다는 설명으로 이해한게 맞나요??
-
미해결실전! 스프링 데이터 JPA
상황에 따라 DB table 칼럼이 추가 또는 변경시 jpa 에서 어떻게 다룰수 있나요?
안녕하세요 김영한님 오랜만에 질문을 남깁니다. 프로젝트 중간중간 jpa 관련 의문점이 들때마다 다시한번 책과 이 강좌가 많은 참고가 됩니다. 이 자리를 빌어 다시한번 감사드립니다^^ 제목에서처럼 테이블명과 기본 구조 칼럼은 그대로이나 상황에 따라 아래의 code000 칼럼의 갯수가 추가 또는 삭제가 되는 그런 구조인데 jpa에서 어떻게 다룰 수 있는지 궁금합니다.
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
JPA Count 쿼리 Return 값
안녕하세요 JPA 사용하다 궁금한게 생겨 질문드립니다.현재 강의를 듣고 Spring Data JPA를 사용중인데요공식 문서를 보니 count query 시 return 값이 Long(Wrapper Type)으로 반환된다 합니다.혹시나 싶어서 jpa에서 count 후 return class type을 보니 java.lang.Long으로 반환되네요....궁금한건 count는 sql도 그렇고 jpa 문서상에도 null인 경우가 없는것으로 보이는데요JPA에서 Count 쿼리 호출시 primitive가 아닌 Wrapper로 반환하는 이유가 있을까요??엔티티 ID처럼 nullable로 인한 이득이 있는것도 아닌데, 결과값을 박싱해서 내려주는게 어떤 이득이 있는지 모르겠습니다.마지막으로 Long으로 반환되는 결과값을 아래처럼 long으로 unboxing 해서 사용해도 성능이나 여러가지면에서 손해보는면이 있을까요?@Query("select count(m) from ....... wherer ....")long countMember();
-
해결됨Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)
RestTemplate 나 FeignClient는 API GATEWAY는 통과하지 않나요?
항상 강의 잘 보고 있습니다. 제목 그대로 RestTemplate로 하나 FeignClient로 하나 Gateway log를 살펴보면 user-service만 호출되있네요. 이전에 RestTemplate에서는 url설정시에는 localhost:8000/order-service/%s...이런 식으로 url을 조합했다면 그 이후엔 url을 @LoadBalanced 어노테이션 붙이고 localhost 대신에 마이크로 서비스 이름을 넣었는데 이 방식과 Feign클라이언트로 하는 방식 모두 api gateway 에서는 order service의 로그가 안뜨더라구요. 원래 이 방식으로하면 gateway를 먼저 거쳐가는 게 아닌 마이크로서비스 간에 직접적으로 통신을 하게되나요. 감사합니다.
-
미해결스프링 데이터 JPA
JPA에서 테이블명을 변수값으로 받아와서 select하는 경우
안녕하세요. 강의를 보면서 이런저런 기능을 구현해보고 있는데요. 혹시 API에서 "/api/{TABLE_NAME}"으로 get 을 보내 받아온 TABLE_NAME과 일치하는 테이블을 찾아 해당 테이블의 값을 모두 findAll 하게 할 수 있나요? 복수의 테이블에 대하여 각각 findAll을 하고자 할때 테이블별로 API를 만드는 방법밖에 없는지 궁금합니다. 테이블마다 구조는 각각 다릅니다. 따라서 테이블마다 entity와 repository는 만들어줄 수밖에 없을 것같은데 {TABLE_NAME}을 해당하는 entity와 repository를 찾도록 분기문을 하는게 최선일까요? 네이티브 쿼리도 생각해 봤는데 가능하면 쿼리 사용안하고 해보고 싶은데 검색해봐도 잘 모르겠네요. 조언 부탁드립니다. 감사합니다.
-
해결됨실전! 스프링 데이터 JPA
@Transaction에 대한 궁금중
안녕하세요 영한님. 강의를 들으면서 문득 @Transaction의 위치와 중첩에 대해서 궁금중이 들어서 제 나름대로 몇가지 테스트를 해봤습니다. 첫 번째로, 리포지토리 레이어에 트랜잭션이 적용되어 있고 해당 리포지토리의 메서드를 사용하는 서비스 레이어에도 트랜잭션이 적용되어 있다면 flush는 두 레이어 중 어느 시점에 발생할까 궁금했습니다. 그래서 테스트를 해본 결과 최종적으로 서비스 레이어의 트랜잭션이 끝나는 순간에 모든 쿼리가 적용되는 것을 볼 수 있었습니다. 두 번째로, 위와 동일한 상황에 서비스 레이어의 트랜잭션 옵션만 readOnly라면 리포지토리의 save 메서드는 실제 db에 저장을 할까 궁금해서 테스트해봤습니다. 결과는 물론 서비스 레이어 트랜잭션이 우선이기에 insert 쿼리는 발생하지 않았습니다. 두 테스트를 통해서 트랜잭션 우선권은 좀 더 넓은 범위(?), 즉 요청과 응답에 가까운 레이어의 트랜잭션을 따라간다고 이해가 됩니다. 그렇다면 제 생각에는 컨트롤러에 트랜잭션을 적용한다면, OSIV를 사용하지 않아도 영속성 컨텍스트가 화면을 띄울 때까지 유지될 것 같은데 막상 해보면 no Session 에러를 뿜내요.. 컨트롤러에서 html 파일을 렌더링해서 반환하는게 아니라 컨트롤러에서 정적 파일로 일단 반환하고 트랜잭션이 종료된 후에 따로 렌더링하는 작업을 거쳐서 그런걸까요? 쓰다보니 말이 길어져서.. 죄송합니다 ㅎㅎ..
-
해결됨실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
강사님 다대다 설정과 개인프로젝트 적용에 대해 질문있습니다
강좌에는 카테고리-상품에 N:M 다대다 관계로 해놓으셨는데요! 제가 30살인데 29살에 퇴사하고 혼자 JPA공부해서 학교다니던 친구와 백1프론트1을 맡아서 프로젝트를 만들고있는데.. 1:N, N:1은 좀 해결했는데 N:M에서 막히고있어요ㅠㅠ 약간 힌트라도 주실 수 있으신지 해서 여쭤봅니다. 질문이 너무 많아서 힘드시면 직접적으로 말씀하셔도괜찮습니다..! 만약에 이런 경우는 어떻게 하나요? (1) ~한다,~되다의 V 중간테이블 1.여러명의 회원은 여러개의 게시글을 찜목록에 추가할 수 있다. 1-1. 여러명의 회원은 여러개의 공고를 찜목록에 추가할 수 있다. 1-2. 여러명의 회원은 여러개의 기업을 찜목록에 추가할수있다. USER(회원)과 기업/공고/게시글 엔티티 사이에 중간테이블을 놓고 유저ID랑, 각각 필요한 기업/공고/게시글 아이디로 매핑해놓은 상태인데요 - 검은색 이렇게 하는게 맞는지 궁금합니다. (2) ~한다,~되다의 V 중간테이블 [회원]은 여러개의 [이력서]로 [공고]에 [지원]할 수 있다라고 해서 이력서와 공고의 N:M관계를 해소하고자 중간테이블을 만들고 지원한다라는 APPLY 엔티티를 따로 만들었는데, JPA에서도 이렇게 하면 되는건가요..? (3) 중간테이블 질문 DB상으로는 외래키가 PK가 되면서 한 테이블안에 PK가 없어도 된다는 식으로 들었는데, 검색을해보니 JPK는 PK가 있어야된다하더라구요!? 만약에 N:M관계 중간에 중간테이블을 만들면 보통은 외래키 2개가 들어가는데, JPA식으로 만들면 PK, FK, FK해서 총 3개가 만들어지는게 맞나요? (4) intelliJ에서 DB연결을 하고 data Diagram Visualization으로 매핑관계를 살펴보니 이렇게 나오는데.. intelliJ나 datagrip에서는 흔히 까마귀발이라고 하는 1:N, N:1 관계는 안보여주더라구요..! 그리고 mysql workbench reverse engineer모드로 erd를 그려봤는데..여기에서는 1:1관계도 1:N으로 나오는데...(분명히 유저와 멤버는 @OneToOne으로 양방향 매핑했는데도 이렇게 나옵니다. 혹시 DB를 설계하면서 잘 되었는지 erd로 뽑아보고싶은데, 아직 완벽하게 보여주는 건 없는건가요..?
-
해결됨실전! 스프링 데이터 JPA
@Modifying에 관한 질문
안녕하세요 영한님!이번 강의로 Modifying 어노테이션의 clearAutomatically 옵션이 왜 필요한건지 잘 이해했습니다. 그런데 다른 옵션인 flushAutomatically는 제가 아는 상식과 충돌이 되서 질문드립니다.기본적으로 createQuery를 날리면 db에 직접 접근하여 처리하기 때문에 현재 영속성 컨텍스트의 1차 캐시에 있는 것들을 강제로 flush하고 작성한 쿼리를 날리는 것으로 이해를 하고 있습니다.벌크연산 역시 createQuery의 일종이라고 생각하기 때문에 flushAutomatically 옵션의 기본값은 true일 거라고 예상했는데 false로 되어있어서 헷갈립니다.1. Modifying에서 제공하는 자동 flush 옵션과 벌크 연산전에 자동으로 일어나는 flush는 의미가 서로 다른건가요?//추가: 구글링을 해본 결과, 두 flush는 같은 의미가 맞고 @Modifying 옵션 값과 상관없이 hibernate에서 강제로 flush를 해준다고 합니다.그리고 이건 질문은 아니고 제가 정리한게 맞는지 여쭤봅니다.강의 영상처럼 테스트 클래스의 한 @Transaction 안에서 멤버를 생성하고 벌크연산으로 변경하고 조회하는 것이 아니라서비스 레이어에서 각각의 @Transaction가 있는 메서드로 분리해서 테스트를 해봤는데, 여전히 동일한 현상이 일어나더라구요.2. 이것은 OSIV에 의해서 영속성 컨텍스트의 생명주기가 트랜잭션의 종료시점이 아닌 최종적으로 response를 뱉을때까지 유지되고, 1차 캐시 역시 유지되기 때문에 각각의 트랜잭션에서 실행되는 서비스 메서드 간에서도 1차 캐시가 공유된다는 뜻으로 이해하면 될까요?
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
테스트 오류 질문드립니다
안녕하세요 14:57초 쯤에서 진행하는 테스트에서 에러가 나는데 확인해주실 수 있을까요? 에러메시지는 아래와 같이 나옵니다. Could not write standard input to Gradle Test Executor 3. java.io.IOException: 파이프가 닫히는 중입니다 at java.base/java.io.FileOutputStream.writeBytes(Native Method) at java.base/java.io.FileOutputStream.write(FileOutputStream.java:354) at java.base/java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:81) at java.base/java.io.BufferedOutputStream.flush(BufferedOutputStream.java:142) at org.gradle.process.internal.streams.ExecOutputHandleRunner.forwardContent(ExecOutputHandleRunner.java:68) at org.gradle.process.internal.streams.ExecOutputHandleRunner.run(ExecOutputHandleRunner.java:53) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) at java.base/java.lang.Thread.run(Thread.java:834) > Task :test FAILED Error: Could not find or load main class worker.org.gradle.process.internal.worker.GradleWorkerMain Caused by: java.lang.ClassNotFoundException: worker.org.gradle.process.internal.worker.GradleWorkerMain FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':test'. > Process 'Gradle Test Executor 3' finished with non-zero exit value 1 * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Get more help at https://help.gradle.org BUILD FAILED in 3s 4 actionable tasks: 1 executed, 3 up-to-date