최종 프로젝트 중 그룹 리스트페이지에서 랜덤한 이미지를 보여줘야할 필요가 있었다.
우리 프로젝트의 erd였는데
내가 원하는 기능은 이것이었다.
1. 로그인한 유저의 id로 user_group에서 유저가 속한 그룹 랜덤하게 가져오기
2. 가져온 그룹 id를 통해 post테이블에서 해당 그룹에서 생성된 게시글의 대표 이미지 파일명 가져오기
3. 가져온 대표 이미지 파일명을 createSignedUrl을 통해 프라이빗한 이미지 url생성하기
4. 그걸 특정 시간마다 재실행하여 이미지를 계속 변경시키기
근데 supabase-js에는 랜덤하게 하나만 가질 수 있는 기능은 제공되지 않고 있었다....
그래서 일단 차선책으로 아래와 같이 구현해보았다.
1. 로그인한 유저의 id로 user_group에서 유저가 속한 그룹 10개 가져오기
2. 그룹 10개를 랜덤하게 sort하기
3. sort된 배열을 promise.allsettled를 사용하여 post_image_name 가져오기
4. 완료된 promise배열중 status가 fulfilled인 값만 필터링하기
5. 필터링된 배열에 signedurl생성하는 함수를 각각 promise.all사용
6. url배열 완성!
끔찍한 로직
```javascript
const groupLists = await getGroupPostLists(userId);
if (groupLists.length === 0) return null;
//랜덤순으로 그룹리스트 정렬
const randomGroupIds = groupLists.sort(() => Math.random() - 0.5);
const PostsList = await Promise.allSettled(
randomGroupIds.map(async ({ group_id }) => await getPostListsByGroupId(group_id)),
);
const fulfilledLists = PostsList.filter((el) => el.status === 'fulfilled');
const ImageLists = await Promise.all(
fulfilledLists.map((fulfilled) =>
getSignedImgUrl('tour_images', 60 * 10, `group_name/${fulfilled.value.post_thumbnail_image}`),
),
);
```
페이지 이동 후 첫 로직이 3초나 걸린다...
이건 sql을 무조건 써야할거같다는 생각이 듦
1. rpc로 요청을 보내서 post가 있는 group_id를 랜덤하게 하나만 가져온다.
2. post테이블에 group_id로 작성된 post를 랜덤하게 하나만 가져온다.
3. 가져온 이미지파일 하나를 url로 변환한다.
sql문법을 두번만 작성하면 로직이 매우 간단해지고 성능도 오르지 않을까 라는 생각에 해보았다.
근데 내가 어떻게 sql 문법을 작성하나
구글링을 해보고 스택오버플로우에 비슷한 사례가 있는지 검색해보고
gpt한테 물어도 보고 파파고를 사용해 supabase ai와도 대화를 해봤지만
gpt는 테이블 구조를 이해못하고 supabase ai는 아직 좀 부족해서 해결을 못해주었다.
이걸 튜터님한테 들고가면 도대체 뭔 소리를 들을까 싶다가도 아무리생각해도 첫로딩에 3초가 걸리는건 좀 아니다 싶어 들고갔다
3160ms -> 89ms
jesus
```sql
begin
return query
select p.post_thumbnail_image
from public.post p
, (
select p.post_id, count(1)
from public.post p
where p.user_id = input_user_id -- 동적 user_id 파라미터 적용
group by p.post_id
having count(1) > 0
) cal_p
where p.post_id = cal_p.post_id
limit 5;
end;
```
뭔가 같은 데이터만 들어오길래 봤더니 내 게시물의 이미지만 받아오는 거였음
그래서 두번의 rpc를 사용하기로 함
```sql
SELECT user_group.group_id
FROM user_group
JOIN profiles ON profiles.user_id = user_group.user_id
JOIN post on post.group_id = user_group.group_id
WHERE profiles.user_id = insert_user_id
ORDER BY RANDOM()
LIMIT 1;
```
```sql
begin
return (
select p.post_thumbnail_image
from public.post p
where p.group_id = input_group_id
order by random()
limit 1
);
end;
```
처음 user_id를 넣어서 속한 그룹중 post가 있는 group_id를 받아오고
받아온 group_id로 post 중 랜덤한 하나의 썸네일 이미지를 받아온다.
```jsx
useEffect(() => {
const refetchInterval = setInterval(() => {
refetch();
}, 1000 * 10);
refetchInterval;
return () => clearInterval(refetchInterval);
});
```
그렇게 받아온 데이터를 10초에 한번씩 다시 refetch해준다.
정상작동함...ㅠㅠ
근데 infinitequery가 6개씩받아오고, 이미지가 1개씩 받아와야해서 6개의 HTTP요청에 제한이 걸려 해당부분을 해결해야할것도 같다.
infiniteQuery부분의 promise.all을 사용한 createSignedUrl도 합칠수있으면 합치면 좋을 것 같고
변하는 이미지 부분도 한 rpc에서 createSignedUrl로 변경할 수 있으면 매우 좋을 것 같다.
지금 상황이 그냥 한글을 보고 읽을수있는데 뜻을 모르는 상황같다.
뭐 FE개발트랙에서 배워야할 이유는 없는 것이지만 알아둬서 나쁠 이유는 없다고 본다.
baas사용 프로젝트 rpc를 사용해서 성능 최적화를 이끌어냈다는게 나쁜건 아닐거라고 생각한다.
'2차 공부 > TIL' 카테고리의 다른 글
SnapRoad에서 SignedUrl을 사용한 이유 (1) | 2024.11.18 |
---|---|
24.10.20 NextJS react-hook-form Zod (0) | 2024.10.20 |
24.10.19 NextJS react-hook-form (0) | 2024.10.19 |
24.10.15 tailwind에서 제공하지 않는 style속성 사용하기 (0) | 2024.10.15 |
24.10.14 supabase.insert, promise.all과 transaction (1) | 2024.10.15 |