솔미는 성장중
01_ (패스트캠퍼스X야놀자 프론트엔드 개발 부트캠프) Jacksonchameleon 웹사이트 클론 코딩 본문
프로젝트를 시작하며
패스트캠퍼스 X 야놀자 프론트엔드 부트캠프 1기를 참여하며 첫 과제가 나왔다.
HTML,CSS 과제였고, 웹사이트를 클론 코딩하는 것이었다!
HTML/CSS 과제였지만 간단한 부분은 JS로 구현해보았다.
웹사이트 선정 기준
1. 기존에 관심있었던 브랜드
2. 온보딩 강의에서 배웠던 swiper라이브러리와 스크롤 효과를 적용해볼 수 있는 사이트
3. CSS를 통해 다양한 효과를 시도해볼 수 있는 사이트
레퍼런스 사이트 & 프로젝트 결과
23.08.05 기준: 데스크탑에서만 정상적으로 작동한다. (반응형 미구현)
구현 내용
1. 헤더 애니메이션
- 스크롤에 반응
: 상단에 있을 땐 투명 / 어느 정도 스크롤을 내리면 흰색 배경
: Lodash 라이브러리를 통한 함수 실행 제어
: gsap 라이브러리를 통한 애니메이션 효과 - 호버에 반응
: vanilaJS
: 하위 클래스를 통해 상위 클래스의 속성을 제어하기 위해 JS사용
: 스크롤이 상단에 위치했을 때만 호버에 반응
2. 이미지 전환 및 슬라이더 구현
- 마우스 호버 시 이미지 전환
: CSS - 슬라이더
: 버튼 및 마우스 클릭으로 좌우 이동 가능.
: Swiper 라이브러리 사용
3. 섹션 등장 효과 구현
- 스크롤에 반응
: Lodash 라이브러리를 통한 함수 실행 제어 - CSS의 transform과 opacity 속성 사용
4. 버튼 및 글자 효과 구현
- CSS
: before 선택자를 통한 효과 구현
문제상황 및 해결 방법
1. 이미지 전환 효과
이미지 전환 효과을 만드는 방법은 여러가지가 있다.
어떤 것이 효율적인지 모르겠어서 다양한 시도를 해보았다.
1차 시도) 두 개의 이미지를 동일한 위치에 겹쳐두고 hover 시 opacity 조절
결론 : 채택 X.
이유)
만들어야되는 형태가 하나의 box안에 상단은 (전환효과가 되는)이미지 + 하단은 textbox가 들어가 있는 형태였다.
이미지 2장을 동일한 위치에 겹쳐놓기 위해서는 CSS에서 position:absolute를 설정해주어야했다.
이를 통해 이미지 2장을 겹치는 것은 성공했으나 이미지가 차지하는 공간을 textbox가 인식하지 못했다. textbox에도 ㅔㅐposition: absolute; 속성을 부여함으로써 해결할 수 있지만 이는 좋은 방법이 아니라고 판단했다.
2차 시도) JS를 통해 display 및 opacity 조절
결론 : 채택 X.
이유)
html에 2개의 이미지를 우선적으로 배치시켜두고, querySelector를 이용해 이를 가져와 효과를 제어하는 방식을 시도해보았다. 하지만 display는 on/off 형태의 속성이기 때문에 다른 속성값(opacity, scale 등)을 추가적으로 적용할 수 없었다. display값을 쓰지 않으려니 이미지가 차지하고 있는 공간 때문에 box가 늘어나서 원하는 형태로 보여지지 않았다.
3차 시도) 하나의 이미지는 CSS에서 background-image로 설정하고, 다른 이미지는 html img태그에 넣어두기
결론 : 채택 0.
이유)
가장 간단한 방법이었다. 사실 CSS에서 background-image의 url을 써주는 것이 귀찮고, 반복적인 작업이라고 생각해 JS로 해결해보고 싶었으나 원하는대로 작동하지 않아서 이 방법을 채택했다.
하나의 이미지는 background-image로 설정했기에 2개의 이미지를 동일한 위치에 쌓아두는 것이 간단하게 해결됐다. img태그에 대해서 hover 선택자를 적용해 애니메이션 효과를 주었다!
멘토님 추가 의견:
JS로 구현하는 방법에 대해서 질문을 드렸는데, 취향 차이긴 하지만 전환 효과의 경우 CSS로도 충분히 구현 가능하기에 CSS로 작업하는 것을 추천한다고 하셨다.
2. class 이름 정하기
생각보다 작업 시간을 줄이는 데 아주 많이 기여하는 부분이다.
class이름은 '통일성'과 '재사용 여부'를 신경써서 만들어야겠다고 생각했다.
1. 통일성
BEM규칙을 따르도록 하자.
멋대로 짓고 나니 html과 css 파일 내에 코드가 많아지자 계속 ctrl+F해서 검색을 하는데 시간을 많이 쏟고 있는 스스로를 발견할 수 있었다..
2. 재사용 여부
웹사이트를 클론코딩하다가 많이 느꼈던 점이 같은 설정이 여러번 반복되는 경우가 많다는 것이다.
요소에 class이름을 여러개 지을 수 있으니 같은 설정/동작을 가진 요소들은 동일한 class이름을 부여하자.
해당 프로젝트에서도 '슬라이더 내 이미지 호버 효과', '섹션별 제목 폰트 설정(사이즈, 위치, 굵기 등)' 과 같이 반복적인 설정을 요하는 부분에 대해선 같은 class명을 사용했다.
멘토님 추가 의견:
class명은 필요할 때, 그때그때 짓는 것이다.
모든 요소가 class명을 갖고 있어야할 필요는 없다.
3. 파일 분리하기
코드가 많아지다보니 코드를 찾는 데에 많은 시간이 낭비되었다.
그래서 이미지 호버 관련 CSS 내용을 짤 땐 CSS파일을 분리해서 만들었다.
JS, CSS는 내용이 길어지면 기능에 따라/ 효과에 따라 파일을 분리하는 것이 좋을 것 같다.
+) 그리고 reset.css 파일을 만들어 페이지 전체에 적용되는 기본 스타일을 쉽게 제어할 수 있도록 하자!
4. 반응형을 고려한 코드 - 스크롤 애니메이션 효과
요즘은 웹사이트에 접속할 때 데스크탑이 아닌 테블릿/ 스마트폰을 통해 접속하는 경우가 훨씬 많기에 반응형을 고려한 코드는 필수적이라고 생각한다. 이번 프로젝트를 만들 때 반응형을 고려하지 않고 코드를 짰다보니 문제가 생겼었다.
스크롤 애니메이션을 만들 때 scrollY의 위치에 따라 효과가 적용되게 했다. 그랬더니 화면 비율이 다른 스마트폰 형태에서 테스트했을 땐 효과가 의도한 대로 적용되지 않는 문제점이 있었다.
해당 애니메이션에 대해선 scrollY의 위치가 아주 중요하지 않고, 대략적으로만 맞으면 되는 것이기에 intersection observer를 통해 해결할 수 있다!
멘토님 추가 의견:
- Scroll Position과, 해당 element의 위치가 중요한 경우
- offsetPosition이나, getBoundingClientRect 을 사용합니다. 전자는 offsetParent가 body인 경우 등 특수한 상황에만 사용할 수 있기에, 공통 모듈을 설계할 땐 후자를 주로 사용합니다.
- requestAnimationFrame 등을 활용해 event handler가 화면의 refresh rate보다 많이 실행되지 않도록 방지(이 작업은 보통 무조건 진행하긴 합니다)
- 레이아웃을 단순화하여 offsetPosition으로 계산이 가능하도록 수정 시도
- 가시성만 알면 되는 경우
- Intersection Observer를 사용합니다. 지원하는 브라우저 범위에 따라 Polyfill이 필요할 수 있습니다.
5. 버튼 애니메이션 효과 제작
hover시 좌측부터 색상이 차오르고 다시 좌측으로 색상이 사라지는 효과를 만드는 데 방법을 몰랐었다.
before선택자를 통한 해결!
before/after 선택자를 써본 적이 없었어서 해당 선택자를 이용해야겠다는 생각을 못했었다.
해당 선택자를 통해 효과를 줄 다른 색상의 백그라운드 컬러를 가진 네모를 만들어주었고, width를 0으로 두었다가 hover시 100으로 바뀌게 설정했다. 그리고 z-index를 통해 어떤 것이 앞쪽에 노출되기 할지를 결정해주었다!!
멘토님 코드리뷰 & 리팩토링
1. (main.js)
전개연산자를 통해 코드 간소화하기
기존 코드
const hovermenus = document.getElementsByClassName("has-submenu")
let arr = []
for(i=0;i<hovermenus.length;i++){
arr.push(hovermenus[i])
}
getElementByClassName을 사용하면 HTMLCollection 형태로 불러오는데, forEach문법이 이를 지원하지 않아서 elements를 순회할 때 for문을 사용해야 했다. 위 방식을 통해 HTMLCollection으로 불러온 데이터를 배열 데이터로 변환시켜주었다.
코드 리뷰 후 수정 코드
const hoverMenus = [...document.getElementsByClassName("has-submenu")];
전개 연산자를 통해 간단하게 수정 가능했다.
멘토님의 추가 의견)
증감문에서 length를 판별하면 매 iteration마다 해당 작업을 수행해야 합니다!
가독성을 위해 보통 고차 함수를 사용하지만, 성능이 절실한 경우에는 아래와 같이 사용하기도 합니다.
for (let i = 0, max = array.length; i < max; ++i) {}
같이 사용하기도 합니다.
2. (main.js)
스크롤 이벤트 최적화
기존 코드
window.addEventListener('scroll',_.throttle(function(){
if(window.scrollY>=200){
gsap.to(whiteLogo, 0, {display:'none'})
.
.
.
}else{
.
.
.
}
},200)) //0.2s 간격으로 이벤트 체크
위 방식을 통해 스크롤 이벤트를 최적화했다. Lodash라이브러리의 throttle 이라는 속성을 이용했다.
코드 리뷰 후 수정 코드
window.addEventListener('scroll',_.throttle(function(){}, {passive:true} )
"스크롤 이벤트의 최적화를 시도하신 건 좋습니다!
addEventListener의 passive 옵션도 추가로 알아보시면 좋을 것 같네요."
라는 피드백을 받았다.
그래서 passive옵션에 대해 추가적인 조사를 해보다가 좋은 글을 발견했다. (꼭 다시 읽어보기!)
https://velog.io/@sejinkim/Passive-Event-Listeners
원리 핵심 요약)
passive 옵션이 활성화되었다면, Compositor thread는 Main thread의 프로세스를 기다리지 않고 자신의 작업인 Paint와 Composite를 즉시 수행하게 됩니다. 이벤트를 수신하는 즉시 핸들러를 실행하고, 화면에 바로 반영/합성시킬 수 있게 됨으로써 렌더링 퍼포먼스가 향상되는 원리입니다.
3. (index.html)
defer 사용 vs </body>앞에 <script>태그 위치시키기
기존 코드
<script defer src="./main.js"></script>
main.js파일에만 defer 속성을 적용시켰다.
코드 리뷰 후 수정 코드
<link defer rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.8/dist/web/static/pretendard.css" />
<link defer rel="stylesheet" href="./css/reset.css">
<link defer rel="stylesheet" href="./css/main.css">
<link defer rel="stylesheet" href="./css/imageHover.css">
<script defer src="./main.js"></script>
render-blocking resource는 페이지 렌더링 프로세스에 필수적인 글꼴, HTML, CSS 및 JavaScript 파일과 같은 정적 파일을 말한다. 따라서 해당 파일에도 defer 속성을 적용했다.
"다른 render-blocking resource들도 함께 처리하시면 좋을 것 같습니다.
그리고, <script>는 일반적으로 </body> 앞에 위치시키시는 것을 권장드립니다.."
라는 피드백을 받았다.
왜일까? (질문 답변 대기중)
4. (imagehover.css)
반복적인 CSS 스타일 적용을 SCSS 전처리기로 깔끔하게!
기존 코드
/* 이미지 호버를 위한 css코드 */
/* product섹션 */
.has-swiper .box .one {
background: url("../assets/slide2-1_hover.jpg");
background-size: 100%;
}
.has-swiper .box .two {
background: url("../assets/slide2-2_hover.jpg");
background-size: 100%;
}
.has-swiper .box .three {
background: url("../assets/slide2-3_hover.jpg");
background-size: 100%;
}
.has-swiper .box .four {
background: url("../assets/slide2-4_hover.jpg");
background-size: 100%;
}
.has-swiper .box .five {
background: url("../assets/slide2-5_hover.jpg");
background-size: 100%;
}
.has-swiper .box .six {
background: url("../assets/slide2-6_hover.jpg");
background-size: 100%;
}
.has-swiper .box .seven {
background: url("../assets/slide2-7_hover.jpg");
background-size: 100%;
}
.has-swiper .box .eight {
background: url("../assets/slide2-8_hover.jpg");
background-size: 100%;
}
반복적인 코드인데 하나하나 써줘야한다는 점이 매우 비효율적으로 느껴졌었다.
이를 해결하기 위해 JS로 구현을 시도해보기도 했고, CSS에서 다른 방식을 시도해보기도 했다.
하지만 어느 하나 마음에 들지 않았었다.
코드 리뷰 후 수정 코드
.has-swiper .box {
@for $i from 1 through 8 {
.box--#{$i}{
background: url(/assets/slide2-#{$i}_hover.jpg);
background-size: 100%;
}
}
}
SCSS전처리기를 써보면 좋을 것 같다고 말씀해주셨고, 아주 간략하게 줄었다!!
앞으로 자주 사용할 것 같다 😍
SCSS의 장점 / 설치부터 사용까지의 과정이 궁금하다면 아래 링크를 참고하자!!
개선할 사항
- 반응형을 고려한 코드로 수정 (중요)
마무리하며
코드를 깔끔하게 짜는 것이 어려웠었다. 처음 만들다보니 계획을 어떻게 세우고 시작해야될지 몰라 무작정 만들기 시작했더니 시행착오를 많이 겪었다. 다음에는 이번 과제를 하며 겪었던 시행착오를 양분삼아 더 깔끔하게 제작할 수 있을 것 같다 :) HTML구조를 어떻게 효율적으로 짤지, CSS에서의 다양한 수치, flex/grid 사용법, position 지정하는 방법 등이 헷갈렸는데 이번 클론코딩 과제가 해당 의문점을 해결하는 데 큰 도움이 되었다.
마지막 한마디:
처음부터 반응형을 고려한 코드를 짜자!
두려워말고 다양한 도구/라이브러리를 사용해보자!
'프로젝트' 카테고리의 다른 글
04_ (패스트캠퍼스X야놀자 프론트엔드 개발 부트캠프) 숙박 예약 사이트 *기업연계* (4) | 2023.12.19 |
---|---|
Lighthouse 점수 어떻게 올리는 건데~! (feat. 성능 최적화) (0) | 2023.12.15 |
proxy 서버 띄워 작업하기 (feat. vite) (1) | 2023.12.04 |
백엔드와 협업할 때 API를 못 받았다면? MSW mocking서버를 이용해보자! (feat. React & TypeScript) (0) | 2023.11.30 |
02_ (패스트캠퍼스X야놀자 프론트엔드 개발 부트캠프) 직원 관리 사이트 (0) | 2023.08.29 |