







.idea
*.iml
out
gen
.gradle
build

🍀 스프링 프레임 워크 8월 7일 내용
- root-context.xml(요청서)
- 싱글톤 ApplicationContext 객체
- 빈(객체)
- ⇒ 코드가 없는 형태의 클래스 등록
- @Service, @Respository, @Controller, @Component ⇒ 자동등록
- 의존성 주입
- @Autowired 싱글톤 객체를 주입해서 사용
- ORM
- O : Java Object(DTO, VO)
- R : RDB(table row)
- M : Mapping
- CRUD 작업
- Create(insert into)
- Read(select from)
- Update(update set)
- Delete(delete from)
- MyBatis Framework(
ORM기술을 실체화하여 제공하는 프레임워크)
- 스프링에서
싱글톤을 설정하고 사용하는 방법 2가지
- 설정파일 XML을 이용하는 방법 : root-context.xml
- @표시를 이용하는 방법
- 스프링을 배우는 이유?
- 서버측의 객체 생성을 최소화 시키는 것이 필요
- 되도록이면 객체 생성은 한번만 하자.(싱글톤 패턴)
xml 파일로 sql 작성package com.multicampus.springex.mapper;
import org.apache.ibatis.annotations.Select;
public interface TimeMapper2 { //데이터베이스의 현재시각을 문자열로 받아와서 처리
String getNow();
}
💥 주의 : 이름을 위에 Mapper와 똑같이 맞춰줘야 함
xml 파일로 sql 작성<?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="com.multicampus.springex.mapper.TimeMapper2">
<!--어노테이션이 아니라 xml 파일로 sql 작성-->
<select id="getNow" resultType="string">
select now()
</select>
</mapper>
3. ApplicationContext에 추가 해주기 - root-context.xml
<!--세션에다가 connection을 연결해주겠다 = sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- classpath는 resource의 경로 mappers라는 폴더 밑에 ** : 모든 폴더-->
<property name="mapperLocations" value="classpath:/mappers/**/*.xml"/>
</bean>
4. 테스트 코드 작성
package com.multicampus.springex.mapper;
import lombok.extern.log4j.Log4j2;
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;
@Log4j2
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/root-context.xml")
public class TimeMapperTests {
@Autowired(required = false)
private TimeMapper timeMapper;
@Autowired(required = false)
private TimeMapper2 timeMapper2;
@Test
public void testGetTime(){
log.info(timeMapper.getTime());
}
@Test
public void testGetNow(){
log.info(timeMapper2.getNow());
}
}


Front-Controller 패턴 적용
DispatcherServlet을 통하여 처리 ⇒ web.xml에 설정 <!--frontController를 쓰기 위해 서블릿 등록-->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--설정 정보, 위치값을 받아옴-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/servlet-context.xml</param-value>
</init-param>
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
<!--톰캣 로딩할 때 필요한 서블릿을 먼저 로딩-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2, 3. HandlerMapping
@RequestMapping 어노테이션이 적용된 것을 기준으로 판단package com.multicampus.springex.controller;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller //스프링 MVC에서 컨트롤러 역할, 스프링의 빈(Bean)으로 등록
@RequestMapping("/todo")
@Log4j2
public class TodoController {
@RequestMapping("/list") //localhost:8090/todo/list
public void list(){
log.info("todo_lis.....");
}
//@RequestMapping(value="/register", method = RequestMethod.GET) //localhost:8090/todo/register
@GetMapping("/register")
/*제대로 불러오는지 확인하는 작업, 제대로 불러오면 콘솔에 뜬다. */
public void register(){
log.info("GET todo_register.....");
}
// register은 post와 get방식 따로 따로
@PostMapping("/register")
public void registerPost(){
log.info("Post todo register");
}
}
View에 전달해야 하는 데이터는 주로 Model이라는 객체에 담아서 전달InternalResourceViewResolver <!--3. InternalResourceViewResolver 클래 : 스프링 MVC에서 제공하는 뷰를 어떻게 결정할 것인지 설정하는 담당하는 빈(객체)-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--prefix : MVC에서 사용했던 WEB-INF/view 경로-->
<property name="prefix" value="/WEB-INF/views/"></property>
<!--suffix : `.jsp` 확장자 지정-->
<property name="suffix" value=".jsp"></property>
</bean>
<!--자동으로 빈 설정해줘라-->
<context:component-scan base-package="com.multicampus.springex.controller"/>
모든 Request는 DispatcherServlet을 통하도록 설계되는데, 이런 방식을
Front-Controller패턴
- Front-Controller 패턴을 이용하면 전체 흐름을 강제로 제한 가능
EX. HttpServlet을 상속해서 만든 클래스를 이용하는 경우 특정 개발자는 이를 활용할 수 있지만 다른 개발자는 자신이 원래하던 방식대로 HttpServlet을 그대로 상속해서 개발 가능- 모든 Request의 처리에 대한 분배가정해진방식대로만 동작하기 때문에 좀 더 엄격한 구조를 만들수 있다.

DispatcherServlet이 Front Controller"보내다"라는 뜻을 가지고 있습니다.프론트 컨트롤러(Front Controller)디스패처 서블릿이 가장 먼저 받게 된다.DispatcherServlet이 등장함에 따라 web.xml의 역할 축소. 모든 서블릿을 URL 매핑을 위해 web.xml에 모두 등록해주어야 했지만, dispatcher-servlet이 해당 어플리케이션으로 들어오는 모든 요청을 핸들링해주고 공통 작업을 처리면서 편리하게 이용할 수 있게 되었다./apps 의 URL로 접근하면 Dispatcher Servlet이 담당한다./resources 의 URL로 접근하면 Dispatcher Servlet이 컨트롤할 수 없으므로 담당하지 않는다.관적인 설계가 될 수 없다.Dispatcher Servlet이 요청을 처리할 컨트롤러를 먼저 찾고, 요청에 대한 컨트롤러를 찾을 수 없는 경우에, 2차적으로 설정된 자원(Resource) 경로를 탐색하여 자원을 탐색.@Controller 어노테이션으로 컨트롤러로 설정hello() 메서드는 @GetMapping("/hello") 어노테이션으로 "/hello" 경로에 대한 요청을 처리하는 메서드


<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--요청 문서-->
<!--1. 스프링 MVC 설정을 에너테이션 기반으로 처리하겠다 의미-->
<!--스프링 MVC의 여러객체들을 자동으로 스프링빈으로 등록하겠다라는 선언-->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 2. 구조를 만든다는 의미 webapp 디렉토리 하위 resources 디렉토리(정적 파일들의 저장소)의 위치를 등록-->
<!--/resources 경로로 들어오는 요청은 정적파일을 요구하는 것으로 스프링 MVC에서 처리하지 않는다는 의미-->
<mvc:resources mapping="/resources/**" location="/resources/"></mvc:resources>
<!--3. InternalResourceViewResolver 클래 : 스프링 MVC에서 제공하는 뷰를 어떻게 결정할 것인지 설정하는 담당하는 빈(객체)-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--prefix : MVC에서 사용했던 WEB-INF/view 경로-->
<property name="prefix" value="/WEB-INF/views/"></property>
<!--suffix : `.jsp` 확장자 지정-->
<property name="suffix" value=".jsp"></property>
</bean>
<!--자동으로 빈 설정해줘라-->
<context:component-scan base-package="com.multicampus.springex.controller"/>
<!-- <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="com.multicampus.springex.controller.formatter.LocalDateFormatter"/>
<bean class="com.multicampus.springex.controller.formatter.CheckboxFormatter"/>
</set>
</property>
</bean>-->
<!--<mvc:annotation-driven conversion-service="conversionService" />-->
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--싱글톤 패턴형태로 등록-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<!--감시자를 넣음-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--frontController를 쓰기 위해 서블릿 등록-->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--설정 정보, 위치값을 받아옴-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/servlet-context.xml</param-value>
</init-param>
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
<!--톰캣 로딩할 때 필요한 서블릿을 먼저 로딩-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<servlet-name>appServlet</servlet-name>
</filter-mapping>
</web-app>
package com.multicampus.springex.controller;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller //스프링 MVC에서 컨트롤러 역할, 스프링의 빈(Bean)으로 등록
@Log4j2 //Lombok 라이브러리에서 제공하는 어노테이션으로, 로깅(loggin) 기능을 자동으로 구현해주는 기능을 제공
public class SampleController {
@GetMapping("/hello") //Get방식으로 들어오는 요청(request)을 처리하기 위한 에너테이션
public void hello(){
log.info("hello~"); //이것을 사용하기 위해 @Log4j2 선언
}
}

<property name="prefix" value="/WEB-INF/views/"></property>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>FrontController가 응답</title>
</head>
<body>
<h1>FrontController가 응답합니다.</h1>
<br/>
</body>
</html>
@RequestMappingpackage com.multicampus.springex.controller;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller //스프링 MVC에서 컨트롤러 역할, 스프링의 빈(Bean)으로 등록
@RequestMapping("/todo")
@Log4j2
public class TodoController {
@RequestMapping("/list") //localhost:8090/todo/list
public void list(){
log.info("todo_lis.....");
}
//@RequestMapping(value="/register", method = RequestMethod.GET) //localhost:8090/todo/register
@GetMapping("/register")
/*제대로 불러오는지 확인하는 작업, 제대로 불러오면 콘솔에 뜬다. */
public void register(){
log.info("GET todo_register.....");
}
// register은 post와 get방식 따로 따로
@PostMapping("/register")
public void registerPost(){
log.info("Post todo register");
}
}
SampleController 수정 - 기본값 넣어주기
1. 다양한 파라미터 수집해서 처리
2. 스프링MVC파라미터는 기본적으로 요청(request)에 전달된 파라미터 이름을 기준으로 동작
3. 간혹 @GetMapping @PostMapping @RequestMapping(/경로) ==> 전달이 안되는 경우
4. @RequestParame 파라미터로 사용된 변수의 이름과 전달되는 파라미터의 이름이 다른 경우 유용하게 사용
5. @InitBinder
- 파라미터 수집 = 바인딩
- 변환이 가능한 데이터는 자동으로 변환해서 처리해야 하는 경우도 존재
- ex1. 밑에랑 다른 방식으로도 가능

@DateTimeFormat

package com.multicampus.springex.controller;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.time.LocalDate;
@Controller //스프링 MVC에서 컨트롤러 역할, 스프링의 빈(Bean)으로 등록
@Log4j2 //Lombok 라이브러리에서 제공하는 어노테이션으로, 로깅(loggin) 기능을 자동으로 구현해주는 기능을 제공
public class SampleController {
@GetMapping("/hello") //Get방식으로 들어오는 요청(request)을 처리하기 위한 에너테이션
public void hello(){
log.info("hello~"); //이것을 사용하기 위해 @Log4j2 선언
}
@GetMapping("/ex1") //ex1을 만들기 전에는 http://localhost:8090/ex1?name=geumjulee&age=20을 넣어주면 동작시
public void ex1(String name, int age){
log.info("ex1.....");
log.info("name : "+name); //잘 받아졌다면 이것들이 출력 됨 geumjulee
log.info("age : "+age); //age 20으로 받아짐
}
@GetMapping("/ex2")
// exception 처리가 날 수 있으니 defaultValue로 default값 설정
public void ex2(@RequestParam(name="name", defaultValue="CCC") String name,
@RequestParam(name="age", defaultValue="20") int age
) {
log.info("ex2.....");
log.info("name : "+name);
log.info("age : "+age);
}
// 날자값이 문자열로 들어왔을 떄 LocalDate로 알아서 변환해주기 위해 LocalDateFormatter
@GetMapping("/ex3")
public void ex3(LocalDate dueDate){
log.info("ex3.....");
log.info(dueDate);
}
}
LocalDateFormatter : ex3에서 나온 LocalDate 변환을 위한 클래스package com.multicampus.springex.formatter;
import org.springframework.format.Formatter;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class LocalDateFormatter implements Formatter<LocalDate> {
@Override
public LocalDate parse(String text, Locale locale) throws ParseException {
return LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
// format이라는 외부 처리 빈 생성 => servlet-context.xml에서 빈 연결
@Override
public String print(LocalDate object, Locale locale) {
return DateTimeFormatter.ofPattern("yyyy-MM-dd").format(object);
}
}
servlet-context .xml 수정 : 빈을 위해 - 추가 부분<!--Formatter를 servelt-context.xml에 적용하여 스프링의 빈으로 등록
이 등록을 위해 FormattingConversionServiceFactoryBean객체를 스프링의 빈으로 등록-->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<!--빈을 넣어줌-->
<set>
<bean class="com.multicampus.springex.formatter.LocalDateFormatter"/>
<!--<bean class="com.multicampus.springex.controller.formatter.CheckboxFormatter"/>-->
</set>
</property>
</bean>
<!--conversionService이라는 빈을 등록 후 스프링 MVC를 처리할 때 mvc:annotation-driven 설정 반드시 필요-->
<mvc:annotation-driven conversion-service="conversionService" />

package com.multicampus.springex.dto;
import lombok.*;
import java.time.LocalDate;
@ToString
@Data //setter, getter, equals(), toString()등 메서드 자동 생성
@Builder
// 모든 속성에 대한 아규먼트 처리
@AllArgsConstructor //public Todo(Long tno, String title){}을 만들어줌
@NoArgsConstructor //Default 생성자를 만들어줌
public class TodoDTO {
private Long tno;
private String title;
private LocalDate dueDate;
private boolean finished;
private String writer;
}

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>FrontController가 응답</title>
</head>
<body>
<form action="/todo/register" method="post" >
<div>
Title : <input type="text" name="title">
</div>
<div>
DueDate : <input type="date" name="dueDate" value="2023-08-08">
</div>
<div>
Writer : <input type="text" name="writer">
</div>
<div>
Finished : <input type="checkbox" name="finished">
</div>
<div>
<button type="submit">Register</button>
</div>
</form>
</body>
</html>
@GetMapping("/ex4")
public void ex4(Model model){
log.info("ex4.....");
model.addAttribute("message", "Hello Spring project!");
}
@GetMapping("/ex4_1")
public void ex4Extra(@ModelAttribute("dto") TodoDTO todoDTO, Model model){
log.info("ex4_1.....");
log.info(todoDTO);
model.addAttribute("message", "Hello Spring project!");
}

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1> ${message}</h1>
<br />
<h1><c:out value="${message}"></c:out> </h1>
</body>
</html>


<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1> ${dto}</h1>
<br />
<h1><c:out value="${message}"></c:out> </h1>
</body>
</html>
Redirect 이용// ex5으로 들어가고 redirect로 해서 ex6으로 가게 됨
@GetMapping("/ex5")
public String ex5(RedirectAttributes redirectAttributes){
// 객체로 처리하겠다. RESPONCE에 추가하는 작업
redirectAttributes.addAttribute("name", "AACCCDDD");
redirectAttributes.addFlashAttribute("result", "success");
// 원하는 곳으로 이동
return "redirect:/ex6";
}
@GetMapping("/ex6")
public void ex6(){}

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1> Ex6 RedirectAttributes 결과</h1>
</body>
<h1>${result}</h1>
</html>
점점.. 공부해야하는 양은 많아지고 코드는 짧아지나, 어노테이션 공부할 게 많네 ㅠㅠㅠㅠ 와 근데 다이어트로 쓰는 인아웃 앱 공고냈네ㅠㅠㅠ node.js랑 타입스크립트... 꼭 스프링 끝내고 나중에 공부해서 도전 해본다..
트위터에 연결해뒀더니 조회수가 늘었다. 행복