ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [뉴스검색봇] 3. 네이버 api로 데이터 불러오기, 임베드 작성
    DEVELOP/discord-bot 2022. 5. 3. 01:29

    봇 개발을 위한 기본 골격과 명령어 테스트를 마쳤으니,

    본격적으로 기능구현을 시작했다.

     

    이번에 구현한 기능들은 다음과 같다.

    1. 네이버 api호출 테스트

    2. 가져온 JSON형식 데이터에서 태그, 개행문자를 제거하기

    3. 디스코드에 임베드 형식으로 출력

     

     

    먼저 네이버api를 사용해 뉴스데이터들을 불러오는 작업을 했다.

     

    네이버 api를 이용하기 위해선 먼저 등록 신청을 해야한다.

    간단한 정보를 입력하고 본인인증까지 마치면 바로 등록된다.

    내가 사용한 검색 api는 일일 최대 호출량이 25,000회로 제한되어있다.

    내가 만든 봇이 그걸 초과할 리 없으니 사실상 무한이다.ㅋㅋ

     

    등록이 완료되면 내 애플리케이션 정보에서 클라이언트 아이디와 키를 확인할 수 있다.

    봇 토큰과 마찬가지로 공개되어서는 안되니 소스코드 공유에 유의한다.

     

    api를 호출하는 코드는 commands 폴더 내에 있는 명령어 파일에서 작성하면 된다.

    호출하는 방법은 여러가지가 있는 듯 한데, 나는 공식문서에 나와있는 fetch를 이용했다.

    이전에 명령어 파일들을 따로 분리해놓고 작업을 했기 때문에, 다른 파일들은 수정할 필요가 없다.

     

    commands 폴더 내에 lastest.js파일을 만들고 최신뉴스를 보여주는 기능을 테스트해봤다.

    기존 명령어파일 골격을 그대로 가져오고, 상단에 해당 코드를 추가한다.

    const fetch = require("node-fetch");

     

    그리고 execute함수 내에서 불러왔다.

    // ../commands/latest.js
    
    async execute(interaction) {
    	const { Naver_Client_Id, Naver_Client_Secret } = require('../config.json');
        
            await interaction.deferReply();
    
            let header = new fetch.Headers({
                "X-Naver-Client-Id": Naver_Client_Id,
                "X-Naver-Client-Secret": Naver_Client_Secret,
            });
    
            let response = await fetch(
                "https://openapi.naver.com/v1/search/news.json?query=주식&display=1&start=1", 
                { headers: header }
            );
            let data = await response.json();
    
            console.log(data);
            ...
    },

    헤더에 클라이언트 아이디와 키는 외부노출을 방지하기 위해 토큰정보를 저장했던 config.json에 따로 저장해서 require로 불러왔다.

     

    이렇게 보면 간단하게 해결한 것 같지만.. api호출하는 법, 헤더가 뭔지 패치가 뭔지 제대로 모르는 상태에서 시작하느라 고생좀 했다. 특히 대부분의 설명은 웹기반으로 되어있는데 이걸 어떻게 디스코드에 적용시켜야 할지도 생각해야했다.

    이부분은 공식문서랑도 다르고(api가 다르니 당연함.....) 사람마다 호출하는 방식도 약간씩 달라서 혼돈 그자체였다.

    네이버 가이드랑 디스코드 가이드 둘다 보면서 해결했다. 구글링은 덤

     

    지금은 테스트가 목적이므로 쿼리도 리터럴로 넣었고, api를 호출하는 코드 자체를 명령어 파일에 모두 넣어놓았지만,

    저부분은 함수로 따로 작성하면 좋을것 같다.

    Url부분도 변수로 따로 지정해두고 사용자가 지정한 값에 따라 url에 쿼리를 추가하는 방식으로 코드를 작성해야겠다.

    그리고 header와 response는 const로 바꾸는게 좋아보인다.

     

    콘솔에 이 로그 찍히는 순간 너무 기뻤다 정말...


    데이터가 잘 불러와진 것을 확인했으니, 이제 이 데이터들을 디스코드에 예쁘게 출력해줘야 한다.

    처음 목표였던 임베드 형식으로 출력해줄 것이다.

     

    그러나 그 전에, 위에 찍힌 로그를 보면 글자에 매우 거슬리는게 보인다.

    &quot; <b></b> 등등...

     

     

     

     

    네이버 api 가이드 中.. 왜......그렇게 감싸주는 것이죠...?

     

     

     

    이걸 그대로 출력하면 미관상 보기 안좋기 때문에, 제거해주는 작업을 또 해야한다.

    replace함수와 정규식을 사용하면 된다고 한다.

     

    명령어를 만들 때 commands 폴더로 따로 구분해 놓았듯, 함수도 동일하게 만들면 깔끔하게 사용할 수 있을 것 같아

    functions폴더를 만들고, removeTags.js파일을 만들었다.

    여기에 문자를 예쁘게 만들어주는 함수를 작성하고 필요한 곳에 호출했다.

    // ../functions/removeTags.js
    
    module.exports = {
        removeTags : function(text) {
            text = text
                .replace(/<(\/)?([a-zA-Z]*)(\s[a-zA-Z]*=[^>]*)?(\s)*(\/)?>/ig, "")
                .replace(/(&quot\;)/g,"\"");
            
            return text;
        },
    
        trim : function(str, max) {
            (str.length > max ? `${str.slice(0, max - 3)}...` : str);
    
        }
    };

    (태그는 삭제, 따옴표부호는 따옴표로 변환)

     

    removeTags함수와 더불어 trim함수도 작성했는데, 막상 내 결과에선 trim함수를 사용한 것과 안한 것의 차이가 없어서 만들어만 두었다.


    함수도 만들었으니, 임베드 형식을 만들고 함수를 적용해서 출력하면 된다.

     

    다시 latest.js파일로 돌아와서, 상단에 다음 코드를 추가해준다.

    const { MessageEmbed } = require("discord.js");

     

    그리고 execute함수 안에 임베드를 생성해준다.

    당연한 말이지만 데이터를 가져오는 코드 밑에 작성해야 한다.

     

     async execute(interaction) {
     	const rt = require('../functions/removeTags.js');
            ...
    
            const embed = new MessageEmbed()
                    .setColor("#2DB400")
                    .setTitle(rt.removeTags(data.items[0].title))
                    .setURL(data.items[0].link)
                    .addFields(
                        { name: "기사제목", value: rt.removeTags(data.items[0].title) }, 
                        { name: "요약", value: rt.removeTags(data.items[0].description) }
                    );
    
            interaction.editReply({ embeds: [embed] });
     },

     

    간단하게 색상과 제목, 제목에 들어갈 URL, 내용(필드)에는 기사제목과 요약을 넣어줬다.

     

    참고로 저 색상코드의 정체는,

    "NAVER GREEN"

    😎

    .

    .

    .

     

     

    문자형식 데이터들에 만들어둔 함수를 모두 적용해주었다.

    참고로 다른파일의 변수/함수/객체 등등을 불러올 땐 다음과 같이 require('파일경로')로 불러와야 호출가능하다.

     

    완성된 임베드를 editReply에 넣어주어야 디스코드에 출력된다.

     

    그리고 실행하면(deploy-commands.js파일 실행하는거 까먹지 말고)....

     

    .

    .

    .

    .

     

    .

    .

    .

    .

     

     

     

    제목을 클릭하면 기사 전문으로 이동한다.

     

    api 호출할 때 다 지워버릴까 정말 나댔다 공부나 하지라는 생각 10번정도 했다.

    근데 하다보니 되고 하다보니 되고... 를 반복하니 원하는 결과가 나왔다.

    이제 다음부턴 해메지 않아도 된다... 참조은세상 푸하하

     


     

    주요에러

    Cannot read properties of undefined (reading 'title')
        > json 데이터들을 읽어올 때, 내가 원한 내용이 items로 한번 더 감싸져 있어서 난 오류. data.title이 아닌 data.items[idx].title로 가져와야 한다.(items가 배열이기 때문에 인덱스로 접근)

    Cannot read properties of undefined (reading '0')
        > 보통 배열이 아닌 변수에 인덱스로 접근하면 발생하는 오류라고 하는데, 나는 문자제거 함수를 사용할 때 오류가 났다. 이유는 내가 작성한 함수는 문자열에서 적용가능한데, json데이터들을 통째로 넣었더니 오류가 남. 매개변수에 문자열을 넣어줘야 한다.

    ~~ is not a function
        > 파일에서 require를 잘못했거나, 경로를 찾지 못했거나, 함수 코드가 잘못되었거나 등등의 이유로 발생.
    파일 경로 설정을 자꾸 인식을 못해서 이 오류를 참 많이 봤다. 계속 하다가 터득한 수법인데, vscode에서 경로에 마우스 호버링 했을 때 fullname이 뜨면 인식하고 있단 소리다. 풀네임이 안뜨면 실행했을 때 무조건 오류난다.

    Identifier '~~~' has already been declared
        > 매개변수와 동일한 이름의 변수를 선언했을 때 발생

     

    다음에 할 것

    1. 쿼리를 사용자가 선택한 값으로 변경해서 적용하기
    2. 임베드에 pagination을 적용해 페이지를 넘기면 다음 뉴스를 계속 보여주는 기능구현
    3. 임베드의 내용 수정(추가정보 표시, 가독성 좋게 만들기)

    댓글

Designed by Tistory.