본문 바로가기

생활 속 궁금증

Unexpected token '<' 오류, 쉽게 말하면 '주문서 자리에 안내문이 온 것'입니다

반응형

쇼핑몰에서 결제를 누르거나, 앱에서 목록을 새로고침했는데 갑자기 이런 영어가 뜬 적 있으실 겁니다.

Unexpected token '<', "<!DOCTYPE "... is not valid JSON

무슨 말인지 하나도 모르겠고, 화면은 멈춰 있고, 다시 눌러도 똑같습니다. 그런데 이 메시지는 사실 꽤 친절한 편입니다. 무슨 일이 벌어졌는지, 심지어 무엇이 잘못 왔는지까지 알려주고 있거든요.

오늘은 이 한 줄을 뜯어보겠습니다.


프로그램은 '데이터'를 기다렸는데, '웹페이지'가 왔습니다

앱이나 웹사이트가 화면에 목록을 그릴 때, 뒤에서는 이런 대화가 오갑니다.

앱: 서버야, 주문 목록 데이터 좀 줘.
서버: 여기 있어. (데이터 전송)
앱: 받았다. 화면에 그려야지.

이때 오가는 '데이터'는 JSON 이라는 형식입니다. 사람이 읽으라고 만든 게 아니라 프로그램끼리 주고받으라고 만든 형식이라, 괄호와 따옴표만 잔뜩 있는 모양입니다.

문제는 서버가 가끔 엉뚱한 걸 보낸다는 겁니다.

앱: 서버야, 주문 목록 데이터 좀 줘.
서버: 여기 있어. (사람이 볼 웹페이지 전송)
앱: ...이게 뭐야?

식당에 주문서를 넣었더니 주문서 대신 '오늘 휴무입니다' 안내문이 돌아온 것과 같습니다. 안내문 자체는 멀쩡한 종이지만, 주문서를 처리하는 기계는 그걸 읽을 줄 모릅니다.

그래서 프로그램은 첫 글자를 보자마자 포기합니다. 웹페이지는 항상 `<` 로 시작하는데, JSON 데이터는 절대 `<` 로 시작하지 않거든요. Unexpected token '<' 는 바로 그 뜻입니다. "예상 못 한 글자 `<` 를 만났다".


따옴표 안에 든 열 글자가 증거입니다

여기가 재미있는 부분입니다. 메시지 중간의 `"<!DOCTYPE "` 이 부분, 그냥 장식이 아닙니다.

서버가 실제로 보낸 내용의 맨 앞 열 글자를 그대로 복사해 온 것입니다.

`<!DOCTYPE` 은 웹페이지 파일이 시작할 때 맨 위에 붙는 표시입니다. HTML 로 만든 문서의 첫 줄에 거의 항상 들어갑니다. 즉 이 열 글자가 보인다는 건 "데이터가 아니라 웹페이지가 왔다" 는 물증인 셈입니다.

직접 확인해 봤더니 규칙도 정해져 있었습니다.

  • 서버가 보낸 내용이 20글자 이하면 통째로 따옴표 안에 보여줍니다
  • 21글자부터는 앞 열 글자만 남기고 뒤에 `...` 을 붙입니다

그래서 `...` 이 붙어 있으면 "내용이 더 있는데 생략했다"는 뜻이고, `...` 없이 끝나면 따옴표 안에 든 게 전부입니다.

앞 열 글자별로 읽는 법

따옴표 안 실제로 온 것
\`<!DOCTYPE \` 보통의 웹페이지 (오류 안내 페이지, 로그인 페이지)
\`<script sr\` 역시 웹페이지 (보안 장비나 중계 서버가 대신 준 화면)
\`Internal S\` "Internal Server Error" — 서버가 고장 났다는 짧은 문장
\`undefined\` 서버 문제가 아니라 앱 자체의 실수

왜 이런 일이 생기나 — 흔한 다섯 가지

1. 주소가 살짝 틀렸다

사람으로 치면 동·호수를 잘못 적은 것입니다. 없는 주소로 요청이 가면 서버는 "그런 페이지 없습니다" 안내 페이지를 보냅니다. 그게 바로 그 웹페이지입니다.

2. 있던 페이지가 없어졌다

서비스가 개편되면서 주소가 바뀌었는데 앱이 예전 주소를 그대로 부르는 경우입니다. 앱 업데이트를 안 하고 오래 쓰셨다면 이쪽일 수 있습니다.

3. 로그인이 풀렸다 (가장 흔합니다)

일반 사용자가 겪는 경우는 대부분 이겁니다.

로그인 상태가 만료되면 서버는 데이터를 주는 대신 "로그인해 주세요" 페이지로 안내합니다. 사람이 브라우저로 들어갔다면 로그인 화면이 뜨겠지만, 앱은 그 로그인 화면의 코드를 데이터인 줄 알고 받아서 읽으려다 넘어지는 겁니다.

화면에는 로그인하라는 말이 한마디도 안 나오고 영어 오류만 뜨니까 더 당황스럽죠.

4. 개발 중인 설정이 그대로 나갔다

만드는 사람 쪽 사정입니다. 개발할 때 쓰던 임시 설정이 정식 서비스에 빠져서, 데이터 요청이 엉뚱하게 웹페이지 쪽으로 가는 경우입니다.

5. 서버가 진짜로 고장 났다

서버에서 오류가 나면 "죄송합니다, 오류가 발생했습니다" 페이지를 그려서 보냅니다. 이것도 웹페이지라 똑같은 증상이 됩니다. 이 경우는 기다리는 것 말고 할 수 있는 게 없습니다.


그래서 저는 뭘 하면 되나요

순서대로 해 보시면 대부분 해결됩니다.

  1. 새로고침 한 번. 일시적인 문제면 여기서 끝납니다.
  2. 로그아웃했다가 다시 로그인. 세 번째 원인이 가장 흔하니, 이게 실제로 제일 잘 듣습니다.
  3. 앱을 완전히 종료했다가 다시 켜기. 앱이 기억하고 있던 옛날 로그인 정보를 버리게 만드는 방법입니다.
  4. 앱 업데이트 확인. 두 번째 원인이라면 업데이트가 답입니다.
  5. 여기까지 해도 같다면 서버 쪽 문제입니다. 잠시 뒤에 다시 해 보세요.

로그아웃 후 재로그인이 유독 잘 듣는 이유가 이제 설명이 되시죠. "껐다 켜면 된다"가 아무 근거 없는 민간요법이 아니라, 만료된 로그인 정보를 버리고 새로 받아오게 만드는 확실한 조치이기 때문입니다.


왜 이렇게 불친절하게 영어로 나오나

이 메시지는 원래 사용자에게 보여주려고 만든 게 아닙니다. 프로그램을 만드는 사람이 개발 중에 보라고 찍히는 기록입니다.

원래대로라면 앱이 이 오류를 중간에 가로채서 "일시적인 오류입니다. 다시 시도해 주세요" 같은 한국어 안내로 바꿔 보여줘야 합니다. 그 처리가 빠져 있으면 속에 있던 영어가 그대로 화면에 튀어나오는 것이고요.

그러니 이 메시지를 보셨다면, 잘못은 사용자 쪽에 없습니다. 만드는 쪽에서 덜 다듬은 부분이 드러난 것뿐입니다.


정리

  • 이 오류는 JSON 이 잘못된 게 아니라, 애초에 JSON 이 오지 않은 것입니다
  • 따옴표 안의 열 글자는 서버가 실제로 보낸 내용의 앞부분입니다
  • 일반 사용자라면 원인은 대개 로그인 만료 → 재로그인이 답입니다
  • 영어로 튀어나온 건 만드는 쪽에서 안내 문구로 바꿔주지 않아서입니다

더 자세한 정리 — 제 홈페이지 글

개발자분들을 위한 원인별 확인 순서와 코드는 제 홈페이지 코딩나우(coding-now.com)에 정리해 두었습니다. 회원가입 없이 바로 읽을 수 있습니다.

글 제목: Unexpected token '<', "<!DOCTYPE "... 오류 원인과 해결

이동할 주소: https://www.coding-now.com/guides/unexpected-token-doctype-json

반응형