[DoIP 마스터 가이드 4편] 혈압 오르는 NRC 완벽 분석! 실전 트러블슈팅 가이드 (0x13, 0x22, 0x31, 0x78 등)

tags: DoIP, UDS, NRC, Negative Response, 트러블슈팅, Response Pending, 자동차 통신, DevBJ

안녕하세요, DevBJ입니다! 😎

[👉 1편 ~ 3편]을 거치면서 차량에 이더넷을 꽂고, ECU를 찾아서 UDS 진단 메시지를 쏘고, 세션까지 우아하게 유지하는 법을 배웠습니다.

"아!!! 이제 기능 구현만 쫙쫙 뽑아내면 되겠구나!" 싶으시죠?

📌 [DoIP 마스터 가이드 시리즈 목차]

하지만 현실은 냉혹합니다.
우리의 친절한(?) ECU는 요청을 순순히 받아주지 않고 자꾸 에러 코드, 즉 NRC(Negative Response Code)를 뱉어냅니다. 로그창에 7F xx xx가 빨간색으로 도배될 때의 그 막막함... 😭

오늘은 여러분의 퇴근 시간을 앞당겨 드릴 DoIP 실전 트러블슈팅 가이드입니다. 가장 자주 등장하는 단골 NRC들의 진짜 의미와, 엉뚱한 곳(타임아웃이나 TCP)을 삽질하지 않고 빠르게 원인을 찾아내는 방법을 정리해 드립니다.


0. 시작하기 전에: DoIP NACK vs UDS NRC 구분하기!

진짜 너무너무 중요해서 별표 5개(★★★★★) 치고 시작하겠습니다.
에러가 났을 때 이게 '길이 안 뚫려서(Transport) 난 에러'인지, 'UDS 논리가 안 맞아서(Application) 난 에러'인지부터 쪼개야 합니다.

  • DoIP NACK (Negative Acknowledge): TCP가 깨졌거나, 패킷 길이(Payload Length) 계산을 잘못했거나, Routing Activation을 안 했거나! 즉, UDS까지 가보지도 못하고 DoIP 문지기한테 뺨 맞은 상황입니다. (통신 로직, 파서 검토 필요)
  • UDS NRC (7F xx xx): 통신은 완벽했습니다! 차 안의 ECU가 메시지를 잘 받았고 뜯어보기까지 했는데, *"어? 이거 권한이 없네?", "값이 범위를 벗어났네?"* 하고 애플리케이션 단에서 거절한 상황입니다. (진단 시나리오, ECU 상태 검토 필요)

이걸 뭉뚱그려서 "아 진단 통신 실패했어 ㅠ" 라고 해버리면 밤새워도 버그 못 찾습니다.
이제 본격적으로 UDS NRC 단골손님들을 파헤쳐 봅시다!


1. NRC 0x13 / 0x14 : "길이가 왜 이래?" (포맷 오류)

🚨 7F xx 13 (Incorrect Message Length)

"네가 보낸 요청 메시지 길이가 내가 기대한 거랑 달라!"

  • 삽질 패턴: 이걸 DoIP 헤더의 TCP 길이 오류로 착각하기 쉽습니다. 하지만 0x13은 ECU가 뱉은 겁니다. 0x22(ReadData)를 쏠 때 DID 바이트 수가 모자라거나, 0x31(RoutineControl)을 쏠 때 옵션 레코드를 빼먹는 등 UDS 서비스 규격에 맞지 않는 길이를 보냈을 때 발생합니다. UDS 페이로드(SA/TA 뒷부분) 구조를 다시 확인하세요!

🚨 7F xx 14 (Response Too Long)

"요청은 잘 받았는데, 대답해주려니까 내용이 너무 커서 내 입(버퍼)에 다 안 들어가!"

  • 해결책: 테스터가 욕심을 부려서 DID를 한 번의 요청에 너무 많이 묶어 보냈거나(예: 한 번에 DID 10개 요청), 리턴되는 결과(DTC 등)가 ECU의 전송 버퍼 한계를 초과했을 때 발생합니다. 한 번의 요청을 여러 개로 쪼개서(Split) 보내보세요!

2. NRC 0x22 / 0x31 : "지금은 곤란하다. 조금만 기다려 달라?" (조건 및 범위)

🚨 7F xx 22 (Conditions Not Correct)

"네 요청이 뭔지는 알겠는데, 지금 내 상태(전원/모드/세션)에서는 그걸 해줄 수가 없어."

  • 핵심: 타임아웃이나 통신 불량이 아닙니다! ECU는 멀쩡한데, 현재 차의 시동이 안 켜져 있거나(IGN OFF), 차량이 달리고 있거나, 혹은 Extended Session으로 전환을 안 해서 발생하는 '사전 조건(Pre-condition) 미달' 에러입니다. 테스터의 연결 상태 말고 ECU의 상태(모드)를 점검하세요.

🚨 7F xx 31 (Request Out Of Range)

"요청 형식은 맞는데, 네가 달라는 그 DID(혹은 Routine ID)는 내 지원 목록에 없어!"

  • 핵심: 0x13과 헷갈리지 마세요. 0x13은 폼(형식)이 틀린 거고, 0x31은 폼은 맞췄는데 알맹이(값)가 허용 범위를 벗어난 겁니다. ECU 제조사 문서를 열어서 해당 ID를 진짜 지원하는지 확인해야 합니다.

3. NRC 0x78 : "나 지금 바쁘니까 끊지 말고 기다려!!" (제발 타임아웃 주지 마세요)

🚨 7F xx 78 (Response Pending)

제가 이 글을 쓰는 가장 큰 이유 중 하나입니다. 여러분, 0x78은 에러가 아닙니다!!!

  • 상황: 펌웨어 플래싱, 내부 메모리 지우기 등 빡센 작업을 시키면 ECU가 *"야, 나 지금 열일 중이니까 연결 끊지 말고 기다려! 끝나면 진짜 응답 줄게!"* 라며 핑퐁을 날리는 겁니다.
  • 삽질 패턴: 이걸 에러로 인식해서 냅다 retry를 날리거나 socket close를 해버립니다. (ECU는 혼자 열심히 펌웨어 지우고 있는데... 대참사 발생)
  • 해결책: 0x78을 받으면 타임아웃(P2* 타이머)을 넉넉하게 연장해 주고, 진짜 긍정 응답(Positive Response)이나 최종 NRC가 올 때까지 조용히 기다려줘야 합니다. 이때 눈치 없이 Tester Present를 같이 막 쏘면 상태가 꼬일 수 있으니 타이머 분리 설계를 잊지 마세요!

4. NRC 0x21 / 0x24 / 0x35 : 꼬여버린 타이밍과 시퀀스

🚨 7F xx 21 (Busy Repeat Request)

  • 0x78이 "기다려!"라면, 0x21"나 지금 다른 일 하느라 바쁘니까 나중에 다시 와!" 입니다. 타임아웃으로 치부하지 말고, 짧게 delay를 준 다음 다시 재시도(Retry)하는 로직을 태워주세요. 단, 0.001초 간격으로 무한 재시도하면 ECU가 죽어버리니 적절한 백오프(Backoff)가 필요합니다.

🚨 7F 27 24 (Request Sequence Error) / 7F 27 35 (Invalid Key)

  • Security Access(0x27) 할 때 진짜 징글징글하게 보는 에러죠. 0x35(키 틀림)가 나오면 보통 개발자들은 "아 암호화 알고리즘 짰던 거 어디 버그 있나?" 하고 산수부터 다시 합니다.
  • 하지만 현실은! 계산 로직은 맞는데 문맥(시퀀스)이 꼬인 경우가 훨씬 많습니다.
    1. Seed를 요청해 놓고, 너무 늦게 Key를 줘서 세션이 초기화됐거나
    2. TCP가 중간에 끊어져서 이전 Seed를 들고 인증하려 했거나
    3. Seed를 받고 Key를 보내기 전에 다른 UDS 요청(Tester Present 등)이 끼어들어서 ECU가 인증 흐름을 리셋(0x24)해버린 경우입니다.
  • 알고리즘을 의심하기 전에, Seed 발급부터 Key 전송까지 하나의 방해 없는 깨끗한 흐름(Transaction)으로 이어졌는지 로그부터 확인하세요!

📝 4편 마무리: 에러는 나의 적이 아니라 힌트다

자, 어떠신가요? 빨간색으로 도배되던 7F 에러들이 이제 조금 귀엽게(?) 보이지 않으시나요?
NRC는 ECU가 여러분을 괴롭히려고 뱉는 게 아닙니다. "내 상태가 지금 이러이러하니, 테스터야 네가 이렇게 좀 맞춰줘" 라고 아주 친절하게 디버깅 힌트를 던져주는 겁니다.

오늘 배운 NRC의 진짜 의미를 머릿속에 담아두고 Wireshark와 로그를 쳐다보면, 며칠씩 걸리던 원인 파악을 10분 만에 끝내는 마법을 경험하실 수 있을 겁니다.

이제 DoIP의 기본과 트러블슈팅까지 모두 정복하셨습니다!
다음 마지막 5편에서는 일반적인 진단 통신을 넘어, 초대용량 펌웨어를 쏠 때 발생하는 이슈(Firmware Download)와, 복잡한 다중 ECU 환경에서의 게이트웨이 라우팅(Functional Address) 구조 등 고급 실무 스킬을 싹 다 풀어버리겠습니다.

👉 [다음 글 보기: DoIP 마스터 가이드 5편 - 펌웨어 전송과 게이트웨이 다중 라우팅 최적화 (完)]

오늘도 칼퇴를 기원합니다. DevBJ였습니다!