묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
CommentService에서 Repository를 호출하지 않는데도
CommentService에서 Repository를 호출하지 않는데도 DB에 저장되는 이유가 뭔가요? @Transactional 어노테이션에 기능이 있는 것 같은데 이런 건 JPA를 좀 더 공부해야 알 수 있는 내용일까요?
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
javax.validation
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]여기에 질문 내용을 남겨주세요.전체 코드: https://github.com/StrawberryRabbit0623/Spring_study/tree/section7_halt스프링 3.2.2 버전으로 강의를 따라하고 있습니다. 스프링 버전 2.x 에서는 아래 starter-validation이 기본으로 지원된다고 알고있습니다. 그런데 문제는 아래 implementation을 작성해도, javax.validation이 인식되지 않습니다. implementation 'org.springframework.boot:spring-boot-starter-validation' 아래 jakarta implimentation 혹은 javax 는 javax.validation을 가능케 해주지만, 정작 @Valid가 전혀 작동하지 않습니다. 정확히는, 회원가입 시 이름란을 비우고 가입해도 전혀 문제 없이 홈페이지로 복귀합니다. 혹시나 하여 @NotBlank로 바꾸어보았지만 여전합니다. implementation 'jakarta.validation:jakarta.validation-api:2.0.2'implementation 'javax.validation:validation-api:2.0.1.Final'종속성, 코드, 아니면 그 외의 어떤것이 문제인지 모르겠어 질문남깁니다..
-
해결됨Practical Testing: 실용적인 테스트 가이드
강사님은 테스트를 어떻게 하시는지 궁금합니다.
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. Classicist에 대해서 궁금해서 질문드립니다.현업 또는 강사님 혼자서 컨트롤러만 테스트 할 경우 서비스 레이어를 mock, stub을 사용하시는지,아니면 Fake Service 및 Fake Repository를 구현 후 컨트롤러 테스트를 하시는지 궁금합니다.Classicist을 추구하면 외부 환경만 Mock을 사용해서 테스트를 해야하는 지 아니면 최대한 Mock을 지양하지만 컨트롤러 테스트 같은 경우에는 Mock을 사용하나요?
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
어플리케이션 서비스와 도메인 서비스의 구성
안녕하세요.OSIV 강의 후반부 내용에서 규모가 비교적 큰 프로젝트에서는 도메인 계층과 더불어 그 앞단에 애플리케이션 계층을 추가하는걸 고려해보라고 말씀주신 내용이 있었는데요. 이게 제가 찾고 있는 해답이 아닐까 싶어 조금 더 자세히 알고 싶어 질문 드립니다. 질문이 조금은 장황해도 너그러히 이해주시면 감사드리겠습니다. 최근에 개인 프로젝트를 수행해보면서 제가 학습해온 개념들(JPA, DDD, MSA)이 중점적으로 다루는 내용이 다르다보니 프로젝트 구성에서 혼란을 겪어오고 있습니다.현재 개인 프로젝트 패키지 구조는 제가 느끼기에 가장 와닿았던 controller - application - domain - repository로 나뉘어 개발하고 있습니다.이때 application 계층의 역할은 하나의 요구사항(가입, 해지 등)을 해결하기 위해 필요한 하위 계층의 애플리케이션 서비스들을 호출하는 facade를 구성합니다. 또, domain에서는 도메인 객체와 더불어 해당 도메인의 애플리케이션 서비스를 통해 비즈니스적 처리를 추상화하여 표현하고, 데이터 핵심 데이터 처리를 repository에 위임하도록 구성했습니다.다만 이해가 안 갔던건 트랜잭션을 처리 계층에 대한 내용인데요. 제가 본 msa 도서에서는 facade에서 수행되는 애플리케이션 서비스 각각이 서로 다른 도메인 처리이므로 application 계층이 아닌, domain 메소드 단위로 트랜잭션이 구성되어야한다더라구요.그런데 사실 제가 개발하고 있는 프로젝트는 MSA 구조가 아니고, 여러 에그리거트가 복합된 구조이기에 위 구조와 사상을 가지고 개발하기 어렵다는 생각이 들었습니다. 또한, 서로 다른 도메인의 애플리케이션 서비스간에 호출관계에서도 어려움을 겪었는데요.예를 들어, 두 user가 하나의 group을 구성하는 서비스를 만들 때도 groupService라는 group에 종속된 애플리케이션 서비스에서 두 사용자 정보를 불러오는 userService 주입하자니, 서로 상이한 서비스간에 불필요한 의존성이 생기고, 같은 레벨의 서비스를 참조하는 것이 어색하다고 느껴졌습니다. 그래서 facade 계층에서 두 서비스를 주입하여 그룹생성 절차를 처리하자니 한 트랜잭션에 처리되어야한다고 생각하는 그룹 생성 처리 절차에서 회원, 그룹 서비스의 트랜잭션이 분리되니 이상하다고 생각했습니다. 드디어 본격적인 질문인데요. 혹시 영한님께서 말씀하신 애플리케이션 계층이 이렇게 도메인에 종속된 두 개 이상의 서비스가 함께 쓰여야 할 때 쓰이는 것이 맞을까요?맞다면 controller - application - domain - repository 의 계층구조에서 facade가 들어가던 application에 해당 복합(?) 서비스를 구현하고, 해당 서비스에 대한 dto객체정도들만 구현하면 되는걸까요? 3. 굳이 어플리케이션 계층이 필요로 하지않은 하나의 도메인에만 종속된 기능들을 호출구조를 어떻게 가야할까요?애플리케이션 계층을 도입한 상황에서 controller에서 domain 계층을 바로 호출하는 것은 일관성이 없어보이고, 또 일관성을 맞추기위해 application 계층에서 도메인 계층으로 BYPASS하는 객체를 만드는 것도 비효율적으로 보이긴합니다. 각각의 케이스에 어떻게 가는게 좋을까요?
-
미해결스프링과 JPA 기반 웹 애플리케이션 개발
영속성 컨텍스트 질문
9:57 보시면51 라인 Account account = accountRepository.findByEmail(email); 이 순간 account 객체는 Persistent 상태이고,이 코드 이후로 account 이건 다시 detach 상태가 되는데63라인 accountService.completeSignUp(account);이 코드로 인하여 다시 Persistent 로 상태가 만들어진다고 생각하면 될까요 ?? (왜냐하면 accountService.completeSignUp(account);에는 트랜잭션어노테이션이붙어있어서)
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
섹션4 필드와 컬럼매핑
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.메인 메서드 실행시 등록된 DB에 셀렉트 쿼리 조회하면저는 키값인 ID , , , , , 순으로 안나오고AGE, TESTLOCALDTAE, , ID 처럼 키값이 중간에 나옵니다...
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
단방향에서의 연관관계 주인 및 애노테이션
안녕하세요! 몇가지 궁금한게 있어서 질문드립니다. A -> B인 단방향에서 연관관계 주인은 A인가요? 단방향에서 양쪽 엔티티에 애노테이션(ex. @OneToOne)이 가능한걸로 알고있지만 필수로 적어줘야하는건 연관관계 주인쪽인가요?
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
양방향 연관관계에서 JSON 변환 시 무한루프 문제 질문드립니다.
안녕하세요.스프링 시큐리티를 커스텀해서 이런저런 작업 중입니다.로그인 절차를 모두 통과화면 다음 코드에서 응답을 내려줍니다.Account account = (Account) authentication.getPrincipal(); response.setStatus(HttpStatus.OK.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); objectMapper.writeValue(response.getWriter(), account);객체를 JSON으로 만드는 과정에서 무한루프가 발생해 스택오버플로우가 발생했습니다.왜냐하면 Account는 Roles라는 클래스를 알고 있고 Roles는 Account를 알고 있거든요(양방향). Roles는 Account, Role의 다대다 관계를 해소하긴 위한 중간 테이블입니다. Account에서 Roles 정보를 알고 있어야 하기 때문에 양방향 연관관계를 가지게 됐습니다.문득 어느 강의에서 엔티티를 노출하지 말라고 하셨던 게 생각이 났습니다. 검색을 해보니 객체를 json으로 만들 때 특정 객체를 하지 않는 어노테이션이 있는 것 같은데 저의 본능은 이걸 쓰지 말라고 하네요.별도의 반환 객체를 만들고 싶은데 이런 방법이 일반적인 건지 궁금합니다. 저의 사고 흐름도 올바른 건지 알고 싶습니다. account 객체에는 credential 정보도 있으니 필요한 정보만 담을 수 있는 반환용 객체를 만드는 게 맞을 것 같긴 한데 이런 사고흐름에 대한 검증 한 번 부탁드립니다. 그리고 이런 객체도 dto라고 해도 될까요?감사합니다.
-
해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
실전 예제 4번 클립에서 마지막 부분에 하신 말씀 질문입니다!
안녕하세요.11:03 경에 "이런 것들 json으로 말아넣자"라고 말씀하신 부분이요. 조금 더 풀어서 설명해주실 수 있을까요? 당장 이 프로세스가 필요해서는 아니지만 개념적으로라도 알고 있으려고 합니다!"이런 데이터"에 관해서도 조금 더 풀어서 말씀해주실 수 있을까요? 어떤 성격의 데이터들은 테이블이 아니라 json으로 관리할 수 있다는 것인지 조금 더 구체적으로 알고 싶습니다.감사합니다.
-
미해결스프링과 JPA 기반 웹 애플리케이션 개발
다시 강의를 보니 드는생각..
이메일 인증요청시간을 1시간으로 잡으셨는데인증할때마다 마지막인증요청 시간을 업데이트 쳐줘야 하는거 아닌가요 ???(/resend-confirm-email) 이 메서드를 실행 정상 했을때 마다
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
h2 연결 오류가 떠요
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]h2 데이터 베이스를 새로 생성하면서 이름과 패스워드를 설정했는데강의대로 따라가다가 Error : Wrong user name or password가 떠요설정한 이름과 패스워드를 아예 없애는 방법이 있을까요?아니면 속성에 name과 password를 적는 방법을 알려주시면 감사하겠습니다.
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
h2 데이터베이스 설정관련
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]다름이 아니라 다른 프로젝트를 하면서 test라는 이름의 DB를 이미 만들어 놔서 그런지는 몰라도 이러한 오류가 뜨더군요 이게 이전에 만들어 놓은 test라는 이름의 db파일때문인지 궁금합니다 또한 제가 이전에 작업했던 프로젝트도 h2데이터 베이스를 사용하느라 test라는 주소로 연결토록했는데 이전 프로젝트의 application properties를 testDB이런식으로 이름을 바꿔주면 제가 지금 사용할때 test라는 이름으로 h2 데이터베이스를 사용할 수 있을까요? 마지막으로 이미 생성된 test라는 DB파일이 있다면 해당 디비로는 연결이 되면 안되는 건가요? 하나의 디비 파일 연결을 (물론 테이블의 구성요소가 다르겠지만) 다른곳에서 사용할 수 없는 걸까요? 질문 하다 보니 너무 길어진 점 죄송합니다.
-
미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
Editor....를 활용한 패턴에 질문있습니다.
당연히 구글링 해보셨져? 원하는 결과를 못찾으셨나요? 어떤 검색어를 입력했는지 알려주세문제가 발생한 코드(프로젝트)를 Github에 올리시고 링크를 알려주세요.Editor관련한 질문글들을 읽어보아도 제 머리로 이해가 안돼서 또 이렇게 Editor 질문 글을 하나 더 추가합니다... ㅠㅠ [해당 영상을 보지 않았더라면 짰을 코드]Post엔티티에 비즈니스 로직을 작성하고단순하게 PostEdit을 전부 넘겨 비즈니스 로직 change를 호출하여 더티체킹으로 마무리! 이렇게 했을 경우, 파라미터 순서에 무관하게 PostEdit이라는 수정을 위한 Dto 객체를 단 하나만 넘겨 수정을 할 수 있다고 판단했습니다. 물론 위 비즈니스 로직은 null값에 대처는 못하겠지만요! 하지만, 프론트 개발자와 상의하여 '수정 시, 모든 데이터를 넘겨준다는 전제' 에서는 가장 간단한 방법이라고 생각했습니다! [Editor를 작성해보며 느낀 의문점]Post의 toEdit을 통해 기존 가지고 있던 데이터를 PostEditor에게 넘김으로써 Builder에서 null값에 대응할 수 있다는 점 이외에는 또 다른 장점을 이해하지 못하고 있습니다. Request의 title혹은 content가 null일 경우 이를 해결하기 위한 방법을 제시해주는 것 말고는 되려 관리해야 할 것들만 늘어난 느낌이 해소가 되지 않습니다 ㅠㅠ 그래서 제가 이해한 것 까지의 내용들이 잘 이해한 것인지 그리고 추가적으로 제가 이해하지 못한 것들을 이해하고 싶습니다! findById로 수정하려는 엔티티를 가져옵니다.toEdit()을 통해 현재 수정하고자 하는 엔티티의 필드들을 PostEditor에게 넘겨 빌더를 만듭니다. 이는 수정하려는 엔티티가 현재 가지고 있는 필드들을 핸들링 할 수 있도록 해줍니다. (가령, title혹은 content의 null 처리)2번을 통해서 PostEditor가 현재 수정하려는 Post의 필드들을 주입 받았으면, Request로 받은 데이터를 통해 최종 build()를 해줍니다.변경사항을 모두 적용한 postEditor를 Post의 변경비즈니스 메서드 edit(postEditor)를 통해 더티체킹으로 변경해줍니다. [최종적으로 든 생각][Editor를 작성해보며 느낀 의문점] 에서 작성된 것들이 정확하다면,아예 첫 방법을 사용하되, 비즈니스 메서드에서 null 체크를 해주면 어떨까? 하는 생각이 들었습니다.이런 방식으로 진행한다면 문제가 있을까요? 긴 글에 시간 내어주셔서 감사합니다 !
-
미해결Practical Testing: 실용적인 테스트 가이드
OrderProduct 테스트에 대해
안녕하세요 강사님먼저 강의에 대해 너무 감사드린다는 말씀 드리고 싶습니다.강의 내용 중 OrderProduct를 생성하는 부분의 책임이 Order에 있게 설계하는 부분에 정말 매력을 느끼고, 유사한 설계 방법들에 대해 공부하려면 어떤 키워드를 알아보면 좋을까요??또한 이 강의를 따라 공부해서 개인적으로 진행중인 프로젝트에 적용하면서 공부하고 있는데, 딱 Order와 OrderProduct같은 관계에서 OrderProduct를 강의에서처럼 생성자가 아닌 정적 팩토리 메소드를 통해 만들어보고자 하였습니다.하지만 여기에서 OrderProdcut의 정적 팩토리 메소드를 테스트하려면 Order가 필요하고, Order를 create하려면 또 OrderProduct가 내부에서 만들어지는 문제가 발생했습니다.이런 경우는 어떻게 해결할 수 있을까요??임의로 Order에 대해 mock으로 생성해서 해결하고는 있는데, 이게 일반적인 방법이 될 수 있을까요?감사합니다!!
-
해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
멤버와 팀의 영속화 순서를 뒤집었을 때(?) 생성되는 query에 대한 질문입니다!
안녕하세요.이상한 코드이긴 하지만 문득 궁금해서 테스트를 해봤습니다.하이버네이트 버전은 6.4.2.Finalh2 데이터베이스 버전은 2.2.224 Team team = new Team(); team.setName("teamA"); Member member = new Member(); member.setUsername("memberA"); member.setAge(49); member.setTeam(team); em.persist(member); em.persist(team);이 경우 발생하는 query는 다음과 같습니다.Hibernate: /* insert for hellojpa.jpql.Member */ insert into Member (age, TEAM_ID, username, id) values (?, ?, ?, ?) Hibernate: /* insert for hellojpa.jpql.Team */ insert into Team (name, id) values (?, ?) Hibernate: /* update for hellojpa.jpql.Member */ update Member set age=?, TEAM_ID=?, username=? where id=?제가 생각한 흐름은 이렇습니다.멤버가 데이터베이스에 동기화된다팀이 멤버에 저장되어 있지만(객체 상으로), 팀 테이블에는 해당 데이터가 없다멤버의 외래키(team_id)가 아직 null이다팀이 데이터베이스에 동기화된다이제 멤버의 외래키를 업데이트 한다외래키만 업데이트 하면 될 것 같은데 외래키를 가진 레코드 전부를 수정한다이 흐름이 맞을까요? 외래키만 업데이트 하는 게 아니라 멤버의 해당 레코드를 전부 업데이트하는 게 생각했던 것과 달라 질문드리게 됐습니다.답변 미리 감사드립니다.
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
엔티티 매핑 오류 - 실전 예제1
섹션 4 - 엔티티 매핑 에서 실전예제1 관련 질문입니다. 강사님께서 jpashop 프로젝트를 하고 계신데 저도 열심히 따라 하고 있었습니다. 근데 지금 H2 가 연결이 계속 안된다라고 뜨는데 혹시 이유 아시는 분 계실까요.......ㅠㅠㅠㅠ사진 첨부하겠습니다.제발 꼭 좀 도와주세요ㅜㅜㅜ!!!
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
콘솔에 쿼리가 지저분하게 나오는 문제.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.인텔리제이 콘솔창에 쿼리가 이런식으로 지저분하게 보입니다.yml 설정은 이렇게 되어있고요.jpa: hibernate: ddl-auto: create properties: hibernate: # show_sql: true format_sql: true logging: level: org.hibernate.SQL: debug # org.hibernate.type: trace log4j jdbc 설정하는 쪽으로 일단 구글링해서시도해보고 있긴한데, 잘안되네요.혹시 다른 방법이 있을까요
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
페이징과 한계 돌파의 batch_size에 대해
밑줄 친 부분은 yml에 batch_size에 의해 조정되는 것 같습니다(맞는지 모르겠습니다). 근데 저는 그런데 영한님 쿼리는 저 부분에 4,11이 들어가있는 이유가 궁금해요. 저는 저 부분이(?,?,?,?,?,?,?,?,?)가 되어서 그런지 몰라도 h2에서 쿼리할때 에러가 뜹니다.(밑사진)
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
api
api란 개념을 잘 모르겠습니다.그냥 controller에서 mapping하는 것이 api 인가요?1편에서 만들었던@GetMapping("/members/new")public String createForm(Model model) {...}이런 코드들도 각각 api라 하는 것인가요?
-
해결됨실전! Querydsl
Querydsl 조회 시, DTO 필드를 List로 받는 방법을 알고 싶습니다.
안녕하세요.Querydsl 학습 중에 궁금한 점이 있어서 질문드립니다. Projection을 이용해서 조회 데이터를 DTO에 반환 받고 싶습니다.하지만, DTO 필드에 List 타입이 정의되어 있습니다.이런 경우에는 어떤식으로 데이터를 조회할 수 있는지 궁금합니다. 또한, 현업에서는 이런 형태의 데이터를 조회하기 위해서 어떤 방식으로 하는지 궁금합니다.객체 그래프 탐색을 이용한 Lazy 조회 후, 로직에서 조건 필터링 ?각 조건에 맞는 쿼리 여러번 호출 ?DTO Projection을 이용한 한방 쿼리 ?다른 방식 ? 조회 데이터를 반환 받기 위한 ProductDto는 다음과 같습니다.public record ProductDto( Long id, String name, int reviewScore, int reviewCount, int originalPrice, int salesPrice, List<String> productImages, List<String> productDetailImages ) { @QueryProjection public ProductDto { } } 데이터를 조회 하고자 하는 의도가 담긴 Querydsl은 다음과 같습니다.public ProductDto findSellingProductById(Long id) { return queryFactory .select(new QProductDto( product.id, product.name, product.reviewScore, product.reviewCount, productOption.originalPrice, productOption.salesPrice, productImage.imageUrl, productDetailImage.imageUrl )) .from(product) .leftJoin(product.productOptions, productOption) .on(productOption.isDefault.isTrue(), productOption.isSaleable.isTrue(), productOption.isDeleted.isFalse()) .leftJoin(product.productImages, productImage) .on(productImage.isDeleted.isFalse()) .leftJoin(product.productDetailImages, productDetailImage) .on(productDetailImage.isDeleted.isFalse()) .where(product.id.eq(id)) .fetchOne(); } Product, ProductOption, ProductImage, ProductDetailImage 엔티티는 다음과 같습니다.@Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Product extends BaseDateTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "product_id") private Long id; @Column(length = 50) private String name; private int reviewScore; private int reviewCount; // ... 중략 @OneToMany(mappedBy = "product", cascade = CascadeType.ALL) private List<ProductImage> productImages = new ArrayList<>(); @OneToMany(mappedBy = "product", cascade = CascadeType.ALL) private List<ProductDetailImage> productDetailImages = new ArrayList<>(); @OneToMany(mappedBy = "product", cascade = CascadeType.ALL) private List<ProductOption> productOptions = new ArrayList<>(); // ... 중략 } @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ProductOption extends BaseDateTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "product_option_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "product_id") private Product product; @Column(length = 30) private String name; private int originalPrice; private int salesPrice; // ... 중략 private Boolean isDefault; private Boolean isSaleable; private Boolean isDeleted; // ... 중략 } @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ProductImage extends BaseDateTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "product_image_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "product_id") private Product product; private String imageUrl; private Boolean isDeleted; // ... 중략 } @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ProductDetailImage extends BaseDateTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "product_detail_image_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "product_id") private Product product; private String imageUrl; private Boolean isDeleted; // .. 중략 } 감사합니다.