사용자:Hsl0/LinkTools

리버티게임, 모두가 만들어가는 자유로운 게임

LinkTools와 EventTools는 가칭이므로 좋은 이름 추천해주면 감사하겠습니다. 글이 좀 지저분하지만 곧 정리할 예정입니다.

배경

리버티게임의 전신인 백괴게임 초창기 때부터 한 페이지에 변수를 설정하여 다양한 일을 할 수 있는 CGI, 다음에 넘어갈 페이지를 숨겨 스포일러를 방지하는 미궁 등 특수한 링크를 만드는 다양한이 있었다. 하지만 CGI 변수를 설정하면서 다음 페이지를 숨기고 싶은 등 여러 링크 틀의 특성을 조합해야 하는 경우가 생겼고, CGI미궁같이 두가지 특성을 모두 갖는 링크 틀이 만들어지게 되었다.

훗날 기술이 발전하고 플러그인 시스템이 개발되면서 자바스크립트의 관심이 늘어나 자바스크립트를 기반으로 한 다양한 기능이 폭발적으로 생겨나게 되었다. 자바스크립트를 기반으로 한 다양한 링크 틀 역시 생겨났으며, 링크 틀이 꽤 많아져 기존의 CGI미궁같은 해결법을 쓰기에는 곤란해졌다.

그래서 다음에 나온 링크 틀들은 경로 대신 링크를 받을 수 있게 설계되어 여러 특성을 가진 링크를 단번에 만드는 것이 아닌 기존의 링크에 특성을 하나하나 더해가는 방식으로 바뀌었다.

하지만 이 해결 방식은 또다른 문제를 낳았다. 자바스크립트 기반의 특수한 링크가 특성을 부여하기 위해 기존 링크의 동작을 바꾸기 위해 기존 링크를 비활성화하고 자체적인 방식을 구현하였지만, 기존에 부여된 특성이 비활성화되는 문제가 있었다.

예를 들어 링크를 누르면 게임 데이터를 수정하는 DB2와, 뒤로가기가 가능한 이동을 수행하던 기본 링크 클릭 동작을 뒤로가기가 불가능한 이동으로 대체하는 일방통행링크는 모두 를 실행하고 링크 이동을 자체 구현한다. 일방통행링크는 원래 기본값을 수정해야 하는 것이고, DB2는 이벤트 기본값 동작이 비동기 동작을 기다려주지 않는 관계로 기본값 동작을 막고 직접 기본값 동작을 구현한다. 만약 이 둘을 사용하게 될 경우 두 틀을 작동시키는 이벤트 핸들러가 먼저 페이지 이동 동작을 실행하는 경쟁을 할 것이고, 경쟁에서 패배한 둘 중 하나는 동작하지 않을 것이다. 이렇게 기존 이벤트 핸들러는 비동기 동작을 기다려주지 않고, 독립적으로 등록된 핸들러가 기본 동작을 대체할 경우, 무엇이 실행될지 예측하기 어렵다.

또한, 진동과 같이 인터랙티브한 요소가 도입되었지만 페이지 이동에만 실행될 수 있었고, 다양한 곳에 적용시키기 어려웠다.

CGI보호는 무결성 오류가 발생할 때 문서 이동을 넘어 DB 저장 등 더 다양한 동작을 실행시킬 수 있으면 좋겠지만, 이를 구현하기 위한 표준화된 이벤트 API가 필요하다.

이러한 문제점을 해결하기 위해 LinkTools(가칭)과 EventTools(가칭)을 개발하게 되었다.

설계

이 프레임워크는 입력을 받고 가공해서 내보내는 함수형 프로그래밍 철학을 기반으로 만들어졌다. 링크에 이벤트를 직접 배당하기보다는 링크를 재가공하는 함수에 집어넣는 느낌을 만들 수 있도록 하였다. 그리고 링크를 재가공하는 함수는 바로 틀이다. 따라서 클래스를 직접 사용하기 보다는 주로 틀 안에서 사용될 것이며, 틀로 사용하면 더 아름다운 모습이 나온다... 지만 그냥 기존에 쓰던 틀의 모습이다. 물론 직접 써도 된다. 자바스크립트를 부분적으로 사용하고 UI를 HTML로 구현하는 경우 직접 사용할 수도 있다.

미디어위키에서는 링크에 직접적으로 클래스를 적용할 수 없기에 링크에 래퍼를 씌우고 그 래퍼 안의 링크를 가리켜서 링크를 간접적으로 수정한다. 이 프레임워크는 이러한 방식을 그대로 계승하면서 래퍼, 즉 링크에 적용할 특성의 우선순위를 정해준다. 함수형 패러다임 역시 기존의 틀에서 활용하던 방식이 이 패러다임에 가까웠기 때문에 이를 계승한 것이다.

LinkModifier와 EventTools는 밖에서 안으로, 또는 안에서 밖으로 실행되는 규칙이 있지만, 밖에 있는 함수는 입력으로 받은 링크가 어떤 링크인지 신경쓸 필요가 없다는 원칙에서 도출된 것이다. 그냥 링크가 존재하는지만 중요하며, LinkCreater를 쓰면 이 조차도 신경 쓸 필요가 없다. LinkModifier의 경우, 안쪽의 modifier가 먼저 링크를 수정하고 바깥쪽 modifier는 이미 수정된 링크를 받는 느낌이다. EventTools는 안쪽의 이벤트 listener/handler가 바깥쪽 listener/handler 관점에서 기본 동작이고, 바깥쪽 listener/handler가 기본 동작보다 먼저 실행되는 것이다. handler의 경우 기본값을 오버라이딩해서 기본값을 대체한다. 따라서 기본값은 실행하지 않는다. 기본 동작은 절대적이지 않고 상대적이며, 기본 동작은 꼭 DOM 표준만 되라는 법은 없고, 링크 수정 함수가 입력으로 받은 링크가 원래 하던 모든 행동들은 모두 기본 동작이라는 법칙에서 도출된 것이다. 기본값은 대단한 것이 아니라 그냥 다음에 실행될 이벤트 listener/handler다.

EventTools는 아직까지는 링크만 다룰 수 있고 LinkTools와 함께 개발된 LinkTools의 구성요소로써 함께 묶여서 다루고 있지만, 나중에 컴포넌트 비슷한 개념을 도입하면 기능을 확장해서 독립할 예정이기 때문에 별도의 이름을 가지고 있고 클래스 접두어가 다른 것이다. 링크만 다루고 link밖에 못쓰면서 쓸데없이 data-target이 있는 건 미래를 대비하기 위함이다.

구성요소

LinkModifier

  • 태그 안의 링크를 수정한다.
  • 안쪽이 먼저 실행되고 바깥쪽이 나중에 실행된다.
<span class="link-modifier" data-modifier="a">
    <span class="link-modifier" data-modifier="b">
        [[리버티게임:대문]]
    </span>
</span>

b 실행-a 실행

LinkCreater

  • link-slot-dummy: 태그 안에 아무런 링크가 없다면, 아무 기능이 없는href가 없고, 눌렀을 때 반응이 없는 링크(<a>)를 생성한다. EventTools와 함께 쓰면 좋다.
  • link-slot-self: 태그 안에 아무런 링크가 없다면, 현재 페이지를 가리키는href가 현재 페이지인 링크(<a>)를 생성한다. LinkModifier와 함께 쓰면 좋다.

EventTools

  • 이벤트를 비동기적으로 만든다.
  • 이벤트 handler/listener의 기본값을 재정의한다. 이벤트 handler/listener의 기본값은 안쪽에서 정의된 이벤트이다. (listener는 기존 listener/handler를 오버라이딩하지 않는다.)
  • listener는 (상대적) 기본값을 오버라이딩하지 않고, handler는 오버라이딩한다.
  • 바깥쪽이 먼저 실행되고 안쪽이 나중에 실행된다.
<span class="event-listener" data-target="link" data-listen-click='["a"]'>
    <span class="event-listener" data-target="link" data-listen-click='["b"]'>
        [[리버티게임:대문]]
    </span>
</span>

a 실행-b 실행-리버티게임:대문으로 이동

a에서는 기본 동작이 b 실행-리버티게임:대문으로 이동이다.

<span class="event-handler" data-target="link" data-handle-click='["a"]'>
    <span class="event-handler" data-target="link" data-handle-click='["b"]'>
        [[리버티게임:대문]]
    </span>
</span>

a 실행

a에서는 기본 동작이 b 실행-리버티게임:대문으로 이동이다. handler는 기본 동작을 실행시키지 않는다.

data-listen-(이벤트명)="(플래그) (동작)"
data-handle-(이벤트명)="(플래그) (동작)"
  • 이벤트 handler/listener는 기본적으로 동시에 실행되지만 다음 플래그는 이를 변경시킬 수 있다.
  • once는 처음 이벤트가 발생할 때 한번만 실행되고 그 이후 이벤트는 실행하지 않는다.
  • defer는 먼저 실행되는 listener/handler가 모두 작업을 완료했을 때 실행한다.
  • await는 다음에 실행되는 listener/handler가 이번 이벤트의 작업이 모두 완료될 때까지 기다린 후 실행되게 한다.
이번 이벤트 handler/listener 대기 여부
↓ 이전
이번 →
defer await defer
await
즉시 대기 즉시 대기
defer 즉시 대기 즉시 대기
await 대기 대기 대기 대기
defer await 대기 대기 대기 대기
a
  defer ba 대기 후 실행
c
    defer da, b, c 대기 후 실행
e
await f
  gd 대기 후 실행
      await defer ha, b, c, d, e, f, g 대기 후 실행
        ia, b, c, d, e, f, g, h 대기 후 실행

이벤트 동작은 첫번째 요소가 동작 이름이고 나머지가 해당 동작의 인자인 JSON 배열으로, 다음과 같다. S-표현식을 JSON 배열로 나타낸 것이라고 생각해도 된다.

["a", "b", 1, true, null, ["c"], {}]

이 때 a 동작이 실행되고 ["b", 1, true, null, ["c"], {}]가 인자로 제공된다.

도보시오