Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
ba87e0b
fix: 호스트의 대기실 퇴장 처리 관련 수정
codemario318 Dec 4, 2024
ffb313d
fix: 결과 페이지에서 새로고침해도 페이지 보이도록 수정
joyjhm Dec 4, 2024
fd98cf1
test: result 반환값 변경에 따른 테스트 코드 수정
joyjhm Dec 4, 2024
8b42ef4
fix: 퀴즈존 설명 최대 길이를 500자에서 300자로 수정
typingmistake Dec 4, 2024
d338537
fix: quizZoneReducer 상태 업데이트 코드 정리
codemario318 Dec 5, 2024
b5a1af2
feat: TypeScript 컴파일러 설정에 웹 워커 지원 추가
krokerdile Dec 5, 2024
a78f6f9
feat: Vite 웹 워커 설정 추가
krokerdile Dec 5, 2024
031bdd4
feat: 타이머 웹 워커 구현
krokerdile Dec 5, 2024
ba84644
feat: 타이머 웹 워커 메시지 타입 정의 추가
krokerdile Dec 5, 2024
3c19988
refactor: 타이머 커스텀 훅 웹 워커 기반으로 개선
krokerdile Dec 5, 2024
212222d
refactor: 퀴즈 진행 컴포넌트에 타이머 훅 적용
krokerdile Dec 5, 2024
4e6415c
refactor: ProgressBar 컴포넌트 단순화 및 타이머 연동
krokerdile Dec 5, 2024
eb11ffd
feat: quizZone Controller respsonse serverTime 추가
krokerdile Dec 5, 2024
721ffd7
feat: QuizZone Type serverTime, offset 추가
krokerdile Dec 5, 2024
1170efb
feat: init 할 시에 serverTime 기반 offset 계산 로직 추가
krokerdile Dec 5, 2024
9ebc249
feat: offset 계산을 위한 now(현재 시각) 전달
krokerdile Dec 5, 2024
2a52cca
Merge branch 'develop' into feature/web-worker-time-sync
krokerdile Dec 5, 2024
aea7531
Merge branch 'develop' into fix/host-close-event
codemario318 Dec 5, 2024
ed9f55c
feat: quizZoneInfo await 적용
krokerdile Dec 5, 2024
4fd47a6
feat: NotFound Page 추가
krokerdile Dec 5, 2024
7072fe6
fix: ProgressBar 스토리 수정
krokerdile Dec 5, 2024
97e466d
fix: QuizZoneInProgress undefined 해결
krokerdile Dec 5, 2024
f3eb8da
fix: e2e 테스트 코드 수정
codemario318 Dec 5, 2024
46029dd
Merge remote-tracking branch 'origin/feature/web-worker-time-sync' in…
codemario318 Dec 5, 2024
be09b68
remove: useTimer.test 삭제
krokerdile Dec 5, 2024
613bd91
test: useValidInput 테스트 코드 추가
krokerdile Dec 5, 2024
b4ea3c5
test: useQuizZone test 추가
krokerdile Dec 5, 2024
f03794f
Merge pull request #233 from boostcampwm-2024/feature/web-worker-time…
codemario318 Dec 5, 2024
d6b9d3d
Merge branch 'develop' into fix/host-close-event
codemario318 Dec 5, 2024
d5665c4
Merge pull request #234 from boostcampwm-2024/fix/host-close-event
codemario318 Dec 5, 2024
3321d2d
chore: vite-env web Worker 설정 추가
krokerdile Dec 5, 2024
8c7b136
feat: vite.config 롤업 옵션 추가
krokerdile Dec 5, 2024
81c9163
remove: console 삭제
krokerdile Dec 5, 2024
78a09eb
Merge pull request #235 from boostcampwm-2024/feature/web-worker-vite…
codemario318 Dec 5, 2024
d65144b
fix: 퀴즈셋 검색 시 추천 퀴즈가 앞으로 오도록 수정
joyjhm Dec 5, 2024
14990cc
chore: 디렉토리 경로 clean 옵션 추가
krokerdile Dec 5, 2024
698543d
Merge pull request #237 from boostcampwm-2024/feature/vite-clean-option
joyjhm Dec 5, 2024
6107b73
feat: webWorker 쿼리 추가
krokerdile Dec 5, 2024
8bcad49
Merge pull request #238 from boostcampwm-2024/fix/webworker-query
codemario318 Dec 5, 2024
559a408
fix: worker 파일 형식 수정
codemario318 Dec 5, 2024
dc19b8a
Merge pull request #239 from boostcampwm-2024/fix/webworker-refact
joyjhm Dec 5, 2024
0447498
docs: readme 수정
joyjhm Dec 5, 2024
0c8a285
fix: webWorker 이슈로 인해 기존 useTimer로 복구
krokerdile Dec 5, 2024
073105f
fix: progress 바 사용 시 초기화 시간 로직 수정
krokerdile Dec 5, 2024
5830572
Merge pull request #236 from boostcampwm-2024/fix/search-query
codemario318 Dec 5, 2024
0109647
feat: 퀴즈존 생성 기능 개선 및 유효성 검사 추가
typingmistake Dec 5, 2024
1de9611
Merge pull request #240 from boostcampwm-2024/fix/useTimer-return-pro…
codemario318 Dec 5, 2024
c2f5df3
test: 테스트 코드 수정
joyjhm Dec 5, 2024
1e55b1a
fix: 부하 테스트 완료
codemario318 Dec 5, 2024
384a876
Merge pull request #241 from boostcampwm-2024/test/stress--test-end
codemario318 Dec 5, 2024
b90d672
chore: action 수정
joyjhm Dec 5, 2024
f9831c8
Merge pull request #242 from boostcampwm-2024/docs/readme
codemario318 Dec 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test-backend.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: backend test

on:
push:
pull_request: [develop, main]
pull_request:
branches: [develop, main]
paths:
- apps/backend/**

Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@

## 🛠 기술 스택

| 영역 | 기술 스택 |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **공통** | ![TypeScript](https://img.shields.io/badge/TypeScript-5.6.3-3178C6?style=flat-square&logo=typescript&logoColor=white) ![WebSocket](https://img.shields.io/badge/WebSocket-8.18.0-010101?style=flat-square&logo=socket.io&logoColor=white) ![TSDoc](https://img.shields.io/badge/TSDoc-0.26.11-3178C6?style=flat-square&logo=typescript&logoColor=white) |
| **Frontend** | ![React](https://img.shields.io/badge/React-18.3.1-61DAFB?style=flat-square&logo=react&logoColor=white) ![Vite](https://img.shields.io/badge/Vite-5.4.10-646CFF?style=flat-square&logo=vite&logoColor=white) ![Tailwind CSS](https://img.shields.io/badge/Tailwind%20CSS-3.4-38B2AC?style=flat-square&logo=tailwind-css&logoColor=white) ![shadcn/ui](https://img.shields.io/badge/shadcn%2Fui-latest-000000?style=flat-square) ![Vitest](https://img.shields.io/badge/Vitest-latest-6E9F18?style=flat-square&logo=vitest&logoColor=white) ![Testing Library](https://img.shields.io/badge/Testing%20Library-latest-E33332?style=flat-square&logo=testing-library&logoColor=white) ![Storybook](https://img.shields.io/badge/Storybook-8.4.2-FF4785?style=flat-square&logo=storybook&logoColor=white) |
| **Backend** | ![NestJS](https://img.shields.io/badge/NestJS-10.4.7-E0234E?style=flat-square&logo=nestjs&logoColor=white) ![MySQL](https://img.shields.io/badge/MySQL-2-4479A1?style=flat-square&logo=mysql&logoColor=white) ![SQLite](https://img.shields.io/badge/SQLite-3-003B57?style=flat-square&logo=sqlite&logoColor=white) ![TypeORM](https://img.shields.io/badge/TypeORM-0.3.20-E93524?style=flat-square&logo=typeorm&logoColor=white) ![Swagger](https://img.shields.io/badge/Swagger-8.0.5-85EA2D?style=flat-square&logo=swagger&logoColor=black) ![Jest](https://img.shields.io/badge/Jest-Testing-C21325?style=flat-square&logo=jest&logoColor=white) ![SuperTest](https://img.shields.io/badge/SuperTest-Testing-009688?style=flat-square&logo=testing-library&logoColor=white) ![Artillery](https://img.shields.io/badge/Artillery-2.0.21-CA2B2B?style=flat-square&logoColor=white) |
| **인프라** | ![NCP](https://img.shields.io/badge/Naver%20Cloud%20Platform-latest-03C75A?style=flat-square&logo=naver&logoColor=white) ![Nginx](https://img.shields.io/badge/Nginx-1.24.0-009639?style=flat-square&logo=nginx&logoColor=white) ![GitHub Actions](https://img.shields.io/badge/GitHub%20Actions-3.0-2088FF?style=flat-square&logo=github-actions&logoColor=white) ![Docker](https://img.shields.io/badge/Docker-24.0.7-2496ED?style=flat-square&logo=docker&logoColor=white) ![Linux](https://img.shields.io/badge/Linux-Ubuntu%2022.04-FCC624?style=flat-square&logo=linux&logoColor=black) |
| **협업 도구** | ![Notion](https://img.shields.io/badge/Notion-2.0.41-000000?style=flat-square&logo=notion&logoColor=white) ![Figma](https://img.shields.io/badge/Figma-latest-F24E1E?style=flat-square&logo=figma&logoColor=white) ![Excalidraw](https://img.shields.io/badge/Excalidraw-latest-6965DB?style=flat-square&logo=excalidraw&logoColor=white) ![Zoom](https://img.shields.io/badge/Zoom-5.17.0-2D8CFF?style=flat-square&logo=zoom&logoColor=white) ![Git](https://img.shields.io/badge/Git-2.42.0-F05032?style=flat-square&logo=git&logoColor=white) ![GitHub](https://img.shields.io/badge/GitHub-latest-181717?style=flat-square&logo=github&logoColor=white) ![GitHub Projects](https://img.shields.io/badge/GitHub%20Projects-latest-181717?style=flat-square&logo=github&logoColor=white) |
| 영역 | 기술 스택 |
| ------------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **공통** | ![TypeScript](https://img.shields.io/badge/TypeScript-5.6.3-3178C6?style=flat-square&logo=typescript&logoColor=white) ![WebSocket](https://img.shields.io/badge/WebSocket-8.18.0-010101?style=flat-square&logo=socket.io&logoColor=white) ![TSDoc](https://img.shields.io/badge/TSDoc-0.26.11-3178C6?style=flat-square&logo=typescript&logoColor=white) |
| **Frontend** | ![React](https://img.shields.io/badge/React-18.3.1-61DAFB?style=flat-square&logo=react&logoColor=white) ![Vite](https://img.shields.io/badge/Vite-5.4.10-646CFF?style=flat-square&logo=vite&logoColor=white) ![Tailwind CSS](https://img.shields.io/badge/Tailwind%20CSS-3.4-38B2AC?style=flat-square&logo=tailwind-css&logoColor=white) ![shadcn/ui](https://img.shields.io/badge/shadcn%2Fui-latest-000000?style=flat-square) ![Vitest](https://img.shields.io/badge/Vitest-latest-6E9F18?style=flat-square&logo=vitest&logoColor=white) ![Testing Library](https://img.shields.io/badge/Testing%20Library-latest-E33332?style=flat-square&logo=testing-library&logoColor=white) ![Storybook](https://img.shields.io/badge/Storybook-8.4.2-FF4785?style=flat-square&logo=storybook&logoColor=white) |
| **Backend** | ![NestJS](https://img.shields.io/badge/NestJS-10.4.7-E0234E?style=flat-square&logo=nestjs&logoColor=white) ![MySQL](https://img.shields.io/badge/MySQL-2-4479A1?style=flat-square&logo=mysql&logoColor=white) ![SQLite](https://img.shields.io/badge/SQLite-3-003B57?style=flat-square&logo=sqlite&logoColor=white) ![TypeORM](https://img.shields.io/badge/TypeORM-0.3.20-E93524?style=flat-square&logo=typeorm&logoColor=white) ![Swagger](https://img.shields.io/badge/Swagger-8.0.5-85EA2D?style=flat-square&logo=swagger&logoColor=black) ![Jest](https://img.shields.io/badge/Jest-Testing-C21325?style=flat-square&logo=jest&logoColor=white) ![SuperTest](https://img.shields.io/badge/SuperTest-Testing-009688?style=flat-square&logo=testing-library&logoColor=white) |
| **인프라** | ![NCP](https://img.shields.io/badge/Naver%20Cloud%20Platform-latest-03C75A?style=flat-square&logo=naver&logoColor=white) ![Nginx](https://img.shields.io/badge/Nginx-1.24.0-009639?style=flat-square&logo=nginx&logoColor=white) ![GitHub Actions](https://img.shields.io/badge/GitHub%20Actions-3.0-2088FF?style=flat-square&logo=github-actions&logoColor=white) ![Docker](https://img.shields.io/badge/Docker-24.0.7-2496ED?style=flat-square&logo=docker&logoColor=white) ![Linux](https://img.shields.io/badge/Linux-Ubuntu%2022.04-FCC624?style=flat-square&logo=linux&logoColor=black) ![Grafana](https://img.shields.io/badge/Grafana-10.2.3-F46800?style=flat-square&logo=grafana&logoColor=white) ![Prometheus](https://img.shields.io/badge/Prometheus-2.43.0-E6522C?style=flat-square&logo=prometheus&logoColor=white) ![k6](https://img.shields.io/badge/k6-0.55.0-7D64FF?style=flat-square&logo=k6&logoColor=white)|
| **협업 도구** | ![Notion](https://img.shields.io/badge/Notion-2.0.41-000000?style=flat-square&logo=notion&logoColor=white) ![Figma](https://img.shields.io/badge/Figma-latest-F24E1E?style=flat-square&logo=figma&logoColor=white) ![Excalidraw](https://img.shields.io/badge/Excalidraw-latest-6965DB?style=flat-square&logo=excalidraw&logoColor=white) ![Zoom](https://img.shields.io/badge/Zoom-5.17.0-2D8CFF?style=flat-square&logo=zoom&logoColor=white) ![Git](https://img.shields.io/badge/Git-2.42.0-F05032?style=flat-square&logo=git&logoColor=white) ![GitHub](https://img.shields.io/badge/GitHub-latest-181717?style=flat-square&logo=github&logoColor=white) ![GitHub Projects](https://img.shields.io/badge/GitHub%20Projects-latest-181717?style=flat-square&logo=github&logoColor=white) |

## 🏗 시스템 아키텍처

Expand Down Expand Up @@ -456,7 +456,7 @@ pnpm run start
└── ...
```

## 팀 소개(표인데 얘는 수정하지 말아주세요)
## 팀 소개

| [J004 강준현](https://github.com/JunhyunKang) | [J074 김현우](https://github.com/krokerdile) | [J086 도선빈](https://github.com/typingmistake) | [J175 이동현](https://github.com/codemario318) | [J217 전현민](https://github.com/joyjhm) |
| --------------------------------------------------------- | --------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------- | --------------------------------------------------------- |
Expand Down
6 changes: 6 additions & 0 deletions apps/backend/src/play/entities/quiz-summary.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Rank } from './rank.entity';

export interface QuizSummary {
readonly ranks: Rank[];
readonly endSocketTime?: number;
}
6 changes: 6 additions & 0 deletions apps/backend/src/play/entities/rank.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface Rank {
readonly id: string;
readonly nickname: string;
readonly score: number;
readonly ranking: number;
}
15 changes: 7 additions & 8 deletions apps/backend/src/play/play.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ import { SendEventMessage } from './entities/send-event.entity';
import { ClientInfo } from './entities/client-info.entity';
import { WebSocketWithSession } from '../core/SessionWsAdapter';
import { RuntimeException } from '@nestjs/core/errors/exceptions';
import { CLOSE_CODE } from '../common/constants';
import { SubmitResponseDto } from './dto/submit-response.dto';
import { clearTimeout } from 'node:timers';
import { ChatMessage } from 'src/chat/entities/chat-message.entity';
import { ChatService } from '../chat/chat.service'; // 경로 수정
import { ChatService } from '../chat/chat.service';
import { CLOSE_CODE } from '../common/constants'; // 경로 수정

/**
* 퀴즈 게임에 대한 WebSocket 연결을 관리하는 Gateway입니다.
Expand Down Expand Up @@ -248,7 +247,7 @@ export class PlayGateway implements OnGatewayInit {

const clientsIds = summaries.map(({ id }) => id);

this.clearQuizZone(clientsIds, quizZoneId, endSocketTime);
this.clearQuizZone(clientsIds, quizZoneId, endSocketTime - Date.now());
}

/**
Expand All @@ -258,16 +257,16 @@ export class PlayGateway implements OnGatewayInit {
* - 일반 플레이어가 나가면 퀴즈 존에서 나가고 다른 플레이어에게 나갔다고 알립니다.
* @param clientIds - 퀴즈존에 참여하고 있는 클라이언트 id 리스트
* @param quizZoneId - 퀴즈가 끝난 퀴즈존 id
* @param endSocketTime - 소켓 연결 종료 시간 종료 시간
* @param time - 소켓 연결 종료 시간 종료 시간
*/
private clearQuizZone(clientIds: string[], quizZoneId: string, endSocketTime: number) {
private clearQuizZone(clientIds: string[], quizZoneId: string, time: number) {
setTimeout(() => {
clientIds.forEach((id) => {
this.clearClient(id, 'finish');
});
this.playService.clearQuizZone(quizZoneId);
this.chatService.delete(quizZoneId);
}, endSocketTime - Date.now());
}, time);
}

/**
Expand All @@ -286,7 +285,7 @@ export class PlayGateway implements OnGatewayInit {

if (isHost) {
this.broadcast(playerIds, 'close');
playerIds.forEach((id) => this.clearClient(id, 'Host leave.'));
this.clearQuizZone(playerIds, quizZoneId, 0);
} else {
this.broadcast(playerIds, 'someone_leave', clientId);
this.clearClient(clientId, 'Client leave');
Expand Down
6 changes: 5 additions & 1 deletion apps/backend/src/play/play.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,14 +304,18 @@ export class PlayService {
const now = Date.now();
const endSocketTime = now + socketConnectTime;

return [...players.values()].map(({ id, score, submits }) => ({

const summaries = [...players.values()].map(({ id, score, submits }) => ({
id,
score,
submits,
quizzes,
ranks,
endSocketTime
}));

quizZone.summaries = {ranks, endSocketTime};
return summaries;
}

public clearQuizZone(quizZoneId: string) {
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/src/quiz-zone/dto/create-quiz-zone.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class CreateQuizZoneDto {

@IsInt({ message: '최대 플레이어 수가 없습니다.' })
@Min(1, { message: '최소 1명 이상이어야 합니다.' })
// @Max(300, { message: '최대 300명까지 가능합니다.' })
@Max(300, { message: '최대 300명까지 가능합니다.' })
readonly limitPlayerCount: number;

@IsNotEmpty({ message: '퀴즈존을 선택해주세요.' })
Expand Down
8 changes: 8 additions & 0 deletions apps/backend/src/quiz-zone/dto/find-quiz-zone.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { PLAYER_STATE, QUIZ_ZONE_STAGE } from '../../common/constants';
import { CurrentQuizDto } from '../../play/dto/current-quiz.dto';
import { SubmittedQuiz } from '../entities/submitted-quiz.entity';
import { ChatMessage } from 'src/chat/entities/chat-message.entity';
import { Rank } from '../../play/entities/rank.entity';
import { Quiz } from '../entities/quiz.entity';

/**
* 퀴즈 게임에 참여하는 플레이어 엔티티
Expand Down Expand Up @@ -42,4 +44,10 @@ export interface FindQuizZoneDto {
readonly currentQuiz?: CurrentQuizDto;
readonly maxPlayers?: number;
readonly chatMessages?: ChatMessage[];

readonly ranks?: Rank[];
readonly endSocketTime?: number;
readonly score?: number;
readonly quizzes?: Quiz[];
readonly submits?: SubmittedQuiz[];
}
2 changes: 2 additions & 0 deletions apps/backend/src/quiz-zone/entities/quiz-zone.entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Quiz } from './quiz.entity';
import { Player } from './player.entity';
import { QUIZ_ZONE_STAGE } from '../../common/constants';
import { QuizSummary } from '../../play/entities/quiz-summary.entity';
/**
* 퀴즈 게임을 진행하는 공간을 나타내는 퀴즈존 인터페이스
*
Expand Down Expand Up @@ -28,4 +29,5 @@ export interface QuizZone {
currentQuizStartTime: number;
currentQuizDeadlineTime: number;
intervalTime: number;
summaries?: QuizSummary;
}
3 changes: 3 additions & 0 deletions apps/backend/src/quiz-zone/quiz-zone.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,11 @@ describe('QuizZoneController', () => {
stage: 'LOBBY',
playerCount: 1,
maxPlayers: 8,
serverTime: 0,
};

Date.now = jest.fn().mockReturnValue(0);

it('퀴즈존 정보를 성공적으로 조회한다', async () => {
const session = { id: 'sessionId' };
mockQuizZoneService.getQuizZoneInfo.mockResolvedValue(mockQuizZoneInfo);
Expand Down
8 changes: 6 additions & 2 deletions apps/backend/src/quiz-zone/quiz-zone.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,16 @@ export class QuizZoneController {
@Session() session: Record<string, any>,
@Param('quizZoneId') quizZoneId: string,
) {
const quizZoneInfo = this.quizZoneService.getQuizZoneInfo(
const serverTime = Date.now();
const quizZoneInfo = await this.quizZoneService.getQuizZoneInfo(
session.id,
quizZoneId,
session.quizZoneId,
);
session['quizZoneId'] = quizZoneId;
return quizZoneInfo;
return {
...quizZoneInfo,
serverTime,
};
}
}
Loading
Loading