최종프로젝트 12일차 경기 일정 조회 작업, 쿼리 최적화에 대해
전날 작업한 경기장 예약 화면은 끝내고, 경기 일정을 조회하는 달력화면 구현하는 데에 시간을 쓰기 시작했습니다. 이와 함께 기술면접 준비로 쿼리 최적화에 대한 설명도 기록하겠습니다.
React-Datepicker로 만든 달력 화면
전날 작업 기록 입니다
우선 프로젝트 기획단계에서 구현하려고 계획한 경기 일정 조회 화면은 다음과 같습니다.
달력 형식으로 월별 경기 일정을 한눈에 볼 수 있도록 하기로 계획하였고, 하루를 넘겨 마무리한 화면은 다음과 같습니다. 최대한 비슷하게 구현하려고 노력하였고, 달력 안에 이미지 넣는 것도 몇번의 시행착오 끝에 화면에 맞게 들어가도록 구현할 수 있었습니다.
위 화면에서 1월 23일에 등록되어 있는 로고는 상대팀 로고로 이 날 경기가 예약되어 있다는 뜻을 의미합니다. 이 로고를 클릭하게 되면 아래와 같이 해당 일자의 경기 일정 정보를 볼 수 있습니다.
힘들었던 점
위 작업을 하며 가장 어려웠던 부분은 달력 안에 이미지를 넣는 부분이었습니다.
위 화면은 전날 작업한 경기장 예약 화면입니다. 중간에 있는 달력도 React-Datepicker를 사용하였는데, 이는 일자만 보여주면 되었기에 작업에 큰 어려움은 없었습니다.
일자만 보여지게 하는건 React Datepicker에서 어렵지 않게 할 수 있지만 달력안에 이미지를 넣게 하는건 추가적으로 커스텀 작업이 필요했습니다.
먼저 달력에 경기 일정을 보여주기 위해 해당 팀의 경기 일정을 가져오는 API를 호출하는 것에서 작업을 시작했습니다. 아래 코드에서 보듯이 토큰을 헤더에 담아 get 방식으로 API 요청 후 response 안에 담긴 data들을 변환하였습니다. 위 달력에 쓰는 데이터는 newEventDates이며, newSchelduleInfo는 일자 클릭시 나타나는 모달창 정보를 담고 있습니다.
useEffect(() => {
const getScheldule = async () => {
try {
const response = await axios.get("http://localhost:3001/api/match/team/schedule/"+teamId, {
headers: {
Authorization: `Bearer ${accessToken}` // Bearer 토큰 추가
}
});
const resultTeam = response.data.data;
if (Array.isArray(resultTeam)) {
// resultTeam 배열을 이용해 newEventDates 배열 생성
const newEventDates = resultTeam.reduce((acc, team) => {
acc[team.date] = team.logo_url;
return acc;
}, {});
const newSchelduleInfo = resultTeam.map(team => ({
field_name: team.field_name,
name: team.name,
date: team.date,
time: team.time
}));
setEventDates(newEventDates); // 상태 업데이트
setScheldules(newSchelduleInfo);
} else {
console.log('resultTeam is not an array:', resultTeam);
}
} catch (error) {
console.error("데이터 불러오기 실패:", error);
}
};
getScheldule();
}, []);
그 후 달력에서 일자별 렌더링 할 때 아래 함수를 거치도록 하여, 일자별 순회하는 동안 일자별로 div를 커스텀 하도록 로직을 만들었습니다. 일자별로 div 태그가 만들어지면서 안에 일자가 담긴 span과 위에서 가져온 newEventDates 정보를 토대로 해당 일자에 경기가 있으면 img 태그를 없으면 내용 없는 빈 div를 생성하도록 했습니다. 비어있더라도 일정의 공간을 넣어주어 달력의 모습을 유지하도록 하였습니다.
// 달력 내 각 날짜의 내용을 렌더링하는 함수입니다.
const renderDayContents = (day: number, date: Date): JSX.Element => {
const formattedDate = format(date, 'yyyy-MM-dd');
const imageUrl = eventDates[formattedDate];
console.log("imageUrl:",imageUrl);
return (
<div onClick={() => handleDayClick(date)} style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
height: '90px'
}}>
<span style={{ margin: '0 0 0 10px', alignSelf: 'flex-start' }}>{day}</span>
{imageUrl ? (
<img src={imageUrl} alt="Event" style={{ width: '80px', height: '80px', margin: '0 auto', borderRadius: '50%', objectFit: 'cover' }} />
) : (
<div style={{ width: '50px', height: '50px', margin: '0 auto' }} /> // 이미지가 없을 때를 위한 빈 공간
)}
</div>
);
};
여기까지 정리하고 보면 어렵지 않게 했을 것으로 보여질 수도 있는데 생각한것 이상의 시간과 헛발질을 거쳐 여기까지 왔습니다. 그래도 하루 안에 여기까지 구현할 수 있어서 개인적으로 성과 있는 하루라 생각합니다.
쿼리 최적화를 위해 할 수 있는 방법들
참고하면 좋을 사이트들
0. 쿼리 최적화의 필요성
쿼리 최적화는 데이터베이스의 응답 시간을 단축시키고 성능을 향상시키는 과정입니다. 이를 위한 방법으로는 인덱스 추가 및 스키마 구조 개선, 트랜잭션 관리 등이 있습니다. 특히, 인덱스는 검색 속도를 높이는 데 중요한 역할을 하며, 쿼리는 가능한 한 간결하게 작성하여 불필요한 데이터 검색을 줄여야 합니다.
1. 인덱스 사용
쿼리 최적화에서 가장 대표적인 방법 중 하나는 '인덱스(Index)'의 사용입니다. 인덱스를 쉽게 설명하자면, 데이터베이스에서 데이터를 빠르게 찾기 위한 목차나 색인과 같습니다. 책에서 목차를 통해 원하는 내용을 빠르게 찾는 것처럼, 데이터베이스 인덱스도 특정 컬럼의 데이터를 빠르게 검색할 수 있도록 도와줍니다.
데이터베이스에서 데이터를 검색할 때, 인덱스가 없으면 데이터베이스는 모든 행(row)을 하나씩 검사하는 '풀 스캔(full scan)'을 수행해야 합니다. 이는 많은 시간이 소요될 수 있습니다. 하지만 적절한 컬럼에 인덱스를 생성해두면, 데이터베이스는 인덱스를 통해 필요한 데이터의 위치를 빠르게 찾아 접근할 수 있습니다.
단, 인덱스는 데이터의 검색 속도를 높이지만, 추가적인 저장 공간을 사용하며 데이터가 변경될 때마다 업데이트되어야 하기 때문에, 쓰기 작업의 성능은 다소 저하될 수 있습니다. 따라서 인덱스는 검색이 자주 일어나는 컬럼에 신중하게 적용해야 합니다.
2. 스키마 구조 개선을 위한 정규화
스키마 구조 개선의 대표적인 예로 정규화(Normalization)를 들 수 있습니다. 정규화는 데이터베이스 스키마 설계에서 중복을 최소화하고 데이터의 무결성을 유지하기 위해 데이터를 구조화하는 과정입니다. 이 과정을 통해 각 데이터 항목은 적절한 테이블에 한 번만 저장되며, 이로 인해 데이터의 일관성과 유지보수가 용이해집니다.
정규화를 통해 여러 가지 이점을 얻을 수 있습니다:
- 중복 제거: 데이터의 중복을 제거하여 저장 공간을 절약하고, 데이터 무결성을 향상시킵니다.
- 데이터 무결성 유지: 각 데이터 항목이 한 곳에만 저장되어, 데이터 수정 시 일관성을 유지할 수 있습니다.
- 쿼리 성능 개선: 중복 데이터를 줄이면 쿼리 실행 시 필요한 데이터 양이 줄어들어 성능이 개선될 수 있습니다.
하지만 과도한 정규화는 쿼리의 복잡성을 증가시키고 성능 저하를 야기할 수 있으므로, 적절한 수준에서의 정규화가 필요합니다. 데이터베이스의 사용 목적과 성능 요구사항을 고려하여 적절한 수준의 정규화를 결정하는 것이 중요합니다.
3. 트랜잭션 관리
트랜잭션 관리는 데이터베이스 시스템에서 안정성과 일관성을 유지하는 데 중요한 역할을 합니다. 트랜잭션은 여러 데이터베이스 작업을 하나의 논리적 단위로 묶는 것을 말하며, 이를 통해 모든 작업이 완전히 수행되거나 아무 것도 수행되지 않도록 보장합니다.
트랜잭션 관리를 통한 쿼리 최적화의 주요 측면은 다음과 같습니다.
- 트랜잭션의 크기 조정: 너무 많은 작업을 하나의 트랜잭션에 포함시키면 시스템의 성능에 부정적인 영향을 미칠 수 있습니다. 반대로, 작은 단위로 트랜잭션을 나누면 성능을 향상시킬 수 있습니다.
- 동시성 제어: 여러 트랜잭션이 동시에 실행될 때, 데이터의 정합성을 유지하면서 성능을 최적화하는 것이 중요합니다. 이를 위해 락(lock) 메커니즘, 낙관적(optimistic) 또는 비관적(pessimistic) 동시성 제어 등의 전략을 사용할 수 있습니다.
- 트랜잭션 격리 수준: 격리 수준(isolation level)을 조정하여 트랜잭션 간의 간섭을 최소화하고 성능을 최적화할 수 있습니다. 격리 수준을 낮추면 동시성은 높아지지만, 데이터 무결성 문제가 발생할 수 있습니다.
- 커밋과 롤백 전략: 적절한 시점에 트랜잭션을 커밋(commit)하거나 롤백(rollback)하는 전략도 중요합니다. 이는 데이터 무결성을 유지하면서 트랜잭션의 성능을 최적화하는 데 도움이 됩니다.
따라서, 트랜잭션 관리는 데이터베이스의 전체적인 성능과 안정성을 유지하는 데 중요한 요소이며, 쿼리 최적화 과정에서 핵심적인 부분을 차지합니다.
▼ 이전 진행한 프로젝트들 ▼
'내일배움캠프 > 축구팀 관리 프로젝트' 카테고리의 다른 글
축구팀 관리 프로젝트 14일차 - 전술 설정 화면, nestjs cron 사용 (1) | 2024.01.26 |
---|---|
축구팀 관리 프로젝트 13일차 - 포메이션 관리 화면, ts(2339) 오류 (1) | 2024.01.25 |
최종프로젝트 11일차 - 경기장 예약 완료, ORM 쿼리 복잡해질 때 (1) | 2024.01.23 |
최종프로젝트 10일차 - 경기장 예약 화면 작성 중, 좌표에서 주소 변환 (0) | 2024.01.21 |
최종프로젝트 9일차 - 리엑트 공부 시작, 리엑트 부트스트랩 사용 (1) | 2024.01.20 |