프로젝트

[Decoupled Architecture] 프론트엔드 & 백엔드 서버 분리 환경에서의 데이터 전달

sian han 2022. 12. 6. 18:40

지금까지 만든 토이프로젝트들은 Monolithic architecture(모놀리식 아키텍쳐: 일체형 구조) 소프트웨어 설계구조를 가졌다.

모놀리식은 하나의 서버에 서비스 할 모든 애플리케이션 소스가 존재하는 구조이다.

 

▶ Monolithic architecture 동작구조

1. 클라이언트가 요청을 보내면

2. 서버에 존재하는 웹 어플리케이션이 데이터베이스 서버와 통신하여 필요한 데이터들을 전부 가져온다.

3. HTML, CSS, JavaScript 파일들을 만들어서 클라이언트에게 제공한다.

4. 클라이언트는 제공받은 파일을 브라우저에 전달한다. 

 

Monolithic 은 아키텍쳐가 단순하고, 프로덕트 와 DB 서버 2대만 운영하면 되어서 혼자 관리하기 용이했기 때문에

그동안 토이 프로젝트는 Spring 을 이용한 모놀리식 구조로 설계했었다.

 

 

 

이런 구조는 프로젝트가 커짐에 따라 다음과 같은 문제점이 발생한다.

 

  • 조그마한 수정사항이 있어도 전체를 다시 빌드하고 배포해야 함
  • 유지보수가 힘듦
  • 프로젝트 규모가 커질수록 구동 시간이 늘어남
  • 일부분의 오류가 전체에 영향을 미침
  • 각 기능별로 알맞은 언어나 프레임워크를 선택하기가 까다로움

 

규모가 큰 프로젝트를 진행 준비를 위해 Decoupled Architecture (분리된 아키텍쳐)를 공부해보자.

 


백엔드와 프론트엔드 서버를 분리한다 .. ?

 

가장 먼저 떠오르는 궁금증은

 

클라이언트는 서버에 어떤 식으로 데이터를 요청하고

서버는 클라이언트에 어떤식으로 데이터를 전달하는가 ? 

둘은 도대체 어떻게 통신을 한단말인가 ? 이다.

 

구글링을 해보면 React 로 구성한 프론트 환경에서는 서버와 통신을 하기 위해 fetch 또는 axios 를 사용한다고 한다. 

무슨 말인지 잘 모르겠다.

백엔드 서버와 프론트엔드 서버가 fetch, axios 을 이용해 어떻게 통신하는지 알아보기 위해

환경을 구축해 클라이언트에서 서버에 데이터를 요청해보자.

 

▶ 환경구축

1. express 서버 구성하기

프로젝트를 진행할 폴더(React-request-server) 안에 server, client 폴더를 만든다

npm init 
npm i express

server 에서 위 명령어를 사용해 package.json 만들고, express를 설치해준다.

 

npm 공식 사이트에 접속하고 express 를 검색해 sample code 를 복사한다.

server 폴더 내에 app.js 라는 파일을 만들어 붙여넣어준다. (서버 코드 짜기)

 

sample code 에 실행되었는지 확인할 수 있는 간단한 콜백함수를 추가한다.

const express = require('express')
const app = express()

app.get('/', function (req, res) {
  res.send('Hello World')
})

app.listen(3000, ()=> {
    console.log('Listening on port 3000');
});
node app.js //server 실행

 

 

2. 서버 API 테스트

2번 과정을 완전히 이해하기 위해서는 express api 에 관한 지식이 필요한데,

이번 포스팅의 주제인 [백엔드 서버와 프론트엔드 서버는 어떻게 통신하는가 ?]

깊게 연관된 것이 아니기 때문에 이해하지 못하고 넘어가도 된다.

(환경을 구축하기 위한 작업이다)

 

테스트를 하기 위한 데이터가 필요한데, DB 을 붙이지 않고 데이터를 메모리에 저장 해놓겠다.

const jjigae = [{
  id : 1,
  name : '김치찌개',
  description : '주의: 우리집 김치로 만들어야 맛있음',
}];

 

▷ get 요청을 하는 api 생성

app.get('/api/jjigae',(req, res)=>{
  res.json(jjigae); //json 형태로 jjigae 를 보내줌
})

 

 

▷ post 요청을 하는 api 생성

 - 프론트에서 서버에 데이터를 보낼 때 body 에 데이터를 넣어서 보낼것이다. 

 - express 에서 body 에서 데이터를 꺼내 쓰려면 body-parser 가 필요하다

app.use(express.json()) // for parsing application/json
app.use(express.urlencoded({ extended: true }))

( express 공식문서를 통해 위 두줄을 추가하면 body-parser 사용가능함을 알 수 있다 https://expressjs.com/en/4x/api.html )

 

 

api 작성

app.post('/api/jjigae',(req,res)=>{
  const {jjigaeName , description} = req.body; //body-parser를 통해 데이터를 꺼내옴
    jjigae.push({
      id : id ++,
      jjigaeName,
      description,
    });
    return res.send('success'); 
});

 

 

POSTMAN TEST

 

json 데이터를 POST 로 보내면

보낸 데이터가 잘 추가된 것을 확인할 수 있다.

 

여기까지 서버에서 데이터를 보내는 환경을 만들었다.

다음은 프론트에서 데이터를 받는 환경을 구축해보자.

 

 

3. 리액트 구성하기

client 폴더로 이동해서 react 설치

npx create-react-app .

 

App.js

function App() {
  return (
    <div className="App">
     <h1>찌개 리스트</h1>
    </div>
  );
}

export default App;
npm start //react 실행

서버를 실행하면 페이지가 잘 나오는 것을 확인할 수 있다.

드디어 환경 구축이 완료되고 클라이언트에서 서버에 데이터를 요청해볼 수 있게 되었다.

 

 

4. 클라이언트에서 데이터 요청하기

서버에 데이터를 요청하려면

  • 서버의 주소를 알아야 한다.
  • 어떤 HTTP METHOD 를 사용할지 알아야 한다.

앞서 리액트에서 서버에 데이터를 요청하는 방법은 fetch 와 axios 가 있다고 했는데, 차례대로 살펴보겠다.

 

 

▶ Fetch

자바스트립트에 내장된 API (ES6 이상)

fetch(url, [옵션]);

url - 접근하고자 하는 URL

options - method 나 header 등을 지정할 수 있다. 

 

 

▷ fetch 기본 구조

fetch(url, {
	method: 'POST',
    headers : {
    	'Content-Type' : 'application/json'
    },
    body: JSON.stringify(data)
});
	.then((response)=> response.json())
    .chatch((error)=>console.log(error))

 

 

▷ fetch 사용하기

 

프론트에서 서버에 데이터를 요청하는 코드 작성

function App() {
    fetch('http://localhost:4000/api/jjigae')
    .then((response) => response.json())
    .then((data)=>console.log(data));

    return(
    <div className="App">
     <h1>찌개 리스트</h1> 
    </div>
  );
}

export default App;

3000번 포트로 리액트를 실행시키고

4000번 포트로 노드를 실행시킨다. 

코드를 작성하고 서버에 데이터 요청을 했더니 CORS 라는 에러가 발생한다.

 

 

CORS ERROR : Access to fetch at 'http://localhost:4000/api/jjigae' from origin 'http://localhost:3000' has been blocked by CORS policy: 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 란 ?

Cross Origin Resource Sharing

 

클라이언트 : localhost:3000 리액트 개발서버

서버 : localhost:4000

 

둘은 PORT 가 다르기 때문에 데이터의 출처(Origin)가 다르다. 

네이버에서 구글의 데이터를 빼가도 되는가 ? 안된다.

그래서 ' 오리진이 다르면 리소스를 꺼내갈 수 없다 ' 라는 정책(SOP)이 있고, CORS 에러를 해결하기 위해서는 서버에서 허용을 해줘야한다. 

 

CORS 에 대한 자세한 내용은 아래 포스트로 정리했다.

https://feelfreetothink.tistory.com/165

 

[Decoupled Architecture] CORS

※ CORS (Cross-Origin Resource Sharing) 추가 HTTP 헤더를 사용하여 한 출처에서 실행중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제 ▶

feelfreetothink.tistory.com

 

CORS 모듈울 사용해 좀더 간편하면서 유기적으로 출처를 필터링 할수 있다.

 

server에 CORS 모듈설치

$ npm install cors

 

const cors = require('cors'); //공식문서에는 var 라고 되어있는데 const 로 바꿔주자
app.use(cors())

 

다시 요청을 하면 CORS 에러가 안뜨고

서버에서 요청한 데이터를 전달해준 것을 확인할 수 있다. 

 

 

▶ axios

 

axios 설치

npm i axios

axios로 서버에서 데이터 요청하는 코드 작성

import axios from 'axios';

axios.get('http://localhost:4000/api/jjigae')
.then((response) = setJjigae(response.data);

fetch 로 작성한 코드와 동일하게 서버에서 데이터를 받아온다.

axios 는 fetch 보다 훨씬 간단하게 작성할 수 있고, async await 문법을 사용하면 더 직관적으로 작성이 가능하다.

(axios 는 다른 포스팅에서 자세히 다뤄보도록 하겠다)

 

 

 

서버가 분리된 환경에서 서버와 클라이언트는 어떻게 통신하는가 ? 

에 대해 알아보기 위해 분리된 서버 환경을 구축해서

클라이언트에서 요청한 데이터를 서버에서 보내줘봤다.

 

 

 

 

 

 

https://it-eldorado.tistory.com/85

https://www.indicative.com/resource/decoupled-architecture/