JPA 프록시의 field 접근과 get메서드 접근
2022. 5. 4. 19:51
JPA
문제 서비스 테스트는 성공하는데 API 요청 테스트는 실패하는 현상이 발생했다. 상황 설명 댓글을 작성한다. 대댓글을 작성한다. 대대댓글을 작성하면 대댓글로 작성되어야 한다. 3번이 문제 상황이다. 3번을 서비스 레어어에서 테스트 코드로 테스트할 때는 정상 동작했다. 하지만, API 요청 테스트를 하면 실패했다. 실패했다는건 아예 작성이 되지 않는 상황이었던 것이다. 원인 트랜잭션과 하이버네이트 프록시가 원인이었다. 하이버네이트 프록시 원인 하이버네이트 프록시는 아래와 같은 구조를 기진다. 동작 과정을 살펴보자. getName() 요청 시 프록시 객체의 타겟은 null이다. 진짜 객체에서 가져오도록 영속성 컨텍스트에 요청한다. 영속성 컨텍스트에 없으면 DB 조회를 한다. 실제 엔티티를 생성한다. 프록시는..
스프링 순환 참조와 생성자 주입을 사용해야 하는 이유
2022. 4. 20. 16:59
Spring
스프링에는 3가지 빈 주입을 제공한다. 필드 주입, 수정자 주입, 생성자 주입이다. 대부분 스프링을 한다면, 알고 있는 내용일 것이다. 하지만 조금 더 알아 보자. 생성자 주입을 사용해야 하는 이유 필드 주입 장점 사용하기 가장 편하다. 단점 순환 참조를 컴파일 타임에 알 수 없다.(스프링 부트 2.5이하) 런타임에 로직 호출 시에 알 수 있다. 컴파일 시점에는 각자 주입만할 뿐 누가 자신을 사용하고 있는지는 알지 못하기 때문이다. 하지만 2.6이상 부터는 이를 개선하여 컴파일 타임에 알 수 있다. 객체를 바꿀 수 없다. 즉, 테스트 할 때 불편하다. 수정자 주입 장점 객체를 바꿀 수 있다. 즉, 테스트 할 때 편하다. 단점 순환 참조를 컴파일 타임에 알 수 없다.(스프링 부트 2.5이하) 런타임에 로직..
JPA Auditing으로 생성시간, 수정시간 자동화하기
2022. 4. 19. 12:18
Spring
BaseTimeEntity.java @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public class BaseTimeEntity { @CreatedDate private LocalDateTime createDate; @LastModifiedDate private LocalDateTime modifiedDate; public LocalDateTime getCreateDate() { return createDate; } public LocalDateTime getModifiedDate() { return modifiedDate; } } @MappedSuperclass → JPA Entity 클래스들이 BaseTimeEntity를 상속할..
스프링에서 Enum과 Bean Validation으로 예외 관리하기
2022. 4. 18. 23:41
Spring
서론 개인 프로젝트를 진행하던 중 예외 코드가 하드 코딩으로 관리되고 있어서 방법을 찾아보던 중 세 가지 후보를 고민했다. 설정 파일 DB enum 비지니스 커스텀 예외는 Enum 실무에서는 설정 파일과 DB를 사용했었다. 이미 그렇게 되어있어 고민없이 사용하게 되었다. 이 때 느낀 것은 아래와 같았다. 비지니스 커스텀 예외란 직접 개발자가 throw new 하는 것으로 정의했다. 설정 파일 코드를 직접 문자열로 매핑을 해야한다. 생산성 저하 객체에 매핑하여 사용할 수 있지만 설정 파일을 읽어오기 위한 빈 주입이 필요하기 때문에 번거로울 수 있다. DB 네트워킹이 필요하다. 하지만 처음 메인화면 오픈 시 캐싱해두면 되기 때문에 문제는 아니다. 재적용이 아주 쉽다. 배포가 필요가 없다. 최고의 장점이다. ..
Controller와 Service 레이어 간 의존성 관리
2022. 4. 15. 18:03
Spring
컨트롤러와 서비스 레이어 간 파라미터 의존성 관리를 어떻게 해야할까? 고민한 내용은 간단하다. Controller → Service로 파라미터를 넘길 때 의존도를 어떻게 할지이다. Controller @GetMapping(value = "/members/me", produces = MediaType.APPLICATION_JSON_VALUE) public MemberResponse findMemberOfMine(@Login LoginMember loginMember) { Member member = memberService.findMemberOfMine(loginMember.getEmail()); return new MemberResponse(member); } Service @Transactional(r..
테스트 더블 테스트 VS 실제 객체 테스트(classicist vs mockist)
2022. 4. 15. 17:47
기타 IT
테스트 더블과 실제 객체란 테스트 더블 테스트에 필요한 협력 객체들을 가짜 객체로 대체하는 것 내부 구현을 알아야하기 때문에 화이트 박스 테스트가 된다. 실체 객체 테스트에 필요한 협력 객체들을 그대로 사용하는 것 내부 구현을 알지 못하므로 블랙 박스 테스트가 된다. classicist vs mockist MemberService.java // 내 회원 정보 조회 @Transactional(readOnly = true) public Member findMemberOfMine(String email) { return memberRepository.findByEmail(email); }classicist 대부분 실제 객체 사용을 선호하고, 실제 객체 사용이 어렵다면 테스트 더블을 사용한다. 당연히 실제 객체..
@NotNull vs @Column(nullable = false)
2022. 4. 15. 14:09
Spring
서론 여기까지 고민하게 된 계기는 Entity에서 필드 검증을 직접해야 할까? 였다. 그러면 항상 로직을 짜아하고, null체크와 같은 것들은 굉장히 중복되는 것들인데 모든 Entity에 비슷한 코드가 들어가게 된다. 그리고 Entity 검증 테스트를 작성할 필요가 있을까? 단순한 검증 로직 귀찮은데? 기존에는 아래 코드와 같이 직접 StringUtils.isBlank와 같이 검증을 했다. 그리고 테스트 작성도 했다. @Entity public class Member { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String email; @Column(nullable = false) private Str..
@Configuration과 @TestConfiguration
2022. 4. 15. 12:48
Spring
문제점 OAuth 로그인을 구현하다 보니, 로그인 테스트를 작성하기 위해 OAuth에 요청 로직을 목킹해야하는 일이 생겼다. 어차피 OAuth 서비스는 요청만 잘하면 정상적인 동작이 보장되므로 실제로 요청할 필요가 없기 때문이다. 내가 테스트해야 하는 것은 요청에 의해 받아온 승인코드, accessToken, 회원 정보들을 조합하여 나의 서비스에 로그인이 되는지 회원가입이 되는지에 대한 것이다. 하지만 이를 인터셉터에서 처리하고 있다보니, 어떻게 목킹해야 할지 고민하게 되었다. @TestConfiguration을 사용했는데 안된다. 여러 글을 찾아보니 @TestConfiguration을 사용하면 된다는 것을 알게 되었다. 먼저 코드 구조는 WebConfig에서 실제 환경 인터셉터를 구성하고, TestWe..