프론트 엔드의 배포 과정 또한 이전 글에서 메모리 스와핑까지의 과정을 거치면 된다.
이 후의 과정에서, 몇개의 수정과정을 거쳤다.
가장 중요한 과정은 CORS 에러를 해결하는 방법이다.
우리는 이전에 이것을 리액트의 Webpack Dev Server
라이브러리의 프록시 서버를 이용하여 해결했다.
package.json
{
...
"proxy": "http://localhost:4000"
}
그러나 Webpack Dev Server
는 개발과정에서만 운용이 되고, 배포과정에서는 실행이 어렵다. 따라서 이러한 CORS 에러의 해결을 백엔드 과정에서 해결하도록 한다.
컨트롤러에서 CrossOrigin
어노테이션을 이용할 수도 있지만, 컨트롤러의 수가 하나가 아니므로, 모두에게 적용될 수 있도록 config
파일에서 CORS 정책을 설정해보자.
우리는 Spring Security를 사용했으므로, 우리의 config
파일은 WebSecurityConfig
다.
/config/WebSecurityConfig.java
@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
@Component
public class WebSecurityConfig {
...
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors().configurationSource(corsConfigurationSource())
.and()
...
...
.and()
.authorizeRequests()
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
.antMatchers(...
...
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://localhost:3000"); // 로컬
config.addAllowedOrigin("http://프론트 AWS 주소"); // 프론트 IPv4 주소
config.addAllowedMethod("*"); // 모든 메소드 허용.
config.addAllowedHeader("*");
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}
기본적인 구조는 corsConfigurationSource
메서드를 통해 설정을 거친 CorsConfigurationSource
를 반환하여 filterChain
메서드의 HttpSecurity
값에 적용시키는 것이다.
이후 requestMatchers
부분은 CORS preflight
요청은 인증처리를 하지 않겠다는 뜻인데, 이 부분은 복잡하니 나중에 설명을 하겠다. 간단히 설명하자면, CORS
요청 전송 이전에 서버에서 어떤 origin과 method를 허용하는지 알려주는 요청인데, 이는 401 응답을 하지 않겠다는 의미로 보면 된다. 이 과정이 있어야 CORS
요청이 정상적으로 이루어진다.
먼저 CorsConfiguration
객체를 만들고, 허가해 줄 Origin (출처, protocol + host + port)를 추가해준다.
우리는 여기서 기본적으로 테스트 해볼 http://localhost:3000
과 앞서 발급한 프론트의 인스턴스 주소를 넣어준다.
또한 모든 HTTP 메소드도 허용해주고, 모든 헤더도 허용해준다.
이후 우리는 쿠키를 통한 인증과정을 거치므로 Credentials도 허가해준다. 이후 새로운 source 객체를 만든 다음, config
객체를 적용해준 다음 반환하면 된다.
이후 변경한 Spring 프로젝트를 백엔드 인스턴스에서 git pull을 통해 업데이트 한 다음 재빌드하거나
혹은 vi 명령어를 통해 수정한 다음 재빌드하면 된다.
프론트엔드에선 일단 CORS 문제를 백엔드에서 해결했으므로, package.json
의 proyx
부분을 지워준다.
이후 백엔드의 주소를 프론트 앱 전체에 적용해주기 위해 따로 유틸리티 파일을 만들었다.
/utility/uri.ts
export const URI = "http://백엔드 주소";
이후 이를 전체 통신을 담당하는 fetch-action.ts
에 적용시켜준다,
/store/fetch-action.ts
import { URI } from '../utility/uri';
...
const uri = URI;
const fetchAuth = async (fetchData: FetchData) => {
...
try {
const response:AxiosResponse<any, any> | false =
(method === 'get' && (await axios.get(uri + url, header))) ||
(method === 'post' && (await axios.post(uri + url, data, header))) ||
(method === 'put' && (await axios.put(uri + url, data, header))) ||
(method === 'delete' && (await axios.delete(uri + url, header))
);
...
}
이전 과정과 메모리스와핑 까지는 똑같이 따라와도 되나, 포트는 톰캣을 사용하는 백엔드서버와 달리 nginx
를 이용할 것이므로 다르게 설정한다.
기존 22포트 이외에 80포트만 열어주면 된다.
리액트 앱은 톰캣이 존재하는 스프링과 달리 스스로 배포하기가 힘들기 때문에 nginx
를 통해 배포를 진행할 것이다.
$ sudo apt install nginx
nginx
를 설치해준다.
$ sudo apt-get install systemd
이후 ubuntu에서 BSD init
대신 프로세스를 관리하는 init 시스템인 systemd
를 설치해준다.
$ sudo apt install npm
노드 패키지 매니저를 설치한다.
$ git clone https://github.com/[리액트 저장소]
리액트 프로젝트를 가져오자. 이후 해당 프로젝트로 이동한다
$ npm run build
빌드를 시작한다.
이런 화면이 나왔다면 빌드가 성공적으로 진행된 것이다.
이제 nginx
를 빌드한 리액트 앱과 연결해주는 과정을 거치자.
nginx
의 설정을 건드리려면 nginx.conf
라는 파일을 수정하면 된다. 수정해보자
$ sudo vi /etc/nginx/nginx.conf
여기서 아예 작성해서 nginx와 리액트를 연결할 수도 있지만, 좀 더 깔끔하고 자주 이용하는 방식을 사용하자.
여기서 위처럼
include /etc/nginx/sites-enabled/*.conf;
문장을 추가하면 된다. 이렇게 되면, /etc/nginx/sites-enabled
이라는 디렉토리에 있는 conf
파일을 적용한다는 뜻이 된다.
만약
server {
listen 80 default_server
...
}
와 같은 부분이 활성화 되어있다면, 이부분을 전부 주석처리 해야한다. 필자가 설치한 1.18.0 버전에는 이 부분이 존재하지 않았다.
$ sudo mkdir /etc/nginx/sites-available
$ sudo mkdir /etc/nginx/sites-enabled
앞서 말한 /etc/nginx/sites-enabled
디렉토리는 존재하지 않으므로 생성한다. 그리고 또 /etc/nginx/sites-available
또한 생성할 것이다.
그렇다면 이 디렉토리는 왜 생성하는가?
/etc/nginx/sites-available
에서 만든 conf
파일을 Symbolic link해서 /etc/nginx/sites-available
에 생성하는 방식으로 만들 것이기 때문이다.
여기서 Symboilic link
는 윈도우의 바로가기
와 유사하다고 보면 된다.
그렇다면 왜 이런 방식을 쓰는가? 만약 해당 인스턴스의 Nginx
에서 이 리액트 서비스 말고 더 많은 서비스를 운영하고 있다고 가정해보자.
만약 /etc/nginx/sites-enabled
에만 파일을 작성했다면, 이런 모양일 것이다. 그런데 여기서, 특정 서비스 몇개를 잠시만 종료하고 싶다고 가정하자. 그러면 여기의 conf
파일을 지우면 되는데, 그러면 따로 백업을 하지 않는 이상 conf
파일은 영영 없어지게 된다.
다음은 우리가 하는 Symbolic link
의 방식이다. 이 경우에서 몇몇 서버를 종료하고 싶으면 어떻게 하면 되는가?
이렇게 단순히 /etc/nginx/sites-enabled
에서 Symbolic link
만 삭제 하면 종료가 되고, 원본 설정 파일도 존재한다. 다시 서버를 시작하고 싶으면 Symbolic link
만 설정한다면 다시 실행할 수 있는 것이다.
이제 작성해서 연결해보자.
$ sudo vi /etc/nginx/sites-available/[원하는 이름].conf
server {
listen 80;
location / {
root /home/user/[리액트 프로젝트 이름]/build;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
이렇게 작성하면 된다.
해당 내용을 하나하나 보면
index.html
을 리턴하도록 설정한 것이다. 이제 심볼릭 링크를 설정하자.
$ sudo ln -s /etc/nginx/sites-available/[원하는 이름].conf /etc/nginx/sites-enabled/[원하는 이름].conf
이렇게 되면 /etc/nginx/sites-available/
의 conf
파일을 참조한 심볼릭 링크 파일이 생성된다.
설정파일이 제대로 되었는지 체크해보자
$ sudo nginx -t
제대로 설정되었다.
$ sudo systemctl start nginx
nginx를 실행해보자
그러면 이제 프론트엔드 인스턴스의 IP 주소로 접속해보자.
정상적으로 실행이 된다!
참고로 nginx를 멈추는 명령어는 다음과 같다
$ sudo systemctl stop nginx