Skip to content

fix: MessageConverterImpl convertToTypedObject의 null List/Record NullPointerException 방지 - #305

Open
z3rotig4r wants to merge 1 commit into
eGovFramework:mainfrom
z3rotig4r:fix/messageconverter-response-null-guard
Open

fix: MessageConverterImpl convertToTypedObject의 null List/Record NullPointerException 방지#305
z3rotig4r wants to merge 1 commit into
eGovFramework:mainfrom
z3rotig4r:fix/messageconverter-response-null-guard

Conversation

@z3rotig4r

Copy link
Copy Markdown
Contributor

변경 이유

MessageConverterImpl.convertToTypedObject()의 List 분기와 Record 분기가 source를 널 검사 없이 역참조합니다. List 분기는 (Object[]) source를 그대로 순회하고 Record 분기는 Field.get(source)를 호출하므로, source가 null이면 NullPointerException이 발생합니다.

null은 실제 호출 경로에서 들어옵니다.

  • EgovWebServiceClientImpl(355~360행)은 OUT·INOUT 파라미터를 new Holder<Object>()로 만들어 넘깁니다. 응답 메시지에 해당 요소가 없으면 Holder.value가 null인 채 변환기에 도달합니다.
  • ServiceBridgeImpl(110~117행)은 인바운드 요청에서 IN 파라미터가 빠지면 params.get(name)이 null을 돌려줍니다.
  • Value Object의 필드가 null이면 재귀 호출로 같은 분기에 다시 들어옵니다.

두 호출처 모두 ClassNotFoundException·IllegalAccessException·IllegalArgumentException·InstantiationException·NoSuchFieldException만 잡습니다. NullPointerException은 이 목록에 없어 호출 스택 위로 그대로 전파됩니다. EgovWebServiceClientImpl은 응답 파싱을 가능한 만큼 수행하고 실패분을 FAIL_IN_PARSING_RESPONSE_MESSAGE 결과코드로 돌려주도록 설계되어 있는데, NPE가 그 부분 실패 처리 경로를 건너뜁니다.

새 정책을 도입하는 변경이 아니라 이미 정해진 계약에 구현을 맞추는 변경입니다. 프레임워크 타입 체계는 null을 유효한 값으로 규정합니다. ListType.convertToTypedObject()(114116행), RecordType.convertToTypedObject()(135137행), PrimitiveType의 모든 구현이 source가 null이면 null을 반환합니다. isAssignableValue(null)도 세 타입 모두 true입니다. 같은 메서드의 Primitive 분기(114~116행) 역시 이미 null을 그대로 반환합니다. List·Record 분기만 예외로 남아 있었습니다.

역방향 메서드인 convertToValueObject()에는 같은 가드가 이미 반영되어 있습니다(#269). 두 메서드는 서로 역함수이고 같은 호출처에서 짝으로 쓰입니다. 클라이언트는 요청 생성(289행)과 응답 파싱(360행)에서, 서버 브리지는 요청 파싱(117행)과 응답 생성(195행)에서 각각 호출합니다. 이번 수정으로 양방향의 null 처리가 대칭이 됩니다.

변경 내용

  • convertToTypedObject()의 List 분기와 Record 분기에 source가 null이면 null을 반환하는 가드를 각각 추가했습니다.
  • 가드는 메서드 첫머리가 아니라 분기 안에 두었습니다. 첫머리에 두면 알 수 없는 Type이 들어왔을 때 InstantiationException을 던지는 현재 계약까지 null 반환으로 바뀝니다. 분기별 배치는 그 계약을 그대로 보존합니다. convertToValueObject()에 이미 있는 가드와 형태도 같습니다.
  • 테스트 3건을 추가했습니다. List 타입에 null을 넘기는 경우, Record 타입에 null을 넘기는 경우, List 필드가 null인 Value Object를 변환해 나머지 필드는 정상 변환되고 해당 키는 null로 남는지 확인하는 경우입니다.

테스트 방법

mvn -B -pl Integration/org.egovframe.rte.itl.webservice test

org.egovframe.rte.itl.webservice 모듈 테스트 19건이 모두 통과합니다(신규 3건 포함, 실패·오류 0건).

회귀 여부는 본문 수정만 되돌리고 테스트만 적용해 확인했습니다. 이 상태에서 신규 3건이 모두 NullPointerException으로 실패합니다.

  • testConvertToTypedObjectWithNullListSourceCannot read the array length because "components" is null
  • testConvertToTypedObjectWithNullRecordSourceCannot invoke "Object.getClass()"
  • testConvertToTypedObjectWithNullListField — 재귀 호출로 List 분기에서 같은 지점이 실패

영향 범위

  • 수정 파일은 MessageConverterImpl.java(본문 10줄)와 같은 모듈의 테스트 MessageConverterImplTest.java(33줄) 두 개입니다.
  • 기존에 NullPointerException으로 끝나던 입력만 null 반환으로 바뀝니다. null이 아닌 입력의 동작은 그대로입니다.
  • 공개 API 시그니처, 설정 파일, 의존성 변경은 없습니다.

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.

1 participant