ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • typeorm 기본 구조 만들기 (3) - repository, service, controller
    공부하기/node.js 2022. 1. 24. 15:34

    app.ts, index.ts에 이어서 기본 entity를 만들어보는 게시글입니다.

    기본 구조에서 설명되지 않는 코드는 예제 코드에서 확인하시기 바랍니다~
    예제 git : https://github.com/kboysm/typeorm_study

     

    GitHub - kboysm/typeorm_study: typeorm 탐색 및 프로젝트에 사용할 수 있는지 여부 테스트

    typeorm 탐색 및 프로젝트에 사용할 수 있는지 여부 테스트. Contribute to kboysm/typeorm_study development by creating an account on GitHub.

    github.com

    1. 예제로 사용될 엔티티를 제작합니다. (User.ts, UserInfo.ts, index.ts)

    // src/entity/ User.ts
    import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn,} from "typeorm";
    @Entity("user_info") // 테이블명
    export class UserInfo { 
      @PrimaryGeneratedColumn()
      id: number;
      @Column({ type: "varchar", length: 150 })
      address: string;
      @Column({ type: "varchar", length: 25 })
      name: string;
      @Column()
      age: number;
    }
    // src/entity/ UserInfo.ts
    import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn,} from "typeorm";
    @Entity("user_info")
    export class UserInfo {
      @PrimaryGeneratedColumn()
      id: number;
      @Column({ type: "varchar", length: 150 })
      address: string;
      @Column({ type: "varchar", length: 25 })
      name: string;
      @Column()
      age: number;
    }
    // src/entity/index.ts

    import { User } from "./User";
    import { UserInfo } from "./UserInfo";
    export { User, UserInfo };

    2.  EntityRepository를 생성합니다.

    // src/repository/UserQueryRepo.ts
    import { createQueryBuilder, EntityRepository } from "typeorm";
    import { Service } from "typedi";
    import { User } from "../entity";
    import { PageReq } from "../api/PageReq";

    @Service()
    @EntityRepository(User)
    export class UserQueryRepo {
    findAll(param: PageReq) {
    return (
    createQueryBuilder("user") // 테이블명
    .leftJoinAndSelect("User.userInfo", "info") // leftJoinAndSelect("엔티티클래스명.프로퍼티","해당 쿼리에서 사용될 이름")
    .skip(param.getOffset())
    .take(param.getLimit())
    .getManyAndCount() );
    }
    findOne(id: number) {
    return
    createQueryBuilder("user")
    .where("User.id = :id", { id })
    .getOne();
    }
    // .addSelect("user.password")가 있다는건 비밀번호 비교용 함수, 외부노출 api에 사용금지
    findByEmail(email: string) {
    return
      createQueryBuilder()
      .select("user")
      .addSelect("user.password")
      .from(User, "user")
      .where("email = :email", { email })
      .getOne();
    }
    // create(paramObj: UserDto) { // return createQueryBuilder().insert().into(User).values(paramObj).execute(); // }   
      // update(paramObj: UserDto, id: number) {
      // return createQueryBuilder()
      // .update(User)
      // .set(paramObj)
      // .where("id = :id", { id })
      // .execute();
      //
    }
      delete(id: number) {
        return createQueryBuilder()
        .delete()
        .from(User)
        .where("id = :id", { id })
        .execute();
      }
    }

    3. service를 제작합니다.

    import { Service } from "typedi";
    import { InjectRepository } from "typeorm-typedi-extensions";
    import { PageReq } from "../api/PageReq";
    import { UserQueryRepo } from "../repository/UserQueryRepo";
    import { User, UserInfo } from "../entity";
    import { UserDto } from "../dto";
    import { EntityManager, Transaction, TransactionManager } from "typeorm";
    import { PageResList } from "../api/PageResList";
    import { PageResObj } from "../api/PageResObj";

    @Service()
    export class UserService {

    constructor( @InjectRepository() readonly userQueryRepo: UserQueryRepo ) {}

    async findAll(param: PageReq): Promise<PageResList<User>> {
    const result = await this.userQueryRepo.findAll(param);
    return new PageResList<User>(
    result[1],
    param.limit,
    result[0].map((el: User) => el),
    "User 목록을 찾는데 성공했습니다" );
    }

    async findOne(id: number): Promise<PageResObj<User>> {
    const result: User = await this.userQueryRepo.findOne(id);
    return new PageResObj(result, "User를 찾는데 성공했습니다.");
    }

    @Transaction() async create(
    paramObj: UserDto,
    @TransactionManager() manager: EntityManager ): Promise<PageResObj<User>> {
    const createedUserInfo = await manager.insert(UserInfo, { ...paramObj.userInfo, });
    const createedUser = await manager.insert(User, { ...paramObj.getUser, userInfoId: createedUserInfo.identifiers[0].id, });
    const result: User = await manager.findOne( User, { id: createedUser.identifiers[0].id }, { relations: ["userInfo"] } );
    return new PageResObj(result, "User 생성에 성공했습니다.");
    }

    @Transaction() async update(
    paramObj: UserDto,
    id: number,
    @TransactionManager() manager: EntityManager ): Promise<PageResObj<User>> {
    const oldUser: User = await manager.findOne( User, { id }, { relations: ["userInfo"] } );
    if (paramObj.checkExistUser) {
    await manager.update(User, { id }, { ...paramObj.getUpdatedUser() });
    }
    if (paramObj.checkExistUserInfo) {
    await manager.update( UserInfo, { id: oldUser.userInfoId }, { ...paramObj.getUpdatedUserInfo() } );
    }
    const result: User = await manager.findOne( User, { id }, { relations: ["userInfo"] } );
    return new PageResObj(result, "User 정보 수정에 성공했습니다.");
    }

    async delete(id: number) {
    const result = await this.userQueryRepo.findOne(id);
    const { affected } = await this.userQueryRepo.delete(id);
    return new PageResObj( result, affected === 0 ? "User 삭제에 실패했습니다." : "User 삭제에 성공했습니다." ); }
    }

    4. 컨트롤러를 제작합니다

    import { UserService } from "../service//UserService";
    import {
      Body,
      Get,
      HttpCode,
      JsonController,
      Param,
      Post,
      QueryParams,
      Patch,
      Res,
      Delete,
      UseBefore,
    } from "routing-controllers";
    import { Response } from "express";
    import { Inject, Service } from "typedi";
    import { UserDto } from "../dto";
    import { PageReq, PageResObj, PageResList } from "../api";
    import { QueryFailedError } from "typeorm";
    import { CustomValidation, IdValidation } from "../class/CustomValidation";
    
    @Service()
    @JsonController("/user")
    export class UserController {
      @Inject()
      userService: UserService;
      // constructor(private userService: UserService) {}
    
      @Get("/findall")
      public async get(@QueryParams() param: PageReq, @Res() res: Response) {
        try {
          return await this.userService.findAll(param);
        } catch (err) {
          if (err instanceof QueryFailedError) {
            console.log("Instance of QueryFailedError!");
            return new PageResObj({}, err.message, true);
          }
          return new PageResObj({}, err.message, true);
        }
      }
    
      @Get("/findone/:id")
      public async getOne(@Param("id") id: number, @Res() res: Response) {
        const validationResult = new IdValidation(id, "number");
        if (!validationResult.result) {
          return validationResult.getRes();
        }
        try {
          return await this.userService.findOne(id);
        } catch (err) {
          if (err instanceof QueryFailedError) {
            console.log("Instance of QueryFailedError!");
            return new PageResObj({}, err.message, true);
          }
          return new PageResObj({}, err.message, true);
        }
      }
    
      @Post("/create")
      public async create(@Body() createDto: UserDto, @Res() res: Response) {
        const validationResult = await new CustomValidation(
          createDto
        ).checkValidation();
        if (validationResult) return validationResult;
    
        try {
          return await this.userService.create(createDto, null);
        } catch (err) {
          if (err instanceof QueryFailedError) {
            console.log("Instance of QueryFailedError!");
            return new PageResObj({}, err.message, true);
          }
          return new PageResObj({}, err.message, true);
        }
      }
    
      @Patch("/update/:id")
      public async update(
        @Body() updateDto: UserDto,
        @Param("id") id: number,
        @Res() res: Response
      ) {
        const validationResult = await new CustomValidation(
          updateDto
        ).checkUpdateDtoValidation();
        if (validationResult) return validationResult;
        const idValidationResult = new IdValidation(id, "number");
        if (!idValidationResult.result) {
          return idValidationResult.getRes();
        }
        try {
          return await this.userService.update(updateDto, id, null);
        } catch (err) {
          if (err instanceof QueryFailedError) {
            console.log("Instance of QueryFailedError!");
            return new PageResObj({}, err.message, true);
          }
          return new PageResObj({}, err.message, true);
        }
      }
      @Delete("/delete/:id")
      public async delete(@Param("id") id: number) {
        const validationResult = new IdValidation(id, "number");
        if (!validationResult.result) {
          return validationResult.getRes();
        }
        try {
          return await this.userService.delete(id);
        } catch (err) {
          if (err instanceof QueryFailedError) {
            console.log("Instance of QueryFailedError!");
            return new PageResObj({}, err.message, true);
          }
          return new PageResObj({}, err.message, true);
        }
      }
    }

    5. UserDto를 생성합니다. ( src/dto/index.ts )

    import {
      Contains,
      IsInt,
      Length,
      IsEmail,
      IsFQDN,
      IsDate,
      Min,
      Max,
      IsString,
      MinLength,
      MaxLength,
      NotContains,
      ValidateNested,
      IsEnum,
      IsNumber,
      IsUrl,
      IsBoolean,
      IsOptional,
    } from "class-validator";
    import { Type } from "class-transformer";
    class UserInfoDto {
      @IsString()
      address: string;
      @IsString()
      name: string;
      @IsInt()
      age: number;
    }
    
    class UserDto {
      @IsEmail()
      email: string;
      password: string;
      @IsOptional()
      @IsString()
      userInfoId: string;
      @ValidateNested({ each: true, message: "userInfo에러" })
      @Type(() => UserInfoDto)
      userInfo: UserInfoDto;
    
      // createService
      get getUser() {
        return {
          email: this.email,
          password: this.password,
        };
      }
    
      get checkExistUser() {
        return this.email || this.password;
      }
      get checkExistUserInfo() {
        return this.userInfo;
      }
    
      // updateService - User
      getUpdatedUser() {
        const userKey = ["email", "password"];
        const result = {};
        userKey.forEach((key) => {
          if (this[key]) result[key] = this[key];
        });
        return result;
      }
    
      // updateServuce - UserInfo
      getUpdatedUserInfo() {
        const userInfoKey = ["address", "name", "age"];
        const result = {};
        userInfoKey.forEach((key) => {
          if (this?.userInfo[key]) result[key] = this?.userInfo[key];
        });
        return result;
      }
    }
    
    export { UserDto };

     

Designed by Tistory.