[React]Unity Webgl Memory 에러

전수현·2024년 6월 18일

Unity

목록 보기
2/2

저번 글에 이어 Web에서 Unity Webgl을 이용해 미니 게임을 만드는 중 생긴 이슈에 대해 소개(?)합니다.
Unity의 스킬에 대해서는 다른 분들의 글이 훨씬 훌륭하기에 저는 React에서 Webgl 이용 시 생긴 이슈에 대해서만 소개하고 있는데 강의나 책에서는 나오지 않는 내용이라 오류를 잡는데 너무 애를 먹었습니다 ㅠ

정확히 어떤 이슈가 발생했는지 보여드리자면,

처음 게임 구동시에는 발생하지 않으나 뒤로가기나 다른 페이지에 갔다가 다시 되돌아올 경우 System Out of memory!!!!!! 에러를 내뱉으며 게임이 실행되지 않았습니다.
또한 해당 에러는 PC에서는 발생하지 않고 모바일 기기에서만 발생하다보니 발견도 늦었습니다.

처음 해당 이슈를 직면했을 때는 별 생각없이 검색하면 나오겠지 싶었는데.. 내용은 많으나 저와 같은 상황인 사람도 없었으며 정확한 해결 방법이 나와 있지 않았습니다.

스스로 문제를 해결해보자.....생각해보자....system out of memory면 메모리 누수가 원인이지 않을까? 라는 생각에 Unity Memory 관리에 대한 검색을 시작!
Memory Heap을 늘리는 내용에 대한 내용이 있었지만 근본적인 해결책은 아니었습니다.

그럼 뒤로가기나 다른 페이지로 이동하기 전에 메모리를 해제하는게 없을까 싶어 찾아보니,
react-unity-webgl hook에 unload라는 함수를 발견!


unload() 함수를 호출 시 console에 Quitting... -> done! 이라는 문구가 뜹니다.
ios 기기에서 Memory를 확인할 수 있는 방법이 없기에 메모리 해제가 잘 되었는지 눈으로 확인할 수는 없지만 기분상(?) 메모리 할당이 해제되지 않나 싶습니다.
직접 모바일 기기에서 테스트 하니 더이상 오류로 인해 실행되지 않던 문제가 해결되었습니다!
다만 지금은 버튼을 누르면 unload()를 호출하고 그 이후에 페이지 이동을 하는 식으로 테스트를 했으나, 사용자 입장에서 그건 너무 불편하기에 뒤로가기나 페이지 이동 시 interceptor 후 unload를 호출하고 페이지를 이동시키는 로직 추가가 필요했습니다.

기존에도 입력 form에서 실수로 뒤로가기나 새로고침을 막기 위해 window.addEventListener의 popstate와 beforeunload 이벤트를 활용해왔습니다. popstate와 beforeunload 시 unload를 호출하면 되지 않을까 싶었으나..

Unable to Quit Unity while Unity is not Instantiated.

오류 발생.. unity instance를 찾지 못해 생기는 오류라고 하는데..난 분명 load를 했건만?
popstate, beforeunload 이벤트 발생 시 unity component가 없어지나 싶었지만 다시 게임을 재개해도 잘 되는거 보면 그건 아닌거 같습니다. 그리고 또 하나 문제는 popstate를 써보면 알겠지만 현재 페이지의 url를 한번 더 넣어 뒤로가기가 되어도 현재 페이지의 url를 유지하게끔 눈속임(?)을 하는거다 보니 나처럼 unload() 후 원래 하려던 페이지 이동을 하게 하는게 힘들었습니다.

다른 방법이 없는지 다시 서칭하던 중 아주 좋은걸 발견!
React Router Dom v6 버전에 추가된 'useBlocker' hook!

https://reactrouter.com/en/main/hooks/use-blocker
공식 문서 참조

간략하게 번역하면 'useBlocker를 사용하면 사용자가 현재 위치에서 벗어나는 것을 방지하고 사용자 지정 UI를 제공하여 탐색을 확인할 수 있습니다.' 라고 합니다.

사용 방법은 생각보다 간단합니다!

const blocker = useBlocker(({ currentLocation, nextLocation }) => {
	return true // true가 Return 되면 block이 걸려 페이지 이동이 안됩니다.
})

useEffect(() => {
	// useBlocker에 true가 return되면 blocker의 state가 'blocked' 상태로 변경
	if (blocker.state === 'blocked') { 
    	//해당 부분에 Alert를 띄워주거나 추가 작업을 하면 됩니다.
        // 
    }
}, [blocker])

blocker 객체를 조금 더 살펴보면

location에 현재 페이지 이동을 원하는 주소가 들어가게 되고 심지어 useNavigate() hook 사용시 유용한 state값도 들어가는걸 확인할 수 있습니다.

이걸 활용해 페이지 벗어남이 감지되었을 때 unload 이후 원래 가려던 페이지로의 이동이 가능해졌습니다.

	const blocker = useBlocker(({ currentLocation, nextLocation }) => {
        return !exitFlag.current // 페이지 벗어날 때 한번만 block을 걸기 위해 ref 변수 사용
    })

    useEffect(() => {
        if (blocker.state === 'blocked') {
            unload().then(() => {
                exitFlag.current = true
                if (blocker.location.pathname) navigate(blocker.location.pathname, { state: blocker.location.state })
                else navigate(-1)
            })
        }
    }, [blocker])

이렇게 또 해당 프로젝트를 이어나갈 수 있게 되었습니다.
Unity 자체의 정보는 굉장히 많지만 Webgl에 관한 정보는 상대적으로 부족해 오류 해결이 너무나도 어려웠습니다 ㅠ
저와같은 고충을 겪는 개발자 분들에게 도움이 되는 글이 되길 바랍니다!

profile
느렸지만 빠른

0개의 댓글