Notice
Recent Posts
Recent Comments
Link
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Tags
more
Archives
Today
Total
관리 메뉴

che01 님의 블로그

플러스 주차 본문

카테고리 없음

플러스 주차

che01 2025. 7. 4. 12:22

Level 1 트러블슈팅

1. @Transactional(readOnly = true)에서 insert 발생 시 예외 해결

문제 상황 서비스 클래스에 @Transactional(readOnly = true)가 설정되어 있는 상태에서 데이터 삽입을 시도했을 때 Connection is read-only 예외가 발생했습니다.

원인: 읽기 전용 트랜잭션에서는 데이터 변경 작업이 허용되지 않습니다.

해결 방법: 데이터 변경이 필요한 메서드에 @Transactional(readOnly = false) 명시적으로 설정하여 메서드 레벨에서 오버라이드했습니다.

2. User 엔티티에 nickname 컬럼 추가 및 JWT 토큰 처리

문제 상황 기존 JWT 토큰에는 userId만 포함되어 있어 매번 사용자 정보를 조회해야 했습니다.

해결 과정

  • User 엔티티에 nickname 컬럼 추가
  • JWT 생성 시 Claims에 nickname 정보 포함
  • 토큰 검증 후 nickname을 바로 조회할 수 있도록 구현

주의사항: 기존 토큰과의 호환성을 위해 nickname이 없는 경우 기본값 처리 로직을 추가했습니다.

3. JPQL 기반 동적 검색 기능 구현

문제 상황 날씨 조건과 수정일 기간 조건을 선택적으로 적용하는 동적 검색 기능이 필요했습니다.

해결 방법 @Query 어노테이션 내부에서 :조건 IS NULL OR 필드 = :조건 방식을 사용하여 동적 조건을 처리했습니다.

핵심 포인트: 파라미터가 null인 경우 해당 조건을 무시하고, 값이 있는 경우에만 조건을 적용하는 방식으로 구현했습니다.

4. Controller 테스트 예외 메시지 불일치 해결

문제 상황 테스트 코드에서 예상한 예외 메시지와 실제 발생한 예외 메시지가 달라 테스트가 실패했습니다.

원인: 실제 예외 메시지를 확인하지 않고 추측으로 테스트 코드를 작성했습니다.

해결 방법: 디버깅을 통해 실제 예외 메시지를 확인하고 테스트 코드를 수정했습니다.

5. AOP 로그 처리 시점 수정

문제 상황 메서드 실행 후에 로그가 남아 실행 전 상태를 확인할 수 없었습니다.

해결 방법: @After 대신 @Before 어노테이션을 사용하여 메서드 실행 전에 로그를 수행하도록 수정했습니다.

Level 2 트러블슈팅

1. Todo 생성 시 유저 자동 매니저 등록 cascade 적용

문제 상황 Todo 생성 시 해당 유저를 매니저로 등록하는 과정에서 별도의 save 호출이 필요했습니다.

해결 방법: @OneToMany(mappedBy = "...", cascade = CascadeType.PERSIST) 적용으로 Todo 저장 시 연관된 매니저도 자동으로 저장되도록 했습니다.

주의사항: cascade 타입 선택 시 의도하지 않은 영속성 전이가 발생하지 않도록 주의해야 합니다.

2. 댓글 목록 조회 시 N+1 문제 해결

문제 상황 댓글 목록 조회 시 각 댓글의 작성자 정보를 가져오기 위해 추가 쿼리가 N번 실행되었습니다.

해결 방법: @EntityGraph(attributePaths = {"user"}) 또는 JPQL fetch join을 사용하여 한 번의 쿼리로 연관 데이터를 함께 조회했습니다.

성능 개선: N+1 문제 해결로 쿼리 실행 횟수가 N+1개에서 1개로 감소했습니다.

3. findByIdWithUser() QueryDSL 전환

문제 상황 JPQL로 작성된 쿼리를 QueryDSL로 전환하면서 N+1 문제도 함께 해결해야 했습니다.

해결 방법: QueryDSL의 join 기능을 사용하여 연관 관계를 한 번에 처리하도록 구현했습니다.

장점: 컴파일 타임에 쿼리 오류 검증 가능 및 타입 안정성 확보했습니다.

4. Spring Security 적용

문제 상황 기존 필터 기반 인증 구조를 Spring Security로 전환하면서 기존 JWT 토큰 방식을 유지해야 했습니다.

해결 과정

  • 기존 필터 제거 및 SecurityFilterChain 설정
  • UserDetailsService 구현하여 사용자 정보 로드
  • @AuthenticationPrincipal 적용으로 컨트롤러에서 사용자 정보 주입
  • JWT 토큰 검증 로직은 JwtFilter에서 처리

주의사항: 기존 API 호환성을 유지하면서 점진적으로 Security 기능을 적용했습니다.

Level 3 트러블슈팅

1. 일정 검색 기능 구현 (QueryDSL 기반)

문제 상황 복잡한 검색 조건과 성능 최적화가 필요한 일정 검색 기능 구현이 필요했습니다.

해결 과정

  • QueryDSL을 사용한 동적 쿼리 작성
  • Projections.fields()로 필요한 필드만 조회하여 성능 최적화
  • 제목 및 닉네임 부분 일치 검색 지원
  • Pageable 인터페이스를 통한 페이징 처리

2. 트랜잭션 분리 처리 (로그 기록)

문제 상황 매니저 등록 시 로그 테이블에 요청 정보를 저장하는데, 로그는 등록 실패와 상관없이 항상 저장되어야 하는 요구사항이 있었습니다.

첫 번째 시도와 문제점 @Transactional(propagation = REQUIRES_NEW)를 적용했지만 다음 문제들이 발생했습니다:

셀프 호출 문제: 같은 클래스 내부에서 메서드를 호출할 때 Spring AOP 프록시를 거치지 않아 새로운 트랜잭션이 생성되지 않았습니다.

예외 전파 문제: 로그 저장 메서드에서 예외 발생 시 부모 트랜잭션에 영향을 주어 전체 작업이 롤백되었습니다.

해결 방법

  1. 별도 서비스 클래스 분리: LogService를 별도 클래스로 분리하여 AOP 프록시가 정상 작동하도록 했습니다.
  2. 예외 처리 로직 추가: 로그 저장 메서드에서 예외 발생 시 catch하여 처리하고, 부모 트랜잭션에 영향을 주지 않도록 했습니다.
  3. 트랜잭션 전파 설정: REQUIRES_NEW를 통해 독립적인 트랜잭션에서 로그를 저장하도록 했습니다.

최종 결과: 매니저 등록 성공/실패 여부와 관계없이 로그가 항상 저장되도록 구현했습니다.