(TIL) 2022-03-29

Optional Chaining

가끔 데이터를 불러올 때 존재하지 않는 값을 불러오려고 하다보면 에러가 뜨면서 코드가 중간에 멈춘적이 있다.

let object = {}
console.log(object.apple) // undefined
console.log(object.apple.price) // TypeError: Cannot read properties of undefined (reading 'price')

이전에는 &&를 사용한 단축 평가를 통해 null 또는 undefined를 확인했었다. 그래서 값이 존재하지 않더라도 코드가 멈추지는 않게되었는데 코드가 비효율적이였다.

console.log(object.apple && object.apple.price) // undefined
//좌항(object.apple) 이 null 또는 undefined 라면 undefined 를 반환하고, 그렇지 않으면 우항(object.apple.price) 을 반환한다.

그래서 도입된게 Optional Chaining 이다. "?." 앞의 평가 대상이 undefined나 null이면 평가를 멈추고 undefined를 반환하고, 그게 아니라면 값이 존재한다고 평가를 이어나간다.

console.log(object.apple.price) // TypeError: Cannot read properties of undefined (reading 'price')
console.log(object.apple?.price) // undefined

https://ko.javascript.info/optional-chaining


  • MVC패턴에 해당하는 Controller 에서, 핵심적인 비즈니스 로직에 해당하는 부분(여러 컨트롤러에서도 가져와서 사용할 수 있는 기능들)은 Service로 분류한다. (ex: DB에서 직접 데이터를 가져오거나 넣는 기능)
    이 부분은 개발 상황과 팀원들과의 조율에 따라 다양하게 바뀔 수 있다.

  • 강한 결합(Tight Coupling)

    한 클래스안에 중요한 동작을 하는 함수(메소드) 변수들이 배치되어있어서, 컨트롤러와 서비스가 서로에게 직접적으로 연관되어있는 것을 강한 결합이라고 표현한다. 강한 결합의 단점은 컨트롤러를 생성할 때마다 서비스를 new 생성자로 만들어지기 때문에 메모리의 낭비가 발생하고, 새로운 동작을 추가하게되면 항상 컨트롤러 내부를 직접 수정을 해야해서 유지 보수가 어려워진다.
    ex) 메소드 내에서 직접 어떤 기능을 가진 클래스를 생성하는 것.

  • 느슨한 결합(Loose Coupling)

    강한 결합의 상태를 개선해서 각 클래스와 객체가 서로 의존하는 부분을 줄인 형태를 느슨한 결합이라고 한다. 클래스 내부의 구조를 분할해서 종속된 메소드와 변수를 하지 않게해서 코드의 재사용성을 높힐 수 있다. 이렇게 되면 코드의 새로운 기능을 추가하거나 유지 보수하기가 수월해진다.
    ex) 외부에서 생성된 클래스를 constructor로 가져와서 this.클래스 로 사용하는 것.

  • 의존성 주입 DI (Dependency Injection)

    강한 결합을 느슨한 결합으로 전환시키는 방법을 의존성 주입이라고 한다.

  • 제어의 역전 IoC (Inversion of Control)

    의존성 주입과 같이 '의존(Dependency)'에 대한 제어권이 개발자에서 -> 프레임워크로 넘어가게 되는 것을 말한다. 프레임워크에서는 이런 의존성을 유지보수를 관리하기 쉽게 만들어주는데, 이 때 개발자는 프레임워크가 정한 틀에 맞춰가야하기 때문에 개발자가 직접 제어하는것이 아닌 프레임워크가 제어를 하게된다는 의미이다.

  • 싱글톤 패턴(Singleton Pattern)

    외부에 어떤 클래스를 한번 생성해서 여러곳에서 재사용이 가능하게 하는것을 말함. 싱글톤이면 의존성 주입이라고 할 수는 있지만, 의존성 주입은 항상 싱글톤으로 볼 수는 없다.

NestJS

  • 보일러 플레이트(초기 폴더 구조)

개발자들이 단순 노동에 가까운 셋팅 작업을 하지 않아도 되도록 만들어진 코드 템플릿을 말함. 쉽게 말하면 바로 테스트를 진행해볼 수 있도록 셋팅된 환경이다.

  •  /src/app.controller.ts
//  localhost:3000/ 접속시 실행
@Get()// <-- "/" 이 생략되어있다. 
  getHello(): string {
    return this.appService.getHello();
  }

//  localhost:3000/shop 접속시 실행
@Get("/shop")//
  getHello(): string {
    return this.appService.getHello();
  }
  • src폴더에는 API의 단위로 그룹을 나누게 되는데, 한 그룹에는 각각 controller.ts, module.ts, service.ts 파일 3개로 만들어진다.

  • package.json 파일에

"dependencies": {
    "@nestjs/common": "^8.0.0",
    "@nestjs/core": "^8.0.0",
    "@nestjs/platform-express": "^8.0.0",
    "reflect-metadata": "^0.1.13",
    "rimraf": "^3.0.2",
    "rxjs": "^7.2.0"
  },
  "devDependencies": {
    "@nestjs/cli": "^8.0.0",
    "@nestjs/schematics": "^8.0.0",
    "@nestjs/testing": "^8.0.0",
    "@types/express": "^4.17.13",
    "@types/jest": "27.4.1",
    "@types/node": "^16.0.0",
    "@types/supertest": "^2.0.11",
    "@typescript-eslint/eslint-plugin": "^5.0.0",
    "@typescript-eslint/parser": "^5.0.0",
    "eslint": "^8.0.1",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-prettier": "^4.0.0",
    "jest": "^27.2.5",
    "prettier": "^2.3.2",

이런식으로 dependencies가 나뉘어져 있다.
devDependencies - 개발할 때에만 필요한 패키지들(ESLing, Prettier 등)로 분류되어 있으며, 패키지를 설치 할 때 yarn add [패키지] --dev 로 하면 devDependencies로 분류되게 하면서 설치할 수 있다.
dependencies - 실행할 때(배포할 때)에 필요한 패키지들을 분류해놓은 것이다. 이것만 설치하려면 yarn install --production 방법이 있다.

  • ESLint 에서 tsconfig 파일을 못읽어오면 여기에 tsconfigRootDir 를 추가하자.
parserOptions: {
    project: 'tsconfig.json',
    tsconfigRootDir: __dirname,  // <---  이거
    sourceType: 'module',
  },

Typescript

  • 타입 명시와 타입 추론
    타입 명시는 변수를 생성할 때 타입을 먼저 명시를 해주는 것이고, 타입 추론은 타입을 명시하지는 않았지만 해당 변수에 처음 들어온 값을 기반으로 타입을 적용하는 것을 말한다.
let 타입명시: string = "hello"
타입명시 = 10  // Type 'number' is not assignable to type 'string'.

let 타입추론 = "hello"
타입추론 = 10  // Type 'number' is not assignable to type 'string'.

let 타입추론2 = {
  apple: "red",
  banana: "yellow",
  kiwi: "green"
}
 타입추론2.kiwi = 5   // Type 'string is not assignable to type 'number'.
  •  Typescript 에서 객체 타입을 정해줄 때 Interface 라는 키워드로 타입을 만들어준다. 이름에는 Interface 의 'I' 를 따와서 관례상 맨앞에 붙여준다.
Interface IApple{
  name: string;
  color: string;
  myFavorite: boolean;
}

let apple:IApple = {
  name : "apple",
  color : "red",
  myFavorite: true
}
  • Typescript 에서 객체를 생성할 때 key 뒤에 물음표를 붙여주면 해당 프로퍼티는 존재거나 존재하지않거나 둘다 가능하다.
// 객체타입 
interface IApple {
    name: string
    color: string
    rate: number | string  //number 일수도 string 일수도
    comment?: string; //물음표를 붙이면 있을 수도 없을 수도 있을 때
}

let apple: IApple = {
    name: "사과",
    color: "red",
    rate: 5
}
apple.rate = "5점"; // IApple 에서 number,string 다 가능하게 했기때문에 이제는 가능해짐
apple.comment = "사과가 제일 좋아";

console.log(apple)
/*
{
  name: '사과',
  color: 'red',
  rate: '5점',
  comment: '사과가 제일 좋아'
}
*/
  
  •  Typescript 에서 함수는 타입추론이 불가능하다. 그래서 직접 명시를 해줘야하는데, 제일 마지막에는 받는(return) 값의 타입을 적어줘야 한다.
const apple = (name: string, color: string, price: number): string => {
	return name + color + price   // 결국엔 문자+문자+숫자 이기 때문에 string 으로 받게된다.  
}
  • decorator
function callDecorator(Decorator) {
  console.log("---------");
  console.log(Decorator);
  console.log("---------");
}

@callDecorator
class Decorator {}

/*
---------
[class Decorator]
---------
*/
  • Private은 내부에서만 참조 가능

좋은 웹페이지 즐겨찾기