하루 만에 혼자 3D 로 신년카드 웹앱을? [항해+ 코육대 2회 회고]

하민·2024년 1월 1일
250
post-thumbnail

신년카드 웹앱 이미지

새해인사 우체통 (Web)

서비스 링크: https://posts.run
위 URL 에서 실제로 신년인사 카드를 작성해보실 수 있어요!

코육대 예시

항해 플러스 코육대에서 행사를 연다는 소식을 한참 뒤늦게 듣고 빠르게 도전해보기 위해 연말 하루를 달려본 끝에 회고 글을 작성하게 되었네요!, 이번에 주어진 주제가 굉장히 다양했는데 저는 신년카드 메이커를 한번 만들어보고 싶더라고요! 그래서 한 번 연말에 작업을 해봤어요!

사용한 기술 스택


왜 Spline Design 을?

스플라인 디자인 이미지
스플라인 디자인은 웹에디터에서 바로 3D 에셋을 디자인 한 후 배치하고, 즉시 React 에 결과물을 연동시킬 수 있는 3D 툴이에요, 3D 에셋에 적용되는 이벤트에 따른 트랜지션 처리를 미리 구성하거나, 아예 미리 배치된 3D 파일들을 react-three-fiber 에서 사용할 수 있는 코드로 추출할 수도 있는 강력한 기능들을 가지고 있어요.

무엇보다 언리얼 엔진이나 유니티처럼 공식적으로 제공하는 무료 튜토리얼들 같은 무료 에셋들이 굉장히 많이 있어서 바로 원하는 디자인을 빠르게 시작해볼 수 있다는 점도 강력한 점이기도 하고요.

기능 구현 설명

3D State 작업

State 설명 이미지

가장 먼저 제가 진행한 작업은 우선 스플라인 디자인에서 제공되는 무료 에셋들을 원하는 대로 배치한 후, 각 에셋들의 State 를 주는 작업이였어요.

스플라인은 3D 사물의 A 상태와 B 상태를 미리 스냅샷을 찍어놓는 것처럼 개별적으로 만들 수 있고, 내부에서 Transition 기능을 사용하면, A 상태에서 B 상태로 변화하는 과정을 끊김없이 보간처리해서 표시를 해줘요. 이걸 무한히 반복되게 한다면 쉽게 Emoji 애니메이션을 만들 수 있어요!

그리고 스플라인 내에서는 물리엔진도 제공이 되는데 이를 이용하면 일부 Emoji 들에는 중력 효과를 적용한 후 떨어지게 하는 작업을 진행할 수 있었어요, (언리얼엔진의 가상 조이스틱 같은 GamePad 기능으로 더욱 다양한 기능이 있어서 실제 게임도 만들 수 있어요)

3D Event 작업

Event 설명 이미지

그 다음은 "우체통의 문을 Click 했을 때" 를 이벤트를 거는 작업을 진행했어요, 처음 눌렸을 때 사물을 움직이는 트랜지션을 주고, 미리 배치된 다른 카메라들로 카메라를 전환 시키는 작업이나, 효과음을 재생시킨다던지, 우체통 안 편지지를 크게 가져와서 줌인시키는 등의 이벤트 연동 처리를 구성해놓았어요.

Variable 연동 작업

Variable 설명 이미지

그리고 기타 다른 소리나 모션 등의 연동을 끝낸 후엔, 해당 모든 상호작용들이 Variable 에 의해서 동작되게 다른 상호작용 설정들을 모두 연동해주는 작업을 진행했어요, 3D 공간 내에서 표시되는 글자들도 변수에 의해 동작되도록 설정해주고나면, 이제 React 나 자바스크립트에서 이 변수들을 제어할 수 있게되고 외부에서 원하는대로 3D 요소가 변경되도록 할 수 있어요.

물론 이러한 작업은 react-three-fiber 에 비해선 아직 극히 제한적인 영역에서 바인딩이 가능하며 자유도도 떨어지는 단점들이 있어요. 하지만 점점 무서운 속도로 발전하고 있고, Spline Design 내에서 배치 작업만 한 후에 코드로 내보낼 수도 있으니, 무척 자유로운 사용이 가능하기 때문에 큰 불편함은 없었어요.

Next.js + Tailwind + Valtio

IDE

3D 구성을 끝낸 다음엔, 바로 Next.js 를 초기 설정한 후, 테일윈드를 이용해서 스타일링을 진행하고, Valtio 로 상태관리를 빠르게 구성을 진행하기 시작했어요, 테일윈드 같은 경우는 인라인에서 바로 스타일 작성이 가능해서 매우 직관적으로 스타일링을 빠르게 진행할 수 있다는 장점이 있었고, Valtio 같은 경우는 Proxy 패턴을 이용해서 바로 상태를 수정하는 코드를 작성할 수 있다는 점이 이번 빠르게 작성해야하는 프로젝트에 매우 적합하다 판단하여 진행을 시작했어요.

왜 Next.js 를 선택했는지도 얘기하자면, Next.js 는 BFF(Back-End For Front-End) 라고 불리우는 작업을 할 때 매우 유용한데, 프론트엔드 작업을 위한 백엔드를 별도로 구성하는 작업을 매우 신속하고 유기적으로 구성할 수 있고, 이 과정에서 모노레포를 별도로 구성할 필요도 없었다는 점이 가장 큰 강점이였어요.

Valtio 를 사용한 상태 관리

Valtio 설명 이미지

Valtio 로 상태 저장소를 만든 간단한 모습이에요, 이용자가 새해인사 카드에 작성하는 메세지와, 수신자 명, 그리고 이미지 내 첨부되는 이미지들을 상태로 간단하게 정리한 모습이에요. Zustand 의 Flux 패턴과는 다른 점으로는 상태를 변경하기 위한 패턴이 디스패쳐 내부로만 한정된 점이 아니라는 점인데, Valtio 는 Proxy 패턴을 이용하기 때문에 상황에 따라 매우 유연하게 설령 디스패쳐라 하더라도 원하는 형태로 관심사 분리를 빠르고 자유롭게 구성할 수 있는 장점이 있다고 판단해서 사용을 하였어요.

Valtio 연동 설명 이미지

Valtio 로 구성된 상태를 실제 컴포넌트 내에서 사용할 때에는 그냥 바로 사용하면 되는 구조에요, Proxy 패턴을 이용하면 Context 사용량도 줄이고 여러 최적화도 진행할 수가 있는 장점이 있지만, 성능보다는 저는 아무래도 편리하고 직관적인 구조 때문에 간단한 프로젝트에서 주로 사용하고 있어요.

Konsta UI 기반 컴포넌트 구성

Konsta UI 설명 이미지

이번 코육대에는 A.I 로 신년카드에 사용될 이미지를 생성하는 기능이 있었는데, 이걸 위해서 별도로 Tailwind 프레임워크인 Konsta UI 를 이용해보았어요. 콘스타 UI 는 어플 느낌을 주는 디자인을 제공해주는데, 아무래도 빠르게 UI 를 구성할 때에 모바일 UI 만을 구성한다면 매우 빠른 속도로 개발이 가능하기 때문에, 모바일 디자인 만을 고려한 프레임워크를 가져와서 사용하였어요.

A.I 이미지 생성 API 개발

이미지 생성 코드 이미지

그 이후 Next.js 내에서 API 를 개발하는 작업을 바로 진행해보았는데 다행이도 Open A.I 에서는 라이브러리를 제공하고 있었기 때문에 매우 빠르게 DALL-E-3 로 이미지 생성 호출을 사용할 수 있었고, 이미지 업로드는 Cloudflare R2 저장소를 이용해서 구성했어요 (R2 는 S3 에 비해 쉽게 생성 가능하고, S3 와 호환되면서 송신비가 무료인 장점이 있어요.)

DALL-E-3 프롬프트 엔지니어링

프롬프트 엔지니어링

DALL-E-3 의 이미지를 더욱 더 잘 출력하기 위해서, 이용자에게 출력하고 싶은 이미지의 색상과 스타일 설정을 입력받아서 출력하는 프롬프트 엔지니어링을 진행했어요. 프롬프트 작성은 직접 원하는 내용을 정리한 다음 GPT-4 에게 다시 검증하고 고쳐달라는 작업을 여러번 거쳐서 잘 동작하는 프롬프트를 얻을 수 있었어요.

DALL-E-3 출력 결과물 이미지

그리고 위와 같은 화려한 마치 반 고흐의 그림 스타일 느낌이 나는 이미지를 출력받을 수 있었어요. 하지만 A.I 이미지 출력 보단 실제 추억이 담긴 이미지들을 업로드하고 싶어하는 사람들이 더 많을 것 같아서, 그냥 개인의 이미지를 업로드 할 수 있는 기능 또한 추가해놓았어요.

Sharp 를 이용한 이미지 압축 처리

이미지 압축처리

DALL-E-3 를 이용해서 이미지를 출력하고 보니, 5.5MB라는 무척 큰 이미지 용량이 출력되는 모습을 볼 수 있었어요, 이를 해결하기 위해서 sharp (이미지 처리 라이브러리) 를 이용해서 이미지를 webp 로 변환하는 작업을 단 한줄로 추가할 수 있었고, 5MB -> 400kb 내외로 이미지가 급격하게 줄어드는 것을 확인할 수 있었어요.

Supabase 를 이용한 DB 구성

Supabase 에디터

Supabase 는 PostgreSQL 기반 서비스로, Firebase 와 유사한 포지션을 가지나, 오픈소스이기 때문에 원하는 서버에 직접 운영할 수 있다는 강력한 장점과, SaaS 서비스를 제공하고 있어서 무료로도 편하게 프로젝트를 생성해볼 수 있다는 장점이 있어요.

다른 여러 장점들도 많지만, 이번 프로젝트에서는 우선 DB 구조를 GUI 에서 엑셀 칸 만들듯이 작성할 수 있다는 장점과, ORM 라이브러리 또한 제공해줘서 쿼리문을 매우 간단하게 JS 문법으로 사용할 수 있다는 점을 활용하기 위해 Supabase 를 사용해보았어요.

Supabase 비주얼라이징

사실 이번 기능은 별다른 테이블이 필요 없이 단순 이벤트용 저장 기능 만을 구성했어요. 테이블 하나만 설정하고 빠르게 마무리 지었어요.

Supabase 문서

이렇게 만들고나면 프로젝트용 Supabase DB 사용 문서도 자동으로 만들어주는 친절함을 가지고 있어요!

안전하고 짧은 이용자 URL 만들기

슈도셔플 사용 이미지

그리고 저는 Supabase 로 이용자들이 업로드 한 신년카드 정보를 토대로, posts.run/new-year/ragsw5 과 같이 무작위로 짧은 7자 이하의 문자열 코드를 발급해서 이용자에게 전달하는 코드를 작성해주고 싶었어요.

만든 순서대로 숫자를 그냥 뒤에 붙이면 되지 않을까요? 라고 생각할 수 있지만, 이렇게 구성한 경우에는 시간대 별로 서버 전체에 작성된 모든 신년인사 카드 정보들을 하나씩 이용자가 접근하게 될 수 있다는 단점이 발생하게되고, 어쩌면 보안 상의 문제로 이어질 수도 있는 단점이 있어요.

원래는 이러한 작업을 할 땐, 별도로 테이블을 하나 더 만들고, 거기서 마치 쿠폰코드를 발행하듯이 한가득 코드들을 발행시켜놓고 하나씩 차감하는 형태로 개발을 하는데, 이러면 유지보수도 번거롭기에, Pseudo-Shuffle 이라는 라이브러리를 사용해서 해당 기능을 구현했어요.

슈도셔플

사실 이 라이브러리는 형태보존 암호화라는 기법을 사용하는 node-fe1-fpe 라는 라이브러리에서 몇가지 치명적인 문제점들을 직접 고쳐서 수정해놓은 제가 개발한 라이브러리인데, 이번에 활용 사용하게 되었어요!

동작 방식을 최대한 간단하게 설명하면, 원래 문자열은 암호화를 거치고나면, 원본 문자열에 비해서 훨씬 그 길이가 길어져요, 하지만 형태보존 암호화를 이용하면, 길이를 늘리지 않고도 내가 원하는 범위 내에서 암호화 된 결과 값들이 나오게 할 수 있어요.

이를 이용하면 주민등록번호를 섞어서 암호화 한다던지, 이번처럼 안전한 URL을 만든다던지 하는 다양한 경우에 활용될 수 있는 장점이 있지만, 원래 node-fe1-fpe 에는 치명적 단점들이 있었는데 아래와 같아요.

  • 소수(Prime Number) 에 해당하는 순서번호인 경우 암호화가 실패했어요. (pseudo-shuffle 은 모든 경우수를 암호화하고 복호화 할 수 있게 예외처리를 진행해놓았어요.)
  • 암호화 길이가 초과되는 경우에 대한 예외처리가 없었어요. (pseudo-shuffle 에서는 범위를 초과한 경우에는 초과한 범위 만큼 다시 섞는 코드를 추가해놓았어요.)

위와 같은 단점들을 극복한 경험을 통해서, 간단하게 pseudo-shuffle 내에 있는 encodedecode 함수를 이용해서 간단하게 문제를 한줄로 안전한 URL을 만드는 문제를 해결할 수 있었어요.

SEO 메타 태그 처리

Next.js metadata 처리

요즘 Next.js 는 메타 태그 작성하는 구간이 정말 잘 구성되어 있어서, 그냥 metadata 란에서 자동 완성이 뜨는 키 값들을 모두 열어서 모두 작성해넣으면 Open Graph 와 Twitter (이제는 X..) 의 메타 태그 처리를 참 쉽게 완성할 수 있었고, 이 결과물이 카카오톡과 같은 SNS 에서 잘 표시되는 것을 확인할 수 있었어요.

Next.js -> Vercel 배포

Vercel 배포

그 후 최종적으로 Vercel 에 실제 웹앱 코드를 배포했어요. BE 와 FE 를 동시에 배포하는 것이니 관리도 쉽고 업데이트도 동시에 발생할 뿐더러, 어디서 문제가 발생했는지 로그 추적하기가 굉장히 편리한 경험을 주는 Vercel 을 이용해서 실제 앱 배포는 5분도 안 되서 쉽게 배포를 마칠 수 있었어요.

회고를 마치면서

이번에 정말 짧은 시간 안에 타임어택 하면서 FE와 BE, 3D랑 인프라를 한 번씩 모두 작업하고 실제 프로젝트를 출시까지 해보면서 한 해를 마무리 짓는 재밌는 경험을 할 수 있었어요. 좋은 경험을 할 수 있게 이벤트 기회를 마련해주신 항해 99에 감사드려요!


Web Service: posts.run
Github: https://github.com/hmmhmmhm/posts.run
항해플러스 코육대 2회: https://hanghaeplusevent20241215.oopy.io

profile
하이브리드 웹 개발자입니다. 웹 기술에 관심이 많고 최근엔 Spline Design 을 웹에 접목해서 3D 프로젝트를 개발하는 것에 관심이 많습니다!

31개의 댓글

comment-user-thumbnail
2024년 1월 2일

그저 빛...

1개의 답글
comment-user-thumbnail
2024년 1월 2일

우와 신기하네요! 좋은 글 감사합니다 ~~

1개의 답글
comment-user-thumbnail
2024년 1월 2일

와.... 쩐다

1개의 답글
comment-user-thumbnail
2024년 1월 2일

저도 언젠가 한번 spline 써봐야겠습니다. 빠르게 개발할때 좋아보이네요..ㄷ 좋은 글 감사합니다!

1개의 답글
comment-user-thumbnail
2024년 1월 2일

spline3D 너무 좋은 프로덕트죠! 베타 때부터 종종 쓰고 있는데, variant를 연동해서 쓰는 방법도 제안하는지는 몰랐네요. 소개해주셔서 감사합니다. 그리고 같은 인터랙티브 웹을 다른 주제에서 풀어내신 걸 보고 반가웠어요. 나중엔 생성된 신년 카드도 3d면 어떨까.. 하는 상상을 하게 되네요. 신년인사 카드를 만드신 만큼 올해 즐겁고 재미난 프로젝트로 가득 하시길 바랄게요~ 새해 복 많이 받으세요~!

1개의 답글
comment-user-thumbnail
2024년 1월 2일

너무너무 도움되는 멋진 포스트 감사드립니다!! 혹시 spline 에서 code export 하실 때 react 로 export 하신 후 다른 r3f / react 코드들과 작업하셨나요? 아니면 다른 방법을 쓰셨나요? 제가 만들고 있던 r3f 프로젝트에서 나무 모델 컴포넌트만 떼서 spline 에서 애니메이션을 추가한 후 export 해서 제 프로젝트에 가져오려고 하니까 무슨 일인지 에러가 계속 생겨서요! Uncaught Error: R3F: Canvas is not part of the THREE namespace! Did you forget to extend? 이런 에러들이 있는데 혹시 방법 공유해주실 수 있으시면 정말 감사하겠습니다!

1개의 답글
comment-user-thumbnail
2024년 1월 3일

와...멋있습니다.

1개의 답글
comment-user-thumbnail
2024년 1월 4일

엄청난 고수.. 이걸 하루만에 한다고요?

1개의 답글
comment-user-thumbnail
2024년 1월 4일

재밌게 잘 읽었습니다

1개의 답글
comment-user-thumbnail
2024년 1월 5일

안녕하세요 지금 카드 만들어보려고 했더니 이미지가 안떠서 오류난 것 같은데 확인 가능하실까요~?

2개의 답글
comment-user-thumbnail
2024년 1월 7일

우왕왕 멋있어요

1개의 답글
comment-user-thumbnail
2024년 1월 8일

선생님 vscode 테마 정보 궁금합니다.

1개의 답글

굉장하군요

답글 달기
comment-user-thumbnail
2024년 1월 9일

대단합니다

답글 달기
comment-user-thumbnail
2024년 3월 21일

혹시 vscode theme 뭐 쓰시는지 여쭤봐도 될까요??

답글 달기