그래픽 시스템을 만들자 -2-

1편보기

그림을 그리는 하나의 단위

지오메트리는 전체 그림 그리기의 영역 계산의 결과입니다만, 그림을 그리기 위해 하나하나 나눠서 처리할 단위가 필요합니다.
쉽게 생각해 화면에 사각형을 네개 그린다면 네 개의 아이템이 필요하다는 것이죠.
네 개의 아이템별로 크기와 위치를 계산하는 식으로 지오메트리 단계가 진행될 것입니다.

앞 서 컨테이너와 아이템이란 개념을 쓰기로 했으니 모든 아이템은 컨테이너로 작동하기도 합니다.
이러한 개념은 알고리즘 공부할 때 흔히 등장하는 트리구조와 일치하게 됩니다.

그럼 우리가 사용할 아이템을 보다 구체적으로 생각해보죠.

  1. 하나의 Rect를 소유하여 지오메트리 계산 결과인 크기와 위치를 나타낸다.
  2. 컨테이너로서 자식 아이템을 갖을 수 있다.
  3. 크기를 결정하는 알고리즘을 갖고 있다.
  4. 자식 아이템의 위치를 결정하는 알고리즘을 갖고 있다.

결국 아이템은 하나의 지오메트리 단위로서 원래의 책임인 크기와 위치를 결정해야 합니다. 단지 다양한 형태의 방식이 있으므로 직접적인 코드로 알고리즘을 갖고 있기 보다는 전략 패턴 등으로 외부에서 알고리즘을 공급받는 편이 좋겠죠.

그 외엔 어째서 본인의 위치를 직접 정하는 게 아니라 자식 아이템의 위치를 정하는가인데, 자기 스스로 위치를 정하는 것으로는 정말 자기만 정할 수 있습니다. 하지만 본인의 위치는 형제들이나 부모의 상황에 따라 달라질 수도 있을 것입니다. 이를 위해 본인이 위치를 정하기 보다는 컨테이너 쪽에서 일괄로 자식아이템들의 위치를 조율하여 정해주는 것이 훨씬 유리하기 때문입니다.

여기까지를 간단히 코드로 표현하면 다음과 같을 것입니다.

class Item{
  offset = Rect.new();
  sizer:Sizer;
  container:Item;
  children:Item[] = [];
};
  1. offset – 지오메트리 계산의 결과를 저장할 사각형입니다.
  2. sizer – 크기를 계산해줄 외부에서 공급될 전략 객체입니다.
  3. container – 본인의 부모가 될 컨테이너입니다.
  4. children – 본인이 소유할 자식 아이템들입니다.
정말 요 정도로 된건가요?
여기에 조금씩 추가해가면 되니까 ^^
역시 이게 끝은 아니었어요!


크기 측정하기

일단 지오메트리 계산의 기본인 자신의 크기를 정해야 합니다. 중요한 점은 아이템 자신 혼자 이를 결정할 수 없다는 것입니다.
예를 들어 다음과 같은 HTML을 생각해보죠.

<div>
  <img src="some.jpg"/>
</div>

이 구조에서 div의 크기는 얼마일까요?
별다른 CSS설정이 없었다면 정답은

img에 지정된 some.jpg의 크기에 달렸다

입니다.

여기서 핵심은 컨테이너인 div가 자식 아이템인 img에 영향을 받아 크기가 변했다는 점입니다. div가 컨테이너로서의 전략이 어떤지 좀 더 깊이 생각해보죠.

  1. div는 자식이 커지면 자신도 따라서 커지게 되는 전략을 갖고 있다고 할 수 있습니다.
  2. 이는 div에 overflow:auto 라는 기본 전략이 있기 때문입니다.
  3. 이 기본값은 크기를 지정하지 않은 이상 자식들의 크기에 따라 자신도 크기가 변하는 알고리즘입니다.

같은 원리로 img에도 자신이 아이템으로서의 크기 전략이 숨어있습니다.

  1. 기본값은 1×1로 시작한 뒤
  2. 이미지가 로딩 완료된 시점에 이미지의 100%크기에 맞춰 본인의 크기를 재조정합니다.

즉 측정이 한 번만 이뤄지는게 아니라 여러차례 계속 일어나고 있다는 점입니다. 게다가 img이 로딩 후에 측정을 다시하면 그 컨테이너도 측정을 다시 해야하는 것이죠.
여기까지의 흐름을 반영하면 다음과 같은 측정메소드를 만들 수 있습니다.

class Item{
  offset = Rect.new();
  sizer:Sizer;
  container:Item;
  children:Item[] = [];

  measure(){
    //1
    const {sizer, container} = this;
    //2
    const rect = sizer.setSize(this);
    const {width, height} = rect;
    const isEquals = this.offset.equals(rect)
    //3
    this.offset = Rect.new({width, height}, this.offset);
    //4
    if(!isEquals) container.measure();
  }
};

주석번호에 맞춰 코드를 순서대로 살펴보죠.

  1. 우선 크기를 정해주는 sizer와 컨테이너를 추출합니다.
  2. sizer에게 본인의 크기를 계산하여 그 결과를 rect로 받고, 우선 기존 크기와 같은지를 비교해둡니다.
  3. 2번에서 isEquals를 미리 계산해뒀으므로 이제 offset의 크기를 갱신해줍니다.
  4. 만약 자신의 크기가 달라졌다면 부모 단계에서부터 다시 크기를 측정하도록 합니다.

4번 항목이 재귀적인 성격이라 약간 이해하기 어려울 수 있습니다만 기본적으로는 간단한 로직입니다. sizer에게 크기를 재계산시켜 기존의 값과 다르다면 컨테이너 단계부터 재계산을 시키는 것이죠.

위의 Item클래스를 img태그에 적용했다고 생각해보죠.
1. 처음에 크기가 1×1이지만 로딩후에 본인의 measure()를 다시 호출한다면 그때는 로딩된 이미지의 크기로 변경될 것입니다.
2. 이로 인해 기존의 크기와 새 크기가 달라지겠죠.
3. 따라서 div(컨테이너)의 measure()를 호출하게 되는 것입니다.
4. div는 재측정시 자식인 img의 크기가 커졌으므로 본인도 여기에 맞춰 커지게 됩니다.

<

div class=”eind”>
자신이 변했지만 컨테이너를 재계산 시키네요.

컨테이너의 크기는 자식들의 크기에 영향을 받거든.
자식의 크기가 왜 컨테이너에 영향을 주는지 안보여요.
sizer안에 숨어있기 때문이야.
아, 그럼 sizer에 따라 영향이 없을 수도 있겠네요?
맞아! 단지 영향이 있는 sizer도 있으니 저렇게 정한거야?


자식이 컨테이너에 미치는 영향

계속 자식이 컨테이너의 크기에 영향을 끼친다고 했는데 대체 어떻게 작동하는 걸까요.
다시 html의 도움을 받아 이해해보죠.

<section>
  <div style="display:inline-block;width:40%;height:50px">A</div>
  <div style="display:inline-block;width:40%;height:50px">B</div>
</section>

일단 이걸 렌더링 하면 다음과 같이 그려집니다.

A
B

A, B는 각 40%를 차지하면서 한 줄에 그려지죠. 이게 어찌된 일인가 차근차근 살펴보죠.

두 개의 div를 배치해서 section의 크기는 가로가 그대로 유지되고 세로는 50px가 됩니다.
지금은 A, B가 한 줄에 그려지기 때문이죠.
하지만 만약 다음과 같이 B의 크기를 70%로 늘린다면 더 이상 A, B는 같은 줄에 들어갈 수 없어 두 줄이 되어버립니다.

A
B

A, B가 둘 다 inline을 따르고 있으므로 40 + 70 = 110%가 되어 더 이상 한 줄에 위치시킬 수 없는 거죠.
그 결과 section의 세로 크기는 100px가 됩니다.

이 단순한 실험에서 알 수 있는 점은 컨테이너(section)의 세로 크기가 아이템(div)의 배치 때문에 변한다는 점입니다. 다시 말해

자신의 크기를 결정하려면 자식의 배치를 끝내야만 한다.

라고 정리할 수 있습니다. 더 나아가 다음의 사실도 알 수 있습니다.

컨테이너의 크기는 자식의 배치가 끝난 뒤 전체 자식이 차지해야할 공간의 크기를 이용하여 계산된다.

즉 자식 아이템을 다 배치하고 끝이 아니라 그 배치의 결과로 자식 아이템들 전체가 차지하는 공간의 크기를 반환받아야 컨테이너의 크기를 계산할 수 있다는 뜻입니다.

<

div class=”eind”>
자식 배치로 본인 크기가 변할 수 밖에 없나요?

자신의 크기를 고정시키면 영향은 없을거야.
그럼 자식들은 어떻게 되나요?
컨테이너 영역 밖은 잘려서 안 보이거나 컨테이너 안에 스크롤바가 생길 수도 있지.
그럼 sizer는 단지 자식의 배치만 갖고 크기를 결정하지 않겠네요!
sizer도 컨테이너의 상태를 최우선적으로 고려해야 겠지 ^^


결론

이번에는 크기가 결정되는 보다 구체적인 방법을 살펴봤습니다. 살펴본 결과를 클래스로 간단히 모델링도 해보았죠.
다음 연재에서는 자식 아이템들의 위치가 정해지는 것이 대해 생각해보겠습니다.

이번이 사각형보다 더 어려운거 확실해요.
사람 따라 그럴지도 몰라 ?
그럼 거짓말 한거죠.
사람 따라 아닐지도 몰라 ?