java, spring

[Spring framework] 스프링 MVC 설정과 Controller 실습 1

isaac.kim 2021. 5. 19. 19:56
728x90
반응형

 

[Spring framework] 스프링 MVC 설정과 Controller 실습 1

도움이 되셨다면 광고 한 번 클릭 부탁드립니다. 한 번의 클릭이 제게 큰 힘이 됩니다!^^

 

스프링 MVC는 웹 관련 스프링 라이브러리로 최근 웹 개발에서 필수적으로 사용되는 구조입니다. 국내에선 전자정부 표준 프레임워크를 스프링 프레임워크와 스프링 MVC를 이용해서 공공 프로젝트에서 표준으로 사용하고 있습니다. 일반적인 경우라면 스프링 프레임워크를 이용한다는 의미는 스프링 MVC 프로젝트인 경우가 대부분입니다.

 

반응형

 

스프링 프레임워크(Springframework)가 Main Project, 스프링 MVC는 서브 프로젝트입니다. 스프링은 하나의 기능을 위해 만들어진 프레임워크가 아니라 '코어'라고 할 수 있는 프레임워크에 여러 서브 프로젝트를 결합해서 다양한 상황에 대처할 수 있도록 개발되었습니다. 서브 프로젝트라는 의미를 개발자 입장에서 쉽게 이해할 수 있는 방법은 '별도의 설정이 존재할 수 있다'는 개념입니다.

 

스프링 MVC 프로젝트의 내부 구조

스프링 MVC 프로젝트를 구성해서 사용한다는 의미는 내부적으로 root-context.xml로 사용하는 일반 Java 영역(흔히 POJO(Plain Old Java Object)과 servlet-context.xml로 설정하는 Web 관련 영역을 같이 연동해 구동합니다.

 

1. 스프링 MVC 프로젝트 시작하기

Spring Legacy Project 생성 > Spring MVC Project > 패키지 네임 설정 

 

 

2. pom.xml 수정 (필요한 라이브러리 dependency를 설정하여 프로젝트에 설치하기)

 

spring version 수정

	<properties>
		<java-version>1.8</java-version>
		<org.springframework-version>5.1.5.RELEASE</org.springframework-version>
		<org.aspectj-version>1.6.10</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
	</properties>

spring-test 추가

		<!-- spring-test -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

lombok 추가

		<!-- lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.0</version>
			<scope>provided</scope>
		</dependency>

 

Spring Legacy Project로 생성된 프로젝트는 서블릿 버전이 2.5버전을 사용하나, Java 설정 등을 이용하려면 3.0 이상을 사용하는 것이 좋습니다. pom.xml을 수정합니다. (2.5버전 주석처리 혹은 삭제)

		<!-- Servlet -->
		<!-- <dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>  -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
		</dependency>

Maven의 컴파일 옵션 1.8버전으로 변경 (이전 글 참고)

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>

 

 

pom.xml 수정이 완료되었으면 프로젝트의 'Maven' 메뉴에서 'update project'를 실행합니다.

 

다음으로 해당 프로젝트를 실행해봅니다.

프로젝트 실행 : 프로젝트 > Run As > Run on Server 로 해당 프로젝트 실행

웹 브라우저로 접속 (http://localhost:8080/controller/)

 

해당 프로젝트의 기본 경로를 '/' 로 실행될 수 있도록 처리합니다.

 

Servers > Tomcat 더블클릭 > Modules 탭 > Edit > Path의 값을 '/' 로 수정

 

tomcat - project 재실행

 

 

3. Java 설정을 이용하는 경우

web.xml, servlet-context.xml, root-context.xml을 제거합니다.

 

그리고 pom.xml에 web.xml이 없다는 설정을 추가해야 합니니다. pom.xml에 <plugin> 설정을 추가합니다.

			<!-- web.xml이 없다는 설정 -->
            <plugin>
            	<groupId>org.apache.maven.plugins</groupId>
            	<artifactId>maven-war-plugin</artifactId>
            	<version>3.2.0</version>
            	<configuration>
            		<failOnMissingWebXml>false</failOnMissingWebXml>
            	</configuration>
            </plugin>

Java 설정을 이용하는 경우 sevlet-context.xml이 없으므로,

com.project.config 패키지를 작성하고, WebConfig 클래스와 RootConfig 클래스를 작성합니다.

 

 

 

RootConfig 클래스

package com.project.config;

import org.springframework.context.annotation.Configuration;

@Configuration
public class RootConfig {

}

WebConfig 클래스

package com.project.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebConfig
extends AbstractAnnotationConfigDispatcherServletInitializer{

	@Override
	protected Class<?>[] getRootConfigClasses() {
		// TODO Auto-generated method stub
		return new Class[] {RootConfig.class};
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	protected String[] getServletMappings() {
		// TODO Auto-generated method stub
		return null;
	}

}

 

Spring MVC를 이용하는 경우에는 servlet-context.xml을 대신하는 별도의 ServletConfig 클래스를 작성합니다.

ServletConfig 클래스는 servlet-context.xml에 설정된 모든 내용을 담아야 합니다.

package com.project.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@EnableWebMvc
@ComponentScan(basePackages = {"com.project.controller"})
public class ServletConfig implements WebMvcConfigurer {
	
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		InternalResourceViewResolver bean = new InternalResourceViewResolver();
		bean.setViewClass(JstlView.class);
		bean.setPrefix("/WEB-INF/views/");
		bean.setSuffix(".jsp");
		registry.viewResolver(bean);
	}
	
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
	}
}

 

WebMvcConfigurer는 스프링 MVC와 관련된 설정을 메서드로 오버라이드 하는 형태를 이용할 때 사용합니다. ServletConfig 클래스 역시 @componentScan을 이용해서 다른 패키지에 작성된 스프링의 객체(Bean)를 인식할 수 있습니다.

 

작성된 ServletConfig 클래스를 정상적으로 실행하려면 WebConfig의 설정은 아래와 같이 ServletConfig를 이용하고, 스프링 MVC의 기본 경로도 '/'로 변경되어야 합니다.

WebConfig 클래스

package com.project.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebConfig
extends AbstractAnnotationConfigDispatcherServletInitializer{

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class[] {RootConfig.class};
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class[] {ServletConfig.class};
	}

	@Override
	protected String[] getServletMappings() {
		return new String[] {"/"};
	}

}

 

설정 이후 XML 방식과 동일하게 Tomcat에서 실행하면 됩니다.

 


다시,

 

4, 프로젝트의 로딩 구조

 

프로젝트 구동 시 관여하는 XML은 web.xml, root-context.xml, servlet-context.xml 파일입니다. 이 파일들 중 web.xml은 Tomcat 구동과 관련된 설정이고, 나머지 두 파일은 스프링과 관련된 설정입니다. 프로젝트의 구동은 web.xml에서 시작합니다. web.xml의 상단에는 가장 먼저 구동되는 Context Listener가 등록되어 있습니다. 

 

web.xml

 

프로젝트를 구동했을 때 출력되는 Log를 보면 web.xml에 설정된 Listener가 가장 먼저 출력됩니다.

 

<listener>에는 스프링 MVC의 org.springframework.web.context.ContextLoader가 등록되어 있고, 이 리스너는 다음 context-param으로 등록된 root-context.xml 파일을 읽습니다.

 

root-context.xml이 처리되면 파일에 있는 빈(Bean) 설정들이 동작하게 됩니다. 

 

root-context.xml에 정의된 객체(Bean)들은 스프링의 영역(context) 안에 생성되고, 객체들 간의 의존성이 처리됩니다. root-context.xml이 처리된 후에는 스프링 MVC에서 사용하는 DispatcherServlet이라는 서블릿과 관련된 설정이 동작합니다. 

 

org.springframework.web.servlet.DispatcherServlet 클래스는 스프링 MVC의 구조에서 가장 핵심적인 역할을 하는 클래스입니다. 내부적으로 웹 관련 처리의 준비작업을 진행하는데 이때 사용하는 파일이 servlet-context.xml입니다.

 

DispatcherServlet에서 XmlWebApplicationContext를 이용해서 servlet-context.xml을 로딩하고 해석하기 시작합니다.

이 과정에서 등록된 객체(Bean)들은 기존에 만들어진 객체(Bean)들과 같이 연동됩니다.

 

root-context.xml에서 정의된 Bean들과 servlet-context.xml에서 정의된 Bean들이 연동되는 것이죠.

 

 

5. 스프링 MVC

 

이 글은 스프링 MVC 설정에 대한 글이므로 스프링 MVC에 대해 간단하게만 알아보겠습니다.

 

스프링 MVC는 내부적으로는 Servlet API를 활용합니다. 따라서 개발자들은 Servlet/JSP의 API인 HttpServletRequest/HttpServletResponse라는 타입의 객체를 직접적으로 사용할 필요성이 현저하게 줄어듭니다. 스프링은 중간에 연결 역할을 하기 때문에 이런 코드를 작성하지 않고도 원하는 기능을 구현할 수 있게 됩니다.

 

스프링 MVC 개발 패턴은 쉽게 말해서 로직과 화면을 분리하는 스타일의 개발 방식입니다. 

 

M : Model (데이터를 담는 객체 담당)

V : View (보여지는 화면단을 담당)

C : Controller (Request에 대해 처리하는 로직을 작성, View에 전달할 데이터는 Model 객체에 담아 전달)

 

6. Controller

src/main/java 패키지에 SampleController 클래스 생성, 그리고 다음과 같이 작성합니다.

 

기존에 있던 HomeController와 다른 부분으로 어노테이션 @Controller를 설정하지 않았습니다. 해당 파일에는 HomeController 클래스와 달리 작은 s 가 없습니다. 즉 스프링에서 관리되지 않는 Bean을 얘기합니다. @Controller를 설정해주면 스프링에서 관리되는 Bean으로 등록되어 작은 s 가 생겨나게 됩니다.

 

 

스프링에서 관리되는 이유는 servlet-context.xml 에 com.project.controller 패키지를 스캔하도록 등록했기 때문.

스프링에서 객체를 관리하는 내용은 이전 글을 참고해주세요.

 

다시 SampleController를 보겠습니다.

클래스에 @RequestMapping("/sample/*") 으로 지정한 어노테이션은, 요청된 url이 /sample/* 로 들어오는 것은 SampleController 클래스가 모두 처리하게 합니다. 다음과 같은 url을 처리할 수 있게 되는 것이죠.

 

- /sample/aaa

- /sample/bbb

 

@RequestMapping 어노테이션은 클래스의 선언과 메서드 선언에 사용할 수 있습니다.

 

 

다음과 같은 url 요청이 있으면,

/sample/* 요청에 대해서는 SampleController가 그리고 내부에 "" 로 맵핑된 basic 메소드 실행됩니다.

 

src/resources 폴더 내 log4j.xml의 모든 'info'를 'debug'로 수정하면 아래와 같은 로그가 보입니다.

DEBUG: org.springframework.web.servlet.DispatcherServlet - GET "/sample/aaa", parameters={}
DEBUG: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped to public void com.project.controller.SampleController.basic()
INFO : com.project.controller.SampleController - basic...
DEBUG: org.springframework.web.servlet.view.JstlView - View name 'sample/aaa', model {}
DEBUG: org.springframework.web.servlet.view.JstlView - Forwarding to [/WEB-INF/views/sample/aaa.jsp]
DEBUG: org.springframework.web.servlet.DispatcherServlet - Completed 404 NOT_FOUND

화면까지 보이도록 하려면 [ /WEB-INF/views/sample/aaa.jsp ] jsp 파일을 생성해야겠네요.

 

7. Controller의 파라미터

Controller의 가장 편리한 기능은 파라미터가 자동으로 수집되는 기능입니다. 이 기능을 이용하면 매번 request.getParameter()를 이용하는 불편함을 없앨 수 있습니다.

 

실습을 위한 패키지와 클래스를 생성합니다. com.project.domain.SampleDTO 클래스

SampleDTO 클래스

package com.project.domain;

import lombok.Data;

@Data
public class SampleDTO {
	private String name;
	private int age;
}

 

SampleController의 메서드가 SampleDTO를 파라미터로 사용하게 되면 자동으로 setter 메서드가 동작하면서 파라미터를 수집하게 됩니다.

package com.project.controller;

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

import com.project.domain.SampleDTO;

import lombok.extern.log4j.Log4j;


@Controller
@RequestMapping("/sample/*")
@Log4j
public class SampleController {
	
	@RequestMapping("")
	public void basic() {
		log.info("basic...");
	}
	
	@GetMapping("/ex01")
	public String ex01(SampleDTO dto) {
		log.info("" + dto);
		return "ex01";
	}
	
}

 

SampleController의 경로가 '/sample/*' 이므로 ex01() 메서드를 호출하는 경로는 /sample/ex01 이 됩니다.

@GetMapping 어노테이션이 사용되었으므로, 필요한 파라미터를 URL 뒤에 '?name=AAA&age=10'과 같은 형태로 추가해서 호출할 수 있습니다.

 

url 요청

결과

 

실행된 결과를 보면 SampleDTO 객체 안에 name과 age 속성이 제대로 수집된 것을 볼 수 있습니다. 특히 주목할 점은 자동으로 타입을 변환해서 처리한다는 점입니다.

 

7.1 Controller 파라미터의 수집과 변환

 

Controller가 파라미터를 수집하는 방식은 파라미터 타입에 따라 자동으로 변환하는 방식을 이용합니다. 예를 들어, SampleDTO에는 int 타입으로 선언된 age가 자동으로 숫자로 변환되는 것을 볼 수 있습니다.

 

만일 기본 자료형이나 문자열 등을 이용한다면 파라미터의 타입만을 맞게 선언해주는 방식을 사용할 수 있습니다.

	@GetMapping("/ex02")
	public String ex02(@RequestParam("name") String name, @RequestParam("age") int age) {
		log.info("name : " + name);
		log.info("age : " + age);
		return "ex02";
	}

 

7.2 리스트, 배열 처리

 

동일한 이름의 파라미터가 여러 개 전달되는 경우에는 ArrayList<> 등을 이용해서 처리가 가능합니다.

	@GetMapping("/ex02List")
	public String ex02List(@RequestParam("ids") ArrayList<String> ids) {
		log.info("ids : " + ids);
		return "ex02List";
	}

스프링은 파라미터의 타입을 보고 객체를 생성하므로 파라미터의 타입은 List<>와 같이 인터페이스 타입이 아닌 실제적인 클래스 타입으로 지정합니다. 위 코드의 경우 'ids'라는 이름의 파라미터가 여러 개 전달되더라도 ArrayList<String>이 생성되어 자동으로 수집됩니다.

 

브라우저 등을 이용해서 '프로젝트 경로/sample/ex02List?ids=111&ids=222&ids=333'을 호출한다면 아래와 같이 로그가 출력됩니다.

 

7.3 객체 리스트

 

전달하는 데이터가 SampleDTO와 같이 객체 타입이고 여러 개를 처리해야 한다면 약간의 작업을 통해서 한 번의 처리를 할 수 있습니다. 예를 들어 SampleDTO를 여러 개 전달받아서 처리하고 싶다면 SampleDTO의 리스트를 포함하는 SampleDTOList 클래스를 설계합니다.

package com.project.domain;

import java.util.ArrayList;
import java.util.List;

import lombok.Data;

@Data
public class SampleDTOList {
	
	private List<SampleDTO> list;
	
	public SampleDTOList() {
		list = new ArrayList<>();
	}
	
}

SampleController에서는 SampleDTOList 타입을 파라미터로 사용하는 메서드를 작성합니다.

 

SampleController에 추가

	@GetMapping("/ex02Bean")
	public String ex02Bean(SampleDTOList list) {
		log.info("list dtos : " + list);
		return "ex02Bean";
	}

 

파라미터는 '[인덱스]'와 같은 형식으로 전달해서 처리할 수 있습니다.

 

요청 url (Tomcat 버전에 따라 문자열에서 '[ ]' 특수문자로 허용하지 않을 수 있음)

http://localhost:8080/sample/ex02Bean?list[0].name=aaa&list[2].name=bbb

 

 - javascript를 이용하는 경우에 encodeURIComponent()와 같은 방법으로 해결할 수 있습니다.

 

요청 url

http://localhost:8080/sample/ex02Bean?list%5B0%5D.name=aaa&list%5B2%5D.name=bbb

 

 

결과

 

여러 개의 SampleDTO 객체를 생성하는 것을 볼 수 있고, '[ ]' 안에 인덱스 번호에 맞게 객체의 속성값이 세팅된 것을 확인할 수 있습니다.

 

 


이번 글에서는 스프링 MVC 설정을 알아보고, Controller 구현과 Controller에 Parameter 를 넘기는 실습을 해보았습니다. 내용이 너무 길어져서 다음 글에서 작성하도록 하겠습니다.

 

도움이 되셨다면 광고 한 번 클릭 부탁드립니다. 한 번의 클릭이 제게 큰 힘이 됩니다!^^
728x90
반응형