9:15 경 학원 도착.
오늘은 오전 수업만 하고
오늘 수업 내용 중요한거 한다고 하심.
<9:30 1교시>
오늘 리액트 수업은 리액트를 하면서 스프링부트를 같이 할 거다.
일단 개발 환경에서 React js Server(3000 port), Spring Boot Server(9000 port) 있다.
React js Server 에서 App.jsx에서 리턴한 내용이 WebBrowser에 UI를 구성하게 되는걸 연습하고 있었고,
Spring Boot Server는 따로 html 페이지를 만들어서 Controller로 요청에 응답하는걸 연습하고 있었다.
우리는 이 둘을 합쳐서, 리액트에서 개발한게 끝나면 그걸 Spring에서 돌릴 예정.
개발 환경 자체는 이런식으로 분리되어 있을 수 밖에 없는데, 리액트에서 만든 결과물을 스프링의 스태틱 폴더에 넣고 배포를 하게 됨.
개발 중에는 어쩔 수 없이 서버를 두 개 돌리면서 분리되어 있는 상황인데,
웹 브라우저에서 form 전송을 페이지 전환없이 전송한다 가정했을 때, 리액트 서버에서 폼 전송을 처리해서 스프링부트 서버로 진행을 해볼 것.
스프링부트에서 제공한 콘텐츠를 출력한 웹브라우저면 웹브라우저에서 스프링부트 서버의 Controller에서 다루는데,
리액트에서 제공한 콘텐츠를 출력한 웹브라우저가 전송하는 내용은 스프링부트 입장에선 당황스러운게 왜 폼전송은 나한테 함? 이러면서 안받아주는게 정상인데 특별한 설정을 하면 받아줌.
웹브라우저 입장에서 node js 서버로부터 콘텐츠를 받고 spring boot 서버로 폼 전송을 하는 상황 : Cross Origin
그래서 폼 전송은 React js Server로 할 거고 proxy Server를 이용해서 리액트 서버와 스프링부트 서버의 Controller 사이에서 중개 역할을 하게 할 것이다.
Proxy Server를 어떻게 설정할 것인가?에 대해서 한줄 정도만 작성하면 됨.
일단 12예제에서 해보기로.
타임리프는 사용할 필요가 없는 이유 : 12 예제에서는 html로 응답할 필요가 없다.
왜냐하면 리액트에서 html을 응답해줄 것이기 때문.
무조건 json 응답을 한다(Controller 상에서 설정으로 바로 응답)
POST 방식의 /posts (경로)요청 : Post 추가 일것 같음
GET 방식의 /posts (경로)요청 : Post 목록 요청일 것 같음
GET 방식의 /posts/1 (경로) 요청 : 1번 글 자세히 보기 요청일 것 같음
우리가 그동안 만들었던 요청 방식이 페이지 전환이 일어나는 POST와 GET 방식이었는데,
(왜냐하면 form 요소가 지원하는 method="post, get"뿐이어서)
API를 만들 때는 이 뿐만 아니라 DELETE 방식의 요청도 있다.
(자바 스크립트로 요청을 하게 되면 페이지 전환 없이 전송하는 방식 5개 POST, GET, DELETE, PUT, FETCH를 다 사용할 수 있다.)
DELETE 방식의 /posts/1 (경로)요청 : 1번 글 삭제 요청일 것 같음
PUT 방식의 /posts/1 (경로)요청 : 1번글 수정 요청(보통 PUT은 전체수정)
FETCH 방식의 /posts/1 (경로)요청 : 1번글 수정 요청(보통 FETCH는 일부 수정)
그동안 해왔던 방식,
보통 API 전송이면 클라이언트가 Json 문자열이 전송해서 json 문자열을 추출하는 방식이 달라짐.
즉, @RequestBody라는 어노테이션을 붙여서 추출해야 함.
@RequestBody : 요청의 Body에서 json 문자열을 읽어내겠다는 의미.
테스트를 위해서 dto를 리턴받게 만들어보고
클라이언트요청은 탭드 포스트맨으로 전송해보기
<10:30 2교시>
위에서 전송해본 결과 전송한 내용이 그대로 응답되는걸 봐선 전송한 json 문자열이 dto에 잘 담겨있다는걸 확인할 수 있고, 그래서 @RequestBody 어노테이션의 기능을 확인해봤음.
원래 폼 전송하던 양식으로 받으려면 @RequestBody 대신에 @RequestParam을 쓰면 되는데 디폴트라 생략 가능하다
방금은 탭드 포스트맨에서 요청을 했는데, 이번에는 리액트 환경에서 이 요청을 실행해볼 거임.
요청을 보낼 리액트 페이지 구성하기
Object의 fromEntries 함수를 이용해서 object를 만들고 이걸 Json 문자열로 변환해준 다음,
일단 스프링부트로 보내지 않고 어떻게 출력되는지 한 번 보기
이제 스프링부트 서버로 요청을 보낼 준비하기
fetch 함수 작성하기 : 기본 틀
fetch함수 작성하기
프록시 서버 설정하기
아까 전의 post 요청은 http://locahost:3000/posts 요청을 node js 서버가 받아서
프록시 설정된 http://locahost:9000으로 보내서 스프링 부트 서버에서 응답
프록시 설정했다가 node js 서버 껐다 키면 spring 부트에서 응답을 해서 전달해줌
선언만 하면 의존 객체가 자동으로(@Autowired PostRepository repo) dependency injection 될 수 있다.
<11:30 3교시>
toEntity 함수에서 null 체크 해주는 이유 : repository의 save 메소드가 update와 insert를 모두 담당하기 때문에
toEntity는 Entity에, toDto는 Dto에 만들기
리액트가 구성한 웹브라우저인데 Spring의 엔티티에 저장이 된다.
save() 함수가 리턴하는 것은 저장된 Entity의 정보이다. 그래서 바로 Dto로 바꿔서 응답해볼 수도 있다.
바로 Dto로 바꿔서 응답하면 웹브라우저에서 Spring이 응답해준 내용이 콘솔창에서 확인된다.
json 전송하려면 Dto가 필요하다. Entity로는 응답할 수 없다
post 배열을 이용해서 <tr></tr>이 여러개 들어있는 배열을 만들어낸다.
오후에는 refresh()를 호출하는 시점에 대해서 다루어볼 거라고 하심.
<12:30 4교시>
오전 진도 복습하고 정리해야지.
import { useState } from "react";
function App(){
//글 목록을 상태값으로 관리하기 위해
const [posts, setPosts]=useState([]);
//글 목록 데이터를 받아오는 함수
const refresh= ()=>{
//Get 방식 /posts 요청하기기
fetch("/posts")
.then(res=>res.json())
.then(data=>{
//서버로부터 받아온 배열로 상태값을 변경하기
setPosts(data);
})
.catch(error=>{
console.log(error);
})
}
return(
<div className="container">
<h1>새글 작성 폼</h1>
<form action="/posts" onSubmit={(e)=>{
e.preventDefault();//폼 전송 막고 fetch함수로 직접 전송하겠다
//요청 url
const url=e.target.action;
//폼에 입력한 내용을 json 형식의 문자열로 바꾸기
//1.0. FormData 객체
const formData=new FormData(e.target);
//1. 폼에 입력한 내용을 object로 변환하기 : Object의 fromEntries()함수
const obj =Object.fromEntries(formData);
//2. object 에 있는 내용을 이용해서 json 문자열 만들기
const json = JSON.stringify(obj);
//console.log(obj);
//console.log(json);
//3. fetch 함수를 이용해서 페이지 전환 없이 post 방식의 요청을 하면서 json 문자열을 전송하기
fetch(url, {
method:"POST",
headers:{"Content-Type":"application/json"},
body:json
})
.then(res=>res.json())
.then(data=>{
//data는 서버에서 응답한 json 문자열이 object로 변경되어서 들어온다
console.log(data);
})
.catch(error=>{
console.log(error);
})
}}>
<input type="text" name="title" placeholder="제목 입력" />
<input type="text" name="author" placeholder="작성자 입력" />
<button type="submit">저장</button>
</form>
<table>
<thead>
<tr>
<th>글번호</th>
<th>제목</th>
<th>작성자</th>
</tr>
</thead>
<tbody>
{posts.map(item =>
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.title}</td>
<td>{item.author}</td>
</tr>
)}
</tbody>
</table>
</div>
)
}
export default App;
오후 진도가 궁금해서 챗지피티에게 물어봤는데, refresh() 시점을 data 가져온 이후에 위치시키면 될 것 같아서 해봤더니 무한로딩 걸리지 않는다. 좋군.
아래는 챗 지피티 답변이고, 1번은 useEffect 훅을 몰라서 안써봤는데 2번대로 해보니까 데이터 새로 불러와질 때만 호출되고 무한 호출 루프는 끝임. 굳.
refresh() 함수는 주로 서버에서 새로운 글 목록을 받아오는 함수인데, 이 함수를 적절한 시점에 호출해야 합니다. 주로 다음과 같은 경우에 호출할 수 있습니다:
- 컴포넌트가 처음 렌더링될 때: React에서는 useEffect 훅을 사용하여 컴포넌트가 처음 마운트될 때 refresh() 함수를 호출할 수 있습니다.
- 새 글이 작성될 때: 글이 성공적으로 작성된 후에 refresh()를 호출하여 최신 글 목록을 받아올 수 있습니다.
여기서는 useEffect를 사용하여 컴포넌트가 마운트되었을 때 자동으로 refresh()가 호출되도록 하고, 글 작성 후에도 refresh()를 호출하여 목록을 갱신하도록 할 수 있습니다.
1. 컴포넌트가 처음 렌더링될 때 refresh() 호출
useEffect를 사용하여 컴포넌트가 처음 렌더링되면 자동으로 refresh()가 실행되도록 할 수 있습니다.
import { useState, useEffect } from "react";
function App() {
const [posts, setPosts] = useState([]);
// 글 목록을 받아오는 함수
const refresh = () => {
fetch("/posts")
.then(res => res.json())
.then(data => {
setPosts(data);
})
.catch(error => {
console.log(error);
})
};
// 컴포넌트가 처음 렌더링될 때 refresh() 호출
useEffect(() => {
refresh();
}, []); // 빈 배열은 컴포넌트가 처음 마운트될 때만 실행되도록 함
return (
<div className="container">
<h1>새글 작성 폼</h1>
<form action="/posts" onSubmit={(e) => {
e.preventDefault();
const url = e.target.action;
const formData = new FormData(e.target);
const obj = Object.fromEntries(formData);
const json = JSON.stringify(obj);
fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: json
})
.then(res => res.json())
.then(data => {
console.log(data);
// 새 글 작성 후 목록 갱신
refresh();
})
.catch(error => {
console.log(error);
})
}}>
<input type="text" name="title" placeholder="제목 입력" />
<input type="text" name="author" placeholder="작성자 입력" />
<button type="submit">저장</button>
</form>
<table>
<thead>
<tr>
<th>글번호</th>
<th>제목</th>
<th>작성자</th>
</tr>
</thead>
<tbody>
{posts.map(item =>
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.title}</td>
<td>{item.author}</td>
</tr>
)}
</tbody>
</table>
</div>
)
}
export default App;
2. 새 글 작성 후 refresh() 호출
폼이 제출되어 새 글이 작성된 후, refresh() 함수를 호출하여 서버에서 최신 글 목록을 받아옵니다. 이 부분은 이미 코드에 반영되어 있습니다:
.then(data => {
console.log(data);
// 새 글 작성 후 목록 갱신
refresh();
})
결론
- useEffect 훅을 사용해 컴포넌트가 처음 마운트될 때 refresh()를 자동으로 호출합니다.
- 새 글을 작성한 후에도 refresh()를 호출하여 목록을 갱신합니다.
이렇게 함으로써, refresh()가 적절한 시점에 호출되어 글 목록이 자동으로 갱신됩니다.
'자바풀스택 과정 > 자바 풀 스택 : 수업내용정리' 카테고리의 다른 글
자바 풀 스택 3/6 오후 기록 068-2 (0) | 2025.03.06 |
---|---|
자바 풀 스택 3/6 오전 기록 068-1 (0) | 2025.03.06 |
자바 풀 스택 3/4 오후 기록 066-2 (0) | 2025.03.04 |
자바 풀 스택 3/4 오전 기록 066-1 (0) | 2025.03.04 |
자바 풀 스택 2/28 오후 기록 065-2 (0) | 2025.02.28 |