네이버 맞춤법 검사기 요청 "유효한 키가 아닙니다" 오류 해결하기

송재민·2024년 2월 10일
0

문제 상황

내가 개발에 참여했던 대학생 메일 양식 생성기 웹사이트 <대설교메>의 맞춤법 검사기 기능이 동작하지 않는 것을 발견했다. 본래는 '맞춤법 검사하기' 버튼을 눌렀을 때 검사가 적용된 결과가 출력된다.
맞춤법 검사 결과는 네이버 맞춤법 검사기 http 요청시 날아오는 response에서 크롤링하여 출력하고 있었다.

console로 날아오는 response를 확인해보니 "유효한 키가 아닙니다." 라는 에러가 찍히고 있었다.


문제 해결기

https://github.com/ssut/py-hanspell/issues/38
같은 네이버 맞춤법 검사기를 사용하는 파이썬 라이브러리 py-hanspell 깃허브의 issue를 확인해보니 네이버 맞춤법 검사기 요청에 passportKey라는 파라미터가 추가된 것이 문제의 원인이었다.
위 이슈를 참고해서 프론트엔드에서 passportKey 발급을 시도해보았으나, CORS 오류가 발생하였다.

백엔드 파트에서 passportKey를 발급해줄 수 있을 것 같아 백엔드 담당자에게 요청을 했다.

passportKey를 발급받는 백엔드 코드.
감사합니다 은주님.. (https://github.com/Song-EunJu)

네이버 검색창에 '맞춤법검사기'를 입력하면 Response로 날아오는 데이터 중에서 passportKey를 뽑아낼 수 있다.
이렇게 뽑아낸 passportKey를 반환해주는 api를 만들어 주셔서, passportKey를 가지고 맞춤법 검사기 결과를 받을 수 있도록 프론트엔드 코드를 짰다.

//맞춤법 검사기 결과 호출
const getSpellCheckerResult = async (stringToCheck: string) => {
  let spellCheckerURL = `https://m.search.naver.com/p/csearch/ocontent/util/SpellerProxy?passportKey=${localStorage.getItem(
    "spellCheckerPassportKey"
  )}&_callback=mycallback&q=${stringToCheck}&where=nexearch&color_blindness=0&_=1643811632694`;
  let result = await axios.get(spellCheckerURL);

  const jsonData = result.data.match(/\{.*\}/)[0];
  const parsedData = JSON.parse(jsonData);

  const errorMessage = parsedData.message.error;

  //passportkey가 없거나 유효하지 않을 때 passportkey 업데이트
  if (errorMessage === "유효한 키가 아닙니다.") {
    await getSpellCheckerPassportKey();
    spellCheckerURL = `https://m.search.naver.com/p/csearch/ocontent/util/SpellerProxy?passportKey=${localStorage.getItem(
      "spellCheckerPassportKey"
    )}&_callback=mycallback&q=${stringToCheck}&where=nexearch&color_blindness=0&_=1643811632694`;

    result = await axios.get(spellCheckerURL);
  }

  return result;
};

//html로 파싱된 맞춤법 검사기 get
export const getParsedSpellCheckerResult = async (stringToCheck: string[]) => {
  let checkFinal: string = "";
  let checkerResultDataString: string = "";
  for (let i = 0; i < stringToCheck.length; i++) {
    if (stringToCheck[i] !== "") {
      const result = await getSpellCheckerResult(stringToCheck[i]);
      checkerResultDataString = result.data;
      checkerResultDataString = checkerResultDataString
        .replace("mycallback(", "")
        .replace(");", "");
      checkerResultDataString =
        JSON.parse(checkerResultDataString).message.result.html + "<br>";
      checkFinal = checkFinal + checkerResultDataString;
    }
  }
  return checkFinal;
};

//맞춤법 검사기 passportkey 업데이트
const getSpellCheckerPassportKey = async () => {
  const result = await axios.get(
    `${process.env.REACT_APP_SPELL_URL}/passportKey`
  );

  if (result.status === 200) {
    localStorage.setItem("spellCheckerPassportKey", result.data.passportKey);
  }
};

passportKey를 받아오고 passportKey를 사용해서 맞춤법 검사기 요청을 보내기 위한 코드를 추가했다.
전체적인 로직은 다음과 같다.

1. 맞춤법 검사기 결과 요청 시 localStorage에 저장된 passportKey를 가지고 요청을 보낸다.
2. 키가 유효하지 않다는 에러가 발생할 경우 api를 통해 passportKey를 발급 받아 localStrage에 저장한 후 다시 요청을 보낸다.
3. 맞춤법 검사기 결과를 다시 html 형태로 파싱한다.


느낀점

  1. 대설교메는 2년 전에 배포한 웹사이트지만 꾸준히 유지보수를 진행하는 경험을 쌓아 뿌듯했다.
    예전보다 많지는 않지만 아직도 사용해주는 분들이 계시기 때문에, 그 분들을 위해 꾸준히 유지보수를 진행하고 싶다.

  2. 지금 보니 마음에 안 드는 코드가 굉장히 많다.. 이번에 passportKey 발급을 위해 코드를 추가한 부분도 있지만, 기존에 불필요하게 반복되는 코드가 많아 이를 줄이기 위해 util화를 진행한 부분도 있다.

  3. 프론트엔드 파트에서의 소통도 부족했던 것 같다.
    팀원과 서로 역할을 딱 나눠놓고 개발을 진행했었는데, 각자 개발한 기능이 서로 연관되는 부분에서 비효율적인 코드가 많았던 것 같다.. 언젠가 리팩토링을 진행할 수 있을까..?

대설교메 프론트엔드 깃허브 레포지토리 바로가기

profile
Software Engineer @Samsung Heavy Industries

0개의 댓글