칸은 남는 자리를 나눠 갖는 방식으로 정해진다
Esc

    칸은 남는 자리를 나눠 갖는 방식으로 정해진다

    –UnityUIUI Toolkit

    이름을 붙여야 스타일을 걸 수 있다에서 값을 어디에 적고 누구에게 거는지까지 봤습니다. 이제 그 값 중에 제일 많이 쓰는 것, 자리와 크기입니다.

    uGUI 에서 이 자리를 맡던 것은 RectTransform 이었습니다. 앵커, 피벗, 오프셋을 각 요소가 스스로 들고 있었고, 부모는 Layout Group 을 붙여야만 자식 배치에 끼어들 수 있었습니다. UI Toolkit 에는 앵커도 피벗도 없습니다. 반대로 되어 있습니다. 자리는 부모가 정하고 자식은 쌓입니다.

    앵커가 없어지고 쌓기가 남습니다

    자리를 누가 들고 있는가 uGUI Button 앵커와 오프셋을 요소가 직접 들고 있습니다 UI Toolkit flex-direction: column Label Button Button 부모가 방향을 정하고 자식은 차례로 쌓입니다
    읽는 단위가 요소 하나에서 부모와 자식 묶음으로 올라갑니다.

    문서 표현으로 “The layout engine uses the layout principles of Yoga, which implements a subset of Flexbox” 입니다. 웹의 Flexbox 를 부분집합으로 가져온 것입니다.

    그래서 요소 하나를 보고 “이게 어디 붙어 있지” 를 알 수 없습니다. 부모가 어느 방향으로 쌓는지, 남는 자리를 누가 가져가기로 했는지를 봐야 합니다. 값을 읽는 단위가 요소에서 부모와 자식 묶음 으로 올라갑니다.

    Layout Group 을 아는 사람에게는 이렇게 말하는 게 빠릅니다. 모든 요소에 Vertical Layout Group 이 기본으로 붙어 있는 상태 입니다. 붙일지 말지를 고르는 게 아니라, 방향을 무엇으로 할지만 고릅니다.

    기본 방향이 세로입니다

    여기서 웹을 해 본 사람이 제일 먼저 걸립니다. CSS 의 flex-direction 기본값은 row 인데, UI Toolkit 은 column 입니다. 문서가 “makes the main-axis run from top to bottom” 이라고 적어 뒀습니다.

    .uxml 에 요소 셋을 넣고 아무 스타일도 안 걸면 세로로 쌓입니다. 가로로 놓고 싶으면 부모에 한 줄을 적습니다.

    .row {
        flex-direction: row;
    }

    쓸 수 있는 값은 row, row-reverse, column, column-reverse 넷입니다. -reverse 는 순서를 뒤집습니다. uxml 의 줄 순서를 그대로 두고 화면에서만 뒤집을 때 씁니다.

    남는 자리를 누가 가져갑니까

    칸 크기를 정하는 값이 셋입니다.

    속성 하는 일 기본값
    flex-basis 나누기 전 시작 크기 auto (내용 크기)
    flex-grow 남는 자리를 가져가는 비율 0
    flex-shrink 모자랄 때 줄어드는 비율 1

    기본값이 flex-grow: 0 이라서, 아무것도 안 적으면 각 칸은 내용만큼만 차지하고 남는 자리는 빈 채로 남습니다. 늘리고 싶은 칸에만 flex-grow: 1 을 겁니다.

    주의할 조합이 하나 있습니다. 칸 셋에 전부 flex-grow: 1 을 줘도 칸 크기가 같아지지 않습니다. 시작 크기가 내용 크기라서, 남는 자리만 똑같이 나눠 가질 뿐입니다. 진짜로 삼등분하려면 시작 크기를 0 으로 놓아야 합니다.

    .item {
        flex-grow: 1;
        flex-basis: 0;
    }

    문서가 이 조합을 “ensures an even split regardless of content” 라고 적어 뒀습니다. 위 인터랙티브에서 전부 1 과 전부 1, basis 0 을 번갈아 보면 차이가 보입니다.

    축이 둘이고, 각각 쓰는 속성이 다릅니다

    쌓이는 방향을 주축, 그에 직각인 방향을 교차축이라고 부릅니다. 정하는 속성이 다릅니다.

    flex-direction: column flex-direction: row 교차축 · align-items 주축 · justify-content 남는 자리 주축 · justify-content 교차축 · align-items 남는 자리 칸이 쌓이는 쪽이 주축입니다. 기본 방향이 column 이라 주축이 세로입니다
    두 칸 다 교차축은 stretch 라서 꽉 차 있습니다. 남는 자리는 주축 쪽에만 생깁니다.
    축 속성 값
    주축 justify-content flex-start flex-end center space-between space-around
    교차축 align-items flex-start flex-end center stretch
    교차축 (한 칸만) align-self 위와 같고 auto 가 추가

    align-items 의 기본값이 stretch 입니다. 이것 때문에 처음에 헷갈리는 장면이 하나 나옵니다. 세로로 쌓은 칸에 가로 폭을 하나도 안 줬는데 부모 폭을 꽉 채우고 있습니다. 교차축이 늘어나기 때문입니다.

    내용 크기만큼만 쓰고 싶으면 부모에 align-items: flex-start 를 주거나, 그 칸에만 align-self: flex-start 를 줍니다.

    줄바꿈은 flex-wrap 입니다. nowrap 이 기본이고 wrap, wrap-reverse 가 있습니다. uGUI 의 Grid Layout Group 에 해당하는 것이 따로 없어서, 격자는 보통 flex-wrap: wrap 에 칸 폭을 정해서 만듭니다.

    크기는 세 군데에서 옵니다

    width 와 height 를 직접 적는 것이 첫째입니다. px 와 % 를 씁니다. % 는 부모 크기 기준입니다.

    둘째가 위에서 본 flex-basis 와 flex-grow 입니다. 셋째가 아무것도 안 적었을 때의 내용 크기입니다.

    경계는 min-width max-width min-height max-height 로 겁니다. flex-shrink 가 기본 1 이라 칸은 기본적으로 줄어드는데, 글자가 뭉개지면 안 되는 칸에는 min-width 를 걸어 두는 쪽이 낫습니다.

    짧은 글자일수록 번역에서 더 길어진다에서 본 문제가 여기서도 똑같습니다. 다른 점은 uGUI 의 Content Size Fitter 에 해당하는 것이 따로 없다는 것입니다. 아무것도 안 적으면 그게 이미 내용 크기입니다.

    겹쳐 놓을 때만 absolute 입니다

    쌓기로 안 되는 배치가 있습니다. 배지를 아이콘 오른쪽 위에 얹는다든가, 팝업을 화면 위에 띄운다든가 하는 것입니다. 그때 쓰는 것이 position 입니다.

    흐름 안에 있는가, 빠져 있는가 position: relative 차례로 쌓입니다 position: absolute 배지 흐름에서 빠져 위에 얹힙니다 left: 0; right: 0 양쪽에 붙어 늘어납니다 stretch 앵커가 하던 일
    가운데 그림에서 아래 칸은 배지가 없는 것처럼 자리를 잡습니다.

    기본값은 relative 이고, 이때 요소는 쌓기 흐름 안에 있습니다. left 나 top 을 주면 자기 자리를 그대로 차지한 채로 그만큼 밀려납니다.

    absolute 는 다릅니다. 문서 표현으로 “Absolute mode makes an element invisible to the default Flexbox-based layout engine” 입니다. 흐름에서 빠지기 때문에 형제들 배치가 그 요소를 없는 것으로 치고 다시 잡힙니다.

    여기서 uGUI 의 앵커와 만나는 지점이 하나 있습니다. left 와 right 를 둘 다 주면 칸이 그 사이만큼 늘어납니다. top 과 bottom 도 마찬가지입니다. RectTransform 의 stretch 앵커가 하던 일이 이 두 줄입니다.

    .overlay {
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
    }

    다만 문서가 남용을 막아 뒀습니다. “Avoid using Absolute position mode because it bypasses the built-in layout engine in UI Toolkit and can lead to high-maintenance UI.” 오버레이와 팝업에는 맞고, 일반 배치를 이걸로 짜면 화면 크기가 바뀔 때마다 손이 갑니다.

    숨기는 방법이 두 가지입니다

    한 칸이 바뀌면 그 캔버스는 통째로 다시 계산된다에서 uGUI 쪽 창 숨기기 세 가지를 봤습니다. 여기서 그 자리에 있는 것이 두 줄입니다.

    적는 것 칸 언제
    display: none 칸까지 없어집니다 목록에서 항목을 빼듯 배치가 다시 잡혀야 할 때
    visibility: hidden 칸은 그대로 남습니다 자리는 지키고 안 보이게만 할 때

    문서 표현으로 “Set the visibility to hidden to hide the element, but the element still occupies space in the layout” 입니다. display 쪽은 값이 flex 와 none 둘뿐입니다. CSS 의 display 를 아는 사람에게는 이 부분이 좁아 보이는데, 문서가 “supports keywords that work with the Yoga layout engine” 이라고 이유를 적어 뒀습니다.

    opacity: 0 도 있습니다. 칸도 그대로이고 값이 0 과 1 사이로 이어져서, 페이드처럼 전환이 필요한 자리에 씁니다.

    내용이 넘칠 때 자르는 것은 overflow 입니다. 기본값이 visible 이라 안 잘립니다. 스크롤 영역이나 카드 안쪽처럼 잘려야 하는 곳에는 overflow: hidden 을 직접 적어야 합니다.

    정리

    • 앵커와 피벗이 없습니다. 부모가 방향을 정하고 자식이 쌓입니다. 모든 요소에 Layout Group 이 붙어 있는 상태라고 보면 됩니다
    • 기본 방향이 column 입니다. CSS 의 기본값과 다릅니다
    • flex-grow 기본값이 0 이라 안 적으면 내용 크기입니다. 남는 자리를 나누려면 적어야 합니다
    • 전부 flex-grow: 1 이어도 크기가 같아지지 않습니다. 똑같이 나누려면 flex-basis: 0 을 같이 줍니다
    • 주축은 justify-content, 교차축은 align-items 입니다. align-items 기본값이 stretch 라서 폭을 안 줘도 꽉 찹니다
    • position: absolute 는 흐름에서 빠집니다. left 와 right 를 둘 다 주면 stretch 앵커처럼 늘어납니다. 오버레이에만 씁니다
    • display: none 은 칸까지 없애고 visibility: hidden 은 칸을 남깁니다. overflow 기본값은 visible 이라 안 잘립니다