Skip to content

fix: BaseException·BaseRuntimeException 이 전달받은 원인 예외를 보관하지 않아 getWrappedException() 이 항상 null 인 문제 수정 - #350

Merged
jei007 merged 1 commit into
eGovFramework:mainfrom
masiljangajji:fix/wrapped-exception-not-retained
Aug 24, 2026
Merged

jei007 merged 1 commit into
eGovFramework:mainfrom
masiljangajji:fix/wrapped-exception-not-retained

Conversation

@masiljangajji

Copy link
Copy Markdown
Contributor

수정 사유 Reason for modification

소스를 수정한 사유가 무엇인지 체크해 주세요. Please check the reason you modified the source. ([X] X는 대문자여야 합니다.)

  • 버그수정 Bug fixes
  • 기능개선 Enhancements
  • 기능추가 Adding features
  • 기타 Others

수정된 소스 내용 Modified source

BaseException 과 BaseRuntimeException 은 protected Exception wrappedException 필드와 getWrappedException() 접근자를 제공합니다. 그런데 원인 예외를 받는 모든 생성자가 super(wrappedException) 만 호출하고 이 필드에는 대입하지 않습니다. 저장소 전체에서 이 필드에 대입하는 곳은 setWrappedException() 하나뿐이므로, 생성자로 원인 예외를 넘겨도 getWrappedException() 은 항상 null 을 반환합니다. getCause() 는 정상 동작하므로 같은 객체에서 두 접근자가 서로 다른 값을 주는 상태입니다.

대입이 빠진 이유는 타입 폭이 맞지 않기 때문입니다. 필드는 Exception 인데 생성자 파라미터는 Throwable 이라 그대로 대입하면 컴파일되지 않습니다. 반면 EgovBizException 과 EgovBatchException 은 파라미터를 Exception 으로 선언해 같은 필드에 대입하고 있고, 그래서 이 두 클래스에서는 접근자가 정상 동작합니다. 이 두 클래스는 각각 #341 과 #307 에서 원인 예외 전달이 정리되었으며, 이번 수정은 그 상위 클래스에 남아 있던 같은 성격의 문제를 다룹니다.

수정 방법은 필드와 접근자 시그니처를 그대로 두고 종단 생성자에서 instanceof 로 좁혀 대입하는 방식을 선택했습니다.

this.message = userMessage;
if (wrappedException instanceof Exception exception) {
    this.wrappedException = exception;
}

필드 타입을 Throwable 로 넓히는 방법도 검토했으나, protected 필드이므로 이 필드를 Exception 으로 참조하는 하위 클래스가 있다면 컴파일이 깨질 수 있어 선택하지 않았습니다. 저장소 안에서 이 필드를 읽는 하위 클래스는 없고 EgovBizException 과 EgovBatchException 이 대입만 하고 있으나, 저장소 밖의 사용 여부는 확인하지 못했습니다. Error 나 Exception 이 아닌 Throwable 이 원인으로 전달되는 경우 이 필드에는 담기지 않지만 getCause() 로는 그대로 조회됩니다.

FdlException 은 종단 생성자가 super(wrappedException) 으로 BaseException 의 생성자를 호출하므로 별도 수정 없이 함께 해소됩니다. 회귀 테스트에 이 경우도 포함했습니다.

영향 범위는 이 두 클래스를 직접 생성하는 경로입니다. ExceptionTransfer 는 분류되지 않은 예외를 BaseException 으로 재생성해 던지고(ExceptionTransfer.java processException), EgovExcelServiceImpl 은 BaseRuntimeException 을 원인 예외와 함께 7곳에서 던집니다. 후처리 핸들러가 getWrappedException() 으로 원인을 조회하는 경우 지금까지는 값을 받지 못했습니다. 저장소에 포함된 핸들러 예제 OthersServiceExceptionHandler 도 이 접근자를 사용합니다.

최초 커밋부터 이 필드가 생성자에서 채워진 적이 없어, 기존에 이 값을 받아 동작하던 코드는 없습니다.

변경 규모는 소스 2파일 각 6줄, 테스트 1파일 신규입니다.

JUnit 테스트 JUnit tests

테스트를 완료하셨으면 다음 항목에 [대문자X]로 표시해 주세요. When you're done testing, check the following items.

  • JUnit 테스트 JUnit tests
  • 수동 테스트 Manual testing

신규 테스트 WrappedExceptionRetentionTest 7건을 추가했습니다. BaseException · BaseRuntimeException · FdlException 각각에 대해 기본 메시지 생성자와 MessageSource 생성자 두 경로에서 getWrappedException() 과 getCause() 가 전달한 객체와 동일한지 확인하고, 원인 예외를 넘기지 않은 생성자에서는 null 인 것도 함께 확인합니다.

수정을 되돌린 상태에서 신규 테스트를 실행하면 6건이 전부 expected: <java.lang.IllegalStateException: ...> but was: <null> 로 실패하고, 원인 예외를 넘기지 않는 1건만 통과합니다.

측정 환경은 JDK 21, Maven 3.9.16 입니다.

대상 결과
Foundation/org.egovframe.rte.fdl.cmmn 38건 통과, 실패 0
Foundation/org.egovframe.rte.fdl.excel 33건 통과, 실패 0
Foundation/org.egovframe.rte.fdl.idgnr 42건 통과, 실패 0
Batch/org.egovframe.rte.bat.core 86건 통과, 실패 0

fdl.excel 과 fdl.idgnr 은 각각 BaseRuntimeException 과 FdlException 을 사용하는 모듈이고, bat.core 는 BaseRuntimeException 을 상속한 EgovBatchException 을 사용하는 모듈이라 회귀 확인 대상에 포함했습니다.

테스트 브라우저 Test Browser

테스트를 진행한 브라우저를 선택해 주세요. Please select the browser(s) you ran the test on. (다중 선택 가능 you can select multiple) [X] X는 대문자여야 합니다.

  • Chrome
  • Firefox
  • Edge
  • Safari
  • Opera
  • Internet Explorer
  • 기타 Others

예외 클래스의 생성자 동작 변경이라 화면 동작과 무관하여 브라우저 테스트는 진행하지 않았습니다.

테스트 스크린샷 또는 캡처 영상 Test screenshots or captured video

화면 변경이 없어 스크린샷을 첨부하지 않았습니다. 대신 수정 전후 동작 차이를 아래 표로 정리했습니다. 각 클래스에 원인 예외를 생성자로 전달한 뒤 두 접근자를 조회한 결과입니다.

클래스 수정 전 getWrappedException() 수정 후 getWrappedException() getCause() (수정 전후 동일)
BaseException null 전달한 원인 예외 전달한 원인 예외
BaseRuntimeException null 전달한 원인 예외 전달한 원인 예외
FdlException null 전달한 원인 예외 전달한 원인 예외
EgovBizException 전달한 원인 예외 전달한 원인 예외 전달한 원인 예외

…pedException() 이 항상 null 인 문제 수정

BaseException 과 BaseRuntimeException 은 protected Exception wrappedException
필드와 getWrappedException() 접근자를 제공한다. 그런데 원인 예외를 받는 모든
생성자가 super(wrappedException) 만 호출하고 이 필드에는 대입하지 않는다.
저장소 전체에서 이 필드에 대입하는 곳은 setWrappedException() 하나뿐이므로,
생성자로 원인 예외를 넘겨도 getWrappedException() 은 항상 null 을 반환한다.
getCause() 는 정상 동작하므로 같은 객체에서 두 접근자가 서로 다른 값을 준다.

대입이 빠진 이유는 타입 폭이 맞지 않기 때문이다. 필드는 Exception 인데
생성자 파라미터는 Throwable 이라 그대로 대입하면 컴파일되지 않는다.
EgovBizException 과 EgovBatchException 은 파라미터를 Exception 으로 선언해
같은 필드에 대입하고 있으며, 그래서 이 두 클래스에서는 접근자가 정상이다.

필드와 접근자 시그니처를 바꾸지 않고 종단 생성자에서 instanceof 로 좁혀
대입했다. 필드 타입을 Throwable 로 넓히는 방법도 있으나 protected 필드라
이 필드를 Exception 으로 참조하는 하위 클래스의 컴파일을 깨뜨릴 수 있어
선택하지 않았다. Error 나 Exception 이 아닌 Throwable 이 원인으로 전달되는
경우 이 필드에는 담기지 않지만 getCause() 로는 그대로 조회된다.

FdlException 은 종단 생성자가 super(wrappedException) 로 BaseException 의
생성자를 호출하므로 별도 수정 없이 함께 해소된다. 회귀 테스트로 확인했다.

영향 범위는 이 두 클래스를 직접 생성하는 경로다. ExceptionTransfer 는
분류되지 않은 예외를 BaseException 으로 재생성해 던지고, EgovExcelServiceImpl
은 BaseRuntimeException 을 원인 예외와 함께 7곳에서 던진다. 후처리 핸들러가
getWrappedException() 으로 원인을 조회하는 경우 지금까지는 값을 받지 못했다.
저장소에 포함된 핸들러 예제도 이 접근자를 사용한다.

최초 커밋부터 이 필드가 생성자에서 채워진 적이 없어 기존 값에 의존하던
코드는 없다.

검증
- 신규 테스트 7건 추가. BaseException·BaseRuntimeException·FdlException 각
  두 종류 생성자에서 getWrappedException() 과 getCause() 가 전달한 객체와
  동일한지 확인하고, 원인 예외를 넘기지 않은 경우 null 인 것도 확인한다.
- 수정을 되돌리면 신규 테스트 6건이 전부 null 로 실패한다.
- org.egovframe.rte.fdl.cmmn 38건 통과.
- 이 두 클래스를 사용하는 모듈 회귀: fdl.excel 33건, fdl.idgnr 42건,
  bat.core 86건 전부 통과.

@jei007 jei007 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

표준프레임워크에 대한 지속적인 참여에
대단히 감사드립니다.

@jei007
jei007 merged commit 1d332c5 into eGovFramework:main Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants