오류 분석

SAXParseException으로 인한 Spring Boot 서버 부팅 실패

somuxsomu 2025. 5. 8. 13:32

SAXParseException으로 인한 Spring Boot 서버 부팅 실패

 

 

💥 Spring Boot 내장 톰캣 서버 부팅 실패 💥

— MyBatis XML 한 줄 오류로 벌어진 도미노 참사

 

 

회사에서 최신 소스를 받고 개발 하다가,

테스트를 하기위해... 서버를 실행하였는데

 

Spring Boot 프로젝트가 부팅 도중 Error creating bean을 뱉으며 꺼져버렸다.

 

우선 콘솔 로그를 보았는데,

주로 이러한 오류 패턴인 경우에는

Mapper(xml 파일)쪽에 문제가 있어기때문에

 

바로 내가 개발한 쿼리부터 체크해보았는데

내가 고작 작성한 쿼리는 단순 update 3개 뿐이였고..

아무리 보아도 문제가 없었다

🤯

 

그럼 도대체 어떤 부분이 문제일까 하고

다시 로그 파일을 열어 위에서부터 차근 차근 자세히 보니

UnsatisfiedDependencyException, BeanCreationException, sqlSessionFactory

관련된 키워드들이 쏟아지고 있었다...! 

 

여기서 힌트를 얻었다.

 


🧨 발생한 에러 로그 요약 (일부)

 

Error creating bean with name 'baseCode': Unsatisfied dependency expressed through field 'commonDao':
Error creating bean with name 'commonDao': Injection of resource dependencies failed ... Caused by: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 1468; columnNumber: 100; The content of elements must consist of well-formed character data or markup.

🔍 원인: 매퍼 XML에 무심코 쓴 < >

 

에러 로그에 있는

SAXParseException, lineNumber, well-formed character에서 힌트를 얻고
최신 커밋 이력에 있는 파일 중 exampleMapper.xml을 확인해보았다.

(내가 작성한 파일이 아닌, 팀원이 올린 소스의 mapper가 범인이였다 😮)

 

 

해당 파일의 lineNumber 1468줄을 확인해보니,

< > 가 범인이라는걸 바로 알아챘다.

, IFNULL(cs.in_tot, 0) + IFNULL(cs.out_tot, 0) + IFNULL(cs.add_tot, 0) - IF(cs.add_tot <> 0, (GREATEST(IFNULL(cs.in_tot, 0), IFNULL(cs.out_tot, 0), IFNULL(cs.add_tot, 0))), 0)

 

 

MyBatis XML 에서 <, >는 XML 예약 문자이기 때문에

반드시 escape 하거나 CDATA로 감싸야 한다.


< >를 그대로 쓰면 SAX 파서가 문법 오류로 인식해버린다.


🧯 해결 방법

XML에서는 아래처럼 반드시 바꿔야 한다

 

🔸 방법 1: CDATA로 감싸기 

제일 추천 하는 방법.

<![CDATA[ IF(cs.add_tot <> 0, ...) ]]>
 

🔸 방법 2:  escape 

< → &lt;, > → &gt; 로

 

가독성이 떨어진다는 단점이 있다.

IF(cs.add_tot &lt;&gt; 0, ...)

 

🔍

그리고 보통 빌드가 실패할때

마지막 줄 로그를 먼저 확인을 하는데

 xml(mybatis mapper) 때문에 실패했다는 내용보다는

commonDao와 baseCode오류에 관한 내용이 많이 언급된다. 

자세한 이유는 아래에서 살펴보자.

 

[2025-05-08 10:27:38:13529] [][] INFO [restartedMain] o.apache.jasper.servlet.TldScanner - At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
[2025-05-08 10:27:38:13545] [][] INFO [restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
[2025-05-08 10:27:38:13546] [][] INFO [restartedMain] o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 9499 ms
[2025-05-08 10:27:39:14000] [][] INFO [restartedMain] o.s.b.web.servlet.RegistrationBean - Filter disableSpringBootErrorFilter was not registered (disabled)
[2025-05-08 10:27:39:14789] [][] INFO [restartedMain] c.g.c.mybatis.MybatisInterceptor - properties => {} [2025-05-08 10:27:40:15244] [][] WARN [restartedMain] o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'baseCode': Unsatisfied dependency expressed through field 'commonDao': Error creating bean with name 'commonDao': Injection of resource dependencies failed [2025-05-08 10:27:40:15263] [][] INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
[2025-05-08 10:27:40:15470] [][] INFO [restartedMain] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed. [2025-05-08 10:27:40:15474] [][] INFO
[restartedMain] o.a.catalina.core.StandardService - Stopping service
[Tomcat] [2025-05-08 10:27:40:15495] [][] INFO [restartedMain] o.s.b.a.l.ConditionEvaluationReportLogger - Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.

 

👉🏼 실패 도미노 흐름 : 의존성 

 

XML 문법 오류 발생 (exampleMapper.xml)

🔽

MyBatis가 sqlSessionFactory 생성 실패

🔽

sqlSessionFactory를 사용하는 commonDao 생성 실패

🔽

commonDao를 의존하는 baseCode 생성 실패

🔽

baseCode Bean 생성 실패 ==> Spring 전체 ApplicationContext 초기화 실패

🔽

Spring Boot 앱 부팅 실패, 서버 종료

 

 

🧱 1단계: sqlSessionFactory 생성 실패

Spring Boot에서 MyBatis를 쓸 때,

SqlSessionFactory는 MyBatis의 핵심 엔진이다.

 
@Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {     SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
 factoryBean.setDataSource(dataSource);   
 factoryBean.setMapperLocations(...); // 여기에 XML들 로딩됨
 return factoryBean.getObject();

 // 💥 여기서 터짐!
}
  • exampleMapper.xml에 문법 오류가 있음
  • MyBatis가 해당 XML을 파싱할 수 없음 (SAXParseException)
  • 그래서 sqlSessionFactory 생성 자체가 실패함

 

🧱 2단계: commonDao 주입 실패

이 프로젝트에서 commonDao.java 파일은

MyBatis SqlSessionTemplate을 사용하는 Dao 혹은 공통 Mapper로 쓰이는 Bean이다.

이 Bean이 생성될 때, 내부적으로 SqlSessionFactory를 필요로 하다.

 
public class CommonDao {
 @Autowired private SqlSessionTemplate sqlSessionTemplate;
 // 💥 얘가 sqlSessionFactory 필요
}
 
  • sqlSessionFactory 자체가 안 만들어졌기 때문에, commonDao도 Bean 생성 실패 → 주입 불가

 

🧱 3단계: baseCode Bean 생성 실패

@Component public class BaseCode {
  @Resource(name = "commonDao")
  private CommonDao commonDao;
  // 💥 여기서 터짐
}
 
  • commonDao 주입 안 되니, baseCode 생성도 실패
  • Spring은 의존성 주입이 1개라도 실패하면 해당 Bean 전체를 생성 안한다.

 

🧱 4단계: Spring Boot 전체 컨텍스트 초기화 실패

  • Spring Boot는 ApplicationContext 생성에 실패하면 부팅 자체를 중단한다.
  • @Component, @Service 같은 Bean들 중 하나라도 생성 실패 → 앱 자체가 기동 안 됨

 

👀 마지막으로

 

  1. MyBatis XML 안에서 절대 <, >를 그냥 쓰지 말자
    • <, >는 XML 문법의 일부이므로 무조건 escape or CDATA 처리 필요!
  2. SAXParseException + lineNumber = 무조건 Mapper XML 확인
    • 줄 번호는 신의 선물이다.
  3. DI 실패는 종착지가 아니다, 대부분은 앞단에서 이미 실패 중
    • Bean 생성 실패가 나도 진짜 원인은 그 앞줄 어딘가에 있다.

 

✅ 이런 구조 이해하고 있으면 앞으로 로그확인시 DAO 주입 실패 → Mapper XML부터 의심가능 

sqlSessionFactory 관련 문제는 거의 대부분 Mapper XML 거나 경로 문제이다.