React에서 부모 Flex 방향을 자식에게 전달하는 방법 | data-attribute 패턴
Flexbox의 flex-direction에 따라 자식 노드의 fill 사이징 CSS가 달라지는 문제를, HTML data-attribute로 해결한 실전 사례를 코드와 함께 정리합니다.

웹 에디터에서 Framer 스타일의 사이징 시스템을 구현하면서, 같은 fill 모드라도 부모의 flex-direction에 따라 적용해야 하는 CSS가 완전히 달라지는 문제를 만났다. 이 글에서는 문제의 원인을 분석하고, data-attribute를 활용해 해결한 과정을 코드와 함께 정리한다.
문제 상황: fill 사이징과 Flexbox 축의 관계
fill 사이징이란
fill은 부모의 남은 공간을 채운다는 의미의 사이징 모드다. Framer, Figma 같은 디자인 도구에서 흔히 볼 수 있는 개념으로, CSS에서는 Flexbox의 속성을 조합해 구현해야 한다.
문제는 이걸 CSS로 변환하려면 flex: 1과 alignSelf: "stretch" 중 어느 쪽을 써야 하는지가 부모의 flex-direction에 따라 달라진다는 점이다.
Flexbox의 main axis와 cross axis
Flexbox에는 main axis(정렬 방향)와 cross axis(교차 방향)라는 두 개의 축이 있다. flex-direction이 바뀌면 두 축의 역할이 뒤바뀐다.
codeColumn Stack (세로 배치):
main axis = height (세로)
cross axis = width (가로)
Row Stack (가로 배치):
main axis = width (가로)
cross axis = height (세로)
fill을 CSS로 변환할 때의 규칙은 다음과 같다.
- main axis에서의 fill →
flex: 1— 남은 공간을 비율로 채움 - cross axis에서의 fill →
alignSelf: "stretch"— 교차 방향으로 늘림
이를 표로 정리하면 다음과 같다.
| 사이징 모드 | Column Stack (세로) | Row Stack (가로) |
|---|---|---|
widthMode: "fill" |
alignSelf: "stretch" (cross) |
flex: 1 (main) |
heightMode: "fill" |
flex: 1 (main) |
alignSelf: "stretch" (cross) |
같은 widthMode: "fill"이라도 부모가 column이면 alignSelf: "stretch", row면 flex: 1이 된다. 자식 노드가 자기 스타일을 결정하려면 부모의 direction을 반드시 알아야 한다.
해결: data-attribute로 부모 방향을 DOM에 노출
React prop 전달이 불가능한 구조적 이유
일반적인 React 패턴이라면 부모가 자식에게 direction을 prop으로 내려주면 된다. 하지만 이 에디터에서는 적합하지 않았다.
크기를 결정하는 resolveSizeStyle 유틸리티 함수는 React 컴포넌트 트리 바깥에서 독립적으로 동작한다. 노드 데이터와 DOM 정보만으로 스타일을 계산하는 구조이기 때문에, React의 prop 체인에 접근할 수 없다.
Stack 컴포넌트에 data-stack-direction 추가
Stack 컴포넌트가 렌더링할 때 data-stack-direction 속성을 DOM 요소에 남기도록 했다.
tsx// Stack.tsx — 부모 컴포넌트
export default function StackComponent({ node, children }: NodeComponentProps<StackNode>) {
const direction = node.props.direction ?? "column";
return (
<div
data-component-id={node.id}
data-stack-direction={direction} // ← 자식이 읽을 수 있도록 노출
style={{ ...cssProps, flexDirection: direction }}
>
{children}
</div>
);
}
핵심은 data-stack-direction={direction} 한 줄이다. 이 속성은 브라우저 렌더링에 영향을 주지 않지만, 다른 코드에서 부모의 방향 정보를 읽을 수 있게 한다.
자식의 크기 계산 함수에서 부모 방향 읽기
자식의 CSS를 결정하는 resolveSizeStyle에서 부모 DOM 요소의 data-stack-direction을 읽는다.
tsx// resolveSizeStyle.ts — 자식의 크기 계산
const parentEl = document.querySelector(`[data-component-id="${parentNode.id}"]`);
const parentDir = parentEl?.getAttribute("data-stack-direction") as "row" | "column" ?? "column";
// 부모 방향(main/cross axis)에 따라 fill → CSS 변환 분기
if (widthMode === "fill") {
if (parentDir === "row") {
style.flex = 1; // main axis → flex로 남은 공간 채움
} else {
style.alignSelf = "stretch"; // cross axis → stretch로 교차 방향 늘림
}
}
querySelector로 부모 요소를 찾고, getAttribute로 문자열 하나를 읽는 것만으로 축 판별이 끝난다.
getComputedStyle 대신 data-attribute를 선택한 이유
getComputedStyle(parentEl).flexDirection으로도 같은 값을 얻을 수 있다. 그럼에도 data-attribute를 선택한 이유는 두 가지다.
1. 성능: 레이아웃 재계산 없이 값 읽기
getComputedStyle()은 호출 시 브라우저가 레이아웃 재계산(reflow)을 트리거할 수 있는 무거운 API다. 반면 getAttribute()는 DOM 요소에 저장된 문자열을 단순히 읽는 연산으로, 비교할 수 없을 만큼 가볍다.
2. 명시성: 코드 의도가 드러나는 네이밍
data-stack-direction이라는 이름에서 "이 요소는 Stack이고, 방향 정보를 자식에게 제공한다"는 설계 의도가 명확히 드러난다. getComputedStyle로 스타일 값을 우회적으로 읽는 방식보다 코드를 읽는 사람에게 의도를 전달하기 쉽다.
정리: data-attribute 패턴이 유효한 조건
data-attribute를 통해 부모 상태를 자식에서 조회하는 이 패턴은 만능이 아니다. 다음 조건에서 유효하다.
- React prop 체인 밖에서 부모 정보에 접근해야 할 때
- 부모의 특정 속성이 자식의 렌더링 로직을 결정할 때
getComputedStyle()대신 가볍고 명시적인 값 전달이 필요할 때
반대로, 모든 로직이 React 컴포넌트 안에서 완결된다면 prop 전달이나 Context API가 더 적절하다. data-attribute는 DOM을 통해 부모 상태를 조회해야 하는 특수한 상황에서 유용한 패턴이다.
Flexbox flex-direction main axis / cross axis data-attribute fill 사이징 getComputedStyle 대안 flex:1 vs alignSelf:stretch