Spring Boot/스프링부트 예제

front + backend 게시판 CRUD 구현 (2)

2주녘 2023. 10. 20. 16:44
반응형

https://devjunyeok.tistory.com/186#3)%20%EB%B6%80%EC%84%9C(Department)%20%EA%B2%8C%EC%8B%9C%ED%8C%90%20%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0-1

 

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) 프론트엔드 + 벡엔드 연동 확인

프론트 엔드 서버와 벡엔드 서버를 재시작하여 전체 조회 기능을 확인합니다.

반응형