Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactor] play 모듈 msa를 위해 분리 #9

Draft
wants to merge 6 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@web08-booquiz/shared": "workspace:*",
"@nestjs/axios": "^3.1.3",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.3.0",
"@nestjs/core": "^10.0.0",
Expand All @@ -31,6 +31,7 @@
"@nestjs/swagger": "^8.0.5",
"@nestjs/typeorm": "^10.0.2",
"@nestjs/websockets": "^10.4.7",
"@web08-booquiz/shared": "workspace:*",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"cookie": "^1.0.1",
Expand Down
9 changes: 7 additions & 2 deletions apps/backend/src/play/play.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { PlayService } from './play.service';
import { PlayGateway } from './play.gateway';
import { QuizZoneModule } from '../quiz-zone/quiz-zone.module';
import { ChatModule } from 'src/chat/chat.module';

import {HttpModule} from "@nestjs/axios";
import {QuizZoneClient} from "./repository/quiz-zone-client";
@Module({
imports: [QuizZoneModule, ChatModule],
imports: [QuizZoneModule, ChatModule, HttpModule],
providers: [
PlayGateway,
{
Expand All @@ -17,6 +18,10 @@ import { ChatModule } from 'src/chat/chat.module';
useValue: new Map(),
},
PlayService,
{
provide: 'QuizZoneClient',
useClass: QuizZoneClient,
}
],
})
export class PlayModule {}
36 changes: 19 additions & 17 deletions apps/backend/src/play/play.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@ import { RuntimeException } from '@nestjs/core/errors/exceptions';
import { clearTimeout } from 'node:timers';
import { Player } from '../quiz-zone/entities/player.entity';
import { CurrentQuizResultDto } from './dto/current-quiz-result.dto';
import {IQuizZoneClient} from "./repository/quiz-zone-client.interface";

@Injectable()
export class PlayService {
constructor(
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
private readonly quizZoneService: QuizZoneService,
// private readonly quizZoneClient: QuizZoneService,
@Inject('PlayInfoStorage') private readonly plays: Map<string, NodeJS.Timeout>,
@Inject('QuizZoneClient') private readonly quizZoneClient: IQuizZoneClient,
) {}

async joinQuizZone(quizZoneId: string, sessionId: string) {
const { players } = await this.quizZoneService.findOne(quizZoneId);
const { players } = await this.quizZoneClient.findOne(quizZoneId);

if (!players.has(sessionId)) {
throw new NotFoundException('참여하지 않은 사용자입니다.');
Expand All @@ -39,7 +41,7 @@ export class PlayService {
}

async startQuizZone(quizZoneId: string, clientId: string) {
const quizZone = await this.quizZoneService.findOne(quizZoneId);
const quizZone = await this.quizZoneClient.findOne(quizZoneId);
const { hostId, stage, players } = quizZone;

if (hostId !== clientId) {
Expand All @@ -50,7 +52,7 @@ export class PlayService {
throw new BadRequestException('이미 시작된 퀴즈존입니다.');
}

await this.quizZoneService.updateQuizZone(quizZoneId, {
await this.quizZoneClient.updateQuizZone(quizZoneId, {
...quizZone,
stage: QUIZ_ZONE_STAGE.IN_PROGRESS,
});
Expand All @@ -67,22 +69,22 @@ export class PlayService {
* @throws {NotFoundException} 퀴즈존 정보가 없을 경우 예외가 발생합니다..
*/
async playNextQuiz(quizZoneId: string, timeoutHandle: Function) {
const quizZone = await this.quizZoneService.findOne(quizZoneId);
const quizZone = await this.quizZoneClient.findOne(quizZoneId);
const { players, intervalTime } = quizZone;

const currentQuizResult = this.getCurrentQuizResult(quizZone);

const nextQuiz = await this.nextQuiz(quizZoneId);

await this.quizZoneService.updateQuizZone(quizZoneId, {
await this.quizZoneClient.updateQuizZone(quizZoneId, {
...quizZone,
players: new Map(
[...players].map(([id, player]) => [id, { ...player, state: PLAYER_STATE.WAIT }]),
),
});

setTimeout(() => {
this.quizZoneService.updateQuizZone(quizZoneId, {
this.quizZoneClient.updateQuizZone(quizZoneId, {
...quizZone,
players: new Map(
[...players].map(([id, player]) => [
Expand Down Expand Up @@ -134,7 +136,7 @@ export class PlayService {
* @throws {NotFoundException} 모든 퀴즈가 이미 진행되었을 경우 예외가 발생합니다.
*/
private async nextQuiz(quizZoneId: string): Promise<CurrentQuizDto> {
const quizZone = await this.quizZoneService.findOne(quizZoneId);
const quizZone = await this.quizZoneClient.findOne(quizZoneId);
quizZone.currentQuizIndex++;

const { quizzes, currentQuizIndex, intervalTime } = quizZone;
Expand All @@ -159,7 +161,7 @@ export class PlayService {
}

async finishQuizZone(quizZoneId: string) {
const quizZone = await this.quizZoneService.findOne(quizZoneId);
const quizZone = await this.quizZoneClient.findOne(quizZoneId);
quizZone.stage = QUIZ_ZONE_STAGE.RESULT;
return [...quizZone.players.values()].map((player) => player.id);
}
Expand All @@ -169,7 +171,7 @@ export class PlayService {
* @param quizZoneId - 퀴즈 존 ID.
*/
private async quizTimeOut(quizZoneId: string) {
const quizZone = await this.quizZoneService.findOne(quizZoneId);
const quizZone = await this.quizZoneClient.findOne(quizZoneId);
const { players } = quizZone;

players.forEach((player) => {
Expand All @@ -193,7 +195,7 @@ export class PlayService {
* @throws {BadRequestException} 답변을 제출할 수 없는 경우 예외가 발생합니다.
*/
async submit(quizZoneId: string, clientId: string, submittedQuiz: SubmittedQuiz) {
const quizZone = await this.quizZoneService.findOne(quizZoneId);
const quizZone = await this.quizZoneClient.findOne(quizZoneId);
const { stage, players } = quizZone;

if (stage !== QUIZ_ZONE_STAGE.IN_PROGRESS) {
Expand Down Expand Up @@ -291,7 +293,7 @@ export class PlayService {
* @returns 퀴즈 결과 요약 DTO를 포함한 Promise
*/
async summaryQuizZone(quizZoneId: string, socketConnectTime: number = 30 * 1000) {
const quizZone = await this.quizZoneService.findOne(quizZoneId);
const quizZone = await this.quizZoneClient.findOne(quizZoneId);
const { players, quizzes } = quizZone;

this.clearQuizZoneHandle(quizZoneId);
Expand Down Expand Up @@ -319,7 +321,7 @@ export class PlayService {
}

public clearQuizZone(quizZoneId: string) {
this.quizZoneService.clearQuizZone(quizZoneId);
this.quizZoneClient.clearQuizZone(quizZoneId);
}

private calculateQuizRanks(
Expand Down Expand Up @@ -432,7 +434,7 @@ export class PlayService {
}

async leaveQuizZone(quizZoneId: string, clientId: string) {
const quizZone = await this.quizZoneService.findOne(quizZoneId);
const quizZone = await this.quizZoneClient.findOne(quizZoneId);
const { stage, hostId, players } = quizZone;

const isHost = hostId === clientId;
Expand All @@ -442,7 +444,7 @@ export class PlayService {
}

if (isHost) {
await this.quizZoneService.clearQuizZone(quizZoneId);
await this.quizZoneClient.clearQuizZone(quizZoneId);
} else {
players.delete(clientId);
}
Expand All @@ -454,7 +456,7 @@ export class PlayService {
}

async chatQuizZone(clientId: string, quizZoneId: string) {
const quizZone = await this.quizZoneService.findOne(quizZoneId);
const quizZone = await this.quizZoneClient.findOne(quizZoneId);
const { players } = quizZone;

if (players.get(clientId).state === PLAYER_STATE.PLAY) {
Expand All @@ -481,7 +483,7 @@ export class PlayService {
}

async changeNickname(quizZoneId: string, clientId: string, changedNickname: string) {
const quizZone = await this.quizZoneService.findOne(quizZoneId);
const quizZone = await this.quizZoneClient.findOne(quizZoneId);
const { players } = quizZone;

if (!players.has(clientId)) {
Expand Down
10 changes: 10 additions & 0 deletions apps/backend/src/play/repository/quiz-zone-client.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { QuizZone } from "src/quiz-zone/entities/quiz-zone.entity";

export interface IQuizZoneClient {

findOne(quizZoneId: string): Promise<QuizZone>;

clearQuizZone(quizZoneId: string): Promise<void>;

updateQuizZone(quizZoneId: string, quizZone: QuizZone): Promise<void>;
}
35 changes: 35 additions & 0 deletions apps/backend/src/play/repository/quiz-zone-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Injectable } from "@nestjs/common";
import { firstValueFrom } from 'rxjs';
import {IQuizZoneClient} from "./quiz-zone-client.interface";
import { QuizZone } from "src/quiz-zone/entities/quiz-zone.entity";
import {HttpService} from "@nestjs/axios";

@Injectable()
export class QuizZoneClient implements IQuizZoneClient {
private readonly baseUrl: string ="http://url"; // TODO: url 수정 필요

constructor(
private readonly httpService: HttpService,
) {
// this.baseUrl = this.configService.get<string>('QUIZ_ZONE_SERVER_URL');
}

async findOne(quizZoneId: string): Promise<QuizZone> {
const { data } = await firstValueFrom(
this.httpService.get<QuizZone>(`${this.baseUrl}/quiz-zone/${quizZoneId}`)
);
return data;
}

async clearQuizZone(quizZoneId: string): Promise<void> {
await firstValueFrom(
this.httpService.delete(`${this.baseUrl}/quiz-zone/${quizZoneId}`)
);
}

async updateQuizZone(quizZoneId: string, quizZone: QuizZone): Promise<void> {
await firstValueFrom(
this.httpService.patch(`${this.baseUrl}/quiz-zone/${quizZoneId}`, quizZone)
);
}
}
2 changes: 2 additions & 0 deletions apps/frontend/vite.config.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare const _default: import("vite").UserConfigFnObject;
export default _default;
25 changes: 25 additions & 0 deletions apps/play/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
61 changes: 61 additions & 0 deletions apps/play/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# compiled output
/dist
/node_modules
/build

:booquiz
:memory

# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# OS
.DS_Store

# Tests
/coverage
/.nyc_output

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# temp directory
.temp
.tmp

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

:booquiz
4 changes: 4 additions & 0 deletions apps/play/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}
Loading