[Rune] rune-decorator (POC) - rune-ts/server 데코레이터에 맛있게 졸이기

June·2025년 1월 19일
0

rune-decorator 의 전체 예시 코드는 여기 에서 확인할 수 있습니다.

1. Rune-ts?

Rune-tsmarpple co. 에서 만든 경량 프론트엔드 라이브러리입니다. Rune 을 사용하여 컴포넌트를 개발하고 @rune-ts/server 를 사용하여 페이지 라우팅 및 hydration 등을 진행합니다.

이 때 @rune-ts/server 는 아래와 같은 형태로 페이지를 라우팅하게 됩니다. (참고)

  • Rune 페이지 파일
import { createRouter } from '@rune-ts/server';
import { html, Page } from 'rune-ts';

class HelloWorldPage extends Page<{ title: string }> {
  override template({ title }) {
    return html` <div>hello, world: ${title}</div> `;
  }
}

const homeRouter = {
  ['/']: HelloWorldPage,
};

class HelloRunePage extends Page<{ title: string }> {
  override template({ title }) {
    return html` <div>hello, rune: ${title}</div> `;
  }
}

const runeRouter = {
  ['/rune']: HelloRunePage,
};

type Router = typeof homeRouter & typeof runeRouter;

const routers = createRouter<Router>({
  ...homeRouter,
  ...runeRouter,
});
  • client entry 파일
import { hydrate } from '@rune-ts/server';

import { routers } from '../router';

hydrate(routers);
  • routing 파일
import { app } from '@rune-ts/server';
import { MetaView } from '@rune-ts/server';

const server = app();

server.get(routers['/'].toString(), function (req, res) {
  const layoutData: LayoutData = {
    head: {
      title: 'HOME',
      description: 'sss',
    },
  };
  res.send(new MetaView(routers['/']({ name: '', price: 100 }), layoutData).toHtml());
});

위 코드 처럼 express 기반의 rune-ts/server 를 클래스와 IOC 기반의 구조화된 코드 작성 방식으로 유지보수하기 더 쉽고 다른 프레임워크와 유사한 경험을 주고자 rune-decorator 를 기획하고 개발을 시작하게 되었습니다.

2. 개선 방향

Client side

  • Rune page 선언
    • 일반적인 rune page 클래스 선언과 동일하지만 @Rune.Page() 페이지 데코레이터를 통해 라이브러리에서 제어할 수 있는 페이지로 표기 합니다.
    • 해당 데코레이터를 등록하면 해당 Page 인스턴스는 hydration이 가능한 상태 가 됩니다.
import { html, Html, Page } from "rune-ts";
import { Rune } from "rune-decorator/client";
import style from "./app.page.module.scss";

export interface PageProps {
  message: string;
}

@Rune.Page()
export class AppPage extends Page<PageProps> {
  template(): Html {
    return html` <div class="${style.container}">
      <span class="${style.title}">${this.data.message}</span>
      <button class="${style.action}">Click</button>
    </div>`;
  }
}
  • Navigator
    • 루트 네비게이터 클래스에 @Rune.Navigator() 데코레이터를 적용하고, pages 배열에 hydration이 필요한 페이지 클래스를 등록합니다.
    • pages 배열에 등록된 페이지들은 필요한 시점에 hydration이 됩니다.
import { Rune } from "rune-decorator/client";
import { AppPage } from "../pages/app.page";

@Rune.Navigator({
  pages: [AppPage],
})
export class AppNavigator {}
  • Controller
    • Nest.js 와 유사하게 라우터는 Controller 로 등록된 클래스에 작성하게 되고, page 인스턴스를 반환하면 해당 페이지가 렌더링됩니다.
    • controller 의 라우터에서 page 를 반환하면 현재 기본으로 MetaView 로 warpping 하여 페이지를 응답합니다.
    • 예시 이외에도 @Body(), @Res() 와 같은 편한 개발자 경험을 위한 파라미터 데코레이터들도 지원합니다.
import { Controller, Get } from "rune-decorator/server";
import { AppService } from "./app.service";
import { AppPage } from "../pages/app.page";

@Controller("")
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): AppPage {
    const page = new AppPage({ message: this.appService.getHello() });

    return page;
  }
}

3. 정리

  • Rune-Decorator는 기존 @rune-ts/server를 기반으로 구축되어, 기존 Rune.ts 프로젝트와의 호환성을 유지하며, 다른 node.js 백엔드 프레임워크와 유사한 경험을 제공합니다.

    • 클라이언트 사이드는 @Rune.Page(), @Rune.Navigator() 등을 통해 Page 를 등록하여 hydration 과정을 효율 적으로 관리합니다.
    • 서버 사이드는 @Controller(), @Get() 등을 통해 라우팅과 DI(Dependency Injection) 패턴을 간단히 구성할 수 있습니다.
  • rune-decorator 는 현재 POC 단계로, 기존 Rune.ts의 장점을 유지하면서, 데코레이터 기반의 구조화된 코드 작성을 통해 유지보수성을 높이고 개발자 편의성을 향상시키는 것을 목표로 합니다.

  • 앞으로는 이 구조 위에 인증/인가, 에러 핸들링, 미들웨어, 인터셉터 등 다른 프레임워크에서 제공하던 기능과 함께 rune-ts 에 적합한 기능을 점진적으로 추가해나갈 예정입니다.

0개의 댓글

관련 채용 정보