.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>
@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");
}
}
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랑 타입스크립트... 꼭 스프링 끝내고 나중에 공부해서 도전 해본다..
트위터에 연결해뒀더니 조회수가 늘었다. 행복