strapi 쿼리를 이용한 태그 필터링 구현 | 스데브 개발일지8

dana·2022년 2월 5일
1

velog clone coding

목록 보기
10/11
post-thumbnail

♻️ 리팩토링

strapi relation 데이터 불러오기

hashtag에 relation으로 post를 연결한 뒤, 연결된 값을 이용해 데이터 필터링을 만드려고 했다. hashtag의 데이터 타입은 다음과 같아 api를 이용해 데이터를 불러오도록 했다.

하지만 예상했던 결과값과 달리 hashtag에 포함된 post값이 출력되지 않았다.

왜 이런 결과값이 나왔을까 싶어 확인해본 결과, relation된 데이터를 불러오기 위해선 쿼리를 수정해주어야한다는 사실을 알게되었다.

쿼리문 작성하기

릴레이션 된 데이터를 전체 가져오기 위해선 /api/articles?populate=%2A 쿼리를 주소에 입력해주면 된다. 여기서 %2A*을 나타내는 UTF-8 코드로, *모든 것을 의미하는 와일드카드를 말한다.

이렇게 주소에 직접적으로 쿼리문을 넣을 수도 있지만, 해당 코드를 외워야한다던가 조건이 많아지면 복잡해진다는 단점이 있다. 이를 보완하기 위해 쿼리문을 객체로 만들어 주소로 전달해주는 방법도 있다.

위의 예시를 살펴보면,

const qs = require('qs');
const query = qs.stringify({
  populate: '*', 
}, {
  encodeValuesOnly: true,
});

await request(`/api/articles?${query}`);

와 같이 표현할 수 있다.

populate를 위한 쿼리 외에도 페이지네이션 코드들 역시 쿼리문을 객체로 만들어 보낼 수 있어, 기존 useData 훅을 리팩토링 하기로 했다.

리팩토링

// useData

import useSWR from "swr"
import { fetcher } from "../utils/fetcher"
import { API_ENDPOINT } from "../constants"

export const useData = (path: string, query : string = "") => {
    return useSWR(`${API_ENDPOINT}/${path}?${query}`, fetcher)
}
// 기존 코드
//export const useData = (path: string) => {
//  return useSWR(`${API_ENDPOINT}/${path}`, fetcher)
//}

기존의 코드에서 query 부분을 수정해 request에 조건을 입력할 수 있도록 했다. 기존 코드를 사용한 부분에서 에러가 나지 않도록 default 파라미터 값으로 빈 문자열을 설정해주었다.

const qs = require("qs")

공식 문서에 따르면 쿼리스트링을 stringify로 넘겨주기 위해 qs를 입력받아야한다. 하지만 현재 프로젝트는 모듈타입의 파일을 사용하고 있어 commonJS 방식으로 입력이 불가능하다.

에러 메세지에서 npm i --save-dev @types/qs 명령어를 입력해 삭제하라는 메세지가 떠 그대로 실행해주었다. (현재 프로젝트에서는 yarn을 사용하고 있기때문에 명령어 실행 후 생성된 package.lock.json을 삭제해주었다.)

삭제 후, import qs from "qs" 로 불러오기가 가능해졌다.

"esModuleInterop": true
에러메세지에 표기된 방법 이외의 방법을 찾아보다가 발견한 ts설정값.
우리는 이미 true로 설정을 해준 상태여서 별로 도움이 되지는 못했지만, 다음 프로젝트를 위해 적어둔다.
ES6 모듈 베이스에서 CommonJS모듈을 가져오려면 에러가 발생한다.
const example = require("Exam") ❌
그래서 이를 치환해 다음과 같이 import 해오는 방법을 사용하는데 이것도 ES6 사양을 충족시키진 못한다.
사양을 충족시키지 못한다는 말은 example 값이 *에 대한 별칭일 뿐, 호출할 수 있는 객체로 사용되지 못함을 의미한다.
import * as example from "Exam" ⚠️
이 때 ES6 사양에 맞춰 import할 수 있게 해주는 속성이 "esModuleInterop": true 이다
true로 값을 설정한 뒤, 원래의 ES6문법으로 import해오면 example은 더이상 별칭이 아닌 불러오려던 객체 자체로 인식될 수 있다.

// tsconfig.json
"esModuleInterop": true
// any module files
import example from "Exam" ⭕️

https://stackoverflow.com/questions/56238356/understanding-esmoduleinterop-in-tsconfig-file

tag 데이터의 구조와 쿼리문

앞서 말한 것과 같이, hashtag의 데이터 중 다른 테이블과 relation 된 값이 존재한다. Hashtag에는 해당 태그를 갖고 있는 Post값과 relation을 맺고 있으며(n:m), Post는 작성자인 User 데이터와 relation을 맺고 있다(1:n). 따라서 user에 따른 태그값을 뽑아내기 위해 쿼리문을 다음과 같이 입력해주었다.

const query = qs.stringify(
        {
            populate: {
                posts: {
                    populate: ["userid"],
                },
            },
        },
        {
            encodeValuesOnly: true,
        }
    )

이렇게 전달된 쿼리로 데이터는 다음과 같이 출력된다.

filter in filter?

mypage의 유저가 사용했던 태그 데이터만 뽑아내기 위해 filter안에 filter 함수를 넣어 사용해보았다.
ex) www.sdv.com/deli-ght -> deli-ght 유저가 작성한 포스트에 존재하는 tag 데이터를 뽑아내야함.

data.data.filter((e: any) =>
     e.attributes.posts.data.filter(
         (post: any) =>
             post.attributes.userid.data?.attributes.nickname ===
             username
         )
     )

하지만 필터를 사용하면 다음과 같이 모든 태그 데이터들이 출력되어버리는 것을 확인할 수 있다. filter는 true false에 따라 값을 출력하기 때문에 리턴값이 존재해버리니 (필터링된 배열을 리턴) 빈 배열을 제외한 모든 배열을 true로 간주한다.

따라서 이를 해결하기 위해 filter in some 를 이용했다. some은 주어진 조건에 해당되는 값 즉, true가 나오면 순회를 곧장 종료하고 true를 리턴한다. 값이 true or false로 리턴되기 때문에 filter함수가 리턴된 값을 통해 필터링을 실행할 수 있다.



해당 유저별로 알맞게 태그가 들어가는 것을 확인할 수 있다.
(* 아직 전체보기 기능은 구현되지 않은 상태)

profile
PRE-FE에서 PRO-FE로🚀🪐!

0개의 댓글