안녕하세요 이번에는 글 수정을 구현해보도록 하겠습니다.
글 수정은 글 상세보기에서 버튼을 누르면 수정하는 폼을 보여주도록 하는 방식으로 구현해보겠습니다.
기존에 구현되어 있는 글 상세보기입니다.
우선 기존의 글 상세보기를 컴포넌트화 하는 작업을 해주도록 하겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
<template>
<div>
<el-table border :data="boardList" @row-click="clickRow">
<el-table-column prop="bno" label="글번호"></el-table-column>
<el-table-column prop="title" label="제목"></el-table-column>
<el-table-column prop="content" label="내용"></el-table-column>
<el-table-column prop="writer" label="작성자"> </el-table-column>
<el-table-column prop="regDate" label="등록일" :formatter="DateFormat"></el-table-column>
</el-table>
<board-detail ref="detailPopup"></board-detail>
</div>
</template>
<script>
import axios from 'axios';
import moment from 'moment';
import BoardDetail from './BoardDetail';
export default {
name: 'BoardList',
components: { BoardDetail },
data() {
return {
boardList: [],
};
},
created() {
this.getBoardList();
},
methods: {
DateFormat(row) {
return moment(row.regDate).format('YYYY-MM-DD hh:MM:ss');
},
getBoardList() {
axios
.get('http://localhost:8080/board/get-board-list.do')
.then((response) => {
if (response.data.success) {
this.boardList = response.data.result;
}
})
.catch(function(error) {
console.log(error);
});
},
clickRow(row) {
this.$refs.detailPopup.getBoardDetail(row);
},
},
};
</script>
<style scoped></style>
|
cs |
기존의 BoradList에서 BoardDetail를 분리해 BoardList는 리스트의 역할만 하도록 하고 BoardDetail컴포넌트를 만들어서 BoardDetail컴포넌트에서 상세보기를 하도록 합니다.
components아래에 BoardDetail.vue를 생성해줍니다. BoradList에 있던 상세보기 코드를 컴포넌트화 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
<template>
<div>
<!-- 글 상세 보기 내용 -->
<el-dialog title="글 상세 보기" :visible.sync="openDetail" width="30%" center>
글 번호 : {{ boardDetail.bno }} <br>
제목 : {{ boardDetail.title }} <br>
등록일 : {{ boardDetail.regDate }} <br>
내용 : {{ boardDetail.content }} <br>
작성자 : {{ boardDetail.writer }} <br>
</el-dialog>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'BoardDetail',
data() {
return {
openDetail: false,
boardDetail: [],
};
},
computed: {
setParams() {
const params = {
title: this.title,
content: this.content,
};
return params;
},
},
methods: {
getBoardDetail(row) {
const params = {
bno: row.bno,
};
axios
.post('http://localhost:8080/board/get-board-detail.do', params)
.then((response) => {
if (response.data.success) {
this.boardDetail = response.data.result;
this.openDetail = true;
}
})
.catch(function(error) {
console.log(error);
});
},
},
};
</script>
<style scoped></style>
|
cs |
컴포넌트화를 하고 나서도 잘 동작하는지 확인해줍니다.
이번에는 RegistBoard.vue도 등록만을 위한 컴포넌트로 활용하기 위해서 글쓰기 버튼을 분리해줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
<template>
<div>
<!-- 팝업 내용 -->
<el-dialog title="글쓰기" :visible.sync="openPopup" width="30%" center>
<!-- 본문 영역 -->
<el-input placeholder="제목을 입력해 주세요" v-model="title"></el-input>
<el-input
style="margin-top:30px;"
type="textarea"
:rows="30"
placeholder="내용을 입력해 주세요."
v-model="content"
>
</el-input>
<!-- dialog footer 영역 -->
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="registboard()">확인</el-button>
<el-button @click="openPopup = false">취소</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'RegistBoard',
data() {
return {
openPopup: false,
title: '',
content: '',
};
},
computed: {
setParams() {
const params = {
title: this.title,
content: this.content,
};
return params;
},
},
methods: {
registboard() {
axios
.post('http://localhost:8080/board/regist-board.do', this.setParams)
.then((response) => {
if (response.data.success || response.data.result) {
// 창의 화면 변수 false로 창 닫기
this.openPopup = false;
// 입력되어 있는 변수들의 값을 초기화
this.title = '';
this.content = '';
// 목록을 재로딩을 위해 이벤트 emit
this.$emit('reload');
}
})
.catch(function(error) {
console.log(error);
});
},
},
};
</script>
<style scoped></style>
|
cs |
App.vue에 글쓰기 버튼을 배치하고 ref속성을 통해 openPopup 변수를 true로 바꿔줌으로써 팝업을 보이게 하도록 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<template>
<div id="app">
<!-- 글쓰기 버튼 -->
<el-button type="danger" @click="openRegistPopup()">글쓰기</el-button>
<regist-board @reload="reload" ref="registPopup"></regist-board>
<board-list ref="list"></board-list>
</div>
</template>
<script>
import RegistBoard from './components/RegistBoard.vue';
import BoardList from './components/BoardList.vue';
export default {
name: 'App',
components: {
RegistBoard,
BoardList,
},
methods: {
reload() {
this.$refs.list.getBoardList();
},
openRegistPopup() {
this.$refs.registPopup.openPopup = true;
}
},
};
</script>
<style></style>
|
cs |
자 이제 다시 글 상세보기로 돌아와서 글 수정 버튼을 추가하도록 하겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
<template>
<div>
<!-- 글 상세 보기 내용 -->
<el-dialog title="글 상세 보기" :visible.sync="openDetail" width="30%" center>
글 번호 : {{ boardDetail.bno }} <br>
제목 : {{ boardDetail.title }} <br>
등록일 : {{ boardDetail.regDate }} <br>
내용 : {{ boardDetail.content }} <br>
작성자 : {{ boardDetail.writer }} <br>
<el-row type="flex" justify="end" style="margin-top:30px;">
<el-button type="primary" @click="clcikEditButton()">수정</el-button>
</el-row>
</el-dialog>
<regist-board @reload="reload" ref="registPopup"></regist-board>
</div>
</template>
<script>
import axios from 'axios';
import RegistBoard from './RegistBoard.vue';
export default {
name: 'BoardDetail',
components: { RegistBoard },
data() {
return {
openDetail: false,
boardDetail: [],
};
},
computed: {
setParams() {
const params = {
title: this.title,
content: this.content,
};
return params;
},
},
methods: {
getBoardDetail(row) {
const params = {
bno: row.bno,
};
axios
.post('http://localhost:8080/board/get-board-detail.do', params)
.then((response) => {
if (response.data.success) {
this.boardDetail = response.data.result;
this.openDetail = true;
}
})
.catch(function(error) {
console.log(error);
});
},
clcikEditButton() {
this.openDetail = false;
this.$refs.registPopup.bno = this.boardDetail.bno;
this.$refs.registPopup.title = this.boardDetail.title;
this.$refs.registPopup.content = this.boardDetail.content;
this.$refs.registPopup.editMode = true;
this.$refs.registPopup.openPopup = true;
},
reload() {
// 목록을 재로딩을 위해 이벤트 emit
this.$emit('reload');
}
},
};
</script>
<style scoped></style>
|
cs |
BoardRegist.vue를 재사용하는 방법으로 수정 버튼을 누르게 되면 ref를 통해서 현재 상세 보기 팝업을 닫고 bno, title, content, editMode(수정인지 등록인지 구분하기 위한 플래그)의 정보를 전달해서 registPopup을 열어주도록 합니다.
이제 RegistVue에 타이틀을 구분하기 위해 editMode는 기본은 false이지만 true일 경우 글 수정 팝업 제목을 가지게 됩니다.
자 그럼 지금은 확인을 누르게 되면 새로운 글이 등록이 되니 수정일 경우에는 수정하게끔 로직을 수정할 필요가 있습니다.
editMode를 통해서 editMode이면 edit-boiard.do을 호출해 글을 수정할 수 있도록 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
<template>
<div>
<!-- 팝업 내용 -->
<el-dialog :title="ModalTitle" :visible.sync="openPopup" width="30%" center>
<!-- 본문 영역 -->
<el-input placeholder="제목을 입력해 주세요" v-model="title"></el-input>
<el-input
style="margin-top:30px;"
type="textarea"
:rows="30"
placeholder="내용을 입력해 주세요."
v-model="content"
>
</el-input>
<!-- dialog footer 영역 -->
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="registboard()">확인</el-button>
<el-button @click="openPopup = false">취소</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'RegistBoard',
data() {
return {
openPopup: false,
bno: '',
editMode: false,
title: '',
content: '',
};
},
computed: {
setParams() {
const params = {
bno: this.bno,
title: this.title,
content: this.content,
};
return params;
},
ModalTitle() {
return this.editMode === true ? '글 수정' : '글 등록';
}
},
methods: {
registboard() {
// 글 수정이 아니라면 글 등록 호출
if (!this.editMode) {
axios
.post('http://localhost:8080/board/regist-board.do', this.setParams)
.then((response) => {
if (response.data.success || response.data.result) {
// 창의 화면 변수 false로 창 닫기
this.openPopup = false;
// 입력되어 있는 변수들의 값을 초기화
this.title = '';
this.content = '';
// 목록을 재로딩을 위해 이벤트 emit
this.$emit('reload');
}
})
.catch(function(error) {
console.log(error);
});
}
// 글 수정을 호출
else {
axios
.post('http://localhost:8080/board/edit-board.do', this.setParams)
.then((response) => {
if (response.data.success || response.data.result) {
// 창의 화면 변수 false로 창 닫기
this.openPopup = false;
// 입력되어 있는 변수들의 값을 초기화
this.title = '';
this.content = '';
// 목록을 재로딩을 위해 이벤트 emit
this.$emit('reload');
}
})
.catch(function(error) {
console.log(error);
});
}
},
},
};
</script>
<style scoped></style>
|
cs |
그럼 edit-board.do에 대한 내용을 controller에 작성해주도록 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
package com.dream.controller;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.dream.service.BoardService;
import com.dream.vo.BoardVO;
import com.dream.vo.ResultVO;
@CrossOrigin(origins = "*")
@Controller
@RequestMapping("board/")
public class BoardController {
private static final Logger LOG = LoggerFactory.getLogger(BoardController.class);
@Autowired
BoardService service;
@ResponseBody
@RequestMapping(value = "get-board-list.do", method = RequestMethod.GET)
public ResultVO getBoardList()
{
// 호출 시 찍히게 될 로그
LOG.info("[GET] getBoardList");
// 결과 값을 담을 ResultVO를 선언한 생성자를 통해서 만드는데 기본값은 success는 false, result는 null로 세팅
ResultVO result = new ResultVO(false, null);
try {
result.setResult(service.getBoardList());
result.setSuccess(true);
} catch (Exception e) {
// TODO: handle exception
LOG.error("[Board] getBoardList : " + e.getMessage(), e);
}
return result;
}
@ResponseBody
@RequestMapping(value = "regist-board.do", method = RequestMethod.POST)
public ResultVO registBoard(@RequestBody BoardVO vo)
{
// 호출 시 찍히게 될 로그
LOG.info("[POST] registBoard");
// 결과 값을 담을 ResultVO를 선언한 생성자를 통해서 만드는데 기본값은 success는 false, result는 null로 세팅
ResultVO result = new ResultVO(false, null);
try {
result.setResult(service.registBoard(vo));
result.setSuccess(true);
} catch (Exception e) {
// TODO: handle exception
LOG.error("[Board] getBoardList : " + e.getMessage(), e);
}
return result;
}
@ResponseBody
@RequestMapping(value = "get-board-detail.do", method = RequestMethod.POST)
public ResultVO getBoardDetail(@RequestBody BoardVO vo)
{
// 호출 시 찍히게 될 로그
LOG.info("[POST] getBoardDetail");
// 결과 값을 담을 ResultVO를 선언한 생성자를 통해서 만드는데 기본값은 success는 false, result는 null로 세팅
ResultVO result = new ResultVO(false, null);
try {
result.setResult(service.getBoardDetail(vo));
result.setSuccess(true);
} catch (Exception e) {
// TODO: handle exception
LOG.error("[Board] getBoardDetail : " + e.getMessage(), e);
}
return result;
}
@ResponseBody
@RequestMapping(value = "edit-board.do", method = RequestMethod.POST)
public ResultVO editBoard(@RequestBody BoardVO vo)
{
// 호출 시 찍히게 될 로그
LOG.info("[POST] editBoard");
// 결과 값을 담을 ResultVO를 선언한 생성자를 통해서 만드는데 기본값은 success는 false, result는 null로 세팅
ResultVO result = new ResultVO(false, null);
try {
result.setResult(service.editBoard(vo));
result.setSuccess(true);
} catch (Exception e) {
// TODO: handle exception
LOG.error("[Board] editBoard : " + e.getMessage(), e);
}
return result;
}
}
|
cs |
BoardService에도 editBoard 메소드를 추가해줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
package com.dream.service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dream.mapper.BoardMapper;
import com.dream.vo.BoardVO;
@Service
public class BoardService implements BoardServiceIF {
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Override
public List<BoardVO> getBoardList() {
// TODO Auto-generated method stub
List<BoardVO> boardList = new ArrayList<BoardVO>();
try (SqlSession session = sqlSessionFactory.openSession()) {
BoardMapper mapper = session.getMapper(BoardMapper.class);
boardList = mapper.getBoardList();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return boardList;
}
public Boolean registBoard(BoardVO vo) {
// TODO Auto-generated method stub
vo.setRegDate(new Date());
vo.setWriter("user");
boolean result = false;
try (SqlSession session = sqlSessionFactory.openSession()) {
BoardMapper mapper = session.getMapper(BoardMapper.class);
// 매퍼의 결과를 담을 변수
int mapperResult = 0;
// 성공시 1이 반환됩니다.
mapperResult = mapper.registBoard(vo);
//정상 동작시 return 값을 true
if (mapperResult > 0) {
result = true;
}
//정상 동작이 아닐 시 return 값을 false
else {
result = false;
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return result;
}
public BoardVO getBoardDetail(BoardVO vo) {
// TODO Auto-generated method stub
BoardVO board = new BoardVO();
try (SqlSession session = sqlSessionFactory.openSession()) {
BoardMapper mapper = session.getMapper(BoardMapper.class);
board = mapper.getBoardDetail(vo.getBno());
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return board;
}
public Object editBoard(BoardVO vo) {
vo.setRegDate(new Date());
vo.setWriter("user");
boolean result = false;
try (SqlSession session = sqlSessionFactory.openSession()) {
BoardMapper mapper = session.getMapper(BoardMapper.class);
// 매퍼의 결과를 담을 변수
int mapperResult = 0;
// 성공시 1이 반환됩니다.
mapperResult = mapper.editBoard(vo);
//정상 동작시 return 값을 true
if (mapperResult > 0) {
result = true;
}
//정상 동작이 아닐 시 return 값을 false
else {
result = false;
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return result;
}
}
|
cs |
mybatis에 update를 위한 인터페이스와 sql문도 정의를 해줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.dream.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.dream.vo.BoardVO;
@Mapper
public interface BoardMapper {
public List<BoardVO> getBoardList();
public int registBoard(BoardVO vo);
public BoardVO getBoardDetail(int bno);
public int editBoard(BoardVO vo);
}
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
<?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.dream.mapper.BoardMapper">
<resultMap type="com.dream.vo.BoardVO" id="boardMap">
<id property="bno" column="bno"></id>
<result property="title" column="title"/>
<result property="content" column="content"/>
<result property="writer" column="writer"/>
<result property="regDate" column="regDate"/>
</resultMap>
<select id="getBoardList" resultMap="boardMap">
SELECT * FROM BOARD
</select>
<insert id="registBoard">
INSERT INTO BOARD
(title, content, writer, regDate)
VALUES (#{title}, #{content}, #{writer}, #{regDate})
</insert>
<select id="getBoardDetail" resultMap="boardMap">
SELECT * FROM BOARD WHERE bno = #{bno}
</select>
<update id="editBoard">
UPDATE BOARD
SET title = #{title}, content = #{content}, writer = #{writer}, regDate = #{regDate}
WHERE BNO = #{bno}
</update>
</mapper>
|
cs |
백엔드까지 잘 작성을 했으니 수정을 해보겠습니다.
기존의 리로드 게시글을 수정해보겠습니다.
분명히 변경을 했는데 변경사항이 적용이 안됩니다.
새로고침을 해보면 적용이 되어 있는 걸 볼 수 있는데 수정에서의 reload 메소드를 처리하지 않아서입니다.
BoardDetail에서 reload를 emit 하는데 BoardDetail에서 emit 한 reload를 boardList에서 처리를 해주도록 하겠습니다.
@reload를 emit으로 이벤트를 발생시키면 getBoardList()메소드를 호출하여 목록을 다시 불러주도록 합니다.
다시 시도 후에는 정상적으로 목록을 다시 불러와서 수정 후에 수정된 목록까지 잘 호출되어 바뀌는 걸 확인할 수 있습니다.
추가적으로 모달 밖을 클릭하게 되면 모달이 닫히는데 해당 속성을 통해 닫히지 않도록 추가를 해주도록 하여 x버튼이나 취소를 누르면 닫히고 여백을 클릭하면 닫히지 않도록 합니다.
해당 소스 프로젝트의 코드는 아래 깃허브에서 확인할 수 있습니다.
https://github.com/skarbgud/Board-Project
'Spring > 게시판 만들기' 카테고리의 다른 글
스프링 (Spring Vue) 게시판 만들기 #14. 글 삭제 구현 (마지막) (0) | 2021.12.13 |
---|---|
스프링 (Spring Vue) 게시판 만들기 #12. 글 상세보기 구현 (0) | 2021.11.29 |
스프링 (Spring Vue) 게시판 만들기 #11. 글등록 한글 오류 및 등록 후 목록 리로드 (0) | 2021.11.28 |