TIL 0617

먼지·2024년 6월 16일

Today I Learned

목록 보기
78/89
post-thumbnail

이론

스프링 AOP

로깅과 같은 기본적인 기능에서부터 트랜잭션이나 보안과 같은 기능에 이르기까지 어플리케이션 전반에 걸쳐 적용되는 공통기능을 핵심 기능과 분리해서 모듈로 구현한 뒤 핵심 기능이 수행될 때 필요로 하는 곳에 공통기능이 적용되게 하는 기능이다.

AOP 소개

AOP(Aspect Oriented Programming)는 문제를 바라보는 관점을 기준으로 프로그래밍하는 기법. 공통 관심 사항을 구현한 코드를 핵심 로직을 구현한 코드 안에 삽입하는 것을 의미.

AOP 용어

용어
Advice언제 공통 관심 기능을 핵심 로직에 적용할 지를 정의.
JoinpointAdvice를 적용 가능한 지점을 의미. 메서드 호출, 필드 값 변경 등이 Joinpoint에 해당
PointcutJoinpoint의 부분 집합으로서 실제로 Advice가 적용되는 Joinpoint를 나타냄.
WeavingAdvice를 핵심 로직 코드에 적용하는 것을 weaving이라고 함.
Aspect여러 객체에 공통으로 적용되는 공통 관심 사항

세 가지 Weaving 방식

  • 컴파일 시점에 코드에 공통 기능을 추가하는 방법(스프링 기본 지원이 아니며 AspectJ와 같이 AOP를 위한 전용 도구를 사용해야 함)
  • 클래스 로딩 시점에 바이트 코드에 공통 기능을 추가하는 방법(스프링 기본 지원이 아니며 AspectJ와 같이 AOP를 위한 전용 도구를 사용해야 함)
  • 런타임에 프록시 객체를 생성해서 공통 기능을 추가하는 방법(스프링이 제공하는 AOP 방식)

스프링에서의 AOP

  • XML 스키마 기반의 POJO 클래스를 이용한 AOP 구현
  • @Aspect 어노테이션 기반의 AOP 구현

구현 가능한 Advice 종류

종류설명
Before Advice대상 객체의 메서드 호출 전에 공통 기능을 실행
After Returning Advice대상 객체의 메서드가 예외 없이 실행한 이후에 공통 기능을 실행
After Throwing Advice대상 객체의 메서드를 실행하는 도중 예외가 발생한 경우에 공통 기능을 실행
After Advice대상 객체의 메서드를 실행하는 도중에 예외가 발생했는지의 여부와 상관없이 메서드 실행 후 공통 기능을 실행.
Around Advice대상 객체의 메서드 실행 전, 후 또는 예외 발생 시점에 공통 기능을 실행하는데 사용

XML 스키마를 이용한 설정

<!-- Aspect 설정 : Advice를 어떤 Pointcut에 적용할지 설정 -->
<aop:config>
	<aop:aspect id="traceAspect1" ref="performanceTraceAdvice">
		<aop:pointcut expression="execution(public * com.spring.board..*(..))" id="publicMethod"/>
		<aop:around method="trace" pointcut-ref="publicMethod"/>
	</aop:aspect>
</aop:config>
	
<bean id="writeArticleService" class="com.spring.board.service.WriteArticleServiceImpl">
	<constructor-arg>
		<ref bean="articleDao"/>
	</constructor-arg>
</bean>
<bean id="articleDao"  class="com.spring.board.dao.WriteArticleDao"/>
태그
<aop:config>AOP 설정 정보임을 나타냄
<aop:aspect>Aspect를 설정함
<aop:pointcut>Pointcut을 설정함
<aop:around>Around Advice를 설정함

Advice 정의 관련 태그

종류설명
<aop:before>메서드 실행 전에 적용되는 Advice를 정의한다.
<aop:after-returning>메서드 정상적으로 실행된 후에 적용되는 Advice를 정의한다.
<aop:after-throwing>메서드가 예외를 발생시킬 때 적용되는 Advice를 정의한다. try-catch블록에서 catch 블록과 비슷하다.
<aop:after>메서드가 적상적으로 실행되느니지 또는 예외를 발생시키는지 여부에 상관없이 적용되는 Advice를 정의한다. try-catch-finally에서 finally 블록과 비슷하다.
<aop:around>메서드 호출 이전, 이후, 예외 발생 등 모든 시점에 적용 가능한 Advice를 정의한다.

AspectJ의 Pointcut 표현식

<aop:aspect id="traceAspect1" ref="performanceTraceAdvice">
		<aop:pointcut expression="execution(public * com.spring.board..*(..))" id="publicMethod"/>
		<aop:around method="trace" pointcut-ref="publicMethod"/>
	</aop:aspect>

execution 명시자는 Advice를 적용할 메서드를 명시할 때 사용됨

execution(식어패턴 리턴타입패턴 클래스이름패턴 이름패턴(파라미터패턴)
  • execution(public void set*(..))
    리턴 타입이 void이고 메서드 이름이 set으로 시작하고, 파라미터가 0개 이상인 메서드 호출
  • execution( com.spring.ch01..*())
    com.spring.ch01 패키지의 파라미터가 없는 모든 메서드 호출
  • execution( com.spring.ch01...*(..))
    com.spring.ch01 패키지 및 하위 패키지에 있는 파라미터가 0개 이상인 메서드 호출
  • execution(Integer com.spring.ch01..WriteArticleService.write(..))
    리턴 타입이 Integer인 WriteArticleService 인터페이스의 write() 메서드 호출
  • execution( get(*))
    이름이 get으로 시작하고 1개의 파라미터를 갖는 메서드 호출
  • execution( get(,))
    이름이 get으로 시작하고 2개의 파라미터를 갖는 메서드 호출
  • execution( read(Integer,*))
    메서드 이름이 read로 시작하고, 첫 번째 파라미터 타입이 Integer이며, 1개 이상의 파라미터를 갖는 메서드 호출

@Aspect 어노테이션을 이용한 AOP

@Aspect
public class ProfilingAspect {
	
	@Pointcut("execution(public * com.spring.board..*(..))")
	private void profileTarget(){}
	
	@Around("profileTarget()")
	public Object trace(ProceedingJoinPoint joinPoint)
			                                  throws Throwable{
		String signatureString = 
				joinPoint.getSignature().toShortString();
		System.out.println(signatureString + " 시작");
		long start = System.currentTimeMillis();
		Object result = null;
		try{
			//핵심 비지니스 로직을 수행
			result = joinPoint.proceed();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			//메서드 실행 직후의 시간을 저장
			long finish = System.currentTimeMillis();
			System.out.println(signatureString + "종료");
			System.out.println(signatureString + " 실행 시간 : " + (finish-start)+"ms");
		}
		return result;
	}
}

스프링 MVC

스프링 MVC의 주요 구성 요소

구성요소설명
DispatcherServlet클라이언트의 요청을 전달받는다. 컨트롤러에게 클라이언트의 요청을 전달하고, 컨트롤러가 리턴한 결과값을 View에 전달하여 알맞은 응답을 생성하도록 한다.
HandlerMapping클라이언트의 요청 URL을 어떤 컨트롤러가 처리할지를 결정한다.
컨트롤러(Controller)클라이언트의 요청을 처리한 뒤, 그 결과를 DispatcherServlet에 알려준다. 스트럿츠의 Action과 동일한 역할을 수행한다.
ModelAndView컨트롤러가 처리한 결과 정보 및 뷰 선택에 필요한 정보를 담는다.
ViewResolver컨트롤러의 처리 결과를 생성할 뷰를 결정한다.
뷰(View)컨트롤러의 처리 결과 화면을 생성한다. JSP나 Velocity 템플릿 파일 등을 뷰로 사용한다.

스프링 MVC 설정

maven 설정 pom.xml 파일에서 jar 파일 설정
web.xml 설정 DispatcherServlet 매핑
root-context.xml 파일에 데이터베이스 연동 설정
servlet-context.xml에 bean 설정

DispatcherServlet 설정 및 스프링 컨텍스트 설정

DispatcherServlet의 설정은 웹 어플리케이션의 /WEB-INF/web.xml 파일에 추가

<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/spring/appServlet/servlet-context.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>appServlet</servlet-name>
	<url-pattern>*.do</url-pattern>
</servlet-mapping>

컨트롤러 구현 및 설정 추가

@Controller
public class HelloController {

	@RequestMapping("/hello.do")
	public ModelAndView hello() {
		ModelAndView mav = new ModelAndView();
		//뷰 이름 지정
		mav.setViewName("hello");
		//뷰에서 사용할 데이터 셋팅
		mav.addObject("greeting", "Hello World!!");
		return mav;
	}
}

servlet-context.xml 설정

bean 설정 및 viewResolver 설정

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
	
	<!-- bean 설정 -->
	<beans:bean id="helloController" class="kr.spring.ch01.controller.HelloController" />

        <!-- viewResolver 설정 -->
        <beans:bean id="viewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
</beans:beans>

뷰 코드 구현

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>인사</title>
</head>
<body>
인사말: <strong>${greeting}</strong>
</body>
</html>

실습

kr.spring.product

Product

핵심 기능을 담고 있는 코드

package kr.spring.product;

public class Product {
	// 핵심 기능 수행
	public String launch() {
		System.out.println("launch() 메서드 출력하기");
		
		return "[상품 출시]";
	}

}

kr.spring.ch20.pojo

My First Advice

package kr.spring.ch20.pojo;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyFirstAdvice {
	// 공통 기능 수행
	public void before() {
		// 메서드 시작 직전에 동작하는 어드바이스
		System.out.println("Hello Before! ** 메서드가 호출되기 전에 나온다!");
	}
	
	public void afterReturning(String msg) {
		// 핵심 기능이 동작된 후에 반환하는 데이터를 받아 출력하기
		// 메서드 호출이 예외를 내보내지 않고 종료했을 때 동작하는 어드바이스
		System.out.println("Hello After Returning! ** 메서드가 호출한 후 발생! 전달된 객체 : " + msg);
	}
	
	public void afterThrowing(Exception ex) {
		// 메서드 호출이 예외를 던졌을 때 동작하는 어드바이스
		System.out.println("Hello After Throwing ** 예외가 생겼을 때 출력! 예외 : " + ex);
	}
	
	public void after() {
		// 메서드 종료 후에 동작하는 어드바이스(예외가 발생해도 실행된다)
		System.out.println("Hello After ** 메서드 호출 된 후 출력!");
	}
	
	public String around(ProceedingJoinPoint joinPoint) throws Throwable {
		// 메서드 호출 전 후에 동작하는 어드바이스
		// 핵심 기능 동작 전에 호출
		System.out.println("Hello Around Before! ** 메서드 호출 전 출력!");
		
		// 핵심 기능 부분
		String s = null;
		// try ~ catch ~ finally 구조로 명시해야 예외가 발생하더라도 메서드 실행 후 공통 기능 수행
		try {
			// 핵심 기능이 수행 된 후 데이터 반환
			s = (String)joinPoint.proceed();	// 핵심 기능을 갖고 있는 메서드가 실행
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 핵심 기능 이후의 동작(공통 기능)
			System.out.println("Hello Around After! ** 메서드 호출 후 출력! 반환된 객체 : " + s);
		}
		
		return s;
	}
}

Application Context AOP 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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
	<!-- 공통 기능을 갖고 있는 클래스 빈 객체 설정 -->
	<bean id="myFirstAdvice" class="kr.spring.ch20.pojo.MyFirstAdvice"/>
	
	<!-- 핵심 기능을 구현한 클래스 빈 객체 설정 -->
	<bean id="product" class="kr.spring.product.Product"/>
	
	<!-- AOP 설정 -->
	<aop:config>
		<!-- 공통기능을 갖고 있는 클래스 지정 -->	
		<aop:aspect id="aspect" ref="myFirstAdvice">
			<!-- 핵심 기능을 실행할 때 어느 시점에 공통 기능을 적용할지 지정 -->
			<!-- 핵심 기능 지정 -->
			<aop:pointcut expression="execution(public String launch())" id="publicMethod"/>
			<!-- 공통 기능 지정 -->
			<!-- Advice 종류 - before -->
			<!-- <aop:before method="before" pointcut-ref="publicMethod"/> -->
			
			<!-- Advice 종류 - after-returning -->
			<!-- 반환하는 데이터를 msg에 넣어서 넘겨라 -->
			<!-- <aop:after-returning method="afterReturning" pointcut-ref="publicMethod" 
			returning="msg"/> -->
			
			<!-- Advice 종류 - after-throwing -->
			<!-- <aop:after-throwing method="afterThrowing" pointcut-ref="publicMethod" 
			throwing="ex"/> -->
			
			<!-- Advice 종류 - after -->
			<!-- <aop:after method="after" pointcut-ref="publicMethod"  /> -->
			
			<!-- Advice 종류 - Around After -->
			<aop:around method="around" pointcut-ref="publicMethod"/>
			
			
		</aop:aspect>
	</aop:config>
	
</beans>

Spring Main

package kr.spring.ch20.pojo;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import kr.spring.product.Product;


public class SpringMain {
	public static void main(String[] args) {
		AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContextAOP.xml");
		context.registerShutdownHook();
		
		Product p = (Product)context.getBean("product");
		p.launch();
		
		//어플리케이션 종료시 컨테이너에 존재하는 모든 빈(객체)를 종료
		context.close(); 
	}
}

Advice 종류별 실행 결과

Before를 사용했을 때


After-Returning 사용했을 때


에외 발생시키는 코드를 넣었을 때


after-throwing 사용했을 때


after 사용했을 때


around 사용했을 때

kr.spring.ch21.annot

My First Aspect

package kr.spring.ch21.annot;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class MyFirstAspect {
	/*
	 @Before
	 @AfterReturning
	 @AfterThrowing
	 @After
	 @Around
	 */
	
	@Pointcut("execution(public String launch())")
	// 주의 ! 메서드명이 getPointcut으로 정해져 있기 때문에 변경할 수 없다.
	public void getPointcut() {
		
	}
	
	//@Before("getPointcut()")
	public void before() {
		// 메서드 시작 직전에 동작하는 어드바이스
		System.out.println("Hello Before! ** 메서드가 호출되기 전에 나온다!");
	}
	
	//@AfterReturning(value = "getPointcut()", returning = "msg")
	public void afterReturning(String msg) {
		// 핵심 기능이 동작된 후에 반환하는 데이터를 받아 출력하기
		// 메서드 호출이 예외를 내보내지 않고 종료했을 때 동작하는 어드바이스
		System.out.println("Hello After Returning! ** 메서드가 호출한 후 발생! 전달된 객체 : " + msg);
	}
	
//	@AfterThrowing(value = "getPointcut()", throwing="ex")
	public void afterThrowing(Exception ex) {
		// 메서드 호출이 예외를 던졌을 때 동작하는 어드바이스
		System.out.println("Hello After Throwing ** 예외가 생겼을 때 출력! 예외 : " + ex);
	}
	
//	@After("getPointcut()")
	public void after() {
		// 메서드 종료 후에 동작하는 어드바이스(예외가 발생해도 실행된다)
		System.out.println("Hello After ** 메서드 호출 된 후 출력!");
	}
	
	@Around("getPointcut()")
	public String around(ProceedingJoinPoint joinPoint) throws Throwable {
		// 메서드 호출 전 후에 동작하는 어드바이스
		// 핵심 기능 동작 전에 호출
		System.out.println("Hello Around Before! ** 메서드 호출 전 출력!");
		
		// 핵심 기능 부분
		String s = null;
		// try ~ catch ~ finally 구조로 명시해야 예외가 발생하더라도 메서드 실행 후 공통 기능 수행
		try {
			// 핵심 기능이 수행 된 후 데이터 반환
			s = (String)joinPoint.proceed();	// 핵심 기능을 갖고 있는 메서드가 실행
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 핵심 기능 이후의 동작(공통 기능)
			System.out.println("Hello Around After! ** 메서드 호출 후 출력! 반환된 객체 : " + s);
		}
		
		return s;
	}
}

Application Context AOP 2 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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- 어노테이션 방식으로 AOP 구현할 때 명시 -->
	<aop:aspectj-autoproxy/>
	
	<!-- 공통 기능을 구현된 클래스 빈 객체 설정 -->
	<bean id="myFirstAspect" class="kr.spring.ch21.annot.MyFirstAspect"/>
	
	<!-- 핵심 기능이 구현된 클래스 빈 객체 설정 -->
	<bean id="product" class="kr.spring.product.Product"/>


</beans>

Spring Main

package kr.spring.ch21.annot;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import kr.spring.product.Product;


public class SpringMain {
	public static void main(String[] args) {
		AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContextAOP2.xml");
		context.registerShutdownHook();
		
		Product p = (Product)context.getBean("product");
		p.launch();
		
		//어플리케이션 종료시 컨테이너에 존재하는 모든 빈(객체)를 종료
		context.close(); 
	}
}

@Before("getPointcut()") 결과


@AfterReturning(value="getPointcut()", returning="msg") 결과


@AfterThrowing(value = "getPointcut()", throwing="ex") 결과


@After("getPointcut()") 결과


@Around("getPointcut()") 결과

ch08 SpringMVC

kr.spring.ch01.controller

Hello Controller

package kr.spring.ch01.controller;

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

@Controller
public class HelloController {
	// 요청 URL과 실행 메서드 연결
	@RequestMapping("/hello.do")
	public ModelAndView hello() {
		// model = 데이터(request) view = 뷰(viewResolver)
		ModelAndView mav = new ModelAndView();
		
		// 뷰 이름 지정 -> hello가 어딨는지 찾고, 확장자도 알려주기 때문에 이름만 명시
		mav.setViewName("hello"); 	// /WEB-INF/views/ -> prefix hello. jsp->s
		
		// 뷰에서 사용할 데이터 세팅
		// request에 저장되기 때문에 el을 이용하여 데이터 출력할 수 있음
		mav.addObject("greeting","안녕하세요!");
		
		
		return mav;
	}
}

Servlet-Context XML

<beans:bean id="helloController" class="kr.spring.ch01.controller.HelloController"/>

Hello

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>인사</title>
</head>
<body>
인사 : <b>${greeting}</b>
</body>
</html>

index

hello.do에 그냥 접근 할 수 없기 때문에 index에 링크를 걸어서 열 수 있도록 설정

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Spring MVC</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/hello.do">Hello Controller</a>
</body>
</html>


Hello Controller에

mav.addObject("lunch","곧 점심시간입니다!");

추가해본다면
hello.jsp 도 변경

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>인사</title>
</head>
<body>
인사말 <b>${greeting}</b> <br>
점심시간 <b>${lunch}</b>
</body>
</html>


kr.spring.ch02.controller

Search Controller

package kr.spring.ch02.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class SearchController {
	// 요청 URL과 실행 메서드 연결
	@RequestMapping("/search/internal.do")
	public ModelAndView searchInternal(String query) {
		System.out.println("query = " + query);
//		ModelAndView mav = new ModelAndView();
		// 뷰 이름 지정
//		mav.setViewName("search/internal");
		
		return new ModelAndView("search/internal");
	}

}

Servlet - Context XML

<beans:bean id="searchController" class="kr.spring.ch02.controller.SearchController"/>

Internal

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>내부 검색</title>
</head>
<body>
<b>내부 검색</b>
</body>
</html>


Search Controller

코드 추가

@RequestMapping("/search/external.do")
	public ModelAndView searchExternal(@RequestParam String query, @RequestParam("p") int pageNumber) {
		
		System.out.println("query = " + query);
		System.out.println("p = " + pageNumber);
		
		ModelAndView mav = new ModelAndView();
		 // 뷰 이름 지정
		mav.setViewName("search/external");
		
		
		return mav;
	}

External

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>외부 검색</title>
</head>
<body>
외부 검색
</body>
</html> 

Index

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Spring MVC</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/hello.do">Hello Controller</a>
<a href="${pageContext.request.contextPath}/search/internal.do">Search Controller(Internal)</a>
<a href="${pageContext.request.contextPath}/search/external.do?query=여름&p=10">Search Controller(External)</a>
</body>
</html>


kr.spring.ch03.vo

New Article VO

package kr.spring.ch03.vo;

public class NewArticleVO {
	private String title;
	private String name;
	private String content;
	
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	
	@Override
	public String toString() {
		return "NewArticleVO [title=" + title + ", name=" + name + ", content=" + content + "]";
	}
	
	
}

kr.spring.ch03.controller

New Article Controller

package kr.spring.ch03.controller;

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

import kr.spring.ch03.vo.NewArticleVO;

@Controller
public class NewArticleController {

	// get 요청이 들어올 때 호출
	@GetMapping("/article/newArticle.do")
	public String form() {
		
				// 뷰 이름 지정
		return "article/newArticleForm";
	}
	
	// post 요청이 들어올 때 호출
	@PostMapping("/article/newArticle.do")
	public String submit(@RequestParam String title,
						 @RequestParam String name,
						 @RequestParam String content ) {
		NewArticleVO vo = new NewArticleVO();
		vo.setTitle(title);
		vo.setName(name);
		vo.setContent(content);
		
		System.out.println(vo);
		
				// 뷰 이름 지정
		return "article/newArticleSumitted";
	}
}

Servlet - Context XML

<!-- @Autowired 사용을 위해서 -->
	<context:annotation-config/>
	
<!-- 전송된 데이터 자바빈(VO)에 담기 -->
	<beans:bean id="newArticleController" class="kr.spring.ch03.controller.NewArticleController"/>

new Article Form

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시글 쓰기</title>
</head>
<body>
게시글 쓰기 입력 폼
<form action="newArticle.do" method="post">
	제목 : <input type="text" name="title"><br>
	작성자 : <input type="text" name="name"><br>
	작성자 : <textarea rows="5" cols="30" name="content"></textarea><br>
	<input type="submit" value="전송">
	
</form>
</body>
</html>

new Article Sumitted

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시글 쓰기 완료</title>
</head>
<body>
게시글이 등록되었습니다.
</body>
</html>


kr.spring.ch03.service

New Article Service

package kr.spring.ch03.service;

import kr.spring.ch03.vo.NewArticleVO;

public class NewArticleService {
	public void writeArticle(NewArticleVO vo) {
		System.out.println("신규 게시글 등록 : " + vo);
	}
}

New Article Controller

수정하기

package kr.spring.ch03.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import kr.spring.ch03.service.NewArticleService;
import kr.spring.ch03.vo.NewArticleVO;

@Controller
public class NewArticleController {
	
	// autowired가 set을 자동으로 만들어주기 때문에 굳이 만들 필요가 없다
	@Autowired
	private NewArticleService newArticleService;

	// get 요청이 들어올 때 호출
	@GetMapping("/article/newArticle.do")
	public String form() {
		
				// 뷰 이름 지정
		return "article/newArticleForm";
	}
	
	// post 요청이 들어올 때 호출
	@PostMapping("/article/newArticle.do")
	public String submit(@RequestParam String title,
						 @RequestParam String name,
						 @RequestParam String content ) {
		NewArticleVO vo = new NewArticleVO();
		vo.setTitle(title);
		vo.setName(name);
		vo.setContent(content);
		
		newArticleService.writeArticle(vo);
		
				// 뷰 이름 지정
		return "article/newArticleSumitted";
	}
	
	
}

Servlet - Context XML

추가하기

<beans:bean id="newArticleService" class="kr.spring.ch03.service.NewArticleService"/>


@ModelAttribute("command") NewArticleVO vo 형식
@ModelAttribute(속성명) NewArticleVO vo
지정한 속성명으로 JSP에서 request에 접근하여 자바빈(VO) 호출 가능
ex) ${속성명.title} , ${속성명.name}, ${속성명.content}

New Article Controller

package kr.spring.ch03.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import kr.spring.ch03.service.NewArticleService;
import kr.spring.ch03.vo.NewArticleVO;

@Controller
public class NewArticleController {
	
	// autowired가 set을 자동으로 만들어주기 때문에 굳이 만들 필요가 없다
	@Autowired
	private NewArticleService newArticleService;

	// get 요청이 들어올 때 호출
	@GetMapping("/article/newArticle.do")
	public String form() {
				// 뷰 이름 지정
		return "article/newArticleForm";
	}
	// post 요청이 들어올 때 호출
	@PostMapping("/article/newArticle.do")
	public String submit(@ModelAttribute("command") NewArticleVO vo) {
		
		newArticleService.writeArticle(vo);
				// 뷰 이름 지정
		return "article/newArticleSumitted";
	}
	
	
}


@ModelAttribute NewArticleVO vo 형식
@ModelAttribute를 명시할 때 속성명을 생략할 수 있다.
속성명을 생략하면 클래스명의 첫 글자를 소문자로 속성명을 자동 생성한다.
ex) @ModelAttribute NewArticleVO vo
${newArticleVO.title} , ${newArticleVO.name},${newArticleVO.content}

New Article Controller

package kr.spring.ch03.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import kr.spring.ch03.service.NewArticleService;
import kr.spring.ch03.vo.NewArticleVO;

@Controller
public class NewArticleController {
	
	// autowired가 set을 자동으로 만들어주기 때문에 굳이 만들 필요가 없다
	@Autowired
	private NewArticleService newArticleService;

	// get 요청이 들어올 때 호출
	@GetMapping("/article/newArticle.do")
	public String form() {
				// 뷰 이름 지정
		return "article/newArticleForm";
	}	
	// post 요청이 들어올 때 호출
	@PostMapping("/article/newArticle.do")
	public String submit(@ModelAttribute NewArticleVO vo) {
		
		newArticleService.writeArticle(vo);
				// 뷰 이름 지정
		return "article/newArticleSumitted";
	}
	
	
}


NewArticleVO vo 형식
@ModelAttribute 생략
호출 메서드에 인자명만 명시
ex) NewArticleVO vo와 같이 인자명만 명시
request에 저장되는 속성명은 newArticleVO로 저장됨

New Article Controller

package kr.spring.ch03.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import kr.spring.ch03.service.NewArticleService;
import kr.spring.ch03.vo.NewArticleVO;

@Controller
public class NewArticleController {
	
	// autowired가 set을 자동으로 만들어주기 때문에 굳이 만들 필요가 없다
	@Autowired
	private NewArticleService newArticleService;

	// get 요청이 들어올 때 호출
	@GetMapping("/article/newArticle.do")
	public String form() {
				// 뷰 이름 지정
		return "article/newArticleForm";
	}
	// post 요청이 들어올 때 호출
	@PostMapping("/article/newArticle.do")
	public String submit(NewArticleVO vo) {
		
		newArticleService.writeArticle(vo);
				// 뷰 이름 지정
		return "article/newArticleSumitted";
	}
	
	
}

kr.spring.ch04.controller

package kr.spring.ch04.controller;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

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

@Controller
public class CookieController {
	@RequestMapping("/cookie/make.do")
	public String make(HttpServletResponse response) {
		// 쿠키를 생성해서 클라이언트에 전송
		response.addCookie(new Cookie("auth", "10"));
		return "cookie/make";
	}
}

Servlet - Context XML

<!-- @CookieValue 어노테이션을 이용한 쿠키 매핑 -->
	<beans:bean id="cookieController" class="kr.spring.ch04.controller.CookieController"/>

Make

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>쿠키 값 생성하기</title>
</head>
<body>
쿠키를 생성함
</body>
</html>


package kr.spring.ch04.controller;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

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

@Controller
public class CookieController {
	@RequestMapping("/cookie/make.do")
	public String make(HttpServletResponse response) {
		// 쿠키를 생성해서 클라이언트에 전송
		response.addCookie(new Cookie("auth", "10"));
		return "cookie/make";
	}
	
	@RequestMapping("/cookie/view.do")
	public String view(@CookieValue("auth") String auth) {
		// 쿠키명 지정하고 쿠키 불러오기
		System.out.println("auth 쿠키 : " + auth);
		return "cookie/view";
	}
}

View

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


package kr.spring.ch04.controller;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

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

@Controller
public class CookieController {
	@RequestMapping("/cookie/make.do")
	public String make(HttpServletResponse response) {
		// 쿠키를 생성해서 클라이언트에 전송
		response.addCookie(new Cookie("auth", "10"));
		return "cookie/make";
	}
	
	@RequestMapping("/cookie/view.do")
	public String view(@CookieValue String auth) {
		// 쿠키명 지정하고 쿠키 불러오기
		System.out.println("auth 쿠키 : " + auth);
		return "cookie/view";
	}
}
package kr.spring.ch04.controller;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

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

@Controller
public class CookieController {
	@RequestMapping("/cookie/make.do")
	public String make(HttpServletResponse response) {
		// 쿠키를 생성해서 클라이언트에 전송
		response.addCookie(new Cookie("auth", "10"));
		return "cookie/make";
	}
	
	@RequestMapping("/cookie/view.do")
	public String view(String auth) {
		// 쿠키명 지정하고 쿠키 불러오기
		System.out.println("auth 쿠키 : " + auth);
		return "cookie/view";
	}
}


package kr.spring.ch04.controller;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

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

@Controller
public class CookieController {
	@RequestMapping("/cookie/make.do")
	public String make(HttpServletResponse response) {
		// 쿠키를 생성해서 클라이언트에 전송
		response.addCookie(new Cookie("auth", "10"));
		return "cookie/make";
	}

	@RequestMapping("/cookie/view.do")
	public String view(@CookieValue(value="auth", required=false) String auth) {
		// 쿠키명 지정하고 쿠키 불러오기
		System.out.println("auth 쿠키 : " + auth);
		return "cookie/view";
	}
}
package kr.spring.ch04.controller;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

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

@Controller
public class CookieController {
	@RequestMapping("/cookie/make.do")
	public String make(HttpServletResponse response) {
		// 쿠키를 생성해서 클라이언트에 전송
		response.addCookie(new Cookie("auth", "10"));
		return "cookie/make";
	}

	@RequestMapping("/cookie/view.do")
	public String view(@CookieValue(value="auth", defaultValue="0") String auth) {
		// 쿠키명 지정하고 쿠키 불러오기
		System.out.println("auth 쿠키 : " + auth);
		return "cookie/view";
	}
}

profile
Lucky Things🍀

0개의 댓글