Day66

강태훈·2026년 4월 6일

nbcamp TIL

목록 보기
66/97

Spring Plus 개인과제 트러블슈팅 모음

Spring Boot 3.3.3 기반 프로젝트에서 발생한 트러블슈팅 정리


목차

  1. @Transactional readOnly 속성 오류
  2. Spring Security 전환 - JwtFilter 이중 등록
  3. POST 요청만 401 발생
  4. H2 콘솔 접근 불가
  5. data.sql Primary Key 충돌
  6. TodoCustomRepositoryImpl Bean 등록 실패

1. @Transactional readOnly 속성 오류

문제 상황

/todos API 호출 시 아래 에러 발생.

Connection is read-only. Queries leading to data modification are not allowed
insert into todos (contents,created_at,modified_at,title,user_id,weather) values (?,?,?,?,?,?)

원인

TodoService 클래스 레벨에 @Transactional(readOnly = true) 설정 상태에서, saveTodo() 메소드에 별도 @Transactional 미적용.
읽기 전용 트랜잭션에서 INSERT 시도 → 오류 발생.

해결 방법

쓰기 작업 메소드에 @Transactional 명시적 추가.

// 클래스 레벨
@Transactional(readOnly = true)
public class TodoService {

    // 쓰기 작업 메소드에 별도 선언
    @Transactional
    public TodoSaveResponse saveTodo(...) { ... }
}

2. Spring Security 전환 - JwtFilter 이중 등록

문제 상황

shouldNotFilter에서 /api/auth를 제외 처리했음에도 회원가입/로그인 요청에서 400 Bad Request - JWT 토큰이 필요합니다. 발생.

원인

@Component를 붙인 JwtFilter가 Spring Security 필터 체인과 서블릿 필터 양쪽에 이중 등록.

요청 흐름:
1. 서블릿 필터로 실행 → shouldNotFilter 무시 → JWT 검증 시도 ❌
2. Spring Security 필터 체인 실행 → shouldNotFilter 동작 ✅

해결 방법

FilterRegistrationBean으로 서블릿 필터 자동 등록 방지.

@Bean
public FilterRegistrationBean<JwtFilter> jwtFilterRegistration(JwtFilter jwtFilter) {
    FilterRegistrationBean<JwtFilter> registration = new FilterRegistrationBean<>(jwtFilter);
    registration.setEnabled(false); // 서블릿 필터 자동 등록 방지
    return registration;
}

3. POST 요청만 401 발생

문제 상황

GET 요청(조회)은 정상 동작하지만, POST 요청(생성)만 401 Unauthorized 발생.
토큰도 정상적으로 설정된 상태.

원인

Postman의 Inherit Auth from Parent 옵션 사용 중, 회원가입 요청에도 토큰이 자동 포함.
실제로는 500 에러(data.sql PK 충돌)였지만 Postman에서 401로 오인.

해결 방법

Postman에서 회원가입/로그인 요청의 Authorization을 No Auth로 명시적 설정.

회원가입 POST /api/auth/signup → Authorization: No Auth
로그인   POST /api/auth/signin → Authorization: No Auth
기타 API                       → Authorization: Bearer Token

4. H2 콘솔 접근 불가

문제 상황

http://localhost:8080/h2-console 접속 시 "localhost에서 연결을 거부했습니다" 표시.
SecurityConfig에서 permitAll() 설정 완료 상태.

원인

Spring Security가 기본적으로 X-Frame-Options: DENY를 설정하여 iframe 차단.
H2 콘솔은 내부적으로 iframe을 사용하므로 렌더링 불가.

해결 방법

SecurityConfigframeOptions 비활성화 추가.

http
    .csrf(AbstractHttpConfigurer::disable)
    .headers(headers -> headers
        .frameOptions(frame -> frame.disable()) // H2 콘솔 iframe 허용
    )

application-local.yml H2 콘솔 활성화 확인.

spring:
  h2:
    console:
      enabled: true
      path: /h2-console

5. data.sql Primary Key 충돌

문제 상황

Todo 생성, 회원가입 등 INSERT 작업 시 아래 에러 발생.

Unique index or primary key violation: PRIMARY KEY ON PUBLIC.TODOS(ID)

원인

data.sql에서 ID를 직접 지정(1~6)하여 초기 데이터 삽입.
JPA의 ID 시퀀스(AUTO_INCREMENT)는 1부터 시작하므로 충돌 발생.

-- data.sql: ID 1~6 직접 삽입
INSERT INTO todos (id, ...) VALUES (1, ...) ~ (6, ...)

-- JPA: 새 데이터 삽입 시 ID=1 부터 시작 → 충돌!

해결 방법

data.sql 하단에 각 테이블의 ID 시퀀스를 초기 데이터 이후 값으로 재설정.

ALTER TABLE users    ALTER COLUMN id RESTART WITH 3;
ALTER TABLE todos    ALTER COLUMN id RESTART WITH 7;
ALTER TABLE managers ALTER COLUMN id RESTART WITH 7;
ALTER TABLE comments ALTER COLUMN id RESTART WITH 19;

6. TodoCustomRepositoryImpl Bean 등록 실패

문제 상황

애플리케이션 실행 시 아래 에러 발생.

Failed to instantiate TodoCustomRepositoryImpl: No default constructor found
Caused by: java.lang.NoSuchMethodException: TodoCustomRepositoryImpl.<init>()

원인

TodoCustomRepositoryImplEntityManager를 받는 생성자만 존재.
Spring이 Bean 등록 시 기본 생성자(파라미터 없는 생성자)를 찾지 못함.

// 문제 코드 - Spring이 자동 주입 불가
public class TodoCustomRepositoryImpl {
    private final JPAQueryFactory queryFactory;

    public TodoCustomRepositoryImpl(EntityManager em) {
        queryFactory = new JPAQueryFactory(em);
    }
}

해결 방법

EntityManager 대신 이미 Bean으로 등록된 JPAQueryFactory를 직접 주입.
@RequiredArgsConstructor로 생성자 자동 생성.

// 수정 후
@Repository
@RequiredArgsConstructor
public class TodoCustomRepositoryImpl implements TodoCustomRepository {

    private final JPAQueryFactory queryFactory; // Bean으로 등록된 JPAQueryFactory 주입
}

JPAQueryFactoryQuerydslConfig에서 Bean으로 등록되어 있음.

@Configuration
public class QuerydslConfig {

    @PersistenceContext
    private EntityManager em;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(em);
    }
}

전체 요약

#문제원인해결
1Todo 저장 시 read-only 에러@Transactional(readOnly=true) 상속메소드에 @Transactional 추가
2shouldNotFilter 무시@Component로 이중 등록FilterRegistrationBean 추가
3POST만 401 발생Postman 토큰 자동 포함No Auth 명시적 설정
4H2 콘솔 접근 불가Security iframe 차단frameOptions().disable()
5PK 충돌data.sql과 시퀀스 불일치ALTER TABLE RESTART WITH
6Bean 등록 실패기본 생성자 없음JPAQueryFactory 직접 주입

0개의 댓글