diff --git a/.env.example b/.env.example deleted file mode 100644 index e084950..0000000 --- a/.env.example +++ /dev/null @@ -1,11 +0,0 @@ -# 서버 애플리케이션 포트 관련 변수입니다. -PORT=8000 - -# DB 세팅 환경 변수입니다 -DB_HOST=localhost -DB_PORT=3306 -DB_USERNAME=user -DB_PASSWORD=password -DB_DATABASE=nest_db - -DB_SYNC=true \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a31e1a7..8d05ed4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "attendance-api", + "name": "nest-phone-verify-1", "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "attendance-api", + "name": "nest-phone-verify-1", "version": "0.0.1", "license": "UNLICENSED", "dependencies": { @@ -13,14 +13,15 @@ "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", - "@nestjs/swagger": "^7.1.13", - "@nestjs/typeorm": "^10.0.0", + "@nestjs/swagger": "^7.1.16", + "@nestjs/typeorm": "^10.0.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "dotenv": "^16.3.1", - "mysql2": "^3.6.1", + "mysql2": "^3.6.4", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", + "swagger-ui-express": "^5.0.0", "ts-mockito": "^2.6.1", "typeorm": "^0.3.17", "typeorm-transactional": "^0.4.1" @@ -1578,9 +1579,9 @@ } }, "node_modules/@nestjs/mapped-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.2.tgz", - "integrity": "sha512-V0izw6tWs6fTp9+KiiPUbGHWALy563Frn8X6Bm87ANLRuE46iuBMD5acKBDP5lKL/75QFvrzSJT7HkCbB0jTpg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.3.tgz", + "integrity": "sha512-40Zdqg98lqoF0+7ThWIZFStxgzisK6GG22+1ABO4kZiGF/Tu2FE+DYLw+Q9D94vcFWizJ+MSjNN4ns9r6hIGxw==", "peerDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", "class-transformer": "^0.4.0 || ^0.5.0", @@ -1689,15 +1690,15 @@ } }, "node_modules/@nestjs/swagger": { - "version": "7.1.13", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.1.13.tgz", - "integrity": "sha512-aHfW0rDZZKTuPVSkxutBCB16lBy5vrsHVoRF5RvPtH7U2cm4Vf+OnfhxKKuG2g2Xocn9sDL+JAyVlY2VN3ytTw==", + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.1.16.tgz", + "integrity": "sha512-f9KBk/BX9MUKPTj7tQNYJ124wV/jP5W2lwWHLGwe/4qQXixuDOo39zP55HIJ44LE7S04B7BOeUOo9GBJD/vRcw==", "dependencies": { - "@nestjs/mapped-types": "2.0.2", + "@nestjs/mapped-types": "2.0.3", "js-yaml": "4.1.0", "lodash": "4.17.21", "path-to-regexp": "3.2.0", - "swagger-ui-dist": "5.9.0" + "swagger-ui-dist": "5.9.1" }, "peerDependencies": { "@fastify/static": "^6.0.0", @@ -1747,11 +1748,11 @@ } }, "node_modules/@nestjs/typeorm": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.0.tgz", - "integrity": "sha512-WQU4HCDTz4UavsFzvGUKDHqi0MO5K47yFoPXdmh+Z/hCNO7SHCMmV9jLiLukM8n5nKUqJ3jDqiljkWBcZPdCtA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.1.tgz", + "integrity": "sha512-YVFYL7D25VAVp5/G+KLXIgsRfYomA+VaFZBpm2rtwrrBOmkXNrxr7kuI2bBBO/Xy4kKBDe6wbvIVVFeEA7/ngA==", "dependencies": { - "uuid": "9.0.0" + "uuid": "9.0.1" }, "peerDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", @@ -1761,6 +1762,18 @@ "typeorm": "^0.3.0" } }, + "node_modules/@nestjs/typeorm/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -6480,9 +6493,9 @@ "dev": true }, "node_modules/mysql2": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.1.tgz", - "integrity": "sha512-O7FXjLtNkjcMBpLURwkXIhyVbX9i4lq4nNRCykPNOXfceq94kJ0miagmTEGCZieuO8JtwtXaZ41U6KT4eF9y3g==", + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.4.tgz", + "integrity": "sha512-x27dKYnowULCLFIdrtYKKNtUIIdfWSx5JR+UE94rJT09XPPnh4w4lJLHo97HAxdZ+3zlklRZOQN68WyLOeXu7w==", "dependencies": { "denque": "^2.1.0", "generate-function": "^2.3.1", @@ -8015,9 +8028,23 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.9.0.tgz", - "integrity": "sha512-NUHSYoe5XRTk/Are8jPJ6phzBh3l9l33nEyXosM17QInoV95/jng8+PuSGtbD407QoPf93MH3Bkh773OgesJpA==" + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.9.1.tgz", + "integrity": "sha512-5zAx+hUwJb9T3EAntc7TqYkV716CMqG6sZpNlAAMOMWkNXRYxGkN8ADIvD55dQZ10LxN90ZM/TQmN7y1gpICnw==" + }, + "node_modules/swagger-ui-express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz", + "integrity": "sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } }, "node_modules/symbol-observable": { "version": "4.0.0", diff --git a/package.json b/package.json index f6f92b7..60fa162 100644 --- a/package.json +++ b/package.json @@ -24,14 +24,15 @@ "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", - "@nestjs/swagger": "^7.1.13", - "@nestjs/typeorm": "^10.0.0", + "@nestjs/swagger": "^7.1.16", + "@nestjs/typeorm": "^10.0.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "dotenv": "^16.3.1", - "mysql2": "^3.6.1", + "mysql2": "^3.6.4", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", + "swagger-ui-express": "^5.0.0", "ts-mockito": "^2.6.1", "typeorm": "^0.3.17", "typeorm-transactional": "^0.4.1" diff --git a/src/app.module.ts b/src/app.module.ts index 8309d36..c1a13ca 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -3,6 +3,8 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { ConfigModule } from '@nestjs/config'; import { addTransactionalDataSource } from 'typeorm-transactional'; import { DataSource } from 'typeorm'; +import { PhoneController } from './phone/phone-controller/phone-controller.controller'; +import {PhoneService} from './phone/phone-service/phone-service.service'; @Module({ imports: [ @@ -29,7 +31,7 @@ import { DataSource } from 'typeorm'; }, }), ], - controllers: [], - providers: [], + controllers: [PhoneController], + providers: [PhoneService], }) export class AppModule {} diff --git a/src/main.ts b/src/main.ts index 620bdfe..fe74845 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,15 +1,21 @@ import { NestFactory } from '@nestjs/core'; +import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { AppModule } from './app.module'; -import { initializeTransactionalContext } from 'typeorm-transactional'; async function bootstrap() { - initializeTransactionalContext(); - const app = await NestFactory.create(AppModule); - // TODO: 프로그램 구현 - await app.listen(process.env.PORT || 8000); - console.log(`Application is running on: ${await app.getUrl()}`); -} + const config = new DocumentBuilder() + .setTitle('Phone Verification API') + .setDescription('The phone verification API description') + .setVersion('1.0') + .addTag('phone-verification') + .build(); + + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('api', app, document); + await app.listen(3001); +} bootstrap(); + diff --git a/src/notificaiton/dto/notificaiton-response.dto.ts b/src/notificaiton/dto/notificaiton-response.dto.ts new file mode 100644 index 0000000..00650cd --- /dev/null +++ b/src/notificaiton/dto/notificaiton-response.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString, Length } from "class-validator"; + +export class CheckCodeDto { + @IsNotEmpty() + @IsString() + @Length(6, 6) + @ApiProperty({ example: '123456', description: '인증 코드' }) + code: string; +} diff --git a/src/phone/dto/GetAllVerificationsDto.ts b/src/phone/dto/GetAllVerificationsDto.ts new file mode 100644 index 0000000..a0b117a --- /dev/null +++ b/src/phone/dto/GetAllVerificationsDto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { VerifyCodeDto } from './VerifyCodeDto'; + +export class GetAllVerificationsDto { + @ApiProperty({ + type: [VerifyCodeDto], + description: '저장된 모든 전화번호와 그 인증 코드 목록' + }) + verifications: VerifyCodeDto[]; +} diff --git a/src/phone/dto/SendCodeDto.ts b/src/phone/dto/SendCodeDto.ts new file mode 100644 index 0000000..1ae0e02 --- /dev/null +++ b/src/phone/dto/SendCodeDto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsString, Matches } from 'class-validator'; + +export class SendCodeDto { + @IsNotEmpty() + @IsString() + @ApiProperty({ example: '010-1234-5678', description: '전화번호' }) + @Matches(/^010-\d{4}-\d{4}$/, { message: '잘못된 전화번호 형식입니다' }) + phoneNumber: string; +} diff --git a/src/phone/dto/VerifyCodeDto.ts b/src/phone/dto/VerifyCodeDto.ts new file mode 100644 index 0000000..f400340 --- /dev/null +++ b/src/phone/dto/VerifyCodeDto.ts @@ -0,0 +1,16 @@ +import { IsNotEmpty, IsString, Length, Matches } from 'class-validator'; +import { ApiProperty } from "@nestjs/swagger"; + +export class VerifyCodeDto { + @IsNotEmpty() + @IsString() + @ApiProperty({ example: '010-1234-5678', description: '전화번호' }) + @Matches(/^010-\d{4}-\d{4}$/, { message: '잘못된 전화번호 형식입니다' }) + phoneNumber: string; + + @IsNotEmpty() + @IsString() + @Length(6, 6, { message: '코드는 6자리 숫자여야 합니다' }) + @ApiProperty({ example: '123456', description: '코드' }) + code: string; +} diff --git a/src/phone/entity/PhoneVerification.ts b/src/phone/entity/PhoneVerification.ts new file mode 100644 index 0000000..cf45fa1 --- /dev/null +++ b/src/phone/entity/PhoneVerification.ts @@ -0,0 +1,14 @@ +import { Entity, Column } from "typeorm"; + +@Entity() +export class PhoneVerification { + @Column("전화번호") + phoneNumber: string; + + @Column("코드") + code: string; + + @Column("시간") + timestamp: Date; +} + diff --git a/src/phone/exception/CodeNotFound.ts b/src/phone/exception/CodeNotFound.ts new file mode 100644 index 0000000..3367531 --- /dev/null +++ b/src/phone/exception/CodeNotFound.ts @@ -0,0 +1,7 @@ +import { HttpException, HttpStatus } from "@nestjs/common"; + +export class CodeNotFound extends HttpException { + constructor() { + super('인증번호가 맞지 않습니다', HttpStatus.BAD_REQUEST); + } +} \ No newline at end of file diff --git a/src/phone/exception/TimeOver.ts b/src/phone/exception/TimeOver.ts new file mode 100644 index 0000000..4d4d7bd --- /dev/null +++ b/src/phone/exception/TimeOver.ts @@ -0,0 +1,7 @@ +import { HttpException, HttpStatus } from "@nestjs/common"; + +export class TimeOver extends HttpException { + constructor() { + super('인증번호 유효시간이 지났습니다', HttpStatus.BAD_REQUEST); + } +} \ No newline at end of file diff --git a/src/phone/exception/TitleNullException.ts b/src/phone/exception/TitleNullException.ts new file mode 100644 index 0000000..d36fc49 --- /dev/null +++ b/src/phone/exception/TitleNullException.ts @@ -0,0 +1,7 @@ +import { HttpException, HttpStatus } from "@nestjs/common"; + +export class TitleNullException extends HttpException { + constructor() { + super('전화번호를 입력해주세요', HttpStatus.BAD_REQUEST); + } +} \ No newline at end of file diff --git a/src/phone/phone-controller/phone-controller.controller.spec.ts b/src/phone/phone-controller/phone-controller.controller.spec.ts new file mode 100644 index 0000000..009cd6a --- /dev/null +++ b/src/phone/phone-controller/phone-controller.controller.spec.ts @@ -0,0 +1,49 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { PhoneController } from './phone-controller.controller'; +import { PhoneService } from '../phone-service/phone-service.service'; +import { SendCodeDto } from '../dto/SendCodeDto'; +import { CheckCodeDto } from '../../notificaiton/dto/notificaiton-response.dto'; + +describe('PhoneController', () => { + let controller: PhoneController; + let mockPhoneService: PhoneService; + + beforeEach(async () => { + mockPhoneService = { + sendCode: jest.fn(), + checkCode: jest.fn(), + getAllVerifications: jest.fn() + } as any; + + const module: TestingModule = await Test.createTestingModule({ + controllers: [PhoneController], + providers: [{ provide: PhoneService, useValue: mockPhoneService }], + }).compile(); + + controller = module.get(PhoneController); + }); + + describe('sendCode', () => { + it('PhoneService의 sendCode 메서드를 호출해야 함', async () => { + const sendCodeDto: SendCodeDto = { phoneNumber: '010-1234-5678' }; + await controller.sendCode(sendCodeDto); + expect(mockPhoneService.sendCode).toHaveBeenCalledWith(sendCodeDto); + }); + }); + + describe('checkCode', () => { + it('정확한 매개변수와 함께 PhoneService의 checkCode 메서드를 호출해야 함', async () => { + const phoneNumber = '010-1234-5678'; + const checkCodeDto: CheckCodeDto = { code: '123456' }; + await controller.checkCode(phoneNumber, checkCodeDto); + expect(mockPhoneService.checkCode).toHaveBeenCalledWith(phoneNumber, checkCodeDto.code); + }); + }); + + describe('getAllVerifications', () => { + it('PhoneService의 getAllVerifications 메서드를 호출해야 함', async () => { + await controller.getAllVerifications(); + expect(mockPhoneService.getAllVerifications).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/phone/phone-controller/phone-controller.controller.ts b/src/phone/phone-controller/phone-controller.controller.ts new file mode 100644 index 0000000..716054e --- /dev/null +++ b/src/phone/phone-controller/phone-controller.controller.ts @@ -0,0 +1,43 @@ +import { Body, Controller, Post, HttpCode, HttpStatus, Get, Query, Param } from "@nestjs/common"; +import { ApiOperation, ApiBody, ApiResponse, ApiQuery, ApiParam, ApiTags } from "@nestjs/swagger"; +import { VerifyCodeDto } from '../dto/VerifyCodeDto'; +import {PhoneService} from "../phone-service/phone-service.service"; +import {SendCodeDto} from "../dto/SendCodeDto"; +import { CheckCodeDto } from "../../notificaiton/dto/notificaiton-response.dto"; +@ApiTags('Phone') +@Controller('api/v1/Phone') +export class PhoneController { + constructor(private readonly phoneVerificationService: PhoneService) {} + + + @Post('/send-code') + @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Send Verification Code', description: 'Sends a verification code to the given phone number.' }) + @ApiBody({ type: SendCodeDto }) + @ApiResponse({ status: 200, description: 'Verification code sent', type: String }) + sendCode(@Body() sendCodeDto: SendCodeDto) { + return this.phoneVerificationService.sendCode(sendCodeDto); + } + + @Post('/check-code/:phoneNumber') + @ApiOperation({ summary: '인증 코드 확인', description: '제공된 전화번호에 대한 인증 코드의 유효성을 검증합니다.' }) + @ApiBody({ type: CheckCodeDto }) + @ApiParam({ name: 'phoneNumber', required: true, type: String, description: '전화번호' }) + @ApiResponse({ status: 200, description: '인증 코드 검증 결과', type: Boolean }) + checkCode(@Param('phoneNumber') phoneNumber: string, @Body() checkCodeDto: CheckCodeDto): { success: boolean, message: string } { + return this.phoneVerificationService.checkCode(phoneNumber, checkCodeDto.code); + } + + + + @Get('get-all-verifications') + @ApiOperation({ summary: 'Get all stored phone numbers and verification codes' }) + @ApiResponse({ + status: 200, + description: 'List of all stored phone numbers and verification codes', + type: [VerifyCodeDto] + }) + getAllVerifications(): VerifyCodeDto[] { + return this.phoneVerificationService.getAllVerifications(); + } +} diff --git a/src/phone/phone-service/phone-service.service.spec.ts b/src/phone/phone-service/phone-service.service.spec.ts new file mode 100644 index 0000000..5f5832d --- /dev/null +++ b/src/phone/phone-service/phone-service.service.spec.ts @@ -0,0 +1,82 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import {PhoneService} from './phone-service.service'; +import { SendCodeDto } from "../dto/SendCodeDto"; +import { TitleNullException } from "../exception/TitleNullException"; +import { exhaustiveTypeException } from "tsconfig-paths/lib/try-path"; +import { CodeNotFound } from "../exception/CodeNotFound"; +import { TimeOver } from "../exception/TimeOver"; + +describe('PhoneServiceService', () => { + let service: PhoneService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + exports: [PhoneService], + providers: [ + { + provide: PhoneService, + useValue: { + sendCode: jest.fn(), + getAllVerifications:jest.fn(), + checkCode:jest.fn(), + }, + }, + ], + }).compile(); + + service = module.get(PhoneService); + }); + + it('전화번호가 주어지면, 인증 코드를 생성하고 반환해야 함', async () => { + const mockSendCodeResult = { code: '123456' }; + jest.spyOn(service, 'sendCode').mockResolvedValue(mockSendCodeResult); + + const mockPhoneNumber = '010-1234-5678'; + const sendCodeDto: SendCodeDto = { phoneNumber: mockPhoneNumber }; + + const result = await service.sendCode(sendCodeDto); + + expect(result).toEqual(mockSendCodeResult); + }); + + + it('전화번호가 주어지지 않으면, TitleNullException을 발생시켜야 함', async () => { + const sendCodeDto: SendCodeDto = { phoneNumber: '' }; + + jest.spyOn(service, 'sendCode').mockRejectedValue(new TitleNullException()); + + await expect(service.sendCode(sendCodeDto)).rejects.toThrow(TitleNullException); + }); + + + describe('getAllVerifications',() =>{ + service.sendCode({phoneNumber:'010-1234-1234'}); + service.sendCode({phoneNumber:'010-1234-1111'}); + + const result = service.getAllVerifications(); + expect(result).toHaveLength(2); + }); + + describe('checkCode',()=>{ + it('올바른 전화번호와 코드로 인증 성공해야 함',()=>{ + service.sendCode({phoneNumber:'010-1234-1234'}); + expect(()=> service.checkCode('010-1234-1234','wrongcode')).toThrow(CodeNotFound); + }); + + it('코드 유효 시간이 지나면 TimeOver 예외를 발생시켜야 함', () => { + jest.useFakeTimers(); + + const sendCodeDto: SendCodeDto = { phoneNumber: '010-1234-1234' }; + const sentCode = service.sendCode(sendCodeDto); // 변수 이름 수정 + + const futureTime = new Date(); + futureTime.setMinutes(futureTime.getMinutes() + 5); // 오타 수정 + jest.setSystemTime(futureTime); + + // 코드 검증 시도 + expect(() => service.checkCode(sendCodeDto.phoneNumber, sentCode.code)).toThrow(TimeOver); // 변수 이름 수정 + + jest.useRealTimers(); // 테스트 후 실제 타이머로 다시 설정 + }); + }); +}); diff --git a/src/phone/phone-service/phone-service.service.ts b/src/phone/phone-service/phone-service.service.ts new file mode 100644 index 0000000..dbe9ab5 --- /dev/null +++ b/src/phone/phone-service/phone-service.service.ts @@ -0,0 +1,63 @@ +import { Injectable } from '@nestjs/common'; +import { PhoneVerification } from '../entity/PhoneVerification'; +import { VerifyCodeDto } from '../dto/VerifyCodeDto'; +import {SendCodeDto} from "../dto/SendCodeDto"; +import { TitleNullException } from "../exception/TitleNullException"; +import { CodeNotFound } from "../exception/CodeNotFound"; +import { TimeOver } from "../exception/TimeOver"; + +@Injectable() +export class PhoneService { + private readonly verifications: PhoneVerification[] = []; + private readonly waitingClients: Map void> = new Map(); + + //인증 코드 생성 + sendCode(sendCodeDto: SendCodeDto): any { + + //만약 전화번호 작성하지 않았으면 + if (!sendCodeDto.phoneNumber) { + throw new TitleNullException(); + } + const verificationCode = Math.floor(100000 + Math.random() * 900000); // 6자리 랜덤 숫자 생성 + + const verification: PhoneVerification = { //매핑 + phoneNumber: sendCodeDto.phoneNumber, + code: verificationCode.toString(), + timestamp: new Date() + }; + + this.verifications.push(verification); + return { code: verificationCode }; + } + + //모든 전화번호와 코드 확인 + getAllVerifications(): VerifyCodeDto[] { + return this.verifications.map(verification => ({ + phoneNumber: verification.phoneNumber, + code: verification.code + })); + } + + + // 인증 코드 확인 + checkCode(phoneNumber: string, code: string): { success: boolean, message: string } { + const currentTime = new Date(); // 현재 시간 + + for (const verification of this.verifications) { + if (verification.phoneNumber === phoneNumber && verification.code === code) { + // 인증번호의 생성 시간과 현재 시간의 차이가 5분 이내인지 확인 + const timeDiff = (currentTime.getTime() - verification.timestamp.getTime()) / 60000; // 분 단위로 변환 + + if (timeDiff <= 5) { // 5분 이내이면 유효 + this.waitingClients.delete(verification.phoneNumber); + return { success: true, message: "인증 성공" }; // 인증 성공 + } else { + // 5분 초과 시 + throw new TimeOver();// 코드 유효시간이 지나면 TimeOver 예외 발생 + } + } + } + // 일치하는 코드가 없으면 CodeNotFound 예외 발생 + throw new CodeNotFound(); + } +} \ No newline at end of file