[JS] JS에 JSX 적용해보기 - 컴포넌트 기능 추가(1)

jiseong·2022년 5월 10일
0

T I Learned

목록 보기
241/291
markup() {
  return (
    <div>
	  <Customcomponent />
      <div>책목록</div>
    </div>
  );
}

React에서 jsx문법을 사용하다보면 위와 같이 Component도 jsx 안에 넣으면 해당 컴포넌트도 렌더링 되는 것을 볼 수 있다. 이전에 구현했던 jsx 기능은 텍스트, 일반 태그와 같은 노드만을 렌더링 할 수 있는데 한번 Component도 렌더링 할 수 있는 기능도 구현해보고싶어 코드를 추가해보려고 한다.

jsx 함수 역할 분리

우선, 기존의 jsx함수는 jsx(jsx(jsx())) 재귀함수를 반복하며 node를 생성하여 노드를 반환하였다면 이번에는 jsx함수는 반복적으로 vDOM 형태의 객체만 반환해주는 역할만 하고 vDOM을 가지고 실제 DOM을 만드는 함수는 따로 만들어 역할을 나눴다.

childrenElements.flat()은 자식이 배열형식일때 children: [[c1, c2]]와 같이 중첩배열을 이루기 때문에 한번 풀어주는 역할을 한다.

function jsx(type: string, attributes: TAttribute, ...children: any[]) {
  const childrenElements = children.reduce((acc: any[], child:any) => {
    if (typeof child == null) return acc;
    if (typeof child === "boolean") return acc;

    typeof child === "object"
      ? acc.push(child)
      : acc.push(jsx("TEXT_NODE", { 'textContent': child }));

    return acc;
  }, [])

  return {
    type,
    attributes,
    children: childrenElements.flat()
  }
}

다음과 같은 HTML 구조가 있다면,

  markup() {
    return (
      <div>
        <div>책목록</div>
      </div>
    );
  }

vDOM 객체 형태로 반환되는 값은 다음과 같다.

이제 해당 vDOM 객체를 이용하여 컴포넌트라면 컴포넌트에 대한 처리, 일반 노드라면 일반 노드에 대한 처리를 분기해주면 된다.

function vDomToNode(vDOM: IDom, container: Element | null, oldDOM?: IDom) {
  
  // 컴포넌트의 경우
  if(컴포넌트인지 판단){
    componentNode(vDOM, container, oldDOM);
  }
  // 일반 노드의 경우
  else originNode(vDOM, container, oldDOM);
}

vDOM을 실제 Node로 변환

일반 노드일 경우

function originNode(vDOM: IDom, container: Element | null, oldDOM?: IDom) {
  let newNode: any = null;

  if (vDOM.type === 'TEXT_NODE') {
    const {textContent} = vDOM.attributes;

    newNode = document.createTextNode(textContent);
  } else {
    newNode = vDOM.type === "fragment"
      ? document.createDocumentFragment()
      : document.createElement(vDOM.type);

    if(!(newNode instanceof DocumentFragment)) {
      updateNode(newNode, vDOM);
    }
  }

  vDOM.children.forEach((child: any) => vDomToNode(child, newNode));
  
  // 실제 부착
  container?.appendChild(newNode);
}

일반 노드를 생성할 때는 텍스트노드와 fragment만 주의해서 Node를 생성하면 된다. updateNode는 이벤트 부착이나 속성이 존재시 속성을 지정해주는 역할을 하는 함수이며 현재는 속성만을 지정해주고 있다.

function updateNode(newNode: any, vDOM: any){
    // 이벤트나 속성 처리
    Object.entries(vDOM.attributes || {}).forEach(([key, value]) => {
      newNode.setAttribute(key, value);
    });
}

여기까지 작성해주면 다음과 같은 형태의 문법들은 오류없이 화면에 렌더링 되는 것을 볼 수 있다. 컴포넌트의 경우에는 내일 마저 구현해볼 예정이다.

0개의 댓글