비트교육센터/Spring

[비트교육센터][Spring] 스프링 32일차 jsp 게시판 만들기(2), SLF4J, WAV 배포, REST API

달의요정루나 2023. 7. 13. 21:07

1. jsp 게시판 만들기(수정, 삭제 추가)

1. noticeboard.xml

<?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 = "mapper.notice">
	<resultMap id="noticeResult" type="onboardDto">
		<result property="article_no" column="article_no"/>
		<result property="title" column="title"/>
		<result property="content" column="content"/>
		<result property="write_date" column="write_date"/>
		<result property="write_id" column="write_id"/>
		<!-- property가 dto의 필드명과 일치해야함,
		column은 데이터베이스의 칼럼명과 정확히 일치해야함 -->
	</resultMap>

	<select id="selectAllArticles" resultMap="noticeResult">
		<![CDATA[
			select * from noticeboard order by write_date desc
		]]> <!-- xml로 인지하지 않고 패스한다. -->
	</select>
	<insert id="insertArticle" parameterType="onboardDto">
		<![CDATA[
			INSERT INTO noticeboard (title, content, write_date, write_id)
			values(#{title},#{content},default,#{write_id})
		]]>
	</insert>
	<select id="selectArticle" resultType="onboardDto" parameterType="int">
		<![CDATA[
			select * from noticeboard where article_no=#{article_no}
		]]> <!-- xml로 인지하지 않고 패스한다. -->
	</select>
	<update id="updateArticle" parameterType="onboardDto">
		<![CDATA[
			update noticeboard set title=#{title}, content=#{content} where article_no=#{article_no}
		]]>
	</update>
	<delete id="deleteArticle" parameterType="int">
		<![CDATA[
			delete from noticeboard where article_no=#{article_no}
		]]>
	</delete>
</mapper>

- update와 delete 쿼리를 추가한다.

 

2. BoardDao.java

package com.julian5383.model;

import java.io.Reader;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class BoardDao {
	private static SqlSessionFactory sessionFactory = null;

	public static SqlSessionFactory getInstance() {
		if (sessionFactory == null) {
			try {
				String resource = "mybatis-config.xml";
				Reader reader = Resources.getResourceAsReader(resource);
				sessionFactory = new SqlSessionFactoryBuilder().build(reader);
				reader.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return sessionFactory;
	}
	
	public List<OnboardDto> selectAllArticles(){
		sessionFactory = getInstance();
		SqlSession session = sessionFactory.openSession();
		
		List<OnboardDto> articleList = session.selectList("mapper.notice.selectAllArticles");
		session.close();
		return articleList;
	}
	
	public void insertArticle(OnboardDto onboardDto) {
		sessionFactory = getInstance();
		SqlSession session = sessionFactory.openSession();
		session.insert("mapper.notice.insertArticle",onboardDto);
		session.commit();
	}//세션: 스프링 애플리케이션과 데이터베이스의 연결통로
	
	public OnboardDto selectArticle(int articleNo) {
		sessionFactory = getInstance();
		SqlSession session = sessionFactory.openSession();
		OnboardDto article = session.selectOne("mapper.notice.selectArticle", articleNo);
		session.close();
		return article;
	}
	
	public void updateArticle(OnboardDto onboardDto) {
		sessionFactory = getInstance();
		SqlSession session = sessionFactory.openSession();
		session.update("mapper.notice.updateArticle",onboardDto);
		session.commit();
	}
	
	public void deleteArticle(int articleNo) {
		sessionFactory = getInstance();
		SqlSession session = sessionFactory.openSession();
		session.delete("mapper.notice.deleteArticle",articleNo);
		session.commit();
	}
}

- 게시글 수정을 하는 updateArticle메소드와 게시글 삭제를 하는 deleteArticle메소드를 추가한다.

3. BoardService.java

package com.julian5383.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import com.julian5383.model.BoardDao;
import com.julian5383.model.OnboardDto;

public class BoardService {
	@Autowired
	BoardDao boardDao;
	
	public List<OnboardDto> listArticles(){
		List<OnboardDto> articleList = boardDao.selectAllArticles();
		return articleList;
	}
	
	public void addArticle(OnboardDto onboardDto) {
		boardDao.insertArticle(onboardDto);
	}
	
	public OnboardDto viewArticle(int articleNo) {
		OnboardDto article = boardDao.selectArticle(articleNo);
		return article;
	}
	
	public void editArticle(OnboardDto onboardDto) {
		boardDao.updateArticle(onboardDto);
	}
	
	public void removeArticle(int articleNo) {
		boardDao.deleteArticle(articleNo);
	}
}

- Dao가 DB에서 받아온 수정, 삭제 데이터를 가공해 줄수 있는 수정(editArticle), 삭제(removeArticle) 메소드를 입력한다.

4. BoardController.java

package com.julian5383.controller;

import java.util.List;

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.RequestParam;
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
@RequestMapping("/board")
public class BoardController {
	@Autowired
	BoardService boardService;
	@Autowired
	OnboardDto onboardDto;
	
	List<OnboardDto> articleList;
	
	@RequestMapping({"/list", "/"})
	public String getArticleList(Model model) {
		articleList = boardService.listArticles();
		model.addAttribute("data_list",articleList);
		return "list";
	}
	
	@RequestMapping("/add")
	public String writeArticle() {
		return "write";
	}
	
	@PostMapping("/addArticle")
	public String addArticle(@RequestParam(value="i_title")String title, @RequestParam(value="i_content")String content) {
		onboardDto.setTitle(title);
		onboardDto.setContent(content);
		onboardDto.setWrite_id("bit");
		boardService.addArticle(onboardDto);
		
		return "redirect:list";
	}
	
	@GetMapping("/view")
	public ModelAndView viewArticle(@RequestParam(value="no")String articleNo) {
		onboardDto = boardService.viewArticle(Integer.parseInt(articleNo));
		ModelAndView mv = new ModelAndView();
		mv.setViewName("view");
		mv.addObject("article", onboardDto);
		return mv;
	}
	
	@PostMapping("/edit")
	public String editArticle(@RequestParam(value="articleNo") String articleNo,
			@RequestParam(value="title") String title,
			@RequestParam(value="content") String content, RedirectAttributes redirectAttr) {
            //view.jsp에서 activleNo, title, content name을 가진 태그의 값을 가져와 입력한다.
            //수정이기 때문에 해당 게시물 id를 가져와야해서 articleNo(input hidden에 있음)도 입력한다.
		onboardDto.setArticle_no(Integer.parseInt(articleNo));
		onboardDto.setTitle(title);
		onboardDto.setContent(content);
		boardService.editArticle(onboardDto);
		redirectAttr.addAttribute("no", articleNo);
        //no에 articleNo를 담아서 redirect시키기 위해 넣는다. (안넣으면 400에러가 뜨며 url 맨끝에no=번호가 안뜬다.)
        //ex)http://localhost:8080/board/board/view?no=7 에서 no=7에 들어가는 숫자를 말한다.
		return "redirect:view";
	}
	
	@PostMapping("/remove")
	public String removeArticle(@RequestParam(value="articleNo") String articleNo) {
		boardService.removeArticle(Integer.parseInt(articleNo));
		return "redirect:list";
	}
}

- 컨트롤러에 수정(editArticle), 삭제(removeArticle)메소드를 입력한다.

5. view.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<%
request.setCharacterEncoding("UTF-8");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상세 조회</title>
<script>
    function backToList(frm){
        frm.action="${contextPath}/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="${contextPath}/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);//input태그를 form태그의 자식요소로 만듦
        document.body.appendChild(form);//form태그를 body 태그의 자식요소로 만듦
        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" value="${article.article_no}" disabled>
                        <input type="hidden" value="${article.article_no}" name="articleNo">
                        <!--게시글 수정시 해당 게시글의 id를 가져와야 하기 때문에 선언한 input hidden이다.-->
                    </td>
                </tr>
                <tr>
                    <td width="150" align="center" bgcolor="#FF9933">작성자</td>
                    <td>
                        <input type="text" value="${article.write_id}" name="writer" disabled>
                    </td>
                </tr>
                <tr>
                    <td width="150" align="center" bgcolor="#FF9933">제목</td>
                    <td>
                        <input type="text" 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" 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="삭제" 
                        onclick="fn_remove('${contextPath}/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>
            </tbody>
        </table>
    </form>
</body>
</html>

--> 결과

1. 게시물 수정하기

1) 게시글 리스트 출력
2) 수정하기를 누른다.
3) 수정하기를 누르면 제목, 내용의 disabled가 풀린다.
4. 수정하려는 내용을 입력하고 저장을 누른다.
5) 목록보기를 누른다.
6) 수정된 게시물이 올라온 것을 볼수 있다.

2. 삭제

1) 삭제 버튼을 누른다.
2) 게시글이 삭제된 것을 볼 수 있다.

- 웹 서버 구조

 

2. SLF4J

board프로젝트에서 이어서 진행된다.

- Simple Logging Facade for Java의 줄임말로 logging 인터페이스를 제공하는 API이다.

- log4j, log4j2, JUL(Java Util Logging) 보다 우수한 성능을 보임

- Logback

[Layout]
%m : 로그내용이 출력
%p : trace > debug > info > warn > error 등의 priority 출력
%r : 어플리케이션이 시작되어 로깅이벤트가 발생하는 시점까지의 경과시간을 밀리세컨드로 출력
%c : 예) 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됩니다.
%n :  플랫폼 종속적인 개행문자가 출력된다. \r\n 또는 \n 일것이다
%d : 로깅이벤트가 일어나 날짜 출력 ( 프로그램의 실행속도를 느리게 한다.)
예) %d{HH:mm:ss} 또는 %d{dd MMMM yyyy HH:mm:ss}
%C : 호출자의 클래스명 출력
예) 클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면 %C{2}는 xyz.SomeClass 가 출력됩니다
%M : 로깅이 발생한 method 이름을 나타냅니다.
%F : 로깅이 발생한 프로그램 파일명을 나타냅니다.
%l : 로깅이 발생한 caller의 정보를 나타냅니다
%L : 로깅이 발생한 caller의 라인수를 나타냅니다
%x : 로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를 출력합니다.
%X : 로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를 출력합니다.
%% : % 표시를 출력하기 위해 사용한다.

%t : 로그이벤트가 발생된 쓰레드의 이름을 출력합니다

1. pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.julian5383</groupId>
  <artifactId>board</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <release>17</release>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.3</version>
      </plugin>
    </plugins>
  </build>
   <dependencies>
	<dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>javax.servlet-api</artifactId>
	    <version>4.0.1</version>
	    <scope>provided</scope>
	</dependency>
	<dependency>
	    <groupId>javax.servlet.jsp</groupId>
	    <artifactId>javax.servlet.jsp-api</artifactId>
	    <version>2.3.3</version>
	    <scope>provided</scope>
	</dependency>
	<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
	<dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>jstl</artifactId>
	    <version>1.2</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-webmvc</artifactId>
	    <version>5.3.28</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
	<dependency>
	    <groupId>mysql</groupId>
	    <artifactId>mysql-connector-java</artifactId>
	    <version>8.0.33</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
	<dependency>
	    <groupId>org.mybatis</groupId>
	    <artifactId>mybatis</artifactId>
	    <version>3.5.13</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
	<dependency>
	    <groupId>org.mybatis</groupId>
	    <artifactId>mybatis-spring</artifactId>
	    <version>2.1.1</version>
	</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
	<dependency>
	    <groupId>ch.qos.logback</groupId>
	    <artifactId>logback-classic</artifactId>
	    <version>1.4.8</version>
	</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
	<dependency>
	    <groupId>org.slf4j</groupId>
	    <artifactId>slf4j-simple</artifactId>
	    <version>1.7.36</version>
	    <!--<scope>test</scope>--><!-- 테스트 할때만 동작-->
	</dependency>

  </dependencies>
</project>

- board 프로젝트에 있는 pom.xml에 새로운 의존성을 추가한다.

1. logback-classic 1.4.8 maven을 입력한다.
2. SLF4J Simple Binding 1.7.36 maven을 입력한다.

2. logback.xml

resources 폴더에 logback.xml을 생성한다.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<property name="LOG_PATH" value="E:/tmp/logs" />
	<!-- log정보를 어디로 보낼지 결정, 폴더경로생성 -->
	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
	<!-- 이름을 stdout으로 설정, 터미널로 로그를 쏨 -->
		<target>System.out</target>
		<encoder>
			<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] [%-5level] %logger{35} - %msg%n</pattern>
		</encoder>
	</appender>
	<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
	<!-- 파일로 쏨 -->
		<file>${LOG_PATH}/logback.log</file>
		<append>true</append>
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] [%-5level] %logger{35} - %msg%n</pattern>
		</encoder>
		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
		<!-- info레벨부터 출력 -->
        	<level>INFO</level>
    	</filter>
	<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
	<!-- 크기와 시간을 베이스로 롤링을 한다. -->
		<fileNamePattern>${LOG_PATH}/logback.%d{yyyy-MM-dd}.%i.log.zip
		<!-- 하루가 바뀌면 해당 zip파일을 출력 -->
		</fileNamePattern>
		<maxFileSize>5MB</maxFileSize>
		<maxHistory>30</maxHistory>
		<!-- 30일이 지난 zip파일을 지우기 -->
	</rollingPolicy>
	</appender>


	<root level="debug">
		<appender-ref ref="ROLLING" />
		<appender-ref ref="STDOUT" />
	</root>
</configuration>

3. BoardController.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.bind.annotation.RequestParam;
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
@RequestMapping("/board")
public class BoardController {
	@Autowired
	BoardService boardService;
	@Autowired
	OnboardDto onboardDto;
	
	List<OnboardDto> articleList;
	
	Logger logger = LoggerFactory.getLogger("com.julian5383.controller.BoardController");
	
	@RequestMapping({"/list", "/"})
	public String getArticleList(Model model) {
		articleList = boardService.listArticles();
		model.addAttribute("data_list",articleList);
		return "list";
	}
	
	@RequestMapping("/add")
	public String writeArticle() {
		return "write";
	}
	
	@PostMapping("/addArticle")
	public String addArticle(@RequestParam(value="i_title")String title, @RequestParam(value="i_content")String content) {
		onboardDto.setTitle(title);
		onboardDto.setContent(content);
		onboardDto.setWrite_id("bit");
		boardService.addArticle(onboardDto);
		
		return "redirect:list";
	}
	
	@GetMapping("/view")
	public ModelAndView viewArticle(@RequestParam(value="no")String articleNo) {
		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;
	}
	
	@PostMapping("/edit")
	public String editArticle(@RequestParam(value="articleNo") String articleNo,
			@RequestParam(value="title") String title,
			@RequestParam(value="content") String content, RedirectAttributes redirectAttr) {
		onboardDto.setArticle_no(Integer.parseInt(articleNo));
		onboardDto.setTitle(title);
		onboardDto.setContent(content);
		boardService.editArticle(onboardDto);
		redirectAttr.addAttribute("no", articleNo);
		return "redirect:view";
	}
	
	@PostMapping("/remove")
	public String removeArticle(@RequestParam(value="articleNo") String articleNo) {
		boardService.removeArticle(Integer.parseInt(articleNo));
		return "redirect:list";
	}
}

- viewArticle 메소드에 Logger인터페이스를 선언해 콘솔에 로그 데이터를 출력한다.

로그의 종류, 로그 레벨 맨 아래가 1, 맨위가 5이다.

- Trace: debug보다 좀 더 상세한 메시지

- debug: 개발시 디버깅 목적으로 출력하는 메시지

- info: 로그인이나 상태 변경과 같은 정보성 메시지

- warn: 처리 가능한 문제이지만 향후 시스템 에러의 원인이 될 수 있는 문제

- error: 사용자 요청을 처리하는 중 발생한 문제

--> 결과

로그기록이 만들어졌다. logback.xml에 입력한 경로에 폴더를 선언해야 한다.
콘솔창에 컨트롤러에 선언한 로그기록이 띄어진다.

3. war 파일로 웹 어플리케이션 배포하기

- war: 패키지된 웹 애플리케이션이다.

1. 게시판 내용이 있는 war파일을 옮기기

board 프로젝트를 Maven clean 해준다.
board 프로젝트를 Maven install 해준다.
자바 작업환경에서 해당 프로젝트의 target으로 간다.
war파일의 이름을 변경한다.(입력하기 편하게 하기 위해)
해당 war파일을 톰캣폴더의 webapps로 이동시킨다.
잠시 상위폴더로 가서 bin폴더의 startup.bat을 실행시킨다.
톰캣을 실행시킨다.
실행이 끝나고 board 프로젝에 들어간다. 이 방식으로 이클립스에서 실행하지 않고 실행할 수 있다.
실행 후 webapps폴더에 bitboard 폴더가 있는 것을 확인할 수 있다.

2. ip주소로 실행하기

하단에 네트워크에서 '네트워크 및 인터넷 설정 열기'를 클릭한다.
네트워크 및 공유 센터로 들어간다.
고급 공유 설정 변경으로 들어간다.
개인 카테고리의 네트워크 검색에서 네트워크 검색 켜기를 체크해준다.
게스트 또는 공유에서 네트워크 검색 켜기를 누르고 저장한다.
암호로 보호된 공유에서 암호 보호 공유 끄기를 누른다.
제어판->시스템 및 보안->방화벽으로 와서 고급 설정을 누른다.
인바운드 규칙->새 규칙...->규칙 종류에서 포트를 누르고 다음을 누른다.
특정 로컬 포트에 8080을 입력한다.
작업, 프로필에서는 계속 다음을 누르고 이름에서 이름을 지정하고 마침을 누른다.
새로운 인바운드 규칙이 뜬것을 볼 수 있다.
톰캣의 bin폴더로 와서 startup.bat를 클릭해 톰캣을 실행한다.
이번에는 자신의 아이피 주소로 접속한다.

 

4. REST API

- 모바일 기기(phone, tablet)가 서버와 연동 시 네트워크 전송량, 클라이언트의 메모리 등을 고려해야 함
- 모바일 기기는 화면을 유지하면서 필요한 데이터만 전송 받아 빠르게 화면을 갱신함
- 모바일 기기연동이 많아 지면서 Spring Framework에서도 데이터만 전송하기 위해 REST 방식을 사용
- REST(REpresentational State Transfer)는 하나의 URI가 고유한 리소스를 처리하는 방식
- board/110 ==> 게시판의 110번째 글
- REST 방식으로 제공되는 API를 REST API 또는 RESTful API 라고 함

프로젝트 구조이다. board프로젝트에서 복붙해왔다.

- RestProject를 만들어준다. Dynamic Web Project로 만들고 Convert to Maven 설정도 해주어야 한다.

1. board 파일 옮기기

[1] pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.julian5383</groupId>
  <artifactId>RestProject</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <release>17</release>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.3</version>
      </plugin>
    </plugins>
  </build>
  
  <dependencies>
	<dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>javax.servlet-api</artifactId>
	    <version>4.0.1</version>
	    <scope>provided</scope>
	</dependency>
	<dependency>
	    <groupId>javax.servlet.jsp</groupId>
	    <artifactId>javax.servlet.jsp-api</artifactId>
	    <version>2.3.3</version>
	    <scope>provided</scope>
	</dependency>
	<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
	<dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>jstl</artifactId>
	    <version>1.2</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-webmvc</artifactId>
	    <version>5.3.28</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
	<dependency>
	    <groupId>mysql</groupId>
	    <artifactId>mysql-connector-java</artifactId>
	    <version>8.0.33</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
	<dependency>
	    <groupId>org.mybatis</groupId>
	    <artifactId>mybatis</artifactId>
	    <version>3.5.13</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
	<dependency>
	    <groupId>org.mybatis</groupId>
	    <artifactId>mybatis-spring</artifactId>
	    <version>2.1.1</version>
	</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
	<dependency>
	    <groupId>ch.qos.logback</groupId>
	    <artifactId>logback-classic</artifactId>
	    <version>1.4.8</version>
	</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
	<dependency>
	    <groupId>org.slf4j</groupId>
	    <artifactId>slf4j-simple</artifactId>
	    <version>1.7.36</version>
	    <!--<scope>test</scope>--><!-- 테스트 할때만 동작-->
	</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
	<dependency>
	    <groupId>com.fasterxml.jackson.core</groupId>
	    <artifactId>jackson-databind</artifactId>
	    <version>2.15.2</version>
	</dependency>

  </dependencies>
</project>

jackson-databind 2.15.2 maven을 입력한다.

- board에 있는 의존성과 함께 새로운 의존성 jackson-databind를 입력한다.

- jackson: Java Object를 JSON으로 변환하거나 JSON을 Java Object로 변환하는데 사용할 수 있는 Java 라이브러리이다.

[2] web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>RestProject</display-name>
  <filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextClass</param-name>
			<param-value>
				org.springframework.web.context.support.AnnotationConfigWebApplicationContext
			</param-value>
		</init-param>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>
				com.julian5383.config.MvcConfig
				com.julian5383.config.BoardConfig
			</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>

- board의 web.xml을 그대로 복사해왔다. 복사할때 <display-name>태그를 주의해야한다.

[3]

board의 pom.xml,  logback.xml, mybatis-config.xml, noticeboard.xml과 config, controller, model, service패키지와 클래스그리고 views에 있는 jsp 파일들 전부 복사해온다.

 

[4] RestSvcController.java

package com.julian5383.controller;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
@RequestMapping("/bit") //이 주소를 통해서 시작한다.
public class RestSvcController {
	
	@Autowired
	BoardService boardService;
	@Autowired
	OnboardDto onboardDto;
	
	@RequestMapping("/hello")
	public String hello() {
		return "Hello REST!!";
	}

	@RequestMapping("/hello2")
	public Map<String, String> hello2() {
		Map<String, String> map = new HashMap<String, String>();
		map.put("name", "Tom");
		map.put("age", "19");
		map.put("job", "white-water");
		return map;
	}
	
	@GetMapping("/article/{num}")
	public OnboardDto getArticle(@PathVariable("num")int articleNo) {
		onboardDto = boardService.viewArticle(articleNo);
		return onboardDto;
	}//jackson이 해주는 기능
	
	
}

- @RestController 어노테이션을 붙인 경우 스프링 MVC는 요청 매핑 어노테이션을 붙인 메소드가 리턴한 객체를 알맞은 형식으로 변환해 응답데이터를 전송한다.

- restapi는 return에 웹페이지가 아닌 실제 데이터 리턴한다.(세미콜론, xml 태그, json 태그 형식 등이 있음)

- Jackson이 존재하면 JSON형식의 문자열로 변환해 응답한다.(JSON은 간단한 형식을 갖는 문자열로 데이터 교환에 사용한다.)

[5] BoardConfig.java

package com.julian5383.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.julian5383.controller.BoardController;
import com.julian5383.controller.RestSvcController;
import com.julian5383.model.BoardDao;
import com.julian5383.model.OnboardDto;
import com.julian5383.service.BoardService;

@Configuration
public class BoardConfig {
	@Bean
	public OnboardDto onboardDto() {
		return new OnboardDto();
	}
	
	@Bean
	public BoardDao boardDao() {
		return new BoardDao();
	}
	
	@Bean
	public BoardService boardService() {
		return new BoardService();
	}
	
	@Bean
	public BoardController boardController() {
		return new BoardController();
	}
	
	@Bean
	public RestSvcController restSvcController() {
		return new RestSvcController();
	}
}

- BoardConfig.java에 restSvcController 메소드를 선언한다.

 

--> 결과

결과 1

hello 메소드 결과

결과 2

hello2 메소드 결과

결과 3

getArticle 메소드 결과
article_no가 3번인 데이터가 출력된다.