본 캠프 14일차 TIL

정희준·2023년 3월 31일
0
post-thumbnail

📌 오늘의 내용

Typescript Type

generic type

// 1. 문자/숫자/불린 기본타입
const getPrimitive = (arg1: string, arg2: number, arg3: boolean): [boolean, number, string] => {
  return [arg3, arg2, arg1];
};

const result = getPrimitive("철수", 123, true);

//
//
// 2. any 타입(자바스크립트랑 같음)
const getAny = (arg1: any, arg2: any, arg3: any): [any, any, any] => {
  console.log(arg1 + 100); // any는 아무거나 다 됨!
  return [arg3, arg2, arg1];
};

const result2 = getAny("철수", 123, true);

//
//
// 3. unknown 타입
const getUnknown = (arg1: unknown, arg2: unknown, arg3: unknown): [unknown, unknown, unknown] => {
  if (typeof arg1 === "number") console.log(arg1 + 100); // unknown은 들어올수는 있지만 타입이 안맞는 연산은 불가능

  return [arg3, arg2, arg1];
};

const result3 = getUnknown("철수", 123, true);

//
//
// 4. generic 타입
function getGeneric1<MyType1, MyType2, MyType3>(
  arg1: MyType1,
  arg2: MyType2,
  arg3: MyType3
): [MyType3, MyType2, MyType1] {
  return [arg3, arg2, arg1];
}

const result4 = getGeneric1("철수", 123, true);

//
//
// 4. generic 타입 -2
function getGeneric2<T1, T2, T3>(arg1: T1, arg2: T2, arg3: T3): [T3, T2, T1] {
  return [arg3, arg2, arg1];
}

const result5 = getGeneric2("철수", 123, true);

//
//
// 4. generic 타입 -3
function getGeneric3<T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] {
  return [arg3, arg2, arg1];
}

const result6 = getGeneric3("철수", 123, true);

//
//
// 4. generic 타입 - 화살표함수
const getGeneric4 = <T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] => {
  return [arg3, arg2, arg1];
};

const result7 = getGeneric4<string, number, boolean>("철수", 123, true); // 제네릭에서, 타입 명시가 필요한 상황

어제 배운 타입에서 generic 타입이 추가 되었다 얼핏 보면 any 타입과 비슷해보이지만
제너릭 타입은 들어오는 Type으로 설정이 되기 떄문에 타입스크립트 기능인 타입 추론이 가능해진다!

Generic Type은 우리가 함수를 만들어서 제공해 줄 때 안전하고 확장성 높은 코드 사용을 위해 많이 사용합니다.

  1. 배포할 라이브러리 개발시
  2. 사내 라이브러리 사용시

utility type

interface IProfile {
  name: string;
  age: number;
  school: string;
  hobby?: string;
}

// 1. Partial  
// 타입 모든 속성을 선택사항으로 바꿔주는 역할을 합니다.
type partial = Partial<IProfile>;

// 2. Required  타입 
// 모든 속성을 필수사항으로 바꿔주는 역할을 합니다. ( Partial Type과 반대 )
type required = Required<IProfile>;

// 3. Pick  타입
// 원하는 속성만을 뽑아서 사용하고 싶을 때 사용합니다.
type pick = Pick<IProfile, "name" | "age">;

// 4. Omit 타입
// 원하는 속성만 제거하여 사용하고 싶을 때 사용합니다.
type omit = Omit<IProfile, "school">;

// 5. Record  타입
/* 
Utility Type 속성을 다른 Type으로 매핑 시키고자 할 때 사용합니다.
Record<Key, Type>으로 사용하며, Key로 들어온 타입을 Type 값을 가지는 타입으로 만들 수 있습니다.
*/
type union = "철수" | "영희" | "훈이"; // Union 타입
let child1: union = "철수"; // 철수, 영희, 훈이 만 됨
let child2: string = "사과"; // 철수, 영희, 훈이, 사과, 바나나 다 됨

type record = Record<union, boolean>;
let test: record = { 철수: true, 영희: true, 훈이: false };

// 6. 객체의 key들로 Union 타입 만들기
type ggg = keyof IProfile; // "name" || "age" || "school" || "hobby"
let myProfile: ggg = "hobby";

// 7. type vs interface 차이  =>  interface는 선언병합 가능
interface IProfile {
  candy: number;
}

let profile: IProfile = {
  name: "철수",
  age: 12,
  school: "거북초",
  candy: 10,
};

// 8. 배운것 응용
let profile2: Partial<IProfile> = {
  candy: 10,
};

가장 많이 사용되는 것들로 예제 코드를 진행 해봤다 하지만
Utility Type은 위 5가지 이외에도 많은 Type들이 존재하므로
상황에 맞게 찾아보도록 하자!


Typeorm을 활용한 NestJs와 MySQL

app.module

import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BoardsModule } from './apis/boards/boards.module';
import { Board } from './apis/boards/entities/board.entity';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [
    BoardsModule,
    // PrdocuctsModule,
    // UsersModule
    ConfigModule.forRoot(),
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: 'src/commons/graphql/schema.gql',
    }),
    //forRoot 모든 api 전체 적용시켜줘
    TypeOrmModule.forRoot({
      type:
        process.env.DATABASE_TYPE === 'mysql'
          ? process.env.DATABASE_TYPE
          : 'postgres',
      host: process.env.DATABASE_HOST,
      port: Number(process.env.DATABASE_PORT),
      username: process.env.DATABASE_USERNAME,
      password: process.env.DATABASE_PASSWORD,
      database: process.env.DATABASE_DATABASE,
      entities: [Board],
      synchronize: true,
      logging: true,
    }),
  ],
})
export class AppModule {}

어제와 동일한 board 로직에 typeorm을 사용하여 mysql에 연결 하였고 env 파일을 활용하여 정보들을 숨겨주었다!

그 과정에서 필요한 라이브러리와 셋팅정보는 nestjs 공식사이트 독스와 공식 git sample을 활용하여 설치후 셋팅을 진행!
entities -> 사용할 테이블
synchronize -> Entity 와 MySQL DB에 있는 실제 저장된 컬럼들이 서로 같게 동기화 시켜주는것!
logging -> TypeORM을 통해 SQL Query문으로 만들어져셔 보이게 하는 설정

// typeorm을 사용하기 위해 설치!
$ yarn add @nestjs/typeorm typeorm mysql2
// env 변수를 사용하기위해 설치!
$ yarn add @nestjs/config

entity

// board.entity.ts
import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
@ObjectType()
export class Board {
  @PrimaryGeneratedColumn('increment')
  @Field(() => Int)
  number: number;
  @Field(() => String)
  @Column()
  writer: string;
  @Field(() => String)
  @Column()
  title: string;
  @Field(() => String)
  @Column()
  contents: string;
}

@ObjectType() -> 객체 형태의 GraphQL 타입으로
@Field() -> GraphQL Field 임을 알려주었고, 타입을 지정해주기


dto
DTO (data transfer object)
: 데이터 전송 객체. 즉, 네트워크 간에 데이터를 어떤 식으로 보낼지를 정의한 객체입니다.

// create-board.input.ts
import { Field, InputType } from '@nestjs/graphql';

@InputType()
export class CreateBoardInput {
  @Field(() => String)
  writer: string;
  @Field(() => String)
  title: string;
  @Field(() => String)
  contents: string;
}

@InputType() -> gql 설정에 InputType임을 알려주기


interface

// boards-service.interface.ts
import { CreateBoardInput } from '../dto/create-board.input';

export interface ICreateBoardInput {
  createBoardInput: CreateBoardInput;
}

boardservice에서 인터페이스가 지금은 하나만 존재하지만
실제 기능을 위핸 많은 인터페이스가 필요하기 때문에 따로 폴더를 나누어 관리!


module

import { Module } from '@nestjs/common';
import { BoardsResolver } from './boards.resolver';
import { BoardsService } from './boards.service';

@Module({
  imports: [],
  controllers: [],
  providers: [
    BoardsResolver, //
    BoardsService,
  ],
})
export class BoardsModule {}

resolver

import { Query, Resolver, Mutation, Args } from '@nestjs/graphql';
import { BoardsService } from './boards.service';
import { CreateBoardInput } from './dto/create-board.input';
import { Board } from './entities/board.entity';

@Resolver()
export class BoardsResolver {
  constructor(private readonly boardsService: BoardsService) {}

  @Query(() => [Board], { nullable: true })
  fetchBoards(): Board[] {
    return this.boardsService.findAll();
  }
  @Mutation(() => String)
  createBoard(
    // @Args('writer') writer: string,
    // @Args('title') title: string,
    // @Args({ name: 'contents', nullable: true }) contents: string,
    @Args('createBoardInput') createBoardInput: CreateBoardInput,
  ): string {
    return this.boardsService.create({ createBoardInput });
  }
}

@Arg() 안은 gql 타입이고, 그 뒤는 타입스크립트의 타입을 의미합니다.


service

import { Injectable, Scope } from '@nestjs/common';
import { Board } from './entities/board.entity';
import { ICreateBoardInput } from './interfaces/boards-service.interface';
// 인젝션-스코프 =>  1. 싱글톤(new 한 번)으로 할래?
//            =>  2. Request 스코프( 매 요청마다 new) 할래?
//            =>  3. Transient 스코프(매 주입마다 new)로 할래?
@Injectable({ scope: Scope.DEFAULT }) // 기본값 => 싱글톤
export class BoardsService {
  findAll(): Board[] {
    // 1. DB에 접속 후 , 데이터를 조회 => 데이터를 조회했다고 가정
    const result = [
      {
        number: 1,
        writer: '철수',
        title: '제목입니다~~',
        contents: '내용이에요!!',
      },
      {
        number: 2,
        writer: '영희',
        title: '영희입니다~~',
        contents: '영희에요!!',
      },
      {
        number: 3,
        writer: '훈이',
        title: '훈이입니다~~',
        contents: '훈이에요!!',
      },
    ];
    // 2. DB에서 꺼내온 결과를 브라우저에 응답(response) 주기
    return result;
  }

  create({ createBoardInput }: ICreateBoardInput): string {
    // 1. 브라우저에서 보내준 데이터 확인하기
    console.log(createBoardInput.writer);
    console.log(createBoardInput.title);
    console.log(createBoardInput.contents);

    // 2. DB에 접속 후 데이터를 저장 => 데이터를 저장했다고 가정

    // 3. DB에 저장된 결과를 브라우저에 응답(response) 주기
    return '게시글 등록에 성공하였습니다.';
  }
}

DB와 연동은 했지만 데이터조작을 아직 배우지 않아 하드코딩 후 진행!


하루를 마치며:)

어제 만들어본 게시판 로직을 typeorm을 활용하여 mysql과 연동 후 엔티티,dto,인터페이스를 만들어 보고 실행까지 시켜 보았다
아직 큰 구조를 잡고 셋팅하는 단계라 헷갈리지만 익숙해질 때까지 더 노력해보자!

profile
같이 일하고 싶은 사람이 되어보자! 다시 시작하는 개발 블로그.

0개의 댓글