* 이 포스트는 학습 과정에서 그 내용을 기록한 글이기에 부정확한 정보가 포함될 수 있습니다.
따라서 해당 글은 참고용으로만 봐주시고 틀린 부분이 있다면 알려주시면 감사하겠습니다.
🤚 이 포스트를 보시는 분이 입문자시라면 다음과 같은 선행 지식을 갖추셔야 이해하기 편하실겁니다. 과정마다 필요 지식을 태그해드릴테니 필요하시면 먼저 학습하시길 추천합니다. 괄호는 모르셔도 큰 문제는 없습니다.
- 스프링 부트 : Controller 및 Service 활용(RestController 포함), @ResponseBody, (Dto)
- 리액트 : state 기초, axios 라이브러리, (useEffect hook)
이어서 통신 과정을 진행할텐데 제가 중간에 리액트 문법을 헷갈려서 프로젝트를 진행하며 리액트도 같이 공부하는 중입니다. 한마디로 리액트는 정말 기본만 아시면 글 보시는데 큰 문제는 없을거라는 겁니다.
이번 포스트는 앞서 소개했던 전체 연동과정 중 2번을 마저 다 진행합니다.
1 스프링 부트 폴더와 리액트 폴더를 한 디렉토리에 패키징
2 서버인 스프링부트(default로 8080 포트 사용)와 클라이언트인 리액트(default로 3000 포트 사용)가 통신이 가능하도록 설정
3 최종적으로 8080포트만 이용하도록 jar 빌드
# 클라이언트-서버 API 통신(서버 > 클라이언트)
* 필요 선행지식 : Controller 활용(restController 포함), axios 라이브러리, state 기초, useEffect hook
@ axios 설치
이제 포트가 일치하니 서로 통신할 준비가 되었습니다. 우리는 클라이언트와 서버의 통신을 하기 위한 다양한 방법 중에서 일반적으로 사용되는 비동기 HTTP 통신을 이용할 것이고 그 중 이전 프로젝트에선 ajax를 사용했었기에 axios 라이브러리를 사용할 예정입니다. 분량상 axios에 대한 내용을 기본적으로 안다고 진행할 것이고 axios와 ajax의 차이에 관해선 추후 포스팅할 수 있으면 하겠습니다. 이번 포스트에선 간단하게 axios가 모듈을 추가로 설치해야한다는 것 말고는 ajax보다 장점이 많아서 사용한다고 넘어가겠습니다.
일단 다음 명령어로 axios 라이브러리를 깔아줍니다.
npm install axios --save
잘 깔렸는지 확인하시려면 package.json을 확인해보시면 됩니다.
@ 클라이언트 > 서버 요청을 위한 App.js 수정
라이브러리 설치를 했으면 우선 클라이언트에서 서버에 통신을 요청하는 코드를 작성하겠습니다. 홈페이지 메인에 표시될 App.js 파일(리액트)에 다음과 같이 코드를 작성합니다.
// App.js
import React, {useEffect, useState} from 'react';
import axios from 'axios'; // *1
function App() {
const [output, setOutput] = useState('') // *2
useEffect(() => {
axios.get('/api/output') // 작성한 url 주소에 get 요청을 보냄
.then(response => setOutput(response.data)) // 요청이 성공적으로 도착했다면 reponse에 서버 데이터를 set
.catch(error => console.log(error))
}, []); // *3
return (
<div> 백엔드에서 가져온 데이터입니다 : {output} </div> // *4
);
}
export default App;
우선 *1처럼 사용에 필요한 axios 라이브러리와 useEffect, useState hook을 import합니다. 다음르로 *2처럼 state인 hello를 만듭니다. 만든 후 *3처럼 useEffect hook을 이용해 axios로 서버에서 데이터를 받아오는 코드를 작성합니다. *3에서 state인 hello에 가져온 데이터를 set했으므로 *4에서 hello를 작성하면 출력할 겁니다.
@ 클라이언트 요청을 받을 서버의 컨트롤러 작성
위에서 통신을 위해 코드를 작성했지만 정작 요청을 받는 서버의 /api/hello에 대한 코드가 없습니다. 이러면 아무리 클라이언트가 /api/hello로 api 요청을 해도 서버에서 요청을 받아줄 곳이 없습니다. 따라서 우리는 요청을 받을 수 있게 서버에 컨트롤러를 작성하겠습니다. .../java/fadet/GptTest 하위에 web 패키지를 하나 생성하고 TestAxiosController 클래스를 하나 작성합니다.
//TestAxiosController
@RestController
public class TestAxiosController {
@GetMapping("/api/output")
public String test() {
return "Hello, world!";
}
}
안다고 가정하고 넘어가겠지만 혹시 모르니 @restController는 @Controller+@ResponseBody인 어노테이션인데 쉽게 말해서 get, post 등의 restful한 api 요청을 받는 컨트롤러라고 생각하시면됩니다.
이렇게 서버에서 get 요청을 받을 수 있는 컨트롤러까지 만들었으니 클라이언트에서 요청을 보내면 성공적으로 도착해서 응답을 생성해 보내줄 수 있습니다. 서버를 켜서 각각 다음과 같이 나오면 서버에서 데이터 받아오는 것은 성공입니다.
그리고 반드시 클라이언트와 서버 둘다 켜줘야 합니다. 만약 클라이언트만 키시면 실행은 되어도 다음과 같은 프록시 에러가 뜨면서 3000포트의 백엔드에서 가져온 데이터입니다 뒤에 Hello, world!가 안나올겁니다.
# 클라이언트-서버 API 통신(클라이언트 > 서버)
* 필요 선행지식 : Controller, Service 기본(restController 포함), @ResponseBody, (Dto)
axios 라이브러리, state 기초, useEffect hook
@ 클라이언트에서 서버에 데이터를 보내도록 App.js에 코드 추가
이전까지 서버에 있는 데이터를 클라이언트로 가져오는 과정을 진행했는데 반대로 클라이언트에서 서버로 데이터가 가는지 확인만 하면 api 테스트가 끝날 것 같습니다. 이번 과정은 리액트와 스프링 부트의 기본적인 부분을 모르면 보기 좀 어려우실 수도 있으니 유의바랍니다.
일단 마찬가지로 App.js에 다음과 같은 코드를 추가합니다.
// App.js
function Form(props) {
return <form onSubmit={event =>{
props.onForm();
}}><p><input type="submit" value="create"></input></p>
</form>
}
리액트를 조금은 안다고 가정하고 진행하겠습니다. 우선 Form component 하나를 작성합니다. 이 컴포넌트 안에 간단한 submit버튼 하나 만들겠습니다. 해당 버튼을 누르면 onForm()이 실행되도록 코드를 작성합니다.
function App() {
return ( <div>
<Form onForm={() => {
axios.post('/api/request', {
title: "title1",
content: "content1" // *1
}).then(function (response) {
console.log(response); //
}).catch(function (error) {
console.log(error);})
}}></Form>
백엔드에서 가져온 데이터입니다 : {output}
</div>
);
}
export default App;
아래에서 onForm()을 정의해 주겠습니다. *1처럼 axios.post()로 포스트 요청을 보내주고 요청이 도착하는 주소는 /request 로 해주겠습니다. 보내는 body는 json이며 위와 같이 작성합니다.
@ 클라이언트 데이터를 받을 서버의 컨트롤러 추가
이번 테스트에선 처음 봐도 이해가 될 수 있도록 쉽게 service 컴포넌트를 작성했는데 실제 프로젝트에선 제대로 domain, repository도 생성하시고 서비스에 변수랑 함수 떡칠하면 안됩니다. 예제로써만 봐주세요.
처음했던 것과 마찬가지로 /request에 도착하는 요청을 받는 컨트롤러를 추가하겠습니다. 여기서부턴 스프링의 Service를 아신다는 가정하에 진행하겠습니다.
@RestController
@RequiredArgsConstructor // *1
public class TestAxiosController {
private final ApiService apiService; // *2
@PostMapping("/api/request")
public void save(@RequestBody TestDto testDto) { // *3
apiService.save(testDto); // *4
}
}
일단 이 컨트롤러를 작동시키기위해 service인 ApiService 클래스와 dto인 TestDto 클래스를 하나 만들텐데 이것은 이후에 설명하겠습니다.
*1을 보시면 있는 어노테이션은 스프링에서 DI를 할때 생성자 주입을 위해 넣었습니다. *2를 보면 ApiService를 주입받는데 private final을 붙이면 생성자 주입 과정이 간소화되는데 잘 모르시면 아래 refer 참고하시면 될 것 같습니다. *3을 보시면 @RequestBody 어노테이션을 이용해 App.js에서 보내는 json 데이터를 java 객체로 변환해서 받을 수 있습니다. *4는 변환된 객체를 service에서 저장하도록 제가 service안에 save함수를 만들어 사용했습니다.
다음은 위 컨트롤러를 작동시키기 위해 생성한 ApiService 클래스입니다.
@Service
public class ApiService {
String set = ""; // *1
public void save(TestDto testDto) {
set = testDto.getTitle() +" "+ testDto.getContent(); // *2
}
public String response() {
return set; // *3
}
}
@service 어노테이션을 추가해서 이 클래스를 service 컴포넌트로 만들어주고 *1처럼 데이터를 임시로 담을 set 변수를 하나 만듭니다. 다음 *2처럼 데이터를 저장할 save()함수를 생성하는데 save함수는 보시다시피 testDto를 인자로 받아서 만들어둔 set에 문자열로 붙여서 저장합니다. *3은 response()함수인데 만들어놓은 변수 set을 호출하는 단순한 함수입니다.
@Getter
@Setter
@NoArgsConstructor
public class TestDto {
private String title;
private String content;
}
다음은 데이터를 임시로 담을 TestDto를 만드는 일인데 이건 단순한 dto기 때문에 크게 설명할 건 없습니다. 혹시 Dto를 잘 모르신다면 이거 안 쓰시고 그냥 서비스 안에 객체 더 만드셔서 그걸로 받으시면 됩니다.
App.js와 TestDto, ApiService, TestAxiosController를 위처럼 모두 작성하셨다면 이제 서버를 켜보겠습니다. 이번에도 양쪽 다 켜주셔야 합니다.
버튼을 눌렀을때 위와 같이 뜬다면
1 클라이언트 > 서버로 {title : "title1", content: "content1"}이라는 json 데이터를 post 요청으로 /request에 전달
2 서버의 컨트롤러에서 /api/request로 온 데이터를 받아 set이라는 변수에 임시 저장
3 서버 > 클라이언트로 서버에 저장된 변수 set 데이터를 클라이언트가 /api/out으로 요청
4 서버가 임시 저장되어 있던 변수 set 데이터를 클라이언트로 응답해주어 {output}에 담겨서 화면에 출력
이 과정을 모두 완료한 것입니다.
* 추가 내용
만약 axios 통신에 문제가 생기시면 리액트와 스프링 부트 코드 각각에 로그를 출력해보시면 어떤 부분에서 막혔는지 알기 편하실 겁니다. 아래 이미지같이 컨트롤러에 로그로 dto 객체가 생성되면 콘솔에 출력되도록 하면 클라이언트에서 서버로 데이터가 전송된 부분까진 문제가 없다는 뜻일겁니다.
이거 하다가 기존에 타임리프로 작성했던 view가 먹통이 되길래 한 번 호기심에 GPT에 질문해봤습니다. 그랬더니
A : Yes, integrating React with Spring can result in Thymeleaf not being used for rendering. (후략)
이렇게 답변해주길래 구글링해서 교차 검증해보니 맞는 답변이더라구요. 그래서 GPT 능력도 알아볼겸 앞으로 진행하다 궁금한 거 생기면 한 번 질문해보겠습니다. 당연히 그 대답은 교차 검증해봐야겠죠?
refer
https://velog.io/@u-nij/Spring-Boot-React.js-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85
Spring Boot + React.js 개발환경 연동하기
Spring Boot와 React.js를 연동해 개발환경을 만들고, 빌드해서 jar 파일로까지 만들어보는 과정입니다.
velog.io
https://mangkyu.tistory.com/125
[Spring] 다양한 의존성 주입 방법과 생성자 주입을 사용해야 하는 이유 - (2/2)
Spring 프레임워크의 핵심 기술 중 하나가 바로 DI(Dependency Injection, 의존성 주입)이다. Spring 프레임워크와 같은 DI 프레임워크를 이용하면 다양한 의존성 주입을 이용하는 방법이 있는데, 각각의 방
mangkyu.tistory.com
'Project' 카테고리의 다른 글
P3_GPT API로 프로그래밍 AI 웹서비스 만들어보기(spring Boot+React)_5 기능 명세 정의 (0) | 2023.02.26 |
---|---|
P3_GPT API로 프로그래밍 AI 웹서비스 만들어보기(spring Boot+React)_4 CORS로 인한 기능 수정 (0) | 2023.02.09 |
P3_GPT API로 프로그래밍 AI 웹서비스 만들어보기(spring Boot+React)_2 리액트+스프링 부트 연동1 (0) | 2023.02.05 |
P3_GPT API로 프로그래밍 AI 웹서비스 만들어보기(spring Boot+React)_1 API TEST (0) | 2023.02.04 |
P3_GPT API로 프로그래밍 AI 웹서비스 만들어보기(spring Boot+React)_Ready (0) | 2023.02.02 |