front + backend 게시판 CRUD 구현 (1)
안녕하세요 ! :) front + backend 를 통합한 게시판을 생성하는 예제를 만들어보겠습니다. 📖 frontend작업 프론트 작업은 VSCode를 이용하여 프로젝트를 생성하였습니다. frontend 프로젝트는 React + typescri
devjunyeok.tistory.com
지난시간에 이어 이번에는 부서 게시판 기능 중 부서 추가 페이지를 생성해보겠습니다 :)
먼저 프론트 프로젝트 작업부터 시작하겠습니다.
📖 프론트 작업
1) pages > dept-nop > AddDeptNop.tsx 파일 생성
이 페이지는 부서를 추가하는 기능을 하는 페이지 입니다. 그래서 번저 함수 안에 변수를 정의합니다.
// todo ) 새 부서(객체 1개) 저장 페이지
function AddDeptNop() {
// 변수정의
// todo : 초기화 객체
const initialDept = {
dno: null,
dname: "",
loc: "",
}
그리고 부서를 담을 변수와 저장하면 바뀌는 변수 (true/false)를 선언합니다.
// todo 바인딩 변수
// todo 새부서 객체 변수
const [dept, setDept] = useState<IDept>(initialDept);
// todo 저장하면 true, 아니면 false 인 변수(값에따라 화면바뀜)
const [submitted, setSubmitted] = useState<boolean>(false);
그리고 아래에 함수를 정의합니다.
함수는 새로운 입력 폼을 보여주는 함수와 그리고 각 입력창에 수동 바인딩 공통함수와 저장함수를 작성합니다.
// 함수정의
// todo 새로운 폼(form)을 보여주는 함수
const newDept = () => {
// 새 폼 == 객체 초기화, submitted 변수 초기화(false)
setDept(initialDept); // 객체 초기화
setSubmitted(false);
}
// todo 각각의 입력창 수동 바인딩 공통함수
const handleInputChange = (e:React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target; // 화면 값[이름]
// 화면 값을 Dept 객체의 속성에 저장
setDept({...dept, [name]: value});
}
// todo 저장 함수
const saveDept = () => {
// 임시 부서 변수(저장될 객체)
var data = {
dname: dept.dname,
loc: dept.loc
}
// 저장 함수 호출
DeptService.create(data) // 벡엔드로 저장 요청
.then((response:any)=>{
// 저장 성공유무 -> submitted 변수에 값을 true로 변경
setSubmitted(true); // 화면 변경
console.log(response.date);
})
.catch((e:Error)=>{
console.log(e);
})
}
그리고 리턴 값 아래의 HTML 작성은 다음과 같이 하였습니다.
return (
<div className="row">
{submitted ? (
// 저장버튼 클릭하면 아래 화면이 보임
<div className="col-6 mx-auto">
<h4>You submitted successfully!</h4>
{/* Add 버튼 클릭하면 다시 새로운 부서 저장 페이지로 이동(새폼 보이기) */}
<button className="btn btn-success" onClick={newDept}>
Add
</button>
</div>
) : (
<>
{/* 제목 start */}
<TitleCom title="Add Dept No Page" />
{/* 제목 end */}
<div className="col-6 mx-auto">
{/* 부서명 입력창 */}
<div className="row g-3 align-items-center mb-3">
<div className="col-3">
<label htmlFor="dname" className="col-form-label">
Dname
</label>
</div>
<div className="col-9">
<input
type="text"
id="dname"
required
className="form-control"
value={dept.dname}
onChange={handleInputChange}
placeholder="dname"
name="dname"
/>
</div>
</div>
{/* 부서명 입력창 끝*/}
{/* 부서위치 입력창 시작 */}
<div className="row g-3 align-items-center mb-3">
<div className="col-3">
<label htmlFor="loc" className="col-form-label">
Loc
</label>
</div>
<div className="col-9">
<input
type="text"
id="loc"
required
className="form-control"
value={dept.loc}
onChange={handleInputChange}
placeholder="loc"
name="loc"
/>
</div>
</div>
{/* 부서위치 입력창 끝 */}
{/* 저장버튼 시작 */}
<div className="row g-3 mt-3 mb-3">
<button
onClick={saveDept}
className="btn btn-outline-primary ms-2 col"
>
Submit
</button>
</div>
{/* 저장버튼 끝 */}
</div>
</>
)}
</div>
);
}
export default AddDeptNop;
App.tsx 파일의 AddDeptNop 페이지 라우트 처리
{/* dept */}
<Route path="/dept-nop" element={<DeptListNop />} />
<Route path="/add-dept-nop" element={<AddDeptNop />} />
📖 벡엔드 작업
1) service > DeptService.java 파일에 전체조회, 검색어 조회 함수를 작성합니다.
@Service
public class DeptService {
@Autowired
DeptRepository deptRepository; // DI
/** 전체조회 */
public List<Dept> findAll(){
List<Dept> list = deptRepository.findAll();
return list;
}
/** 검색어(dname like) 조회 함수 */
public List<Dept> findAllByDnameContaining(String dname){
List<Dept> list = deptRepository.findAllByDnameContaining(dname);
return list;
}
2) controller > DeptController.java 파일에 전체 조회 함수를 작성합니다.
@Slf4j
@RestController
@RequestMapping("/api")
public class DeptController {
@Autowired
DeptService deptService; // DI
/**
* 전체 조회 + like 검색
*/
@GetMapping("/dept")
public ResponseEntity<Object> getDeptAll(
@RequestParam(defaultValue = "") String dname) {
try {
// 전체 조회 + like 검색
List<Dept> list = deptService.findAllByDnameContaining(dname);
if (list.isEmpty() == false) {
// 성공
return new ResponseEntity<>(list, HttpStatus.OK);
} else {
// 데이터 없음
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
3) API 테스트 - 아래 화면과 같이 GET 메서드로 테스트 합니다.
결과
GET http://localhost:8000/api/dept
HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Transfer-Encoding: chunked
Date: Fri, 20 Oct 2023 07:42:35 GMT
Keep-Alive: timeout=60
Connection: keep-alive
[
{
"insertTime": "2023-10-20 07:42:32",
"updateTime": null,
"deleteYn": "N",
"deleteTime": null,
"dno": 50,
"dname": "ACCOUNTING",
"loc": "NEW YORK"
},
{
"insertTime": "2023-10-20 07:42:32",
"updateTime": null,
"deleteYn": "N",
"deleteTime": null,
"dno": 60,
"dname": "RESEARCH",
"loc": "DALLAS"
},
{
"insertTime": "2023-10-20 07:42:32",
"updateTime": null,
"deleteYn": "N",
"deleteTime": null,
"dno": 70,
"dname": "SALES",
"loc": "CHICAGO"
},
{
"insertTime": "2023-10-20 07:42:32",
"updateTime": null,
"deleteYn": "N",
"deleteTime": null,
"dno": 80,
"dname": "OPERATIONS",
"loc": "BOSTON"
}
]
응답 파일이 저장되었습니다.
> 2023-10-20T164235.200.json
Response code: 200; Time: 234ms (234 ms); Content length: 529 bytes (529 B)
4) 프론트엔드 + 벡엔드 연동 확인
프론트 엔드 서버와 벡엔드 서버를 재시작하여 전체 조회 기능을 확인합니다.
'Spring Boot > 스프링부트 예제' 카테고리의 다른 글
QnA 다양한 검색 게시판 CRUD (1) (1) | 2023.10.24 |
---|---|
게시판 페이징 처리 (1) | 2023.10.23 |
JPA - 연관관계 매핑 (0) | 2023.10.19 |
JPA 페이징 처리 (1) | 2023.10.18 |
JPA - JPQL(Java Persistence Query Language) (1) | 2023.10.17 |