카카오도 그러더니 네이버도 axios에러를 뱉어낸다. 다른점은 AxiosError: Request failed with status code 401로 에러코드가 401번이라는 점이다. 이전 카카오의 axios에러는 400에러로 badrequest에러 즉 전송정보에 문제가 발생했기 때문에 발생한 에러였는데..
이번엔 401에러로 권한인증 에러를 뱉어낸다. 보통 오픈API의 권한은 해당 플랫폼에서 발급하는 accesskey와 secretkey로 처리하는데 분명 이번에도 적절하게 네이버 클라우드 플랫폼에서 발급하는 API 엑세스키와 시크릿키를 작성에서 넣어줬는데도, 또 에러를 뱉어낸다.
해당 에러에 대해서 플랫폼의 AI봇에게 질문하였지만, 원하는 응답을 해주지 못했다.
실시간 채팅상담은 오후5시 까지 인데 해당에러가 발생한 시점이 5시3분경에 확인되었다. axios에러는 401을 뱉어내는데, 왜 코드는 ERR_BAD_REQUEST일까? 이 부분은 잘 모르겠다.
C#코드로 로직을 작성한 포스팅을 보았는데 이분은 timestemps를 문자열 처리를 하지않고 헤더에 보내고, 시그니처를 Base64로 인코딩 하지 않고 헤더에 보내서 문제가 발생한 경우였다.
나는 두 가지 모두 처리를 해 두었기에 해당사항에 대해서 문제가 발생하지 않았을 것이다.
401에러가 발생하는 원인을 4가지로 나열해 주었다.
결과적으로는 해당 사이트에서도 내가 원하는 정답을 얻을 수 없었다.
네이버sens에서 시그니처 생성에 대한 코드 샘플이 있었는데, 여기서 crypto-js를 이용해 해싱을 하는 부분이 3.1.2버전으로 조금 오래된 과거 버전으로 샘플이 작성되어있다.
따라서 본인은 최신 4.1.1버전을 받아서 똑같이 SHA256해싱하고 Base64로 변환하는 과정을 거쳐 시그니처를 생성했다.
makeSignature(): string {
const space = ' ';
const newLine = '\n';
const method = 'POST';
const timestamp = Date.now().toString()
const url = `/sms/v2/services/${NAVER_SMS_SERVICE_ID}/messages`;
const accessKey = NAVER_API_ACCESS_KEY_ID;
const secretKey = NAVER_API_SECRET_KEY;
const massage =
method + space + url + newLine + timestamp + newLine + accessKey;
const hmac = CryptoJS.HmacSHA256(massage, secretKey);
const hash = hmac.toString(CryptoJS.enc.Base64);
return hash;
}
이게 원인인지 알 수 없었지만, 그래도 샘플과 많은 SENS사용된 포스팅에서는 과거의 3.1.2버전의 샘플예시과 동일하게 작성한 부분이 많아서 최신버전을 지우고 샘플버전 예시와 같이 작성해 보았다.
makeSignature() {
const space = ' ';
const newLine = '\n';
const method = 'POST';
const timestamp = Date.now().toString()
const url = `/sms/v2/services/${NAVER_SMS_SERVICE_ID}/messages`;
const accessKey = NAVER_API_ACCESS_KEY_ID;
const secretKey = NAVER_API_SECRET_KEY;
const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, secretKey);
hmac.update(method);
hmac.update(space);
hmac.update(url);
hmac.update(newLine);
hmac.update(timestamp);
hmac.update(newLine);
hmac.update(accessKey);
const hash = hmac.finalize();
return hash.toString(CryptoJS.enc.Base64);
}
하지만 이 방법 또한 같은 에러를 내뿜었다.
5시 부터 8시 까지 해당 에러를 해결하기 위해서 정보의 바다를 휘저었지만, 도저히 찾을 방법이 보이지 않아서 일단은 네이버 클라우드 플랫폼의 문의하기를 이용해 문의내용을 남겨두었다.
퇴근시간이 지났음에도 불구하고 답변이 왔다. 답변내용을 보자마자. 😲헉! "시그니처의 timestemp와 헤더의timestemp 같아야 하는구나.. 나는 머리가 1차원적이구나"라고 생각했다.
//시그니처 제작
makeSignature(): string {
const space = ' ';
const newLine = '\n';
const method = 'POST';
const url = `/sms/v2/services/${NAVER_SMS_SERVICE_ID}/messages`;
const timestamp = Date.now().toString();
const accessKey = NAVER_API_ACCESS_KEY_ID;
const secretKey = NAVER_API_SECRET_KEY;
const massage =
method + space + url + newLine + timestamp + newLine + accessKey;
const hmac = CryptoJS.HmacSHA256(massage, secretKey);
const hash = hmac.toString(CryptoJS.enc.Base64);
console.log(hash);
return hash;
}
//sms보내기
async sendSMS(sendSmsDto: SendSmsDto) {
const signature: string = this.makeSignature();
const code: string = this.createRandomNum().toString();
const headers = {
'Content-Type': 'application/json; charset=utf-8',
'x-ncp-apigw-timestamp': Date.now().toString();,
'x-ncp-iam-access-key': NAVER_API_ACCESS_KEY_ID,
'x-ncp-apigw-signature-v2': signature,
};
const body = {
type: 'SMS',
from: SMS_CALLING_NUMBER,
content: `인증번호는 ${code}입니다.`,
messages: [{ to: sendSmsDto.phone_number }],
};
const url = `https://sens.apigw.ntruss.com/sms/v2/services/${NAVER_SMS_SERVICE_ID}/messages`;
const res = await firstValueFrom(
this.http.post(url, body, { headers }),
).catch((e) => {
console.error(e);
throw new InternalServerErrorException();
});
console.log(res.data);
}
내 로직은 위와 같이 각각의 함수를 나눠서 생각했기 때문에 각각 Date.now().toString()
로 요청을 감지한 시간을 스트링으로 변환한다. 하지만 sms보내기 로직에서 시그니처를 먼저 만들고 이후에 헤더를 만들게 되어, 사람은 느낄 수 없지만 컴퓨터가 인지하는 밀리초 단위의 차이가 발생하기 때문에 헤더에 작성된 'x-ncp-apigw-timestamp'
헤더가 시그니처의 암호화된 시간과 일치하지 않아서 발생한 문제였다.