Spring 심화 주차 개인 과제를 구현하며 겪은 문제점과 해결방법, 새로 알게된 점을 기록합니다.

유효성 검사를 선행하여 검사를 통과하지 못한 경우 encodePassword를 실행하지 않고 예외를 던진다.

if-else { if } 구조로 불필요하게 중첩된 조건문을 삭제한다.


@Valid, @Pattern 어노테이션을 사용하여 요청 데이터에 대한 검증을 Controller 에서 수행
Todo 엔티티와 User 엔티티의 연관관계가 N:1 (@ManyToOne) 으로 맺어져 있고 지연로딩(FetchType.LAZY) 설정이 되어있다.
지연로딩으로 Todo의 목록을 조회할 때 User 객체를 즉시 가져오지 않고, proxy 객체로 가지고 있다가 User가 사용될 때 조회쿼리를 실행한다. 👉 "N+1 문제" : Todo 목록 조회 쿼리 (1) + 목록의 User를 조회(N) 만큼 수행하게 된다.
이를 해결하기 위해 Todo 목록 조회 쿼리에서 fetch join, @EntityGraph 으로 해결한다.
@Query("SELECT t FROM Todo t LEFT JOIN FETCH t.user u ORDER BY t.modifiedAt DESC")
Page<Todo> findAllByOrderByModifiedAtDesc(Pageable pageable);
@Query("SELECT t FROM Todo t " +
"LEFT JOIN FETCH t.user " +
"WHERE t.id = :todoId")
Optional<Todo> findByIdWithUser(@Param("todoId") Long todoId);
@EntityGraph(attributePaths = {"user"}, type = EntityGraph.EntityGraphType.FETCH)
@Query("SELECT t FROM Todo t LEFT JOIN t.user u ORDER BY t.modifiedAt DESC")
Page<Todo> findAllByOrderByModifiedAtDesc(Pageable pageable);
@EntityGraph(attributePaths = {"user"}, type = EntityGraph.EntityGraphType.FETCH)
@Query("SELECT t FROM Todo t " +
"LEFT JOIN t.user " +
"WHERE t.id = :todoId")
Optional<Todo> findByIdWithUser(@Param("todoId") Long todoId);


passwordEncoder의 matches 메서드는 1번째 인자로 rawPassword, 2번째 인자로 encodePassword를 받는다.


todo의 user가 null 일때, getId()로 값을 가져오는 경우 null.getId() 이므로 예외가 발생한다.

📋 요구사항
어드민 사용자만 접근할 수 있는 특정 API에는 접근할 때마다 접근 로그를 기록해야 한다.
✍️ 로깅 구현 방법
Interceptor✔️ HandlerInterceptor 의 구현체를 생성하여 Overriding
preHandler (전처리)
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
→ true를 반환하면 요청을 계속 진행하고, false를 반환하면 요청 처리를 중단한다.
postHandle (후처리)
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
afterCompletion (요청 완료 후)
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
AOP
@Around 어노테이션을 사용하여 어드민 API 메서드 실행 전후에 요청/응답 데이터를 로깅합니다.Logger 클래스를 활용하여 기록🕹️ 실행시점 제어 어노테이션
| 어노테이션 | 실행 시점 | 주요 활용 |
|---|---|---|
@Before | 메서드 실행 이전 | 입력 값 검증, 로깅, 인증 |
@After | 메서드 실행 이후 (예외 포함) | 리소스 해제, 트랜잭션 관리 |
@AfterReturning | 메서드가 정상 실행된 후 | 반환값 로깅 및 조작 |
@AfterThrowing | 예외 발생 시 | 예외 로깅 및 에러 처리 |
@Around | 메서드 실행 전후 모두 | 실행 시간 측정, 로깅, 응답 변조 |

최대한 풀커버하기 위해 노력했지만 테스트코드 작성이 어려워 생각보다 진도가 빨리빨리 나가지 않아 일부 도메인에서만 테스트를 진행하였습니다. 현업에서도 TDD를 지향하지만 도입하기 힘들어하는 지 이해가 되었습니다. 🫠
🧑🏫 과제 해설 강의 메모
터미널에서 아래 명령어를 입력하여 랜덤 키 생성
openssl rand -hex 32
쓸데없이 사용되는 코드가 있는지 확인하기
@EntityGraph 를 사용하는 경우 @Query를 사용하여 직접 sql를 작성하지 않아도 된다.
jpa method 로 자동 생성된 쿼리를 사용할 수 있다.