728x90

https://ungumungum.tistory.com/48#comment18876777

 

React에서 화면 크기를 구해주는 커스텀 훅 만들기 (feat. UI라이브러리에 반응형 크기 주기)

화면 크기를 구해주는 커스텀 훅을 만들게 된 이유는 UI 라이브러리의 경우 간혹 max-width나 100% 같은 반응형 크기가 먹히지 않는 경우가 있습니다. 제가 사용하는 calendar , cropImage, antd 라이브러리

ungumungum.tistory.com

 

현재 커스텀훅을 통하여서 windowWidth를 구하는데, 이에 대한 링크는 위에 남겨두겠습니다.

 

 

자바스크립트가 아닌 타입스크립트를 쓰는 시점에서 ,   *1 과 같이 암묵적 타입 변환을 하려고 하면 아래와 같은 에러가 뜹니다.

산술 연산의 왼쪽은 'any', 'number', 'bigint' 또는 열거형 형식이어야 합니다.ts(2362) 

 

따라서 string을  int 로 변환하는 단계가 필요한데, 앞서서 debounce 훅의 경우에는  string 값이 주로 활용되기 떄문에, 그냥 useCalWindowWidth 훅에서도 string을 반환하려고 했습니다. 하지만 string으로 반환을 하다보니 중간로직에 계쏙 불필요한 타입변화 로직이 필요했습니다. 결국 최종적으로 라이브러리에 스타일을 줄 때는 숫자값+"단위" 형식의 string을 줘야하지만, 크기를 변환한다던가, 넓이 기반으로 높이를 구할 때는 number 타입인게 훨씬 편합니다.

 

따라서 string으로 반환했던 windowWidth로 짜였던 코드들을 number로 리팩토링 하게 되었습니다.

 

우선 대략적인 로직은 아래와 같습니다.

const windowWidth = useCalWindowWidth();
const [uploaderSize, setUploaderSize] = useState({ width: '28rem', height: '21rem' });
useEffect(() => {
	const width = pxToRemWithResizer(windowWidth, 3 / 10);
	const height = ratioConverter(width, 3 / 4, 'rem');
	setUploaderSize({ width, height });
}, [windowWidth]);

윈도우 넓이 구하기,, 라이브러리에 크기를 줄 uploaderSize 선언

useEffect로  윈도우 넓이가 변할때마다 크기를 셋해주기

하지만 이때 px기반의 넓이를 Rem 단위로 바꾸고, 비율에 맞게 높이 설정하기

(width의 경우 조건문을 통해서 반응형을 구현해야 하지만 그전에 리팩토링을 하려고 합니다.)

 

그리고 그 과정을 진행해줄 유틸함수는

const pxToRemWithResizer = (px: string, ratio: number) => {
	const rootFontNum = parseFloat(rootFontSize.slice(0, -2)); // rootFontSize를 숫자로 변환

	const returnNum = (ratio * parseFloat(px)) / rootFontNum;
	if (isNaN(returnNum)) {
		alert('숫자로 된 값만 입력됩니다. windowWidth 그대로 단위없이 넣어주세요');
		return '16rem';
	} else {
		return returnNum + 'rem';
	}
};

const ratioConverter = (origin: string, ratio: number, unit: string) => {
	const ratioOriginNum = parseFloat(origin.slice(0, -unit.length)); // rootFontSize를 숫자로 변환
	return ratioOriginNum * ratio + unit;
};

문자값을 입력받고~ 그걸 단위 분리해주고, 비율입력받고,, 너무 불편한 구조입니다.

만약 width가 number 타입이었고, 그걸 곱한다음에 최종적으로 set할때만 단위를 더하면 되는 로직을 만들었다면!!...

 

따라서 리팩토링을 진행했습니다.

 

불필요하다 느낀 함수 ratioConverter,  효율성을 위해서 화면 넓이는 number 타입으로 반환. 그리고 이에 맞게 나머지 함수들을 수정

 

1. 화면 넓이 number 타입으로 반환하기

import { useEffect, useState } from 'react';
import { useDebounce } from '.';

export const useCalWindowWidth = () => {
	const [windowSize, setWindowSize] = useState(window.innerWidth);
	const debouncedValue = useDebounce(window.innerWidth + '', 300);
	useEffect(() => {
		const handleResize = () => {
			setWindowSize(parseFloat(debouncedValue));
		};

		window.addEventListener('resize', handleResize);

		return () => {
			window.removeEventListener('resize', handleResize);
		};
	}, [debouncedValue]);

	return windowSize;
};

debounce 훅에서는 타입변환을 통해서 여전히 string 값을 넘겨줍니다.

하지만 debouncedValue의 반환값이 숫자로 암묵적타입변환이 가능한 값이기에 parseFloat을 통해서  숫자형으로 변환한 후에 windowSize에 상태를 저장합니다.

 

2. px단위의 값을 root 폰트 사이즈를 통해서 나누는 함수 리팩토링

const pxToRemWithResizer = (pxUnitNum: number) => {
	const rootFontNum = parseFloat(rootFontSize.slice(0, -2)); // rootFontSize를 숫자로 변환
	const returnNum = pxUnitNum / rootFontNum;
	return returnNum;
};

export {  pxToRemWithResizer };

스타일 리팩토링을 용이하게 하기 위해서  팀에선 px보다 rem 단위를 가급적이면 쓰기로 하였습니다.

하지만 window.innerWidth 를 통해서 구하는 값의 단위는 px이죠.. 따라서 이를 변환하기 좋은 util함수가 있으면 팀에 도움이 될 것 같습니다. 하지만 불필요한 string타입으로 값을 받아서 복잡했떤 코드를 위와 같이 좀 더 간단하게 리팩토링했습니다.

 

기존에 역할하던 resize기능은 굳이 위 함수를 통해 할 필요가 없으니 함수명도  pxToRem 으로 바꾸면 좋겠네요!

 

 

최종적으로 반응형 반응한 코드는 다음과 같습니다.

 

	const windowWidth = useCalWindowWidth();
	const [uploaderSize, setUploaderSize] = useState({ width: '28rem', height: '21rem' });

	useEffect(() => {
		let widthNumber: number;
		const ratio = 3 / 4;

		switch (true) {
			case windowWidth < 427:
				widthNumber = 16;
				break;
			case windowWidth >= 427 && windowWidth < 747:
				widthNumber = pxToRem(windowWidth) * 0.6;
				console.log(widthNumber);
				break;
			default:
				widthNumber = 28;
		}
		const width = widthNumber + 'rem';
		const height = widthNumber * ratio + 'rem';
		setUploaderSize({ width, height });
	}, [windowWidth]);

하단에 있는 이미지와 크기를 맞추기 위해서 위와 같이 분기처리하였습니다.

const HouseImg = styled.img`
	justify-self: center;
	max-width: 28rem;
	margin-bottom: 0.5rem;
	object-fit: contain;
	border-radius: 0.5rem;
	width: 60vw;
	min-width: 16rem;
`;

 

+ Recent posts