본문 바로가기

프론트엔드

absolute와 fixed로 다양한 스마트폰 너비 대응하기

  • 프로젝터: SNU FESTIVAL
  • 키워드: absolute, responsive web
  • 상황
    1. 보통 화면 레이아웃은 flex, grid 등의 css 속성을 이용하지만 축제 메인화면이라는 특수한 상황에 맞도록 디자이너와 함께 레이아웃을 고민했다. 결과적으로 아래와 같은 디자인이 확정되었다.
      축제 메인화면 모바일버전
    2. 통계상 80%의 유저가 모바일로 접속하기 때문에, 모든 기기에서 레이아웃이 깨지지 않도록 신경을 써야한다.
    3. 배경 이미지(island)를 화면에 꽉 차도록하고, 하위 요소(굿즈, 공연, 행사, 미니게임, 방명록 등)의 위치를 상대적으로 잡아야한다.
  • 해결방법
    1. 먼저 모든 기기에 대해 화면에 딱 맞게 배경이 그려져야한다. 보통은 '100%' 를 이용하여 표현하지만 이 경우엔 배경 이미지의 비율이 바뀌지 않고 항상 일정(늘어짐없이 디자인과 일치)해야하고, absolute로 위치를 잡을 하위 요소가 많았다(부모의 width, height 비율이 일정하지 않으면 하위 요소들의 위치도 깨짐. 'vh', 'auto' 등의 속성은 사용 불가).
      따라서 실제 브라우저 width를 구하고, 디자이너가 준 width/height 비율과 맞도록 px을 직접 계산하여 넣어주었다.
        // 실제 디자인상 height/width = 2.1653
      <LayoutDiv width={theme.windowWidth} height={theme.windowWidth * 2.1653}>
        <IslandImg src={MobileIsland} ... />
        ...
      </>
      
      const Island = styled.img`
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%; // LayoutDiv에 전달한 px에 딱 맞도록
      `;


      배경을 넣은 모습


    2. 이제 하위 요소들의 위치는 absolute로 쉽게 잡을 수 있다.
      // windowWidth는 실제 브라우저 너비(document.documentElement.clientWidth)
      // 375px은 디자이너의 기준 너비(iPhone X)
      const ratio = useMemo(() => theme.windowWidth / 375, [theme.windowWidth]);
       기기의 너비에 따라 하위 요소의 크기도 변해야하므로 기준 너비(375px)에 대한 현재 너비 ratio를 구한다. 그리고 ratio를 이용하여 width를 정하고 디자인에 맞게 top, left속성도 정한다.
        // 188px은 디자인상 기준 너비
      <Landmark src={GuestBook} alt="방명록" top={87} left={20} width={188 * ratio} ... />
        
      const Landmark = styled.img`
        position: absolute;
        width: ${({ width }) => width}px;
        height: auto;
        ${props => props.top && css`top: ${props.top}%`};
        ${props => props.left && css`left: ${props.left}%`};
      `;
      부모 div의 width/height 비율이 일정함이 보장되어있으므로(px로 직접 계산) top, left에 '%'값을 부여하면 브라우저 너비에 관계없이 항상 똑같은 UI를 보여줄 수 있다.
      하위요소가 3개 추가된 화면

      같은 방법으로 10개의 하위요소를 추가하면 상황1에 있는 디자인과 똑같이 완성된다!
  • 한 걸음 더
    1. height/width = 2.1653으로 고정되어있는 상황이기 때문에, 세로 스크롤이 생기지 않을만큼 길면서도 비율이 2.1653보다 큰 기기일 경우 아래와 같이 빈 공간이 생길 수도 있다(실제로 이런 비율을 가진 기기를 찾진 못했다).


    2. Background color를 줘서 빈 공간과 island 배경을 자연스럽게 이어준다.
        <Background />
      
      export const Background = styled.div`
        position: fixed;
        width: 100%;
        height: 100%;
        background-color: #A2CCE9;
      `;