비트교육센터/Spring

[비트교육센터][Spring] 스프링 34일차 스프링부트(2)

달의요정루나 2023. 7. 18. 09:20

1. 스프링부트(Spring Boot)

https://julian5383.tistory.com/215

 

[비트교육센터][Spring] 스프링 33일차 RestAPI, 스프링부트

1. RestAPI(2) 1. @RestController [1] RestSvcController.java package com.julian5383.controller; import java.util.HashMap; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.

julian5383.tistory.com

- 해당 게시물 스프링부트 list.html 이후부터 이어진다.

1. html 띄우기

[1] write.html

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>글 쓰기</title>
<script th:inline="javascript">
    function backToList(frm){//thymeleaf에서 url 또는 변수를 주는 법
        frm.action=/*[[@{/board/list}]]*/
        frm.submit();
    }
</script>
<style>
    .class-caption {
        width:100px;
    }
    .class-content {
        width:500px;
    }
</style>
</head>
<body>
    <h1 style="text-align: center;">글 쓰기</h1>
    <form name="articleForm" method="post" th:action="@{/board/addArticle}">
        <table border="0" align="center">
            <tbody>
                <tr>
                    <td align="right" class="class-caption">글제목: </td>
                    <td colspan="2">
                        <input type="text" maxlength="100" name="i_title" class="class-content">
                    </td>
                </tr>
                <tr>
                    <td align="right" valign="top" class="class-caption">글내용: </td>
                    <td colspan="2">
                        <textarea name="i_content" rows="10" maxlength="2000" class="class-content"></textarea>
                    </td>
                </tr>
                <tr>
                    <td align="right"></td>
                    <td colspan="2">
                        <input type="submit" value="저장">
                        <input type="button" value="목록보기" onclick="backToList(articleForm)">
                    </td>
                </tr>
            </tbody>
        </table>
    </form>
</body>
</html>

- 글 작성 html이다.

[2] view.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상세 조회</title>
<script th:inline="javascript">
    function backToList(frm){
        frm.action=/*[[@{/board/list}]]*/
        //contextPath = board
        frm.submit();
    }
    function fn_enable(frm){
        document.querySelector('#i_title').disabled=false;
        document.querySelector('#i_content').disabled=false;
        document.querySelector('#tr_btn').style.display='none';
        document.querySelector('#tr_btn_modify').style.display='table-row';
    }
    function fn_modify_article(frm){
        frm.action=/*[[@{/board/edit}]]*/
        frm.submit();
    }
    function fn_remove(url, no){
        let form = document.createElement("form");
        form.setAttribute("method","post");
        form.setAttribute("action",url);
        let input = document.createElement("input");
        input.setAttribute("type","hidden");
        input.setAttribute("name","articleNo");
        input.setAttribute("value",no);
        form.appendChild(input);
        document.body.appendChild(form);
        form.submit();
    }
</script>
<style>
    #tr_btn_modify{
        display:none;
    }
</style>
</head>
<body>
    <form name="articleForm" method="post" action="">
        <table border="0" align="center">
            <tbody>
                <tr>
                    <td width="150" align="center" bgcolor="#FF9933">글번호</td>
                    <td>
                        <input type="text" th:value="${article.article_no}" disabled>
                        <input type="hidden" th:value="${article.article_no}" name="articleNo">
                    </td>
                </tr>
                <tr>
                    <td width="150" align="center" bgcolor="#FF9933">작성자</td>
                    <td>
                        <input type="text" th:value="${article.write_id}" name="writer" disabled>
                    </td>
                </tr>
                <tr>
                    <td width="150" align="center" bgcolor="#FF9933">제목</td>
                    <td>
                        <input type="text" th:value="${article.title}" name="title" id="i_title" disabled>
                    </td>
                </tr>
                <tr>
                    <td width="150" align="center" bgcolor="#FF9933">내용</td>
                    <td>
                        <textarea rows="20" cols="60" name="content" id="i_content" disabled>
                            [[${article.content}]]
                        </textarea>
                    </td>
                </tr>
                <tr>
                    <td width="20%" align="center" bgcolor="#FF9933">작성일</td>
                    <td>
                        <input type="text" th:value="${article.write_date}" disabled>
                    </td>
                </tr>
                <tr id="tr_btn">
                    <td colspan="2" align="center">
                        <input type="button" value="수정" onclick="fn_enable(articleForm)">
                        <input type="button" value="삭제" 
                        th:onclick="|fn_remove('@{/board/remove}','${article.article_no}')|">
                        <input type="button" value="목록보기" onclick="backToList(articleForm)">
                    </td>
                </tr>
                <tr id="tr_btn_modify">
                    <td colspan="2" align="center">
                        <input type="button" value="저장" onclick="fn_modify_article(articleForm)">
                        <input type="button" value="취소" onclick="backToList(articleForm)">
                    </td>
                </tr>
                <!--inner html은 대괄호 2개를 써야함 -->
            </tbody>
        </table>
    </form>
</body>
</html>

- 게시물 상세페이지 이다.

--> 결과

1) 게시글 추가

리스트 페이지이다. 아래 글쓰기 버튼을 누르면 글쓰기 페이지로 이동한다.
글쓰기 페이지로 이동했다.
게시물을 작성하고 저장을 클릭한다.
게시물이 입력된 것을 볼 수 있다.

2) 게시글 조회

제목 컬럼에 있는 글 하나를 클릭시 상세페이지로 이동한다.

3) 게시글 수정

게시물 상세페이지에서 수정을 누르면 제목과 내용의 disabled가 풀린다.
제목과 내용을 바꾸어 주고 저장을 누른다.
게시물이 수정된 모습이다.
리스트 페이지로 나와보면 제목이 변경된 것을 볼수 있다.

4) 게시글 삭제

상세페이지로 들어가서 삭제를 누른다.
리스트 페이지를 보면삭제된 것을 볼 수 있다.

2. 사진 띄우기

static 폴더 밑에 image 폴더를 선언하고 이미지를 넣는다.

[1] BoardController.java

package com.julian5383.controller;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

public interface BoardController {
	public String getArticleList(Model model) throws Exception;
	
	public String writeArticle() throws Exception;
	
	public String addArticle(@RequestParam(value="i_title")String title,
			@RequestParam(value="i_content")String content) throws Exception;
	
	public ModelAndView viewArticle(@RequestParam(value="no")String articleNo) throws Exception;
	
	public String editArticle(@RequestParam(value="articleNo") String articleNo,
			@RequestParam(value="title") String title,
			@RequestParam(value="content") String content, RedirectAttributes redirectAttr) throws Exception;
	
	public String removeArticle(@RequestParam(value="articleNo") String articleNo) throws Exception;
	
	//사진 띄우기 메소드
	public String viewImage() throws Exception;
}

- 맨 아래에 viewImage라는 추상메소드를 선언한다.

[2] MainController.java

package com.julian5383.controller;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.julian5383.model.OnboardDto;
import com.julian5383.service.BoardService;

@Controller("boardController")
@RequestMapping("/board")
public class MainController implements BoardController{

	@Autowired
	BoardService boardService;
	@Autowired
	OnboardDto onboardDto;
	
	List<OnboardDto> articleList;
	
	Logger logger = LoggerFactory.getLogger("com.julian5383.controller.MainController");
	
	@Override
	@RequestMapping({"/list", "/"})
	public String getArticleList(Model model) throws Exception {
		// TODO Auto-generated method stub
		articleList = boardService.listArticles();
		model.addAttribute("data_list",articleList);
		return "list";
	}

	@Override
	@RequestMapping("/add")
	public String writeArticle() throws Exception {
		// TODO Auto-generated method stub
		return "write";
	}

	@Override
	@PostMapping("/addArticle")
	public String addArticle(String title, String content) throws Exception {
		// TODO Auto-generated method stub
		onboardDto.setTitle(title);
		onboardDto.setContent(content);
		onboardDto.setWrite_id("bit");
		boardService.addArticle(onboardDto);
		
		return "redirect:list";
	}

	@Override
	@GetMapping("/view")
	public ModelAndView viewArticle(String articleNo) throws Exception {
		// TODO Auto-generated method stub
		onboardDto = boardService.viewArticle(Integer.parseInt(articleNo));
		ModelAndView mv = new ModelAndView();
		mv.setViewName("view");
		mv.addObject("article", onboardDto);
		logger.trace("trace==>articleNo: "+articleNo);
		logger.debug("debug==>articleNo: "+articleNo);
		logger.info("info==>articleNo: "+articleNo);
		logger.warn("warn==>articleNo: "+articleNo);
		logger.error("error==>articleNo: "+articleNo);
		return mv;
	}

	@Override
	@PostMapping("/edit")
	public String editArticle(String articleNo, String title, String content, RedirectAttributes redirectAttr)
			throws Exception {
		// TODO Auto-generated method stub
		onboardDto.setArticle_no(Integer.parseInt(articleNo));
		onboardDto.setTitle(title);
		onboardDto.setContent(content);
		boardService.editArticle(onboardDto);
		redirectAttr.addAttribute("no", articleNo);
		return "redirect:view";
	}

	@Override
	@PostMapping("/remove")
	public String removeArticle(String articleNo) throws Exception {
		// TODO Auto-generated method stub
		boardService.removeArticle(Integer.parseInt(articleNo));
		return "redirect:list";
	}
	//인터페이스에 html에서 끌어와야할 name태그르 딸 기록,메소드 처리 내용만 기록, 브라우저 상에 name태그를 따로 쓸 필요 없음.

	//사진 띄우기 메소드
	@Override
	@GetMapping("/show")
	public String viewImage() throws Exception {
		// TODO Auto-generated method stub
		return "show";
	}
}

- 아래세 viewImage라는 메소드를 선언한다. 주소에 show를 입력시 이동할 수 있다. 

[3] show.html

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>show image</title>
</head>
<body>
    <img th:src="@{/image/rabbit.jpg}" width="400" height="500">
</body>
</html>
<!--static 밑에 위치-->

- img 태그에  resources 경로에 있는 image 폴더의 rabbit.jpg 사진을 가져오도록한다.

--> 결과

사진이 출력된 것을 확인할 수 있다.

3. 이미지 바꾸기

이미지 하나를 더 삽입한다.

[1] BoardController.java

package com.julian5383.controller;

import java.util.Map;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

public interface BoardController {
	public String getArticleList(Model model) throws Exception;
	
	public String writeArticle() throws Exception;
	
	public String addArticle(@RequestParam(value="i_title")String title,
			@RequestParam(value="i_content")String content) throws Exception;
	
	public ModelAndView viewArticle(@RequestParam(value="no")String articleNo) throws Exception;
	
	public String editArticle(@RequestParam(value="articleNo") String articleNo,
			@RequestParam(value="title") String title,
			@RequestParam(value="content") String content, RedirectAttributes redirectAttr) throws Exception;
	
	public String removeArticle(@RequestParam(value="articleNo") String articleNo) throws Exception;
	
	//사진 띄우기 메소드
	public String viewImage() throws Exception;
	
	//사진 바꾸기 메소드
	public Map<String, String> changeImage() throws Exception;
}

- Map으로 changeImage 메소드를 선언한다.

[2] MainController.java

package com.julian5383.controller;

import java.util.HashMap;
import java.util.List;
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.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.julian5383.model.OnboardDto;
import com.julian5383.service.BoardService;

@Controller("boardController")
@RequestMapping("/board")
public class MainController implements BoardController{

	@Autowired
	BoardService boardService;
	@Autowired
	OnboardDto onboardDto;
	
	List<OnboardDto> articleList;
	
	Logger logger = LoggerFactory.getLogger("com.julian5383.controller.MainController");
	
	@Override
	@RequestMapping({"/list", "/"})
	public String getArticleList(Model model) throws Exception {
		// TODO Auto-generated method stub
		articleList = boardService.listArticles();
		model.addAttribute("data_list",articleList);
		return "list";
	}

	@Override
	@RequestMapping("/add")
	public String writeArticle() throws Exception {
		// TODO Auto-generated method stub
		return "write";
	}

	@Override
	@PostMapping("/addArticle")
	public String addArticle(String title, String content) throws Exception {
		// TODO Auto-generated method stub
		onboardDto.setTitle(title);
		onboardDto.setContent(content);
		onboardDto.setWrite_id("bit");
		boardService.addArticle(onboardDto);
		
		return "redirect:list";
	}

	@Override
	@GetMapping("/view")
	public ModelAndView viewArticle(String articleNo) throws Exception {
		// TODO Auto-generated method stub
		onboardDto = boardService.viewArticle(Integer.parseInt(articleNo));
		ModelAndView mv = new ModelAndView();
		mv.setViewName("view");
		mv.addObject("article", onboardDto);
		logger.trace("trace==>articleNo: "+articleNo);
		logger.debug("debug==>articleNo: "+articleNo);
		logger.info("info==>articleNo: "+articleNo);
		logger.warn("warn==>articleNo: "+articleNo);
		logger.error("error==>articleNo: "+articleNo);
		return mv;
	}

	@Override
	@PostMapping("/edit")
	public String editArticle(String articleNo, String title, String content, RedirectAttributes redirectAttr)
			throws Exception {
		// TODO Auto-generated method stub
		onboardDto.setArticle_no(Integer.parseInt(articleNo));
		onboardDto.setTitle(title);
		onboardDto.setContent(content);
		boardService.editArticle(onboardDto);
		redirectAttr.addAttribute("no", articleNo);
		return "redirect:view";
	}

	@Override
	@PostMapping("/remove")
	public String removeArticle(String articleNo) throws Exception {
		// TODO Auto-generated method stub
		boardService.removeArticle(Integer.parseInt(articleNo));
		return "redirect:list";
	}
	//인터페이스에 html에서 끌어와야할 name태그르 딸 기록,메소드 처리 내용만 기록, 브라우저 상에 name태그를 따로 쓸 필요 없음.

	//사진 띄우기 메소드
	@Override
	@GetMapping("/show")
	public String viewImage() throws Exception {
		// TODO Auto-generated method stub
		return "show";
	}

	@Override
	@PostMapping("change")
	@ResponseBody
	public Map<String, String> changeImage() throws Exception {
		// TODO Auto-generated method stub
		Map<String, String> map= new HashMap<String, String>();
		map.put("file", "/image/cat.jpg");
		map.put("name","cat");
		logger.info("=====> "+map.get("file"));
		return map;
	}
}

- changeImage 메소드를 선언한다.

- @PostMapping("change"): change 경로로 POST 요청이 들어오면 changeImage 메소드를 실행한다.

- @ResponseBody: RESTful API 개발에 이용되며 해당 어노테이션이 메소드에 지정되면 문자열을 그대로 본문에 전송한다.

- 메소드는 HashMap<String, String> 객체를 생성하고, "file" 키에는 "/image/cat.jpg" 값을, "name" 키에는 "cat" 값을 저장합니다. 이후, 로그에 "=====> /image/cat.jpg"를 출력하고, 최종적으로 맵을 반환한다.

[3] show.html

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>show image</title>
    <script th:inline="javascript">
        async function changeImg(frm){
            let formData = new FormData(frm);

            const options = {
                method:"post",
                body: formData
            }

            try {
                let response = await fetch(/*[[@{/board/change}]]*/ '',options);
                let result = await response.json();
                let img = document.querySelector('#img_tag');
                img.src = result.file;
                let input_tag=document.querySelector('#pic_name');
                input_tag.value = result.name;
            } catch (error) {
                alert(error);
            }
        }
    </script>
</head>
<body>
    <form id="img_form">
        <img id="img_tag" th:src="@{/image/rabbit.jpg}" width="400" height="500"><br>
        <input id="pic_name" type="text"><br>
        <input type="button" value="이미지 바꾸기" onclick="changeImg(img_form)">
    </form>
</body>
</html>
<!--static 밑에 위치-->

-  이미지를 변경하는 HTML 페이지이다.

- 먼저 th:src="@{/image/rabbit.jpg}" 태그를 통해 해당 html을 실행시 먼저 토끼사진을 띄운다.

- <input> 태그: id="pic_name"으로 지정되어 있으며, 이미지 이름을 입력하는 역할을 한다.

- changeImg() 함수: 이미지를 변경하기 위한 JavaScript 함수이다.

 1) async 키워드를 사용하여 비동기 함수로 정의되어 있다. 원하는 때에 동작을 시작할 수 있다.

 2) FormData 객체를 사용하여 <form> 요소의 데이터를 수집하고, fetch() 함수를 사용하여 서버로 데이터를 전송한다.

 3) response.json(): 서버로부터 받은 응답을 JSON 형식으로 해석하고 출력한다.

 

- ajax를 이용할 수 있지만 오래된 코드여서 쓰지 않았다.

- 모던 자바스크립트, 해당 사이트를 참고했다.

https://ko.javascript.info/async

 

프라미스와 async, await

 

ko.javascript.info

 

--> 결과

첫화면은 토끼사진이지만 이미지 바꾸기 버튼을 누르면
input 상자에 문자가 뜨면서 사진이 바뀐다.

3. jar 파일 배포

- 스프링부트는 톰캣을 내장해서 지난때 war 파일을 선언할 필요 없다.

- jar 파일 배포가 권장사항

먼저 Maven clean을 해준다.
그 다음 Maven install을 해준다.
그러면 해당 프로젝트의 target 폴더에 jar 파일이 생성되어 있다.
명령 프롬포트를 접근하기 쉬운 경로로 옮겨준다.
작성하기 편하게 jar 파일 이름을 바꾸었다.
명령 프롬포트를 실행해 E 드라이브로 경로를 바꾼후 java -jar myboard.jar이라고 입력해준다. jar 파일을 실행하는 명령어이다.
해당 jar 파일이 실행되어진다.
브라우저 주소창에 스프링부트로 실행할때 처럼 실행시 똑같이 실행된다.
출력된 화면이다.

- warning이 뜰때 신경쓰인다면

맨 아래를 보면 WARNING이 뜬다. 실행에는 문제가 안되지만 신경쓰일시 안띄울수 있다.
Properties로 들어간다.
Maven 카테고리로 들어가서 해당 체크박스를 해제하고 Apply and Close를 해주면 된다.