BE - 시험

수현·2023년 10월 17일
0

Ucamp

목록 보기
18/19
post-thumbnail

📒 DB 설정

  • mariadb 실행
mysql -u boot(user이름) -p;
use boot_db; // db 사용 

desc customer; 
select * from customer; // table 조회 
  • table 생성
create table customer(
	id int(10) not null auto_increment primary key,
	name varchar(100) not null,
	email varchar(100) not null,
	age int(10),
	entry_date date,
 	UNIQUE KEY uk_name (email)
);
// alter table customer add unique(id);

insert into customer(name, email, age, entry_date) values ('gildong', 'gildong@naver.com', 20, '2023-10-01');
insert into customer(name, email, age, entry_date) values ('dooly', 'dooly@google.com', 25, '2023-10-05');
insert into customer(name, email, age, entry_date) values ('huidong', 'huidong@google.com', 18, '2023-09-05');
insert into customer(name, email, age, entry_date) values ('micole', 'micole@naver.com', 28, '2022-10-10');
insert into customer(name, email, age, entry_date) values ('ddochi', 'ddochi@google.com', 20, '2023-05-05');
commit;

📒 Spring Framework + MyBatis + JSP

📘 문제

  • 목록 조회 + 개별 조회

xml 관련 파일은 코드의 이해도를 위해 분할하여 추가하는 방식으로 작성하였으나, 시험때는 하단의 코드 복사해도 좋습니당

📚 DataSource

  • DB와 관계된 커넥션 정보를 담고있으며 빈으로 등록하여 인자로 넘겨준다. → 이 과정을 통해 Spring은 DataSource로 DB와의 연결을 획득한다.
    • DB 서버와의 연결을 해준다.
    • DB Connetion pooling기능
  • 이 전에 DBConn.java로 SQL + DB 연결 설정 정보를 저장
    • SQL변경하거나 DB 연결 바꿀때마다

📋 DB 설정_spring-bean-customer.xml

  • DB 설정
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="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">

	<!-- Properties file 정보 설정 -->
	<context:property-placeholder location="classpath:value.properties"/>
	
	<!-- DataSource 구현체인 HikariDataSource를 SpringBean으로 등록 -->
	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" 
		p:driverClassName="${db.driverClass}"
		p:jdbcUrl="${db.url}"
		p:username="${db.username}"
		p:password="${db.password}"
	/>
</beans>

📋 value.properties

  • 속성값을 properties 파일에 작성
db.driverClass=org.mariadb.jdbc.Driver
db.url=jdbc:mariadb://127.0.0.1:3306/boot_db?useUnicode=true&charaterEncoding=utf-8&useSSL=false&serverTimezone=UTC
db.username=boot
db.password=boot

myname=Spring
myprinter=printer
value1=JUnit
value2=AOP
value3=DI
printer1=stringPrinter
printer2=consolePrinter

✅ 테스트

CustomerDBTest.java

package myspring.customer;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:spring-beans-customer.xml")
public class CustomerDBTest {
	@Autowired
	DataSource dataSource;
	
	@Test
	public void conn() {
		try {
			Connection connection = dataSource.getConnection();
			DatabaseMetaData metaData = connection.getMetaData();
			System.out.println("DB Product Name : " + metaData.getDatabaseProductName()); //MariaDB
			System.out.println("DB Driver : " + metaData.getDriverName()); // MariaDB Connector/J
			System.out.println("DB URL : " + metaData.getURL()); // jdbc:mariadb://127.0.0.1/boot_db?user=boot&password=***&...
			System.out.println("DB Username : " + metaData.getUserName()); // boot
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

📚 SqlSession

  • sqlsessionfactory

    • setDataSource() 메서드의 인자로 hikaridatasource 들어옴
    • setConfigLocation() 으로 MyBatisConfig(sqlMapConfig) 파일 연결
    • setMapperLocations() mapping (*Mapper) 파일 연결
  • sqlsession

    • sql 실행 목적
    • SqlSessionFactory를 통해 취득한 SqlSession을 실행중인 트랜잭션에 할당함

📚 VO

📋 CustomerVO.java

  • myspring.customer.vo 패키지 생성
  • CustomerVO 클래스 생성
    • src/main/java 하단에 생성
package myspring.customer.vo;

public class CustomerVO {
	
	private Long id;
	private String name;
	private String email;
	private int age;
	private String entryDate;

	// 기본생성자 
	public CustomerVO() {}
	
	public CustomerVO(String name, String email, int age, String entryDate) {
		this.name = name;
		this.email = email;
		this.age = age;
		this.entryDate = entryDate;
	}
	public CustomerVO(Long id, String name, String email, int age, String entryDate) {
		this(name, email, age, entryDate);
		this.id = id;
	}

	// getter와 setter 생성
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getEntryDate() {
		return entryDate;
	}

	public void setEntryDate(String entryDate) {
		this.entryDate = entryDate;
	}

	// toString 생성
	@Override
	public String toString() {
		return "CustomerVO [id=" + id + ", name=" + name + ", email=" + email + ", age=" + age + ", entryDate="
				+ entryDate + "]";
	}
}

📋 sqlSession설정_spring-beans-customer.xml

// 하단에 추가 

<!-- Mybatis-spring의 SqlSessionFactoryBean을 SpringBean으로 등록 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
		<property name="mapperLocations">
			<list>
				<value>classpath:mybatis/*Mapper.xml</value>
			</list>
		</property>
	</bean>

<!-- SqlSessionTemplate -->
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg ref="sqlSessionFactory"/>
		
	</bean>

📋 sqlMapConfig.xml

  • src/main/resources 하단에 생성
    • mybatis 폴더 생성
    • log4j2 설정
    • CustomerVO 설정
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
	"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
	<!-- log4j2 설정 -->
	<settings>
         <setting name="defaultStatementTimeout" value="3"/> 
         <setting name="logImpl" value="LOG4J2"/>
    </settings>
    
	<typeAliases>
		<!-- CustomerVO 설정  -->
		<typeAlias alias="Customer" type="myspring.customer.vo.CustomerVO" />
	</typeAliases>

</configuration>

📋 log4j2.xml

  • src/main/resources 하단에 추가
  • appender를 이용해서 console에도 찍을 수 있고 file에 로그 정보 저장 가능
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
	<Appenders>
		<File name="File" fileName="./logs/logfile.log" append="true">
			<PatternLayout
				pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-5level %logger{36} - %msg%n" />
		</File>
		<Console name="console" target="SYSTEM_OUT">
			<PatternLayout
				pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-5level %logger{36} - %msg%n" />
		</Console>
	</Appenders>

	<Loggers>
		<Logger name="org.springframework" level="INFO" additivity="false" />
			
		<Logger name="myspring" level="DEBUG" />

		<Root level="DEBUG">
			<AppenderRef ref="console" level="DEBUG" />
			<AppenderRef ref="File" level="DEBUG" />
		</Root>
	</Loggers>
</Configuration>

✅ 테스트

CustomerDBTest.java

package myspring.customer;

import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import myspring.customer.vo.CustomerVO;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:spring-beans-customer.xml")
public class CustomerDBTest {
	@Autowired
	SqlSession sqlSession;
	
	
	@Test
	public void session() {
		CustomerVO customer = sqlSession.selectOne("customerNS.selectCustomerById", "dooly");
		System.out.println(customer);
	}

}

📚 Mapper

  • mapper 사용시 주의 사항
    • Mapper.xml과 Mapper 인터페이스의 메서드명 일치시키기
    • xml이 수정될 때마다 업데이트 필요
    • Mapper과 SqlSession 부르기 위해 연결하기 위한 설정 필요

📋 CustomerMapper.java

package myspring.customer.dao.mapper;

import java.util.List;

import myspring.customer.vo.CustomerVO;

public interface CustomerMapper {
	CustomerVO selectCustomerById(String id);
	List<CustomerVO> selectCustomerList();
}

📋 CustomerMapper.xml

  • src/mainresoures/mybatis 하단에 생성
<?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="myspring.customer.dao.mapper.CustomerMapper">
	<!-- 조회 쿼리는 id를 이용하여 실행 -->
	<!-- parameter에 들어가는 값이 {value} 변수로 들어옴  -->
	<select id="selectCustomerById" parameterType="string" resultType="Customer"> 
		select * from customer where name=#{value}
	</select>

	<select id="selectCustomerList" resultType="Customer">
		select * from customer order by id
	</select>
	
</mapper>

📋 mapper와 sqlSession 연결_spring-beans-customer.xml

// 하단에 추가

<!-- Mybatis-Spring의 MapperScannerConfigurer을 SpringBean으로 등록 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<!-- 참조하는 것 없어서 bean id 없어도 됨 -->
		<property name="basePackage" value="myspring.customer.dao.mapper" />
		<property name="sqlSessionTemplateBeanName" value="sqlSession"/>
	</bean>

✅ 테스트

CustomerDBTest.java

package myspring.customer;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import myspring.customer.dao.mapper.CustomerMapper;
import myspring.customer.vo.CustomerVO;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:spring-beans-customer.xml")
public class CustomerDBTest {
	@Autowired
	CustomerMapper customerMapper;
	
	@Test
	public void mapper() {
		// id가 메서드 이름이 되어 argument 전달 
		CustomerVO customer = customerMapper.selectCustomerById("dooly"); 
		System.out.println(customer);
	}

}

📚 DAO

📋 CustomerDao.java

  • src/main/java 하단에 생성
package myspring.customer.dao;

import java.util.List;

import myspring.customer.vo.CustomerVO;

public interface CustomerDao {
	public List<CustomerVO> readAll();
	public CustomerVO read(String id);
}

📋 CustomerDaoImpl.java

package myspring.customer.dao;

import java.util.List;

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

import myspring.customer.dao.mapper.CustomerMapper;
import myspring.customer.vo.CustomerVO;

@Repository("customerDao")
public class CustomerDaoImpl implements CustomerDao {
	@Autowired
	private CustomerMapper customerMapper;	
	
	@Override
	public CustomerVO read(String id) {
		CustomerVO customer  = customerMapper.selectCustomerById(id);
		return customer;
	}
	
	public List<CustomerVO> readAll() {
		List<CustomerVO> customerList = customerMapper.selectCustomerList();
		return customerList;
	}
}

📚 Service

📋 CustomerService.java

package myspring.customer.sevice;

import java.util.List;

import myspring.customer.vo.CustomerVO;

public interface CustomerService {
	public List<CustomerVO> getCustomerList();
	public CustomerVO getCustomer(String id);
}

📋 CustomerServiceImpl.java

package myspring.customer.sevice;

import java.util.List;

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

import myspring.customer.dao.CustomerDao;
import myspring.customer.vo.CustomerVO;

@Service("customerService")
public class CustomerServiceImpl implements CustomerService {

	@Autowired
	CustomerDao customerdao;

	public List<CustomerVO> getCustomerList() {
		return customerdao.readAll();
	}

	@Override
	public CustomerVO getCustomer(String id) {
		return customerdao.read(id);
	}

}

📋 dao와 service의 component scan_spring-beans-customer.xml

// 하단에 추가 
<!-- DAO, Service에 해당하는 Bean을 Scanning -->
	<context:component-scan base-package="myspring.customer" >
		<!-- 하단의  myspring.customer에서 controller만 제외하고 scan -->
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>

✅ 테스트

CustomerDBTest.java

package myspring.customer;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import myspring.customer.dao.mapper.CustomerMapper;
import myspring.customer.sevice.CustomerService;
import myspring.customer.vo.CustomerVO;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:spring-beans-customer.xml")
public class CustomerDBTest {
	@Autowired
	CustomerService customerService;
	
	@Test
	public void service() {
		CustomerVO customer = customerService.getUser("dooly");
		System.out.println(customer);
	}

}

📋 web.xml

  • contextLoadListener 추가

    • tomcat 메모리 상에 application context를 로드하는 것
    • param location 태그에 spring-beans-user.xml을 알려줘야함
  • dispatcherservlet 추가

    • servlet의 param location : controller 쪽에만 적용되는 xml 파일 있을 경우 적용
  • src/main/webapp/WEB-INF의 web.xml 수정

// 하단에 코드 추가 

 <!-- needed for ContextLoaderListener -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-beans-customer.xml</param-value>
	</context-param>

	<!-- Bootstraps the root web application context before servlet initialization -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
	<servlet>
		<servlet-name>springDispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-beans-customer.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

📋 index.js

  • tomcat 구동하고 index.jsp 통해 실행
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>customer list</title>
</head>
<body>
	<h1>고객 관리 메인</h1>
	<ul>
		<li><a href="customerList.do">고객 리스트</a></li>
	</ul>
</body>
</html>

📚 Controller

📋 beans-web.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
	
	<context:component-scan base-package="myspring.customer">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>

	<!-- Spring MVC에 필요한 Bean들을 자동으로 등록해주는 태그-->
	<mvc:annotation-driven />
	
	<!-- DispatcherServlet의  변경된 url-pattern 때문에 필요한 태그 설정 -->	
	<mvc:default-servlet-handler/>
	
	<!-- 아래 주석은 Controller에서  포워딩 되는  .jsp 확장자를  생략할 수 있다. -->
	<bean id="viewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/" />
		<property name="suffix" value=".jsp" />
	</bean>


	<!-- annotation-driven  태그에서 내부적으로 처리해주는 설정 -->
	<!-- <bean id="jsonHttpMessageConverter"  -->
	<!--         class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" /> -->
	<!-- <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> -->
	<!-- 	<property name="messageConverters"> -->
	<!-- 	  <list> -->
	<!-- 		<ref bean="jsonHttpMessageConverter"/> -->
	<!-- 	  </list> -->
	<!-- 	</property> -->
	<!-- </bean> -->

</beans>

📋 web.xml

// 하단에 추가 
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
	<servlet>
		<servlet-name>springDispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:beans-web.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

📋 customerList.jsp

  • 목록
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>고객 관리</title>
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

</head>
<body>
	<div class="container">
		<h2 class="text-center">고객 목록</h2>
		<table class="table table-bordered table table-hover"> 
			<thead> 
				<tr> 
					<th>번호</th>
					<th>이름</th>
					<th>이메일</th>
					<th>나이</th>
					<th>등록일</th>
				</tr> 
		</thead> 
		<tbody> 
			<c:forEach var="customer" items="${customerList}">
				<tr>
					<td>${customer.id}</td>
					<td>
					 	<a href="getCustomer.do?id=${customer.name}">${customer.name}</a>
					 </td>
					<td>${customer.email}</td>
					<td>${customer.age}</td>
					<td>${customer.entryDate}</td>
				</tr>
			</c:forEach>
		</tbody> 
	</table>
	</div>
</body>
</html>

📋 customerInfo.jsp

  • 상세 정보
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>고객 상세정보</title>
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
	<div class="container">
	    <h2 class="text-center">고객 상세정보</h2>
		<table class="table table-bordered table table-hover">
			<tr><td>이름 :</td><td>${customer.name}</td></tr>
			<tr><td>이메일 :</td><td>${customer.email}</td></tr>
			<tr><td>나이 :</td><td>${customer.age}</td></tr>
			<tr><td>등록일자 : </td><td>${customer.entryDate}</td></tr>
		</table>
	</div>
</body>
</html>

📋 CustomerController.java

package myspring.customer.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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import myspring.customer.sevice.CustomerService;
import myspring.customer.vo.CustomerVO;

@Controller
public class CustomerController {
	@Autowired
	private CustomerService customerService;

	
	// db에서 가져오고 화면에 보이는 것도 함께 설정 
	// View와 Model을 한꺼번에 전달하는 방법
	@RequestMapping("/customerList.do")
	public ModelAndView customerList() {
		// service 불러와서 리스트로 받기
		// 뿌려줄 jsp 페이지를 ModelAndView 객체에 담음 (viewName=jsp파일 이름(jsp확장자 없이 이름만 기재), modelName=키값(forEach구문의 items), modelList=서비스에서 받아온 list 기재)
		// key 값(customerList)과 일치하여 list 변수명 바꾸기 
		List<CustomerVO> customerVOList = customerService.getCustomerList();
		// ModelAndView(viewName, keyName, valueObject)
		return new ModelAndView("customerList", "customerList", customerVOList);
	}
	
	//getUser.do?id=dooly
	// View와 Model을 분리해서 전달하는 방법
	@RequestMapping("/getCustomer.do")
	public String getCutomer(@RequestParam("id") String userId, Model model) {
		// @requestparam을 이용하여 ?(쿼리 스트링) 다음의 id 값 가져올 수 있음
		// 받아온 customerVO를 model에 담아줌
		CustomerVO customerVO = customerService.getCustomer(userId);
		model.addAttribute("customer", customerVO);
		// 페이지 이름 return 
		return "customerInfo";
	}
	
}

✅ 테스트

  • localhost:8080 실행

📖 참고_파일 위치

📒 SpringBoot + JPA + REST API

📕 RestController + Repository

📘 문제

📖 참고

  • 해당 미션 시작 전 하단에 있는 application.properties + pom.xml 파일 복사해 그대로 설정
  • 예외시 정상 종료 및 개발자 의도에 맞는 에러 메시지 출력을 위한 advice + Business/System Exception 패키지 및 클래스 복사

📋 MyMissionAppliaction.java

📚 @SpringBootApplication

package com.mission.mymission;

import org.modelmapper.ModelMapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class MyMissionApplication {

	public static void main(String[] args) {
		SpringApplication application = new SpringApplication(MyMissionApplication.class);
		application.run(args);
	}
}

📋 Customer.java

📚 Entity 기능

📚 @Entity

📚 @Table

📚 @Id

📚 @GeneratedValue

📚 @Column

📚 CreationTimestamp

package com.mission.mymission.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;

import java.time.LocalDateTime;

@Entity
@Table(name = "customer")
@Getter @Setter
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String email;

    private int age;

    @Column(updatable = false)
    @CreationTimestamp
    private LocalDateTime entryDate = LocalDateTime.now();
}

📋 CustomerRepository.java

📚 Repository 기능

📚 Optional

📚 < Entity 클래스, PK값 >

package com.mission.mymission.repository;

import com.mission.mymission.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;

public interface CustomerRepository extends JpaRepository<Customer, Long> {
    // < Entity 클래스, PK값 >
    // Insert, Delete, Select만 존재

    Optional<Customer> findByEmail(String email);
    List<Customer> findByName(String name);
}

📋 CustomerController.java

📚 Controller 기능

📚 @RestController

📚 @RequestMapping

📚 @Autowired

📚 Get/Post/Put/Patch/Delete Mapping

  • Controller-Repository 연결
package com.mission.mymission.controller;

import com.mission.mymission.entity.Customer;
import com.mission.mymission.exception.BusinessException;
import com.mission.mymission.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/customer")
public class CustomerController {
    @Autowired
    private CustomerRepository customerRepository;
}
  • 등록
    @PostMapping
    public Customer create(@RequestBody Customer customer) {
        return customerRepository.save(customer);
    }

✅ 테스트

locolhost:8080(포트번호)/customer(테이블명)

  • 조회
    @GetMapping
    public List<Customer> getCustomers() {
        return customerRepository.findAll();
    }

✅ 테스트

locolhost:8080(포트번호)/customer(테이블명)

  • 상세정보 조회 (id/email/name)
    @GetMapping("/{id}")
    public Customer getCustomer(@PathVariable Long id) {
        Optional<Customer> optionalCustomer = customerRepository.findById(id);

        Customer customer = optionalCustomer.orElseThrow(() -> new BusinessException("Customer Not Found", HttpStatus.NOT_FOUND));
        return customer;
    }

    @GetMapping("/email/{email}")
    public Customer getCustomerByEmail(@PathVariable String email) {
        return customerRepository.findByEmail(email)
                .orElseThrow(() -> new BusinessException("Email이 존재하지 않습니다", HttpStatus.NOT_FOUND));
    }

    @GetMapping("/name/{name}")
    public List<Customer> getCustomerByName(@PathVariable String name) {
        return customerRepository.findByName(name);
}

✅ 테스트

locolhost:8080(포트번호)/customer(테이블명)

  • 삭제
    @DeleteMapping("/{id}")
    public ResponseEntity<?> deleteUser(@PathVariable Long id) {
       Customer customer = customerRepository.findById(id)
                .orElseThrow(() -> new BusinessException("Customer Not Found", HttpStatus.NOT_FOUND));
        customerRepository.delete(customer);
        return ResponseEntity.ok(id + " User가 삭제 되었습니다");
    }
}

✅ 테스트

locolhost:8080(포트번호)/customer(테이블명)

📕 RestController + Service + Repository

📘 문제

  • 이전 문제 + Service 추가

📋 CustomerReqDTO.java

  • 요청할 경우 정보를 담아줄 객체 생성
  • 변수로 선언 + @Getter/@Setter 할 경우 자동으로 get/set 메서드가 구현됨

📚 RequestDTO / ResponseDTO

📚 @NoArgsConstructor

📚 @AllArgsConstructor

📚 @Getter / @Setter

📚 @Email

package com.mission.mymission.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@AllArgsConstructor
@Getter @Setter
public class CustomerReqDTO {
    private String name;
    @Email(message = "Email 형식이 아닙니다")
    private String email;
    private int age;
}

📋 CustomerResDTO.java

  • 응답받을 경우 정보를 담아줄 객체 생성
  • 변수로 선언 + @Getter/@Setter 할 경우 자동으로 get/set 메서드가 구현됨
package com.mission.mymission.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.LocalDateTime;

@NoArgsConstructor
@AllArgsConstructor
@Getter @Setter
public class CustomerResDTO {
    private Long id;
    private String name;
    private String email;
    private int age;
    private LocalDateTime entryDate = LocalDateTime.now();
}

📋 CustomerService.java

📚 Service 기능

📚 @Service

📚 ModelMapper

📚 @RequiredConstructor

📚 Transactional

  • Service-Repository 연결
package com.mission.mymission.service;

import com.mission.mymission.dto.CustomerReqDTO;
import com.mission.mymission.dto.CustomerReqForm;
import com.mission.mymission.dto.CustomerResDTO;
import com.mission.mymission.entity.Customer;
import com.mission.mymission.exception.BusinessException;
import com.mission.mymission.repository.CustomerRepository;
import org.modelmapper.ModelMapper;
import lombok.RequiredArgsConstructor;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

@Service
@RequiredArgsConstructor
@Transactional
public class CustomerService {
    private final CustomerRepository customerRepository;
    private final ModelMapper modelMapper;
}
  • 등록
    public CustomerResDTO saveCustomer(CustomerReqDTO customerReqDTO) {
        Customer customer = modelMapper.map(customerReqDTO, Customer.class);
        Customer savedCustomer = customerRepository.save(customer);
        return modelMapper.map(savedCustomer, CustomerResDTO.class);
    }
  • 조회
    @Transactional(readOnly = true)
    public CustomerResDTO getCustomerById(Long id) {
        Customer customerEntity = customerRepository.findById(id) // return type : Optional<Customer>
                .orElseThrow(() -> new BusinessException(id + "User Not Found", HttpStatus.NOT_FOUND));
        // Entity -> ResDTO로 변환
        CustomerResDTO customerResDTO = modelMapper.map(customerEntity, CustomerResDTO.class);
        return customerResDTO;
    }
  • 전체 목록 조회
    @Transactional(readOnly = true)
    public List<CustomerResDTO> getCustomers() {
        List<Customer> customerList = customerRepository.findAll(); // List<Customer>

        // List<Customer> -> List<CustomerResDTO>
        List<CustomerResDTO> customerResDTOList = customerList.stream() // List<Customer> -> Stream<Customer>
                .map(customer -> modelMapper.map(customer, CustomerResDTO.class)) // Stream<Customer> -> Stream<CustomerResDTO>
                .collect(toList());// Stream<CustomerResDTO> -> List<CustomerResDTO>

        return customerResDTOList;
    }
  • 수정
    public CustomerResDTO updateCustomer(String email, CustomerReqDTO customerReqDto) {
        Customer existCustomer = customerRepository.findByEmail(email)
                .orElseThrow(() ->
                        new BusinessException(email + " User Not Found", HttpStatus.NOT_FOUND));

        if (customerReqDto.getName() != null)
            existCustomer.setName(customerReqDto.getName());
        if (customerReqDto.getAge() != 0)
            existCustomer.setAge(customerReqDto.getAge());
        return modelMapper.map(existCustomer, CustomerResDTO.class); // Customer -> CustomerResDTO
    }
  • 삭제
    public void deleteCustomer(Long id) {
        Customer customer = customerRepository.findById(id) //Optional<Customer>
                .orElseThrow(() ->
                        new BusinessException(id + " User Not Found", HttpStatus.NOT_FOUND));
        customerRepository.delete(customer);
    }
}

📋 CustomerController2.java

  • Controller-Service 연결
package com.mission.mymission.controller;

import com.mission.mymission.dto.CustomerReqDTO;
import com.mission.mymission.dto.CustomerResDTO;
import com.mission.mymission.service.CustomerService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/customer")
@RequiredArgsConstructor
public class CustomerController2 {
    private final CustomerService customerService;
}
  • 등록
    @PostMapping
    public CustomerResDTO saveCustomer(@RequestBody CustomerReqDTO customerReqDTO) {
        return customerService.saveCustomer(customerReqDTO);
    }

✅ 테스트

locolhost:8080(포트번호)/api/customer(테이블명)

  • 조회
    @GetMapping("/{id}")
    public CustomerResDTO getCustomerById(@PathVariable Long id) {
        return customerService.getCustomerById(id);
    }

✅ 테스트

locolhost:8080(포트번호)/api/customer(테이블명)

  • 전체 목록 조회
    @GetMapping
    public List<CustomerResDTO> getCustomers() {
        return customerService.getCustomers();
    }

✅ 테스트

locolhost:8080(포트번호)/api/customer(테이블명)

  • 수정
    @PatchMapping("/{email}")
    public CustomerResDTO updateCustomer(@PathVariable String email, @RequestBody CustomerReqDTO customerReqDTO) {
        return customerService.updateCustomer(email, customerReqDTO);
    }

✅ 테스트

locolhost:8080(포트번호)/api/customer(테이블명)

  • 삭제
    @DeleteMapping("/{id}")
    public ResponseEntity<?> deleteCustomer(@PathVariable Long id) {
        customerService.deleteCustomer(id);
        return ResponseEntity.ok(id + " Customer가 삭제처리 되었습니다.");
    }
}

✅ 테스트

locolhost:8080(포트번호)/api/customer(테이블명)

📒 SpringBoot + JPA + Thymeleaf

📕 Controller + Service + Repository

📘 문제

  • 이전 문제 + IndexController 추가 + thymeleaf 추가

📋 IndexController.java

📚 IndexController

package com.mission.mymission.controller;

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

@Controller
public class IndexController {
    @GetMapping("/")
    public String index() {
        return "redirect:/cpage/index";
    }
}

📋 CustomerReqDTO.java

  • 등록/수정시 값이 입력되지 않을 경우 메시지 출력

📚 @Valid

📚 @NotEmpty

📚 @NotBlank

package com.mission.mymission.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@AllArgsConstructor
@Getter @Setter
public class CustomerReqDTO {
    @NotEmpty(message = "Name은 필수 입력 항목입니다") // " " 허용
    private String name;
    @NotBlank(message = "Email은 필수 입력 항목입니다") // " " 허용하지 않음
    @Email(message = "Email 형식이 아닙니다")
    private String email;
    private int age;
}

📋 index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<body>
<table border="1">
    <tr>
        <th>Id</th>
        <th>Name</th>
        <th>Email</th>
        <th>Age</th>
        <th>entryDate</th>
        <th>Update</th>
        <th>Delete</th>
    </tr>

    <tr th:each="customer, customerStat : ${customer}">
        <td th:text="${customerStat.count}"></td> <!-- id 연속적인 수로 출력 -->
        <td th:text="${customer.name}"></td>
        <td th:text="${customer.email}"></td>
        <td th:text="${customer.age}"></td>
        <td th:text="${#temporals.format(customer.entryDate, 'yyyy-MM-dd hh:mm')}"></td>
        <td><a th:href="@{/cpage/edit/{id}(id=${customer.id})}">Update</a></td>
        <td><a th:href="@{/cpage/delete/{id}(id=${customer.id})}">Delete</a></td>
    </tr>
</table>
<p><a href="/cpage/signup">Insert</a></p>
</body>
</html>

📋 add-customer.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<body>
<form action="#" th:action="@{/cpage/addcustomer}" th:object="${customerReqDTO}" method="post">
    <label for="name">Name</label>
    <input type="text" th:field="*{name}" id="name">
    <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
    <br/>
    <label for="email">Email</label>
    <input type="text" th:field="*{email}" id="email">
    <span th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></span>
    <br/>
    <label for="age">Age</label>
    <input type="text" th:field="*{age}" id="age">
    <span th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></span>
    <br/>
    <input type="submit" value="Add Customer">
</form>
</body>
</html>

📋 update-customer.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<body>
<form action="#" th:action="@{/cpage/update/{id}(id=${customer.id})}" th:object="${customer}" method="post">
    <label for="name">Name</label>
    <input type="text" th:field="*{name}" id="name">
    <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span> <br />
    <label for="email">Email</label>
    <input type="text" th:field="*{email}" id="email" readonly>
    <span th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></span> <br />
    <label for="age">Age</label>
    <input type="text" th:field="*{age}" id="age">
    <span th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></span> <br />
    <input type="submit" value="Update Customer">
</form>
</body>
</html>

📋 CustomerController3.java

  • Controller-Service 연결
package com.mission.mymission.controller;

import com.mission.mymission.dto.CustomerReqDTO;
import com.mission.mymission.dto.CustomerReqForm;
import com.mission.mymission.dto.CustomerResDTO;
import com.mission.mymission.service.CustomerService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@Controller
@RequestMapping("/cpage")
@RequiredArgsConstructor
public class CustomerController3 {
    private final CustomerService customerService;
}
  • 초기화면(목록) 출력
    @GetMapping("/index")
    public ModelAndView index() {
        List<CustomerResDTO> customerResDTOList = customerService.getCustomers();
        return new ModelAndView("index", "customer", customerResDTOList);
    }
  • 등록
    // 등록 폼 호출
    @GetMapping("/signup")
    public String showSignUpForm(CustomerReqDTO customer) {
        return "add-customer";
    }

    // 등록
    @PostMapping("/addcustomer")
    public String addCustomer(@Valid CustomerReqDTO customer, BindingResult result, Model model) {
        if (result.hasErrors()) {
            return "add-customer"; // 이전 페이지 그대로 유지
        }
        customerService.saveCustomer(customer);
        return "redirect:/cpage/index"; // 등록 후 첫화면으로 돌아감
    }
  • 수정
    // 수정 폼 호출
    @GetMapping("/edit/{id}")
    public String showUpdateForm(@PathVariable Long id, Model model){
        CustomerResDTO customerResDTO = customerService.getCustomerById(id);
        model.addAttribute("customer", customerResDTO);
        return "update-customer";
    }

    // 수정
    @PostMapping("/update/{id}")
    public String updateCustomer(@PathVariable("id") Long id, @Valid CustomerReqForm customer, BindingResult result, Model model) {
        if (result.hasErrors()) {
            model.addAttribute("customer", customer);
            return "update-customer";
        }
        customerService.updateCustomerForm(customer);
        return "redirect:/cpage/index";
    }
  • 삭제
    @GetMapping("/delete/{id}")
    public String deleteCustomer(@PathVariable("id") Long id) {
        customerService.deleteCustomer(id);
        return "redirect:/cpage/index";
    }
}

📋 CustomerService.java

  • Controller 부분에서 수정 기능이 id를 Service에 전달하여 수정하기 때문에, 기존 구문에 없었던 id 부분 추가 필요
    public void updateCustomerForm(CustomerReqForm customerReqForm) {
        Customer exist = customerRepository.findById(customerReqForm.getId())
                                            .orElseThrow(() ->
                                                    new BusinessException(customerReqForm.getId() + " User Not Found", HttpStatus.NOT_FOUND));
        exist.setName(customerReqForm.getName());
        exist.setAge(customerReqForm.getAge());
    }

📋 CustomerReqForm.java

  • 상단과 동일한 이유로 id 값 포함한 DTO 객체 따로 생성
package com.mission.mymission.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.*;

@NoArgsConstructor // 기본 생성자 생성
@AllArgsConstructor
@Getter @Setter
@ToString
public class CustomerReqForm {
    private Long id;

    @NotEmpty(message = "Name은 필수 입력 항목입니다") // " " 허용
    private String name;

    @NotBlank(message = "Email은 필수 입력 항목입니다") // " " 허용하지 않음
    @Email(message = "Email 형식이 아닙니다")
    private String email;

    private int age;
}

✅ 테스트

locolhost:8080(포트번호)/cpage/index

📖 참고_파일 위치

  • 등록

  • 수정

  • 삭제

📖 참고 (MySpringCustomer/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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.maven.spring</groupId>
	<artifactId>MySpringFW</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<name>CustomerSpringWeb Maven Webapp</name>
	<!-- FIXME change it to the project's website -->
	<url>http://www.example.com</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
		<spring.version>5.2.25.RELEASE</spring.version>
	</properties>

	<dependencies>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-test</artifactId>
		    <version>${spring.version}</version>
		    <scope>test</scope>
		    <!-- scope : test라고 이름 지어진 폴더에만 적용 -->
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
		<dependency>
		    <groupId>org.mariadb.jdbc</groupId>
		    <artifactId>mariadb-java-client</artifactId>
		    <version>3.1.4</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-jdbc</artifactId>
		    <version>${spring.version}</version>
		</dependency>
		
		<!-- Hikari Connection Pooling -->
		<dependency>
		   <groupId>com.zaxxer</groupId>
		   <artifactId>HikariCP</artifactId>
		   <version>4.0.3</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis</artifactId>
		    <version>3.5.6</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis-spring</artifactId>
		    <version>2.0.6</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>2.11.0</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-webmvc</artifactId>
		    <version>${spring.version}</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		
	</dependencies>

	<build>
		<finalName>MySpringFW</finalName>
		<pluginManagement><!-- lock down plugins versions to avoid using Maven 
				defaults (may be moved to parent pom) -->
			<plugins>
				<plugin>
					<artifactId>maven-clean-plugin</artifactId>
					<version>3.1.0</version>
				</plugin>
				<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
				<plugin>
					<artifactId>maven-resources-plugin</artifactId>
					<version>3.0.2</version>
				</plugin>
				<plugin>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.8.0</version>
				</plugin>
				<plugin>
					<artifactId>maven-surefire-plugin</artifactId>
					<version>2.22.1</version>
				</plugin>
				<plugin>
					<artifactId>maven-war-plugin</artifactId>
					<version>3.2.2</version>
				</plugin>
				<plugin>
					<artifactId>maven-install-plugin</artifactId>
					<version>2.5.2</version>
				</plugin>
				<plugin>
					<artifactId>maven-deploy-plugin</artifactId>
					<version>2.8.2</version>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>
</project>

📖 참고 (application.properties)

  • DB, port 등 설정 정보 파일
# 서버 포트 설정
#server.port=8087

# 한글 사용하기 위한 스프링 유니코드 작성
# (applcation-prod/test.properties에서 설정)
myboot.name=\uc2a4\ud504\ub9c1 
myboot.fullName=${myboot.name} Boot

# mariaDB 연결 정보 설정
spring.datasource.url=jdbc:mariadb://127.0.0.1:3306/boot_db
spring.datasource.username=boot
spring.datasource.password=boot
spring.datasource.driverClassName=org.mariadb.jdbc.Driver

# JPA를 사용한 데이터베이스 초기화
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

# DB Dialect 설정
spring.jpa.database-platform=org.hibernate.dialect.MariaDBDialect

📖 참고 (pom.xml)

  • depency 등 의존성 설정 파일
<?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>3.0.11</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.basic</groupId>
	<artifactId>MySpringBoot3</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>MySpringBoot3</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
		<spring-framework.version>6.0.12</spring-framework.version> <!-- spring framework 버전 바꾸기 가능 (spring-boot-dependencies에서 추가)-->
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.mariadb.jdbc</groupId>
			<artifactId>mariadb-java-client</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-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- log4j2 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j2</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
		<dependency>
			<groupId>org.modelmapper</groupId>
			<artifactId>modelmapper</artifactId>
			<version>3.1.1</version>
		</dependency>
		<!-- thymeleaf -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</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>

📖 참고 (Exception)

  • BusinessException/SystemException으로 구분
  • java폴더 -> com.패키지 -> exception 패키지 생성 -> 하단 코드 복사
  • runtime에 발생하는 에러를 확인할 목적으로 에러 처리 필요

📋 BusinessException.java

package com.mission.mymission.exception;

import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
public class BusinessException extends RuntimeException {
	
	private static final long serialVersionUID = 1L;
    private String message;
    private HttpStatus httpStatus;

    public BusinessException(String message) {
        //417
        this(message, HttpStatus.EXPECTATION_FAILED);
    }

    public BusinessException(String message, HttpStatus httpStatus) {
        this.message = message;
        this.httpStatus = httpStatus;
    }
}

📋 SystemException.java

package com.mission.mymission.exception;

import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
public class SystemException extends RuntimeException {
	private static final long serialVersionUID = 1L;
	private String message;
    private HttpStatus httpStatus;
    private Throwable throwable; 

    public SystemException(Exception e) {
        this(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    public SystemException(String message) {
        this(message, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    public SystemException(String message, Throwable t) {
    	this.message = message;
    	this.throwable =t;
    }
    
    public SystemException(Throwable t) {
    	this.throwable = t;
    }
    
    public SystemException(String message, HttpStatus httpStatus) {
        this.message = message;
        this.httpStatus = httpStatus;
    }
    
    public Throwable getThrowable() {
    	return this.throwable;
    }
}

📖 참고 (Advice)

  • java폴더 -> com.패키지 -> advice 패키지 생성 -> 하단 코드 복사
  • Exception에서 에러가 발생했을 때 원하는 포맷(message + Http Status)으로 메시지 출력

📋 DefaultExceptionAdvice.java

package com.mission.mymission.advice;

import com.mission.mymission.exception.BusinessException;
import com.mission.mymission.exception.SystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

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

@RestControllerAdvice
public class DefaultExceptionAdvice {
	private final Logger LOGGER = LoggerFactory.getLogger(DefaultExceptionAdvice.class);


    @ExceptionHandler(BusinessException.class)
    protected ResponseEntity<Object> handleException(BusinessException e) {
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("message", "[안내] " + e.getMessage());
        result.put("httpStatus", e.getHttpStatus().value());

        return new ResponseEntity<>(result, e.getHttpStatus());
    }
    
    @ExceptionHandler(SystemException.class)
    protected ResponseEntity<Object> handleException(SystemException e) {
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("message", "[시스템 오류] " + e.getMessage());
        result.put("httpStatus", e.getHttpStatus().value());

        return new ResponseEntity<>(result, e.getHttpStatus());
    }

    //숫자타입의 값에 문자열타입의 값을 입력으로 받았을때 발생하는 오류
    @ExceptionHandler(HttpMessageNotReadableException.class)
    protected ResponseEntity<Object> handleException(HttpMessageNotReadableException e) {
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("message", e.getMessage());
        result.put("httpStatus", HttpStatus.BAD_REQUEST.value());

        return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(Exception.class)
    protected ResponseEntity<Object> handleException(Exception e) {
        Map<String, Object> result = new HashMap<String, Object>();
        ResponseEntity<Object> ret = null;
        
        if (e instanceof BusinessException) {
        	BusinessException b = (BusinessException) e;
        	result.put("message", "[안내]\n" + e.getMessage());
        	result.put("httpStatus", b.getHttpStatus().value());
        } else if ( e instanceof SystemException) {
    		SystemException s = (SystemException)e;
            result.put("message", "[시스템 오류]\n" + s.getMessage());
            result.put("httpStatus", s.getHttpStatus().value());
            ret = new ResponseEntity<>(result, s.getHttpStatus());
            
            LOGGER.error(s.getMessage(), s);
    	 } else {
    		String msg = "예상치 못한 문제가 발생했습니다.\n관리자에게 연락 하시기 바랍니다.";
	        result.put("message", msg);
	        result.put("httpStatus", HttpStatus.INTERNAL_SERVER_ERROR.value());
	        ret = new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
	        e.printStackTrace();
	        
            LOGGER.error(e.getMessage(), e);
    	}
        return ret;
    }
}

  • 인증할 때 email을 이용해서 인증하도록 구현 했기 때문에 이제 username이 아닌 email/pwd 이용

    • authenticationProvide 설정 필요해서 에러 발생
  • SecurityConfig.java 수정

    • 직접 작성한 UserDetailsService가 아닌 직접 custom한 implement받은 UserDetailsService를 연결 설정 필요
package com.basic.myspringboot.security.config;

import com.basic.myspringboot.security.service.UserInfoUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity // 인가
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> {
                    auth.requestMatchers("/api/users/welcome",
                                    "/userspage/**",
                                    "/userinfos/new").permitAll()
                            .requestMatchers("/api/users/**").authenticated();
                })
                .formLogin(withDefaults())
                .build();
    }


    @Bean
    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new UserInfoUserDetailsService();
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider
                = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService
                (userDetailsService());
        authenticationProvider.setPasswordEncoder
                (passwordEncoder());
        return authenticationProvider;
    }
//    @Bean
//    //authentication
//    public UserDetailsService userDetailsService (PasswordEncoder encoder){
//        UserDetails admin = User.withUsername("adminboot")
//                .password(encoder.encode("pwd1"))
//                .roles("ADMIN")
//                .build();
//        UserDetails user = User.withUsername("userboot")
//                .password(encoder.encode("pwd2"))
//                .roles("USER")
//                .build();
//        return new InMemoryUserDetailsManager(admin, user);
//    }
}

📖 CustomerMapper.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="myspring.customer.dao.mapper.CustomerMapper">
	<!-- 조회 쿼리는 id를 이용하여 실행 -->
	<!-- parameter에 들어가는 값이 {value} 변수로 들어옴  -->
	<select id="selectCustomerById" parameterType="string" resultType="Customer"> 
		select * from customer where name=#{value}
	</select>

	<select id="selectCustomerList" resultType="Customer">
		select * from customer order by id
	</select>
	
</mapper>

📖 mybatis/SqlMapConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
	"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
	<!-- log4j2 설정 -->
	<settings>
         <setting name="defaultStatementTimeout" value="3"/> 
         <setting name="logImpl" value="LOG4J2"/>
    </settings>
    
	<typeAliases>
		<!-- CustomerVO 설정  -->
		<typeAlias alias="Customer" type="myspring.customer.vo.CustomerVO" />
	</typeAliases>

</configuration>

📖 beans-web.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
	
	<context:component-scan base-package="myspring.customer">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>

	<!-- Spring MVC에 필요한 Bean들을 자동으로 등록해주는 태그-->
	<mvc:annotation-driven />
	
	<!-- DispatcherServlet의  변경된 url-pattern 때문에 필요한 태그 설정 -->	
	<mvc:default-servlet-handler/>
	
	<!-- 아래 주석은 Controller에서  포워딩 되는  .jsp 확장자를  생략할 수 있다. -->
	<bean id="viewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/" />
		<property name="suffix" value=".jsp" />
	</bean>


	<!-- annotation-driven  태그에서 내부적으로 처리해주는 설정 -->
	<!-- <bean id="jsonHttpMessageConverter"  -->
	<!--         class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" /> -->
	<!-- <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> -->
	<!-- 	<property name="messageConverters"> -->
	<!-- 	  <list> -->
	<!-- 		<ref bean="jsonHttpMessageConverter"/> -->
	<!-- 	  </list> -->
	<!-- 	</property> -->
	<!-- </bean> -->

</beans>

📖 log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
	<Appenders>
		<File name="File" fileName="./logs/logfile.log" append="true">
			<PatternLayout
				pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-5level %logger{36} - %msg%n" />
		</File>
		<Console name="console" target="SYSTEM_OUT">
			<PatternLayout
				pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-5level %logger{36} - %msg%n" />
		</Console>
	</Appenders>

	<Loggers>
		<Logger name="org.springframework" level="INFO" additivity="false" />
			
		<Logger name="myspring" level="DEBUG" />

		<Root level="DEBUG">
			<AppenderRef ref="console" level="DEBUG" />
			<AppenderRef ref="File" level="DEBUG" />
		</Root>
	</Loggers>
</Configuration>

📖 spring-beans-customer.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="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">

	<!-- Properties file 정보 설정 -->
	<context:property-placeholder location="classpath:value.properties"/>
	
	<!-- DataSource 구현체인 HikariDataSource를 SpringBean으로 등록 -->
	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" 
		p:driverClassName="${db.driverClass}"
		p:jdbcUrl="${db.url}"
		p:username="${db.username}"
		p:password="${db.password}"
	/>
	
	<!-- Mybatis-spring의 SqlSessionFactoryBean을 SpringBean으로 등록 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
		<property name="mapperLocations">
			<list>
				<value>classpath:mybatis/*Mapper.xml</value>
			</list>
		</property>
	</bean>
	
	<!-- SqlSession으로 sql 실행  -->
	<!-- SqlSessionFactory를 통해 취득한 SqlSession을 실행중인 트랜잭션에 할당함 -->
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg ref="sqlSessionFactory"/>
	</bean>
	
	<!-- Mybatis-Spring의 MapperScannerConfigurer을 SpringBean으로 등록 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<!-- 참조하는 것 없어서 bean id 없어도 됨 -->
		<property name="basePackage" value="myspring.customer.dao.mapper" />
		<property name="sqlSessionTemplateBeanName" value="sqlSession"/>
	</bean>
	
	<!-- DAO, Service에 해당하는 Bean을 Scanning -->
	<context:component-scan base-package="myspring.customer" >
		<!-- 하단의  myspring.customer에서 controller만 제외하고 scan -->
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>
</beans>

📖 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_3_1.xsd" 
	id="WebApp_ID" version="3.1">
  <display-name>CustomerSpringWeb</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  <!-- needed for ContextLoaderListener -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-beans-customer.xml</param-value>
	</context-param>

	<!-- Bootstraps the root web application context before servlet initialization -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
	<servlet>
		<servlet-name>springDispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:beans-web.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>
  
</web-app>

📖 MySpringCustomer/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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.maven.spring</groupId>
	<artifactId>MySpringFW</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<name>CustomerSpringWeb Maven Webapp</name>
	<!-- FIXME change it to the project's website -->
	<url>http://www.example.com</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
		<spring.version>5.2.25.RELEASE</spring.version>
	</properties>

	<dependencies>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-test</artifactId>
		    <version>${spring.version}</version>
		    <scope>test</scope>
		    <!-- scope : test라고 이름 지어진 폴더에만 적용 -->
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
		<dependency>
		    <groupId>org.mariadb.jdbc</groupId>
		    <artifactId>mariadb-java-client</artifactId>
		    <version>3.1.4</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-jdbc</artifactId>
		    <version>${spring.version}</version>
		</dependency>
		
		<!-- Hikari Connection Pooling -->
		<dependency>
		   <groupId>com.zaxxer</groupId>
		   <artifactId>HikariCP</artifactId>
		   <version>4.0.3</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis</artifactId>
		    <version>3.5.6</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis-spring</artifactId>
		    <version>2.0.6</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>2.11.0</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-webmvc</artifactId>
		    <version>${spring.version}</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		
	</dependencies>

	<build>
		<finalName>MySpringFW</finalName>
		<pluginManagement><!-- lock down plugins versions to avoid using Maven 
				defaults (may be moved to parent pom) -->
			<plugins>
				<plugin>
					<artifactId>maven-clean-plugin</artifactId>
					<version>3.1.0</version>
				</plugin>
				<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
				<plugin>
					<artifactId>maven-resources-plugin</artifactId>
					<version>3.0.2</version>
				</plugin>
				<plugin>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.8.0</version>
				</plugin>
				<plugin>
					<artifactId>maven-surefire-plugin</artifactId>
					<version>2.22.1</version>
				</plugin>
				<plugin>
					<artifactId>maven-war-plugin</artifactId>
					<version>3.2.2</version>
				</plugin>
				<plugin>
					<artifactId>maven-install-plugin</artifactId>
					<version>2.5.2</version>
				</plugin>
				<plugin>
					<artifactId>maven-deploy-plugin</artifactId>
					<version>2.8.2</version>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>
</project>
profile
Notion으로 이동 (https://24tngus.notion.site/3a6883f0f47041fe8045ef330a147da3?v=973a0b5ec78a4462bac8010e3b4cd5c0&pvs=4)

0개의 댓글