donghakim.dev — zsh
← ls ../blog

붙여넣은 토큰의 개행 때문에 fetch 가 요청을 보내기 전에 실패했다

The string did not match the expected pattern. 이라는 메시지에는 헤더도 토큰도 등장하지 않습니다

backend · cover image

새로 추가한 화면에서 API 호출이 전부 실패하는데, 에러 메시지가 이것뿐이었습니다.

The string did not match the expected pattern.

네트워크 탭에는 요청이 아예 없었습니다. 브라우저가 요청을 서버로 전송하기 전에 예외를 던진 것입니다.

  • Safari(WebKit) 가 이 문구의 SyntaxError 를 던집니다. Chrome 계열은 같은 상황에서 Failed to execute 'fetch' on 'Window': Invalid value 를 던집니다.
  • 메시지 어디에도 헤더나 토큰이라는 말이 없어서 URL 문제로 오인하기 쉽습니다.

원인

fetch(url, { headers: { Authorization: `Bearer ${token}` } })

token 에 헤더 값으로 허용되지 않는 문자(개행 등)가 포함돼 있으면 브라우저가 Headers 를 생성하는 단계에서 예외를 던집니다.

이 문제는 토큰을 개발자도구 콘솔이나 .env 에 손으로 붙여넣는 개발 초기 단계에서 자주 발생합니다.

  • 메신저나 문서에서 복사할 때 함께 포함된 개행과 공백
  • 값에 함께 입력한 따옴표 ("eyJ...")
  • Bearer eyJ... 처럼 접두어까지 통째로 저장한 경우. 이건 예외는 아니고 Bearer Bearer ... 가 되어 401 이 발생합니다.

로그인 화면이 구현되기 전까지 localStorage 와 환경변수로 토큰을 주입하고 있었는데, 거기 붙여넣은 값에 개행이 섞여서 화면의 모든 API 호출이 이 에러로 실패했습니다.


해결

토큰을 조회하는 함수 한 곳에서 앞뒤 공백, 따옴표, Bearer 접두어, 중간 개행·공백을 제거한 뒤 순수 토큰만 헤더에 추가합니다.

export function getToken() {
  const raw = localStorage.getItem(TOKEN_KEY) ?? import.meta.env.VITE_API_TOKEN ?? ''
  return raw
    .trim()
    .replace(/^["']|["']$/g, '')   // 앞뒤 따옴표
    .replace(/^Bearer\s+/i, '')    // 접두어 중복
    .replace(/\s+/g, '')           // 중간 개행·공백
}

localStorage 값이 환경변수보다 우선하는 구조라면, 환경변수로 전환할 때 브라우저에서 localStorage.removeItem(TOKEN_KEY) 로 잘못 저장된 값을 삭제해야 합니다. 코드를 수정해도 저장된 값이 계속 먼저 조회됩니다.

#auth#fetch#frontend#jwt#safari#troubleshooting