728x90
반응형
SMALL
목차
1. 뷰포트를 사용한 이유
2. css 단위 변경 및 이유
3. 결과
1. 뷰포트를 사용한 이유
처음 reracle프로젝트는 화면 크기에 따라 레이아웃이 자동으로 조정되도록 모든 레이아웃에 vh와 vw 단위를 사용했습니다. vh는 화면의 높이를, vw는 화면의 너비를 기준으로 비율을 설정해주는 단위인데, 이 단위를 사용하면 화면 크기가 달라질 때마다 비율에 맞춰 요소들이 함께 변형되어서 반응형 디자인을 쉽게 구현할 수 있다는 장점이 있어서 사용했습니다.
하지만 리팩토링을 할 때 고민을 해보니 vh, vw에는 몇 가지 단점이 있었습니다.
export const WasteCategory = () => {
const chunkedCategories = chunkArray(wasteCategories, 9);
return (
<section className="w-[56.3vh] h-[76.7vh] flex flex-col justify-center overflow-y-auto">
<div className="flex items-center rounded-[2vh] border border-purple-600 gap-[1.5vh] px-[2.5vh] mx-auto">
<FaSearch />
<SearchBar />
</div>
<hr className="w-[46vh] h-px my-[3vh] bg-purple-600 mx-auto" />
<h2 className="text-[2.3vh] font-bold text-purple-600 mt-[0.6vh] ml-[5vh]">재활용품 분류</h2>
<SlCarousel pagination mouse-dragging className="w-[46vh] h-[60vh] mx-auto">
{chunkedCategories.map((chunk, index) => (
<SlCarouselItem key={index} className="mb-7">
<div className="grid grid-cols-3 gap-y-[1.5vh] w-[45vh] mt-[2vh]">
{chunk.map((category) => (
<NavLink key={category.id} to={`/${category.id}`} className="text-gray-800 no-underline">
<div className="bg-yellow-100 w-3/4 h-[6rem] flex justify-center items-center rounded-lg mx-auto hover:bg-yellow-300 cursor-pointer">
{category.img && (
<img
src={wasteCategoryImages[category.img]}
alt={category.name}
className="flex justify-center max-w-[60%] h-auto"
/>
)}
</div>
<p className="mt-1 text-base font-medium text-center">{category.name}</p>
</NavLink>
))}
</div>
</SlCarouselItem>
))}
</SlCarousel>
</section>
);
};
export default WasteCategory;
2. css 단위 변경 및 이유
작은 화면에서 불편함
- 모바일 화면처럼 작고 다양한 크기의 디바이스에서는 화면에 맞게 적절한 비율로 콘텐츠가 표시되지 않을 수 있었습니다. 특히, 화면의 높이(vh)가 작은 경우 요소들이 너무 작게 보이는 상황이 있었습니다.
스크롤 바 문제
- 브라우저에서 스크롤 바가 생기면 vw, vh 단위로 설정한 레이아웃이 미묘하게 어긋날 때가 있어요. 스크롤바가 차지하는 공간을 계산하지 못해서 디자인이 깨지는 경우가 있었습니다.
크기 제어 어려움
- vh와 vw는 화면 크기를 기준으로 하다 보니, 정확한 픽셀 단위 제어나 특정 요소의 크기를 고정하기가 어려워 다양한 모바일 사이즈를 고려해서 개발하기가 어려웠습니다.
그래서 vw, vh에서 px, rem, %, vh로 리팩토링을 하기로 결정했습니다.
- rem: rem(Root EM) 단위는 ****HTML 루트 요소의 폰트 사이즈가 계산의 기반값**입니다. 루트 요소의 폰트 사이즈는 웹 브라우저의 기본 폰트 사이즈가 결정하며, 대부분의 웹 브라우저에서 16px로 정해놓고 있습니다. 또한, 저시력자 접근성에 대응 유용합니다.
- px: 픽셀 단위 px는 CSS에서 가장 기본적인 단위입니다.
- %: 부모 요소에 비례해서 크기를 설정할 수 있어서 유동적인 레이아웃을 만들 때 유용합니다.
- vh: 여전히 뷰포트 높이에 따라 조정되는 특정 요소에서는 vh가 적합하여, 일부 요소에 사용했습니다. 예를 들어, 전체 화면의 높이에 맞춰 자동으로 크기가 조정되어야 해서 사용했습니다.
import { useEffect, useState } from "react";
import { db } from "@/firebase";
import { collection, getDocs } from "firebase/firestore";
import { NavLink } from "react-router-dom";
import { chunkArray } from "@/lib/utils/chunkArray";
import {
SlCarousel,
SlCarouselItem,
} from "@shoelace-style/shoelace/dist/react";
import { SearchBar } from "@/lib/common/SearchBar";
type Category = {
id: string;
name: string;
imageURL: string;
};
const Category = () => {
const [categories, setCategories] = useState<Category[]>([]);
const getCategories = async () => {
try {
const categoriesCollectionRef = collection(db, "WasteCategories");
const categoriesSnap = await getDocs(categoriesCollectionRef);
const categoriesData = categoriesSnap.docs.map((doc) => ({
id: doc.id,
...doc.data(),
})) as Category[];
setCategories(categoriesData);
} catch (error) {
console.error("카테고리가 존재하지 않습니다.", error);
}
};
useEffect(() => {
getCategories();
}, []);
const chunkedCategories = chunkArray(categories, 9);
return (
<section className="w-full h-[73vh] flex flex-col justify-center overflow-y-auto">
<div className="mx-auto mt-1">
<SearchBar />
</div>
<h2 className="ml-[5vh] mt-[2vh] text-xl font-bold text-purple">
재활용품 분류
</h2>
<SlCarousel
pagination
mouse-dragging
className="w-full h-[30rem] mx-auto"
>
{chunkedCategories.map((chunk, index) => (
<SlCarouselItem key={index}>
<div className="grid grid-cols-3 gap-y-2 w-[25rem]">
{chunk.map((category) => (
<div key={category.id}>
<NavLink
to={`/category/${category.id}`}
className="no-underline"
>
<div className="bg-yellowLight w-3/4 h-[6rem] flex justify-center items-center rounded-lg mx-auto hover:bg-yellow cursor-pointer">
<img
src={category.imageURL}
alt={category.name}
className="w-[2.5rem] h-[2.5rem]"
/>
</div>
<p className="text-sm font-semibold text-center ">
{category.name}
</p>
</NavLink>
</div>
))}
</div>
</SlCarouselItem>
))}
</SlCarousel>
</section>
);
};
export default Category;
3. 결과
- 리팩토링 후 레이아웃은 각기 다른 디바이스와 화면 크기에 적응하여, 일관된 사용자 경험을 제공합니다. 특히, 작은 모바일 화면에서 잘리지 않도록 설계해 다양한 환경에서의 편리함과 접근성을 개선했습니다.

728x90
반응형
LIST
'FE' 카테고리의 다른 글
서버와 클라우드 컴퓨팅, AWS (0) | 2025.01.13 |
---|---|
AllYouRaffle 사이드 프로젝트 운영했던 경험 (4) | 2025.01.03 |
REracle: 비용 효율적인 서비스 운영 (0) | 2024.12.29 |
React PWA에서 푸시 알림 구현하기: 사용자 경험을 한 단계 업그레이드하세요 (3) | 2024.12.29 |
Styled-components에서 Tailwind로 전환한 이유 (1) | 2024.11.04 |