챌린지 과제 수행 중 생긴 문제들

프레임워크 구현 과제를 시작하기까지

사실 이번 챌린지에서 프레임워크를 구현할 것이라고는 생각도 못했다. 아마 나말고도 많은 사람들이 비슷한 생각을 하는 걸로 알고있다..

처음에는 과제 조건 자체도 이해가 잘 안되어서 (라우팅 모듈 구현) 팀원들과 스터디 미팅을 하면서 겨우 이해하고 구현을 어떻게든 시작하게 되었다. 오랜만에 디자인 패턴 들을 검색하고, 과제의 주 목적중 하나인 ‘Nest.js 와 같은 프레임워크의 이해’ 라는 목적에 걸맞게 Nest.js Reference 를 찾아가서 소스를 둘러보고, 책을 뒤져가며 내가 구현할 프레임워크를 구상했다.

본격적인 프레임워크 구현하기

라우팅 모듈을 구현하면서 마주한 문제는 API spec 을 적어놓은 json 파일을 가져다가 별도의 라우팅 설정 없이 라우팅이 되도록 하는 작업이었다. 어떻게 구현할까 하다가 fastify 에서 jsonSchema 방식을 사용할 수 있다는 점을 참고하여, API spec 에서 key 값에 해당하는 부분을 Controller 역할을 하는 함수명과 매핑시키면 알아서 라우팅이 되지 않을까 하는 아이디어를 떠올려서 바로 작업에 착수했고, 구현에 성공하였다.

// Routing Module - controller 함수를 받아 json schema key 들과 매핑합니다.

class RouterModule implements ModuleDefaultClass {
  private schema: { [x: string]: RouterApiSpec } = {};
  private routeFunctions: { [x: string]: (api: RouterApiSpec) => any } = {};

  constructor({ path, routeFunctions }: RouterProps) {
    const apiSchemaJson = require(path);
    const apiSchema = Object.freeze(apiSchemaJson);

    this.schema = apiSchema;
    this.routeFunctions = routeFunctions
  }

  /**
   * @description json schema router module - API 스펙을 파싱하여 경로를 라우팅합니다.
   */
  init(app: express.Express) {
    const _schema = Object.entries(this.schema);

    _schema.forEach(([key, api]) => {
      if (typeof this.routeFunctions[key] === 'function') {
        app[api.method](api.url, this.routeFunctions[key](api));
      }
    })
...

이제 구현은 되었고, 이 모듈을 이제 제어를 할 Root Class 를 만들어 연결하도록 해야 했다. 그래서 Framework 라는 클래스를 만들었고, 이 클래스를 생성하면, express 서버를 만들고 라우팅 등록을 수행하도록 했다. 생성자에서 라우팅 모듈을 전달받아 라우팅 등록을 수행하도록 모듈을 실행하는 로직을 만들었다.

class Framework {
  private static _instance: Instance;
  private readonly _app: express.Express;
  private readonly _server: Server;
  private readonly _modules: Modules;

  constructor(params: FrameworkProps) {
// 싱글톤 인스턴스 생성
    if (!Framework._instance) {
      Framework._instance = this.init();
    }
// express app 초기화    
    this._app = expressApp.instance;
// 서버 초기화
    this._server = new Server({});
    this._modules = params.appProps.modules;

// 모듈 초기화
    Object.values(this._modules).forEach((module) => module.init(this._app));
  }

  static get instance() {
    return Framework._instance;
  }

  init() {
    return {
      modules: this._modules,
      app: this._app,
    }
  }

  run() {
    this._server.init(this._app);
  }
}

치명적인 문제 봉착

사실 모듈을 연결하는 과정이 순탄치 않았다. 모듈을 만들었다는 것은, DI (Dependency injection) 디자인 패턴을 적용하여 연결하겠다는 것인데, JS 에서 클래스가 사용되는 순간, Framework 클래스가 Singleton instance 로 만들어지는데 이 과정을 기다리지 않기 때문에 ‘모듈과 모듈’ 사이의 연결이 불가능 하다는 점이었다.