본문 바로가기
Spring boot 프로젝트 기록/2. 프론트엔드 개발

서비스통합(백엔드와 프론트엔드 통합하기)

by 으노으뇨 2021. 12. 1.
728x90
반응형
SMALL

서비스 통합

  • 크로스오리진 리소스 셰어링
  • 스프링 @Configuration 을 이용한 CORS 문제해결
  • fetch를 이용한 프론트엔드와 백엔드 통합

현재 저는 독립적으로 동작하는 백엔드 애플리케이션과 독립적으로 동작하는 프론트엔드 어플이 하나씩있습니다!

이제는 두 어플리케이션을 통합해 하나의 기능을 하는 웹 애플리케이션을 완성할 차례입니다!

이제는 프론트엔드에서 백엔드에 API를 요청하는 코드를 작성해보겠습니다

우리는 이제 JS의 fetch API를 이용해서 Todo 아이템 CRUD를 하게 할게요!

하지만 이때 CORS문제에 만닥뜨리게 된다. 이 문제는 보안과 관련되어 있어 백엔드에서 해결해 줘야합니다.

우리가 개발하는 애플리케이션을 통해 CORS가 무엇이고 이를 어떻게 해주어야할까요!?

App컴포넌트에 componentDidMount 추가

App.js에

componentDidMount(){
  const requestOptions = {
    method : "GET",
    headers : { "Content-Type" : "application/json"},
  };
  fetch("http://localhost:8080/todo",requestOptions)
  .then((response)=>response.json())
  .then(
    (response)=>{
      this.setState({
        items: response.date,
      });
    },
    (error)=>{
      this.setState({
        error,
      });
    }
  );
}

를 추가해 주었다.

그리고 localhost3000으로 들어가 개발자 툴의 콘솔창을 켜보면 에러를 확인할 수 있습니다!

Access to fetch at 'http://localhost:8080/todo' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

이는 보안을 위한 CORS 헤더 Policy를 위반했기 때문이다.


CORS

CORS는 크로스 오리진 리소스 셰어링 의 약자로, 처음 리소스를 제공한 도메인이 현재 요청하려는

도메인과 다르더라도 요청을 허락해 주는 웹 보안 방침입니다. 나중에 추후 설명드리겠습니다!

백엔드단에 파일을 추가해주어야 합니다!

CORS설정을 위해 WebMvcConfig를 만들어봅시다

패키지를 config로, 이름을 WebMvcConfig로 지어줍니다!

package com.unoSpringBoot.study.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;

	@Override
	public void addCorsMappings(CorsRegistry registry) {
		// 모든 경로에 대해
		registry.addMapping("/**")
				// Origin이 http:localhost:3000에 대해
				.allowedOrigins("http://localhost:3000")
				// 겟,포스트,풋 등등 메서드를 허용한다.
				.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS").allowedHeaders("*")
				.allowCredentials(true).maxAge(MAX_AGE_SECS);
	}
}

해당 메서드에서 작성한 설정은

모든 경로에 대해 오리진이 해당 로컬호스트주소인경우 

겟, 포스트 등등 메서드를 이용한 요청을 허용한다.! 또 모든 헤더와 인증에 관한 정보도 허용한다

라는 의미입니다

지금과 같은 코드를 넣어주고 시작해보았다.

스프링부트 서버를 시작해주고

로컬호스트 3000 을 새로고침을 해주었더니 갑자기 에러가 발생한다????

하지만!!! 스프링부트 콘솔창에는 인식을 하고있다... 휴... 

프론트엔드 쪽에서 이런 연결을 위한 js파일을 만들어주자!

js폴더안에 api-config라고 생성했다.

let backendHost;
const hostName = window && window.location && window.location.hostName;
if(hostName ==="localhost"){
    backendHost = "http://localhost:8080";
}


export const API_BASE_URL =`${backendHost}`;

ㅇ백엔드로 요청을 보낼때 사용할 유틸리티 함수들을 작성할 js파일도 만들어야합니당! 

service기능을할테니 js폴더안에 service라고 폴더를 새로 생성한다음

ApiService.js라고 만든뒤 내용을 채워넣어줍시다~!!

import { API_BASE_URL } from "../api-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;
    })
  );
}

이제 App.js 기존 함수들을 ApiService의 메서드를 이용해 수정해보겠습니다!!!

  componentDidMount() {
    call("/todo", "GET", null).then((response) =>
      this.setState({ items: response.data })
    );
  }

  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 })
    );
  };

기존의 add, delete, update를 모두 같게하고 call안의 매개변수값만 바꿔주었다!

그리고 조금 수정했다. "/todo"를 전역변수 var todoURL로 관리하여 코드수정이 용이하게 하였다.

이제 정상적으로 웹단에서 돌아가는지 확인해보자!

첫번째로 안녕하세요를 입력하고

자기소개를 했습니다. 과연 새로고침을 하면 사라질까요...?!

그대롭니다!!

백엔드의 데이터베이스에서 Todo리스트를 가져오므로 새로고침을 해도 사라지지 않습니다!

또 프론트엔드를 완전 종료했다가 다시켜도 사리지지않습니다.

매끄럽게 수정시키기

수정을 매끄럽게 해주기 위해서 기존코드를 수정해야합니다.

  update = (item) => {
    call(todoURL, "PUT", item).then((response) =>
      this.setState({ items: response.data })
    );
  };

는 아까 추가해주었지만 다시 한번 말씀을 드리겠습니다. 해당  update를 했다면, 새로 리스트화가 되어주어야 하기때문에

App.js의 렌더 부분의 Todo에 props를 연결해주어야합니다.

  render() {
    var todoItems = this.state.items.length > 0 && (
      <Paper style={{ margin: 16 }}>
        <List>
          {this.state.items.map((item, idx) => (
            <TodoList
              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>
    );
  }
}

연결해줍니다. 

그리고 todo.js의 컨스트럭트 부분에

    constructor(props) {
        super(props);
        this.state = { item: props.item,readOnly : true};
        this.delete = props.delete;
        this.update = props.update; //update를 this.update에 할당
      }

를 추가해줍니다.

그리고 대망의 이벤트핸들러에게 저 update를 넣어줍니다.

      checkboxEventHandler = (e) => {
        const thisItem = this.state.item;
         thisItem.done = !thisItem.done;
         this.setState({ item: thisItem });
         this.update(this.state.item); //체크박스가 변경되면 저장
        };
      enterKeyEventHandler = (e) => {
        if (e.key === "Enter") {
          this.setState({readOnly: true});
          this.update(this.state.item); //엔터누르면 저장
        }
      };

그럼 작동을 잘하는지 확인해봅시다!

수정전인상태입니다~!

이렇게 요청이 성공했다면 성공적으로 업데이트가 된것입니다! 이렇게 수정까지 해 서 로컬환경에서 동작하는 완전한 Todo 애플리케이션을 완성했습니다!!!

728x90
반응형
LIST

'Spring boot 프로젝트 기록 > 2. 프론트엔드 개발' 카테고리의 다른 글

로그인 페이지  (0) 2021.12.15
로그인 컴포넌트  (0) 2021.12.14
Todo 수정  (0) 2021.12.01
Todo 기능(추가, 삭제)  (0) 2021.12.01
리액트로 간단한 컴포넌트 만들어보기  (0) 2021.11.30

댓글