플러터에서는 딱히 단위가 필요 없었다. 위젯의 크기, 텍스트의 크기는 논리 픽셀(Logical Pixel, lp)로 결정되었다. 그런데, 안드로이드 네이티브로 돌아오니, dp, sp 단위가 생겼다. 거기에다 em 단위도 있다.
dp와 sp는 어떻게 다르며, 왜 필요할까?
dp
density-independent pixel - 화면 밀도에 독립적인 단위로, UI 레이아웃의 기본 단위이다 160dpi 화면에서 1dp = 1px.
dp는 어떠한 화면에서도 일정한 크기를 가질 수 있도록 해주는 유연한 단위이다.
그렇기에, dp 단위는 개발자가 다양한 크기의 화면에 대응하는 레이아웃을 쉽게 만들 수 있도록 돕는 단위라 할 수 있다.
sp
scale-independent pixel - dp와 동일하지만 시스템 글꼴 크기 설정(접근성)이 추가로 반영된다. 텍스트 크기의 기본 단위.
dp와 동일하게 작동하나, 폰트에 쓰인다. fontScale이 1.0일때 sp는 dp와 동일하다. 안드로이드 시스템에서 폰트의 크기는 시스템 글꼴 크기 설정에 따라 계산된다.
그렇기에, sp 단위는 개발자가 사용자가 설정한 시스템 글꼴 크기 설정대로 폰트 크기를 변형할 수 있게 하는 단위라 할 수 있다.
em
- 폰트 크기를 기준으로 한 상대적인 배수 단위이다.
1.em은 현재 폰트 사이즈의 100%와 같다. - 자간, 행간, 줄 높이 등, 폰트 크기와 연관 있는 곳에서 사용된다.
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(width = 29.dp, height = 24.dp)
.clip(RoundedCornerShape(12.dp)),
)
Text(
"content",
overflow = TextOverflow.Ellipsis,
maxLines = 2,
fontSize = 14.sp,
color = Colors.ContentText,
)
하나로 사용할 순 없을까?
Flutter에서는 실수에 단위를 붙이지 않고 실수 그대로 사용한다. 위젯 레이아웃 크기에서도 동일하게 단위를 사용하지 않고 텍스트 위젯에서도 단위를 사용하지 않는다.
Container(
width: 300,
height:280,
)
Text(
"텍스트",
style: TextStyle(fontSize: 18)
)
개발편의성 측면에서 안드로이드에서도 단위를 사용하지 않고 하나로 사용할 수 있지 않을까? sp는 dp에 TextScale 값을 곱하여 계산하니까, 텍스트 관련한 컴포넌트에서 따로 처리를 하면 될 것도 같다.
Text 컴포넌트 내부에서 dp 값을 받되, 시스템의 폰트 스케일 팩터를 자동으로 곱해주면 될 것도 같다. 실제로 Flutter는 TextScaler를 통해 위젯 레벨에서 이 처리를 하고 있고, 개발자는 단위를 구분할 필요 없이 logical pixel 하나만 사용한다.
이 점에서 Android Compose와 Flutter는 다른 접근 방법을 취했다. Compose는 .sp라는 단위를 사용하게 해 “텍스트가 아닌 곳에서도 폰트 스케일을 고려하여 크기를 지정할 수 있다”.
텍스트가 아닌 텍스트 옆에 붙는 아이콘 크기, 텍스트를 감싸는 패딩, 텍스트 기반 버튼의 최소 높이 등, 이 크기들은 글꼴 크기가 커질 때 같이 커져야 자연스럽다.
Flutter에서는 같은 방식으로 텍스트가 아닌 곳에 글꼴 크기를 설정하려면 MediaQuery.textScaleFactorOf 를 가져와서 수동으로 곱해주어야 한다. 단위가 하나이기 때문에 텍스트와 관련 없는 위젯에서는 편리하겠지만, 텍스트 크기 연동이 필요하면 수작업이 늘어날 것이다.
안드로이드 컴포즈의 설계 방침은 이해 되나 이게 최선일까 하고 궁금하다. Flutter를 만들때에도 나처럼 sp, dp를 구분하는게 귀찮은 마음에 lp 단위를 만들어서 적용하지 않았을까? 다음에 텍스트 컴포넌트를 만들 일이 있으면 더 편하게 단위를 지정할 방법을 찾아봐야겠다.