프로젝트 예고

타입스크립트 프로젝트

시작

타입스크립트 문법을 익힐겸 방학동안 자바스크립트로 진행한 작은 프로젝트들을 타입스크립트로 해보는 연습을 해보려 한다

간단한 배달 로직 구현하기

프로젝트 요구사항

  • 소비자, 서버, 생산자 등 클래스를 만들어 모듈로 구현한다
  • 한가지 주문만 받되 비동기적으로 코드를 작성한다
  • DB를 활용하여 가능한 선택지를 CLI에 알려준다
  • EventEmitter를 사용하여 구현한다

그냥 어젯밤에 머릿속에서 이렇게 연습해보면 되겠다 싶어서 전에 했던 것과 비슷하지만 조금 난이도를 낮춰서 프로젝트 요구사항을 작성해보고 그대로 구현해보려 한다

이를 위해서 알아야하는 타입스크립트는 다음과같다

  1. 타입스크립트에서 클래스 작성 방법이 자바스크립트와 어떻게 다른가
  2. 타입스크립트에서 모듈로 import, export 할때 자바스크립트랑 어떻게 다른가
  3. 자바스크립트에서 이제 정적 언어로 코드를 바꿔야하는데 어떻게 객체를 생성하고 어떻게 타입을 선언할 것인가
  4. 타입스크립트에서 DB와 연동하는 방법은 어떻게 되는가
  5. 타입스크립트에서 동기 비동기는 자바스크립트랑 어떻게 다른가
  6. 타입스크립트에서 EventEmitter를 사용할때 자바 스크립트와 어떻게 다른가

아직 문법에 대해 하나도 모르는데 차근차근 문법 공부하면서 구현해보려고 한다 (바로 구현한다는건 아니고.. 까먹을까봐 미리 프로젝트 요구사항만 작성)

설계

간단하게 표현을 해봤는데, 로직 흐름은 다음과 같다

  1. 서버에서 소비자의 요청에따라 요청에 맞는 선택지를 보여준다 (DB활용)
  2. 소비자가 가계, 음식, 수량 을 예시대로 입력한다
  3. 서버가 가계에 Event를 발행하여 주문이 들어왔음을 이벤트를 발생시킨다
  4. 가계는 음식에 따라 조리시간만큼 기다린 후 Push 서버에 조리가 완료되었다고 이벤트를 발생시킨다
  5. 가계는 음식이 조리가 완료되었다고 Rider에 이벤틀르 발생시킨다
  6. Rider는 가계에 위치에 따라 배달 시간만큼 기다린 후 Push 서버에 배달이 완료되었다고 이벤트를 발생시킨다
  7. Push 서버는 이벤트를 받으면 바로 Consumer에 이벤트를 발생시켜 소비자 메소드로 커멘드라인에 해당 메시지를 출력한다

설계에서 신경쓴 점

예전에 EventEmitter를 쓴 바로 얘기를 해보자면 나는 양방향 소통을 하고 싶어서 양쪽에서 Event를 발행하고 수신 할 수 있게끔 코드를 작성 했었다.

하지만 이것이 결국 양쪽 클래스에서 각자의 클래스 말고 수신받는 클래스의 정보도 있어야 했기 때문에 단일책임 원칙에서 어긋나기도 하면서, import를 양방향으로 되다 보니까 에러가 발생 했었다.

따라서 EventEmitter를 사용할때 참조 방향이 최대한 단방향이 되면서 책임을 최대한 분산해서 가지게끔 구현하려 했고 그러한 이유로 Push 서버를 따로 만들어 거기서 모든 출력을, 즉 소비자와 관련된 것들을 따로 처리하게끔 구현하였다.

마무리

앞으로 타입스크립트 문법 공부를 하면서 위에 작성한 설계대로 차근차근 구현을 해보자~


비고

단일 책임 원칙이 뭘까

객체지향프로그래밍의 SOLID 원칙중 S에 해당하는 원칙으로 Single Responsibility Principle을 말한다

SOLID 원칙은 다음에 객체지향 챕터에서 더 자세하게 다루는것으로 하고, 조사를 하던중 단일 책임 원칙에 대해 잘못 이해하고 있던 부분들이 있어서 간다하게만 메모하려 한다

나는 객체지향에 대해 지금도 조금 혼란 스러워 하는 중이다. “대체 단일 책임 원칙이 뭘까?? 클래스 내부에 다른 클래스가 멤버 변수로서 존재하면 이미 단일 책임 원칙이 아닌거 아닌가?? ” 하는 생각이 지금까지의 오해였다.

이렇게 생각한 이유는 다음과 같다

class Payment {
  process() {
    // 결제 처리 로직
  }
}
 
class Shipping {
  ship() {
    // 배송 처리 로직
  }
}
 
class Order {
  private payment: Payment;
  private shipping: Shipping;
 
  constructor(payment: Payment, shipping: Shipping) {
    this.payment = payment;
    this.shipping = shipping;
  }
 
  processOrder() {
    this.payment.process();
    this.shipping.ship();
  }
}
 

이러한 코드가 있다고 할때 Order 내부에 다른 클래스가 멤버변수로서 존재하는데,

Order 클래스에서 에러가 발생했을때 이 에러가 Payment 클래스, Shipping 클래스 혹은 두 클래스가 아닌 Order 클래스만의 에러, 이렇게 셋중 어떤 에러인지 파악 하기 힘들기 때문에 단일책임원칙에서 어긋난다고 생각했었는데, 이는 잘못 알고 있던 것이다.

“책임” 이라는 말이 상당히 주관적으로 받아드려질수 있다는것을 알았다. Order는 “주문처리”라는 책임을 맡은 모듈로 내부에 주문처리에 대한 세부적인 하위 클래스가 존재한다고 해서 단일책임원칙에 어긋나는 것이 아니다.

아래 예시를 보자

class Logger {
  log(message: string) {
    console.log(message);
  }
}
 
class Order {
  private logger: Logger;
 
  constructor(logger: Logger) {
    this.logger = logger;
  }
 
  processOrder() {
    this.logger.log('Order processed');
    // 주문 처리 로직
  }
}
 

위와 같은 코드에서 Order는 주문 처리 라는 책임을, Logging은 로그를 남기는책임을 가지고 있기 때문에 서로 맡고 있는 책임이 다르므로 서로 클래스, 모듈을 나눠놓은 것을 볼 수 있다.

하지만 (그럴일을 거의 없을거 같지만..), 팀에서 추구하는 방향에 따라 주문처리가 맡은 책임에 로깅도 포함된다고 생각할 수 있다. 그러면 그냥 주문책임 클래스 내부에 로깅 클래스를 포함 시켜도 된다

따라서 결론은 너무 단일 책임 원칙에 목 맬 필요 없고, 주관적으로 어떻게 구현했을 때 에러가 발생한 경우, 쉽게 디버깅하고, 쉽게 맡은 구역을 찾아 에러를 고칠 수 있을지만 고민하면 된다.