스택 트레이스르 정확히 읽을 줄 알아야 빠른 디버깅이 가능하고, 이는 에러 해결하는 시간을 줄여주는데 직결하기 때문에 알아두는게 좋을 것 같다.
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.mylibrary.ap.xul.builder.handler.MethodCallback.invokeCallback(MethodCallback.java:32)
at com.mylibrary.ap.xul.builder.handler.ViewHandler.invokeActionResult(ViewHandler.java:581)
at com.mylibrary.ap.xul.context.impl.ViewContextImpl$2.onActionResult(ViewContextImpl.java:283)
at com.mylibrary.ap.xul.context.impl.ActionContextImpl.setResult(ActionContextImpl.java:90)
at com.mylibrary.ap.xul.action.stock.DialogAction.handleDialogClose(DialogAction.java:156)
at com.mylibrary.ap.xul.action.stock.DialogAction$1.windowClosed(DialogAction.java:142)
at com.mylibrary.api.Window.fireWindowClosedEvent(Unknown Source)
at com.mylibrary.api.Window.onClose(Unknown Source)
at com.mylibrary.api.platform.WindowBase.BaseClose(Native Method)
at com.mylibrary.api.platform.WindowBase.BaseClose(Unknown Source)
at com.mylibrary.api.Window.close(Unknown Source)
at com.mylibrary.ap.xul.ui.MessageDialog.handleButtonClick(MessageDialog.java:145)
at com.mylibrary.ap.xul.ui.MessageDialog$1.handleClick(MessageDialog.java:134)
at com.mylibrary.api.ClickableSupport.fireClickEvent(Unknown Source)
at com.mylibrary.api.ClickableSupport.handleAction(Unknown Source)
at com.mylibrary.api.Button.actionHook(Unknown Source)
at com.mylibrary.api.Component.action(Unknown Source)
Caused by: java.lang.NullPointerException
at com.mycompany.service.impl.PortalManagerImpl.deleteMenuItem(PortalManagerImpl.java:603)
at com.mycompany.service.impl.PortalManagerImpl.deletePortal(PortalManagerImpl.java:358)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:66)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at $Proxy54.deletePortal(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.mycompany.util.SpringSecurityContextInvocationHandler.invoke(SpringSecurityContextInvocationHandler.java:62)
at $Proxy84.deletePortal(Unknown Source)
at com.mycompany.ui.binding.PortalDataProvider.doDelete(PortalDataProvider.java:81)
at com.mycompany.ui.binding.PortalDataProvider.doDelete(PortalDataProvider.java:12)
at com.mycompany.ui.binding.AbstractEISDataProvider.delete(AbstractEISDataProvider.java:105)
at com.mylibrary.ap.xul.binding.dataset.impl.DatasetImpl.doCommit(DatasetImpl.java:90)
at com.mylibrary.ap.xul.binding.dataset.impl.AbstractDataset.commit(AbstractDataset.java:251)
at com.mylibrary.ap.xul.binding.dataset.impl.AbstractDataset.deleteRow(AbstractDataset.java:201)
at com.mylibrary.ap.xul.action.dataset.DeleteDataRowAction.execute(DeleteDataRowAction.java:22)
at com.mylibrary.ap.xul.context.impl.ViewContextImpl.execute(ViewContextImpl.java:294)
at com.mycompany.ui.portal.PortalInfoHandler.onPostConfirmDeleteAction(PortalInfoHandler.java:192)
... 21 more
위와 같은 에러가 있는 경우 무엇부터 잡아야할까??
나였다면 아마 제일 위의 에러코드 한 줄을 복사해서 바로 구글링을 했을 것이다.
하지만, 문제의 진정한 원인은 윗쪽의 Trace가 아닌 중간 쪽의 Caused By
로 표시되는 널 포인터 에러다.
Caused by: java.lang.NullPointerException
at com.mycompany.service.impl.PortalManagerImpl.deleteMenuItem(PortalManagerImpl.java:603)
at com.mycompany.service.impl.PortalManagerImpl.deletePortal(PortalManagerImpl.java:358)
그럼 위의 에러가 가리키는 세 줄은 정확히 무슨 뜻 일까?
우선 at으로 시작한 가장 상단의 메세지가 결론적으로 에러가 발생한 부분이다
- nullPointerException 발생
com.mycompany.service.impl.PortalManagerImpl
클래스의 deletePortal 메소드 358번 라인에서 deleteMenuItem 메소드를 호출했는데 해당 메소드 603번 째 줄에서 널 포인터 예외가 발생했다.
-
PortalManagerImpl 소스
if (item == null) {
throw new NullArgumentException("item");
}
//중간 생략
List<PortalMenu> children = getMenuItems(item.getPortal().getId(), item.getId()); // 603번째 줄
for (PortalMenu child : children) {
deleteMenuItem(child);
}
NullPointerException이 발생할 수 있는 부분
- children
- item
- item.getPortal()
- item.getPortal().getId()
- item.getId()
이렇게 5번이 있다.
하지만 NullPointerException 에러만 보고 바로 2, 3 번으로 경우를 좁힐 수 있어야 한다.
NullPointerException 는 단순히 변수에 널값이 들어가서 생기는 오류가 아니다.
명확하게 객체의 널 레퍼런스에 대해 메소드 호출이나 필드 참조 등의 작업을 했을 때 발생하는 문제다.
만일 4, 5번이 Null 이라면 널 레퍼런스 호출이 아닌 널값을 'getMenuItems' 메소드의 인자 값으로 넘기기 때문에 널포인터 예외의 원인이 될 수 없다.
물론 getMenuItems 메소드 안에서 널 값으로 인한 예외가 날 수 있지만, 그 경우에는 에러 트레이스가 다른 곳에서 발생했을 것이다.
다시 2, 3번으로 돌아오면 item 이거나, item.getPortal() 인 경우인데 item은 위에서 null 체크를 하기 때문에 정답은 3번이 된다.
스택 트레이스를 읽을 수 있음으로써 의미없는 검색, 질문시간을 줄일 수 있어
생산성의 차이
는 엄청나게 커질 것이다.
초보 개발자로써 꼭 알아둬야 한다고 생각한다.
참고
'나만의 정리' 카테고리의 다른 글
Maven VS Gradle (0) | 2020.06.02 |
---|---|
컴파일과 런타임 (0) | 2019.07.14 |
[웹] White Domain, RBL, SPF, DKIM (0) | 2019.07.14 |
자바 제네릭 완전정복 (2) | 2019.03.21 |
빌더 패턴(Builder Pattern) (0) | 2019.03.21 |
댓글