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

[송지민] 과제 제출 #5

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
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
11 changes: 0 additions & 11 deletions .env.example

This file was deleted.

43 changes: 29 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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/swagger": "^7.1.16",
"@nestjs/typeorm": "^10.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"dotenv": "^16.3.1",
"mysql2": "^3.6.1",
"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"
Expand Down
5 changes: 5 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
import { addTransactionalDataSource } from 'typeorm-transactional';
import { DataSource } from 'typeorm';
import { UsersModule } from './domain/users/users.module';

@Module({
imports: [
Expand All @@ -17,6 +18,9 @@ import { DataSource } from 'typeorm';
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
synchronize: process.env.DB_SYNC === 'true',
entities: ['dist/**/*.entity{.ts,.js}'], // [Item]
migrations: [__dirname + '/migrations/**/*{.ts,.js}'],
migrationsRun: false,
timezone: 'Z',
};
},
Expand All @@ -28,6 +32,7 @@ import { DataSource } from 'typeorm';
return addTransactionalDataSource(new DataSource(options));
},
}),
UsersModule,
],
controllers: [],
providers: [],
Expand Down
19 changes: 19 additions & 0 deletions src/domain/users/dto/auth-codes.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IsNotEmpty, IsString } from 'class-validator';
import { Expose } from 'class-transformer';
import { ApiProperty } from '@nestjs/swagger';

export class AuthCodeDto {
@IsString()
@IsNotEmpty()
@ApiProperty({ description: 'phoneNumber' })
readonly phoneNumber: string;
@IsNotEmpty()
@ApiProperty({ description: 'code' })
readonly code: string;
}

export class ResponseAuthCodeDto {
@Expose()
@ApiProperty({ description: 'result' })
readonly result: boolean;
}
17 changes: 17 additions & 0 deletions src/domain/users/dto/users.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { IsNotEmpty, IsString, MinLength } from 'class-validator';
import { Expose } from 'class-transformer';
import { ApiProperty } from '@nestjs/swagger';

export class CreateUserDto {
@IsString()
@IsNotEmpty()
@ApiProperty({ description: 'phoneNumber' })
readonly phoneNumber: string;
}

export class ResponseUserDto {
@Expose()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expose() 데코레이터는 무엇인가요?!

@MinLength(6)
@ApiProperty({ description: 'code' })
code: string;
}
28 changes: 28 additions & 0 deletions src/domain/users/entities/auth-code.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {
Column,
CreateDateColumn,
DeleteDateColumn,
Entity,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { User } from './user.entity';
import { ApiProperty } from '@nestjs/swagger';

@Entity({ name: 'AUTHCODE' })
export class AuthCode {
@PrimaryGeneratedColumn({ name: 'id' })
@ApiProperty({ description: 'id' })
id: number;
@Column()
@ApiProperty({ description: 'code' })
code: string;
@ManyToOne(() => User, (user) => user.authCodes)
user: User;
@CreateDateColumn({
type: 'timestamp',
})
createdAt: Date;
@DeleteDateColumn({ type: 'timestamp' })
deletedAt?: Date | null;
}
25 changes: 25 additions & 0 deletions src/domain/users/entities/user.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
CreateDateColumn,
DeleteDateColumn,
Entity,
OneToMany,
PrimaryColumn,
} from 'typeorm';
import { AuthCode } from './auth-code.entity';
import { ApiProperty } from '@nestjs/swagger';

@Entity({ name: 'USERS' })
export class User {
@PrimaryColumn()
@ApiProperty({ description: 'phoneNumber' })
phoneNumber: string;
@OneToMany(() => AuthCode, (authCode) => authCode.user)
@ApiProperty({ description: 'authCodes' })
authCodes: AuthCode[];
@CreateDateColumn({
type: 'timestamp',
})
createdAt: Date;
@DeleteDateColumn({ type: 'timestamp' })
deletedAt?: Date | null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

보통 entity에 optional을 붙이기도 하나요?
optioanl을 붙이면 deletedAt Column이 있어도 되고 없어도 된다는 뜻이 되지 않나요?!

하지만 deletedAt Column은 User Entity에 필수로 보여서요!

}
31 changes: 31 additions & 0 deletions src/domain/users/users.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Controller, Post, Body } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto, ResponseUserDto } from './dto/users.dto';
import { AuthCodeDto, ResponseAuthCodeDto } from './dto/auth-codes.dto';
import { ApiCreatedResponse, ApiOperation, ApiTags } from '@nestjs/swagger';

@Controller('api/v1/users')
@ApiTags('유저 API')
export class UsersController {
constructor(private readonly usersService: UsersService) {}

@Post()
@ApiOperation({ summary: '전화번호 등록 API ' })
@ApiCreatedResponse({
description: '전회번호를 등록한다.',
type: ResponseUserDto,
})
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}

@Post('auth')
@ApiOperation({ summary: '인증 API ' })
@ApiCreatedResponse({
description: '전회번호로 발송된 인증번호를 통해 인증을 진행한다.',
type: ResponseAuthCodeDto,
})
auth(@Body() authCodeDto: AuthCodeDto) {
return this.usersService.auth(authCodeDto);
}
}
13 changes: 13 additions & 0 deletions src/domain/users/users.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { AuthCode } from './entities/auth-code.entity';

@Module({
imports: [TypeOrmModule.forFeature([User, AuthCode])],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
82 changes: 82 additions & 0 deletions src/domain/users/users.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
BadRequestException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { CreateUserDto, ResponseUserDto } from './dto/users.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Between, Repository } from 'typeorm';
import { User } from './entities/user.entity';
import { AuthCode } from './entities/auth-code.entity';
import { plainToInstance } from 'class-transformer';
import { AuthCodeDto, ResponseAuthCodeDto } from './dto/auth-codes.dto';

@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
@InjectRepository(AuthCode)
private authCodeRepository: Repository<AuthCode>,
) {}

async create(create: CreateUserDto) {
const phoneNumber = create.phoneNumber;
const user = this.userRepository.create({
phoneNumber: phoneNumber,
});
await this.userRepository.save(user);

console.log(user);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 자주 까먹는데 디버깅용 console.log() 삭제하는 습관을 들이면 좋을 것 같아요👍

const authCode = await this.authCodeRepository.findOne({
where: { user: user },
});

if (authCode) this.authCodeRepository.softDelete(authCode.id);
const newAuthCode = this.authCodeRepository.create({
user: user,
code: String(Math.floor(Math.random() * 1000000)).padStart(6, '0'),
});
await this.authCodeRepository.save(newAuthCode);

return plainToInstance(ResponseUserDto, newAuthCode, {
excludeExtraneousValues: true,
});
}

async auth(auth: AuthCodeDto) {
const phoneNumber = auth.phoneNumber;
const code = auth.code;

const currentDate = new Date();
const fiveMinuteAgo = new Date();
fiveMinuteAgo.setMinutes(currentDate.getMinutes() - 5);
const userData = await this.userRepository.findOne({
where: { phoneNumber: phoneNumber },
});
const user = this.userRepository.create({
phoneNumber: userData.phoneNumber,
});

if (!user)
throw new NotFoundException('등록되어 있지 않은 전화번호 입니다.');
const test = await this.authCodeRepository.find({ where: { user: user } });
console.log(test);
const authCode = await this.authCodeRepository.findOne({
where: { user: user, createdAt: Between(fiveMinuteAgo, currentDate) },
});
if (!authCode) {
throw new NotFoundException(
'인증번호가 없습니다. 인증 요청 후 다시 시도해주세요.',
);
}
if (code != authCode.code) {
throw new BadRequestException(
'잘못된 인증번호 입니다. 정확한 인증번호로 다시 요청해주세요.',
);
}

await this.authCodeRepository.softDelete(authCode.id);
return plainToInstance(ResponseAuthCodeDto, { result: true });
}
}
11 changes: 10 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { initializeTransactionalContext } from 'typeorm-transactional';
import { ValidationPipe } from '@nestjs/common';
import { setupSwagger } from './util/util.swagger';

async function bootstrap() {
initializeTransactionalContext();

const app = await NestFactory.create(AppModule);
// TODO: 프로그램 구현
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
setupSwagger(app);
await app.listen(process.env.PORT || 8000);

console.log(`Application is running on: ${await app.getUrl()}`);
Expand Down
Loading