fender
18k
2016-07-18 20:46:55 작성 2018-11-27 07:39:53 수정됨
41
34299

초보 개발자를 위한 스택트레이스 읽는 법


몇 년 전에 네이버 카페에 썼던 글인데, 답변을 달려다 보니 링크가 안되서 이 곳에 옮겨 적습니다.
초보 개발자분들이 제대로 된 디버그 방법을 배우지 못해 오류가 나면 무턱대고 검색부터 하거나 메시지를 통째로 복사해서 질문글만 올리고 어찌할 바를 모르는 모습을 보면 안타까운 생각이 들었는데, 조금이라도 그런 잘못된 습관을 고치는 데 도움이 되었으면 하는 생각입니다.


면접을 볼 때, 질문할 내용을 미리 정해놓지는 않지만 개인적으로 즐겨내는 문제가 있습니다. 널포인터 예외(NullPointerException)의 원인을 찾는 문제인데, 전에 다니던 회사에서는 실제 제품개발 중에 발생한 오류 보고서가 있어서 면접보시는 분들께 해당되는 소스 파일과 함께 제시하고 원인을 찾아보도록 한 적이 있습니다.

처음보는 다른 사람이 짠 소스를 어떻게 분석할 수 있을까 의아할 수 있지만, 실제로 널포인터 예외는 개발중에 매우 흔히 경험하는 오류이고 특성상 정확한 의미만 알고 있다면 거의 대부분 즉시 정확한 원인을 분석할 수 있습니다. 또한 그러기 위해서는 스택트레이스를 읽을 수 있는 능력이 필요합니다.

개인적으로 예외 처리는 초보 개발자가 가장 중요성을 간과하기 쉬운 분야라고 생각합니다. 예외처리라고 하면 단순하게 '그냥 try-catch하면 되는 게 아닌가?'라고 쉽게 생각할 수도 있지만 사실 이야기를 하자면 상당한 설계 차원의 지식이 필요한 문제가 예외처리입니다.

초보 개발자 분들이라면 그렇게 깊게까지 파고들 필요는 없겠지만 최소한 스택트레이스는 정확히 읽을 줄 알아야만 빠른 디버깅이 가능한 만큼 이 부분에 대해서만은 시간을 투자해서 정확한 지식을 쌓아둘 필요가 있다고 생각합니다.

이번 면접에서 제가 제시한 스택 트레이스는 이렇습니다 :

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

상당히 길죠? 제가 본 많은 수의 초보 개발자분들은 이런 로그를 접하면 지례 겁을 먹고 정확하게 트레이스를 읽으려 들지 않습니다.

사실 예외를 읽는 법은 기초 중에서도 가장 중요한 내용이고, 처음 자바 언어를 배우는 모든 개발자가 반드시 숙지해야할 내용인데도 오히려 제대로 아는 초급 개발자를 만나기 힘든 것을 보면 도대체 자바 학원이라는 곳에선 무엇을 가르치는 건가 의아한 생각이 듭니다.

오류가 발생하면 무조건 오류 메시지부터 완벽하게 이해해야합니다. 왜 책에서 본대로 쳤는데 안되나, 왜 어제까지 됐는데 갑자기 안될까, 아니면 인터넷에서 한 번 찾아볼까 등의 생각은 일단 오류부터 제대로 읽어보고 해도 늦지 않습니다.

예외처리를 제대로 작성한 코드라면 거의 대부분의 문제는 스택트레이스 안에 답이 있기 때문입니다.

우선 위의 예의 경우 트레이스가 두 가지 예외 내용을 포함하고 있다는 것을 바로 파악할 수 있어야 하며, 문제의 진정한 원인은 윗쪽의 트레이스가 아니라 '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)

이 부분을 보여주고 읽는 방법을 알고 있는지 묻는 질문인데, 이를 정확하게 'com.mycompany.service.impl.PortalManagerImpl' 클래스의 'deletePortal' 메소드 358라인에서 같은 클래스의 'deleteMenuItem'메소드를 호출했는데 해당 메소드 603번 째 줄에서 널포인터 예외가 발생했다'라고 해석할 수 있는 지원자가 많지 않았습니다.

아예 대답을 못하거나 트레이스를 위에서 아래로 해석하려 하거나 심지어 이를 '두 번의 널포인터 예외가 발생했다'로 해석하는 경우까지 있더군요. 이런 로그를 보고 곧바로 해당 클래스의 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);
 }

신기하게도 아직까지 면접을 본 개발자 중에 이런 문제를 보고 곧바로 널포인터 예외의 원인을 짚어내는 지원자는 거의 없었습니다. 글을 읽으시는 분들은 모두 금방 답이 보이시나요?

많은 수의 지원자들이 'children'이나 'item.getId()' 등에 널값이 들어간 것 같다고 답했습니다. 이론적으로 해당 라인에서 널값이 들어갈 수 있는 모든 경우의 수는,

  1. children
  2. item
  3. item.getPortal()
  4. item.getPortal().getId()
  5. item.getId()

이렇게 다섯 가지가 전부입니다.

이 중 적어도 두 가지, 즉 2번 혹은 3번으로 가능성을 바로 좁히지 못한다면 그것은 널포인터 예외의 의미를 정확하게 파악하지 못하고 있기 때문입니다.

널포인터 예외는 단순하게 변수에 널값이 들어가서 생기는 오류가 아닙니다. 널포인터 예외는 명확하게 객체의 널레퍼런스에 대해 메소드 호출이나 필드 참조 등의 작업을 했을 때 발생하는 문제라는 것을 이해한다면 이런 문제는 곧바로 원인을 좁힐 수 있어야 합니다.

즉, 1번의 경우처럼 단순히 변수에 널값을 할당하는 것만으로는 절대로 널포인터 예외가 날 수 없습니다. 그리고 만일 4 번 'item.getPortal().getId()'이나 5번 'item.getId()'이 널이라면 이는 널 레퍼런스에 대한 호출이 아니라 널값을 'getMenuItems'라는 메소드의 인자로 넘기는 것 뿐이기 때문에 역시 널포인터 예외의 원인이 될 수 없습니다.

물론 'getMenuItem' 메소드 안에서 해당 인자에 대한 널체크 없이 값을 사용하다가 예외가 날 수도 있겠지만 이 경우엔 절대로 트레이스 상에 굵은 글자로 표시된 603번 째에서 예외를 뿌리지 않습니다.

그렇다면 남은 가능성은 2번 'item'이 널이거나 3번 'item.getPortal()'이 널인 경우뿐인데, 'item' 변수는 위에서 널체크를 하기 때문에 603번 째 줄에서 절대로 널값을 가질 수 없습니다. 그렇기 때문에 답은 3번이 되는 것입니다.

너무 문제가 어렵나요? 길게 설명했지만 개발을 하면 매우 흔하게 접할 수 있는 예외이며 따져보면 결코 어려운 내용이 아닙니다. 위와 같은 문제는 이미 스택트레이스를 능숙하게 읽을 수 있고 널포인터 예외를 이해하고 있는 개발자라면 몇 초 안에 곧바로 원인을 파악할 수 있는 문제입니다.

예외만 읽을 줄 알면 단 몇 초만에 깨달을 수 있는 문제를 다른 개발자는 인터넷 검색과 의미없는 무작위 수정, 게시판 질문글 작성등으로 몇 시간씩 허비한다면 두 개발자 사이의 생산성의 차이는 엄청나게 벌어질 것입니다.

디버깅 같은 기본적 부분에서 나쁜 습관을 빨리 고치지 못한다면 시간 낭비도 문제지만 그 만큼 개발자로서 발전도 늦게되고, 나중에는 인터넷이 없으면 아무 것도 못하는 그냥 복사 붙여넣기만 단순 코더가 한계점이 될 수도 있습니다.

그런 점을 감안해서 이 글을 보는 초보 개발자들은 반드시 제대로된 문제 해결 방법을 공부하셨으면 좋겠습니다.

75
91
  • 댓글 41

  • fender
    18k
    2016-07-18 20:48:08
  • 손이시렵다
    1k
    2016-07-19 02:29:29

    처음 개발을 시작하면서 이 글을 읽었다면 참 좋았을거같단 생각이 듭니다

  • I'm done
    2016-07-19 09:14:59

    좋은 정보 감사드립니다!

  • toxin
    395
    2016-07-19 10:01:36

    감사합니다.

  • 민단진
    521
    2016-07-19 15:51:07

    좋은글 감사합니다. 

    저도 처음 에러로그를 보면서 멘붕와서 아무것도 못하던게 어제같은데,

    사실 지금도 보는게 꺼려지지만, 그래도 꾹꾹 참고 보고있습니다.

    스택트레이스라고 하는군요. 많은 것을 배우고 갑니다.

  • sung
    8
    2016-07-19 16:29:27
    좋은 글 감사합니다.
  • 구너종신
    22
    2016-07-19 21:27:20

    좋은 글 감사합니다!!

    블로그에 게시를 해도 될까요?

    물론 출처 남기겠습니다

  • fender
    18k
    2016-07-20 06:04:59

    네 출처만 명시하시면 어디에 퍼가셔도 관계없습니다.

  • xdrcf
    406
    2016-07-20 13:47:29

    초보개발자인 저로써는 한번쯤 생각만 하고 넘어가던 문제였는데 

    이렇게 보는눈을 넓혀주시니 그저감사할따름입니다!

  • 흐어엉
    1k
    2016-07-20 13:58:38

    신입여러분들...

    이렇게 친절히 설명해주시는 사수분들 만나시면 행운입니다~

  • extreme
    835
    2016-07-20 14:08:58

    내용이 좋네요 :) 근데 2번의 경우 NullArgumentException로 이미 걸렀기 때문에 발생할 수 없는 케이스 아닌가요?

  • S.U.N
    131
    2016-07-20 17:26:32

    좋은 글 감사합니다^^

  • fender
    18k
    2016-07-20 17:27:44

    extreme // 네, 그래서 아래에 "'item' 변수는 위에서 널체크를 하기 때문에 603번 째 줄에서 절대로 널값을 가질 수 없습니다"라고 되어 있습니다 ^^

  • extreme
    835
    2016-07-21 00:46:11

    크흐 이렇게 띄엄띄엄 읽어서 원 ㅠ 암튼 스택트레이스를 읽지 못하는 사람이 태반이라는 얘기에 한번 놀라고, 좋은 문제에 두 번 놀라고 갑니다.

  • 후추
    352
    2016-07-21 09:11:49

    감사합니다 둘다 널포인트 발생하는 것으로 이해하고있었네요 ~~

  • Courage
    2k
    2016-07-21 10:27:23

    이런것을 알려주는 사수가 있었다면 참 좋았을텐데...

    스택트레이스 읽는 방법을 무수히 많은 오류를 접하고 밤새 디버깅하면서 익혔죠...

    얼마나 많은 삽질을 했는지....

  • pannet15
    2k
    2016-07-21 11:18:41

    좋은글 감사합니다.

    좀 더 발전할수 있는 글이었습니다.

  • 라면보이심슨
    15
    2016-07-21 14:55:03

    정말 멋진 글입니다. 개발을 하다 보면 저런 정보가 제공이 되지만 막연한 감이 있고, 내가 짠 부분에서 난 게 아니라면 더더욱 손을 대기가 힘들었는데, 체계적으로 분석하는 방법을 알려주시니 감사합니다^^ 잘 익혀서 더 유능한 개발자가 될 수 있었으면 좋겠습니다 ㅎ

  • 왕철면피
    36
    2016-07-21 16:13:31
    좋네요!! 스택트레이스 뜨면 당황했던 1인이라 큰 도움이 됩니다.
  • artio
    31
    2016-07-22 00:25:30

    제가 아직 1년차 초보라 그런지 모르겠지만 위 스택트레이스는 별로 어려운 문제도 아닌것같은데...제가 이상한가요...?

    짧은 제 경험상 제가 짠 소스에서 문제가 생기는건 한번에 알아볼수 있었거든요

    org.springframe... 이렇게 시작되면... 뭐지... 뭘까... 이러지만

    그게 아닌거라면 아 내가 이런쪽에서 잘못 코딩했구나 바로 나오던데...

  • fender
    18k
    2016-07-22 01:01:55

    artio // 1년차라도 당연히 알아야하는 기본적인 문제인 건 맞습니다. 사실 실무를 다루는 개발자가 코드를 따라 칠 줄만 알고 문제가 생기면 원인을 찾는 방법을 모르는 건 말이 안되니까요.

    근데 그 당연한 걸 아는 초급 개발자를 보기 쉽지 않으니 우리나라 SI 환경이 이상한 것이고, 그런 이상한 환경에서도 저런 문제는 한 눈에 답을 알정도로 실력을 쌓으셨다면, 아마 개발에 대한 남다른 소질이 있다는 이야기겠지요.

    정말 그런 경우라면 꾸준히 경력을 쌓고 공부해나가면 뛰어는 개발자가 될 수 있으실 거라 생각합니다.

  • curtwolf
    219
    2016-07-22 08:31:21

    감사합니다. 잘 읽었습니다~ 

  • 댕구르르르르
    2
    2016-07-23 12:30:43

    잘 읽고 가요 ~ 추천 쾅!

    그래도 스택트레이스 올라오는 문제라면 차라리 고맙다는 생각을 해봅니다.

  • 딧물
    4
    2016-07-25 10:53:36

    잘 읽었습니다 감사합니다

  • kolol
    1k
    2016-07-25 13:21:25

    전 첨에 공부시작할때


    제대로 확인안하고 빌드를 자주 하다보니 에러가 자주 나와서


    항상 스택트레이스를 걸어놓고 하다보니 에러 보는 습관이 들었네요.



  • 알구십다
    18
    2016-08-02 14:35:28

    저는 마지막까지 숨은 의도가 있을 것이라고 생각해서 2번을 골랐는데요. 위의 null 체크 로직이 몇 라인인지 안 나와서 null 체크 후 해당 603라인 사이에 item이 한번 다른 값으로 초기화(null 을 갖는 값으로)될 것을 생각했는데 허탈하네요. 너무 머리를 굴렸나 봅니다.

    좋은 글 감사합니다.

     

  • 뀨와앙
    174
    2016-08-27 03:12:41

    지금 다른 글을 보다가 우연히 읽었는데 초보에게 도움이 되는 좋은 글입니다 감사해요 ㅠ_ㅠ 

    앞으로 자주자주 오류를 읽어보겠습니다 :D 

  • 훈훈
    313
    2016-09-25 01:08:27

    정말좋은 글입니다..


    현재 학원에서 열공하고있는 비전공자인데


    처음에는 막무가내로 수정을 마구마구 하다가


    요근래엔 스택트레이스 따라서 최대한 고쳐보는 편이네요


    이글을 완전히 제것으로 만들때까지 노력해야 겠습니다


  • S.U.N
    131
    2016-10-24 18:21:39
    좋은 글 감사합니다 :)
  • 서나라라
    16
    2017-04-19 10:39:37

    좋은 글 감사합니다!!

  • 달란
    200
    2017-05-09 21:32:05

    정말 잘 배웠습니다.


  • TRIVIUM
    530
    2018-02-10 13:19:06

    와 감사합니다. 에러만 보면 읽어보지도 않고 복사해서 물어보고 했는데 정신이 확 드는 글이였습니다. 지금부터라도 꾸준한 연습을 해야겠습니다.


  • donghaeng
    103
    2018-04-13 23:31:00

    2개월전에 처음봤을 때는.. 이해 안 되는 부분이 있어서 나중에 다시봐야겠다했는데!

    이제는 100% 이해가 되네요. 감사합니다!^^

  • noter
    32
    2018-11-16 18:59:16

    좋은 글 감사합니다.

  • 고그래머
    67
    2018-11-27 00:18:16

    와 아까 열심히 에러 뜬거 구글링하면서 수정해봐도 해결을 못해서 질문올렸는데 그 행동이 부끄러워지는 글입니다ㅠ 이 글을 보고 다시 에러 메시지를 보니 뭐가 문제인지 확 보이네요..

    저도 학원에서 공부를 배우고 있지만.. 제가 짠 소스 쉬운 에러는 감으로 잡지만 어려운건 항상 구글링 하다 잡아도 이게 왜 되는지도 모르고.. 지난 날을 반성하게되네요ㅜ 이제부턴 에러메세지 뜨는거 하나하나 기록하면서 분석해보는 공부를 계속 해야겠어요!

  • madnomad
    70
    2019-08-06 18:39:13 작성 2019-08-06 18:41:38 수정됨

    안녕하세요 

    좋은글 감사합니다.


    질문이 하나 있는데요. 

    스택트레이스에 deleteMenuItem(PortalManagerImpl.java:603)에서 익셉션이 발생했다고 되어있는데요

    List<PortalMenu> children = getMenuItems(item.getPortal().getId(), item.getId()); // 603번째 줄
    
    for (PortalMenu child : children) {
        deleteMenuItem(child);
     }

    해당 소스를 보면 deleteMenuItem()메소드는 606번째줄에 있는데 왜 603번째 줄에서 예외처리가 되는건지 궁금합니다~!

    익셉션 발생하는순간이 children 객체에 담을때가 아닌,  deleteMenuItem()메소드를 실행할 때 인가요? 그렇다면 왜 606번째줄로 찍히지 않는건가요?

  • fender
    18k
    2019-08-06 21:38:21

    roptibug // `deleteMenuItem()`은 재귀 호출을 하는 메서드라서 혼동이 되신 것 같습니다. 스택트레이스의 인용하신 부분은 "603번 째 줄에서 deleteMenuItem()을 호출할 때"가 아니라 "호출된 deleteMenuItem() 메서드의 내부의 603번 째 줄"임을 생각하시면 도움이 될 것 같습니다.

  • madnomad
    70
    2019-08-07 02:00:25

    fender 

    재귀였군요... 이제 이해가 되었습니다.


    답변 감사합니다! 

  • Sruka
    438
    2019-09-11 18:35:30

    C++ 을 주로 쓰다 보니 포인터 쓰다가 에러 스택 읽는게 생활화 되어 버렸습니다...

  • 곰재
    432
    2020-02-04 10:40:05

    감사합니다 블로그에 퍼가겠습니다!! 출처남겼어요!

  • 해탈한곰
    7
    2020-05-08 00:05:09

    이런 친절하고 핵심을 집는 글을 접할 수 있게되어 정말 기쁩니다. 감사합니다, fender님!


    저는 이 글을 읽기 전까지, 제가 오류 트레이스를 읽는 법을 잘 모르고 있다는 것조차 몰랐어요.

    이제껏 오류가 생기면 그냥 오류원인이라고 나온 예외보기 + IDE에서 알려준 코드번호 링크를 타고가서 해당 코드를 보는 것만으로 문제를 유추하면서 주먹구구식으로 풀어 갔었거든요. 그이상 뭘 하는게 좋겠다라든가 지금 내가 하는 행위에 뭐가 부족하다든가에 대한 개념 자체가 없었는데, 덕분에 중요한 걸 깨닫게 됐습니다.


  • 로그인을 하시면 댓글 을 등록할 수 있습니다.