국비지원 과정/Spring

코딩 79 일 / 2022.10.17 / Spring MVC 웹 소켓 프로그램, Spring Boot 환경 설정, Lombok 기능

레이커 2022. 10. 17. 12:50

웹 소켓 기능

- 접속해 있는 유저들끼리 채팅 가능한 기능

- Spirng 외 다른 언어로도 웹 소켓 구현 가능하다

 

웹 소켓 기능 쓰기 위해 추가해야할 환경설정 파일의 코드

1. pom.xml : 웹 소켓 라이브러리

- spring-websocket

2. servlet.xml : Handler 매핑 잡기

 

웹 소켓 예제 : 프로젝트 webSock

실습 준비

 

파일들 살펴보기 : pom.xml

- pom.xml 부분

		<!-- WebSocket -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-websocket</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

- 웹소켓 라이브러리가 추가되어야 웹 소켓으로 통신 가능

 

파일들 살펴보기 : web.xml

- *.do 로 매핑을 잡아뒀다

- 그 외는 기존 내용들과 같다

 

파일들 살펴보기 : servlet-context.xml

- 다른 내용들이 들어가있다

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:websocket="http://www.springframework.org/schema/websocket"
	xsi:schemaLocation="http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
	
	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />
	
	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />
	
	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>	
	
	<context:component-scan base-package="com.ch.webSock" />
	
	<default-servlet-handler/>
	<websocket:handlers>
		<websocket:mapping handler="chatHandler" path="chat-ws.do"/>
	</websocket:handlers>
	<beans:bean id="chatHandler" class="com.ch.webSock.WebChatHandler"/>
		
</beans:beans>

- base-package 와 View 파일 저장 위치인 ViewResolver 는 기존과 같다

- base-package 로 지정된 패키지 하위에 HomeController.java, WebChatHandler.java 가 있다

- ViewResolver 로 지정된 패키지 하위에 chat.jsp, header.jsp 가 있다

 

Handler 매핑 잡기

- 어떤 요청으로 들어올때 웹 소켓을 연결시킬지 매핑을 잡아야한다

- 웹 소켓 기능 처리 위해 만든 클래스인 WebChatHandler 가 웹 소켓 연결, 처리 역할을 한다

- 이 클래스는 필요한 클래스를 상속받아 내가 만든 클래스이다

- 이 클래스로 찾아가기 위해 매핑을 잡아준다, 현재는 "chat-ws.do" 로 요청할때만 chatHandler 라는 id 값과 매핑되어 이 클래스가 동작

 

com.ch.webSock.WebChatHandler 클래스

- WebChatHandler.java

package com.ch.webSock;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

public class WebChatHandler extends TextWebSocketHandler { 
	Map<String, WebSocketSession> users = new HashMap<String, WebSocketSession>();
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		users.put(session.getId(), session);
	}
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
		users.remove(session.getId());
	}
	protected void handleTextMessage(WebSocketSession session, 
			TextMessage message) throws Exception {
		String msg = message.getPayload();
		TextMessage tMsg = new TextMessage(msg.substring(4));
		Collection<WebSocketSession> list = users.values();
		for (WebSocketSession wss : list) {
			wss.sendMessage(tMsg);
		}
	}
}

- 내가 만든 클래스이다

- TextWebSocketHandler 클래스를 상속받아야한다

- afterConnectionEstablished(), afterConnectionClosed(), handleTextMessage() 메소드를 오버라이딩 해야한다

- afterConnectionEstablished() 메소드 : 연결이 된 후 어떤 일을 수행할지 작성

- afterConnectionClosed() 메소드 : 연결이 끊긴 후, 주로 연결을 사겢함

- handleTextMessage() 메소드 : 특정 유저가 전송한 메세지를 다른 유저에게 전송해주는 역할

 

코드 흐름

- HomeController 에서 요청을 받고, chat.jsp 에서 메세지 관련, 소켓 관련 처리를 한다

 

파일들 살펴보기 : root-context.xml

- DB연동을 하지 않으므로 root-context.xml 이 비어있다


흐름 설명

- index.jsp 를 실행

- 브라우저를 바꿔서 다시 실행해서 두개의 브라우저를 열기

- 서로 메세지를 주고 받을 수 있음

+ views/chat.jsp 파일에서 80 포트로 맞춰뒀으므로 80포트를 써야함


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
	location.href="chat.do";
</script>
</body>
</html>

- Dispatcher Servlet -> HomeController 클래스

 

- Controller 클래스 HoemController.java

package com.ch.webSock;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HomeController {
	@RequestMapping("chat.do")
	public String home() {
		return "chat";
	}	
}

- chat.jsp 로 이동

- 채팅하기 위한 페이지인 chat.jsp 로 이동

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="header.jsp"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
	var websock;
	$(function() {
		$('#message').keypress(function(event) {
			var keycode = event.keyCode ? event.keyCode : event.which;
			if (keycode == 13)
				send();
			event.stopPropagation();
		});
		$('#enterBtn').click(function() {
			connect();
		});
		$('#exitBtn').click(function() {
			disconnect();
		});
		$('#sendBtn').click(function() {
			send();
		});
	});
	function send() {
		var nickName = $('#nickName').val();
		var msg = $('#message').val();
		websocket.send('msg:' + nickName + ' => ' + msg);
		$('#message').val('');
	}
	function connect() {
		websock = new WebSocket("ws://localhost:80/webSock/chat-ws.do");
		websock.onopen = onOpen;
//		websock.onclose = onClose;
		websock.onmessage = onMessage;
	}
	function disconnect() {
		websock.close();
	}
	function send() {
		var nickname = $('#nickName').val();
		var message = $('#message').val();
		websock.send('msg:' + nickname + ' : ' + message);
		$('#message').val('');
	}
	function onOpen(event) {
		appendMessage("연결되었습니다.");
	}
	function onClose(event) {
		appendMessage("연결이 종료되었니다.");
	}
	function onMessage(event) {
		var data = event.data;
		appendMessage(data);
	}
	function appendMessage(msg) {
		$('#chatMessageArea').append(msg + "<br>");
		var chatAreaheight = $('#chatArea').height();
		var maxscroll = $('#chatMessageArea').height() - chatAreaheight;
		$('#chatArea').scrollTop(maxscroll);
	}
</script>
</head>
<body>
	<div class="container">
		별명 : <input type="text" id="nickName"> 
		     <input type="button" value="입장" id="enterBtn" class="btn btn-success"> 
		     <input	type="button" value="퇴장" id="exitBtn" class="btn btn-danger">
		     
			 <h2 class="text-primary">대화영역</h2>
			 <input type="text" id="message" required="required"> 
			 <input	type="button" value="전송" id="sendBtn" class="btn btn-info">
			 <div id="chatArea">
				<div id="chatMessageArea"></div>
			 </div>
	</div>
</body>
</html>

-  가장 위에서 jQuery 함수로 이벤트 처리를 하고 있다

- send(), connect(), disconnect(), send(), onOpen(), onClose(), onMessage(), appendMessage() 메소드들

- 별명을 입력하기 위한 양식 폼이 아래에 있다

+ header.jsp 파일에서 각종 라이브러리를 불러오고 있다

- 별명 입력 양식과 대화영역 입력 양식, 버튼들의 id 값들을 자세히 보기

- id 가 "chatMessageArea" 인 div 영역에 대화내용들이 나올 것

 

위 코드 나눠서 캡처 설명

-  가장 위에서 jQuery 함수로 이벤트 처리를 하고 있다

- message 는 메세지를 입력하기 위한 양식, 엔터키를 누르면 아래 내용이 실행됨

+ keypress : 키를 눌렀을때 발생하는 이벤트

- keypress 이벤트 발생시 사용자가 누른 키 코드 keycode 를 가져온다, 13 번은 아스키코드로 "ENTER" 키이다.

- 즉 ENTER 키를 눌렀을때 send() 메소드를 호출해서 메소드를 전송시켜라는 의미

- 즉 메세지 입력 후 ENTER 키를 눌러도 send() 에 의해 메세지가 전송되고, "전송" 버튼을 눌러도 send() 에 의해 메세지가 전송된다

- enterBtn 은 "입장" 버튼, exitBtn 은 "퇴장" 버튼, sendBtn 은 "전송" 버튼, 각 버튼을 불러와서 클릭시 아래의 메소드를 실행

 

- chat.jsp 에서 connect() 메소드 부분

	function connect() {
		websock = new WebSocket("ws://localhost:80/webSock/chat-ws.do");
		websock.onopen = onOpen;
//		websock.onclose = onClose;
		websock.onmessage = onMessage;
	}

- 웹 소켓을 연결하고 있다

- WebSocket 객체를 생성하고 전역변수 websock 로 객체를 받느다

- WebSocket 객체를 생성할때 ip 주소와 포트번호까지 작성, 현재는 localhost 이고 포트번호는 80 포트로 접속

- WebSocket 객체를 생성할때 "chat-ws.do" 로 요청, 이 Handler 매핑은 servlet-context.xml 에서 잡아뒀다

- "chat-ws.do" 로 요청시 servlet-context.xml 에서 매핑이 잡혀서 WebChatHanlder 클래스가 동작한다

+ WebChatHandler 클래스에선 회원의 id 값과 WebSocketSession 세션을 put() 으로 추가해서 맵 users 에 저장함

- 아래에 정의된 onOpen() 메소드를 호출해서 appendMessage() 로 "연결되었습니다" 메세지를 출력 

	function onOpen(event) {
		appendMessage("연결되었습니다.");
	}

 

+ WebChatHandler.java 부분

 

- chat.jsp 에서 send() 메소드 부분만

	function send() {
		var nickName = $('#nickName').val();
		var msg = $('#message').val();
		websocket.send('msg:' + nickName + ' => ' + msg);
		$('#message').val('');
	}

- 사용자가 별명 입력창에 작성한 닉네임과, 대화영역에서 사용자가 입력한 대화내용을 가져옴

- 구해온 WebSocket 객체 websocket 으로 send() 메소드를 사용

- 그럼 WebChatHanlder.java 에서 handleTextMessage() 메소드가 호출되어, 목록을 가진 사용자들에게 메세지를 전송

- WebChatHanlder.java 에서 handleTextMessage() 부분만

- 메세지를 가져오고, 모든 유저를 가져와서 리스트에 저장한 후, for 문을 통해 모든 유저에게 메세지를 뿌림

 

- chat.jsp 에서 disconnect() 메소드 부분만

	function disconnect() {
		websock.close();
	}

- websocket.close() 로 연결을 끊는다

- close() 가 실행되면 WebChatHanlder 의 afterConenctionClosed() 가 호출된다

 

- WebChatHandler.java 에서 afterConnectionClosed() 부분만

- 세션을 지움

- 퇴장 버튼을 눌렀던 유저만 users 에서 빠지는 것

 

결과

- 글을 입력하고 "전송" 을 누르면 브로드캐스팅 하듯이 접속되어있는 모든 사람에게 메세지를 전송한다

- 유저가 여러명이어도 가능


Spring Boot

Spring Boot 특징

- 독립 실행이 가능한 스프링 애플리케이션 개발 가능(Tomcat, Jetty 내장)

- Tomcat 을 설치하지 않아도 자동으로 Tomcat 이 내장되어있으므로 서비스 가능
- 통합 Starter를 이용하여 프로젝트를 만든다

- 통합 Starter 를 이용하여 Maven/Gradle 로 라이브러리 관리
- 통합 Starter를 통한 자동화된 스프링 설정 제공
- 번거로운 XML 설정을 요구하지 않음, XML 파일들이 많이 빠진다

ex) web.xml, servlet-context.xml, root-context.xml 파일 이 없다

- 새로운 환경설정 파일이 하나 만들어지고, 거기에 직접 필요한 환경을 구축해야함

- Spring Actuator 제공 (애플리케이션의 모니터링과 관리를 위해서 사용)

 

Spring Boot 라이브러리 관련 환경설정 파일

- 통합 Starter 를 이용하여 Maven/Gradle 로 라이브러리 관리

- Maven 으로 환경설정시 pom.xml 파일이 생성됨, 체크해서 라이브러리 설치 가능

- Gradle 으로 환경설정시 Gradle 환경설정 파일이 생성됨

 

Spring Boot 환경 구축 방법

1. Eclipse 에 STS 3.x plug-in 추가 또는 STS 를 사용해야한다

- 현재는 STS 3 점대를 설치된 상태이므로 Spring, Spring Boot 프로젝트 모두 생성 가능

+ STS 4 점대는 Spring 프로젝트를 만들 수 있는 메뉴가 없다, Spring Boot 로 넘어가는 추세

 

2. [File] - New - Project

- Spring Starter Project 선택

- Type 에서 라이브러리 관리 방법 선택, Gradle / Maven 중 현재는 "Maven" 선택

+ Gradle 선택시 Gradle 로 라이브러리 관리하는 패키지 생성

- Packaging 에서는 압축 포맷 선택, War / Jar 중 현재는 "War" 선택

- Java Version 은 현재 사용하는 JAVA 버전인 8 을 선택

- Language 는 JAVA 선택

- Package 가 com.example.demo 로 되어있다, 이게 Spring 의 top-level 패키지와 같은 역할

- 이 패키지 com.example.demo 가 java 폴더 하위에 생성된다

- 이 패키지 하위에 Controller 등의 자바 클래스들이 오게 된다

+ demo 는 현재 프로젝트명

- Maven 을 선택했으면, Maven 환경설정 파일 (pom.xml) 안에 여기서 선택한 의존 라이브러리들이 추가됨

- Gradle 을 선택했으면, Gradle 환경설정 파일안에 여기서 선택한 의존 라이브러리들이 추가됨

- 기본적으로 선택해야할 라이브러리 : Spring Web 을 선택해야 Spring MVC 패턴으로 만들어짐

 

+ 다른 라이브러리들

- Lombok 라이브러리 : DTO 클래스 안에 정해진 필드의 Getter / Setter 메소드를 어노테이션 기반으로 쓰기 위해서 사용되는 라이브러리, 이걸 쓰면 Getter / Setter 메소드를 쓰지 않아도 된다, 나중에 사용할 라이브러리

- WebSocket 라이브러리 : 체크하면 WebSocket 라이브러리가 추가됨

- NoSql 카테고리 라이브러리들 : 체크하면 Spring Boot - NoSQL 연동

- SQL 카테고리 라이브러리들 : DB 연결시 필요한 라이브러리

- MyBatis Framework 라이브러리 : 체크하면 Spring Boot - MyBatis 연동

- Oracle Driver 라이브러리 : 오라클 연동시 Oracle Driver (오라클용 JDBC 드라이버) 체크

- [boot] 가 붙은 것은 Spring Boot 프로젝트임을 의미

- 프로젝트를 생성하며 체크했던 라이브러리들이 Maven 환경설정 파일인 pom.xml 에 들어간다

- web.xml, servlet-context.xml, root-context.xml 환경설정 파일들이 없다

 

Spring Boot 프로젝트 실행 방법

- 프로젝트를 오른쪽 마우스 클릭 -> Run As -> Spring Boot App 로 실행해야한다

- 아래와 같이 출력되면 실행 성공

 

파일들 살펴보기 : pom.xml

- Maven 을 선택했으므로 현재 프로젝트 하위에 Maven 환경설정 파일 pom.xml 이 만들어짐

- pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

- 프로젝트 생성 시 Spring Web 을 선택했으므로 spring-boot-starter-web, spring-boot-starter-tomcat 라이브러리 등이 들어가있다

 

Spring 에는 있지만 Spring Boot 에는 없어진 환경설정 파일

- web.xml : webapp 폴더 가 비어있다, 즉 WEB-INF 폴더도 없고, 그 안에 있어야할 web.xml 파일도 없다

- web.xml 에서 불러왔던 spring 환경설정 파일 2개인 servlet-context.xml, root-context.xml 파일도 없다

- Spring Boot 프로젝트에서는 web.xml, servlet-context.xml, root-context.xml 파일이 없다

- Spring 의 6개 환경설정 파일 중에서 pom.xml, Mapper 파일(.xml) 만 Spring Boot 에 있다, Configuration.xml 파일도 필요없다

 

Spring 에는 없지만 Spring Boot 에서 새로 생긴 환경설정 파일 : application.properties
- application.properties 파일에 직접 환경설정을 해야한다
- 없어진 3개의 파일의 환경설정 내용을 여기에 작성해야함
- 프로젝트 생성 직후 application.properties 에는 아무 내용없다

 

Spring 에는 없지만 Spring Boot 에서 새로 생긴 폴더 설명

 

resources폴더 하위 static 폴더

- CSS, JS, 이미지(images) 디자인 관련 파일들을 저장함

- 공유가 되는 폴더, 이 폴더에 저장되면 쉽게 CSS, JS, 이미지들을 불러올 수 있다

- 이 폴더에 있는 파일은 다른 페이지에서 쉽게 불러올 수 있다

+ Python 의 장고 프로젝트가 Spring Boot 프로젝트와 비슷한 구조, 장고에도 static 폴더가 있다

 

resources 폴더 하위 templates 폴더

- EL, JSTL 대신하는게 타임리프, EL, JSTL 대신 타임리프 지원 태그들을 사용

- 타임리프를 쓸때 여기에 View 파일인 HTML 파일들을 저장해야한다
- Spring Boot 에서는 JSTL 을 사용하든지 타임리프를 사용하든지 선택 가능
- 타임리프 사용시 View 파일을 JSP 가 아닌 HTML 파일을 사용해야함

- 그 HTML 인 VIew 페이지들이 여기 저장되어야함
+ HTML 파일을 쓰므로 EL, JSTL 을 사용 못하게되는 것이다

 

 

Controller를 추가해서 Hello World 출력
- src/main/java/com/example/demo/controller – SampleController.java 생성
- index 파일에서 값을 요청했을때 여기서 처리해보자
- top-level 패키지 하위인 com.example.demo 아래에 controller 폴더 생성 후 SampleController.ajva 파일 생성

- 그냥 일반 클래스로 만듬

 

- SampleController.java

package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SampleController {
	@RequestMapping("/")
	public String hello() {
		return "Hello World~!!";
	}
}

+ @RestController = @Controller + @ResponseBody ,

- 현재 프로젝트 Run As -> Run on Server 로 실행 한 후 웹브라우저에 http://localhost:8080 요청한다.

- Hello World ~!! 가 브라우저 나타나면 잘 실행된 것

- web.xml 에서 설정하던 Dispatcher Servlet 매핑을 하지 않았으므로, 그냥 여기 Controller 클래스의 @RequestMapping 만 맞으면 바로 찾아간다

- 현재는 / 이므로 아무거나 요청해도 다받음

 

- 이후 프로젝트를 Run As - Run on Server 로 실행

- View 로 가서 출력하는 대신 @ResponseBody 에 의해 return 만 하면 바로 브라우저에 출력됨

- @RestController 를 썼으므로 @ResponseBody 에 의해 return 시 요청한 브라우저로 바로 찾아가서 돌려준다, 즉 브라우저에 바로 출력된다

 

 

port 번호 설정

- Spring boot 에 내장된 tomcat 은 기본 port 가 8080, 현재 8080 은 오라클에서 쓰고 있는 포트이다

- 포트충돌 가능성, 오류 생긴다면 내장 tomcat port 번호를 80 으로 바꿔줘야한다

 - tomcat port 번호를 바꾸기 위해 application.properties 에 server.port = 80 한줄을 추가해준다

- application.properties

- 이후 다시 실행시 실행 됨
 

Spring Boot 예제 : 프로젝트 boot01

실습 준비

- 클라우드의 프로젝트 boot01 을 다운받아 압축 해제 후 import

 

Spring Boot 프로젝트 import 하는 방법

- Spring 과 같은 방법으로 import 하면 된다

- boot01 프로젝트 import

- 프로젝트 오류 발생, 현재 설정된 JAVA 가 11 버전으로 되어있고, 우리가 설치한 JAVA 는 8 점대 버전이라서 오류 발생

 

프로젝트의 JAVA 버전 설정 수정 방법

- 현재 프로젝트 오른쪽 버튼 -> Properties

- 설치된 JAVA 버전인 8 점대 (=1.8) 선택 후 적용

- 그럼 JAVA 버전이 8점대 (1.8) 로 변경되고 오류가 사라진다\

 

흐름 설명

- Run As -> Run on Server 로 프로젝트 boot1 을 실행

- 이전에 Spring 에서 했었던 예제

- 누르는 메뉴에 따라 다른 화면이 나타남

ex) '구구단' 클릭시 랜덤 단 으로 구구단 나타남


파일들 살펴보기 : pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.9.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>boot01</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>boot01</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>	
		<!-- jsp 파일을 사용하기 위한 의존 라이브러리-->
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
			<scope>provided</scope>
		</dependency>
		
		<!-- jstl -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
		</dependency>	
	
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

- Maven 의 환경설정 파일, Spring Boot 프로젝트 생성시 체크했던 의존 라이브러리만 추가됨

- 원하는 라이브러리를 직접 추가할 수도 있다

- JAVA 는 1.8 버전, Spring Boot 는 2.2.9 버전 사용 중

 

pom.xml 에 새로 추가된 라이브러리 : JSTL 라이브러리

		<!-- jstl -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
		</dependency>

- 결과를 JSTL 로 출력하기 위해 JSTL 라이브러리 jstl 을 추가

+ 현재는 JSTL 로 출력하기, 나중에 타임리프 로 출력시엔 타임리프 라이브러리를 추가해아함


파일들 살펴보기 : application.properties

# port
server.port=80

# prefix and suffix
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

- 이 환경설정 파일 application.properties 은 resources 폴더 안에 있다

- 대부분의 환경설정을 이 파일에서 해야한다

- 서버(tomcat) 의 포트번호를 80 으로 설정하고 있다

- Spring 에서 JSTL 로 출력하기 위해 servlet-context.xml 에서 VIewResolver 를 설정헀던 것처럼, Spring Boot 에서는 prefix, suffix 를 이 파일 application.properties 안에 설정해야한다

- prefix 는 View 파일들이 저장될 최상위 폴더, suffix 는 View 파일 확장자 를 설정

- 설정된 prefix 위치에 해당하는 폴더가 webapp 폴더 하위에 생성되어있어야함, 기준은 webapp 폴더


top-level 패키지

- Spring Boot 패키지 생성시 지정한다

- 그 안에서 각각의 기능에 따른 폴더 (controller, service 등) 를 만들고 그 안에 JAVA 파일들을 넣는다, Spring 과 같다

- Sample 로 만들어진 Boot01Application.java, ServletInitializer.java 는 건드리면 서버 구동 오류 생길 수 있다, 건드리지 않기


파일들 살펴보기 : index 파일

- index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
	$(document).ready(function(){
		$("#sel").change(function(){
			var sel = $("#sel").val();
			location.href=sel;
		});
	});
</script>

</head>
<body>

선택 :
<select id="sel">
	<option value="">메뉴</option>
	<option value="hi">hi</option>
	<option value="welcome">welcome</option>
	<option value="abc">abc</option>
	<option value="hello">hello</option>
	<option value="gugu">구구단</option>
</select>

</body>
</html>

- index 파일은 webapp 폴더 하위에 있어야한다

- select-option 으로 만들어져있고, select 의 id 값이 "sel" 로 되어있다, 이벤트를 발생시킨 태그인 select 태그를 가져온다

- change 이벤트가 발생, change() 함수안에서 선택된 option 의 value 값을 구해온다, 그 value 값이 요청이름값이 됨

- Dispatcher Servlet Mapping 을 설정했던 web.xml 이 없으므로, Controller 클래스의 @RequestMapping 과 이름값만 같으면 Controller 클래스로 찾아간다 

+ Controller 클래스가 여러개 있어도 요청값만 다르게 설정하면 된다

- 현재 프로젝트 선택 - Run As - Run on Server 클릭시 index 파일이 자동 실행된다


파일들 살펴보기 : Controller 클래스들

- HelloController.java

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

	@RequestMapping("/welcome")
	public String welcome() {
		return "welcome";
	}
}

 

- SampleController.java

package com.example.demo.controller;

import java.io.IOException;
import java.util.Random;

import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

//@RestController
@Controller
public class SampleController {

	@RequestMapping("/hi")
	@ResponseBody
	public void hello(HttpServletResponse response) throws IOException {
		response.getWriter().print("Hello world~!!!");
	}
	
	@RequestMapping("/abc")
	@ResponseBody
	public String abc() {
		return "hi abc";
	}
	
	@RequestMapping("/hello")
	public String hello() {
		return "hello";
	}
	
	@RequestMapping("/gugu")
	public String gugu(Model model) {
		
		Random r = new Random();
		int dan = r.nextInt(8) + 2;		// 2 ~ 9단
		
		model.addAttribute("dan", dan);
		
		return "gugu";
	}
	
}

@ResponseBody 어노테이션

- @ResponseBody 를 썼으므로 return 을 하면, 요청한 브라우저로 바로 돌려준다

- @ResponseBody 는 요청한 브라우저에 결과를 바로 돌려줘서, 브라우저에 결과가 바로 나타나게된다

- Spring 과 마찬가지로 요청을 Controller 클래스에서 받는다

ex) "hi" 로 요청이 오면 out 객체를 만들고, 브라우저에 "Hello World~!!!" 메세지를 출력, 이렇게 출력된 메세지를 "hi" 를 요청한 곳에 바로 돌려준다

- View 로 가서 출력하는 대신 @ResponseBody 에 의해 return 만 하면 바로 브라우저에 출력됨

- @ResponseBody 에 의해 return 시 요청한 브라우저로 바로 찾아가서 돌려준다, 즉 브라우저에 바로 출력된다


- @ResponseBody 가 붙은 "hi", "abc" 요청은 요청한 브라우저로 결과를 바로 돌려준다

ex) select-option 에서 "hi" 선택시

- 브라우저 창 URL 을 보면 View 로 이동하지 않고, 요청한 곳(브라우저) 인 http://localhost/boot01/hi 로 돌려줬음을 확인 가능


- @ResponseBody 가 붙지 않은 "hello", "gugu" 는 View 페이지로 돌려줘야한다

- 이땐 Spring 과 마찬가지로 prefix, suffix 를 뺀 경로를 적는다

ex) select-option 에서 "hello" 선택시

- Controller 에서 WEB-INF/views/hello.jsp 로 이동하였고 hello.jsp 인 View 파일의 내용이 출력됨


- @ResponseBody 가 붙지 않은 "hello", "gugu" 는 View 페이지로 돌려줘야한다

- 이땐 Spring 과 마찬가지로 prefix, suffix 를 뺀 경로를 적는다

ex) select-option 에서 "gugu" 선택시

- SampleController 의 @ResquestMapping("/gugu") 로 요청받음

- View 인 WEB-INF/views/gugu.jsp 로 이동하고, 값을 가져가기 위해 Model 객체에 저장

+ Random 클래스 nextInt(8) 메소드 호출 시 0 ~ 7 까지 랜덤 숫자를 발생시킴

- 실행할때마다 난수에 의해 랜덤으로 한개의 단이 나옴

 

- gugu.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>구구단 출력</title>
</head>
<body> 

 [ ${dan} 단 ] <br><br>
<c:forEach var="i" begin="1" end="9">
	${dan} * ${i} = ${dan * i} <br>
</c:forEach>

</body>
</html>

- JSTL forEach 태그를 사용하고 있다, JSTL 을 사용하기 위해 pom.xml 에 JSTL 라이브러리 추가했다



Spring Boot 의 Lombok 기능 예제 1

- 먼저 Lombok 기능을 쓰지 않고 해보고, Lombok 기능을 써서도 프로젝트 만들어보기

 

Lombok 기능

- DTO 클래스에서 접근 제어자가 private 인 필드들의 Getter / Setter 메소드를 쓰지 않고 어노테이션으로 자동 처리

- @Getter, @Setter 어노테이션으로 각 필드마다 Getter / Setter 메소드를 자동으로 만들어줌

 

Lomboc 기능 쓰지 않고 만드는 예제 : 프로젝트 boot02

- 프로젝트 boot02 생성

- Spring Web , Lombok , MyBatis Framework, Oracle Driver 를 추가

- 현재는 DB 연동을 하지 않는 예제이므로 Spring Web, Lombok 만 있으면 된다

- 여기서 체크를 하면 pom.xml 에 Lombok 라이브러리가 추가된다, 하지만 바로 사용가능하지 못함, 설치를 해야한다

- Finish를 하면 원격 저장소에서 로컬 저장소로 라이브러리를 다운로드

 

파일들 살펴보기 : pom.xml (JSP, JSTL 의존 라이브러리 추가 전)

<?xml version="1.0" encoding="UTF-8"?>
<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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>boot02</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>boot02</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.2.2</version>
		</dependency>

		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc8</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

- Spring Boot 프로젝트 생성 시 설정했던 내용들이 있다

- Spring Web 에 체크했으므로 spring-boot-starter-web 등 관련 라이브러리들이 추가되어있다

- Oracle Driver 에 체크했으므로 ojdbc8 라이브러리가 추가되어있고, 다운받아져있다

- Lombok 에 체크했으므려 lombok 라이브러리가 추가되어있고, 다운받아져있다

- MyBatis 에 체크했으므로 MyBatis 관련 라이브러리들이 추가되어있고, 다운받아져있다

+ 지금 프로젝트는 lombok 기능 쓰지 않을 것, 그냥 추가시켜둔 것이다

 

라이브러리 추가

- 현재 pom.xml 에 없는 의존 라이브러리를 dependencies 안에 추가

- JSP 파일을 사용하기 위한 의존 라이브러리와 JSTL 을 사용하기 위한 의존 라이브러리를 추가

 

환경 설정 파일 application.properties 수정

- 이 파일은 /main/resources 폴더 하위에 있다

- 이 파일 안에 필요한 내용 추가함

- 서버의 포트번호 설정과 prefix, suffix 설정

 

prefix 로 설정한 패키지(폴더) 만들기

- application.properties 에서 설정한 prefix 폴더들이 반드시 만들어져 있어야한다

- webapp 폴더를 기준으로 prefix 에 설정된 폴더를 생성해야한다

- webapp 하위에 WEB-INF 폴더, WEB-INF 폴더 하위에 views 폴더 생성

 

DTO 클래스 생성

- /main/java/com/example/demo/model - Member.java 생성

- com.example.demo 패키지 하위에 model 폴더 생성 후 Member.java (DTO) 파일 생성

- Member.java

package com.example.demo.model;

public class Member {
	
	private String id;
	private String passwd;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPasswd() {
		return passwd;
	}
	public void setPasswd(String passwd) {
		this.passwd = passwd;
	}
}

- 접근제어자가 private 인 필드들(원래는 테이블 컬럼명과 같은 이름으로 만듬) 생성, Getter / Setter 생성

- 현재는 Lombok 기능을 사용하지 않고 DTO 클래스를 만들었다

+ Lombok 기능 사용시 Getter / Setter 메소드를 직접 사용하지 않음

 

Controller 클래스 생성

- /main/java/com/example/demo/controller- SampleController.java 생성

- com.example.demo 패키지 하위에 controller폴더 생성 후 SampleController.java (Controller) 파일 생성

- SampleController.java (수정 전)

package com.example.demo.controller;

import org.springframework.stereotype.Controller;

@Controller
public class SampleController {

}

- @Controller 어노테이션을 붙인다

- 나중에 요청 받는 부분을 작성하고, @RequestMapping 어노테이션 사용할 것

 

index 파일 생성

- webapp 폴더 하위에 index.jsp 파일 생성

- index 파일 자동실행을 위해서는 반드시 webapp 폴더 하위에 있어야한다

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script>
	location.href="main";
</script>
</body>
</html>

- 다시 Controller 클래스로 돌아가서 "main" 요청을 받는 코드를 작성하자


- SampleController.java (수정 후 1)

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class SampleController {

	@RequestMapping("main")
	public String main() {
		return "main";
	}
}

- 이때 "main" 은 webapp/WEB-INF/views 하위에 들어가야할 JSP 파일명

 

- webapp/WEB-INF/views 하위에 main.jsp 파일 생성

- main.jsp 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인</title>
</head>
<body>

<form method="post" action="send">
	ID : <input type="text" name="id"><br>
	Password : <input type="text" name="passwd"><br>
	<input type="submit" value="가입">
</form>

</body>
</html>

- form 안에 아이디와 비번 입력 양식을 만들자, 입력 후 "가입" 클릭 시 "send" 로 요청

- 아이디 입력 양식의 name 값은 DTO 프로퍼티명과 같은 "id" 로 설정해야한다, 그래야 @ModelAttribute 로 값 전달 가능

- 비밀번호도 마찬가지

 

- 다시 이 "send" 요청을 받을 코드를 Controller 클래스에 추가

- SampleController.java (수정 후 2)

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.demo.model.Member;

@Controller
public class SampleController {

	@RequestMapping("main")
	public String main() {
		return "main";
	}
	
	@RequestMapping("send")
	public String send(Member member, Model model) {
		System.out.println("id:" + member.getId());
		System.out.println("passwd:" + member.getPasswd());
		
		model.addAttribute("member", member);
		return "result";
	}
}

 

- send() 메소드 매개변수에서 앞의 main.jsp 에서 넘어온 아이디, 비밀번호들을 @ModelAttribute (생략) 으로 DTO Member 객체 member 로 바로 받음

+ 값이 잘 넘어왔는지 넘어온 아이디와 비밀번호를 콘솔창에 찍어보고 있다

- Model 객체에 객체 member 를 저장해서 result.jsp 로 이동

 

- webapp/WEB-INF/views 하위에 result.jsp 파일 생성

- result.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
회원 가입 결과 : <br><br>
id : ${member.id} <br>
pass : ${member.passwd} <br>
</body>
</html>

+ JSTL 과 다르게 EL 은 라이브러리 없이 사용 가능

 

프로젝트 실행해보기

- DB 연결 안하므로 pom.xml 에서 MyBatis, Oracle 관련 라이브러리를 주석으로 막고 실행해야 오류가 발생하지 않음

- 그 후 프로젝트 실행

- 잘 실행된다

 

한글값 인코딩

+ Spring 에서는 web.xml 에서 한글값 인코딩 처리 코드를 넣었다

- Spring Boot 에서는 한글값 인코딩은 자동 처리된다


+ 서버에 돌아가는 프로젝트 Add and Remove 하는 방법

- 오류 생길때 사용



Spring Boot 의 Lombok 기능 예제 2

- 앞에서 했던 프로젝트 boot02 예제에 Lombok 기능을 사용해보기

 

Lombok 라이브러리

- java 라이브러리중 하나

- 멤버 변수에 대한 getter / setter method, toString(), Equals() 등과 생성자 코드를 불필요하게 반복적 만드는 대신 lombok 라이브러리를 사용하면 Annotation(어노테이션) 기반으로 자동으로 메소드를 생성해준다

- lombok 라이브러리를 사용하면 DTO같은 클래스에서 getter 와 setter 메소드를 자동으로 생성해 준다. 

 

Lombok 기능

- DTO 클래스에서 접근 제어자가 private 인 필드들의 Getter / Setter 메소드를 쓰지 않고 어노테이션으로 자동 처리

- @Getter, @Setter 어노테이션으로 각 필드마다 Getter / Setter 메소드를 자동으로 만들어줌

 

Lombok 라이브러리 설치

- Eclipse 나 STS 에서 Lombok 을 사용하기 위해선 Lombok 라이브러리만으로 동작하지 않고 설치를 해야 동작한다

- pom.xml 에는 아까 추가한 Lombok 라이브러리가 추가되어있다

- pom.xml 에 추가 이후 Lombok 라이브러리를 다운 받아서 Eclipse / STS 콘솔창에서 lombok 파일을 명령 프롬프트 창에서 한번 실행시켜줘야함

- 이렇게 Lombok 라이브러리를 한번은 설치해야 사용 가능

 

Eclipse / STS 에 Lombok 라이브러리 설정

- Eclipse 나 STS에서 Lombok을 이용하기 위해서 Lombok 을 설치, 설정해야한다

1. Lombok 사이트(http://projectlombok.org/all-versions) 에서 lombok-1.16.18.jar 파일을 다운로드 받는다.

- 클릭해서 다운 받으면 C:\Users\admin(내 계정)\Downloads 폴더 하위에 다운된다

2. 다운로드 받은 lombok-1.16.18.jar 파일 실행

c:\> cd C:\Users\admin\Downloads # 다운로드 받은 위치로 이동
c:\> java -jar lombok.jar # lombok 파일 실행

- 엔터시 새로운 창이 나타난다

 

- STS 에 lombok 을 설치할 것이므로 Eclipse 는 체크 해제해주고 STS 에 체크해서 Install / Update 누르기

- Install / Update 를 누르면 에 lombok 프로그램이 STS 실행파일 위치에 설치되어 저장된다

- 이 설치 작업은 한번만 수행하면 된다

- 설치가 완료되면 lombok 프로그램이 STS 실행파일 위치에 설치되어 저장된다

 

- 이후 lombok 라이브러리가 필요한 프로젝트의 pom.xml 에 lombok 라이브러리를 추가하면 lombok 을 사용 가능하다

- 프로젝트 boot02 의 pom.xml 부분

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>

 

Lombok 기능을 써서 DTO 작성하기

- Lombok 기능을 써서 기존 프로젝트 boot02 의 DTO 클래스를 수정하자

- Member.java (DTO, lombok 사용해서 수정)

package com.example.demo.model;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;

//@Getter
//@Setter
@Data
public class Member {
	
	private String id;
	private String passwd;
	
//	public String getId() {
//		return id;
//	}
//	public void setId(String id) {
//		this.id = id;
//	}
//	public String getPasswd() {
//		return passwd;
//	}
//	public void setPasswd(String passwd) {
//		this.passwd = passwd;
//	}
}

Lombok 주요 어노테이션

- @Getter 어노테이션은 코드가 컴파일 될 때 속성들에 대해서 Getter 메소드들을 만들어준다

- @Setter 어노테이션은 코드가 컴파일 될 때 속성들에 대해서 Setter 메소드들을 만들어준다

- @ToSTring 어노테이션은 코드가 컴파일 될 때 속성들에 대해서 toString() 메소드를 생성

- @Data 어노테이션은 @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequriedArgsConstructor 를 한꺼번에 설정해주는 어노테이션

+ 위의 어노테이션들은 설치했던 lombok 클래스에서 지원하는 어노테이션들이다

 

프로젝트 실행

- STS 를 Restart 시키고 다시 실행해야한다

+ STS Restart 방법 : [File] - Restart

- 위 코드처럼 DTO 클래스를 수정 후 프로젝트 boot02 를 실행해보자