본문 바로가기

카테고리 없음

kotlin & Spring MVC & embeded tomcat으로 서버구성(without xml)

github repository: https://github.com/SonEunHo/simple_webapp_with_spring_and_tomcat

 

kotlin으로 Spring MVC+ embeded tomcat 로 구성된 서버를 띄워보겠습니다.

사용할 ide는 intellij, 빌드툴은 gradle입니다.

 

이걸로 간단히 프로젝트를 만들고 이름은 대충 넣고 설정도 기본설정으로 했습니다.

프로젝트 내부 구성은 이렇게 되어 있는데 코틀린으로 할거닌까 main과 test밑의 java 디렉토리는 제거하겠습니다.

역시 시작은 hello world부터

여기까지 하고 일단 커밋 (https://github.com/SonEunHo/simple_webapp_with_spring_and_tomcat/commit/2a4cbd08270464bafcd7e79b4e37c3c1a6c52c1c)

 

외부 디펜던시들을 가져와봅시다.

- Spring mvc (https://mvnrepository.com/artifact/org.springframework/spring-webmvc)

- embeded tomcat (https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core)

이 글 작성 시 최신버전으로 가져왔습니다.

 

그리고 Main을 이렇게 수정

import org.apache.catalina.startup.Tomcat
import java.io.File

class Main {
    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            val tomcat = Tomcat().apply {
                setPort(8080)
                addWebapp("/", File("src/main/myapp").absolutePath)
                connector
            }
            tomcat.start()
            tomcat.server.await()
        }
    }
}

그리고 간단한 페이지를 위해서 myapp폴더와 html페이지 만들었습니다.

myapp은 웹어플리케이션의 여러 정보를 모아두는 목적으로 만들었습니다. (예를 들어 html페이지, jsp 등등)

 

이렇게 돌리면 

이렇게 에러같이 뭔가 무섭게 뜰텐데, 그냥 jsp 라이브러리가 없다는 뜻이고, 무시해도 됩니다.

jsp 개발을 할거면 gradle dependency에 jsp를 추가해주면 됩니다(https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jasper)

 

요까지 하고 커밋 (https://github.com/SonEunHo/simple_webapp_with_spring_and_tomcat/commit/e8d398afb596031721bae36d449b37eb94b8bb7a)

 

이제 스프링 컨테인너를 톰캣에 적용해서 어노테이션 기반의 웹 어플리케이션 기반을 만들어보겠습니다.

 

스프링 프레임워크를 톰캣에 넣어주기 위해서 WebApplicationInitializer를 구현한 클래스를 만들었습니다.

package com.nanostudio.webapp.config

import org.springframework.web.WebApplicationInitializer
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext
import org.springframework.web.servlet.DispatcherServlet
import javax.servlet.ServletContext

class MyAppInitializer: WebApplicationInitializer {
    override fun onStartup(servletContext: ServletContext) {
        val myApplicationContext = AnnotationConfigWebApplicationContext()
            .apply { register(MyConfiguration::class.java) }

        val myDispatcherServlet = DispatcherServlet(myApplicationContext)

        servletContext.addServlet("myDispatcherServlet", myDispatcherServlet)
            .also {
                it.addMapping("/")
            }
    }
}

그리고 스프링 설정 파일을 만들었습니다.

여기서 @EnableWebMvc, WebMvcConfigurer 없어도 상관없습니다.

package com.nanostudio.webapp.config

import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.EnableWebMvc
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
@ComponentScan("com.nanostudio.webapp")
@EnableWebMvc
open class MyConfiguration: WebMvcConfigurer {

}

마지막으로 간단한 컨트롤러를 만들었습니다.

package com.nanostudio.webapp.controller

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/hello")
class HelloController {

    @GetMapping
    fun helloWorld() = "world"
}

 

프로젝트 디렉터리 구조는 요렇습니다.

 

이렇게 돌리고 localhost:8080/hello 를 치면 world가 나옵니다.

 

원리

어떻게 동작했길래 서버가 뜨는걸까요?

우리는 톰캣만 실행했을 뿐 MyApplicationInitializer를 호출하진 않았습니다.

 

그 이유는 WebApplicationInitializer구현체를 만들어두면 스프링의 SpringServletContainerInitializer가 감지하여 실행시켜주기 때문입니다.

그러면 SpringServletContainerInitializer 또 누가 실행시켜줄까요?

embeded tomcat은 프로젝트에 존재하는 ServletContainerInitializer 구현체를 찾아서 알아서 실행시켜줍니다.

네, SpringServletContainerInitializer은 ServletContainerInitializer구현체입니다.

 

다시 말하자면

tomcat이 ServletContainterInitializer를 찾아서 등록한

Spring은 ServletContainerInitializer 구현체인 SpringServletContainerInitializer를 미리 만들어서 제공한다.

SpringServletContainerInitializer은 WebApplicationInitializer를 찾아서 실행시켜준다.

 

 

마지막 커밋(https://github.com/SonEunHo/simple_webapp_with_spring_and_tomcat/commit/a9db412a122487e4058e0ac17ad1e43ea08fa899)

 

-------------

추가로 ServletContextListener 구현체도 등록해봄

(https://github.com/SonEunHo/simple_webapp_with_spring_and_tomcat/commit/764ca85417d78f3f4e2ff48b7fea6517674c489d)

 

 

 

----------------------------------------------------

 

코틀린은 기본적으로 클래스가 final이다. 즉, 상속불가능이다.

만약 상속을 허용하고자 한다면 open 키워드를 class 앞에 붙여줘야한다.

 

spring을 사용하게 되면 다양한 annotation을 클래스에 붙이게 되는데 이 때마다 open을 명시해주는 것은 은근히 귀찮은 일이다.

 

plugin을 추가해서 이를 해결할 수 있다.

참고: https://kotlinlang.org/docs/reference/compiler-plugins.html