CSS만으로 마법 만들기 — 별이 흐르는 배경과 3D 카드 뒤집기

 


애니메이션 없는 타로 앱은 카드를 뒤집지 않는 것과 같다

타로 리딩에서 카드를 뒤집는 순간은 가장 극적인 순간이다. 손으로 카드를 천천히 뒤집을 때의 긴장감, 그 앞면이 드러나는 순간의 설렘. 이걸 웹 앱에서 "클릭하면 이미지가 바뀌는 것"으로 구현하면 그 모든 감정이 사라진다.

그래서 애니메이션에 진지하게 투자하기로 했다. 배경부터 카드 인터랙션까지, CSS와 Framer Motion만으로 어디까지 표현할 수 있는지 실험하는 과정이었다. 결론부터 말하면, 생각보다 훨씬 많은 것이 가능했다.

별이 흐르는 배경: radial-gradient 3중 레이어

남색 배경(#0a0a1a)은 충분히 아름답지만, 그것만으로는 밋밋하다. 밤하늘에 별이 없으면 그냥 어두운 천장을 보는 것과 같다. 별 배경을 만드는 방법은 여러 가지가 있다. Canvas API, SVG 애니메이션, 심지어 별 이미지를 깔 수도 있다. 하지만 가장 가볍고 우아한 방법은 CSS radial-gradient를 중첩하는 것이었다.

핵심 아이디어는 간단하다. 아주 작은 원형 그라데이션을 무수히 많이 반복하면 별처럼 보인다. 이걸 크기가 다른 세 개 레이어로 나누면 깊이감이 생긴다. 가장 작은 별은 빠르게, 중간 별은 보통 속도로, 큰 별은 느리게 움직이면 패럴랙스 효과까지 얻을 수 있다.

첫 번째 레이어는 1px 크기의 아주 작은 별들이다. 화면 전체에 촘촘히 흩뿌려진 이 별들은 마치 먼 은하수처럼 보인다. 두 번째 레이어는 2px짜리 별로, 첫 번째보다 좀 더 밝고 덜 촘촘하다. 세 번째 레이어는 3px로, 가장 밝고 드문드문 배치된다. 이 세 레이어를 background 속성에 쉼표로 연결하면 하나의 요소에 모두 적용할 수 있다.

여기에 CSS animation으로 각 레이어의 background-position을 서로 다른 속도로 움직이면 별이 천천히 흐르는 효과가 완성된다. 가장 작은 별은 1분에 한 번, 중간은 90초, 큰 별은 2분에 한 번 순환하도록 설정했다. 이 미세한 속도 차이가 평면 화면에 놀라운 깊이감을 부여한다.

3D 카드 뒤집기: 0.8초의 미학

카드 뒤집기 애니메이션은 이 프로젝트에서 가장 많은 시간을 들인 부분 중 하나다. CSS 3D transform의 기본 원리는 단순하다. 앞면과 뒷면 두 요소를 겹쳐놓고, 부모를 Y축 기준으로 180도 회전시키면 된다. 뒷면에는 backface-visibility: hidden을 적용하면 뒤집어졌을 때 자동으로 사라진다.

하지만 "기술적으로 동작하는 것"과 "기분 좋게 느껴지는 것" 사이에는 큰 차이가 있다. 처음에 transition 시간을 0.3초로 설정했다. 일반적인 UI 인터랙션의 표준적인 시간이다. 그런데 너무 빨랐다. 카드가 "뒤집히는" 게 아니라 "전환되는" 느낌이었다. 긴장감이 없었다.

0.5초, 0.6초, 0.8초, 1초, 1.2초를 모두 테스트했다. 결론은 0.8초였다. 카드가 뒤집히기 시작할 때 살짝 느리게 출발하고, 중간에서 가장 빠르며, 앞면이 드러날 때 다시 약간 느려지는 느낌. 이 "무게감"이 실제 카드를 손으로 뒤집는 물리적 감각과 가장 비슷했다.

이 느낌을 만드는 핵심은 cubic-bezier(0.4, 0, 0.2, 1) 이징 함수다. 구글 머티리얼 디자인에서 "standard easing"으로 사용하는 커브인데, 자연스러운 감속이 특징이다. 카드처럼 물리적 오브젝트의 움직임을 모방할 때 특히 효과적이다.

Framer Motion이 더해주는 세밀함

CSS 3D transform만으로도 카드 뒤집기는 가능하다. 그런데 왜 Framer Motion을 추가로 사용했을까. 이유는 두 가지였다. 첫째, React의 상태 변화와 애니메이션을 자연스럽게 연결하기 위해서다. CSS만으로는 "카드가 뒤집힌 상태"와 "아직 뒤집히지 않은 상태"를 React 상태와 동기화하는 것이 까다롭다.

둘째, 여러 카드를 순차적으로 뒤집는 연출이 필요했다. 쓰리카드 스프레드에서 세 장이 동시에 뒤집히면 극적이지 않다. 첫 번째 카드가 뒤집히고, 0.3초 후 두 번째, 다시 0.3초 후 세 번째가 뒤집히는 "스태거" 효과가 훨씬 낫다. Framer Motion의 staggerChildren은 이걸 한 줄로 해결해준다.

Framer Motion의 animate 속성으로 rotateY 값을 제어하고, CSS의 perspective 속성으로 3D 깊이감을 조절하는 조합이 최종 결과였다. perspective 값은 1000px로 설정했다. 너무 작으면 카드가 과하게 왜곡되고, 너무 크면 3D 효과가 희미해진다.

역방향 카드: 뒤집기 완료 후의 미묘한 처리

타로에는 역방향(리버스) 카드라는 개념이 있다. 카드가 거꾸로 나오면 정방향과 다른 의미를 가진다. 이걸 애니메이션에 어떻게 반영할지가 까다로운 문제였다.

처음에는 뒤집기 시작 전에 카드를 180도 회전시켜놓는 방법을 시도했다. 그러면 뒤집혔을 때 거꾸로 보인다. 하지만 이 방법은 뒷면도 거꾸로 보이는 문제가 있었다. 타로 카드의 뒷면은 대칭이라 상관없을 수 있지만, 사용자에게 미묘한 위화감을 줄 수 있다.

최종 해결 방법은 Y축 뒤집기가 완전히 끝난 후에 역방향 회전을 적용하는 것이었다. 뒤집기가 완료되면 앞면이 보이고, 그 즉시 자연스럽게 180도 회전이 추가된다. 이 두 번째 회전은 아주 빠르게(0.2초) 처리해서 뒤집기 애니메이션의 연장선처럼 느껴지게 만들었다.

카드 뒷면: 절제가 만드는 대비

카드 뒷면 디자인에도 신경을 썼다. 처음에는 복잡한 문양을 넣으려고 했다. 하지만 AI와 논의하면서 다른 결론에 도달했다. 뒷면이 화려하면 앞면이 드러나는 순간의 임팩트가 줄어든다.

결국 뒷면은 남색 배경에 골드 테두리, 중앙에 작은 별 문양 하나만 넣는 절제된 디자인으로 결정했다. 이 "절제된 뒷면"에서 "화려한 앞면"으로의 전환이 뒤집기 애니메이션의 극적인 효과를 극대화한다. 어두운 방에서 카드가 빛나며 드러나는 느낌.

이것은 디자인에서 자주 간과되는 원칙이다. 강조하고 싶은 부분을 돋보이게 하려면 주변을 억제해야 한다. AI도 이 원칙을 잘 이해했고, 뒷면 디자인의 단순화를 먼저 제안한 것이 인상적이었다.

라이브러리 없이 분위기를 만드는 CSS의 가능성

이번 작업을 통해 확인한 것은 CSS가 여전히 강력하다는 사실이다. Three.js나 WebGL 없이도, 순수한 CSS 속성들의 조합만으로 꽤 인상적인 시각적 경험을 만들 수 있다. 별 배경은 이미지 파일 하나 없이 구현되었고, 카드 뒤집기는 CSS 3D transform의 기본 기능이다.

물론 한계도 있다. 별의 개수를 수백 개로 늘리면 성능이 떨어질 수 있고, 복잡한 파티클 효과는 CSS만으로는 어렵다. 하지만 "분위기를 만든다"는 목적에는 CSS만으로도 충분했다. 번들 사이즈를 키우지 않으면서도 사용자 경험을 크게 향상시킬 수 있다는 점에서 CSS의 가성비는 여전히 최고다.

AI와의 협업에서 CSS 관련 질문은 특히 생산적이었다. "이런 효과를 만들고 싶은데 CSS로 가능한가?"라고 물으면 대부분 가능한 방법을 찾아주었다. 불가능한 경우에는 대안을 제시해주었고, 그 판단이 거의 정확했다.

다음 편 예고

화면은 완성되어간다. 배경에는 별이 흐르고, 카드는 아름답게 뒤집힌다. 하지만 아직 해결하지 못한 근본적인 문제가 있다. 78장 타로 카드의 이미지를 어디서 구할 것인가. 다음 편에서는 무료 이미지를 찾아 인터넷을 헤매던 이야기, 저작권의 함정, 그리고 AI가 알려준 URL이 실제로는 존재하지 않았던 당황스러운 경험을 공유한다.

댓글

이 블로그의 인기 게시물

사랑을 직접 올리지 않는 설계

감정을 변수로 옮기다 — 3계층 감정 모델

시작의 충동 — "타로 웹앱을 만들어볼까?"