안녕하세요 SSAFYicial 신산하입니다.
자율 프로젝트도 모두 끝나고, 포트폴리오 위크를 진행하고 있습니다 : )
포트폴리오를 작성하고, 담당 프로님과 어떤 면접 질문이 나올 것 같은지
이야기해보는 시간을 가졌는데요.
그동안은 프로젝트를 하기에 급급해서
제가 프로젝트에 적용했던 기술을 제대로 이해하지 못한 것들이 많았습니다 ㅠㅠ
그래서 마지막 기획 기사로는 프론트엔드 개발자가 접했던
기술들에 대해 써보는 시간을 가져보려 합니다!
1. R3F 최적화
제가 들었던 질문 중에는 "3D 웹페이지의 로딩 시간이 오래 걸리는데, 어떻게 기술적으로 해결하였는지 궁금합니다. "가 있었습니다. 저희 서비스가 로딩이 많이 걸렸던 이유는 gltf, 3D texture의 이미지 업로드 시간이 오래걸려 오브젝트가 많을수록 그 시간은 배로 늘어났습니다.
1) 로딩 페이지를 도입
저희가 실질적으로 유저의 로딩 시간의 체감을 줄이기 위해 도입한 것은 로딩 페이지였습니다.
3D 오브젝트가 업로드 될 때마다 서서히 드러나는 준비가 안된 페이지를 유저에게 보여주기 보다는 로딩페이지를 도입함으로써 준비된 페이지를 보여주고자 했습니다.
2) useMemo, useCallback
사실 로딩 페이지 도입만으로는 기술적인 해결이라고 보기에는 좀 아쉬웠습니다. 그렇다고 필요한 오브젝트의 수를 지워가면서 서비스의 퀄리티를 낮추는 것도 방법이 아니라고 생각했고요. 그래서 알아보니 useMemo와 useCallback을 사용해 최적화를 할 수 있다는 사실을 알게되었습니다.
저는 위 영상을 보고 실질적으로 R3F 코드를 어떻게 최적화 시키는지 이해할 수 있었습니다.
1. r3f-perf 라이브러리를 다운받습니다.
npm i r3f-perf
Perf는 R3F의 GUI 퍼포먼스를 모니터링할 수 있게 도와줍니다.
2. import 합니다.
import { Perf } from 'r3f-perf';
퍼포먼스를 보고싶은 페이지 메인에 Perf를 import 합니다.
그러면 우측 상단에 GPU, CPU, 초당 프레임, geometry, textures 등이 할당되고 있는 현황을 볼 수 있습니다. 현재는 1 후반에서 2 정도의 GPU가 쓰이고 있어요.
3. useMemo 적용
useMemo는 훅이 연산을 수행하면 결과를 메모리에 저장합니다. 입력 목록의 값이 하나라도 변경되면 다시 연산을 하게 됩니다. 이렇게 하면 결과가 항상 최신 상태로 유지되는 동시에, 불필요한 재연산을 피할 수 있습니다.
영상을 보니 useMemo의 경우에는 R3F가 쓰이는 컴포넌트 전체를 감싸게 하고 있었습니다.
React.memo(컴포넌트) 이런식이었어요.
혹은 gltf 요소에 직접 useMemo를 적용하기도 합니다.
const memoizedISS = useMemo(()=> {
return useGLTF('gltf경로')
})
4. useCallback 적용
useCallback 은 메모이제이션된 콜백 함수, 즉 이미 생성된 함수를 반환하는 리액트 훅입니다. useCallback으로 메모이제이션된 함수는 콜백함수의 의존성이 변경되었을 때에만 변경됩니다. 이는 불필요한 렌더링을 방지하기 위해 (ex. shouldComponentUpdate를 사용하여) 참조의 동일성을 보장하거나, 자식 컴포넌트에 의존적인 콜백 함수를 전달할 때 유용합니다.
R3F에서 useCallback은 useFrame 훅에 사용됩니다.
적용 전
const issRef = useRef()
const memoizedISS = useMemo(()=> {
return useGLTF('gltf경로')
})
const xAxis = 2
useFrame(({clock})=>{
issRef.current.position.x = Math.sin(clock.getElasedTime() * 0.8) * xAxis
issRef.current.position.z = Math.cos(clock.getElasedTime() * 0.8) * xAxis
})
적용 후
const issRef = useRef()
const memoizedISS = useMemo(()=> {
return useGLTF('gltf경로')
})
const clockRef = useRef(new THREE.Clock())
const updateMoonPosition = useCallback(()=>{
const xAxis = 2
issRef.current.position.x = Math.sin(clockRef.getElasedTime() * 0.8) * xAxis
issRef.current.position.z = Math.cos(clockRef.getElasedTime() * 0.8) * xAxis
},[])
useFrame()=>{
updateMoonPosition()
})
2. 웹소켓 STOMP
저는 공통 때 webRTC 보다는 웹소켓을 이용해 1:1, M:N 채팅을 구축해 입퇴실, 블랙리스트, 이미지 채팅 전달 등의 활용을 진행했습니다. 그런데 웹소켓 STOMP 사용 이유를 물어보시니 대답을 못하겠더군요 ㅠ
저는 글로된 블로그보다는 유튜브를 많이 보는 편인데 위 유튜브가 이해에 많은 도움이 되었습니다.
우선 웹소켓은 웹 어플리케이션을 위한 양방향 통신기법으로, 실시간성을 보장할 수 있는 방식입니다. STOMP를 쓰는 이유를 알기 위해선 웹 소켓의 한계에 대해 이해해야합니다.
웹소켓은 문자열들을 주고 받을 수 있게 해줄 뿐 그 이상의 일을 하지 않습니다. 주고 받는 문자열의 해독은 온전히 어플리케이션에 맡겨요.
HTTP를 생각해보면 형식이 정해져 있기 때문에 모두가 약속을 따르기만 하면 해석할 수 있습니다. 하지만 웹소켓은 형식이 정해져 있지 않기 때문에 어플리케이션에서 쉽게 해석하기 힘들어요. 때문에 웹소켓 방식은 sub-protocols를 사용해 주고 받는 메세지의 형태를 약속하는 경우가 많습니다.
이런 sub-protocols로 많이 쓰이는 것이 STOMP(Simple Text Oriented Message Protocol)입니다. 또한 STOMP는 프레임 기반의 프로토콜입니다. STOMP는 채팅 통신을 하기 위한 형식을 정의해줍니다. HTTP와 유사하게 간단히 정의되어 해석하기 편한 프로토콜입니다. 일반적으로 웹소켓 위에서 사용합니다.
자주 사용되는 명령은 CONNECT, SEND, SUBSCRIBE, DISCONNECT 등이 있습니다.
저는 STOMP만 사용해서 채팅 등의 기능을 구현하고, 서버에 올렸을 때 WSS라는 프로토콜로만 소통이 가능해서 서버로 올리는 일을 못했습니다. HTTPS로 프로토콜을 교체하는 과정이 필요했는데, 이 역할을 SOCKJS가 도와줬던 경험까지 추가로 남깁니다ㅎㅎ
제가 웹소켓을 썼던 내역은 아래 git에서 참고하실 수 있습니다 :)
모두들 프로젝트 정리 잘하시고,
면접에서 지금까지의 노력을 잘 어필하시고 오셨으면 좋겠습니다 : )
화이팅!
'대외활동 > SSAFYicial' 카테고리의 다른 글
[회고] 내가 만약 자율프로젝트 1일차로 돌아간다면? 징크스를 뚫고 수상까지... (6) | 2023.11.28 |
---|---|
[CS 정리는 내가 할게, 면접은 누가볼래? - 프론트엔드/RN편] 프론트엔드/React Native 면접 질문 필수 암기 모음집 2탄 (1) | 2023.10.23 |
[회고] 내가 만약 특화프로젝트 1일차로 돌아간다면? 하반기 취준과의 전쟁 (0) | 2023.10.22 |
[CS 정리는 내가 할게, 면접은 누가볼래? - 알고리즘편] 자주쓰는 알고리즘 10가지 정리! (0) | 2023.09.26 |
[TMI] 신한 해커톤 with SSAFY 현장 속으로! (0) | 2023.09.26 |