Illegal character ((CTRL-CHAR, code 31)) 오류 해결 방법

Illegal character ((CTRL-CHAR, code 31)) 오류를 겪어서 그 해결 방법에 대해 기술해본다.

현재 내가 일하는 곳의 환경은 웹서버 쪽에서 글로벌 API서버를 호출하여 그 응답을 어플리케이션의 DTO에 매핑하는 구조이다.
(아마도 요즘은 대부분의 엔터프라이즈 환경은 MSA로 구축된 외부 API를 호출하도록 작업이 되어 있을 것이다.)

발생상황

외부 API호출 후 응답이 정상처리인 2XX가 아닌 5XX나 4XX로 떨어짐. 내 경우 전체 오류 메세지는 다음과 같았다.
org.springframework.web.client.ResourceAccessException: I/O error on POST request …
failed to transform message; nested exception is com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens …

위 메세지는 jackson json라이브러리에서 API서버에서 돌려주는 데이터를 json형태로 파싱을 못 한다는 내용이다.
그러나 직접 POSTMAN에서 해당 API를 찔러보면 json응답이 정상적으로 오는 상황.

너땜에 해맸다고~!


POSTMAN에서 정상응답이 오므로 API쪽의 문제일 거라고는 전혀 생각을 못했는데 돌아돌아 원인을 찾아보니 결국 API쪽 서버의 문제였다.
우선 위 에러메세지를 통해서 응답이 jackson json으로 파싱이 안되므로 응답 데이터가 json wellform이 아니라는 것은 확실했다.
그러나 POSTMAN으로는 정상 응답을 받고 있기 때문에 welfrom이라고 생각하고는 쓸데없이 시간낭비를 좀 했다.

결국 해당 API를 POSTMAN으로 호출하지 않고 CURL을 이용하여 호출해보았더니 json으로 된 정상 텍스트 리턴값이 나오지 않고 다음과 같은 오류 메세지를 통해 바이너리로 돌려주고 있다는 것을 알게 되었다. (POSTMAN에서는 gzip바이너리를 친절하게 자동으로 풀어주므로 json형식으로 응답이 잘 오는 것 처럼 보였던 것이었다!)

Warning: Binary output can mess up your terminal. Use “–output -” to tell
Warning: curl to output it to your terminal anyway, or consider “–output
Warning: ” to save to a file.

  • Failure writing output to destination
  • Closing connection 0
  • schannel: shutting down SSL/TLS connection with uat.api-server.com port 443

이것을 바이너리로 저장해보려면 curl옵션으로 -output(-o) 저장할 파일명을 붙혀주면 된다.
또는 상대쪽 API서버가 지원을 하는 경우 curl옵션으로 -H “Accept-Encoding: identity”를 추가하면 압축하지 않은 텍스트 데이터를 돌려준다.

추가적인 시도

-H “Accept-Encoding: identity” 옵션으로 텍스트로 된 json값을 받으려고 시도해보았으나 실패. (서버에서 지원하지 않음)
-o response.gzip 옵션을 추가하여 결과를 파일로 받은 후 압축을 풀어보니 정상적인 json텍스트 파일이 들어 있었다. 오오.. 서버에서 무조건 gzip으로 돌려주는구나하는 사실을 알 수 있었음.

gzip


UAT환경에서만 발생하는 문제였으므로 같은 API를 개발과 운영에 curl로 호출해본 결과 gzip이 아닌 텍스트파일로 돌려주는 것이 확인되었다.

해결방법

우리쪽 어플리케이션에서 해결하는 방법과 상대쪽 어플리케이션에서 해결하는 방법이 있는데 둘 다 설명하겠다.

우리쪽 어플리케이션에서 해결 1)
API호출시 헤더에 “Accept-Encoding: gzip”을 추가하고 그에 맞는 처리를 한다. gzip을 일반 텍스트로 된 json파일로 풀어주도록 해야겠지. (그러나 원래 잘 되던건데 굳이 이렇게 할 필요가 없다. 처음부터 개발할거면 모를까..)

우리쪽 어플리케이션에서 해결 2)
API호출시 헤더에 “Accept-Encoding: identity”을 추가하면 서버에서 무압축 데이터를 보내준다. (단, 이 경우 상대 서버가 지원해야 하고 원래 잘 되던 거라면 이걸 추가하는 것도 부담스럽다)

상대쪽 어플리케이션에서 해결)
Accept-Encoding 파라메터를 보내지 않으면 무조건 무압축(텍스트)으로 보내달라고 요청한다. (우리쪽 소스를 건드릴 필요가 없기 때문에 BEST)
나도 상대쪽 서버에 요청해서 해결했다. (PROD/DEV환경 다 괜찮은데 하필 UAT API 환경에서만 발생하는 문제였기 때문에 요청이 가능했다)

누군가에게 도움이 되길 바란다.

생계형 특급 개발자이자 아들 하나 있는 평범한 아빠. 취미는 요리, 캠핑, 뮤직 페스티발 다니기 등이지만 이 블로그에는 주로 개발관련된 내용만 올릴 예정입니다. 워드프레스를 시작한지 얼마 되지않아 사이트가 허전하지만 좋은 내용으로 채우도록 노력하겠습니다. 자주 놀러오세요 ^^
Posts created 54

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top