반응형 토이프로젝트 회고(?)

박효정·2023년 11월 22일

TIL

목록 보기
12/13

반응형 토이프로젝트를 마친지 일주일이나 지나서야 쓰는 회고록 (사실 써놓았다고 생각한 내 불찰 >_0)
큼큼


반응형 토이프로젝트

media-query를 직접 적용해보자는 동기로 시작한 작고 귀여운 반응형 토이 프로젝트 response-toy

무언가를 소개하는 듯한 느낌의 메인 페이지처럼 보이고 싶어서 각 요소들을 크게크게 잡아서 디자인을 기획했습니다. (크게크게 -> 이건 제목! 이건 게시글! 이렇게 한 눈에 보이게끔..?? )

피그마 원본 보러가기

사진이 왜이리 큰 지 모르겠지만
위 처럼 크게크게 설명하는 듯한 느낌을 주는 반응형 메인페이지입니다.


넣고자 했던 기능들

  • 반응형
  • 메뉴 탭 누르면 해당 섹션 스크롤 이동
  • 모바일 버전 좌우 슬라이드
  • (넣을 생각은 없었지만 넣게 된) 리사이즈 시 리로드

반응형

반응형은 css 파일에서 @media(min-width){} 방식으로 구현했습니다.

// 모바일
@media (min-width: 320px) and (max-width: 768px) {
  header {
    height: 80px;
    padding: 0 20px;
  }

  .headerLogo {
    font-size: 32px;
  }
... 생략
}

데스크탑과 모바일 각각 media-query 조건문 안에 작성하는 것을 계획했지만 겹치는 코드들이 많다보니 우선 데스크탑의 코드들을 조건문 없이 순수하게 작성하고 모바일 버전에서 필요한 css 속성을 수정하는 방향으로 전환하기도 했습니다. 중간에 전환하는 바람에 코드가 중구난방이 되어 정리할 필요가 매우매우 높은 코드가 되었다는 함정도 있습니다. 이래서 코드 작성을 본격적으로 시작하기 전에 컨벤션이나 어떤 스타일로 작성할 지 세심하게 정해야하는 과정이 필수라는 걸 또 느꼈습니다..

메뉴탭 스크롤 이동

메뉴탭 같은 경우는 각 <section> 시멘틱 태그를 기준으로 스크롤 높이를 구하고 클릭 시 해당 스크롤 높이로 이동하게끔 구현했습니다. 섹션 태그에 있는 id를 기준으로 해당 높이를 구하고 클릭 이벤트 발생 시 window.scroll을 이용하여 top 위치를 바꾸었습니다. 하지만 해당 섹션으로 이동시 고정된 헤더와 딱 맞게끔 이동하는 섹션도 있었지만 일부는 섹션 상단이 겹치거나 오히려 더 아래로 떨어져서 빈 공간이 생기기도 하였습니다. 이는 헤더의 높이를 고려하지 않고 섹션의 위치만을 계산해서 발생한 문제였습니다. 그래서 헤더 높이에 해당하는 px 를 변수로 정의하여 각 스크롤 높이에서 해당 변수를 빼 주었습니다.

  
if(window.innerWidth >= 758){
  const desktopMargin = 100;
  const homeHeight = window.pageYOffset + document.querySelector('#homeSection').getBoundingClientRect().top - desktopMargin;
  const previewHeight = window.pageYOffset + document.querySelector('#previewSection').getBoundingClientRect().top - desktopMargin;
  const tryHeight = window.pageYOffset + document.querySelector('#trySection').getBoundingClientRect().top - desktopMargin;
  moveHome.addEventListener('click', (event) => {
    event.preventDefault();
    window.scrollTo({
      top: homeHeight
    })
  })
  ... 생략
}
  

여기서도 각 높이에 초점을 맞추면서도 fixed 시킨 헤더의 높이를 고려하지 않았다는 점에 사소한 계산을 돌이켜 볼 필요를 느꼈습니다. 물론 사소한 계산이 꼭 필요한 계산이지만 ... 하나하나 세심하게 확인하자!

모바일 좌우 슬라이드

모바일 미리보기 섹션의 좌우 슬라이드는 현재 보이는 화면의 너비를 구해서 너비 * 게시글 개수 만큼의 전체 너비를 설정하여 구현했습니다.

좌우 버튼을 만들어 클릭 시 슬라이드 이벤트가 발생하도록 하였고 첫 번째와 마지막 게시글일 경우에는 각각 좌, 우 버튼을 숨겨 ux 를 고려하였습니다.

  // 미리보기 좌우 이동
const previewContentPrevButton = document.querySelector('.previewContentPrevButton'); // 이전 버튼
const previewContentNextButton = document.querySelector('.previewContentNextButton'); // 다음 버튼
const previewContentActullaySeen = document.querySelector('.previewContentActullaySeen'); // 보여지는 영역
const previewContentWrapper = document.querySelector('.previewContentWrapper'); // 컨텐츠 전체 영역
const previewContents = document.querySelectorAll('.previewContent'); // 컨텐츠 영역(개별)

let currentIndex = 0; // 컨텐츠 인덱스
let previewSliderCount = previewContents.length; // 전체 컨텐츠 개수
let previewSliderWidth = previewContentActullaySeen.getBoundingClientRect().width; // 보여지는 영역 너비

previewContentWrapper.style.width = (previewSliderWidth * previewSliderCount) + "px";

updateButton(); // 버튼 초기 설정
function updateSlider() {
  const translateValue = -currentIndex * previewSliderWidth; // 몇번째 컨텐츠인지 인덱스랑 현재 보여지는 너비 곱하기
  // 만약에 두번째 게시글이라면 -1 * 390
  previewContentWrapper.style.transform = `translateX(${translateValue}px)`;
  // 움직이는 전체 영역의 translateX 를 -390px 시켜서 오른쪽 -> 왼쪽 이동처럼 보여짐
  updateButton(); // 버튼 클릭 시 작동
}

previewContentPrevButton.addEventListener('click', () => {
  if(currentIndex > 0){
    currentIndex--;
    updateSlider();
  }
});

previewContentNextButton.addEventListener('click', () => {
  if (currentIndex < previewSliderCount - 1) {
      currentIndex++;
      updateSlider();
  }
  // 현재 인덱스가 개수-1 보다 작으면 다음 버튼이 작동되면서 인덱스 + 1
});

function updateButton () {
  // 첫 번째, 마지막 게시글에는 각각 이전 - 다음 버튼 안보이게
  previewContentPrevButton.style.display = currentIndex === 0 ? 'none' : 'inline';
  previewContentNextButton.style.display = currentIndex === previewSliderCount - 1 ? 'none' : 'inline';
};
}

몇 번째 인덱스인지 파악하여 해당 인덱스에 해당하는 게시글을 보여주기 위해 이벤트 발생 시 translateX 를 조정하였습니다. translateX이 아니라 left 를 사용했지만 적용이 되지 않았고 (이유가 뭘까) 그래서 translateX로 변경하였습니다. 그리고 리플로우를 발생시키며 레이아웃을 조정하는 left 와 달리 translateX를 리플로우, 리페인트가 발생하지 않아 성능적으로도 더 우수하다는 장점도 알게 되었습니다. (오호~)

리사이즈시 리로드

사실 이 기능은 구현 생각이 없었지만 반응형 웹 제작을 하면서 사이즈를 조정했을 때 원하는 css 속성으로 변경되지 않아서 추가한 기능입니다. 데스크탑에서 모바일로 바꿨는데 why 데스크탑같이 보여주냐공요~~!

// 리사이즈 시 0.3초 후 리로드
window.addEventListener('resize', () => {
  setTimeout(() => {
    document.location.reload();
  }, 300)
})

코드 자체는 매우 간단한데 리사이즈 이벤트가 발생하면 0.3초 후에 리로드를 시켜달라는 타임아웃 함수를 적용했습니다. 리로드를 바로 발생시켜버리면 조금만 움직여도 리로드를 시켜버리기 때문에 이는 불필요하다고 판단하였습니다. 사용자 입장에서 그리 길지 않은 시간 안에 리로드가 필요할 것으로 보여 0.3초의 타이머를 적용한 것입니다!


이렇게 반응형 웹 회고를 작성하였는데.. 뭔가 주저리주저리 된 기분입니다요..

세 줄 요약
1. 불필요하게 코드를 여러번 작성하는 일을 만들지 말자(미디어쿼리에서 겹치는 코드들이라던가..)
2. 레이아웃을 건들이는 일에서는 성능도 고려하자(left 말고 translateX 쓰는 것처럼)
3. 기능 구현은 직접 해보자. 작더라도! 해보다보면 이해도 되고 뭐가 더 필요한 지 느낀다. 알고 싶지 않아도 생긴다

profile
코린이 일기장

0개의 댓글