어김없이 유튜브 세상을 떠돌고 있던 나날중, 예전에 구독해두었던 채널에서 굉장히 구미가 당기는 썸네일을 발견하게 되었고, 참을 수 없던 나는 해당 영상을 감상하게 되었다.
Building The Extraordinary Using Only The Ordinary - Hyperplexed
유튜브 영상을 보고 왔다면 알 수 있겠지만, 정말 멋진 카드 컴포넌트인데, 막상 사용하는 기술은 프론트엔드 개발자라면 익히 알고 있을법한 기술들을 사용하고 있음을 알 수 있다.
이는 일종의 신선한 충격으로 다가왔는데, 최근에 Next.js13과 더불어 React18 공부를 하고 있다보니 UI 관련해선 아무런 작업을 진행하지 않았기 때문이었다.
생각해보면 프론트엔드 개발자를 처음으로 꿈꾸게 된 계기도 웹사이트에서 이런 멋진 UI를 직접 개발해보고 싶었기 때문이었다.
직접 UI를 디자인하고, 구현해보고, 애니메이션을 줘서 역동적이게 만들고, 이를 타인에게 공유하여 잘만들었다는 소리 듣는게 프론트엔드 공부하면서 가장 보람찼던 순간들이 아니었나 라는 생각이 들었다.
마침 영상은 튜토리얼 스타일로 따라 만들기 너무 좋게 되어있었기에 바로 코딩을 시작할 수 있었다. 해보지 않을 이유가 없었다.
이를 블로그 글로 남기는 이유는, 어디까지나 개인적인 아카이브의 목적도 있지만, 영상을 보고 나서도 어떻게 구현을 할 수 있는지 잘 이해가 되지 않는 사람들을 위한 좀 더 자세한 설명서가 되길 바라는 마음으로 시작하게 되었다.
얘기하다보니 서론이 길어졌다. 바로 시작해보자!
蛇足:
React, Next.js는 어디까지나 라이브러리, 프레임워크이고 이들로 할 수 있는 일에는 한계가 있음에 분명하다. 그리고 이미 이 분야는 10여년간의 세월동안 깊게 고여서 리액트,Next.js를 가지고 두각을 나타내긴 어려울 수 있다고 개인적으론 생각하고 있다.
물론 React,Next.js 같은 시장에서 요구하는 기술들의 중요성을 잘 알고 있기 때문에, 이를 위한 공부 역시 놓쳐선 안될 것이다.
하지만 UI 디자인,개발,인터랙션은 개발적인 지식도 물론 필요하지만, 어디까지나 개인의 창작욕구, 상상력이 많이 가미될 수 밖에 없다.
그리고 개인적으로 생각하기엔 이런 창의적으로 UI를 디자인하고 개발하며 인터랙션을 줄 수 있는 개발자들이 시간이 지날수록 살아남지 않을까 생각하고 있다.
우리가 만들어 갈 카드 컴포넌트의 결과물이다.
사진으론 잘 안담기는데, 유튜브 영상이나 데모 프로젝트 링크를 클릭한다면 컴포넌트에 가미 된 애니메이션까지 볼 수 있어 100% 본연의 맛을 느낄 수 있다.
사진으로만 보기에도 뭔가 멋지지 않은가? 해당 카드 컴포넌트는 앞서 언급한 유튜브 영상을 통해 만들어 내었지만, 실제 오리지널리티는 다른 곳에 있다.
그 컴포넌트는 요렇게 생겼다.
WebGL을 활용하고, 스크롤로 네비게이션이 가능한 굉장히 멋진 3D 사이트이다.
여기에서 스크롤을 돌리면서 돌아다니다 보면 위와 같은 카드 컴포넌트를 발견할 수 있다.
만들어야 할 컴포넌트에 대한 소개를 끝으로, 해당 컴포넌트를 만들기 위해 어떤 부분들이 필요한지 고민해보자.
사진에는 잘 안담기지만, 유튜브 영상이나 Hall of Zero Limits 페이지에 접속하여 카드 컴포넌트를 살펴보면 영상 컨텐츠 위로 줄무늬가 위로 계속 올라가는 애니메이션이 쓰이고 있음을 발견할 수 있다.
우리가 만들고자 하는 카드 컴포넌트에 어찌보면 가장 중요한 요소라고 볼 수 있는데, 해당 카드 컴포넌트들이 유니크한 느낌을 내는데 관여하는 가장 큰 요소이기 때문이다.
후에 코드를 설명하는 파트에서 자세히 기술하겠지만, 이번 프로젝트에선 줄무늬 간섭을 표현하기 위해 css의 linear-gradient 함수를 활용했다.
유튜브 영상, 데모 프로젝트, 오리지널리티 사이트에 접속하여 각각의 카드 컴포넌트를 확인해보면, 모두 컨텐츠 뒤쪽으로 재생 되는 백그라운드 영상이 존재하고 있음을 알 수 있다.
이를 구현하는 방법은 크게 어렵지 않다. 만약 따로 준비된 비디오 영상이 있다면, 해당 영상을 사용하면 된다.
만약 영상이 없다면, 이미지를 바탕으로 애니메이션을 통해 영상처럼 재생할 수 있는 방법이 있다.
이번 프로젝트에선 영상을 사용하지 않고, 이미지를 통해 영상을 틀어논 것 같은 효과를 주는 법에 대해 알아본다.
컨텐츠는 앞서 설명한 줄무늬 간섭 애니메이션, 백그라운드 영상 위로 보여질 요소들의 총집합을 의미한다.
개발자가 자유롭게 자신이 원하는 내용을 담으면 되기 때문에 이 부분은 크게 설명이 필요하진 않다.
앞서 설명한 3가지 요구사항들에 대해 숙지했다면, 이들을 어떻게 쌓아 하나의 컴포넌트로 만들어낼지 고민해봐야 한다.
우리에게 주어진 요소는 총 3가지.
1. 줄무늬 간섭 애니메이션
2. 백그라운드 영상
3. 컨텐츠
이들을 결과물과 동일한 컴포넌트 처럼 보이게 하기 위해선 바닥에서부터 1->2->3 순서로 쌓이는게 맞는 판단일 것이다.
영상 위로 줄무늬가 보여야 하며, 이들 위로 컨텐츠가 보여야 하기 때문에 1->2->3 의 순서로 레이어를 구성하면 된다.
앞서 정의한 3가지 요소들을 하나씩 구현해보자.
본격적인 구현에 돌입하기 전, 사용한 프로젝트 환경에 대해 알리고 시작하고자 한다.
13.4
(Create-Next-App)3.3
Next.js는 최근에 즐겨 사용한 프레임워크이다 보니 익숙하게 프로젝트 세팅을 할 수 있었기 때문에 사용하였다. 굳이 Next.js가 아니여도 충분하고, 기본적인 html, css, js 파일만 가지고 만들어도 큰 이슈는 없다.
CSS 라이브러리로 TailwindCSS를 사용한 이유도 익숙한 라이브러리였기 때문이다. 일반적인 바닐라CSS를 사용하거나, SASS, CSS-in-JS 등등 CSS를 사용할 수만 있으면 된다.
구현하기로 맘 먹은 독자분들에게 추천하는 방식은 codepen 같은 온라인 코드 에디터를 쓰는 방식이다.
해당 프로젝트를 구현하기 위해 리액트가 필요한 것도 아니기 때문에 바닐라 html, css, js를 통해 충분히 구현할 수 있다.
요구 조건은 크게 2가지이다.
1. 사진과 같이 줄무늬 UI 렌더링
2. 줄무늬가 아래에서 위로 움직이는 애니메이션
최신 CSS 문법에 빠삭한 사람이라면, 줄무늬 UI를 렌더링하기 위해 repeating-linear-gradient()를 사용하면 될 것이라고 생각할 수 있다.
물론 맞는 말이다. 실제로 다음의 코드를 사용하면 줄무늬UI를 쉽게 만들어 낼 수 있다.
background: repeating-linear-gradient(
white,
white 10px,
blue 10px,
blue 20px
);
실제로 해당 코드를 사용하면 위와 같은 흰색/파란색 베이스의 줄무늬를 쉽게 만들 수 있다.
이를 좀 더 실제 프로덕트와 가까운 색으로 바꿔보면 큰 차이가 없음을 알 수 있다.
/*
theme(...)함수는 TailwindCSS에서 제공하는 색상을 사용하기 위해 썼다.
ref:https://tailwindcss.com/docs/functions-and-directives#theme
*/
background: repeating-linear-gradient(
theme("colors.blue.500" / 15%),
theme("colors.blue.500" / 15%) 3px,
transparent 3px,
transparent 9px
);
전혀 문제 없이 줄무늬 UI를 완성했다!
UI 렌더링을 완성하였으니, 이 줄무늬UI를 아래에서 위로 움직이는 애니메이션만 달아주면 끝난다.
줄무늬가 적용 된 타겟이 background였으니, background-position을 통해 background를 이동시켜주면 될 것 같다는 생각이 든다.
따라서 아래와 같이 keyframes를 정의하고, 애니메이션을 부여할 수 있다.
@keyframes stripe {
from {
background-position: 0% 0%;
}
to {
background-position: 0% -100%;
}
}
...
animation: stripe 22s infinite linear;
그러나 코드를 실행해보면, 아무런 변화가 없음을 확인할 수 있다.
단순하게 background-position을 바꿔주는 역할만 부여했을 뿐인데, 왜 작동하지 않는 걸까?
이유는 복합적이지만, 다음과 같이 분석할 수 있다.
좀 더 쉽게 풀어내자면, 이미 컨테이너의 전체 사이즈를 이미지로 덮었기 때문에, 이미지 사이즈 변화가 있지 않는 이상 background-position으로 변동을 줘도 변화가 일어날 수 없게 된다.
이는 background-position이 받아들이는 value로 px과 퍼센테이지를 적용할 경우 이미지를 놓을 위치를 잡는 방식이 변경 되기 때문이다.
따라서 repeating-linear-gradient를 사용하면서 줄무늬를 위로 올리는 애니메이션을 사용하고 싶다면, 퍼센테이지 값이 아닌, px 값으로 주면 얼추 해결할 수 있게 된다.
@keyframes stripe {
from {
background-position: 0% 0%;
}
to {
/*기존 퍼센테이지 값에서 px로 변경한다.*/
background-position: 0% -1000px;
}
}
animation: stripe 22s infinite linear;
혹은 background-size를 키워서 사용해도 동일한 결과를 얻을 수 있다.
@keyframes stripe {
from {
background-position: 0% 0%;
}
to {
background-position: 0% -100%;
}
}
/* 높이를 2배로 설정한다 */
background-size:100% 200%;
animation: stripe 22s infinite linear;
이렇게 repeating-linear-gradient를 사용해서 줄무늬 간섭 애니메이션을 구현할 수 있었다.
유튜브 영상에선 repeating-linear-gradient를 사용해서 해결하지 못하여 linear-gradient로 피봇하여 사용하는 방법에 대해 알려주고 있는데, 이 방법도 유용하다.
@keyframes stripe {
from {
background-position: 0% 0%;
}
to {
background-position: 0% -100%;
}
}
background: linear-gradient(
theme("colors.blue.500" / 15%),
theme("colors.blue.500" / 15%) 3px,
transparent 3px,
transparent 9px
);
background-size: 100% 9px;
animation: stripe 22s infinite linear;
가장 큰 차이점은 background-size를 지정했다는 점인데, 높이 값이 정확하게 linear-gradient 에 사용했던 9px 만큼만 가져가고 있음을 알 수 있다.
이는 UI상으론 하나의 줄무늬를 그리는 코드인데, 이것만으로 어떻게 반복적인 줄무늬 UI를 그릴 수 있는가 질문한다면 그 답은 background에 있다.
background를 지정하고, background에 들어온 이미지의 크기가 컨테이너의 크기보다 작고, background-repeat 속성을 no-repeat으로 설정하지 않은 경우라면 알아서 컨테이너 사이즈를 채울 때 까지 반복하여 적용 된다.
흔히 div 태그에 백그라운드 이미지를 넣고, background-size:cover
처리를 하지 않으면 타일 형식으로 이미지가 깔리는 경험을 한번쯤은 해봤을텐데, 바로 그 상황을 연출한 것이라고 보면 된다.
즉, linear-gradient로도 충분히 줄무늬 UI를 렌더링하고, 애니메이션을 부여할 수 있다.
이로써 줄무늬 간섭 애니메이션 파트를 완성할 수 있었다.
핵심은 줄무늬 UI를 렌더링 하기 위해 linear-gradient 혹은 repeating-linear-gradient를 사용할 수 있고, background-position을 변경하는 방식으로 애니메이션을 적용할 수 있다.
실제로 영상이 존재한다면, 영상을 집어 넣고 끝내면 되겠지만, 해당 파트에선 영상이 없는 경우 이미지만으로 영상처럼 보이게 하는 트릭을 배울 수 있다.
흔히 이미지를 삽입하기 위해선 2가지 방법을 주로 쓰게 된다.
img
태그를 활용하여 이미지 삽입div
태그 내부에 css로 background-image 속성으로 이미지 삽입1번의 방법이 좀 더 시맨틱 태그의 관점에서 적절하지만, 이번 경우엔 이미지를 확대,축소 하는 기능이 필요하기 때문에 2번의 방법에 background-*
속성들을 통해 이미지를 영상처럼 보이도록 만들 계획이다.
과정은 다음과 같다.
div
태그 내부에 background-image
속성으로 이미지 삽입 background-size
, background-position
을 서로 다르게 주어 이미지를 확대,축소한 상태로 이동하도록 하는 keyframes를 구현div
태그에 animation 속성으로 적용.@keyframes pan-image {
0% {
background-position: 36% 42%;
background-size: 200%;
}
20% {
background-position: 30% 35%;
background-size: 200%;
}
20.0001% {
background-position: 60% 85%;
background-size: 500%;
}
40% {
background-position: 49% 81%;
background-size: 500%;
}
40.0001% {
background-position: 80% 42%;
background-size: 300%;
}
60% {
background-position: 84% 33%;
background-size: 300%;
}
60.0001% {
background-position: 0% 0%;
background-size: 300%;
}
80% {
background-position: 15% 4%;
background-size: 300%;
}
80.0001% {
background-position: 80% 10%;
background-size: 300%;
}
}
background-size: 300%;
animation: pan-image 15s linear infinite;
애니메이션이 동작하는 15초 동안 pan-image keyframes가 동작하면서 매 순간마다 background-position
, background-size
를 다르게 주고 있는데, 이를 직접 테스트해보면 background-size
가 변경되는 순간엔 확대,축소 되는 효과를 주고, background-position
가 변경 되는 순간엔 이미지를 이동하는 효과를 주어, 마치 편집된 영상을 재생하고 있는 것 같은 효과를 주게 된다.
background-position
,background-size
에서 사용한 값들은 임의로 지정한 값들이기 때문에 직접 구현할 때는 다른 값으로 변경해도 전혀 무방하다.
컨텐츠 영역은 어디까지나 앞서 만든 2개의 레이어 위에 올라오는 영역이고, 미관을 해치지 않는다는 가정하에 개발자가 원하는 대로 꾸며도 큰 문제는 없다.
개인적으론 간단하게 텍스트를 넣어주면서 주변에 테두리도 넣어 심심함을 덜어주려고 노력했다.
앞서 목표로 했던 3가지 요구사항들을 전부 구현했기 때문에 이제 하나로 합치는 일만 남았다.
우리가 구현한 구성 요소는 다음과 같다.
레이어 순서는 백그라운드 영상 위로 줄무늬 애니메이션이 보여야 하고, 그 위로 컨텐츠가 보여야하기 때문에 줄무늬 간섭 애니메이션 -> 백그라운드 영상 -> 컨텐츠 순으로 레이어가 쌓이게 된다.
이들을 이제 하나의 컴포넌트로 모아주면 되는데, 이는 position:absolute
, z-index
을 통해 쉽게 구현할 수 있다.
position:absolute
,z-index
에 대해 잘 모르겠다면 mdn의 쌓임 맥락에 대해 한번 공부하고 오면 더 좋다.
/* 줄무늬 간섭 애니메이션 */
.stripe-gradient {
background: linear-gradient(
theme("colors.blue.500" / 15%),
theme("colors.blue.500" / 15%) 3px,
transparent 3px,
transparent 9px
);
background-size: 100% 9px;
animation: stripe 22s infinite linear;
position: absolute;
inset: 0px;
z-index: 2;
}
/* 백그라운드 영상 */
.stripe-image {
width: 100%;
height: 100%;
background-image: url("https://images.unsplash.com/photo...);
animation: pan-image 15s linear infinite;
opacity: 0.6;
filter: sepia(100%) hue-rotate(180deg);
background-size: 300%;
position: absolute;
inset: 0px;
}
//컨텐츠(tailwindcss)
<div className="absolute inset-0 z-10 w-full h-full p-5">
<div className="border border-blue-500/70 rounded-lg w-full h-full flex justify-center items-center flex-col">
<h1 className="text-5xl font-medium text-blue-100">Hello world!</h1>
</div>
</div>
velog 특징상 비디오 첨부나 유튜브 영상을 첨부해도 링크로 보이기 때문에 데모 프로젝트 링크를 공유하여 볼 수 있도록 하였다.
오랫만에 이런 인터랙티브한 UI 작업을 진행할 수 있어서 너무 재밌었다.
취업준비에 따른 시장에서 요구하는 기술들을 배우고, 심화하는 과정과는 약간의 다른 즐거움이 있는 것 같다.
리액트, Next.js를 공부하면 서버-클라이언트에 대한 복합적인 고민과, 컴포넌트를 어떻게 나눠야 잘 나눴다고 소문이 나고, 서버 컴포넌트는 어떻게 쓰면 좋을지 연구하고, 등등 좀 더 프론트엔드 전반적인 프로젝트를 더 견고히 하는 방식에 대해 고민하고, 배움이 있다.
그에 비해 이런 인터랙티브한 UI 제작은 프론트엔드 개발자로서 원초적인 재미가 담겨있는 것 같다.
개인적인 감상일순 있겠지만, 어느 프론트엔드 개발자도 이런 작업을 마다하진 않으리라 생각한다. 자신이 작성한 코드가 실시간으로 브라우저로 보여지고, 심지어 예쁘고 멋진 UI라면 더더욱 그러하리라 생각한다.
개인적으론 이런 인터랙티브 UI 제작에 좀 더 많은 시간을 써보면서 감각을 익혀두는 것도 분명 리액트,Next.js를 공부하는 것 만큼이나 도움이 될 것이라고 생각하고, 앞으로도 정진하여 나아가려고 한다.
Using percentage values with background-position on a linear-gradient