본문 바로가기
Three.js/Intro

[threejs-journey 1-7] Resizing & FullScreen

by SL123 2025. 6. 12.

 

개요

Three.js를 이용해 웹에서 3D Scene을 구현할 때는 브라우저의 크기 변화나 디스플레이 환경에 따라 다양한 문제가 발생할 수 있습니다. 본 문서에서는 창 리사이징 시 발생하는 화면 찌그러짐과 여백 문제, 그리고 전체화면(Fullscreen) 모드 구현에 대한 해결 방법을 다루겠습니다.

 

 

 

- Resizing

Three.js로 만든 3D Scene을 웹을 렌더링하면,  브라우저 창 크기를 조절할 때 그림이 찌그러지거나, 모서리에 하얀 여백이 생기는 현상이 발생할 수 있습니다.

 

기존 sizes를 800x600 에서 window.inner 변수를 이용해 실제 뷰 포트를 조정해줍니다.

const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

현재 해당 이미지에서 하얀 여백이 모서리 부분에 액자처럼 생긴 것을 확인할 수 있습니다. 이러한 부분을 css 추가를 통해 수정할 수 있도록 해주겠습니다.

 

 

- CSS

먼저 style.css파일을 생성해주고 index.html 에서 .css파일을 불러옵니다.

 <link rel="stylesheet" href="./style.css">

 

 

이후 .css 파일을 작성합니다.

* {
    margin: 0;
    padding: 0;
}

html, body {
    overflow: hidden;
}

.webgl {
    position: fixed;
    top: 0;
    left: 0;
    outline: none;
}

canvas {
    display: block;
}

margin, padding0으로 설정하여 기본적으로 생기는 브라우저의 공백을 제거해줍니다. html, bodyoverflow: hidden을 적용하면 화면 조작 중 스크롤이 생기는 걸 방지할 수 있습니다. webgl 클래스에 position: fixed, top: 0, left: 0, outline: none을 적용하면 화면 전체를 꽉 채우는 형태로 렌더링 할 수 있으며, 마찬가지로 canvas display: block을 설정하면 기본적으로 생기는 줄바꿈 여백도 제거됩니다.

 

 

이렇게 공백 없는 Scene이 렌더링되는 것을 볼 수 있습니다.

 

 

- Resizing 대응(Javascript)

.css 설정을 마쳤다면, 이제 .js에서 창 크기 변화에 대응하여 3D Scene도 동적으로 반응하도록 만들어야 합니다. 아래는 일반적으로 많이 사용하는 방식으로 sizes 객체를 통해 브라우저의 크기를 감지하고, 카메라의 비율(aspect)만 조정해주는 코드입니다.

// 처음 브라우저 크기를 저장
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

// 브라우저 창 크기 변경 감지
window.addEventListener('resize', () =>
{
    // 1. 크기 갱신
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    // 2. 카메라 비율만 갱신
    camera.aspect = sizes.width / sizes.height
})

하지만 이 코드만으로는 리사이징이 완전히 적용되지 않습니다. 창 크기를 줄이면 하얀 여백이 생기거나, 화면 일부가 잘리는 현상, 혹은 큐브가 찌그러져 보이는 문제가 발생하죠.

 

 

- 문제가 발생하는 이유

위 코드는 단지 카메라의 비율(aspect) 값만 바꿨을 뿐, 투영 행렬(Projection Matrix) 이나 캔버스 크기(Renderer 크기) 는 갱신하지 않았기 때문입니다. 해결 방법은 간단합니다. camera 안에 있는 투영행렬 업데이트 함수를 호출하고 renderer를 리사이징하면 완료됩니다.

window.addEventListener('resize', () =>
{
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    // Update camera
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})

위 그림은 위아래로 리사이징해도 잘 따라오는 예시입니다.
(GIF 프레임 때문에 조금 어색해 보일수도 있습니다.

 

이제 브라우저 창을 위아래, 좌우로 줄이거나 늘려도 3D Scene이 자동으로 맞춰지면서, 화면이 찌그러지거나 하얀 여백이 생기지 않습니다!

 

 

 

- 픽셀 비율 처리

마지막으로 현재 콘솔에 window.devicePixelRatio를 선언한다면 기본적인 PC에서는 1이 나오고 레티나 디스플레이나 고해상도 화면에서는 2, 3 또는 더 큰 값이 나오죠. 

 

픽셀 비율이 높을수록 선명해지지만, 그 만큼 GPU 리소스를 더 많이 소모합니다. 그래서 보통 2로 제한하는 것이 성능과 화질 사이의 균형을 맞추는 좋은 선택입니다.

 

resize event는 디스플레이거 변경되거나 브라우저 확대/축소 등의 특수한 상황에서 호출되므로, 이 시점에 setPixelRatio를 설정해주는 것이 가장 적절합니다.  

window.addEventListener('resize', () =>
    {
        // Update sizes
        sizes.width = window.innerWidth
        sizes.height = window.innerHeight

        // Update camera
        camera.aspect = sizes.width / sizes.height
        camera.updateProjectionMatrix()

        // Update renderer
        renderer.setSize(sizes.width, sizes.height)
       
        // window.devicePixelRatio는 물리 픽셀과 CSS 픽셀의 비율입니다.
        // 일반적으로 1이지만, 고해상도 디스플레이에서는 2 또는 3일 수 있습니다.
        // 픽셀 비율이 너무 높으면 성능 저하가 발생하므로,
        // 최대값을 2로 제한하여 적절한 품질과 성능을 유지합니다.
        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
    })
 

 

 Math.min()을 설정해서 최대 픽셀 비율을 2로 고정하고, 너무 고해상도에서 프레임 드랍이 발생하지 않도록 방지합니다.

 

 

 

- Fullscreen

Fullscreen은 웹 또는 모바일 환경에서 더블 클릭이나 버튼 클릭을 통해 화면 전체를 채우는 기능입니다. 다음은 해당 기능을 구현하는 코드이며, 브라우저 호환성을 최대한 고려한 예시입니다.

window.addEventListener('dblclick', () => {
   
    const fullscreenElement = document.fullscreenElement ||
                                               document.webkitFullscreenElement ||
                                               document.mozFullScreenElement ||
                                               document.msFullscreenElement;
 
    if (!fullscreenElement) {
        if (canvas.requestFullscreen) {
            canvas.requestFullscreen();
        } else if (canvas.webkitRequestFullscreen) {
            canvas.webkitRequestFullscreen();
        } else if (canvas.mozRequestFullScreen) {
            canvas.mozRequestFullScreen();
        } else if (canvas.msRequestFullscreen) {
            canvas.msRequestFullscreen();
        }
    } else {  
        if (document.exitFullscreen) {
            document.exitFullscreen();
        } else if (document.webkitExitFullscreen) {
            document.webkitExitFullscreen();
        } else if (document.mozCancelFullScreen) {
            document.mozCancelFullScreen();
        } else if (document.msExitFullscreen) {
            document.msExitFullscreen();
        }
    }
})

기본적으로 canvas.requestFullscreen, canvas.exitFullscreen을 사용하면 되지만, 브라우저 마다 사용하는 API가 다르기 때문에 아래처럼 구분이 필요합니다 :

  • fullscreentElement: 표준 (크롬, 파폭, 엣지 최신)
  • webkitFullscreenElement: 사파리
  • mozFullScreentElement: Firefox(구버전)
  • msFullscreentElement: IE/Edge 구버전

이처럼 다양한 브라우저 기능을 모두 대응할 수 있도록 조건문을 구성한 것입니다.

 

 

 

- 최신 브라우저만 고려한다면?

최신 환경에서는 아래 코드만으로도 충분합니다:

canvas.requestFullscreen();

하지만 Safari, 일부 기업용 환경, 구형 브라우저까지 고려한다면 위에서 작성한 다양한 fallback이 포함된 코드를 사용하는 것이  더 안정적입니다.

 

 

 

결론

이번 작업을 통해 화면 크기 변경, 고해상도 디스플레이, 다양한 브라우저 환경에서도 문제 없이 동작하는 안정적인 3D Scene을 만들 수 있게 되었습니다. 기본적인 대응만으로도 사용자 경험이 크게 개선된다는 점을 확인할 수 있었습니다.

 

'Three.js > Intro' 카테고리의 다른 글

[threejs-journey 1-9] Debug UI  (0) 2025.06.22
[threejs-journey 1-8] Geometries  (0) 2025.06.16
[threejs-journey 1-6] Control  (0) 2025.06.10
[threejs-journey 1-6] Camera  (3) 2025.06.10
[threejs-journey 1-5] Animation  (0) 2025.06.03