Category: Spring

0

Spring Boot 게시판 만들기 16 - Github와 jenkins 연동하기

16. Github와 jenkins 연동하기Github webhookngrok을 이용해 외부접근이 가능하도록 네트워크 열기Push 이벤트가 일어났을 때 로컬 Jenkins가 해당 훅을 받기 위해서는 해당 네트워크(포트)를 외부접근이 가능하게 열어놔야 한다.ngrok 프로그램을 사용해 Github로부터 hook을 받을 수 있도록 Jenkins 포트를 열어준다. ngrok은 local PC를 외부에서 접근이 가능하도록 열어주는 프로그램이다. Webhook 추가하기Project Repository > settings > Webhooks > add webhook 로 이동해 Payload URL에 http://[서버 IP주소]:[Port번호]/github-webhook/ 형식으로 날릴 주소를 기입하도록한다. 반드시 주소 뒤에 /github-webhook/ 를 추가해야 한다. Push가 일어났을 때 Payload URL(Jenkins)로 Hook을 날린다.

0

Spring Boot 게시판 만들기 14 - Jenkins 프로젝트 만들기

14. Jenkins 프로젝트 만들기 새 프로젝트 만들기 소스 코드 관리 소스 코드를 가져오기 위한 계정 추가하기 빌드 유발 선택 Build 선택하기 프로젝트 확인 프로젝트 build하기 프로젝트 결과 확인 새 프로젝트 만들기create a Job > FreeStyle project 를 선택해 새 프로젝트를 만든다. 소스 코드 관리git을 선택한 후 Build할 Repository URL을 넣어준다. 계정 추가하기

0

Spring Boot 게시판 만들기 13 - jenkins를 이용해 build 하기

13. jenkins를 이용해 build하기jenkins를 로컬에 바로 설치해 사용해도 되지만 해당 프로젝트에서는 docker를 이용해 jenkins이미지를 불러와 빌드를 진행할 것이다. docker 설치하기도커는 https://docs.docker.com/get-docker/ 에서 본인 운영체제에 맞는 것을 선택해 다운로드 한다. 본인이 사용하는 프로젝트의 jdk 버전에 맞는 jenkins 파일 가져와야 build시에 jdk 버전 오류가 안생긴다. JDK11버전의 jenkins 이미지를 불러오도록 한다. docker images 명령어를 통해 이미지들을 확인할 수 있다. # lts버전의 jenkins 이미지docker pull jenkins/jenkins:lts# jdk11버전의 jenkins 이미지docker pull jenkins/jenkins:jdk11# 설치된 이미지들 확인docker images jenkins 이미지 실행하기

0

Spring Boot 게시판 만들기 12 - 타임리프 오류 해결하기

12. 타임리프 오류 해결하기null 오류게시물이 하나도 없을 때 Thymeleaf에서 null인 객체를 참조하여 오류가 발생하는 것을 확인할 수 있었다. 객체를 반환할 때 데이터가 null인 경우 비어있는 객체를 생성해 반환하도록 한다. @GetMapping("/")public String board(@PageableDefault Pageable pageable, Model model) { Page<Post> posts = postService.getPosts(pageable); if(posts == null){ posts = new PageImpl<Post>(new ArrayList<>()); } model.addAttribute("PostList", posts); return "board";} paging 오류데이터가 하나도 없는데 pagination 목록에서 1과 0이 떠있는 오류를 발견했다.

0

Spring Boot 게시판 만들기 11 - 페이징 처리하기

11. 페이징 처리하기메인 게시판을 접근하게 되면 한번에 너무 많은 게시글들이 쏟아져 나오게 돼 보기가 좋지 않고 서버도 많은 양의 데이터를 한번에 보내야 하기 때문에 성능에서도 좋지 않다.사용자에게 전체 데이터에서 적당한 양의 데이터만 보여줘 사용자 입장에서도 서버 입장에서도 부담이 없게 한다. Control 로직 수정하기Spring에서 제공하는 Pageable 인터페이스를 사용하면 쉽게 페이징 기능을 사용할 수 있다. BoardController.java @Controller@RequiredArgsConstructorpublic class BoardController { private final PostService postService;// @GetMapping("/")// public String board(Model model){// List<Post> posts = postService.getAllPosts();// model.addAttribute("PostList", posts);// return "board";// } @GetMapping("/") public String board(@PageableDefault Pageable pageable, Model model) { Page<Post> posts = postService.getPosts(pageable); model.addAttribute("PostList", posts); return "board"; }} 테스트 코드 로직 변경@Test@DisplayName("모든 Post를 가져온다.")public void board() throws Exception { List<Post> posts = new ArrayList<>(); posts.add(Post.builder() .id(1L) .title("test") .name("tester") .content("test") .writeTime(LocalDateTime.now()) .build() ); Page<Post> pagePosts = new PageImpl<>(posts); PageRequest pageRequest = PageRequest.of(1, 10); given(postService.getPosts(pageRequest)).willReturn(pagePosts); ResultActions resultActions = mockMvc.perform(get("/") .param("page", "1")// .flashAttr("PostList", new ArrayList<>()) ); resultActions .andExpect(status().isOk()) .andDo(print()); verify(postService).getPosts(pageRequest);} Servic 로직 추가

0

Spring Boot 게시판 만들기 10 - 포스트 삭제하기

10. 포스트 삭제하기PostController.java @PostMapping("/post/{postId}/delete")public String deletePost(@PathVariable("postId") Long id){ postService.deletePostById(id); return "redirect:/";} PostService.java @Transactionalpublic void deletePostById(Long id){ postRepository.deleteById(id);} Post.html <tbody class="text-center"><tr th:each="Post:${PostList}" th:id="*{Post.id}"> <td class="align-middle" th:text="${PostStat.index+1}"></td> <td class="align-middle"> <a th:href="@{/post/{id}(id=${Post.id})}" th:text="${Post.title}"></a> </td> <td class="align-middle" th:text="${Post.name}"></td> <td class="align-middle" th:text="${Post.writeTime}"></td> <td class="text-center align-middle"> <a class="btn btn-primary" th:href="@{/post/{id}/revise(id=${Post.id})}">수정</a> <a href="#" th:href="'javascript:deletePost('+${Post.id}+')'" class="btn btn-danger">삭제</a><!--<button id="delete-btn" type="submit" class="btn btn-danger" th:onclick="deletePost([[ ${Post.id} ]]);">삭제</button>--> </td></tr></tbody> function deletePost(id) { if(confirm(id + "번 게시글을 삭제하시겠습니까?")) { const action = "/post/" + id + "/delete" let form = document.createElement("form"); form.setAttribute("method", "post"); form.setAttribute("action", action); document.body.appendChild(form); form.submit(); }}// var table = document.getElementById('PostTable');//// async function deletePost(id) {// url = "http://localhost:8080/post/" + id + "/delete";// console.log(url);// const response = await fetch(url, {// method: 'post'// });// }

0

Spring Boot 게시판 만들기 9 - 페이지 수정하기

9. 페이지 수정하기데이터를 수정하기 위해서는 수정하고자 하는 데이터를 찾아야 하기 때문에 id값이 필요하다. input태그의 type속성을 hidden으로해 form을 작성한 후 나머지 데이터와 함께 id값을 넘겨주도록 한다. post.html <h2>게시글 작성</h2><div class="card" style="padding: 20px; border-radius: 15px; margin: 20px auto;"> <form class=" form-horizontal" method="post" th:action="@{/post}" th:object="${postDto}"> <input type="hidden" th:if="*{id != null and id > 0}" th:field="*{id}"/> <div class="form-group"> <label>제목</label> <div class="col-sm-12"> <input type="text" style="border-radius: 5px;" class="form-control" th:field="*{title}" placeholder="제목을 입력해 주세요."/> </div> </div> <div class="form-group"> <label>이름</label> <div class="col-sm-12"> <input type="text" style="border-radius: 5px;" class="form-control" th:field="*{name}"placeholder="이름을 입력해 주세요."/> </div> </div> <div class="form-group"> <label>내용</label> <div class="col-sm-12"> <textarea class="form-control" style="height: 300px; border-radius: 5px;" th:field="*{content}" placeholder="내용을 입력해 주세요."></textarea> </div> </div> <div class="btn_wrap text-center"> <a th:href="@{/}" class="btn btn-default waves-effect waves-light">뒤로가기</a> <button type="submit" class="btn btn-primary waves-effect waves-light">저장하기</button> </div> </form></div> 수정페이지로 이동하기데이터를 수정위해서 URL경로를 통해 수정할 데이터의 id값을 받아온 후 해당 id값을 이용해 데이터를 조회한 뒤 기존 데이터를 반환하도록 한다. 내부 로직은 상세페이지를 가져오는 것과 똑같고 반환하는 템플릿만 다르게 반환한다. PostController.java @GetMapping("/post/{postId}/revise")public String getPostDetailsToRevise(@PathVariable("postId") Long id, Model model) { Post post = postService.getPostById(id); PostDto postDto = PostDto.builder() .id(post.getId()) .name(post.getName()) .title(post.getTitle()) .content(post.getContent()) .writeTime(post.getWriteTime()) .build(); model.addAttribute("postDto", postDto); return "post";} PostControllerTest.java

0

Spring Boot 게시판 만들기 8 - 상세 페이지

<!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><head th:replace="/fragments/head :: main-head"></head><body class="bg-light"><nav th:replace="/fragments/navbar :: main-nav"></nav><div class="container col-lg-6 "> <h2>상세 페이지</h2> <div class="card" style="padding: 20px; border-radius: 15px; margin: 20px auto;"> <form class=" form-horizontal" method="post" th:object="${postDto}"> <h1 th:text="*{title}">제목</h1> <span>작성자 : </span> <span th:text="*{name}">Tester </span> <hr> <p class="lead" style="height: 300px; border-radius: 5px;" th:text="*{content}"></p> <hr> <div class="text-right" style="padding-right: 10px;"> <span class="text-right">작성일 : </span> <span class="text-right" th:text="*{writeTime}">작성일 </span> </div> <hr> <div class="btn_wrap text-center"> <a href="#" class="btn btn-default waves-effect waves-light">뒤로가기</a> <button type="submit" class="btn btn-primary waves-effect waves-light">수정하기</button> <button type="submit" class="btn btn-danger waves-effect waves-light">삭제하기</button> </div> </form> </div></div></body></html>

0

Spring Boot 게시판 만들기 7 - 특정 페이지를 가져오기

7. 특정 포스트를 불러오기특정 포스트 불러오기 요청 처리하기 PostController @GetMapping("/post/{postId}")public String getPostDetails(@PathVariable("postId")Long id, Model model){ Post post = postService.getPostById(id); PostDto postDto = PostDto.builder() .id(post.getId()) .name(post.getName()) .title(post.getTitle()) .content(post.getContent()) .writeTime(post.getWriteTime()) .build(); model.addAttribute("postDto", postDto); return "details";} PostControllerTest.java @Test@DisplayName("상세 페이지를 가져온다.")public void getPostDetails() throws Exception{ Post mockPost = Post.builder() .id(1L) .name("tester") .title("test") .content("test") .writeTime(LocalDateTime.now()) .build(); given(postService.getPostById(any())).willReturn(mockPost); ResultActions resultActions = mockMvc.perform(get("/post/1")); resultActions .andExpect(status().isOk()); verify(postService).getPostById(any());} PostControllerTest.java @Test@DisplayName("상세 페이지를 못 가져온다.")public void getPostDetailsException() throws Exception{ given(postService.getPostById(any())).willThrow(new PostNotExistedException(1L)); ResultActions resultActions = mockMvc.perform(get("/post/1")); resultActions .andExpect(status().isNotFound()); verify(postService).getPostById(any());} 포스트가 없는 경우에 대한 예외처리하기

0

Spring Boot 게시판 만들기 6 - 게시판 페이지 만들기

게시판 페이지 템플릿게시판 페이지도 게시판 작성페이지와 똑같이 Bootstrap 과 BootWatch 를 이용해 만들었다. <!DOCTYPE html><html lang="ko"><head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="stylesheet" href="./css/bootstrap.css"> <link rel="stylesheet" href="./css/custom.min.css"> <title>게시글 작성 페이지</title></head><body class="bg-light"><nav class="navbar navbar-expand-lg navbar-dark bg-primary fixed-top"> <a class="navbar-brand" href="#">게시판</a></nav><div class="container col-lg-6 "> <form class="form-inline my-2 my-lg-0 float-right" style="padding-bottom : 16px;"> <input class="form-control mr-sm-2" type="text" placeholder="Search"> <button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button> </form> <div class="table-responsive clearfix"> <table class="table table-hover"> <thead> <tr> <th>번호</th> <th>제목</th> <th>작성자</th> <th>등록일</th> </tr> </thead> <tbody> <tr> </tr> </tbody> </table> <div class="btn_wrap text-right"> <a href="#" class="btn btn-primary waves-effect waves-light">Write</a> </div> <div class="text-center "> <ul class="pagination" style="justify-content: center;"> <li class="page-item disabled"> <a class="page-link" href="#">&laquo;</a> </li> <li class="page-item active"> <a class="page-link" href="#">1</a> </li> <li class="page-item"> <a class="page-link" href="#">2</a> </li> <li class="page-item"> <a class="page-link" href="#">3</a> </li> <li class="page-item"> <a class="page-link" href="#">4</a> </li> <li class="page-item"> <a class="page-link" href="#">5</a> </li> <li class="page-item"> <a class="page-link" href="#">&raquo;</a> </li> </ul> </div> </div></div></body></html> 타임리프 문법 적용하기<!DOCTYPE html><html lang="ko" xmlns:th="http://www.thymeleaf.org"><head th:replace="fragments/head :: main-head"/><body class="bg-light"><nav th:replace="fragments/navbar :: main-nav"/><div class="container col-lg-6 "> <form class="form-inline my-2 my-lg-0 float-right" style="padding-bottom : 16px;"> <input class="form-control mr-sm-2" type="text" placeholder="Search"> <button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button> </form> <div class="table-responsive clearfix"> <table class="table table-hover"> <thead class="text-center"> <tr> <th>번호</th> <th>제목</th> <th>작성자</th> <th>등록일</th> <th >수정/삭제</th> </tr> </thead> <tbody class="text-center"> <tr th:each="Post:${PostList}"> <td class="align-middle" th:text="${PostStat.index+1}"></td> <td class="align-middle"> <a th:text="${Post.title}"></a> </td> <td class="align-middle" th:text="${Post.name}"></td> <td class="align-middle" th:text="${Post.writeTime}"></td> <td class="text-center align-middle"> <button class="btn btn-primary">수정</button> <button class="btn btn-danger">삭제</button> </td> </tr> </tbody> </table> <div class="btn_wrap text-right"> <a href="#" class="btn btn-primary waves-effect waves-light">Write</a> </div> <div class="text-center "> <ul class="pagination" style="justify-content: center;"> <li class="page-item disabled"> <a class="page-link" href="#">&laquo;</a> </li> <li class="page-item active"> <a class="page-link" href="#">1</a> </li> <li class="page-item"> <a class="page-link" href="#">2</a> </li> <li class="page-item"> <a class="page-link" href="#">3</a> </li> <li class="page-item"> <a class="page-link" href="#">4</a> </li> <li class="page-item"> <a class="page-link" href="#">5</a> </li> <li class="page-item"> <a class="page-link" href="#">&raquo;</a> </li> </ul> </div> </div></div></body></html>

0

Spring Boot 게시판 만들기 5 - 모든 Post 가져오기

5. 모든 포스트 가져오기모든 포스트 가져오기 요청 처리하기BoardController.java @Controller@RequiredArgsConstructorpublic class BoardController { private final PostService postService; @GetMapping("/") public String board(Model model){ List<Post> posts = postService.getAllPost(); model.addAttribute("PostList", posts); return "board"; }} 모든 Post 가져오기 요청에 대한 테스트 코드 작성BoardControllerTest.java @WebMvcTestclass BoardControllerTest { @Autowired private MockMvc mockMvc; @MockBean private PostService postService; @Test @DisplayName("모든 Post를 가져온다.") public void board() throws Exception { List<Post> posts = new ArrayList<>(); posts.add(Post.builder() .id(1L) .title("test") .name("tester") .content("test") .writeTime(LocalDateTime.now()) .build() ); given(postService.getAllPost()).willReturn(posts); ResultActions resultActions = mockMvc.perform(get("/")); resultActions .andExpect(status().isOk()) .andDo(print()); verify(postService).getAllPost(); }} 데이터 조회@Service@RequiredArgsConstructorpublic class PostService { private final PostRepository postRepository; public Post addPost(Post newPost) { return postRepository.save(newPost); } public List<Post> getAllPost() { return postRepository.findAll(); }}

0

Spring Boot 게시판 만들기 4 - My SQL과 연동하기

4. My SQL과 연동의존성 추가My Sql과 연동하기 위해서는 Springboot프로젝트에 의존성을 추가해야 한다. build.gradle implementation 'mysql:mysql-connector-java' 데이터베이스 접속 설정하기application-mysql.yml spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/[DB]?serverTimezone=UTC&characterEncoding=UTF-8 username: [DB 접속 Id] password: [DB 접송 Password] 접속 설정 가져오기

0

Spring Boot 게시판 만들기 3 - 게시글 작성하기

3. 새로운 Post 생성 요청 처리하기데이터를 저장하는 로직은 DB에 접근을 하기 때문에 다음과 같은 로직을 추가해 DB에 데이터들이 제대로 저장되는지 확인한다. 어플리케이션을 실핸한 후 http://localhost:8080/h2-console 를 통해 데이터 베이스에 접근할 수 있다. application.yml spring: h2: console: enabled: true path: /h2-console datasource: username: sa password: url: jdbc:h2:mem:testdb driver-class-name: org.h2.Driver /post경로로 post 요청이 들어올 경우 입력된 정보들이 제대로 들어왔는지 Validation을 진행하고 문제가 없을 경우 데이터를 저장한다. PostController.java @PostMapping("/post")public String creatNewPost(@Valid @ModelAttribute("postDto") PostDto postDto) { postService.addPost(postDto); return "redirect:/";} 생성 요청 테스트 코드 작성하기MockBean 추가하기

0

Spring Boot 게시판 만들기 2 - 게시글 작성페이지 만들기

2. 게시글 작성페이지 설계도메인 설계하기사용자로부터 게시글 제목, 작성자 이름, 게시글 내용과 게시글들을 구별해주기 위해 Id값과 게시글이 언제 작성되었는지 알기 위한 작성시간을 추가해 도메인을 만든다. Post.java @Entity@NoArgsConstructor@AllArgsConstructor@Builder@Getterpublic class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotEmpty private String title; @NotEmpty private String name; @NotEmpty private String content; @NotNull private LocalDateTime writeTime;} DTO 작성하기사용자로부터 Form을 통해 title, name, content를 입력 받는다. Post 객체를 사용해 데이터를 전달받을 수 있지만 View로직이 추가되게 된다. Entity는 DB와 데이터를 주고 받는 객체이기에 DTO를 만들어 Entity를 View로직으로부터 분리해 추후 생길 수 있는 문제를 없애도록 한다.

0

Spring Boot 게시판 만들기 1 - 게시글 작성 페이지

1. 게시글 작성 페이지 만들기간단하게 게시글 제목, 사용자 이름, 게시글 내용 세가지만 입력 받는 폼을 만들 것이다. 게시글 작성페이지는 Bootstrap을 이용해 만들건데, BootsWatch의 Cosmo navbar가 마음에 들어 이것도 사용할 것이다. 먼저, Bootstrap 사이트에서 css, js, fonts를 다운 받고 BootsWatch사이트 Cosmo 페이지에서 custom.min.css을 다운 받은 후 springboot 프로젝트내의 resources/static위치에 폴더들은 넣는다. Bootstrap 사이트 : https://getbootstrap.com/docs/4.6/getting-started/introduction/Bootswatch 사이트 : https://bootswatch.com/