스프링 프레임워크의 레이어드 아키텍쳐를 이용한 방명록 페이지입니다.
<개발환경>
언어: Java 8
프레임워크: Spring 4.3.5
Database: MySQL
화면구현방식: jsp
자세한 코드 보기 : https://github.com/Heegene/personal_projects/tree/master/guestbookwithspringmvc
구현 화면입니다.
구현 내용:
-. 이름과 내용을 입력 후 등록 버튼을 누르면 id가 자동으로 부여되어(auto-increment) 올라갑니다.
-. 정렬은 id 내림차순으로 되어 있어 가장 최근에 등록된 방명록이 최상단에 노출됩니다.
-. 페이지 상단에는 전체 방명록의 수가 나타납니다.
-. 한 페이지에서는 5개의 방명록이 보여지며, 5개가 넘어갈 경우 페이징 처리됩니다.
-. 방명록이 입력될 경우, 사용자의 ip(getremoteaddr())와 method(입력 시 "insert"가 입력됩니다), 등록일자(datetime)이 log 테이블에 기록됩니다. 아래는 로그 테이블의 캡쳐입니다.
WebAPI:
get 방식으로 요청 시 json 방식으로 방명록의 리스트를 반환합니다.
post 방식으로 요청하며 "name", "content"의 값을 주면 방명록이 입력됩니다.
delete 방식으로 요청하며 id 번호를 url에 입력할 시 해당 id의 방명록이 삭제됩니다.
API 테스트 결과화면>
주요 코드
-. Service단
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
|
package kr.or.connect.guestbook.service.impl;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import kr.or.connect.guestbook.dao.GuestbookDao;
import kr.or.connect.guestbook.dao.LogDao;
import kr.or.connect.guestbook.dto.Guestbook;
import kr.or.connect.guestbook.dto.Log;
import kr.or.connect.guestbook.service.GuestbookService;
@Service
public class GuestbookServiceImpl implements GuestbookService {
// dto에 repository annotation을 붙여 준 것처럼 service에는 service annotation을 붙임
@Autowired
GuestbookDao guestbookDao;
// autowired annotation을 달면 알아서 객체생성을 도와줌
@Autowired
LogDao logDao;
// method 구현
@Override
@Transactional
public List<Guestbook> getGuestbooks(Integer start) {
// 페이징 처리되는것부터 몇개 이렇게 방명록 목록을 가져오도록 하는 method
List<Guestbook> list = guestbookDao.selectAll(start, GuestbookService.LIMIT);
// start의 경우 argument로 받고, LIMIT은 상수로 지정해 두었음
// 이후 페이지에서 보여줄 방명록의 개수가 변경될 경우, GuestbookService 인터페이스에서 해당 상수값만 변경해주면 됨
return list;
// '읽기'만 하는 메서드는 transactional 이라는 annotation을 붙여 주면
// 내부적으로 readOnly라는 형태로 connection을 사용하게 됨
}
@Override
@Transactional(readOnly =false)
public int deleteGuestbook(Long id, String ip) {
// 삭제를 한 후 Log에 delete action에 대한 로그를 남김 (id, ip, method, 삭제일자)
int deleteCount = guestbookDao.deleteById(id);
Log log = new Log();
// log.setId(id);
log.setIp(ip);
log.setMethod("delete");
log.setRegdate(new Date());
logDao.insert(log); // set한 값을 바탕으로 log 생성
return deleteCount;
}
@Override
@Transactional(readOnly=false)
public Guestbook addGuestbook(Guestbook guestbook, String ip) {
guestbook.setRegdate(new Date());
Long id = guestbookDao.insert(guestbook); // 자동으로 만들어진 id값을 얻어옴
guestbook.setId(id); // id값도 채워져 guestbook이 완성됨
Log log = new Log();
// log.setId(id);
log.setIp(ip);
log.setMethod("insert");
log.setRegdate(new Date());
logDao.insert(log);
return guestbook;
}
@Override
public int getCount() {
// 페이징처리 등을 위해 전체 방명록의 개수를 가져오는 method
return guestbookDao.selectCount();
}
}
|
cs |
-. 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
|
package kr.or.connect.guestbook.controller;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.sound.midi.Soundbank;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import kr.or.connect.guestbook.dto.Guestbook;
import kr.or.connect.guestbook.service.GuestbookService;
@Controller
public class GuestbookController {
@Autowired
GuestbookService guestbookService;
// path가 /list로 들어왔을 때 처리할 부분
@GetMapping(path="/list")
public String list(@RequestParam (
name = "start", // start 라는 값을 꺼내서 사용
required= false,
defaultValue = "0" // 값이 없는 경우 0이라는 디폴트 값을 줌(0부터 시작하므로)
)
int start,
ModelMap model) {
// start로 시작하는 방명록 목록 구하기
List<Guestbook> list = guestbookService.getGuestbooks(start); // start를 인자로 넣어 전체 페이지 수 구함
// 전체 방명록 수를 구한 후 페이지 수 구하기
int count = guestbookService.getCount(); // 전체 방명록의 수 불러옴
int pageCount = count / GuestbookService.LIMIT; // 전체 수를 한 페이지당 보여줄 guestbook 수로 나눔(int이므로 소숫점 버려짐)
if (count % GuestbookService.LIMIT > 0 ) {
pageCount++; // 소숫점이 버려지나 글이 6개여도 2페이지로 보여져야 하므로
// count/limit의 나머지가 발생하는 경우 페이지 수에 1을 더해 줌
}
// 페이지의 수 만큼 start의 값을 리스트로 저장
// 예시: 페이지수가 3이면 0, 5, 10 이렇게 저장됨
// list?star=0, list?start=5, list?start=10 으로 링크가 걸림
List<Integer> pageStartList = new ArrayList<>();
for (int i = 0; i < pageCount; i++) {
pageStartList.add(i * GuestbookService.LIMIT);
}
model.addAttribute("list", list);
model.addAttribute("count", count);
model.addAttribute("pageStartList", pageStartList);
return "list";
}
@PostMapping(path="/write")
public String write(@ModelAttribute Guestbook guestbook,
HttpServletRequest request) {
String clientIp = request.getRemoteAddr();
System.out.println("Client ip -> " + clientIp);
guestbookService.addGuestbook(guestbook, clientIp);
return "redirect:list";
}
}
|
cs |
API 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
|
package kr.or.connect.guestbook.controller;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import kr.or.connect.guestbook.dto.Guestbook;
import kr.or.connect.guestbook.service.GuestbookService;
@RestController
@RequestMapping(path="/guestbooks")
public class GuestbookApiController {
// requestmapping을 이렇게 하면 guestbooks 안쪽으로 떨어지는 것들은 공통으로 사용 가능
// 실행하면 dispatcherservlet이 json message converter를 내부적으로 사용해서 해당 map 객체를 json으로 변환해서 전송
@Autowired
GuestbookService guestbookService;
// getmapping 뒤에 path가 없는 이유는 get방식으로 요청이 들어오면 list를 실행하게 될 것
@GetMapping
public Map<String, Object> list(@RequestParam (name="start",
required=false,
defaultValue = "0")
int start) {
List<Guestbook> list = guestbookService.getGuestbooks(start);
int count = guestbookService.getCount();
int pageCount = count / GuestbookService.LIMIT;
if (count % GuestbookService.LIMIT > 0) {
pageCount++;
}
List<Integer> pageStartList = new ArrayList<>();
for (int i = 0; i < pageCount; i++) {
pageStartList.add(i * GuestbookService.LIMIT);
}
Map<String, Object> map = new HashMap<>();
map.put("list", list);
map.put("count", count);
map.put("pageStartList", pageStartList);
return map;
}
// post 방식의 요청을 처리하기 위한 postmapping
@PostMapping
public Guestbook write(@RequestBody Guestbook guestbook,
HttpServletRequest request) {
String clientIp = request.getRemoteAddr();
// id가 입력된 guestbook이 반환됨
Guestbook resultGuestbook = guestbookService.addGuestbook(guestbook, clientIp);
return resultGuestbook;
}
// guestbooks 뒤에 아이디
@DeleteMapping("/{id}")
public Map<String, String> delete(@PathVariable (name="id") Long id,
HttpServletRequest request) {
String clientIp = request.getRemoteAddr();
int deleteCount = guestbookService.deleteGuestbook(id, clientIp);
return Collections.singletonMap("success", deleteCount > 0 ? "true" : "false");
}
}
|
cs |
각 코드의 내용과 DDL, 쿼리 등은 깃허브를 참고하여 주시기 바랍니다.
'Developments' 카테고리의 다른 글
200804 Spring MVC 게시판(ver.1.0) (0) | 2020.08.04 |
---|---|
[이럴땐이렇게] org.mockito import가 안 될 때 (org.mockito cannot be resolved) (0) | 2020.07.30 |
200724 MVC 기반 Java 방명록 (0) | 2020.07.28 |
[이럴땐이렇게] UiPath - Not all generic types could be resolved 에러 대처법 (0) | 2020.07.22 |
200720 RPA 도입 프로세스 (0) | 2020.07.20 |