날짜 검색에서 LocalDate vs LocalDateTime
왜 LocalDate로 입력을 받았을까?
사용자는 날짜만 신경 쓰지, 시:분:초까지 직접 입력할 필요가 없음
UI/UX 관점에서 날짜 입력이 훨씬 직관적임
DB에는 생성 시점이 LocalDateTime으로 저장됨
응답 DTO는 정확한 생성 시각을 보여주는 게 맞음
@DateTimeFormat + atStartOfDay / LocalTime.MAX를 쓰는 이유
LocalDate는 시간 정보가 없음
DB 컬럼은 LocalDateTime
그래서:
시작일 → 00:00:00
종료일 → 23:59:59.999999999
이 방식이 의도가 가장 명확하다고 함.
QueryDSL/JPA에서 시간 경계 버그를 가장 확실히 방지
QueryDSL에서 BooleanExpression이 null인데도 동작하는 이유
private BooleanExpression titleContains(String keyword) {
return keyword == null ? null : todo.title.contains(keyword);
}
QueryDSL의 where()는 null 조건을 자동으로 무시한다.
조건이 있으면 AND로 결합하고, 없으면 제외한다고 한다.
query.where(
titleContains(keyword),
createdAtBetween(start, end)
);
장점
if-else 지옥 방지
조건 조합 폭발 방지
가독성, 유지보수성 ↑
fetchOne() vs fetch() 차이 (강의내용 중복)
fetchOne()
결과가 1개일 거라 확신할 때만
2개 이상이면 예외 발생
fetch()
리스트 조회
결과 개수와 무관
Optional을 쓰는 이유 & orElseThrow 자동완성
null 반환 → 실수로 NPE 발생
Optional → “없을 수도 있음”을 타입으로 강제
userRepository.findById(id)
.orElseThrow(...)
orElseThrow()는 Optional 전용 메서드
엔티티 자체에는 존재하지 않음
N+1 문제가 발생한 이유와 해결 방식
연관 엔티티가 LAZY 로딩
반복 접근 시 쿼리가 추가로 실행됨
fetch join
QueryDSL에서 join + fetch
필요한 필드만 Projections로 조회
SecurityConfig에 permitAll이 있는데 JWT 필터에서 또 체크하는 이유
.anyRequest().authenticated()
이건 인가(Authorization) 단계
JwtFilter는 그보다 앞단의 인증(Authentication) 필터
필터는 무조건 실행됨
/auth/** 요청에서도 JWT 검사하면 오류 발생
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
return request.getRequestURI().startsWith("/auth");
}
UsernamePasswordAuthenticationFilter 앞에 JWT 필터를 두는 이유
폼 로그인 기반
ID/PW 인증 처리
JWT는 이미 인증된 사용자
UsernamePasswordAuthenticationFilter까지 갈 필요 없음
오늘의 핵심 정리
“Spring / JPA / Security에서의 설정과 패턴은
‘될 때까지 맞추는 코드’가 아니라
왜 이 레이어에서 이 책임을 가지는지를 이해하는 게 중요하다!!!”
재미따 ㅎㅎ 뭔가 저도 했던 고민들인데 제이용님은 잘 정리하여 작성하셨고 저는 그러지 못했네요 ㅠㅠ 반성합니다.