HTML과 CSS를 학습한 후에 진행했던 자기소개 페이지에 JavaScript를 사용해서 동적인 효과들을 추가해보기로 했다. 또, 아쉬움을 남겼던 반응형도 구현해 보자는 생각이 들었다. 그 결과는...?🤭
호기롭게 자기소개 페이지에 동적인 효과를 넣겠다고 생각하던 한 편, 사전 스터디를 진행하는 분들도 함께 리팩토링을 진행하자는 이야기가 나왔다. 사실, 하나의 페이지로 제작했었기 때문에 크게 어렵지 않을 것 같아서 걱정도 하지 않았었다. 하지만......🤣
모바일 퍼스트
라는 말이 있다. 처음 웹을 구성하는 단계부터 모바일을 중심으로 구축하는 것을 말한다. 전부터 들었던 말이었지만 어째서 모바일 퍼스트를 강조하는지는 제대로 느끼지 못했다. 그 결과, 나는 자기소개 페이지를 만들면서 돌아올 수 없는 강을 건넜다. 지극히 데스크톱 화면을 기준으로 레이아웃을 짜고 작성했던 것...... 😮💨
"금방 하겠지?" 라는 생각은 곧 "우와... 답도 없네" 라는 생각으로 바뀌었다. mediaquery
를 이용해서 이것 저것 바꾸면서 시도를 해보던 나는 손을 내려놓았다. 그리고 "에이... 다시 만들자" 라는 생각을 하게 됐다.
모바일 퍼스트
라는 말은 괜히 있는게 아니었다. 모바일을 기준으로 만든 웹을 데스크톱까지 확장하는 것보다 데스크톱을 기준으로 정보들을 담아 만든 웹을 모바일을 기준으로 축소하는 것이 훨씬 어려웠다.
그렇게 교훈을 남긴 리팩토링의 첫 시작은... 리팩토링이 아닌 새로운 제작이 됐다.
"자기소개 페이지 만들기 이제 진짜 시작!"
다시 페이지를 제작하면서 이전의 코드를 참고했는데 보기가 참 어려웠다. 나름의 기준을 가지고 class
명을 작성했다고 생각했는데 그게 아니었다. 그러던 중에 사전 스터디 같은 조에 계시는 동희님의 velog를 보게 됐다. 그 곳에는 BEM
이라는 class 작성 방법에 대한 내용이 있었다.
💡 Point!
BEM은 Blocks - Elements - Modifiers 의 약자로 CSS를 위한 class 명을 작성할 때 사용하는 방법론 중에 하나이다. 이를 사용할 때 다음의 장점이 있다.
1. 쉬운 유지보수
2. 코드의 재사용성
3. 확장 용이
4. 직관적인 이름
참고 : http://getbem.com/introduction/
참고 : CSS 방법론1-BEM
내용에 대해 검색하고 읽어보니 이거다 싶었다. 각 팀마다 약속된 방법들이 있겠지만 현재의 규칙 없는 제멋대로의 class를 벗어나서 일정한 규칙을 가지고 작성하는 것이 좋다는 생각이 들었다.
.blocks__elements--modifires { }
작성하는 방법 역시 위처럼 어렵지 않았다. 요소들을 감싸주는 블록레벨을 먼저 작성하고 그 안에 element 요소들을 __
를 이용해서 연결해준다. 마지막으로 동일 요소에 변화를 주기 위해서 --
를 사용해서 연결해주면 된다.
반응형 웹을 구현하면서 레이아웃을 수정할 필요가 있던 부분이 바로 헤더부분이었다. 데스크톱 화면에서는 가로로 배치되는 메뉴를 이용해서 바로 볼 수 있도록 했지만 모바일 화면으로 축소를 할 때는 메뉴들이 숨겨질 수 있도록 하는 것이 좋았다.
이를 위해서 '햄버거 메뉴' 라고 불리는 아이콘을 넣어주고 JavaScript
를 이용해서 버튼이 눌린 경우에 숨겨진 메뉴를 보여줄 수 있도록 했다.
// 구현한 자바스크립트 코드
const toggleBtn = document.querySelector(".header__toggle");
const menu = document.querySelector(".header__menu");
const item = document.querySelector(".header__item");
const cursor = document.querySelector(".cursor");
toggleBtn.addEventListener("click", () => {
menu.classList.toggle("active");
});
menu.addEventListener("click", (event) => {
if (event.target.matches("a")) {
menu.classList.toggle("active");
}
});
구현한 방법은 .active
라는 클래스를 이용해서 처리했다. 평소 768px 이하인 화면에서는 display:none
을 사용해서 보이지 않도록 했다가 토글 버튼을 클릭한 경우에 .active
클래스를 추가하면서 display: flex
를 가질 수 있도록 했다.
여기서 주의할 점은 visibility: hidden
의 경우에도 비슷한 효과를 얻을 수 있지만 레이아웃의 영향을 끼치기 때문에 display:none
을 사용했다.
어떤 동적인 효과를 줄 수 있을지 고민하다가 마우스 커서를 만들어서 사용자의 마우스 움직임에 반응할 수 있도록 만들고 싶다는 생각이 들었다.
이를 구현하기 위해서 CSS를 통해서 간단한 원 모양의 커서를 만들었고 이를 JavaScript
를 이용해서 움직이도록 했다.
// 마우스 커서 구현 사항
addEventListener("load", (event) => {
const cursorRect = cursor.getBoundingClientRect();
const cursorHalfWidth = cursorRect.width / 2 + 12;
const cursorHalfHeight = cursorRect.height / 2;
document.addEventListener("mousemove", (event) => {
let X = event.clientX;
let Y = event.clientY;
cursor.style.transform = `translate(${X - cursorHalfWidth}px, ${
Y - cursorHalfHeight
}px)`;
});
먼저 getBoundingClientRect()
를 통해서 만들어 둔 커서의 width
, height
를 알아냈다. 요소들의 position의 기준이 왼쪽 상단이기 때문에 평소 가운데 배치를 위해서 translate(-50%, -50%)
를 이용하는데 우리는 translate
을 JavaScript
를 통해서 제어할 것이기 때문에 미리 cursor
의 width
와 height
을 구해서 이용할 수 있도록 했다.
이 때, clientX
는 사용자의 마우스가 이동한 가로 길이, clientY
는 사용자의 마우스가 이동안 세로 값이다.
💡 Point! 기억하자!
요소를 움직이기 위해서
top
,left
를 이용할 수도 있다. 하지만,top
과left
의 경우에는 이벤트가 발생할 때마다 레이아웃을 계속 다시 그려줘야 하기 때문에 성능면에서 좋지 못하다. 그에 비해transform
의 경우에는 Composite만 일어나기 때문에 부담이 적다.
// 메뉴 위에 hover 된 경우에 변화를 주도록 구현.
menu.addEventListener("mouseover", (event) => {
cursor.classList.add("cursor--grow");
});
menu.addEventListener("mouseleave", (event) => {
cursor.classList.remove("cursor--grow");
});
toggleBtn.addEventListener("mouseover", () => {
cursor.classList.add("cursor--grow");
});
toggleBtn.addEventListener("mouseleave", () => {
cursor.classList.remove("cursor--grow");
});
정적인 커서만 있다면 심심하기 때문에 메뉴에 마우스가 hover
된 경우에 색상이 변하면서 크기도 커질 수 있도록 했다.
내가 만든 페이지의 경우에 스크롤을 진행하면서 모든 내용을 볼 수 있도록 작성이 되어있다. 그래서 스크롤을 할 때 동적인 효과를 주고 싶었다.
// 스크롤 관련
const introName = document.querySelector(".introduction__name");
const introPosition = document.querySelector(".introduction__position");
document.addEventListener("scroll", () => {
let moveLeft = -scrollY;
let moveRight = scrollY;
introName.style.transform = `translate(${moveLeft}px, -50%)`;
introPosition.style.transform = `translateX(${moveRight}px)`;
});
사용자의 스크롤 이벤트에 반응해서 이름과 포지션 요소를 좌우로 움직이도록 했다. 이 역시 transform
을 이용해서 움직일 수 있도록 했다.
// 마우스가 브라우저를 떠날 때 없애고 다시 보여주는 기능
document.addEventListener("mouseleave", (event) => {
cursor.style.visibility = "hidden";
});
document.addEventListener("mouseover", (event) => {
if (window.innerWidth > 480) {
cursor.style.visibility = "visible";
}
});
});
처음 커서를 만들고 원하는 모습으로 동작을 하니 기뻤다. 하지만, 조금 시간이 지나가 부족한 모습들이 보이기 시작했다.
모바일 화면을 위해서 브라우저의 크기를 줄이는 과정에서 커서가 벽에 붙어서 남아있는 모습이 보이는 것이다. 또, 모바일 화면의 경우에는 기본적으로 마우스 이동이 없기 때문에 커서가 보이지 않아야 하는데 터치를 할 때마다 커서가 자취를 남기는 모습을 보였다.
눈에 크게 거슬렸고 없애야겠다는 생각이 들었다. 그래서 visibility
를 이용해서 마우스가 벗어난 경우에는 사라지도록, 그리고 마우스가 돌아온 경우에는 다시 나타나도록 했다. 문제는 모바일 화면이었다. 처음 생각대로 코드를 작성하고 돌려보니 모바일 화면에서는 여전히 자취를 남겼고 CSS 를 이용해서 768px 보다 작은 화면에서는 보이지 않도록 했다. 그럼에도 의도와는 다르게 동작이 됐다. JavaScript
에서 따로 화면 크기에 대해 조건을 걸지 않았다보니 생기는 일이었다.
그래서 결국 480
보다 브라우저 화면의 크기가 큰 경우에만 visible
되도록 했다. 480을 기준으로 삼은 이유는 모바일 화면의 분기점이 480이라고 생각해서였다.
html,
body {
max-width: 100%;
overflow-x: hidden;
}
이제 페이지 제작을 마치려던 차에 이상한 점을 발견했다. 모바일 화면으로 보는데 레이아웃이 이상했다. 처음 화면은 멀쩡한데 아래로 스크롤을 하다보면 알 수 없는 가로 여백이 생겨났다. 그러다보니 화면 레이아웃이 엉망이 됐다.
이유를 몰라서 정말로 당황스러웠다. "아니 분명히 잘 됐던 코드인데?" 라는 생각만 들었고 원인을 찾지 못한채로 레이아웃과 관련된 코드들에 손을 댔다. 그래도 고쳐지지 않아서 정말 답답했다.
그러다 찾은 코드가 위의 코드였다. 애초에 overflow-x
에 hidden
을 줘서 가로 스크롤이 발생하지 않도록 했다. 다행스럽게도 위의 코드를 사용하자 알 수 없는 가로 스크롤이 생기지 않았고 모바일 화면 역시 레이아웃이 정상적으로 나왔다.
이유는 무엇이었을까? 이 글을 작성하고 있는 지금은 알고 있다. 바로 위에서 만들었던 스크롤 이벤트 때문이었다. 스크롤이 진행되면서 이름과 포지션을 좌우로 움직이도록 했는데 스크롤이 계속 될 때마다 이동하다보니 모바일 화면에서 쓸데 없는 여백이 생겨버렸던 것이다. 당연히 width
를 100%
줬던 요소들의 크기도 따라 커졌고 레이아웃이 엉망이 된 것이다.
이 경험을 통해서 JavaScript
를 사용해서 동적인 효과를 주는 경우에 생각해야 하는 것들이 많다고 느꼈다. 내가 생각했던 레이아웃의 의도치 않은 영향을 충분히 줄 수 있기 때문이다.
알고리즘 문제를 풀면서 예외 케이스를 꼭 생각해야하는 것처럼 동적인 효과를 주는 경우에도 의도치 않은 변화를 꼭 제어할 수 있어야겠다.
이렇게 다사다난했던 자기소개 페이지 만들기가 끝이 났다. 포트폴리오로 활용하기에는 많이 부족하지만 여러 경험을 해볼 수 있어서 좋았다. 특히
JavaScript
나CSS
를 사용해보면서 예상치 못한 버그를 만나기도 했고 의도한 대로 움직이지 않는 모습을 보면서 답답함도 많이 느꼈기 때문에 공부 의욕이 더 샘솟는 기회가 됐다. 이런 걸로 고생하지 않도록 더 열심히 하자.💪
단기간에 다시 새로운 웹페이지 만들기부터 모바일 반응형까지.. 엄지 척입니다 😎👍🏻