본문 바로가기

java/spring

[spring] 스프링 mvc웹 서비스 만들기 (sts없이 수동 설정)

주제
sts 없이 스프링 mvc 프레임워크 프로젝트 세팅하기

 

sts 없이 수동설정을 하게 된 배경


STS3 기본 설치 방법

sts(spring to suit)는 스프링 프로젝트를 만들 때 자동으로 pom.xml, 디렉토리 구조, web.xml을 세팅해주는 플러그인이다.

원래 설치 방법은 "이클립스 메뉴 탭- help- market place"로 들어가서 spring tools 3 다운로드 후, 새 프로젝트 생성 - spring legacy project - spring mvc project를 선택하면 된다.

 

문제: spring mvc project 없음

하지만, 실제로 위 과정을 실행해보니, 설치 과정에서 여러 충돌이 발생했다. 그 이후 새 프로젝트 생성 탭에서 spring mvc project를 찾아봤지만, 존재하지 않았다.

 

다른 시도: 스프링 공식 홈페이지 STS3 설치

다른 방법으로서 스프링 홈페이지에서 STS3를 직접 설치해보았다.

이 과정에서 현재 사용하는 jdk1.8 버전과 sts3간에 버전 호환 문제를 겪었고 아래 참고 자료를 통해 sts3 설정 파일을 고쳐

설정 파일 고쳐서 처리하였다.

 

참고 자료

https://life-with-coding.tistory.com/446

https://www.snugarchive.com/blog/sts-setup/

 

하지만, 이 방식으로도 spring mvc project를 찾을 수 없었다.

구글링 결과, 이 문제는 다양한 원인으로 발생할 수 있어서 해결방법을 찾기가 쉽지 않았다.

이클립스 버전이 문제일수도 있고, 설정파일이 문제일수도 있는데, 이것저것 시도해봐도 실패했고,
실제 프로젝트에서 이런 문제를 겪을 걸 생각하면 차라리 수동 설정을 위한 템플릿을 만들어놓는 게 났다고 판단했다
.

 

sts 없이 수동설정


아래 분의 글을 참고하여 스프링 프로젝트 수동 설정을 하였다.
참고 자료(수동 
spring 프로젝트 설정)

https://wildeveloperetrain.tistory.com/m/347

 

1. 프로젝트 생성

=> dynamic web project 생성(생성 시 설정에서 xml descriptor 추가)

=> 프로젝트 우클릭 - configure - convert maven project 전환

 

2. pom.xml 작성

스프링 mvc 프레임워크에서 사용하는 의존성을 세팅해준다.

아래는 java 1.8버전에 맞게 세팅한 내 pom.xml이다.

(수정하려면 properties 태그에서 버전만 바꿔주면 된다.)

<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>
	<groupId>testPjt</groupId>
	<artifactId>testPjt14</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

<properties>
		<java-version>1.8</java-version>
		<org.springframework-version>4.1.0.RELEASE</org.springframework-version>
		<org.aspectj-version>1.6.10</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
	</properties>
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>	
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				 </exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
				
		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>	
		
		<!-- Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j-version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
			<exclusions>
				<exclusion>
					<groupId>javax.mail</groupId>
					<artifactId>mail</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.jms</groupId>
					<artifactId>jms</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jdmk</groupId>
					<artifactId>jmxtools</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jmx</groupId>
					<artifactId>jmxri</artifactId>
				</exclusion>
			</exclusions>
			<scope>runtime</scope>
		</dependency>

		<!-- @Inject -->
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>
				
		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	
		<!-- Test -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.7</version>
			<scope>test</scope>
		</dependency>        
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>3.2.3</version>
			</plugin>
		</plugins>
	</build>
</project>

 

 

이 후, 프로젝트 우클릭 - maven- update project를 해준다. 프로젝트 아이콘이 j에서 s로 변하면 완료다.

이 부분이 안되는 경우가 있는데, 이럴 때는 마켓 플레이스에서 sts를 설치하고 project facet에서 spring이 추가되어 있어야 한다.(만약 안된다면, 당장은 이 부분을 넘어가도 괜찮다.)

 

3. 디렉토리 구조 짜기

└── src

└── main

├── java | servlet 등의 자바 파일

└── webapp | 비 자바 웹 설정 파일 및 리소스

├── WEB-INF

|   ── lib

| ├── spring  ── appServlet / servlet-context.xml   |  context.xml 등 스프링 빈 설정 파일

|                    └──  root-context.xml        |  context.xml 등 스프링 빈 설정 파일

| ├──  views | jsp

|  ── web.xml 

└── resources | html,css,js 등 순수 웹 리소스

 

 

4. web.xml

/src/main/webapp/WEB-INF/ 에 있는 web.xml 파일을 작성한다.

이는 mvc 프레임워크에서 사용자 요청을 dispatchServlet으로 매핑하고 filter 등의 설정을 매핑하는데 사용한다.

(해당 파일이 없는 경우, 직접 xml 파일을 생성하거나 다음같이 생성가능

'프로젝트 우클릭' -> 'Java EE Tools' -> 'Generate Deployment Descriptor Stup' )

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<display-name>$ 프로젝트 이름 $$</display-name>

	<!-- The definition of the Root Spring Container shared by all Servlets 
		and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>

	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Processes application requests -->
	<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>/</url-pattern>
	</servlet-mapping>

	<filter>
		<filter-name>encodingFilter</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>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
</web-app>

 

5. 스프링 설정 파일 작성

WEB-INF/spring/root-context.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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
		
</beans>

 

WEB-INF/spring/appServlet/servlet-context.xml

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

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
    <!-- 루트 컨트롤러의 위치 package를 정의한다. -->
	<context:component-scan base-package="com.bs" />
	
	
	
</beans:beans>

 

6. controller  파일 작성

servlet-context에서 정의한 루트 폴더를 따라 임의로 패키지를 생성하고 homeController.java 를 만든다.

package com.bs;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	/**
	 * Simply selects the home view to render by returning its name.
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		
		return "home";
	}
	
}

 

7. view 파일 생성

WEB-INF/views/home.jsp 생성

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Home</title>
</head>
<body>
<h1>
	Hello world!  
</h1>

<P>  The time on the server is ${serverTime}. </P>
</body>
</html>

 

 

 

service & DAO 객체의 의존성 주입, 어노테이션 설정 방식


서비스 객체란

스프링 mvc 패턴은 컨트롤러의 핸들러 메소드에서 주요 로직을 구현한다.

이를 위해 각 메소드는 그에 맞는 하위 로직들을 가지고 있는데, 이를 담당하는 것이 서비스 객체다. 서비스 객체는 dao 객체와 함께 model 역할을 한다.

 

스프링mvc의 컨트롤러에서 서비스 객체를 주입하거나 서비스 객체에서 dao객체를 주입하는 방법은 다음과 같다.

  • new 연산자를 통한 service 객체 인스턴스 생성 방법(싱글톤이 아닌 프로토 타입 방식)
  • 스프링 설정파일 xml과 자동주입 방식
  • 어노테이션 설정을 통한 빈 생성 방식

 

각 방식의 코드는 다음과 같다.

 

new 연산자 방식

자바의 전통적인 인스턴스 생성 방식이지만, 스프링 프레임워크의 디버깅 기능 제한 및 유지보수성 저하로 많이 사용하지 않는다.

@Controller
@RequestMapping(value = "/user")
public class UserController {
	
	UserDAO userDAO =new UserDAO();
	RegisterService registerService =new RegisterService(userDAO);
	
	
}

 

 

스프링 설정파일 xml과 자동주입 방식

xml 형식의 스프링 설정파일과 자동주입 방식을 함께 사용한다.(자동 주입대신, 명시적 주입 방식을 사용할 수도 있다)

아래 코드에서는 dao를 서비스 객체로 자동 주입하고, 서비스 객체는 컨트롤러로 자동주입된다.

	<beans:bean id="userDAO" class="com.bs.dao.UserDAO"></beans:bean>
	<beans:bean id="RegisterService"
		class="com.bs.service.RegisterService" />
public class RegisterService {
private UserDAO userDAO;

@Autowired
public RegisterService(UserDAO userDao) {
	// TODO Auto-generated constructor stub
	this.userDAO=userDAO;
}
}
@Controller
@RequestMapping(value = "/user")
public class UserController {
	@Autowired
	RegisterService registerService;

	@RequestMapping(value="/getUserInfo",method =RequestMethod.GET )
	public String getUserInfo(Model model) {
		return "getUser";
	}
}
  • 장점
    • 주로 스프링 레거시 프로젝트에서 사용했던 방식으로, 요즘에는 후술할 어노테이션 방식을 더 많이 사용한다.
    • xml에서 빈 객체를 중앙집중식으로 관리할 수 있다.
    • xml에 모든 빈 설정이 있기 때문에 배포 환경을 옮길 때 유용하다.
  • 단점
    • xml파일이 커지고 복잡해지면 가독성이 떨어진다.
    • 컴파일 타입 검사가 이뤄지지 않으므로 런타임 에러가 발생하기 쉽다.
    • 어노테이션 설정에 비해서 여러 부가기능이 부족하다.

 

어노테이션 설정을 통한 빈 생성 방식

config 자바 파일이 아닌 자바 파일단(컨트롤러 및 로직)에서 어노테이션으로 빈 객체를 설정하는 방식이다.

일반적으로 계층 어노테이션과 자동 주입 어노테이션을 혼합하여 사용한다.

계층 어노테이션은 컨트롤러, 서비스, dao 처럼 각 로직의 계층을 나타내면서 빈 객체 생성을 해주는 어노테이션으로, @service, @component, @repository 등이 있다.

자동 주입 어노테이션은 의존성 주입을 타입 혹은 이름에 따라 자동으로 해주는 어노테이션으로, @autowired, @resource 등이 있다.

 

계층 특화 어노테이션

계층 특화 어노테이션은 빈 객체 생성과 함께, 이 클래스가 특정한 역할을 담당하는 계층임을 명시하기 위해 사용한다.

이는 개발자 입장에서 가독성을 올려주고, 부가기능을 제공받을 수 있다.

 

@Component

특정 계층을 명시하지 않는다

 

@Repository

데이터 액세스 계층을 나타낸다.

@Repository
public class UserDAO {
	private Map<String, User> DB;

	public UserDAO() {
		// TODO AutString password;o-generated constructor stub
		DB = new HashMap<String, User>();
	}

}

 

@Service

비즈니스 로직을 처리를 위한 서비스 계층을 나타낸다.

@Service
public class RegisterService {
	private UserDAO userDAO;

	@Autowired
	public RegisterService(UserDAO userDao) {
		// TODO Auto-generated constructor stub
		this.userDAO = userDAO;
	}

}

 

@Controller

컨트롤러 계층을 나타내며 dispatcher의 url 매핑에 따라 http요청을 받고 처리하며 응답한다.

@Controller
@RequestMapping(value = "/user")
public class UserController {
	
//	UserDAO userDAO =new UserDAO();
	
	@Autowired
	RegisterService registerService;

	@RequestMapping(value="/getUserInfo",method =RequestMethod.GET )
	public String getUserInfo(Model model) {

		System.out.println(registerService);
		return "getUser";
	}
	
	
}

 

@RestController

RESTful 웹 서비스의 컨트롤러 계층을 나타낸다. @Controller와 @ResponseBody를 결합한 형태로, JSON 또는 XML 형식의 응답을 생성할 때 사용한다.

 

자동 주입 방식

자동 주입 방식은 위에서처럼 타입 기반의 @Autowired를 사용할 수도 있고, 이름 기반의 @Resource를 사용할 수도 있다.

빠른 개발은 전자가 유리하지만, 이름 명시로 보다 안정성을 가지려면 후자의 방식이 좋다.

 

@Resource 방식의 예시

@Service("registerService")
public class RegisterService {
	private UserDAO userDAO;

	@Autowired
	public RegisterService(UserDAO userDao) {
		// TODO Auto-generated constructor stub
		this.userDAO = userDAO;
	}

}
@Controller
@RequestMapping(value = "/user")
public class UserController {

	@Resource(name = "registerService")
	RegisterService registerService;

	@RequestMapping(value="/getUserInfo",method =RequestMethod.GET )
	public String getUserInfo(Model model) {

		System.out.println(registerService);
		return "getUser";
	}
	
	
}

 

어노테이션 방식의 의의

  • 장점
    • 설정이 빠르고 간편하다. 
    • 타입 안정성 컴파일 검사가 이뤄진다.
    • 어노테이션을 통해 직관적으로 기능을 파악할 수 있다.
  • 단점
    • 중앙관리식에 비해 전체적인 구성을 파악하기에는 어렵다.
    • 환경 설정 테스트 및 배포 환경 변경 등에서 어려울 수 있다.

 

controller 객체 구현1 


requestMapping

어떤 요청 url을 받을지 어떤 http method를 받을지 매핑할 수 있다.

(요청 메소드는 생략할 수 있으나, get, post 등의 요청이 선별되지 않으므로 권장하진 않는다)

컨트롤러와 핸들러 메소드 모두에 매핑 가능하다. 컨트롤러에 매핑 시, 라우터의 역할을 한다.

 

예시

@Controller
@RequestMapping(value = "/user")
public class UserController {
// 컨트롤러 자체에 /user 매핑
// 하위 메소드의 공용 url로 설정됨

	
	@Resource(name = "registerService")
	RegisterService registerService;

// /user/getUserInfo 요청에 호출되도록 설정
	@RequestMapping(value="/getUserInfo",method =RequestMethod.GET )
	public String getUserInfo(Model model) {

		System.out.println(registerService);
		return "getUser";
	}

 

request parameter 받기

사용자 요청에 담긴 정보를 컨트롤러 메소드에서 받는 방법에 대해 알아보자.

 

request 수동참조

전통적으로 사용해왔던 방법으로 request 객체를 직접 구조분해하여 원하는 파라미터를 가져오는 방식이다.

	@RequestMapping(value = "/getUserInfo", method = RequestMethod.GET)
	public String getUserInfo(Model model, HttpServletRequest req) {
		String username = req.getParameter("username");
		System.out.println(username);
		return "getUser";
	}

 

스프링 어노테이션 방식

다음 같은 form에서 요청을 보낸다고 가정하자.

<form action="/testPjt14/user/account/login/form-submit" method="POST">
		<h3>login</h3>
		<label> 이름 <input name="username" placeholder="이름" />

		</label> <label> id <input name="id" placeholder="id" />
		</label> <label> password <input name="password"
			placeholder="password" type="password" />
		</label>

		<button>login</button>

	</form>

 

그러면 컨트롤러에서 다음같이 파라미터를 받을 수 있다.

@RequestMapping(value = "/account/login/form-submit", method = RequestMethod.POST)
	public String loginFormSubmit(
    @RequestParam("id") String id, 
    @RequestParam("username")String username,
	@RequestParam("password") String password) {
		System.out.println(id);
		System.out.println(username);
		System.out.println(password);
		return "redirect:/";
	}

 

@RequestParam("key") 방식은 기본적으로 해당 요소를 못 찾을 시에 400응답을 자동으로 보낸다.

따라서 경우에 따라 다음 옵션으로 유효성 검사를 할 수 있다.

  • value | 요청 파라미터의 키 값 지정(기본은 인자명)
  • required | 요청 파라미터의 필수 여부 지정(기본은 true)
  • defaultValue  | 요청 파라미터의 기본 값 지정

 

커맨드 객체 방식

스프링 MVC에서 제공하는 기능으로, request 객체로부터 파라미터를 직접 꺼내지 않고 HTTP 요청 파라미터를 Java 객체로 자동 바인딩한다.

타입 안정성, 코드 축약, 유지 보수성 등이 장점이며 일반적으로 많이 사용한다.

 

사용 방법

  • 객체의 필드 변수는 private 이고 getter, setter 필요
  • request 객체의 파라미터에서 커맨드 객체의 필드 변수명 및 setter와 일치해야 하는 것들은 바인딩되고 나머지는 무시된다.
  • request 객체의 파라미터에서 커맨드 객체의 필드 변수에 맞는 name의 값을 찾지 못하면 해당 변수는 기본 값이 설정된다.
    • 참조 타입  | null 로 설정
    • 기본 타입 | int =0, boolean = false로 설정
  •  유효성 검사는 필요한 의존성 설치와  @Valid나 @Validated 등의 어노테이션 설정으로 이용할 수 있다.

기본 예시

	@RequestMapping(value = "/account/login/form-submit", method = RequestMethod.POST)
	public String loginFormSubmit(LoginCO loginCO) String password) {
		System.out.println(loginCO.getId());
		System.out.println(loginCO.getPassword());
		System.out.println(loginCO.getUsernames());
		return "redirect:/";
	}




유효성 검사

먼저 pom.xml에 다음 의존성을 추가한다.

  • Hibernate Validator, Validation | 유효성 검사를 위한 어노테이션 추가
  • EL API | 표현식 검사 관련 기능 추가
  <!-- Hibernate Validator -->
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.2.0.Final</version>
    </dependency>

    <!-- JSR 303 Validation API -->
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>2.0.1.Final</version>
    </dependency>

    <!-- Java EL API -->
    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.0</version>
    </dependency>

 

그리고 커맨드 객체 클래스에서 원하는 유효성 검사항목을 어노테이션으로 설정한다.

public class LoginCO {
	
	@NotEmpty(message = "id is required")
	private String id;
	
	@NotEmpty
	private String password;
	
	@NotEmpty(message = "username is required")
	@Max(value = 10, message = "username is too long")
	private String username;
	
	public LoginCO() {
		// TODO Auto-generated constructor stub
	}
	
	
// getter, setter
}

 

마지막으로 컨트롤러 클래스의 메소드에서 @Valid로 유효성 검사를 작동시킨다.

	@RequestMapping(value = "/account/login/form-submit", method = RequestMethod.POST)
	public String loginFormSubmit(@Valid LoginCO loginCO, BindingResult result) {
		if (result.hasErrors()) {
			System.out.println("에러 발견");
			System.out.println(result.getAllErrors());
			return "Login";

		}

		System.out.println(loginCO.getId());
		System.out.println(loginCO.getPassword());
		System.out.println(loginCO.getUsername());
		return "redirect:/";
	}

 

커멘드 객체 프로퍼티 데이터 타입

커맨드 객체에 request paramete가 바인딩되는 방식은 두 가지다.

 

기초 데이터 타입

setter의 인자 타입이 int boolean같은 원시 데이터 타입이거나 string 타입인 경우

=> string 타입의 리퀘스트 파라미터가 자동 타입 변환

 

중첩 객체 타입

커맨드 객체의 setter 인자 타입이 list처럼 중첩 객체 형식인 경우

=>name명 설정으로 자동 바인딩 가능

 

예시1: 객체 타입 

public class Address {
    private String street;
    private String city;

    // getters and setters
    public String getStreet() {
        return street;
    }
    public void setStreet(String street) {
        this.street = street;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
}

public class User {
    private Address address;

    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
}

public class Address {
    private String street;
    private String city;

    // getters and setters
    public String getStreet() {
        return street;
    }
    public void setStreet(String street) {
        this.street = street;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
}

public class User {

    private Address address;

    // getters and setters
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
}

@Controller
public class UserController {

    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public String register(@ModelAttribute("user") User user) {
        return "success";
    }
}
<form action="/register" method="post">
    Street: <input type="text" name="address.street"><br>
    City: <input type="text" name="address.city"><br>
    <input type="submit" value="Register">
</form>

위 폼이 제출되면 다음 같이 바인딩된다.

 

  • address.street -> user.address.street
  • address.city -> user.address.city

예시2: 중첩 객체 타입

@Controller
public class UserController {
    @RequestMapping(value = "/register2", method = RequestMethod.POST)
    public String register(@ModelAttribute("user") List< UserPhone> UserPhones) {
        return "success";
    }
PHONE1 : <input type="text" 
name="userPhones[0].userPhone1" size="5"> -
<input type="text" 
name="userPhones[0].userPhone2" size="5"> -
<input type="text" 
name="userPhones[0].userPhone3" size="5">
PHONE2 : <input type="text" 
name="userPhones[1].userPhone1" size="5"> -
<input type="text" 
name="userPhones[1].userPhone2" size="5"> -
<input type="text" 
name="userPhones[1].userPhone3" size="5">

 

 

Controller 객체 구현2: Model


Model

컨트롤러 객체의 메소드에 인자로 자동 매핑되는 model은 response 데이터를 설정할 때 사용한다.

 

컨트롤러 메소드에서 model 값 추가

	@RequestMapping(value = "/account/login/form-submit", method = RequestMethod.POST)
	public String loginFormSubmit(@Valid LoginCO loginCO, BindingResult result, Model model) {
		if (result.hasErrors()) {
			System.out.println("에러 발견");
			System.out.println(result.getAllErrors());
			return "Login";

		}

		model.addAttribute("id",loginCO.getId());
		model.addAttribute("password",loginCO.getPassword());
		model.addAttribute("username",loginCO.getUsername());
		
		return "User-dashboard";
	}

 

jsp에서 model 사용하기

${} 표현식으로 model 객체의 키를 참조하여 사용한다.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>유저 대시 보드</title>
</head>
<body>

<section>
<p>id : ${id}</p>
<p>password : ${password}</p>
<p>username : ${username}</p>

</section>




</body>
</html>

 

커맨드 객체를 사용하는 경우, model 객체에서 값을 꺼내쓰는 대신, 커맨드 객체에서 바로 값을 사용할 수도 있다.

jsp에서 객체 값을 참조할 때는 getter가 작동한다.

이 방식은 코드 양이 적다는 장점이 있다.

	@RequestMapping(value = "/account/login/form-submit", method = RequestMethod.POST)
	public String loginFormSubmit(@Valid LoginCO loginCO, BindingResult result, Model model) {
		if (result.hasErrors()) {
			System.out.println("에러 발견");
			System.out.println(result.getAllErrors());
			return "Login";

		}	
		return "User-dashboard";
	}
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>유저 대시 보드</title>
</head>
<body>

<section>
<p>id : ${loginCO.id}</p>
<p>password : ${loginCO.password}</p>
<p>username : ${loginCO.username}</p>
</section>

</body>
</html>

 

@ModelAttribute

모델 어트리뷰트는 jsp에서 사용할 model 객체에 데이터를 준비하는데 사용한다.

 

메서드 파라미터로 사용(객체 참조명 변경)

메서드 파라미터로 사용 시, 커멘드 객체의 이름을 변경하며 변경된 이름은 jsp 뷰에서 객체를 참고할 때 사용한다.

	@RequestMapping(value = "/account/login/form-submit", method = RequestMethod.POST)
	public String loginFormSubmit(@Valid @ModelAttribute("user") LoginCO loginCO, BindingResult result, Model model) {
		if (result.hasErrors()) {
			System.out.println("에러 발견");
			System.out.println(result.getAllErrors());
			return "Login";

		}

		return "User-dashboard";
	}
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>유저 대시 보드</title>
</head>
<body>

<section>
<p>id : ${user.id}</p>
<p>password : ${user.password}</p>
<p>username : ${user.username}</p>

</section>




</body>
</html>

 

 

메소드 상단에 사용 시(공통 모델 데이터)

메소드 상단에 모델 어트리뷰트를 명시하면 다른 컨트롤러가 요청을 받아 실행된 jsp에서 공통으로 사용가능한 모델 데이터가 된다.

 

아래처럼 모델 어트리뷰트는 2가지 사용 방식이 있으며, 다른 리퀘스트 매핑의 뷰에서 사용가능한 모델로 바인딩된다.

	@ModelAttribute
	public void commonMessage(Model model) {
		model.addAttribute("introMessage", "welcome!!!!");
	}
	
	@ModelAttribute("serverTime")
	public String getServerTime(Locale locale) {
		Date date =new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		return dateFormat.format(date);
	}
    
    	@RequestMapping(value = "/account/login/form-submit", method = RequestMethod.POST)
	public String loginFormSubmit(@Valid @ModelAttribute("user") LoginCO loginCO, BindingResult result, Model model) {
		return "User-dashboard";
	}
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>유저 대시 보드</title>
</head>
<body>

// 공통 데이터//
<p>${serverTime}</p>
<p>${introMessage}</p>

//

<section>
<p>id : ${user.id}</p>
<p>password : ${user.password}</p>
<p>username : ${user.username}</p>

</section>

</body>
</html>

 

model & modelAndView

model과 modelAndView는 둘 다 컨트롤러에서 jsp에 전달될 모델 데이터를 관리한다.

둘의 차이는 다음과 같다.

 

model

view에 데이터만 전달한다.

실행할 view의 이름은 컨트롤러 메소드의 return 값으로 결정한다.

 

modelAndView

데이터와 뷰를 함께 설정한다.

복잡한 로직이나 리다이렉트, 포워딩 등의 로직이 있을 때, 데이터와 뷰를 한번에 관리할 수 있어서 직관성이 좋다.

	@RequestMapping(value = "/account/login/form-submit", method = RequestMethod.POST)
	public ModelAndView loginFormSubmit(@Valid @ModelAttribute("user") LoginCO loginCO, BindingResult result) {
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.addObject("id", loginCO.getId());
		modelAndView.addObject("password", loginCO.getPassword());
		modelAndView.addObject("username", loginCO.getUsername());
		modelAndView.setViewName("User-dashboard");

		return modelAndView;
	}

 

  • setViewName(String viewName): 뷰의 이름을 설정
  • setView(View view): 특정 뷰 객체를 설정
  • addObject(String attributeName, Object attributeValue): 모델에 데이터를 추가
  • getModel(): 모델 데이터를 반환
  • getViewName(): 설정된 뷰 이름을 반환
  • getView(): 설정된 뷰 객체를 반환

 

 

참고자료

스프링 호환 jdk 버전 확인

https://hermeslog.tistory.com/658

 

[Spring & Spring Boot] 스프링 JDK 버전 호환

[Spring &  Spring Boot] 스프링 JDK 버전 호환개발환경을 셋팅하다보면 꼭 찾아보게되는 내용이라.. 정리해봤습니다.스프링 - JDK 버전 호환ReleaseVersionJDK비고2023.11Spring FrameWork 6.2.xJDK 17, 19, 21, 23,

hermeslog.tistory.com

 

https://www.snugarchive.com/blog/sts-setup/

 

https://velog.io/@jeskey/%EC%8A%A4%ED%94%84%EB%A7%81-%EA%B8%B0%EB%B3%B8-%EA%B2%8C%EC%8B%9C%ED%8C%90

 

공식문서(다운링크)

https://github.com/spring-attic/toolsuite-distribution/wiki/Spring-Tool-Suite-3

 

이클립스 버전 확인 및 에러 관련

https://impact-ko.tistory.com/2