본문 바로가기

Project Log/CommentTeller

삽질의 날 - redux-thunk, async/await, 유튜브 댓글 API

와 오늘 진짜 삽질의 날이라고 불러도 과장이 아닐 정도로 연속으로 다른 삽질만 하루 종일 했다.

 

redux-thunk

먼저 프론트는 거의 다 완성돼서 백으로 API요청만 보낼 수 있도록 하면 돼서 redux-thunk를 사용해서 비동기 요청을 처리하도록 했다.

백에서 API요청을 받는 것 까지는 확인 했는데, 요청의 loading/success/error 상태가 전혀 처리되지 않는 것이다. 즉, promise로 형태로 작동하지 않고 요청한 순수 액션에 대한 처리만 작동하고, 심지어 redux-logger도 작동하지 않았다. 아니 대체 왜지? 하면서 미들웨어 잘못 넣었나 하면서 몇 번 바꾸고 이상한 곳만 수정하면서 한두 시간 삽질하다가 이번에도 갓갓 stackoverflow를 뒤적거려보니 비슷한 이유로 logger도, redux-thunk도 전혀 작동을 안 한다는 글을 찾았다.

답변을 보니 그 사람은 thunk를 이용해 action을 dispatch하지 않았기 때문에 그냥 일반 액션에 대한 reducer만 작동한 것이었다. 와 설마 싶어 나도 보니 thunk의 dispatch를 사용하지 않고 모듈에서 export한 일반 액션 함수를 그냥 사용하고 있었다. 그러니까 당연히 안되지... 하아... 그래서 thunk의 dispatch로 보내주니 로거도 잘 찍히고 아주 잘 작동한다.

import { useDispatch } from 'react-redux';
import { getComments } from 'stores/base';

(...)
    const handleSearch = () => {
        if(inputEl.current.value){
            const URL = 'https://www.youtube.com/watch?v=';
            const videoURL = inputEl.current.value;
            if(videoURL.slice(0, 32) === URL){
                const videoId = videoURL.slice(32, videoURL.length);
                
                //이 부분에서 그냥 getComments(videoId)로 해주었으니 당연히 thunk적용이 안됨
                dispatch(getComments(videoId));
            } else {
                setAlert('유효하지 않은 주소입니다. 주소형식을 확인해주세요.');
            }
        } else {
            setAlert('주소를 입력해주세요!');
        }
    }
 (...)

async/await

프론트에서 유튜브 비디오에 대한 id가 오면 먼저 구글 API를 이용해서 댓글을 받아와 준다. 애석하게도 한번 API를 보낼 때 받아볼 수 있는 댓글의 개수는 최대 100개 이기 때문에 만약, 한 영상에 댓글이 2000개라면 API요청을 20번 해야 한다. 그리고 맨 처음 100개를 받고 나면 response로 다음 댓글 페이지에 대한 토큰을 보내주는데, 이 토큰을 이용해서 다시 2번째 요청을 보내면 101번째 댓글부터 불러올 수 있다. reponse에 다음 페이지에 대한 토큰이 없다면 마지막 페이지로 간주하면 된다. 

댓글을 모두 불러오기 위해 첫번째 API요청부터 다음 페이지에 대한 토큰이 없으면 API요청을 멈출 수 있도록 while loop을 사용하기로 했다. 먼저 API를 요청한 후 response를 받아서 이를 댓글만 따로 모으는 식으로 정제가 필요하기 때문에 async/await처리가 필수였다. db에 CRUD 할 때는 잘 쓰다가 갑자기 while문 안에서 쓰려고 하니까 헷갈린 건지 뭔지 아무튼 내가 원하는 방식대로 동작을 안 하고, API를 다 불러오기도 전에 정제 작업이 끝나는 현상이 발생했다. 한번 헷갈리니까 갑자기 머릿속이 뒤죽박죽 돼서 맨 처음에는 대체 어떻게 작성했길래 예상대로 안 돌아간 건지도 기억이 안 난다ㅋㅋㅋㅋㅋㅋㅋ 아무튼 코드도 더 깔끔해지게 API 요청 후, 댓글만 뽑아오는 정제 작업을 하는 메서드를 따로 바깥으로 빼서 async/await 메서드로 만들어 준 후, 라우터에서 이 메서드를 호출할 때는 promise 형식으로 앞의 메서드에서 comments 배열을 넘겨받으면 이걸 가지고 단어 분리 작업이나 필요 없는 이모티콘 등을 삭제는 작업을 하도록 짜 봤다. 

일단 원하는대로 댓글만 뽑아오는 작업까지는 원하는 순서대로 잘 작동하는 모습을 볼 수 있었다.

 

유튜브 댓글 API

후...이것도 말하자면 길다ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 위에서 말한 대로 한번 API요청 당 최대 100개의 댓글만 뽑아 올 수 있기 때문에 다음 101번째 댓글부터는 첫 번째 요청에서 response로 반환해준 nextPageToken값을 파라미터로 넣어서 두 번째 요청을 해야 101번째 댓글부터 뽑아준다. 시험 삼아서 댓글이 대략 2700개(대댓글 포함)인 영상을 api에 돌려봤는데, 무슨 50번이 훌쩍 넘어가게 while loop이 끝나지 않고 돌아가는 것이다. 정상 작동대로라면, 내가 사용하는 API는 대댓글은 불러오지 않기 때문에 2700개보다 개수가 적어야 한다. 그래서 확인해보니 nextPageToken은 계속해서 발급을 해주고 있고, 페이지당 댓글도 변하지 않고 계속 같은 내용을 응답해주고 있었다.

이것도 아니 대체 뭐지... 하면서 꽤 헤맸는데 nextPageToken은 파라미터로 넣어서 보낼 때 파라미터 명을 pageToken으로 넣어서 보냈어야 했다. nextPageToken으로 보냈으니 받는 쪽에서는 pageToken이 없는 것으로 확인하고 계속해서 1번째 페이지 댓글만 반환하고, nextPageToken도 계속해서 반환하는 것이었다. 와 진짜 pageToken으로 바꿔서 postman으로 보내보니 아주아주 잘 받아진다 ^^ 당연히 응답해주는 프로퍼티 명이랑 같을 거라고 생각하고 가이드 문서 제대로 안 읽어본 내 탓이지 뭐 후후... 어쨌든 그래도 해결하고 나니 완전 속 시원하다.

 

이제 받아온 댓글을 단어로 쪼갤 수 있는 형태소 분석기라는 것도 한번 써봤어야 했는데 하루 종일 예상치 못한 삽질로 시간을 많이 보내서 아무래도 내일 해봐야 할 것 같다. 걱정되는 건, API요청으로 댓글을 100개씩 따로따로 받아오는 작업이다 보니 댓글만 추출해서 따오는 것도 생각보다 시간이 오래 걸린다. 그런데 여기에 형태소 분석 작업 + 단어 빈도 계산까지 하면 더 오래 걸릴 것 같은데... 일단 원래 구상한 기능까지는 잘 작동하게 만든 후 시간이 너무 오래 걸리는 건 예외로 처리하던지 아니면 처리하는 댓글의 개수에 한계를 둬야 하는지 고민이다.