본 포스팅은 'React.js, 스프링 부트, AWS로 배우는 웹 개발 101 - 김다정'님의 책을 보고 작성되었습니다.
목차
1. componentDidMount( )
2. CORS
3. fetch
componentDidMount
렌더링이란?
- 컴포넌트의 상태가 변하면 ReactDOM은 이를 감지하고 변경 부분의 HTML을 바꿔준다.
- HTML이 업데이트되면 우리는 변경된 결과를 눈으로 확인할 수 있다. 이를 렌더링이라고 한다.
- 즉 변경된 부분을 우리가 확인하는 것을 '렌더링'이라고 생각하면 된다.
마운팅이란?
render() 함수를 불러 자신의 DOM 트리를 구성하는 과정을 마운팅이라고 한다.
componentDidMount()는?
- 마운팅 과정에서 생성자, render() 함수를 부른다. 마운팅을 마친 후 그 뒤 componentDidMount 함수를 부른다.
- 생성자에서 API를 콜하게 되면 프로퍼티가 준비되지 않은 상태에서 API를 불러내기 때문에 문제가 생겨 마운팅이 끝난 후 componentDidMount 함수를 통해서 API를 콜해준다.
- 즉 마운팅 후 API를 콜할 때 사용하는 함수가 componentDidMount 함수인 것이다.
- 이때 CORS 문제를 해결하지 않고 componentDidMount()를 사용하면 CORS 에러가 뜨게 된다. 아래를 통해 CORS 에러를 해결해보자.
2. CORS
교차 출처 리소스 공유, 교차 출처 자원 공유는 웹 페이지 상의 제한된 리소스를 최초 자원이 서비스된 도메인 밖의 다른 도메인으로부터 요청할 수 있게 허용하는 구조이다.
📌출처: https://ko.wikipedia.org/wiki/%EA%B5%90%EC%B0%A8_%EC%B6%9C%EC%B2%98_%EB%A6%AC%EC%86%8C%EC%8A%A4_%EA%B3%B5%EC%9C%A0
- 간단하게 현재 본인은 프론트 관련 작업을 실행할 때 도메인을 localhost:3000을 사용하고 백엔드 관련 도메인은 localhost:8080을 사용해서 실행을 시켜주게 된다.
- 이때 처음 리소스를 제공한 도메인이 현재 요청하려는 도메인과 다르더라도 요청을 허락해주는 웹 보안 방침이 CORS이다.
CORS 설정
CORS는 서로 다른 도메인을 쓰더라도 요청을 허락하게 해주는 방침이기 때문에 이를 가능하게 하려면 백엔드에서 설정을 해주어야 한다.
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer{
private final long MAX_AGE_SECS = 3600; //6
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")//1
.allowedOrigins("http://localhost:3000")//2
.allowedMethods("GET","POST","PUT","PATCH","DELETE","OPTIONS")//3
.allowedHeaders("*")//4
.allowCredentials(true)//5
.maxAge(MAX_AGE_SECS);//6
}
}
- 모든 경로에 대해서
- origin이 http://localhost:3000인 경우
- "GET","POST","PUT","PATCH","DELETE","OPTIONS" 메서드를 이용한 요청을 허용한다.
- 모든 헤더와
- 인증에 관한 정보도 허용한다. (그러나 이는 보안상 이슈가 있다고 한다. 원한다면 더 찾아볼 것)
- 하드코딩 해놓은 상수를 넣어 최대값을 설정해준다.
3. fetch
자바스크립트는 fetch를 이용해 백엔드 애플리케이션에 HTTP 요청을 보낸다. 그렇다면 fetch는 무엇이고 어떻게 사용하는지 알아보고 HTTP 생성, 검색, 수정, 삭제 요청을 백엔드에 주고받는 로직을 구현해보도록 하겠다.
Promise
- fetch 메서드는 Promise를 리턴한다. Promise는 비동기 오퍼레이션에서 사용한다.
- 자바스크립트는 싱글 스레드 환경에서 동작하는 프로그램으로 HTTP가 요청을 백엔드에 보냈을 때 백엔드가 이를 1시간 넘게 처리한다 할 때 브라우저는 1시간 동안 아무것도 못하는 상태가 된다는 의미이다.
- 이런 문제를 해결하기 위해서 자바스크립트 엔진은 자바스크립트 스레드 밖에서 이런 오퍼레이션을 실행해준다.
- Promise는 콜백 지옥을 피할 수 있게 해주는 하나의 해결법이다.
Promise의 세 가지 상태
- Pending
- 오퍼레이션이 끝나길 기다리는 상태
- Resolve
- 오퍼레이션이 성공적으로 끝나면 resolve( ) 함수를 통해 이 오퍼레이션이 성공적으로 끝났음을 알려준 뒤, 원하는 값을 전달할 수 있다.
- Rejecte
- 오퍼레이션 실행 중 에러가 나는 경우 reject( ) 함수를 콜한다.
- 결과로 catch 매개변수로 넘어오는 함수가 실행된다.
Fetch API
- 자바스크립트가 제공하는 메서드로 API 서버로 http 요청을 송신, 수신할 수 있도록 돕는다.
- fetch( )는 Promise 객체를 리턴한다. 따라서 then과 catch에 콜백 함수를 전달해 응답을 처리할 수 있게 한다.
- fetch는 url을 매개변수로 받거나 url + options(http method, headers 같은 경우..)를 매개변수로 받을 수 있다.
app - config
let backendHost;
const hostname = window && window.location && window.location.hostname;
if(hostname === "localhost"){
backendHost = "http://localhost:8080";
}
export const API_BASE_URL = `${backendHost}`;
- 경로를 하드코딩하기 보다는 설정 파일을 생성해 도메인이 바뀔 때도 유연하게 변경할 수 있도록 작성해주는 것이 좋다.
ApiService.js
import { API_BASE_URL } from "../app-config/app-config";
export function call(api, method, request) {
let options = {
headers: new Headers({
"Content-Type": "application/json",
}),
url: API_BASE_URL + api,
method: method,
};
if (request) {
// GET method
options.body = JSON.stringify(request);
}
return fetch(options.url, options).then((response) =>
response.json().then((json) => {
if (!response.ok) {
// response.ok가 true이면 정상적인 리스폰스를 받은것, 아니면 에러 리스폰스를 받은것.
return Promise.reject(json);
}
return json;
})
);
}
- call 함수를 작성해 백엔드 요청을 보낼 때 사용할 유틸리티 함수를 작성해준다.
App 컴포넌트에서 만들어 놓은 유틸리티 함수 사용
import React from 'react';
import Todo from './Todo';
import {Paper, List, Container} from '@material-ui/core';
import "./App.css";
import AddTodo from './AddTodo';
import {call} from './service/ApiService';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items:[],
};
}
componentDidMount() {
call("/todo","GET", null).then((response) =>
this.setState({items:response.data})
);
}
// add() 함수 추가
add = (item) => {
call("/todo", "POST", item).then((response) =>
this.setState({items:response.data})
);
};
delete = (item) => {
call("/todo", "DELETE", item).then((response) =>
this.setState({items:response.data})
);
}
update = (item) => {
call("/todo", "PUT", item).then((response) =>
this.setState({items:response.data})
);
};
render() {
var todoItems = this.state.items.length > 0 && (
<Paper style={{margin:16}}>
<List>
{this.state.items.map((item,idx)=>(
<Todo
item ={item}
key ={item.id}
delete = {this.delete}
update = {this.update} />
))}
</List>
</Paper>
);
//함수 연결
return (
<div className='App'>
<Container maxWidth="md">
<AddTodo add={this.add} />
<div className='TodoList'>{todoItems}</div>
</Container>
</div>
);
}
}
export default App;
componentDidMount() {
call("/todo","GET", null).then((response) =>
this.setState({items:response.data})
);
}
// add() 함수 추가
add = (item) => {
call("/todo", "POST", item).then((response) =>
this.setState({items:response.data})
);
};
delete = (item) => {
call("/todo", "DELETE", item).then((response) =>
this.setState({items:response.data})
);
}
update = (item) => {
call("/todo", "PUT", item).then((response) =>
this.setState({items:response.data})
);
};
간단 정리쇼
- 로컬 환경에서 백엔드 환경과 프론트엔드 환경을 연결할 때 CORS를 위반하지 않기 위해서 백엔드 내에서 환경설정 하는 방법을 알아 보았다.
- Promis와 fetch의 관계 그리고 사용법을 알아보았다.
- 유틸리티 함수로 만들어서 반복되는 코드 중복을 제거하여 사용하는 방법까지 알아보았다.
'Back-End > React.js, 스프링 부트, AWS로 배우는 웹 개발 101' 카테고리의 다른 글
[React.js, 스프링 부트, AWS로 배우는 웹 개발 101][인증 백엔드 통합] - User Layer 구현 (0) | 2022.06.22 |
---|---|
[React.js, 스프링 부트, AWS로 배우는 웹 개발 101][인증 백엔드 통합] - REST API 인증 기법 (0) | 2022.06.21 |
[React.js, 스프링 부트, AWS로 배우는 웹 개발 101][프론트엔트 개발] - 서비스 개발2 (0) | 2022.06.16 |
[React.js, 스프링 부트, AWS로 배우는 웹 개발 101][프론트엔트 개발] - 서비스 개발 (0) | 2022.06.14 |
[React.js, 스프링 부트, AWS로 배우는 웹 개발 101][프론트엔트 개발] - React.js(브라우저 동작방식 2) (0) | 2022.06.14 |