
개발 배경: 온라인 디버거에 대한 ‘말할 수 없는 공포’
웹 개발, 특히 인증 관련 디버깅을 하다 보면 eyJhb...로 시작하는 긴 JWT(JSON Web Token)를 들여다보는 시간이 아주 많습니다. 이 토큰의 내용을 빠르고 간편하게 확인하기 위해 구글에 ‘JWT Decode’를 검색해 본 경험, 다들 한 번쯤은 있으실 겁니다.
하지만 보안을 공부하면 할수록 이것이 얼마나 위험한 행동인지 깨닫게 됩니다.
JWT에는 사용자의 이메일 주소나 ID, 심지어 시스템 내부의 권한 정보가 포함되는 경우가 많습니다. 만약 그것이 운영 환경의 토큰이라면, **“정체 모를 누군가에게 우리 집 현관문의 보조 키를 맡기는 것”**과 다를 바 없습니다.
“학습하는 사람뿐만 아니라, 서비스를 만드는 제작자가 진심으로 안심하고 사용할 수 있는 ‘성역’ 같은 검증 공간이 필요하다.”
**JWT 디코드/검증 도구**의 개발은 바로 이 ‘온라인 도구에 대한 건전한 불신’에서 시작되었습니다.
기술적 집념: 브라우저를 ‘철벽의 실행 환경’으로 바꾸다
단순히 디코드만 하는 것이라면 Base64를 거꾸로 돌리기만 하면 됩니다. 하지만 제가 고집한 것은 ‘서명 검증(Verify)‘까지 포함된 본격적인 기능을 완전히 로컬 환경에서 구현하는 것이었습니다.
1. 모던 보안의 수호신 ‘jose’ 라이브러리 도입
암호화 관련 로직을 직접 짜는 것은 보안 엔지니어로서 가장 큰 금기 사항입니다.
그래서 현대 웹 표준에서 가장 신뢰도가 높고 Web Crypto API를 풀 활용하는 jose 라이브러리를 채택했습니다.
import * as jose from 'jose';
// 서버로 보내지 않고 브라우저 상에서만 서명을 검증
const secretKey = new TextEncoder().encode(secret);
const { payload, protectedHeader } = await jose.jwtVerify(token, secretKey);
이 라이브4러리를 사용함으로써, 여러분의 PC 밖으로 단 한 방울의 정보도 흘리지 않고도 “이 토큰이 진짜인가?”를 즉시 판단할 수 있는 환경을 구축했습니다.
2. ‘현장 도구’로서의 유연함인 폴백(Fallback) 구현
한편, 실제 개발 현장에서는 ‘이미 깨져버린 토큰’을 조사해야 하는 상황도 발생합니다.
엄격한 라이브러리는 파싱 에러로 멈춰버리지만, “에러가 나는 건 알겠어, 하지만 내용물의 파편이라도 보고 싶다고!” 하는 것이 엔지니어의 솔직한 심정이죠.
그래서 정식 검증과는 별개로, 데이터가 다소 손상되었더라도 내용을 억지로 열어볼 수 있는 Base64 디코드 폴백 로직도 함께 실장했습니다.
// 약간의 포맷 깨짐은 감안하고 JSON 데이터를 구출
const base64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');
const json = decodeURIComponent(atob(base64));
‘표준을 존중하는 엄격함’과 ‘현장의 복잡한 문제에 공감하는 유연함’. 이 두 가지의 공존이야말로 도구로서의 미학이라고 생각합니다.
‘침묵’이라는 이름의 안심
이 도구를 구동하는 동안 브라우저의 ‘네트워크’ 탭을 한번 확인해 보세요.
디코드 버튼을 누르든, 서명 검증 버튼을 누르든, 그 어떤 통신도 발생하지 않을 것입니다.
이 ‘침묵’이야말로 우리가 자랑하는 최대의 보안 기능입니다.
마치며
JWT 도구는 복잡한 기술을 ‘안전성을 단 1mm도 손상시키지 않으면서 얼마나 친숙하게 만들 수 있을까’에 대한 저만의 도전이었습니다.
기밀성 높은 토큰을 다룰 때, 문득 “아, 거기라면 안심하고 테스트할 수 있었지”라고 떠올려 주신다면 제작자로서 그보다 더 기쁜 일은 없을 것입니다.
더 자유롭게, 그리고 더 안전하게. 여러분의 인증 개발 업무가 조금이라도 가벼워지기를 바랍니다.