๋ชฉ์ฐจ
ํ์ฌ ๋ด๋ถ ํ๋ก์ ํธ ์งํํ๊ณ ์๋ ๊ฒ ์ค ์๋์ ๊ฐ์ ์๊ตฌ์ฌํญ์ด ์์ด์ ๊ตฌํ์ ํ๊ณ ์ด๋ฅผ ๊ธฐ๋กํ๋ค.
ํ์ฌ ๊ฐ๋ฐํ๋ฉฐ ๋์ด๋์ ์๊ด์์ด ๊ธฐ๋ก์ ํ๊ณ ์๋๋ฐ ์ด๋ ๋์ค์ ๋ถ๋ช ๋์ ์ข์ ์์ฐ์ด ๋ ๊ฑฐ๋ผ ๋ฏฟ๋๋ค.
๋๊ตฐ๊ฐ์๊ฒ ์ฌ์ด ์ผ์ด ๋ ๋๊ตฐ๊ฐ์๊ฒ ์ข์ ๊ฐ์ ์ค ์๋ ์๊ธฐ์ ์ข์ ์ ์ค์ด๊ณ ์์ ๊ฐ์ ๋๋ฆฌ๊ธฐ ์ํจ์ด๋ค.
์ฒ์๋ถํฐ ์ํ๋ ์ฌ๋๋ ๋ฌผ๋ก ์์ ์ ์๊ฒ ์ง๋ง ๊ทธ๋ฐ์ฌ๋์ด ๊ทธ๋ ๊ฒ ๋ง์ง ์๋ค๊ณ ๋ณธ๋ค.
์ฒ์์ ์ด๋ ค์ ๋ ๊ฒ ํด๋ณด๋ฉด ๋์ค์ ์ฌ์ด๊ฒ ๋๋ค.
๊ตฌ๊ธ์ ์กด์ฌํ๋ฉด ์ํด๋ด์ ์ด๋ ค์ด ๊ฒ์ด์ง ๋ชปํ ๊ฑด ์๋ค.
๊ทธ๋ฆฌ๊ณ ๊ตฌ๊ธ์ ์์ด๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐ ํ ์ ์์ด์ผ ํ๋ก๋ค.(๊ตฌ๊ธ์ ์์ผ๋ฉด ์๊ฐ์ด ๋ ์ค๋ ๊ฑธ๋ฆฌ๊ฒ ์ง)

๋ ์์ง ๋ถ์กฑํ ์ง์ฅ์ธ ๊ฐ๋ฐ์์ด๊ธฐ์ ํ๋ฒํ ๊ฐ๋ฐ์๊ฐ ๋๊ธฐ์ํด ๋์์์ด ๊ธฐ๋กํ๊ณ ๊ณต๋ถํ ๊ฒ์ด๋ค.
์๊ตฌ์ฌํญ์ ๊ด๋ฆฌ์ํ์ด์ง์์ ๊ทธ๋ฆฌ๋๋ก ๋ฐ์ดํฐ๋ฅผ ์์ ํ ์ ์์ด์ผ ๋๋ค.
๊ธฐ์กด์ ํด์๋ ๋ฐ์ดํฐ์ ํค๊ฐ์ผ๋ก ์์ ํ์ด์ง์์ ์์ ์ ํ๋ ๋ฐฉ์์ด ์๋๋ผ ๊ทธ๋ฆฌ๋ ๋ด์์ ์ธ๋ผ์ธ์ผ๋ก ์์ ์ ํ๋ ๊ฒ์ด๋ค. ์๋ฅผ ๋ค๋ฉด ์๋์ ๊ฐ์ ๊ธฐ๋ฅ์ด๋ค.
jqGrid Loading Data - Inline Editing with on Row Click
www.guriddo.net
๊ทธ๋ฆฌ๋์ ํ ์ ํ ํ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ฉด DB์ ๋ฐ์ดํฐ๊ฐ ๋ฐ์์ด ๋์ด์ผ ํ๋ค.
ํด๋ณธ์ ์๋ ์ด ๊ธฐ๋ฅ์ ์ด๋ป๊ฒ ๊ตฌํํ๋ฉด ๋ ๊น?์ฌ๋ฌ ๊ณ ๋ฏผ ๋์ ์๋์ ํ๋ก์ธ์ค ์ ์๋ฅผ ๋ด๋ ธ๋ค.
1. ํ๋ฉด๋จ์์๋ ํ ์ด๋ธ์ td์์๋ฅผ ํธ์งํ๋ค.
2. ์ ์ฅ ๋ฒํผ์ ๋๋ฅผ ๋ table tr์ ๋ฃจํ ๋๋ ค ์ด๋ฅผ ์ ์ฅํ ๋ค์ ์๋ฒ๋ก ์ ์กํ๋ค.
3. ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ ์ฌ๋ฌ๋ฒ DB ์ ๋ฐ์ดํธ๋ฅผ ์ํํ๋ค.
์ด ๋ ์ฌ๋ฌ๋ฒ ์ ๋ฐ์ดํธ ์ํํ๋ ๋ฐฉ๋ฒ์ด java๋จ์์ ์ ๋ฐ์ดํธ๋ฅผ ์ฌ๋ฌ๋ฒ ์ํํ๋ ๊ฒ๊ณผ mybatis์์ ์ํํ๋ ๊ฒ์ด ์๋๋ฐ ๋ ํ์๋ฅผ ํํ๋ค.
์ด๋ฅผ ์ํด์ ์๋์ฒ๋ผ ํ๋กํผํฐ ์์ ์ด ํ์ํ๋ค.
#mariadb
spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
#allowMultiQueries=true ์ด๋ ๊ฒ ์ค์ ์ ๊ผญ ํด์ผ ํ๋ค.
spring.datasource.url=jdbc:log4jdbc:mariadb://localhost:3307/test?characterEncoding=UTF-8&allowMultiQueries=true
spring.datasource.username=test
spring.datasource.password=1234
mybatis.mapper-locations=mybatis-mapper/**/*.xml
contenteditable
๊ตฌ๊ธ๋ง ๊ฒฐ๊ณผ html์ contenteditable์์ฑ์ ์ด์ฉํ๋ฉด ์ ๋ ฅํ๊ทธ(input, textarea ๋ฑ)๊ฐ ์๋ ํ๊ทธ๋ฅผ ํธ์งํ ์ ์๋๋ก ํด์ค๋ค. ์ด๋์ ์ด์ฉํ ๊ฒ์ด๋ค.
์๋์์ ํ ์คํธ ๊ฐ๋ฅํ๋ค.
See the Pen Untitled by SangYeop Lee (@devLsy) on CodePen.
์ฝ๋๋ฅผ ์น๊ธฐ์ ๊ท์ฐฎ์ง๋ง ์ ํํ ์์ ํ ์คํธ๋ฅผ ์ํด์ ์ฝ๋๋ฅผ ์น๊ฒ ๋ค.
์๊ฐ๊ด๊ณ์ ํ๋ฉด ๋์์ธ์ ์๋ค. ๐
ํ๋ก์ ํธ ์์ฑ ํ ๊ฐ๋จํ๊ฒ board ํ ์ด๋ธ์ ํ๋ ๋ง๋ค์๊ณ ๋ชฉ๋ก ์กฐํ, ๋ฑ๋ก, ๋ค๊ฑด ์์ ๋ง ๊ตฌํํ๊ฒ ๋ค.
CREATE TABLE `study_board`(
`board_seq` bigint auto_increment,
`title` varchar (30),
`contents` varchar (100),
`name` varchar (30),
`reg_date` timestamp,
`update_date` timestamp,
primary key(board_seq)
);
BoardVo
package study.ex1.vo;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class BoardVo {
private Long boardSeq;
private String title;
private String contents;
private String name;
private String regDate;
private String updateDate;
}
mapper 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="study.ex1.mapper.BoardMapper">
<resultMap id="boardMap" type="study.ex1.vo.BoardVo">
<id column="board_seq" property="boardSeq"/>
<result column="title" property="title"/>
<result column="contents" property="contents"/>
<result column="name" property="name"/>
<result column="reg_date" property="regDate"/>
<result column="update_date" property="updateDate"/>
</resultMap>
<!-- ์๋๋ ์กฐํ ์ *๋ ์ง์ํด์ผ ํ์ง๋ง ์์ ๋๊น *๋ก ํจ -->
<select id="findContentsList" resultMap="boardMap">
SELECT * FROM study_board
</select>
<!-- ๊ฒ์๊ธ ๋ฑ๋ก -->
<insert id="insertContents" parameterType="study.ex1.vo.BoardVo">
INSERT INTO study_board
(
title
,contents
,name
,reg_date
,update_date
)
VALUES
(
#{title}
,#{contents}
,#{name}
,SYSDATE()
,SYSDATE()
)
</insert>
<!-- ๋ค์ค ์
๋ฐ์ดํธ -->
<update id="updateContentsList" parameterType="list">
<foreach collection="list" item="item" index="index" separator=";">
UPDATE study_board
SET title = #{item.title}
,contents = #{item.contents}
,name = #{item.name}
,update_date = SYSDATE()
WHERE board_seq = #{item.boardSeq}
</foreach>
</update>
</mapper>
mapper interface
package study.ex1.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import study.ex1.vo.BoardVo;
import java.util.List;
@Repository @Mapper
public interface BoardMapper {
//๊ฒ์๊ธ ๋ชฉ๋ก ์กฐํ
List<BoardVo> findContentsList();
//๊ฒ์๊ธ ๋ฑ๋ก
void insertContents(BoardVo boardVo);
//๊ฒ์๊ธ ๋ฆฌ์คํธ ์
๋ฐ์ดํธ
void updateContentsList(List<BoardVo> boardVoList);
}
service
package study.ex1.service;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import study.ex1.mapper.BoardMapper;
import study.ex1.vo.BoardVo;
import java.util.List;
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class BoardService {
private final BoardMapper boardMapper;
/**
* ๊ฒ์๊ธ ๋ชฉ๋ก ์กฐํ
* @return
*/
public List<BoardVo> findContentsList() {
return boardMapper.findContentsList();
}
/**
* ๊ฒ์๊ธ ๋ฑ๋ก
* @param boardVo
*/
@Transactional
public void insertContents(BoardVo boardVo) throws Exception{
boardMapper.insertContents(boardVo);
}
/**
* ๊ฒ์๊ธ ๋ค๊ฑด ์์
* @param boardVoList
*/
@Transactional
public void updateContentsList(List<BoardVo> boardVoList) throws Exception{
boardMapper.updateContentsList(boardVoList);
}
}
jUnit ๋ฑ๋ก, ์กฐํ, ์์ ํ ์คํธ
package study.ex1.service;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Commit;
import org.springframework.transaction.annotation.Transactional;
import study.ex1.vo.BoardVo;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@Slf4j
@Transactional
class BoardServiceTest {
@Autowired BoardService boardService;
@Test
@DisplayName("๊ฒ์๊ธ๋ฑ๋ก")
@Commit
public void ๊ฒ์๊ธ๋ฑ๋ก() throws Exception {
for (int i = 0; i < 11; i++) {
BoardVo boardVo = new BoardVo();
boardVo.setTitle("์ ๋ชฉ : " + i);
boardVo.setContents("๋ด์ฉ : " + i);
boardVo.setName("์์ด์ธ๋งจ");
boardService.insertContents(boardVo);
}
}
@Test
@DisplayName("๊ฒ์")
@Commit
public void ๊ฒ์() throws Exception {
boardService.findContentsList();
}
@Test
@DisplayName("์์ ")
@Commit
public void ์์ () throws Exception {
List<BoardVo> boardVoList = new ArrayList<>();
BoardVo boardVo1 = new BoardVo();
boardVo1.setBoardSeq(1L);
boardVo1.setTitle("์ฒ๋ฅ์ ์ ");
boardVo1.setContents("์ฒ๋ฅ์ ์ ํ ๋ฅด");
boardVo1.setName("thor");
BoardVo boardVo2 = new BoardVo();
boardVo2.setBoardSeq(2L);
boardVo2.setTitle("์ฅ๋์ ์ ");
boardVo2.setContents("์ฅ๋์ ์ ๋กํค");
boardVo2.setName("loki");
boardVoList.add(boardVo1);
boardVoList.add(boardVo2);
boardService.updateContentsList(boardVoList);
}
}
์ต์ข DB๊ฒฐ๊ณผ
11๊ฑด ๋ฐ์ดํฐ ๋ฑ๋ก ํ๊ณ ๊ทธ ์ค 1๋ฒ๊ณผ 2๋ฒ์ ๋ฐ์ดํฐ ์ ์์ ์ผ๋ก ์์ ๋จ
๊ฐ๋จํ CRU ํ ์คํธ ์๋ฃ ํ๊ณ ์ด์ ํ๋ฉด๋จ ์์
board_list.html(thymeleaf)
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- bootstrap, jquery cdn load -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<title>๊ฒ์๊ธ ๋ชฉ๋ก</title>
</head>
<body>
<div class="container">
<h2>๊ฒ์๊ธ ๋ชฉ๋ก</h2>
<div>
<table class="table table-striped">
<thead>
<tr>
<th>๋ฒํธ</th>
<th>์ ๋ชฉ</th>
<th>๋ด์ฉ</th>
<th>์์ฑ์</th>
<th>์์ฑ์ผ</th>
<th>์์ ์ผ</th>
</tr>
</thead>
<tbody>
<tr id="board_tr" th:each="item : ${list}">
<td th:text="${item.boardSeq}"></td>
<!-- ์ ๋ชฉ,๋ด์ฉ,์์ฑ์๋ ํธ์ง ๊ฐ๋ฅ ์ต์
๋ถ์ฌ-->
<td contenteditable="true" th:text="${item.title}"></td>
<td contenteditable="true" th:text="${item.contents}"></td>
<td contenteditable="true" th:text="${item.name}"></td>
<td th:text="${item.regDate}"></td>
<td th:text="${item.updateDate}"></td>
</tr>
</tbody>
</table>
<button type="button" id="updateBtn" class="btn btn-primary">์์ ํ๊ธฐ</button>
</div>
</div> <!-- /.container -->
<script>
$(document).ready(function () {
// ์
๋ฐ์ดํธ ๋ฒํผ ํด๋ฆญ ์ update_contents_list() ํธ์ถ
$("#updateBtn").on("click", function () {
update_contents_list();
});
function update_contents_list() {
// ๊ฐ์ฒด ๋ด์ ๋ฐฐ์ด
let tableArr = new Array();
$("tr#board_tr").each(function (index, item) {
let td = $(this).children();
// ํ
์ด๋ธ ๊ฐ์ฒด
let td_obj = {
boardSeq : td.eq(0).text(),
title: td.eq(1).text(),
contents : td.eq(2).text(),
name : td.eq(3).text()
};
// ๋ฐฐ์ด์ ๊ฐ์ฒด๋ฅผ ์ ์ฅ
tableArr.push(td_obj);
});
// ๋น๋๊ธฐ๋ก ์๋ฒ์ ์
๋ฐ์ดํธ ํธ์ถ
$.ajax({
type : "POST",
url : "/board/update",
data: JSON.stringify(tableArr),
dataType: "JSON",
contentType: "application/json; charset=UTF-8",
// ์๋ฒ๋ก ๋ฐฐ์ด์ ๋๊ธธ ๋๋ ๋ฐ๋์ ์๋ ์ต์
์ ๋ถ์ด๋ผ๊ณ ํ์ง๋ง ๋ด ๊ฒฝ์ฐ๋ JSONํํ ์คํธ๋ง์ผ๋ก ๋๊ธฐ๊ธฐ ๋๋ฌธ์ ์๋๊ฐ ์์ด๋ ์ ์ ์๋ํ๋ค.
// traditional: true,
success : function(data){
if(data.resultCode === "01")
alert("์์ ๋์์ต๋๋ค.");
location.reload();
},
error : function(XMLHttpRequest, textStatus, errorThrown){
alert("ํต์ ์คํจ.");
}
});
}
});
// End of $(document).ready...
</script>
</body>
</html>
ํ๋ฉด์์ ํ๋ ์ผ์ ๊ฐ๋จํ๋ค. study_boardํ ์ด๋ธ์ ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ ๋ฟ๋ฆฌ๊ณ ์ ๋ชฉ, ๋ด์ฉ, ์์ฑ์ ํธ์ง์ด ๊ฐ๋ฅํ๊ณ ์์ ํ๊ธฐ ๋ฒํผ ํด๋ฆญ ์ ๋ฒํธ, ์ ๋ชฉ, ๋ด์ฉ, ์์ฑ์๋ฅผ ๊ฐ์ฒดํ์ ์ผ๋ก ๋ฐฐ์ด์ ๋ด์์ JSON ๋ณํ ํ ์๋ฒ๋ก ๋๊ธด๋ค.
ํ๋ฉด์์ ๋๊ธฐ๋ ๋ฐ์ดํฐ ํํ๋ ์๋์ ๊ฐ๋ค.(๋ฐฐ์ด์์ ๊ฐ์ฒด๊ฐ ๋ด๊ฒจ ์๋ค.)
[{"boardSeq":"1","title":"์ฒ๋ฅ์ ์ ","contents":"๋ง์น์ ์ ํ ๋ฅด","name":"thor"},{"boardSeq":"2","title":"์ฅ๋์ ์ ","contents":"์ฅ๋์ ์ ๋กํค","name":"loki"},{"boardSeq":"3","title":"์ ๋ชฉ : 2","contents":"๋ด์ฉ : 2","name":"์์ด์ธ๋งจ"},{"boardSeq":"4","title":"์ ๋ชฉ : 3","contents":"๋ด์ฉ : 3","name":"์์ด์ธ๋งจ"},{"boardSeq":"5","title":"์ ๋ชฉ : 4","contents":"๋ด์ฉ : 4","name":"์์ด์ธ๋งจ"},{"boardSeq":"6","title":"์ ๋ชฉ : 5","contents":"๋ด์ฉ : 5","name":"์์ด์ธ๋งจ"},{"boardSeq":"7","title":"์ ๋ชฉ : 6","contents":"๋ด์ฉ : 6","name":"์์ด์ธ๋งจ"},{"boardSeq":"8","title":"์ ๋ชฉ : 7","contents":"๋ด์ฉ : 7","name":"์์ด์ธ๋งจ"},{"boardSeq":"9","title":"์ ๋ชฉ : 8","contents":"๋ด์ฉ : 8","name":"์์ด์ธ๋งจ"},{"boardSeq":"10","title":"์ ๋ชฉ : 9","contents":"๋ด์ฉ : 9","name":"์์ด์ธ๋งจ"},{"boardSeq":"11","title":"์ ๋ชฉ : 10","contents":"๋ด์ฉ : 10","name":"์์ด์ธ๋งจ"}]
์๋ฒ์์ ์ด๋ฅผ @RequestBody๋ก ๋ฐ์์ ์ ๋ฐ์ดํธ๋ฅผ ์ํํ๋ค.
JSON ํ์ ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ๋ ๋ฐ๋์ ์ ์๋ ธํ ์ด์ ์ ์ ์ธํด์ผ ํ๋ค. ์๋๋ฉด ๊ฐ์ด null๋ก ๋ค์ด์จ๋ค.
controller
package study.ex1.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import study.ex1.service.BoardService;
import study.ex1.vo.BoardVo;
import java.util.HashMap;
import java.util.List;
@Controller
@Slf4j
@RequiredArgsConstructor
@RequestMapping(value = "board")
public class BoardController {
private final BoardService boardService;
@GetMapping(value = "/list")
public String list(Model model) {
List<BoardVo> contentsList = boardService.findContentsList();
model.addAttribute("list", contentsList);
return "/board/board_list";
}
@PostMapping(value = "/update")
@ResponseBody
public HashMap<String, Object> updateContents(@RequestBody List<BoardVo> boardVoList) {
HashMap<String, Object> resultMap = new HashMap<>();
try {
boardService.updateContentsList(boardVoList);
resultMap.put("resultCode", "01");
resultMap.put("result", "success");
} catch (Exception e) {
log.info("์๋ฌ๋ฌ์ผ๋ ์ ๋ณด๊ณ ๊ณ ์ณ~!");
log.info("exception : {}", e.getMessage());
}
return resultMap;
}
}
์๋๋ ํ ์คํธ ๋ผ์ด๋ธ ๋์์์ด๋ค.(ํ ์ด๋ธ์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ ๋ด์ฉ์ด DB์ ์ ๋ฐ์์ด ๋๋ค.)
๋ง์ ํด๋ณด๊ณ ๋๋ฉด ์ ๋ง ๊ฐ๋จํ์ง๋ง ๊ธฐ๋กํ๋ค.
์ด ์ฝ๋๋ ์ข์ ์ฝ๋๊ฐ ์๋๊ณ ๋น์ฐํ ๋ ์ข์ ๋ก์ง์ด ์์ ๊ฒ์ด๋ค.

'IT > development' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[spring] @RequestBody String type ๋ฐ๊ธฐ (0) | 2023.01.24 |
---|---|
[spring/mybatis] Rest API ๊ณ์ธต๊ตฌ์กฐ(1:N) ๊ฐ์ฒด ์กฐํ(feat .์ฌ์ด ์์ ) (0) | 2022.12.25 |
[ํฐ์คํ ๋ฆฌ] ์ฝ๋๋ธ๋ญ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ ์ถ๊ฐํ๊ธฐ(feat. clipboard.js) (0) | 2022.12.17 |
[mybatis] mybatis multi update(๋ค์ค ์ ๋ฐ์ดํธ) (0) | 2022.12.16 |
[thymeleaf/javascript]thymeleaf๊ฐ์ javascript์์ ์ฌ์ฉ (0) | 2022.12.08 |
๋๊ธ