본문 바로가기

프로젝트 개발

23.12.18 (유저모델 수정, 레시피모델 추가)

 

일단 유저 모델에 이메일 키,  추가

import { IsEmail, IsNotEmpty } from 'class-validator';
import {
  BaseEntity,
  Column,
  Entity,
  PrimaryGeneratedColumn,
  Unique,
} from 'typeorm';
export enum UserRole {
  ADMIN = 'admin',
  User = 'user',
}

@Entity()
@Unique(['username'])
export class User extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  @IsNotEmpty()
  password: string;

  @Column({
    type: 'enum',
    enum: UserRole,
    default: UserRole.User,
  })
  role: UserRole;

  @Column()
  @IsEmail()
  @IsNotEmpty()
  email: string;
}

 

그에 따른 dto 파일에 이메일 키 추가

import {
  IsEmail,
  IsString,
  Matches,
  MaxLength,
  MinLength,
} from 'class-validator';

export class AuthCredentialsDto {
  @IsString()
  @MinLength(4)
  @MaxLength(20)
  username: string;

  @IsString()
  @MinLength(6)
  @MaxLength(20)
  @Matches(/^[a-zA-Z0-9]*$/, {
    message: 'password only accepts english and number',
  })
  password: string;

  @IsString()
  @MaxLength(60)
  @IsEmail()
  email: string;
}

 

레포지토리에 이메일 추가 + 코드리뷰 반영해서 save 간략하게 변경

import { DataSource, Repository } from 'typeorm';
import { User } from './user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { AuthCredentialsDto } from './dto/auth-credential.dto';
import {
  ConflictException,
  InternalServerErrorException,
} from '@nestjs/common';
import * as bcrypt from 'bcryptjs';

export class UserRepository extends Repository<User> {
  constructor(@InjectRepository(User) private dataSource: DataSource) {
    super(User, dataSource.manager);
  }

  async createUser(authCredentialsDto: AuthCredentialsDto): Promise<User> {
    const { username, password, email } = authCredentialsDto;

    const salt = await bcrypt.genSalt();
    const hashedPassword = await bcrypt.hash(password, salt);

    const user = this.create({ username, password: hashedPassword, email });

    try {
      return await this.save(user);
    } catch (error) {
      if (error.code === 'ER_DUP_ENTRY') {
        throw new ConflictException('Duplicate error');
      } else {
        throw new InternalServerErrorException();
      } // 유효성 검사 중 문제 발생 -> 500에러 처리방법
    }
  }
}

 

 

관계 설정

@JoinColumn() - fk임을 명시해 관계 형성함 > 해당 엔티티에서 fk키를 통해 join할 수 있도록 해줌

>>다른 모델로 조인할 때 기준이 되는 모델에 해당 데코레이션 설정하기

 

1:1 

@OneToOne(() => 관계를 맺는 모델 이름)   //인수로 익명함수를 작성해 넘겨줌

해당모델: 해당모델;

 

1:m

@ManyToOne(() => 해당 모델) //many에 해당하는 테이블에 joincolumn 생략 가능

해당 모델에는 1 의 관계에 해당하는 모델을 적어줌

 

n:m

@ManyToMany(() => 해당모델, (해당모델) => 해당모델.기준모델)

해당모델: 해당모델[];

m에 해당하는 모델에는 @ManyToMany 를 적되 해당 모델을 반대로 적음

@joinTable()은기준이 되는 한 테이블에만 적으면 됨 / 중간 테이블을 자동으로 작성해줌

 

 

 

유저 - 레시피와 1:n관계
하위카테고리 - 레시피와 n:1관계 > 카테고리 하나당 상품 여러개
상위카테고리 - 하위 카테고리와 1:n관계 > 상위 하나당 하위 여러개
스페셜카테고리 - 
리뷰 - 유저랑 1:n관계
찜 - 유저랑 1:n관계 / 레시피와 1:n 관계  
파일 - 사진과 1:n관계 
사진 - 파일과 n:1 관계 / 레시피와 n:m 관계

 

 

 

레시피 모델

 

<레시피 모듈>

import { Module } from '@nestjs/common';
import { RecipeController } from './recipe.controller';
import { RecipeService } from './recipe.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Recipe } from './recipe.entity';
import { AuthModule } from 'src/auth/auth.module';
import { RecipeRepository } from './recipe.repository';

@Module({
  imports: [TypeOrmModule.forFeature([Recipe]), AuthModule],
  controllers: [RecipeController],
  providers: [RecipeService, RecipeRepository],
})
export class RecipeModule {}

 

<레시피 레포지터리>

import { DataSource, Repository } from 'typeorm';
import { Recipe } from './recipe.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { CreateRecipeDto } from './dto/create-recipe.dto';

export class RecipeRepository extends Repository<Recipe> {
  constructor(@InjectRepository(Recipe) private dataSource: DataSource) {
    super(Recipe, dataSource.manager);
  }
  async createRecipe(createRecipeDto: CreateRecipeDto): Promise<Recipe> {
    const {
      recipeName,
      img,
      portion,
      leadTime,
      level,
      ingredient,
      ingredientUnit,
      aveStar,
    } = createRecipeDto;

    const recipe = this.create({
      recipeName,
      img,
      portion,
      leadTime,
      level,
      ingredient,
      ingredientUnit,
      aveStar,
    });

    await this.save(recipe);
    return recipe;
  }
}

 

<레시피 dto>

import { IsNumber, IsString } from 'class-validator';

export class CreateRecipeDto {
  @IsString()
  recipeName: string;

  @IsString()
  img: string;

  @IsNumber()
  portion: number;

  @IsNumber()
  leadTime: number;

  @IsNumber()
  level: number;

  @IsString()
  ingredient: string;

  @IsString()
  ingredientUnit: string;

  @IsNumber()
  aveStar: number;
}

 

<레시피 엔티티>

import { IsNotEmpty, IsString } from 'class-validator';
import { User } from 'src/auth/user.entity';
import {
  BaseEntity,
  Column,
  Entity,
  ManyToMany,
  PrimaryGeneratedColumn,
} from 'typeorm';

@Entity()
export class Recipe extends BaseEntity {
  @PrimaryGeneratedColumn()
  recipeId: number;

  @Column()
  @IsNotEmpty()
  @IsString()
  recipeName: string;

  @Column()
  img: string;

  @Column()
  portion: number;

  @Column()
  leadTime: number;

  @Column()
  level: number;

  @Column()
  ingredient: string; //배열

  @Column()
  ingredientUnit: string; //g, 개

  @Column()
  aveStar: number;

  @ManyToMany(() => User, (user) => user.recipes)
  user: User;
}

 

<레시피 컨트롤러>

import {
  Body,
  Controller,
  Get,
  Param,
  ParseIntPipe,
  Post,
  Delete,
} from '@nestjs/common';
import { RecipeService } from './recipe.service';
import { Recipe } from './recipe.entity';
import { CreateRecipeDto } from './dto/create-recipe.dto';

@Controller('recipe')
export class RecipeController {
  constructor(private recipeService: RecipeService) {}

  @Get('/')
  getAllTask(): Promise<Recipe[]> {
    return this.recipeService.getAllRecipe();
  }

  @Post()
  createRecipe(@Body() createRecipeDto: CreateRecipeDto): Promise<Recipe> {
    return this.recipeService.createRecipe(createRecipeDto);
  }
  @Get('/:recipeId')
  getRecipeById(@Param('recipeId') recipeId: number): Promise<Recipe> {
    return this.recipeService.getRecipeById(recipeId);
  }
  @Delete('/:recipeId')
  deleteRecipe(@Param('recipeId', ParseIntPipe) recipeId): Promise<void> {
    return this.recipeService.deleteRecipe(recipeId);
  }
}

 

<레시피 서비스>

import { Injectable, NotFoundException } from '@nestjs/common';
import { RecipeRepository } from './recipe.repository';
import { Recipe } from './recipe.entity';
import { CreateRecipeDto } from './dto/create-recipe.dto';

@Injectable()
export class RecipeService {
  constructor(private recipeRepository: RecipeRepository) {}

  async getAllRecipe(): Promise<Recipe[]> {
    return this.recipeRepository.find();
  }
  createRecipe(createRecipeDto: CreateRecipeDto): Promise<Recipe> {
    return this.recipeRepository.createRecipe(createRecipeDto);
  }
  async getRecipeById(recipeId: number): Promise<Recipe> {
    const found = await this.recipeRepository.findOneBy({ recipeId });

    if (!found) {
      throw new NotFoundException(
        `recipeId가 ${recipeId}인 레시피를 찾을 수 없습니다`,
      );
    }
    return found;
  }
  async deleteRecipe(recipeId: number): Promise<void> {
    const result = await this.recipeRepository.delete(recipeId);

    if (result.affected === 0) {
      throw new NotFoundException(`Can't find Recipe with id ${recipeId}`);
    }
  }
}

 

 

내일 update쪽 > @Path 만들기

 

 

 

#엘리스트랙 #엘리스트랙후기 #리액트네이티브강좌 #온라인코딩부트캠프 #온라인코딩학원 #프론트엔드학원 #개발자국비지원 #개발자부트캠프 #국비지원부트캠프 #프론트엔드국비지원 #React #Styledcomponent #React Router Dom #Redux #Typescript #Javascript