냉메추

[냉메추] 이제 언어가 늘어갑니다!!! JS도 씁니다!!

장아장 2024. 5. 20. 20:24

ts로 툴을 만들어볼까 했지만 어차피 컴파일 및 인터프리팅 하느니 인터프리팅만 하는게 나을 것 같아서 js로 툴을 만들었다.

Kotlin/Spring으로 WAS(Web Application Server)를 만들었다. 

그런데, 우리 서비스는 기능들을 만든다고 바로 사용할 수 있는 서비스가 아니다. 

많은 데이터가 필요하다. 


데이터를 어떻게 구할 것인가?

냉메추에서 필요한 데이터가 무엇일까?

일단 레시피를 보여줄 수 있어야 하며, 레시피에 대한 각 재료들의 데이터도 있어야 한다. 

이걸 어떻게 해결할지 방안을 모색해야한다. 

 

이런 데이터가 존재하는 것을 보았다. 

여기에서 데이터를 전처리해서 재료들을 빼내고, 데이터를 가져오는 것을 1차적으로 생각했다.

그런데 여러 이슈들이 동시적으로 발생했다. 

 

  1. 우리 서비스는 각 음식 사진에 대한 썸네일이 필요하다. 
  2. 우리 서비스는 각 레시피에 대한 재료 데이터를 가져와 ingredients테이블에 넣어둘 것이다. 
  3. 이걸 이용해 레시피는 각 재료의 문자열이 아닌, 비트연산자와 같은 문자열을 가져야 한다. 

이걸 모두 해소하면서 csv파일의 데이터를 전처리해 저장하는 것에 이슈가 있었다. 

 

일단 재료들이 제 1정규형의 구조도 가지지 못한, 속성값을 문자열로 둔 ','로 연결된 재료들의 문자열 컬럼이 존재했던 것이다. 

이 문자열들을 전부 전처리해 저장하자니 경우의 수가 너무 많았다. 

 

그래서 웹 크롤링을 이용해보기로 했다. 


생각해보기

크롤링을 해서 가져올 데이터가 무엇인지, 어디에 있는지를 알아야 한다. 

그래서 만개의 레시피의 화면을 개발자도구로 띄워보았다. 

 

우리에게 필요한 것은 이렇게 두 가지였다. 

실제 레시피 세부사항은 해당 url를 그대로 가지고 있다가 보내주는 식으로 해서, 

실제 레시피는 만개의 레시피를 보게 할 계획 이었다. 

그러면 이 url은 어떻게 볼까?

 

여기에서 앞에 있는 RCP_SNO의 값을 뒤에 붙여서

`https://m.10000recipe.com/recipe/${RCP_SNO}`

이런식으로 사용해주면 모든 url을 정규화시킬 수 있다. 

 

해당 url에서 위의 컴포넌트들을 뽑아내주었다. 

나는 axios, cheerio를 가져와 사용했다. 


어케생겼누?

이런식으로 데이터가 들어오는 것을 볼 수 있다. 


이걸 크롤링해서 데이터에 넣어주는 방식으로 진행했다. 

 

근데 아까 말했듯이 비트연산으로 처리해야했다. 

그러기 위해서 가져온 재료들을 모두 저장해주어야 하는데, 이 때 내가 구상한 로직은 아래와 같다. 

  1. Map을 이용해서 각 재료의 이름과 이름이 작성된 횟수를 센다. 
  2. 이름들을 횟수순서로 정렬한 배열을 만든다. 
  3. 배열의 인덱스 순서로 기본키를 가지게 저장한다. 

를 처리해야했다. 

addIngredients(ingredientIndex) { // ingredientIndex <- Map을 Object로 가져왔다. 속성명을 재료명으로, 값을 인덱스로 담았다. 
    const names = Object.keys(ingredientIndex);
    names.sort((a, b) => ingredientIndex[a] - ingredientIndex[b]);
    const data = [];
    names.forEach((name) => data.push([name, ingredientIndex[name]]));
    const batchSize = 1000; // 일괄 단위의 크기 설정
    this.connection.query("DELETE FROM ingredient", () => {});

    // 데이터를 일괄 단위로 분할하여 처리
    for (let i = 0; i < data.length; i += batchSize) {
      const batchData = data.slice(i, i + batchSize);
      const query = "INSERT INTO ingredient (name, id) VALUES ?";
      this.connection.query(query, [batchData], (err, result) => {
        if (err) {
          console.log(err);
        }
        console.log(result);
      });
    }
  }

이런식으로 데이터를 저장하는 로직을 만들어주었다. 

 

레시피는 아까 말했듯이 각 재료의 값을 비트로 가지게 했다. 

이렇게 레시피 데이터도 성공적으로 저장했다. 


그러면 이제 끝일까?

아니다. 

실제로 웹 크롤링 툴을 만드시는 많은 분들을 보면 파이썬으로 만드신 경우가 많았다. 

사실 파이썬을 제대로 쓸 줄 모르고 만드는 것 보단, 

제대로 만들 줄 아는 js를 쓰는 것이 더 효율적이라고 판단했기에 js로 크롤링 툴을 만든 것이다. 

 

하지만, 이제는 진짜로 파이썬도 써야한다. 

 

이전의 인트로 때에도 이야기했지만, 우리 졸업 프로젝트 담당 교수님은 ML과 빅데이터 관련 연구를 많이 하신 분이다. 

이전에 MemberRecipe라는 객체로 조회한 회원의 성별, 나이를 기준으로 데이터를 저장하게 해두었다. 

이걸 더 손봐서 어떤 재료들인지 확인하게 하고

이런 것들을 토대로 데이터의 군집화에 대해 학습하고 

적용해야 한다. 

아직 갈 길이 멀다.

 

그래도 해야지 개발잔데

 

그럼...twenty thousand...🔥