16강을 수강하고 있습니다. CustomLoginFailureHandler 사용자가 로그인에 실패했을 때 호출되는 메서드에 대한 질문입니다. if(savedRequest != null) { String targetUrl = savedRequest.getRedirectUrl(); //사용자가 접근하려던 URL // 즉, 로그인에 실패했지만 사용자를 원래 있던 페이지로 보내줌 // 실패 시 사용자가 접근하려던 URL 로그 출력 log.info("[핸들러 - 사용자가 접근 시도한 URL]\n-> " + targetUrl); // 사용자가 접근하려던 URL로 리다이렉트 (로그인 실패 후에도 이동) response.sendRedirect(targetUrl); } 그런데 if문의 마지막 줄을 보면 로그인 실패 후에도 targetUrl 즉, 사용자가 접근하려했던 페이지로 이동시킨다고 작성 되어 있는데 ... 로그인 실패 시에도 targetUrl로 리다이렉트시키는 이유가 뭔가요?? 로그인에 실패하면 로그인 페이지에 남아있어야 하는 것 아닌가요 ...?
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? 예 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예 3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예 [질문 내용] left join이 쿼리에 실어지지 않아 의문을 갖던 중 https://inf.run/8ctRk 이 질문에 답변해주신걸 보고 이해했습니다. @Query(value = "select m from Member m left join m.team") Page<Member> findByAge(int age, Pageable pageable); 결론적으로 위와 같이 하면 쿼리에 join문이 붙어 잘 나갑니다. 다만 count 쿼리는 여전히 아래와 같은데 이 부분도 역시 마찬가지로 하이버네이트6에서 count 쿼리에서 join 문을 알아서 빼서 최적화해준건가요? 페이징 쿼리 select m1_0.member_id, m1_0.age, t1_0.team_id, t1_0.name, m1_0.username from member m1_0 left join team t1_0 on t1_0.team_id=m1_0.team_id order by m1_0.username desc fetch first ? rows only 카운트 쿼리 select count(m1_0.member_id) from member m1_0
안녕하세요 강의 도커랑 cicd부분 보고 저희 동아리 프로젝트 초기설정을 했습니다! 동료가 소셜 로그인 구현한다고 해서 https 설정을 해달라고 하는데 제가 예전에 배운 내용은 nginx에 vi로 어떤 설정을 하고 ec2 내에 certbot을 설치하는 것으로 기억합니다 만약 이 강사님 이 알려주신 설정으로 이걸 하려면 도커 컨테이너 들어가서 nginx에 vi로 문서를 작성하는게 맞을까요? 걱정되는게 그러면 컨테이너를 삭제후 재실행하면 문서를 또 작성해야 하는게 걱정됩니다 제가 말한 방법이 맞는건지 아니면 다른 방법이 있는건지 궁금합니다 ai한테도 도움을 요청했는데 docker-compse.yml 에 쓰는 방식을 추천하는 것 같습니다 강사님께서 알려주신 docker-compse에 저 코드들을 붙여넣어도 괜찮을까요?
자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
저는 application.yml 파일에서 mysql username 과 password 를 전부 .env 파일에 넣고 $ {} 를 써서 나타냈습니다. 그래서 .env 파일은 깃허브에 올리면 안돼서 배포해도 제대로 안돌아갈것 같아서 그런데. 혹시 강사님께서는 aws 배포하실때 db username, password 와 같은 민감한 정보들은 어떻게 처리하여 배포하시는걸 추천드리나요?
찾아보니 아래처럼 나옵니다. ``` fetchResults() 는 Querydsl 5.x부터 deprecated(더 이상 권장되지 않음) 처리되었습니다. fetchResults() 는 count 쿼리 와 content 쿼리 를 내부적으로 두 번 실행 해서 결과를 가져옵니다. 즉, 전체 개수( total count )와 페이징된 결과( results )를 한 번에 반환하려고 했는데, JPA 환경에서는 count 쿼리 최적화 문제 SQL이 복잡해질 경우 성능 저하 문제 Querydsl 5.x 이후로는 직접 count 쿼리와 content 쿼리를 분리 해서 호출하는 방식을 권장합니다. ```
안녕하게요 강의를 보고 있는데 Kafka Connect 이 부분이 기존의 zookeeper 에서 kraft 로 버전이 변경되서 지원을 안 하는데 혹시 다른 방법이 있을까 ? 싶어 질문 올립니다. 참고로 블로그 도 여러개 봤는데 계속 에러가 걸려서 질문 올립니다. 정확히 kafka Connect 설치 를 하고 이거를 했을때 C:\Work\confluent-5.5.2>bin\windows\connect-distributed.bat etc\kafka\ connect-distributed.properties Classpath is empty. Please build the project first e.g. by running 'gradlew jarAll' C:\Work\confluent-5.5.2> 윈도우이고 이런 에러가 계속 뜨는데 해결이 안됩니다.
private String makeTempPassword() { StringBuffer buffer = new StringBuffer(); for(int i = 0; i < 10; i++){ buffer.append( (char) ( (int)(Math.random()*55) + 65 )); } return buffer.toString(); } 해당 코드에서 StringBuilder가 아닌 StringBuffer를 사용하는 이유가 따로 있나요?? 성능적으로 큰 차이는 없겠지만 혹시 특별한 이유가 있는지 궁금해서 여쭤봅니다!!
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? 예 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예 3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예 [질문 내용] 영한님 안녕하세요. Spring Data JPA가 제공하는 save, saveAndFlush 메서드에 대해 궁금한 점이 있는데요. 흔히들 saveAndFlush는 save와 달리 트랜잭션 커밋시점이 아닌, 영속성 컨텍스트 변경 사항을 즉각 DB에 반영하여, DB 통신이 증가한다는 단점이 있다고 합니다. for (int i = 0; i < 3; i++) { repository.save(entity); } 그런데, 제가 의문이 드는 점은 save 메서드도 위와 같은 상황이 있을 때, 커밋 시점에 DB에 flush를 하기는 하지만, batch insert가 아니기 때문에 DB 통신 자체는 단 건으로 총 3번 일어나는 것 아닌가요? 따라서, DB 통신 자체에서 saveAndFlush가 얻는 이점은 없다고 생각되는 데 어떤 이점이 있을까요? 감사합니다
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예) [질문 내용] 임베디드 타입 강의 (5:30)에서, @ManyToOne을 지웠는데, 강사님의 Team의 @OneToMany가 어떻게 되어 있기에 H2 데이터베이스와 연결했을 때, Member table에 FK로 TEAM_ID가 들어가게 되는지 (6:58)궁금합니다. 저 같은 경우에는, Team의 어노테이션이 이렇게 되어있는데, @OneToMany(mappedBy = "team") private List<Member> members = new ArrayList<>(); 그대로 실행시켰을 때, 아래처럼 매핑이 잘못되었다는 문구가 뜹니다. Exception in thread "main" org.hibernate.AnnotationException: Collection 'hellojpa.Team.members' is 'mappedBy' a property named 'team' which does not exist in the target entity 'hellojpa.Member'
안녕하세요. 강의를 보면서 차근차근 작업중인데요. 테스트 하다보니 한글로 제목과 내용을 작성하고 등록을 하면 등록이 완료되었다고 뜨나 실제 리스트 페이지에선 업데이트가 되지않고 있습니다. 제가 설정을 빠트린게 있는걸까요? 실제로 영문이나 숫자로만 입력후 등록하면 리스트에도 잘 반영됩니다.
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] fetch join의 동작에 대하여 의구심이 들어 찾아봤더니 "fetch join의 경우 SQL에서 사용하는 join의 종류가 아닌 JPQL에서 성능 최적화를 위해 제공하는 기능인데요. fetch join은 조회하는 주체가 되는 entity 외에 fetch join이 걸린 연관 관계가 있는 entity까지 함께 select 하여 영속화합니다." 라고 합니다. 영속성 컨텍스트는 기본적으로 트랜잭션 범위내에서 생성되고 종료되는거 할거같은데, 어떻게 컨트롤러에서 트랜잭션이 걸려있지 않은 메서드를 바로 호출해서 사용해도 이러한 것이 가능한지 궁금해서 찾아보니 OSIV라는게 있더군요. OSIV는 기본적으로 트랜잭션이 시작 후 종료되어도 일정 부분은 영속성 컨텍스트를 웹요청 전체에 열어둔다. 때문에 OSIV를 끄면 지연로딩같은건 컨트롤러에서 이루어지게 코딩해두었으니 예외가 뜬다. 근데 여기서 문제는 OSIV를 끄면 fetch join도 작동안해야할거같은데 작동을 합니다. 레포에 눈에 보이지 않는 트랜잭션이라도 걸려있는건가요?
[질문 내용] OrderItem에서 가격 총합을 구할 때 자기 자신의 필드임에도 return orderPrice * count; 이렇게 바로 쓰기 보다는 public int getTotalPrice() { return getOrderPrice() * getCount(); } get으로 가져오시더라구요. 그렇다면 Order에서는 //==조회 로직== public int getTotalPrice() { int totalPrice = 0; for (OrderItem orderItem : orderItems) { totalPrice += orderItem.getTotalPrice(); } return totalPrice; } for문에 orderItems에도 getOrderItems()로 하셨었나? 하고 봤더니 이거는 바로 접근을 하시는데 자기 필드를 get으로 접근 하는 것이 조금 어색한데, 혹시 이유가 있을까요?
안녕하세요! 토비님. 헷갈리는 개념이 하나 있어서 여쭤보고 싶습니다. 바로 헥사고날 아키텍처에서 UseCase의 책임 범위인데요. 우선 정답은 없다는 것은 알고 있습니다. 다만 Best Practice나 권장되는 방법이 있는지, 그리고 토비님의 고견이 궁금하여 질문드리게 되었습니다. 기능 단위로 UseCase 인터페이스 분리하기 vs 연관된 기능은 UseCase 인터페이스에 묶음으로 제공하기(메서드별로) 입니다. 전자는 SRP가 매우 엄격하게 준수되고, 테스트 용이성, 개별 인터페이스별로 정책을 다르게 적용할 수 있다는 장점들이 있지만 과도하게 인터페이스화를 하다 보니 관리할 포인트가 많아져 복잡해진다는 게 단점인 것 같습니다. 후자는 SRP가 엄격하게 준수되지 않더라도, 관련된 기능을 응집도 있게 관리하기 때문에 테스트 용이성이 조금 떨어지고, 일관된 정책을 관리하거나 인터페이스가 비대해질 수도 있다는 단점이 있지만, 응집도 있게 관리하여 유지보수에는 편한 장점이 있는 것 같습니다. 코드를 예시로 보면 아래처럼 콘서트를 조회한다고 했을 때, 일반적으로 PK를 기반으로 조회하지만, 아래와 같이 콘서트명도 unique하고, 가수도 1개의 진행 중인 콘서트만 가지고 있을 수 있을 때 조회 조건이 Id, Name, ArtistName으로 분류될 수 있다고 예시를 들어보겠습니다. public interface GetConcertUseCase { ConcertResult findById(Long concertId); ConcertResult findByName(String name); ConcertResult findByArtistName(String ArtistName); ConcertResult findByIdWithSchedules(Long concertId); // Aggregate Member인 ConcertSchedule 목록 정보도 포함하여 조회 } 위에처럼 구성하는 게 후자 방식이고 응집도가 높다고 생각합니다. 그런데 해당 방식은 유스케이스가 비대해질 수 있고, 단일 책임 원칙에서 벗어날 수 있다는 의견 때문에 조회 목적별로 유스케이스 분리하는 것을 권장하는 의견도 있습니다. (전자 방식) public class GetConcertByIdUseCase { ... } public class GetConcertByNameUseCase { ... } public class GetConcertByArtistUseCase { ... } public class GetConcertByIdWithSchedulesUseCase { ... } 정답은 없어서 프로젝트 규모나, 각자의 스타일, 기능 분석에 의해 정해지겠다만, 보편적으로 이런 경우 어떻게 접근하는 게 Best Practice인지 감이 잡히질 않아 질문드리게 되었습니다.
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? 예 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예 3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예 [질문 내용] 본래 DB에 쿼리를 날리는 행위는 트랜잭션 안에서 이루어져야하는데 본 강의부터 뒤쪽 강의까지 컨트롤러단에서 직접 레포지토리 메서드를 호출하여 코드를 작성하는걸 볼 수 있습니다. 제가 알아본 바로는 아래와 같다고 합니다. 쓰기 작업 : 반드시 트랜잭션 필요. 조회 작업 : 트랜잭션 없어도 가능. 단, 지연 로딩 시 트랜잭션 없으면 예외 발생. 실무 규칙 : Repository는 트랜잭션 안 열고, Service 계층에 @Transactional로 비즈니스 단위 트랜잭션 관리. 읽기 전용 조회 최적화 : @Transactional(readOnly = true) 사용 → 성능 최적화 + 예외 방지. 근데 뒤쪽 강의에서는 fetch join을 사용하니 지연 로딩에 대한 예외가 발생안한다고 쳐도 현재 이 강의 초반에 /api/v1/simple-orders 에 대한 메서드에서는 실제로 지연 로딩에 의한 쿼리를 호출합니다. 그렇다면 결국 트랜잭션이 없는 상태에서 이루어진 것이니 LazyInitializationException 예외가 발생해야하는거 아닌가요? 그리고 보통 실무에서 컨트롤러에서 fetch join 쿼리 메서드(트랜잭션이 걸려있지 않은) 등을 레포지토리에서 바로 호출하는 경우가 흔한가요?
repository에 직접 접근하는 기준이 어떤건지 궁금합니다. 강의에서 예를 들면 @Override public Member updateInfo(Long memberId, MemberInfoUpdateRequest memberInfoUpdateRequest) { Member member = memberFinder.find(memberId); checkDuplicateProfile(member, memberInfoUpdateRequest.profileAddress()); member.updateInfo(memberInfoUpdateRequest); return memberRepository.save(member); } private void checkDuplicateProfile(Member member, String profileAddress) { if(profileAddress.isEmpty()) return; Profile currentProfile = member.getDetail().getProfile(); if(currentProfile != null && currentProfile.address().equals(profileAddress)) return; if(memberRepository.findByProfile(new Profile(profileAddress)).isPresent()){ throw new DuplicateProfileException("이미 존재하는 프로필 주소입니다: " + profileAddress); } } 이 부분에서 member를 찾아올 때는 memberFinder를 통해서 가져왔는데 아래 중복 체크에서는 repository를 바로 사용했습니다. 물론 강의에서 profile을 찾는거기에 memberFinder에 넣기에는 책임이 난잡해질 것 같다는 느낌은 듭니다만 여기서 구분한 기준이 있는지 궁금합니다. 다른 예를 들면 public LoginResponse login(LoginRequest request) { Member member = memberFinder.findByEmail(request.email()); if(!member.verifyPassword(request.password(), passwordEncoder)){ //일치하지 않으면 에러 } //이 부분은 직접 접근하는게 좋은지 member.updateLastLoginAt(); memberRepository.save(member); //아니면 memberUpdater라는 포트를 통해 접근하는게 좋은지 memberUpdater.updateLastLoginAt(); return new LoginResponse(member.getId(), member.getStatus()); } 이런 경우 둘중 어떤 방법이 더 구조적으로 좋은지 궁금합니다.
[질문 내용] 24분 27초에 연관관계 편의 메서드 위치는 컨트롤 하는 쪽에 있는 게 좋다. 라고 하셨는데 연관관계의 주인쪽이라고 이해를 했습니다. 그렇다면 OrderItem을 추가하는 메서드는 Order쪽이 아니라 OrderItem에 해주어야 하는 게 아닐까 생각이 드는데 맞을까요?