- Native에서 반응형 UI를 구현하기 위한 크기 변환 유틸 함수 모음입니다. - 기기의 실제 화면 크기(Dimensions)와 디자인 기준 크기(예: iPhone X 기준 375x812)를 비교해서, 모든 UI 요소가 다양한 화면 해상도에서도 비슷한 비율로 보이도록 돕는 구조입니다.
1. 반응형 함수
함수명사용처특징 / 용도
함수명
사용처
특징 / 용도
scaleWidth(val: number)
width, marginLeft/Right, paddingLeft/Right 등 가로 관련 스타일
- 디자인 기준(375px) 대비 실제 기기 width 비율 적용 - 기기의 가로 크기에 따라 비례 조정
scaleHeight(val: number)
height, marginTop/Bottom, paddingTop/Bottom, lineHeight 등 세로 관련 스타일
- 디자인 기준(812px) 대비 실제 기기 height 비율 적용 - 기기의 세로 크기에 따라 비례 조정
scale (상수)
내부 계산용
- 가로/세로 중 작은 비율 선택- 화면 비율이 극단적으로 다른 기기에서도 균형 유지
moderateScale(size: number, factor = 1)
모든 크기 (특히 여백, 버튼 등)
- 단순 scaleWidth 보정 버전- factor로 보정 정도 조절 가능 (0~1)- 너무 큰/작은 값 차이를 줄여줌
scaledSize(size: number)
fontSize
- scale 비율 기반 폰트 크기 조정- 소수점 올림(ceil) 적용 → 텍스트가 흐릿하지 않게 보장
import { Dimensions } from "react-native";
const { height: screenHeight, width: screenWidth } = Dimensions.get("window");
// resolution changes as per design
export const designWidth = 375;
export const designHeight = 812;
/**
* 디바이스 별 반응형 '너비' 적용 함수
* 사용처 -> width, marginWidth, paddingWidth
* @param val
* @returns
*/
const scaleWidth = (val: number) => {
return (screenWidth * val) / designWidth;
};
/**
* 디바이스 별 반응형 '높이' 적용 함수
* 사용처 -> height, marginHeight, paddingHeight, lineHeight
* @param val
* @returns
*/
const scaleHeight = (val: number) => {
return (screenHeight * val) / designHeight;
};
/**
* 단순 비율 조정이 너무 극단적인 경우 적당히 보정하는 경우
* @param size
* @factor size
* @returns
*/
const scale = Math.min(screenWidth / designWidth, screenHeight / designHeight);
/**
* 디바이스 별 단순 비율 조정이 너무 극단적인 경우, 적당히 보정
* @param size
* @factor size
* @returns
*/
const moderateScale = (size: number, factor = 1) =>
size + (scaleWidth(size) - size) * factor;
/**
* 디바이스 별 반응형 '폰트 사이즈' 적용 함수
* 사용처 -> fontSize
* @param size
* @returns
*/
const scaledSize = (size: number) => Math.ceil(size * scale);
export {
moderateScale,
scaledSize,
scaleHeight,
scaleWidth,
screenHeight,
screenWidth,
};
- 위에 함수는 이용하되, 매번 함수 호출을 하여서 디자인을 적용하는 방식이 아닌 별도의 StyleSheet를 구성하여서 감싸는(Wrapper) 방식을 통해서 이를 적용하였습니다.
1. Custom StyleSheet Wrapper
💡 Custom StyleSheet Wrapper
- StyleSheet.create를 감싸서 자동으로 scale을 적용시키는 방식입니다. 해당 방식은 공통적으로 사용하는 스타일 시트를 최초로 설정하고 나서 수행하는 방식입니다. - 이 방식을 사용하면 별도의 DementionUtils를 설정하지 않고 각각의 수치만 입력하면 됩니다.
// utils/ScaledStyleSheet.ts
import { StyleSheet, ViewStyle, TextStyle, ImageStyle } from 'react-native';
import DementionUtils from './DementionUtils';
const { heightRelateSize, widthRelateSize, fontRelateSize } = DementionUtils;
/**
* 어떤 스타일 속성이 width 관련, height 관련, font 관련인지 구분하는 규칙표입니다.
* 크게는 width, height, font 그룹 내로 포함시킵니다.
*/
const KEY_MAP: Record<string, 'width' | 'height' | 'font' | null> = {
// width 관련
width: 'width',
left: 'width',
right: 'width',
marginLeft: 'width',
marginRight: 'width',
paddingLeft: 'width',
paddingRight: 'width',
marginHorizontal: 'width',
paddingHorizontal: 'width',
// height 관련
height: 'height',
top: 'height',
bottom: 'height',
marginTop: 'height',
marginBottom: 'height',
paddingTop: 'height',
paddingBottom: 'height',
marginVertical: 'height',
paddingVertical: 'height',
lineHeight: 'height',
// font 관련
fontSize: 'font',
letterSpacing: 'font',
};
/**
* 스타일에 속성에 맞게 확인 후 그를 변환 함수로 호출하여 실행합니다.
* - width : widthRelateSize 함수 호출
* - height : heightRelateSize 함수 호출
* - font : fontRelateSize 함수 호출
* @param prop
* @param value
* @returns
*/
const scaleValue = (prop: string, value: number): number => {
switch (KEY_MAP[prop]) {
case 'width':
return widthRelateSize(value);
case 'height':
return heightRelateSize(value);
case 'font':
return fontRelateSize(value);
default:
return value;
}
};
/**
* StyleSheet.create에 전달할 스타일 객체를 순회하면서, 숫자 값은 scaleValue로 변환해 줍니다.
* @param styles
* @returns
*/
function mapScaled<T extends Record<string, ViewStyle | TextStyle | ImageStyle>>(styles: T): T {
return Object.entries(styles).reduce((acc, [styleName, styleObj]) => {
if (typeof styleObj !== 'object') {
(acc as any)[styleName] = styleObj;
return acc;
}
const newStyle = Object.entries(styleObj).reduce((sAcc, [prop, value]) => {
(sAcc as any)[prop] = typeof value === 'number' ? scaleValue(prop, value) : value;
return sAcc;
}, {} as ViewStyle | TextStyle | ImageStyle);
(acc as any)[styleName] = newStyle;
return acc;
}, {} as T);
}
/**
* 내부적으로 mapScaled을 거쳐 모든 수치 값을 변환한 뒤, 원래 RN StyleSheet.create로 넘겨줍니다
*/
const ScaledStyleSheet = {
create: <T extends StyleSheet.NamedStyles<T>>(styles: T) => StyleSheet.create(mapScaled(styles)),
};
export default ScaledStyleSheet;
💡 Custom StyleSheet Wrapper - 기존과 다르게 함수를 포함하지 않고, 정상적인 수치만 작성하면 됩니다. 대신 StyleSheet의 create를 하는 것이 아닌 ScaledStyleSheet.create()를 통해서 스타일을 적용합니다.