개발 리포트는 프로젝트가 끝난 후 전체적인 과정을 되돌아보고, 성과와 문제 해결 과정을 정리하여 팀과 공유하는 문서입니다. 프로젝트의 전반적인 진행 상황, 맡은 역할, 해결한 문제 등을 구체적으로 기술하여 향후 프로젝트에 대한 참고자료로 활용할 수 있습니다.
프로젝트 진행 중 직면한 기술적 문제와 이를 해결한 과정을 상세히 설명해 주세요.
별점을 나타낼 때, 소수점 단위까지 이미지에 반영해야 했기 때문에 단순한 이미지 사용으로는 어려움을 겪었습니다.
import React from "react";
interface StarIconType {
width?: number;
height?: number;
rating?: number;
}
function StarIcon({ width = 200, height = 40, rating = 5 }: StarIconType) {
const fullStars = Math.floor(rating);
const partial = rating - fullStars;
const starPath =
"M13.7297 8.11256C15.6297 4.70419 16.5797 3 18 3C19.4203 3 20.3703 4.70419 22.2703 8.11256L22.7618 8.99435C23.3017 9.9629 23.5717 10.4472 23.9926 10.7667C24.4135 11.0862 24.9377 11.2049 25.9862 11.4421L26.9407 11.658C30.6302 12.4928 32.475 12.9102 32.9139 14.3216C33.3528 15.733 32.0951 17.2036 29.5799 20.1449L28.9291 20.9058C28.2144 21.7417 27.857 22.1596 27.6962 22.6766C27.5354 23.1936 27.5895 23.7512 27.6975 24.8663L27.7959 25.8816C28.1762 29.8059 28.3663 31.7681 27.2173 32.6403C26.0682 33.5126 24.341 32.7173 20.8865 31.1268L19.9928 30.7153C19.0111 30.2633 18.5203 30.0373 18 30.0373C17.4797 30.0373 16.9889 30.2633 16.0072 30.7153L15.1135 31.1268C11.659 32.7173 9.93176 33.5126 8.78272 32.6403C7.63368 31.7681 7.82382 29.8059 8.20409 25.8816L8.30248 24.8663C8.41054 23.7512 8.46457 23.1936 8.30379 22.6766C8.14302 22.1596 7.78564 21.7417 7.07088 20.9058L6.42015 20.1449C3.90487 17.2036 2.64724 15.733 3.08613 14.3216C3.52503 12.9102 5.36979 12.4928 9.0593 11.658L10.0138 11.4421C11.0623 11.2049 11.5865 11.0862 12.0074 10.7667C12.4283 10.4472 12.6983 9.9629 13.2382 8.99435L13.7297 8.11256Z";
return (
<svg width={width} height={height} viewBox="0 0 200 40" fill="none" xmlns="<http://www.w3.org/2000/svg>">
{[0, 1, 2, 3, 4].map((i) => {
const fillColor = i < fullStars ? "#FFC149" : "#D9D9D9";
return (
<g key={i} transform={`translate(${i * 40}, 0)`}>
{i === fullStars && partial > 0 ? (
<>
<path d={starPath} fill="#D9D9D9" />
<clipPath id={`clip${i}`}>
<rect width={partial * 36} height={36} />
</clipPath>
<path d={starPath} fill="#FFC149" clipPath={`url(#clip${i})`} />
</>
) : (
<path d={starPath} fill={fillColor} />
)}
</g>
);
})}
</svg>
);
}
export default StarIcon;
Rating의 경우, 처음에는 백엔드에서 모든 리뷰를 불러오고 프론트에서 평균을 구해 처리하는 방법을 이용했습니다. 그러나 기사님을 별점으로 정렬하기 기능 등 프론트에서 평균을 구했을 때 문제가 되는 부분들이 존재했기에 평균을 DB 자체에 저장해두고 리뷰가 업데이트 될 때마다 평균 역시 업데이트하는 방법을 이용하여 정렬 및 기사님 페이지에서 매번 평균을 계산하지 않도록 하였습니다.
// 기사님 상세 정보
model Driver {
id String @id @default(cuid())
authUserId String @unique // AuthUser와 연결
.
.
.
averageRating Float @default(0)
}
무한스크롤을 구현할 때 백엔드에 hasNext 라는 변수를 받아오는 과정이 필요한데, 스킵을 이용하여 다시 기존의 방식은 prisma 를 두번 호출하여 비효율적이라고 생각하였습니다.
const PAGE_SIZE = 3;
const skip = (page - 1) * PAGE_SIZE;
const drivers = await prisma.driver.findMany({
where: {
OR: [{ nickname: { contains: keyword, mode: "insensitive" } }],
...(service && { moveType: { has: service } }),
...(region && { serviceAreas: { some: { region } } })
},
skip: skip,
take: PAGE_SIZE + 1, //hasNext 확인하기 위해 하나 더 가져옴
orderBy: orderByClause,
include: {
_count: { select: { reviewsReceived: true, favorite: true } },
serviceAreas: true,
favorite: userId ? { where: { customerId: userId }, select: { id: true } } : false
}
});
const hasNext = drivers.length > PAGE_SIZE;
정렬 시 기준이 같은 기사들이 많을 경우, 호출이 랜덤으로 되어 같은 기사가 여러번 불리는 현상이 있었습니다.
const orderByClause =
orderBy === "reviewCount"
? [{ reviewsReceived: { _count: "desc" as const } }, { id: "asc" as const }]
: [{ [orderBy]: "desc" as const }, { id: "asc" as const }];
깃허브 액션으로 백엔드 CI/CD를 설정할 때, AWS를 프리티어 버전으로 이용하다보니 EC2에서 빌드 하는 방식으로 하자 배포 할 때마다 CPU 사용률이 너무 커 배포가 계속 실패하는 현상이 발생했습니다. EC2를 껐다 키면 해결되긴 했지만 일시적으로 해결될 뿐 지속적으로 문제가 발생하였기에 CI/CD 를 SCP 방식으로 변경하였습니다. 깃허브 액션에서 빌드를 마치고 압축하여 EC2로 전송하여 서버를 배포하는 방식으로 바뀌자 이전과 비교하여 CPU 사용률이 훨씬 줄어든 것을 확인할 수 있었고 배포가 중단되는 문제도 없어졌습니다.
이전에 거의 50%에 달했던 사용률이 변경 이후 10% 미만으로 보이는 것을 확인할 수 있습니다.