티스토리 뷰

728x90

1. Settings에 있는 이전 포스트와 동일하게 세팅하는 부분만 적어 놓는다. 어디에나 있는 내용은 안쓰려고 한다.

 

2. 내용은 Gradle, Tomcat, Eclipse로 기본 Spring MVC를 세팅하는 부분이다. 

 

3. 우선 이클립스와 톰캣을 설치한다. 이클립스는 2021-09 버전, 톰캣은 9버전이다. 

  3-1 2021-09버전은 Gradle 개발지원 plugin이 설치되어 있다. gradlew가 6.8을 사용한다. JDK11이면 충분하다.

    3-1-1 Gradle 버전은 중요한데 17을 지원하는 최초의 Gradle이 7.3인데, 7.3 이하는 동작하지 않는다는 말이다.

 

 

  3-2 톰캣 9버전을 설치한 이유는 Servlet 4.0을 지원하므로 web.xml 없이 서블릿을 구동할 수 있기 때문이다.

    3-2-1 톰캣9는 자바 1.8이 최소 지원 버전이다. 일반적으로 11버전을 사용하면 된다.

 

 

4. 자바11을 설치한다. 제일 많이 호환되는 버전이라 안전하다. 난 eclipse 안에다가 풀었다.

 

 

5. 이클립스에 tomcat을 등록한다. 그냥 하면 된다. 등록할 때 톰캣과 JDK의 위치를 지정해 줘야 한다.

 

6. 이제 Gradle Project를 만든다. 만들면 그냥 에러가 뜰거다. 왜냐면 Gradle도 JDK가 필요하다.

 

 

  6-2 Preferences에서 Gradle을 찾아서 JDK 경로를 잡아준다. 버전은 위의 표를 참고하면 되는데 그냥 11로 하면 된다.

 

 

  6-3 이제 다시 Gradle Project를 만들면 다음처럼 나온다. gradle은 java library 프로젝트가 기본이다. 짜증난다.

 

 

    6-3-1 최신 버전처럼 app으로 바꿔 주는 게 보기 좋긴 하다.

      6-3-1-0 이클립스에서 이름만 바꾸면 안된다. 폴더명이 바뀌지 않는다.

      6-3-1-1 아래처럼 settings.gradle에 include('lib') 대신 app, build.gradle에는 id java-library를 war로 바꾸었다.

      6-3-1-2 이렇게 하면 플러그인이 맞지 않아 컴파일이 되지 않는다. 그냥 폴더를 청소한다.

 

 

    6-3-2 폴더 안의 자바 코드를 삭제한다. 삭제 후 Refresh Gradle Project를 누르면 에러가 없어 질 거다.

 

 

7. 이제 필요한 라이브러리를 추가한다. org.apache.common은 필요없으니 삭제하는 게 좋다. 

  7-0 이클립스의 New Gradle Project가 너무 구식이라 gradle init으로 만들어서 import하는 게 나아보인다.

  7-1 스프링은 Servlet기반이므로 Servlet 라이브러리가 필요하다. 4.0을 사용하여 web.xml 없이 작성한다.

  7-2 스프링이 기본적으로 동작하려면 core, context가 필요하다.

  7-3 스프링 웹을 사용하려면 web, webmvc가 필요하다.

 

 

  7-3 스프링의 Servlet 지원 패키지 구조가 이해하기 어렵다.

  7-4 web만 하면 되는 걸 webmvc 에도 분산해 놓은 느낌이다.

  7-5 Servlet 4.0 web.xml 대용으로 사용하는 콜백 클래스들을 등록해 놓았지만

    7-5-1 webmvc에 DispatcherServlet이 정의되어 있다. 그리고 servlet의 기본인 routing인 Controller를 찾아가는 HandlerMapper는 보이지 않는 @EnableWebMvc로 동작한다. 아무튼 위의 것들이 필요하다

  7-6 한 가지 아쉬운 건 junit이 4.13이다. eclipse도 gradle 프로젝트 생성 시 옵션을 선택할 수 있게 바뀌었으면 한다.

 

8. 이젠 web.xml 대신 사용할 WebInitializer를 만든다. XML을 안쓸거기 때문에 존나 긴 Initalizer를 사용한다.

  8-0 용도는 DispatcherServlet을 초기화하는데 사용한다. 이거 모르면 서블릿 공부해야 한다.

    8-0-1 단순하게 말하면 web.xml은 서블릿들 명세와 각 경로가 지정되는 테이블이다.

    8-0-2 DispatcherServlet은 하나의 디자인 패턴으로 web.xml에 단 하나의 서블릿만 지정하고 이것이 다시 Request를 Controller로 넘겨주는 형식이다.

    8-0-3 그렇기 때문에 존나긴 DispatcherServletInitalizer에서 DispatcherServlet을 생성하고 ServletContext와 연결한다. 서블릿의 경로를 지정해 주는 것도 당연한 역활이다.

 

  8-1 이 web.xml을 대신하는 용도로 사용하는데 이름이 긴 이유는 이 넘이 ServletContext와 ApplicationContext를 다루기 때문이다.

  8-2 XML을 쓸거면 이것의 부모 클래스 중에서 AbstractDispatcherServletInitializer 이거 쓰면 된다.

 

 

  8-2 내용을 보면 무엇을 하는 것인지 명확하다. 하는 일이 web.xml과 동일하다.

    8-2-0 getServletConfig에 WebMvcConfigurer를 구현한 클래스를 설정 클래스로 등록하면 Controller가 동작하지 않는다.

      8-2-0-1 ServletConfig에 등록한 설정 클래스에 @ComponentScan을 달면 컨트롤러가 동작하지 않는다.

    8-2-1 RootConfg, ServletConfig는 ApplicationContext의 단계적 구조 때문에 정의하는 부분인데,

    8-2-2 단일 레벨을 사용하려면 RootConfig만 정의해도 된다. 거기에 설정 파일을 넣으면 된다.

    8-2-3 일반적으로 Root에 공통적으로 사용하는 데이터베이스 접근과 서비스 레이어의 빈들을 생성한다.

 

package gradle_spring.config;

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

public class MyWebInitalizer extends AbstractAnnotationConfigDispatcherServletInitializer {

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

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return null;
	}

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

}

 

8-3 이제 설정파일을 만든다. 중요한 건 WebMvcConfigurer을 implements 한 것이다.

  8-3-1 AppConfig.java는 단순 ApplicationContext에 등록할 빈을 정의하는 설정클래스이다.

  8-3-2 이 클래스에 WebMvcConfigurer를 구현하면 다양한 기능이 첨가하게 된다. 

  8-3-3 주로 DispatcherServlet이 Http 메시지를 받았을 때 수행하는 기본적인 일에 어떤 구현객체를 사용할지를 지정할 수 있다. 아래 내용을 처리하기 위한 객체를 설정하는 것이 이 인터페이스의 역할이다.

    8-3-3-1 DispatcherServlet은 Request를 받으면 WebApplicationContext를 찾아 Request에 붙인다.

    8-3-3-2 그런 후 locale에 관련된 작업을 한다.

    8-3-3-3 다음에는 view가 사용할 theme을 결정하게 된다.

    8-3-3-4 이제 Request객체의 Content-Type이 Multipart인지를 확인하고 맞으면 MultiPartHttpServletRequest로 변환

    8-3-3-5 URL을 분석하여 적절한 Handler를 찾는다. handler는 일반적으로 Controller이다.

    8-3-3-6 처리를 끝나면 ViewResolver를 통해 view을 생성하고 돌려준다.

 

 

    8-3-4 아래의 설정을 보면 아무 것도 없다. 그러면 스프링은 DispatcherServlet.properties에서 기본값을 가져온다.

      8-3-4-1 DispatcherServlet은 어이없게도 webmvc 패키지에 들어있다.

      8-3-4-2 아래는 jsp 렌더링을 위해서 InternalResourceViewResolver 했지만 이건 안해도 된다.

      8-3-4-3 아래처럼 안하면 Controller에서 pre, post fix를 다 붙여주어야 하는 불편은 있다.

      8-3-4-4 또 하나 중요한 부분은 @EnableWebMvc인데 이게 없으면 Controller 라우팅이 되지 않는다.

      8-3-4-5 라우팅을 할려면 RouterFunctionMapping, HandlerFunctionAdapter 이게 필요한데 이거 기본객체가 생성되지 않는다.

      8-3-4-6 @EnableWebMvc는 DelegatingWebMvcConfiguration.class를 호출하는데 이게 라우팅 객체를 생성한다.

 

package gradle_spring.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@ComponentScan("gradle_spring")
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
	@Bean
	public ViewResolver viewResolver() {
		return new InternalResourceViewResolver("/WEB-INF/views/", ".jsp");
	}
}

 

      8-3-4-1 아래는 spring 5.3.13의 webmvc 에서 가져온 내용이다.

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

 

8. 이제 컨트롤러와 index.jsp 하나 생성한다.

 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>Hello, Pilseong</h1>
</body>
</html>


package gradle_spring.controller;

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

@Controller
public class HomeController {

	@GetMapping("/hello")
	public String hello() {
		return "index.jsp";
	}
}

 

8. 결과는 다음과 같이 잘 나온다.

 

 

  8-1 jsp 위치를 참조하라고 붙였다.

 

 

9 참고로 존나 긴 DispatcherServletInitalizer는 편의를 위해 WebApplicationInitailizer를 구현한 클래스이다.

  9-1 아래 처럼 web.xml에서 하듯 서블릿을 생성하고 ApplicationContext 생성하고 서블릿과 ServletContext를 매핑하고, 매핑과 콘테이너 기동 설정을 해 줄 수도 있다.

  9-1 사실 이게 더 편하게 보인다. 구질구질하게 편의를 제공한 부분도 스프링이 어려운 이유 중 하나이다.

 

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(context);
        ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}

 

 

10. 이클립스에 gradle 프로젝트를 사용하는 게 쉽지 않다.

  10-1 가장 쉬운 방법은 gradle로 프로젝트를 생성해서 build.gradle를 수정한 후에 import하는 방법이다.

  10-2 아래는 참고용

 

/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java library project to get you started.
 * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
 * User Manual available at https://docs.gradle.org/6.8/userguide/building_java_projects.html
 */

plugins {
    // Apply the java-library plugin for API and implementation separation.
    id 'war'
}

repositories {
    // Use JCenter for resolving dependencies.
    jcenter()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'

    compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
    implementation 'org.springframework:spring-core:5.3.15'
    implementation 'org.springframework:spring-context:5.3.15'
    implementation 'org.springframework:spring-web:5.3.15'
    implementation 'org.springframework:spring-webmvc:5.3.15'
    
    // This dependency is used internally, and not exposed to consumers on their own compile classpath.
    implementation 'com.google.guava:guava:29.0-jre'
}
728x90
댓글