본문 바로가기

Program/Spring

Spring Boot(SPA, REACT)

22.09.30 - Spring Boot(SPA, REACT)

Spring-Boot(SPA : 한페이지에 다 있는 거), REACT활용


Controller

  • BoardController

      package com.academy.bootspa.controller;
    
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.PostMapping;
      import org.springframework.web.servlet.ModelAndView;
    
      import com.academy.bootspa.model.domain.Board;
    
      @Controller
      public class BoardController {
          @GetMapping("/board/main")
          public ModelAndView getMain() {
              System.out.println("메인 요청");
    
              return new ModelAndView("main");
          }
    
          //등록요청 처리
          @PostMapping("/board/regist")
          public ModelAndView regist(Board board) {
              System.out.println("동기방식의 등록 요청을 받음"+board);
              return null;
          }
      }

Exception

  • BoardException

      package com.academy.bootspa.exception;
    
      public class BoardException extends RuntimeException{
    
          public BoardException(String msg) {
              // TODO Auto-generated constructor stub
              super(msg);
          }
          public BoardException(Throwable e) {
              // TODO Auto-generated constructor stub
              super(e);
          }
          public BoardException(String msg, Throwable e) {
              // TODO Auto-generated constructor stub
              super(msg,e);
          }
      }

Model

board

  • BoardDAO

      package com.academy.bootspa.model.board;
    
      import java.util.List;
    
      import com.academy.bootspa.model.domain.Board;
    
      public interface BoardDAO {
          public List selectAll();
          public Board select(int board_id);
          public void insert(Board board);
          public void update(Board board);
          public void  delete(Board board);
      }
  • BoardService

      package com.academy.bootspa.model.board;
    
      import java.util.List;
    
      import com.academy.bootspa.model.domain.Board;
    
      public interface BoardService {
          public List selectAll();
          public Board select(int board_id);
          public void insert(Board board);
          public void update(Board board);
          public void  delete(Board board);
      }
  • BoardServiceImpl

      package com.academy.bootspa.model.board;
    
      import java.util.List;
    
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.stereotype.Service;
    
      import com.academy.bootspa.exception.BoardException;
      import com.academy.bootspa.model.domain.Board;
    
      @Service
      public class BoardServiceImpl implements BoardService{
    
          @Autowired
          @Qualifier("hibernateBoardDAO")
          private BoardDAO boardDAO;
    
          @Override
          public List selectAll() {
              // TODO Auto-generated method stub
              return boardDAO.selectAll();
          }
    
          @Override
          public Board select(int board_id) {
              // TODO Auto-generated method stub
              return boardDAO.select(board_id);
          }
    
          @Override
          public void insert(Board board)throws BoardException{
              // TODO Auto-generated method stub
              boardDAO.insert(board);
    
          }
    
          @Override
          public void update(Board board) throws BoardException{
              // TODO Auto-generated method stub
              boardDAO.update(board);
          }
    
          @Override
          public void delete(Board board) throws BoardException {
              // TODO Auto-generated method stub
              boardDAO.delete(board);
          }
    
      }
  • HibernateBoardDAO

      package com.academy.bootspa.model.board;
    
      import java.util.List;
    
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Repository;
    
      import com.academy.bootspa.exception.BoardException;
      import com.academy.bootspa.model.domain.Board;
    
      @Repository
      public class HibernateBoardDAO implements BoardDAO {
    
          @Autowired
          //이걸로 쿼리문을 제어할 수 있음
          private HibernateBoardRepository boardRepository;
    
          @Override
          public List selectAll() {
              // TODO Auto-generated method stub
              return boardRepository.findAll();
          }
    
          @Override
          public Board select(int board_id) {
    
              return boardRepository.findById(board_id).get();
          }
    
          @Override
          public void insert(Board board) throws BoardException{
              // TODO Auto-generated method stub
              Board result =boardRepository.save(board);//자기가 알아서 board와 연결된 table insert까지 함
              if(result==null) {
                  throw new BoardException("Hibernate로 등록실패");
              }
    
          }
    
          @Override
          public void update(Board board) throws BoardException{
              // TODO Auto-generated method stub
              Board result =boardRepository.save(board);
              if(result==null) {
                  throw new BoardException("Hibernate로 수정실패");
              }
          }
    
          @Override
          public void delete(Board board) throws BoardException{
              // TODO Auto-generated method stub
              try {
                  boardRepository.delete(board);
              } catch (Exception e) {
                  // TODO Auto-generated catch block    
                  e.printStackTrace();
                  throw new BoardException("Hibernate로 삭제실패",e);
    
              }
          }
    
      }
  • HibernateBoardRepository

      package com.academy.bootspa.model.board;
    
      import org.springframework.data.jpa.repository.JpaRepository;
    
      import com.academy.bootspa.model.domain.Board;
    
      //Hibernate가 지원하는 JpaRepository 로 정의                                    DTO    primary-key
      public interface HibernateBoardRepository extends JpaRepository<Board, Integer> {
    
      }
  • MybatisBoardDAO

      package com.academy.bootspa.model.board;
    
      import java.util.List;
    
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Repository;
    
      import com.academy.bootspa.exception.BoardException;
      import com.academy.bootspa.model.domain.Board;
    
      @Repository
      public class MybatisBoardDAO implements BoardDAO{
    
          @Autowired
          private MybatisBoardMapper boardMapper;
    
          @Override
          public List selectAll() {
              // TODO Auto-generated method stub
              return boardMapper.selectAll();
          }
    
          @Override
          public Board select(int board_id) {
              // TODO Auto-generated method stub
              return boardMapper.select(board_id);
          }
    
          @Override
          public void insert(Board board)throws BoardException {
              // TODO Auto-generated method stub
              int result = boardMapper.insert(board);
              if(result==0) {
                  throw new BoardException("Mybaits에 의한 등록실패");
              }
          }
    
          @Override
          public void update(Board board) throws BoardException{
              // TODO Auto-generated method stub
              int result = boardMapper.update(board);
              if(result==0) {
                  throw new BoardException("Mybatis에 의한 수정 실패");
              }
          }
    
          @Override
          public void delete(Board board) throws BoardException{
              // TODO Auto-generated method stub
              int result = boardMapper.delete(board);
              if(result==0) {
                  throw new BoardException("Mybatis에 의한 수정 실패");
              }
          }
    
      }
  • MybatisBoardMapper

      package com.academy.bootspa.model.board;
    
      import java.util.List;
    
      import org.apache.ibatis.annotations.Mapper;
    
      import com.academy.bootspa.model.domain.Board;
    
      //SqlSessionTemplate을 사용하지 않고 개발가능
      @Mapper
      public interface MybatisBoardMapper {
          public List selectAll();
          public Board select(int board_id);
          public int insert(Board board);
          public int update(Board board);
          public int delete(Board board);
      }

domain

  • Board

      package com.academy.bootspa.model.domain;
    
      import javax.persistence.Entity;
      import javax.persistence.GeneratedValue;
      import javax.persistence.GenerationType;
      import javax.persistence.Id;
    
      import lombok.Data;
    
      @Data
      @Entity
      public class Board {
    
          //프라이머리 키에 대하여 아래 2줄은 적어야함
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private int board_id;
    
          private String title;
          private String writer;
          private String content;
          private String regdate;
          private int hit;
      }

mybatis

  • BoardMapper.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="com.academy.bootspa.model.board.MybatisBoardMapper">
    
          <select id="selectAll" resultType="Board">
              select * from board
          </select>
    
          <select id="select" parameterType="int" resultType="Board">
              select * from board where board_id=#{board_id}
          </select>
    
          <insert id="insert">
              insert into board(title,writer,content) 
              values(#{title},#{writer},#{content})
          </insert>
    
          <update id="update" parameterType="Board">
              update board set title=#{title}, writer=#{writer},content=#{content}
              where board_id=#{board_id}
          </update>
    
          <delete id="delete" parameterType="Board">
              delete from board where board_id =#{board_id}
          </delete>
      </mapper>
  • config.xml

      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
      <configuration>
          <typeAliases>
              <typeAlias type="com.academy.bootspa.model.domain.Board" alias="Board"/>
          </typeAliases>
        <mappers>
          <mapper resource="com/academy/bootspa/mybatis/BoardMapper.xml"/>
        </mappers>
      </configuration>

RestController

  • RestBoardController

      package com.academy.bootspa.restcontroller;
    
      import java.util.List;
    
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.http.HttpStatus;
      import org.springframework.http.ResponseEntity;
      import org.springframework.web.bind.annotation.DeleteMapping;
      import org.springframework.web.bind.annotation.ExceptionHandler;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.PathVariable;
      import org.springframework.web.bind.annotation.PostMapping;
      import org.springframework.web.bind.annotation.PutMapping;
      import org.springframework.web.bind.annotation.RequestBody;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
    
      import com.academy.bootspa.exception.BoardException;
      import com.academy.bootspa.model.board.BoardService;
      import com.academy.bootspa.model.domain.Board;
      import com.academy.bootspa.util.Message;
    
      @RestController
      @RequestMapping("/rest")
      public class RestBoardController {
    
          @Autowired
          private BoardService boardService;
    
          //기존 폼을 시리얼화하여 전송 파라미터로 만든 후 전송된 요청받음
          @PostMapping("/serial/board")
          public ResponseEntity<Message> registByParam(Board board){
              System.out.println("registBySerial 등록 요청 받음"+board);
    
              boardService.insert(board);
    
              Message message = new Message(1,"registByParam 등록성공");
              ResponseEntity<Message> entity = new ResponseEntity<Message>(message,HttpStatus.OK);
               return entity;
          }
    
          //기존 폼을 시리얼화하여 전송 json문자열로 변환한 후 전송된 요청받기
          @PostMapping("/json/board")
          public ResponseEntity<Message> registByJson(@RequestBody Board board){
              System.out.println("registByJson 등록 요청 받음"+board);
    
              boardService.insert(board);
    
              Message message = new Message(1,"registByJson 등록성공");
              ResponseEntity<Message> entity = new ResponseEntity<Message>(message,HttpStatus.OK);
               return entity;
          }
    
          @GetMapping("/board")
          public List<Board> getList(){
              List boardList = boardService.selectAll();
    
    
        return boardList;
    }

    @GetMapping("/board/{board_id}")
    public Board getDetail(@PathVariable("board_id")  int board_id ) {
        Board board = boardService.select(board_id);
        System.out.println(board);
        return board;
    }

    //한건 수정 요청
    @PutMapping("/board")
    public ResponseEntity<Message> update(Board board){
        System.out.println("수정 요청으로 받은 파라미터"+board);
        boardService.update(board);

        Message message = new Message(1,"수정 성공");
        ResponseEntity<Message> entity = new ResponseEntity<Message>(message,HttpStatus.OK);
         return entity ;
    }

    @DeleteMapping("/board")
    public ResponseEntity<Message> del(Board board){
        boardService.delete(board);

        Message message = new Message(1,"삭제 성공");
        ResponseEntity<Message> entity = new ResponseEntity<Message>(message,HttpStatus.OK);
         return entity ;

    }

    @ExceptionHandler(BoardException.class)
    public ResponseEntity<Message> handle(BoardException e){

        Message message = new Message(0,e.getMessage());
        ResponseEntity<Message> entity = new ResponseEntity<Message>(message,HttpStatus.INTERNAL_SERVER_ERROR);
         return entity;
    }
}
```

util

  • Message

      package com.academy.bootspa.util;
    
      import lombok.AllArgsConstructor;
      import lombok.Data;
    
      @Data
      @AllArgsConstructor
      public class Message {
          private int code;
          private String msg;
      }

resources

  • application.properties

      #서버포트 변경
      server.port=7777
    
      #뷰 매핑
      spring.mvc.view.prefix=/WEB-INF/views/
      spring.mvc.view.suffix=.jsp
    
      #DB연결
      spring.datasource.url=jdbc:mysql://localhost:3306/javastudy?characterEncoding=utf-8
      spring.datasource.username=root
      spring.datasource.password=1234
      spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    
      #connection pool(dbcp)
      spring.datasource.hikari.connection-timeout=10000
      spring.datasource.hikari.maximum-pool-size=20
    
      #mybatis config 설정파일 등록
      mybatis.config-location=classpath:com/academy/bootspa/mybatis/config.xml
    
      #hibernate에 숨겨진 쿼리 출력
      spring.jpa.show-sql=true

View

  • main.jsp

      <%@ page contentType="text/html;charset=UTF-8"%>
      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="UTF-8">
      <title>Insert title here</title>
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
      <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
      <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
      <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    
      <style type="text/css">
          body{margin:0px;}
          #wrapper{
              width:100%;
              height:700px;
              overflow: hidden;
          }
          #input-area{
              width: 20%;
              height:100%;
              float: left;
              background-color:yellow; 
          }
    
          #list-area{
              width: 60%;
              height:100%;
              float: left;
              background-color:skyblue;
              overflow: scroll;
          }
          #detail-area{
              width: 20%;
              height:100%;
              float: left;
              background-color:pink;
          }
          #input-area input,#detail-area input{
              width: 95%;
          }
      </style>
    
      <script type="text/babel">
      /*--------------------------------------------------------------------
       *동기방식의 폼 전송
       ---------------------------------------------------------------------*/
           function regist(){
              $("#input-form").attr({
                  action:"/board/regist",
                  method:"post"
              });
              $("#input-form").submit();
          }
           /*--------------------------------------------------------------------
            *비동기 방식의 기존폼을 이용한 Parameter 문자열 전송
               serialize : queryString의 형식으로 넘어감
               serializeArray : key : value로 나옴
               contentType은 : post방식에서의 네트워크 header부분에 application/x-www-form-urlencoded 이 나와있다.
            ---------------------------------------------------------------------*/
           function registBySerial(){
              var params = $("#input-form").serialize();
              console.log(params);
    
              //이미 전송할 파라미터화가 완료되었으므로, Json으로 변환하지 말고 그냥 보내보자
              $.ajax({
                  url:"/rest/serial/board",
                  type:"post",
                  data:params,
                  contentType:"application/x-www-form-urlencoded;charset=utf-8",
                  success:function(result,status,xhr){
                      getList();
                  },
                  error:function(xhr,status,error){
                      alert(error.msg);
                  }
    
              });
          }
    
           /*--------------------------------------------------------------------
            *비동기 방식의 기존폼을 이용한 json 문자열 전송
            ---------------------------------------------------------------------*/
           function registByJson(){
               var formArray = $("#input-form").serializeArray();
               console.log(formArray);
    
               //원하는 형태의 json으로 가공
               var json = {};
               for(var i =0; i<formArray.length;i++){
                  json[formArray[i].name]=formArray[i].value;                 
               }
               console.log(json);
    
               //json 전송시 주의 : json 객체 자체는 전송 불가 즉 json문자열로 반환
               $.ajax({
                   url:"/rest/json/board",
                   type:"post",
                   data:JSON.stringify(json),
                   contentType:"application/json;charset=utf-8",
                  success:function(result,status,xhr){
                       getList();
                   },
                   error:function(xhr,status,error){
                       alert(error.msg);
                   }
               });
           }
    
           /*--------------------------------------------------------------------
            *비동기 방식으로 목록 가져오기
            ---------------------------------------------------------------------*/
            function getList(){
               $.ajax({
                   url:"/rest/board",
                   type:"get",
                   success:function(result, status, xhr){
                       console.log("서버로부터 받은 json 목록",result);
    
                      printList(result);//json 배열을 넘겨주자
                   },
                   error:function(xhr,status,error){
                       console.log(error);
                   }
    
               });
           }
    
          //비동기 방식으로 한건의 데이터 가져오기    
            function getDetail(board_id){
              console.log("넘겨받은 board_id " +board_id);
    
              $.ajax({
                  url:"/rest/board/"+board_id,
                  type:"get",
                  success:function(result,status,xhr){
                      console.log(result);
                      printBoard(result);
                  },
                  error:function(xhr, status, error){
                      console.log(error);
                  }
              });
          }
          //우측 영역에 한건 출력
          function printBoard(board){
              $("#detail-form input[name='board_id']").val(board.board_id);
              $("#detail-form input[name='title']").val(board.title);
              $("#detail-form input[name='writer']").val(board.writer);
              $("#detail-form textarea[name='content']").val(board.content);
    
          }
           /*--------------------------------------------------------------------
             *React를 이용한 UI처리
             ---------------------------------------------------------------------*/
    
           function Row(props){
              var link = "javascript:getDetail("+props.board_id+")";
               return(
                  <tr align="center">
                      <td>{props.board_id}</td>
                      <td><a href={link}>{props.title}</a></td>
                      <td>{props.writer}</td>
                      <td>{props.regdate}</td>
                      <td>{props.hit}</td>
                  </tr>
               );
           }
    
             function BoardTable(props){
              var list = props.boardList;
              //tr을 반복한 컨테츠를 구성 
              var tag =[];//여기에 tr을 모아둘것임
    
              for(var i=0; i<list.length; i++){
                  var obj = list[i];//게시물 한건 꺼내기
                  tag.push(<Row board_id={obj.board_id} title={obj.title} writer={obj.writer} regdate={obj.regdate} hit={obj.regdate} />); //arrayList.add()와 동일
              }
              return(
                   <table width="100%" border="1px">
                       <thead>
                           <tr>
                               <th>No</th>
                               <th>제목</th>
                               <th>작성자</th>
                               <th>등록일</th>
                               <th>조회수</th>
                           </tr>
                       </thead>
                       <tbody>
                          {tag}
                       </tbody>
                   </table>        
               );
           }
             //화면에 테이블 출력함수
             function printList(jsonArray){
    
               var root = ReactDOM.createRoot(document.getElementById("list-area"));
               root.render(<BoardTable boardList={jsonArray} />);
           }
    
          //수정 요청
          function edit(){
              //비동기 요청시 기존 폼을 이용하는 법(파라미터,json)
              var params = $("#detail-form").serialize();
    
    
        if(confirm("수정하시겠어요")){
            $.ajax({
                url:"/rest/board",
                type:"PUT",
                data:params,
                contentType:"application/x-www-form-urlencoded;charset=utf-8",
                success:function(result,status,xhr){
                    console.log(result.msg);
                    getList();
                }

            });
        }


    }
       //삭제요청
       function del(){
        if(confirm("삭제하시겠어요")){
            $.ajax({
                url:"/rest/board?board_id="+$("#detail-form input[name='board_id']").val(),
                type:"delete",
                success:function(result,status, xhr){
                    console.log(result);
                    getList();
                },
                error:function(xhr,status,error){
                    alert(error);
                }
            });    
        }
    }
    $(function(){
        $($("#input-area button")[0]).click(function(){
            regist();
        });

        $($("#input-area button")[1]).click(function(){
            registBySerial();
        });
        $($("#input-area button")[2]).click(function(){
            registByJson();
        });

        //상세보기 폼의 버튼 이벤트 처리
        $($("#detail-area button")[0]).click(function(){
            edit();
        });

        $($("#detail-area button")[1]).click(function(){
            del();
        });

        getList();
    });
</script>
</head>
<body>
    <div id="wrapper">
        <div id="input-area">
            <form id="input-form">
                <input type="text" name="title" placeholder="제목">
                <input type="text" name="writer" placeholder="작성자">
                <textarea style="width: 95%;height:150px; " placeholder="내용" name="content"></textarea>
                <button type="button">그냥등록</button>
                <button type="button">폼시리얼 등록</button>
                <button type="button">json 등록</button>
            </form>
        </div>
        <div id="list-area">
            <form id="list-form">

            </form>
        </div>
        <div id="detail-area">
            <form id="detail-form">
                <input type="hidden" name="board_id">
                <input type="text" name="title" placeholder="제목">
                <input type="text" name="writer" placeholder="작성자">
                <textarea style="width: 95%;height:150px;" placeholder="내용" name="content"></textarea>
                <button type="button">수정</button>
                <button type="button">삭제</button>
            </form>
        </div>
    </div>
</body>
</html>
```