반응형 웹사이트란, 하나의 웹 템플릿을 통해 모바일, 태블릿, 데스크탑 모든 기기에 대응 할 수 있도록 만든 웹사이트이다.
해상도별로 보여질 화면을 꼼꼼하게 작업해야 하기 때문에 개발시 고정된 px값을 사용하는 것이 아니라 미디어 쿼리와 상대단위를 이용해서 작업한다.
적응형 웹이란, 모바일, 테블릿, 데스크탑 별로 각각 독립적인 템플릿을 만든 후 디바이스에 따라 페이지를 다르게 보여주는 웹이다.
반응형 웹과 비교했을 때, 감지된 디바이스에 꼭 필요한 콘텐츠만 다운받기때문에 로딩속도가 더 빠르다.
펫모리 프로젝트에서는 웹에서의 사용성을 중점에두고 반응형 작업을 진행했다.
먼저 태블릿 기준인 1024px, 모바일 가로기준 768px으로 각각의 화면이 사용자에게 깨지지 않도록 작업하였다.
PC에서 사용자가 두개의 컨텐츠를 띄워서 반으로 사용하는 경우가 많기 때문에, 이때 보여지는 화면도 고려하였다.
먼저 마크업 과정에서 아이템을 묶는 컨테이너 같은 경우 flex나 grid같은 반응형 컨테이너를 사용하였다.
flex나 grid를 사용하면 컨테이너 사이즈에 따라 아이템의 사이즈가 유동적으로 줄어들거나 늘어난다.
width값을 고정 px로 주게되면 해상도에 따라 컨테이너 사이즈가 줄어들지 않는다. 따라서, vw 나 %를 이용한 상대단위를 통해 해상도에 따른 컨테이너 사이즈를 유동적으로 조절하였다.
marign값도 상대단위와 min함수를 사용하였다.
미디어쿼리를 이용해 화면 width가 1024px 아래일 경우 그리드를 2개씩 보여주는 것으로 변경하였다.
import styled from 'styled-components';
export const AlbumContainer = styled.section`
margin: min(70px, 5vw) 10vw;
width: 80vw;
height: 100vh;
overflow: auto;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 24px;
...
@media screen and (max-width: 1024px) {
grid-template-columns: repeat(2, 1fr);
&.imgLink {
font-size: 10px;
}
&.figItemImg {
margin-bottom: 10px;
}
}
`;
UI 컴포넌트를 작업할때는 상대단위를 이용해 작업해야한다.
먼저 공통으로 사용하는 공용 버튼 컴포넌트가 존재했는데, 화면별로 재사용하기 위해 width, height, max-width, min-width 값을 prop으로 전달했다.
prop으로 전달할때는, width와 height값은 vw이나 %단위로 전달했다.
상대단위로 작업했을 때 버튼이 너무 커지거나 작아지지 않도록 max-width와 min-width 값을 고정 px로 전달하여 크기가 너무 크거나 작아지는 것을 방지하였다.
type IconButtonProps = {
width?: string;
height?: string;
maxWidth?: string;
minWidth?: string;
};
export const IconButton = styled.button<IconButtonProps>`
all: unset;
display: flex;
background-color: ${(props) => props.theme.color.main.orange};
color: white;
font-family: ${(props) => props.theme.font.family.pretendard_medium};
border-radius: 8px;
padding: 12px 18px;
align-items: center;
justify-content: space-around;
height: ${(props) => props.height};
width: ${(props) => props.width};
min-width: ${(props) => props.minWidth};
max-width: ${(props) => props.maxWidth};
cursor: pointer;
&:hover {
filter: brightness(1.2);
}
@media screen and (max-width: 1024px) {
& {
height: 16px;
font-size: 13px;
}
}
`;
또한 많은 페이지에서 재사용하는 Emotion Tag라는 공통 컴포넌트가 존재했다.
먼저 감정태그를 나타내는 Tag Button의 경우, fontSize를 기준으로 하는 상대단위인 em을 사용했다.
글자크기를 기준으로 하는 상대단위에는 em과 rem이 존재한다. em은 해당 단위요소가 사용하는 font-size를 기준으로 상대값을 나타내고, rem은 최상위요소 (html태그)의 font-size를 기준으로 하는 상대단위이다.
em의 경우 해당요소의 font-size가 사라진다면, 부모요소의 font-size 값을 상속받기 때문에 변수가 많아 많은 CSS가이드들은 rem 사용을 권장한다.
하지만, 화면에 따라 padding값과 font-size를 변경할 필요성이 있었기 때문에 em단위를 사용하였다.
아래와 같이 container에서 미디어쿼리를 통해 Tag Button의 font-size와 padding값을 변경시켜주었다.
type TagButtonProps = {
fontSize: number;
};
const TagButton = styled.button<TagButtonProps>`
all: unset;
font-family: ${({ theme }) => theme.font.family.pretendard_medium};
color: ${({ theme }) => theme.color.grayScale.gray};
border-radius: 50px;
border: 1px solid #9d9d9d;
background-color: ${({ theme }) => theme.color.grayScale.white};
font-size: ${(props) => props.fontSize}px;
padding: 1em 2em;
text-align: center;
cursor: pointer;
&.active {
background-color: ${({ theme }) => theme.color.grayScale.black};
color: ${({ theme }) => theme.color.grayScale.white};
}
`;
type EmotionContainerProps = {
width: string;
isMargin?: boolean;
};
const EmotionContainer = styled.div<EmotionContainerProps>`
display: flex;
align-items: center;
margin-top: ${(props) => (props.isMargin ? 'min(10vw, 100px)' : '0')};
width: ${(props) => props.width};
justify-content: space-between;
@media screen and (max-width: 1420px) and (min-width: 1024px) {
${TagButton} {
font-size: 16px;
padding: 0.5em 1em;
}
}
@media screen and (max-width: 1024px) and (min-width: 768px) {
${TagButton} {
font-size: 13px;
padding: 0.5em 1em;
}
}
@media screen and (max-width: 768px) {
${TagButton} {
font-size: 11px;
padding: 0.5em 1em;
}
}
`;