안녕하세요! 강사님 ㅎㅎ 컴퓨터 공학 전공자이고 졸업 후 취준을 하고 있습니다 ㅎㅎ 지금까지는 NEST.JS, EXPRESS.JS로 웹서비스 REST API 개발 프로젝트 경험이 있습니닷 스프링과 스프링부트는 강사님의 커리큘럼을 꾸준히 따라오며 곧 MVC2편이 끝납니다 ㅎㅎ 1. 이어서 DB1,2편까지만 듣고도 REST API 기반 서비스를 구현 할 수 있을까요? JPA 로드맵을 사서 듣고 나야 서비스 구현을 할 수 있는 정도가 되는건지 궁금합니닷! (JPA 로드맵을 들으면 좀 더 실무에 가까운 퀄리티 높은 서비스를 만들 수 있는건지, 아니면 아예 들어야만 서비스 구현의 기본 역량을 갖출 수 있는건지 궁금합니닷) 혹은 야생형으로 DB1,2편을 생략하고 JPA로드맵을 먼저 들어가도 고생돼도 할만할지 궁금합니다 프로젝트를 하면서 공부 하고 싶은데 DB 설계가 안되면 프로그램 설계가 막막하니.. 여쭤보게 됐습니다 답변 부탁 드립니닷!
중괄호 등을 통해서 코드를 작성할 때 중괄호가 [] 이렇게 두개 자동으로 생성되는데요. 이외에도 '' 등 두개씩 자동으로 만들어줘서 코드를 작성하는데는 편리한데 'LEE' 등 코드를 쓰고 EE에서 '를 넘어 가기 위해 방향키를 눌러줘야 하는 불편함이 있습니다. 혹시 빠르게 작성하기 위해 단투키 같은게 별도로 있을지 문의드립니다
@Autowired로 EntityManager를 주입받을때 여러 쓰레드가 동시에 접근하면 동시성 문제가 발생합니다. 하지만 @Transactional 을 추가해준다면 //1 @Repository @RequiredArgsConstructor @Transactional public class AutowiredRepository { private final EntityManager em; } //2 @Repository public class PersistenceContextRepository { @PersistenceContext private EntityManager em; } @PersistenceContext 처럼 Transaction에 의해 쓰레드간 동시성 문제를 해결해준다 고 생각하는데 맞게 생각한건지 궁금합니다. 두가지 방법 다 EntityManager를 호출 할때마다 Proxy를 통해 EntityManager를 생성하여 Thread-Safe를 보장해준다라고 볼수 있는 건가요?? 답변주시면 정말 감사하겠습니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 헷갈려서 질문 남깁니다. JpaConfig 를 통해 @Bean 등록 한다 JpaConfig Class 에 있는 EntityManager 는 스트링부트가 자동적으로 Bean 등록을 해주기 때문에 의존관계 주입을 통해 사용이 가능하다. new JpaRepository(em) 을 통해 의존관계 주입 JpaRepository에서의 EntityManager em 은 JpaConfig에서 ItemRepository 를 @Bean 등록할 때 매개변수로 받은 것을 사용 제가 이해하고 있는 흐름이 맞나요?
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 여기에 질문 내용을 남겨주세요. OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release. 이러한 오류가 뜨는데, 실행하는데는 문제가 없습니다. 이 오류를 고쳐야 할 것 같은데, 어떻게 고쳐야할까요?
쿠폰 생성 발급 로직도 컬럼에 쿠폰의 개수를 지정해놓으면 이전 강의랑 똑같은 거 같은데 왜 이번 강의는 쿠폰 엔티티를 새로 생성해서 그 개수를 체크하는 건지 궁금합니다. 이전 컬럼에 개수를 두어 관리하는 거랑 지금처럼 엔티티를 생성하는 방식의 차이점이 너무 궁금해요 항상 감사합니다.
안녕하세요 주니어 개발자 길라잡이가 되주셔서 감사합니다. fulltext index에 대해서 질문이 있습니다. 임계점이 올경우 튜닝으로도 해결할 수 없는 문제라고 말씀해주셨는데요 경험담이라고 해주셨는데 그 경험담을 듣고 싶어서 질문을 남겨봅니다. 공식 문서에서 찾아보니까 https://dev.mysql.com/doc/refman/8.0/en/innodb-fulltext-index.html#innodb-fulltext-index-deletion 동시 경합이 발생할 수 있다고 작성이 되어있는데 이 문제로 발생한건가요 ? 아니면 다른 인덱스와 다르게 인덱스 용량 때문에 발생한 문제이신건가요 ? FULLTEXT INDEX의 한계 검색을 해도 다 좋은점만 나오더라구요..
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 여기에 질문 내용을 남겨주세요. 스프링 부트 3.1.3버전 CGLIB 스프링 부트 3.1.3버전으로 해당 강의를 따라가던 중 proxyCheck() 테스트의 로그가 CGLIB$$0이 출력됩니다. 테스트는 성공하였지만 영한님처럼 프록시 객체의 해시코드가 16진수로 이루어진 값이 아니라 0이 나옵니다. 혹시 몰라서 부트를 2.7.14로 다운그레이드 했더니 영한님과 비슷하게 16진수의 해시코드가 나오더라구요. 혹시 스프링부트3 버전 이상부터 프록시 객체의 해시코드 값이 변경된 걸까요?
안녕하세요. JPA 최적화 강의를 수강하고, 개인적으로 DDD를 책으로 공부해본 후에 개인 토이 프로젝트를 진행하던 중, 뜻밖에 의문이 생겼는데 여쭐 곳이 없어서 이렇게 질문 올리게 되었습니다. 맨 땅에 헤딩으로 이런저런 강의, 책, 다른 분들의 소스를 참고 하려다보니 여러 개념이 뒤섞여서 혼동이 옵니다.. ㅠㅠ 현재 프로젝트에서는 크게 에그리거트를 CUSTOMER, EXTERNAL, SECURITYMEDIA 3개로 나누어 설계했는데요. 강의에서도, 책에서도 DOMAIN 계층에 있는 서비스는 해당 도메인에 대한 순수한 CRUD를 수행하는 것으로 보았습니다. DDD 책에서는 여러 에그리거트가 필요로 하는 기능을 구현할 때는 도메인 서비스로 구현하라고 이야기 했구요. 처음에는 책에서 조언하는 대로 도메인 서비스로 구현해보고자 하다가, 좀처럼 구현이 안되어서 다른 분들이 구현한 소스를 참조하다 보니 application(응용)영역을 FACADE라는 상위 계층을 두는 것을 방식을 알게 되었습니다. 소스를 따라가보니 각 애그리거트의 DOMAIN 영역에 있는 서비스를 주입하여, 각 도메인 영역에 있는 서비스를 적절히 호출하기에 책에서 본 도메인 서비스와 같은 역할을 하겠구나 하여,, 해당 프로젝트 구성 방식을 따라 개발해보기로 했습니다. 그런데 개발을 하다보니,, 참조하는 소스에서 메소드 단위의 트랜잭션의 적용을 facade 영역이 아닌, 도메인 영역의 서비스 구현체에서 하는 것을 알게 되었습니다. 제가 개발하고자 기능은 여러 애그리거트를 생성, 변경하는 하나의 행위가 하나의 트랜잭션으로 묶여야 하는데 말이죠. 이러한 이유 때문에 현재 소스는 FACADE에서는 하나의 도메인 영역의 서비스를 주입하여 하나의 메소드를 호출하도록 되어있고, 도메인 영역에 있는 해당 서비스의 구현체에서 여러 애그리거트의 서비스, 레포지토리를 주입받아 하나의 메소드에서 트랜잭션 단위로 수행하도록 구현되어있습니다.. @Service @RequiredArgsConstructor public class SecurityMediaFacade { private final SecurityMediaService securityMediaService; public SecurityMediaInfo.Main registerOtp(SecurityMediaCommand.RegisterSecurityMediaRequest req) { //디지털otp 발급 // 디지털 otp 발행 return securityMediaService.issueSecurityMedia(req, SecurityMediaType.DIGITAL_OTP); } ... } public interface SecurityMediaService { public SecurityMediaInfo.Main issueSecurityMedia(SecurityMediaCommand.RegisterSecurityMediaRequest req, SecurityMediaType type); ... } @Slf4j @Service @RequiredArgsConstructor public class SecurityMediaServiceImpl implements SecurityMediaService { private final CustomerReader customerReader; private final SecurityMediaStore securityMediaStore; private final TokenStore tokenStore; private final ExternalClientService externalClientService; @Override @Transactional public SecurityMediaInfo.Main issueSecurityMedia(SecurityMediaCommand.RegisterSecurityMediaRequest req, SecurityMediaType type) { // 요청고객 찾기 Customer customer = customerReader.findCustomerByRnn(req.getRnn()); SecurityMedia newOtp = null; if(!customer.existActiveSecurityMedia()) { // otp 신규 SecurityMedia initOtp = req.toEntity(SecurityMediaType.DIGITAL_OTP, customer); newOtp = securityMediaStore.store(initOtp); // 토큰 발급 요청 Token newToken = externalClientService.getToken(customer, newOtp); newOtp.addToken(newToken); tokenStore.store(newToken); } return new SecurityMediaInfo.Main(newOtp); } 위에는 프로젝트의 구성인데.. 첫 단추부터 잘못 끼운 것도 같아서 시작 단계인 지금에서라도 좀 개선을 해보려고 하는데요. 사실 도메인 서비스가 제가 의도로 하는 여러 애그리거트의 서비스 기능을 묶어서 하는 건지 아무리 읽어봐도 혼선이 옵니다. 혹시 DDD 책에서 이벤트라는 개념이 나오는데 도메인 서비스가 아니라, 이 이벤트를 통해 다른 애그리거트의 응용 서비스를 호출하도록 핸들링 하는게 올바른 방법일까요? 지금과 같은 구조를 유지해도 된다면.. facade 영역의 메소드를 트랜잭션으로 묶고, 각 도메인 계층의 서비스들에서 선언된 해당 도메인에 대한 crud 메소드를 적절히 호출해가면서 facade 영역에서 비즈니스 로직을 처리해도 될까요? 너무 글이 장황하고 기네요.. ㅠㅠ 혹시 도움을 주신다면 너무나도 감사드리겠습니다.
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? ( 예 /아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? ( 예 /아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? ( 예 /아니오) [질문 내용] 악! 스프링 웹 개발은 즐거워! 김영한 선생님 질문있습니다. 선생님의 두 로드맵을 거의 다 듣고 그를 바탕으로 포폴용 게시판을 만들어보고 있습니다. 와중 댓글과 대댓글 기능을 구현하는 과정에서 Comment라는 객체를 만들고 객체의 타입을 Comment와 reply로 나누었습니다. 특정 게시판에 관한 데이터를 불러올 때 댓글과 대댓글을 편리하게 불러오기 위해 타입이 Comment인 객체가 reply에 해당하는 객체를 리스트로 갖도록 설계했습니다. 이후 테스트를 진행해보았는데 fetch join을 통한 데이터 로드는 문제 없이 진행되었습니다. 다만 이러한 설계 방식이 올바른지에 대한 질문을 스스로 해결할 수 없어서 글 남깁니다. 아래는 코드와 테스트 코드 및 실행 결과이고, 마지막에 질문이 있습니다. package toy.board.domain.post; import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import org.springframework.util.StringUtils; import toy.board.domain.BaseDeleteEntity; import toy.board.domain.user.Member; @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PROTECTED) @EqualsAndHashCode(callSuper = true) public class Comment extends BaseDeleteEntity { public static final int CONTENT_LENGTH = 1000; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "comment_id", nullable = false) private Long id; @Column(name = "content", nullable = false, length = CONTENT_LENGTH) private String content; @Column(name = "type", nullable = false, updatable = false) private CommentType type; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "post_id", nullable = false, updatable = false) private Post post; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id", nullable = false, updatable = false) private Member member; @OneToMany(mappedBy = "parent") private List<Comment> replies = new ArrayList<>(); @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "parent_comment_id", updatable = false) private Comment parent; /** * 양방향 관계인 Member와 Post에 대해 자동으로 양방향 매핑을 수행한다. */ public Comment( @NotNull final Post post, @NotNull final Member member, @NotNull final String content, @NotNull final CommentType type ) { this.post = post; this.member = member; this.content = content; this.type = type; } public boolean update(final String content) { if (!StringUtils.hasText(content)) { return false; } this.content = content; return true; } public void leaveReply(Comment reply) { if (areTypesCorrectThisAnd(reply)) { throw new IllegalArgumentException("주어진 댓글과 대댓글의 타입이 올바르지 않습니다."); } if (hasComment(reply)) { throw new IllegalArgumentException("대댓글이 이미 다른 댓글에 포섭되어 있습니다."); } if (isNew(reply)) { throw new IllegalArgumentException("댓글이 이미 해당 대댓글을 포함하고 있습니다."); } this.replies.add(reply); reply.parent = this; } private boolean isNew(Comment reply) { return this.replies.contains(reply); } private static boolean hasComment(Comment reply) { return reply.parent != null; } private boolean areTypesCorrectThisAnd(Comment reply) { return this.type != CommentType.COMMENT || reply.type != CommentType.REPLY; } } @Transactional @DisplayName("comment가 List<comment>를 갖고, fetch join으로 가져올 수 있는가?") @Test public void comment_has_comments_fetch_join() throws Exception { //given Member member = Member.builder( "member", new Login("password"), Profile.builder("nickname").build(), LoginType.LOCAL_LOGIN, UserRole.USER ).build(); em.persist(member); Post post = new Post(member, "title", "content"); em.persist(post); Comment comment = new Comment(post, member, "comment", CommentType.COMMENT); em.persist(comment); for (int i = 0; i < 5; i++) { Comment reply = new Comment(post, member, "reply" + String.valueOf(i), CommentType.REPLY); comment.leaveReply(reply); em.persist(reply); } em.flush(); em.clear(); //when QComment reply = new QComment("reply"); List<Comment> findComments = queryFactory .selectFrom(QComment.comment) .leftJoin(QComment.comment.replies, reply).fetchJoin() .where( QComment.comment.post.id.eq(post.getId()), QComment.comment.type.eq(CommentType.COMMENT) ) .fetch(); System.out.println("============================================="); for (Comment findComment : findComments) { System.out.println("findComment.getId() = " + findComment.getId()); System.out.println("findComment.getContent() = " + findComment.getContent()); } //then Comment findComment = findComments.get(0); for (Comment findReply : findComment.getReplies()) { System.out.println("findReply content = " + findReply.getContent()); } } [테스트 실행 시 create query] create table comment ( is_deleted boolean default false not null, type tinyint not null check (type between 0 and 1), comment_id bigint generated by default as identity, created_date timestamp(6), deleted_date timestamp(6), last_modified_date timestamp(6), member_id bigint not null, parent_comment_id bigint, post_id bigint not null, content varchar(1000) not null, created_by varchar(255), last_modified_by varchar(255), primary key (comment_id) ) [테스트 실행 결과] select c1_0.comment_id, c1_0.content, c1_0.created_by, c1_0.created_date, c1_0.deleted_date, c1_0.is_deleted, c1_0.last_modified_by, c1_0.last_modified_date, c1_0.member_id, c1_0.parent_comment_id, c1_0.post_id, r1_0.parent_comment_id, r1_0.comment_id, r1_0.content, r1_0.created_by, r1_0.created_date, r1_0.deleted_date, r1_0.is_deleted, r1_0.last_modified_by, r1_0.last_modified_date, r1_0.member_id, r1_0.post_id, r1_0.type, c1_0.type from comment c1_0 left join comment r1_0 on c1_0.comment_id=r1_0.parent_comment_id where c1_0.post_id=? and c1_0.type=? ===================== findComment.getId() = 1 findComment.getContent() = comment findReply content = reply0 findReply content = reply1 findReply content = reply2 findReply content = reply3 findReply content = reply4 [질문] 제가 궁금한 것을 자세히 말하자면, 테이블이 만들어질 때, comment 테이블의 특정 row(대댓글인 컬럼)가 해당 테이블의 다른 row(댓글인 row)의 PK값을 FK로 갖는데, 댓글에 해당하는 row는 객체가 생성되고 DB에 저장될 때 해당 컬럼에 null이 저장됩니다. 이는 Comment 객체의 ID에 @GenerateValue 설정을 주어서 그렇습니다. 위의 상황은 실무에서 사용할만큼 적절한가요? 아니라면 대안이 있을까요? jpa와 관련된 질문을 읽다보니, 다대일 관계에서 left join fetch의 경우 where문의 결과에 따라 데이터 일관성의 오류가 나타날 수 있다는 답변이 있었습니다.(해당 글: https://www.inflearn.com/questions/15876/fetch-join-%EC%8B%9C-%EB%B3%84%EC%B9%AD%EA%B4%80%EB%A0%A8-%EC%A7%88%EB%AC%B8%EC%9E%85%EB%8B%88%EB%8B%A4 ) 제가 작성한 테스트 코드의 쿼리문은 일대다 관계에서 일에 해당하는 엔티티에 별칭을 주어 where문을 적용한 것이므로 일관성의 문제가 발생하지 않는다고 생각했는데 이것이 옳은 생각인가요? 마지막으로 쿼리의 복잡도와 쿼리 개수는 trade off 관점에서, 특정 게시물에 관한 데이터를 반환해야 하는 api 요청이 들어왔을 때, 게시물과 댓글 엔티티가 단방향 관계일 경우 게시물에 관한 데이터와 댓글 및 대댓글에 관한 데이터를 각각의 저장소를 통해 가져오는 것이 좋을까요 혹은 쿼리가 다소 복잡해지더라도 한 번에 가져오는 것이 좋을까요? 아니면 단방향 관계를 양방향으로 만드는 것이 더 나은 선택일까요? 혼자 공부하니 올바른 방식을 찾는게 참으로 어려운 것 같습니다. 답변 기다리겠습니다. 감사합니다.
안녕하세요 저는 강사님 강의로 공부하고 파이썬으로 코테를 준비하고 있습니다. 저와 같은 상황에 계신분들과 공유하고 싶어 글을 올립니다. 파이썬 풀이에서 부족한 부분 알려주시면 수정하겠습니다.~ import sys sys.setrecursionlimit(10 ** 6) N, M = map(int, sys.stdin.readline().split()) MAX = 1000 + 10 graph = [[False for in range(MAX)] for in range(MAX)] visited = [False for in range(MAX)] for in range(M): x, y = map(int, sys.stdin.readline().split()) graph[x][y] = True graph[y][x] = True def dfs(idx): visited[idx] = True for j in range(1, N + 1): if not visited[j] and graph[idx][j]: dfs(j) cnt = 0 for i in range(1, N + 1): if not visited[i]: dfs(i) cnt += 1 print(cnt)
사용자가 많지 않고 일반 커뮤니티 같은 경우에는 좋아요 개수가 많이 몰릴 일이 없다고 생각해서 PessimisticLock을 적용하려고 합니다. 제가 생각했을 땐 인스타나 그런 대규모 사이트의 좋아요는 PessimisticLock을 적용하면 성능 저하가 뚜렷하게 나타난다고 생각합니다. -> 레디스 사용이 베스트 토이프로젝트 규모의 SNS는 PessimisticLock으로 구현하는게 비용도 들지 않고 데이터 정합성이 보장된다고 판단했습니다. 혹시 제가 틀린 거나 더 나은 방법이 있을까요? 항상 감사합니다! 아 추가로 newFixedThreadPool은 왜 32로 설정하는지도 궁금합니다!!
완전탐색 강의 마지막 문제 1090번 문제 풀이 방법에 의문이 생겨서 질문 올려봅니다. 강의에서는 우리의 집 중에서 한 곳에 모이면 된다고 풀이를 하셨는데, 예시에 나온 4 15 14 15 16 14 15 16 15 4명이 모이기 위한 최적의 장소는 (15,15)입니다. 백준에서도 이것이 정답 좌표이고. 그래서 혹시 제가 1번 2번 3번 아이디어를 이해하는데에서 잘못 이해한 부분이 있을까 싶어 질문을 올립니다. (15 14) (15 16) (14 15) (16 15) 중에 하나에 모인다는 말이 아닌것인가요?
========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 트랜잭션이 실제 적용되고 있는지 확인하는 방법에서 TxApplyBasicTest 코드 짤때요 BasicService 프록시 객체가 스프링빈으로 자동등록되는데 왜 굳이 @TestConfiguration 에서 BasicService를 수동으로 빈 등록을 하는건가요?? 안해도 되는거 아닌가요? 어차피 프록시 객체가 등록되어있어서 @Autowired로 의존관계 주입받을 수 있지 않나요??
fetch join 시 alias를 사용해서 필터링하는게 왜 안되는걸까요? 이거에 대한 답변으로 디비상태와 객체상태의 일관성이 깨지게 됨을 보통 얘기하시는것같아요 alias를 사용해서 필터링해버리면 실제 디비에 있는 데이터보다 적은 개수가 나오게 되니까요. 근데 어차피 그 필터링된 결과만을 결과로 리턴해주어야한다면 사용해도 괜찮을 것 같은데 디비상태와 객체상태의 일관성이 깨지는게 왜 문제가될까요? 그래서 생각을 해봤는데 크게 다음인것같아요 - 유지관리어려울 수 있음 - 캐싱문제 근데 저 두 문제가 정말 큰 문제가 되는지를 잘 모르겠어요...; 유지관리 어려울수야 있겠지만 그렇게 까지 어려울지도 잘 모르겠고, 캐싱문제(쿼리결과캐싱)도 저 코드에 의해 영향이 얼마나 많이 갈지..도 잘 모르겠어요 저 유지관리/캐싱 문제가 아니라.. 2차캐시때문인가요? 예를들어서 team과 member가 일대다 연관이고 team을 select해온다는 sql이 있다고 가정 1. fetch join + on 절 : 디비에 있는 일부 데이터 불러옴 2. fetch join 만있어서 디비에있는 모든 데이터 불러옴 하나의 트랜잭션에서 1호출 뒤에 2를 호출하면 디비에 쿼리를 날리긴하지만 이미 team_id에 해당하는 객체가 영속성 컨텍스트에있어서 가져온거버림 그래서 추후에 문제가생길 수 있음 --- 인건가요?