[Spring]React + SpringBoot 새로고침 오류

Inung_92·2023년 10월 10일
1

Spring

목록 보기
13/15
post-thumbnail

포스팅 목적

📖gradle을 통해 React와 SpringBoot를 빌드한 경우 발생하는 WhiteLabel Error를 해결하기 위한 원인과 방법을 알아보자.

오류 발생 상황

React Route

...생략
return (
	<>
  		<BrowserRouter>
  			<Routes>
  				<Route path='/login' element={<Login/>}/>
				<Route path='/*' element={<Home/>}/>
  			</Routes>
  		</BrowserRouter>
	</>
)
...

위와 같이 Route를 이용해 URL을 통한 컴포넌트 이동을 수행하였다. 개발환경에서는 React와 Springboot가 각자의 포트에서 실행되기 때문에 새로고침 및 뒤로가기를 눌러도 오류가 발생하지 않고, 정상적으로 작동되는 것을 볼 수 있었다.

문제는 gradle을 통해 React와 SpringBoot를 통합 빌드하면서 발생하게된다. 예를 들어 /login URL을 접속한 페이지에서 새로고침 또는 뒤로가기를 수행하면 위에서 본 사진처럼 Whitelabel Error Page가 보이는 것이다.

왜 이런 이유가 발생하는지에 대해서 원인부터 알아보자.

오류 발생 원인

우선적으로 개발 시 React는 3000번 포트에서 동작하고, SpringBoot는 8080번 포트에서 동작한다. 그렇기 때문에 개발 단계에서 CORS 에러를 피하기 위해서 Proxy 설정을 이용해서 개발을 수행하게된다. 나도 마찬가지로 아래와 같이 setupProxy.js를 설정하여 개발을 수행했다.

//setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');

const springURL = process.env.REACT_APP_SPRING_URL;

module.exports = function(app) {
    app.use(
        '/api',
        createProxyMiddleware({
            target: springURL,
            changeOrigin: true,
        })
    );

여기서 내가 잘못알고 있던 부분이 있다. Proxy는 개발 단계에서만 적용되며, 배포를 통해 운영 단계로 넘어가게되면 빌드되는 시점부터 Proxy 설정은 효력이 없다는 것이다. 또한, gradle에 의해서 React의 포트도 8080번 포트 내에서 작동하기 때문에 내가 기대하는 효과를 달성 할 수 없었다.

whitelabel error가 발생하는 이유는 기존에 localhost:3000/login으로 매핑된 Router의 페이지가 SpringBoot 프로젝트에서는 localhost:8080/login으로 받아들이기 때문에 해당 페이지를 찾을 수 없어서 발생하는 것이다.

React의 Router 관점에서 정리해보겠다.

  • 내가 생성한 React 프로젝트는 SPA(Single Page Application)이다.
  • SPA는 index.html 등의 하나의 페이지내에서 논리적으로 동작한다.
  • SPA는 HTTP 요청없이 URL을 통해 특정 경로의 컴포넌트 업데이트가 가능하다.
  • 이러한 것이 가능토록 도와주는 것이 Router의 기능이다.
  • 하지만 Router는 클라이언트 측의 라우팅만을 처리한다.
  • 즉, 서버에 대한 요청이 발생하면 해당 URL을 처리하는 엔드포인트가 없기 때문에 404에러가 발생한다.

그렇다면 React에서 Router를 사용하면서 해결 할 수 있는 방법에 대해서 알아보자.

해결 방법

React에서 해결

React에서 해결하는 방법은 간단하다. 내가 사용한 react-router의 경우 5가지 정도의 Router를 지원한다. 내가 프로젝트에서 사용한 것은 BrowserRouter였고, 해결은 HashRouter를 이용했다. 잠시 두가지 Router에 대한 설명을 보도록하자.

BrowserRouter

HTML5의 HistoryAPI를 사용하여 라우팅 처리를 지원하며 URL 경로를 읽을 때 실제 경로를 읽어온다. 또한, URL에 '#'이 표시되지 않음.

장점으로는 해시를 사용하지 않기 때문에 URL이 읽기 쉬우며, HistoryAPI를 사용한 브라우저의 뒤로 가기, 앞으로 가기 등의 기능이 활용 가능하다.

단점으로는 React 애플리케이션의 모든 경로에 대한 적절한 응답을 제공해야 한다. 이를 위한 추가적인 서버 설정이 필요하다.

HashRouter

URL의 해시 부분('#')을 사용하여 라우팅을 처리하며 해시를 이용하며 페이지의 변화를 감지하고 해당하는 컴포넌트를 렌더링한다.

장점으로는 React 애플리케이션을 호스팅하는데 용이하며 특별히 서버에서 설정이 필요없으며 모든 경로에서 애플리케이션이 동작한다.

단점으로는 URL구조가 다소 복잡해서 덜 자연스럽게 느껴질 수 있다. 또한 검색에 최적화되지 않아 검색 성능이 저하된다.

결론적으로 웹 서버의 구성이 가능하며 URL이 자연스럽게 구성되어야 하는 경우에는 BrowserRouter를 사용하고, 단독으로 호스팅을 사용하거나 서버의 설정이 불필요한 경우에는 HashRouter를 사용하면된다.

코드

// 기존
import BrowserRouter as Router from 'react-router-dom';

// 변경
import HashRouter as Router from 'react-router-dom';

SpringBoot에서 해결

SpringBoot에서 해결하는 것은 위에서 설명한 BrowserRouter를 사용하는 경우이다. React를 빌드하게되면 Springboot에는 static 디렉토리에 index.html만 생성된다. 이러한 부분을 고려하여 모든 URL 요청을 index.html로 포워딩해주는 설정을 하면된다.

// WebController
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class WebController implements ErrorController {
    @GetMapping({"/", "/error"})
    public String index() {
        return "index.html";
    }
}

모든 경로에 대해서 index.html을 반환해주는 Controller를 작성하면 된다.


마무리

React와 Srpingboot를 연동해서 개발하는 것은 어느정도 경험은 했지만 실제로 배포 환경에서 빌드된 프로젝트를 구동시켜본 것은 처음이었다. 이런 기회를 통해서 개발을 할 때와 빌드된 프로젝트를 배포 할 때는 다르다는 것을 충분히 인지하였다.

처음에는 왜 새로고침을 했는데 오류가 발생하는지 도저히 알지 못했다. 하지만 생각을 하다보니 Springboot에는 당연히 내가 호출한 URL과 매핑된 view가 없기 때문에 페이지를 반환 할 수 없다는 것을 깨닫고 해결책을 찾기 시작했다.

아직도 많이 서툴지만 이런 부분들을 계속 경험하고 원인과 해결방안을 찾아서 정리하다보면 나의 개발 경험치로 축적 될 것이라 생각한다.

그럼 이만.👊🏽

profile
서핑하는 개발자🏄🏽

0개의 댓글