Category: Spring

0

[Spring Core] - 스프링 컨테이너 생성과 Bean 등록, 의존성 주입

목차 [Spring Core] - 의존성 주입 방식 [Spring Core] - 컴포넌트 스캔과 의존성 주입 [Spring Core] - BeanDefinition [Spring Core] - 스프링 빈 [Spring Core] - 스프링 컨테이너 생성과 Bean 등록, 의존성 주입 [Spring Core] - 스프링 컨테이너 스프링 컨테이너 생성 과정과 Bean 등록, 의존성 주입스프링 실행시 스프링 컨테이너가 생성되면 전달 받은 설정 정보를 이용해 스프링 빈을 생성 및 등록 합니다. 빈 등록이 완료된 후에는 설정 정보를 참고해 의존관계를 주입합니다. 이렇게 스프링은 빈을 생성하고 의존관계를 주입하는 관계가 나눠져 있습니다. 하지만 자바 코드로 빈을 등록하게 되면 객체의 생성자를 호출하면서 의존관계 주입까지 한번에 이뤄지게 됩니다. 1. 스프링 컨테이너 생성스프링 컨테이너를 생성할 때는 구성 정보를 지정해주어야 합니다. 구성 정보는 자바 클래스나 xml 을 이용해 정의할 수 있습니다. 스프링 컨테이너 생성시 설정 정보를 이용해 스프링 컨테이너를 생성합니다. 아래에서는 자바 클래스 AppConfig.class 를 설정 정보로 이용해 스프링 컨테이너를 생성합니다. //스프링 컨테이너 생성ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

0

[Spring Core] - 스프링 빈

목차 [Spring Core] - 의존성 주입 방식 [Spring Core] - 컴포넌트 스캔과 의존성 주입 [Spring Core] - BeanDefinition [Spring Core] - 스프링 빈 [Spring Core] - 스프링 컨테이너 생성과 Bean 등록, 의존성 주입 [Spring Core] - 스프링 컨테이너 스프링 빈 스프링 IoC 컨테이너에 의해 생성되고 관리되는 객체를 보고 스프링 빈이라 부릅니다. Bean 객체는 스프링 컨테이너에 의해 생성 및 관리되며 일반적으로 싱글톤 객체로 생성됩니다. Bean 끼리 의존관계가 있을 경우 의존성 주입을 통해 사용할 수 있습니다. 스프링 컨테이너에 객체를 Bean 으로 등록하는 방법 XML 에 직접 등록하는 방법 @ComponentScan 을 이용해 자동적으로 Bean 을 등록하는 방법 @Configuration 과 @Bean 을 이용하여 직접적으로 등록을 하면 된다. Bean 어노테이션으로 정의할 때는 @Configuration 가지고 있는 클래스 안에 정의해야 한다. @Configuration 안에서 @Bean 을 사용해야 싱글톤을 보장 받을 수 있습니다. XML 을 이용해 Bean 등록하기최근 스프링 부트의 등장으로 xml 을 이용해 스프링을 설정하는 방법은 잘 사용하지 않는 방식입니다. xml 을 이용해 스프링 컨테이너를 설정하는 것의 장점은 컴파일 없이 빈 설정 정보를 변경할 수 있다는 점이다.

0

[Spring Core] - 스프링 컨테이너

목차 [Spring Core] - 의존성 주입 방식 [Spring Core] - 컴포넌트 스캔과 의존성 주입 [Spring Core] - BeanDefinition [Spring Core] - 스프링 빈 [Spring Core] - 스프링 컨테이너 생성과 Bean 등록, 의존성 주입 [Spring Core] - 스프링 컨테이너 스프링 컨테이너와 IoC(Inversion of Control)객체의 생성, 객체간의 의존성 과 같은 프로그램의 흐름 을 개발자가 아니라 프레임워크나 외부 컨테이너에가 관리 해주는 것을 의미합니다. 즉, 프로그램에 대한 제어권이 개발자가 아니라 프레임워크나 외부 컨테이너에서 관리가 됩니다. 스프링의 핵심요소인 스프링 컨테이너는 객체의 생성과 관리, 의존성 주입, 생명주기등을 해주기 때문에 IoC 컨테이너 라고 불립니다. 스프링 컨테이너의 주요 기능스프링 컨테이너는 객체를 생성하고 컨테이너에 등록합니다. 이때, 스프링 컨테이너에 등록된 객체들을 스프링 Bean 이라 부르고 컨테이너는 등록된 Bean 의 생성, 초기화, 소멸까지의 전체적인 생명주기를 관리합니다. 스프링 Bean 들이 생성된 후 스프링 컨테이너는 객체간의 관계를 확인 후 설정에 따라 의존성을 주입합니다. Bean 으로 등록되지 않은 객체에 대해서는 의존성 주입이 이뤄지지 않습니다.

0

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

15. 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 게시판 만들기 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(); }}