드디어 Spring boot, Kotlin으로 jsp 띄우기 성공

TrainingDummy1·2021년 1월 8일
0

wordbook

목록 보기
1/1

처음에는 단순히 Spring boot를 연습할 생각이었지만... Kotlin을 보고 이름이 마음에 들어서 무작정 뛰어들었다.
게다가 어디선가 'xml은 점점 안쓰이는 추세다'라는 소문을 들어서 설정파일도 싹 다 Kotlin으로 짜보겠다고 난리를 치다보니 한참 걸렸다... jsp 하나 띄우는데...
게다가 처음엔 Eclipse에 Kotlin Plugin이 있어서 이걸로 했었는데, 실행이 안돼서 한참 헤멨다. 결론은 플러그인의 문제라 판단. IntelliJ 체험판으로 똑같은 프로젝트가 잘 돌아가더라!!!!!
그 이후로 DB 연동할 때 가볍게 쓰는게 H2라 해서 써보려 했다...만, H2는 시스템에서 테이블 불러오는 쿼리 자체가 잘못되어있는데, 그걸 수정할 수 없게 만들어놔서 사용도 못하고 시간만 날렸다.
결국 Oracle 연동을 가볍게 해주고 시작했다.
(그런데 마지막에 반전이 있다...)

각설하고 본론으로 들어가보자. 도움 안됨 주의

개발 환경
IntelliJ
Spring boot 2.4.1(JavaSE 11.0.9, Kotlin 1.4.21, maven, war)

파일 리스트

pom.xml (artifact-id)

spring-boot-starter-jdbc -> db 사용
spring-boot-starter-web
jackson-module-kotlin
kotlin-reflect
kotlin-stdlib-jdk8
mybatis-spring-boot-starter -> db 설정
ojdbc8 -> oracle 사용
spring-boot-starter-tomcat
spring-boot-starter-test
tomcat-embed-jasper -> jsp 사용
jstl -> tag library 사용(현재는 사용 X)

RootConfig.kt

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

@Configuration
@ComponentScan(basePackages = ["com.dummy.wordbook.controller", "com.dummy.wordbook.dto", "com.dummy.wordbook.mapper", "com.dummy.wordbook.service"])
class RootConfig : WebMvcConfigurer {
    /*override fun configureViewResolvers(registry: ViewResolverRegistry) {
        registry.jsp("/WEB-INF/views/", ".jsp")
    }*/
}

@EnableWebMvc를 붙여봤으나 오히려 방해만 돼서 빼버렸다. 그리고 prefix랑 suffix 설정은 application.properties에서 설정했기에 여기서도 가능하지만 뺐다.

WebConfig

import org.springframework.context.ApplicationContext
import org.springframework.web.WebApplicationInitializer
import org.springframework.web.context.ContextLoaderListener
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext
import org.springframework.web.filter.CharacterEncodingFilter
import org.springframework.web.servlet.DispatcherServlet
import java.util.*
import javax.servlet.*

class WebConfig: WebApplicationInitializer {
    override fun onStartup(servletContext: ServletContext) {
        registerDispatcherServlet(servletContext)
        registerCharacterEncodingFilter(servletContext)
    }

    private fun registerDispatcherServlet(servletContext: ServletContext) {
        val context = AnnotationConfigWebApplicationContext()
        context.register(ApplicationContext::class.java)

        servletContext.addListener(ContextLoaderListener(context))

        val webContext = AnnotationConfigWebApplicationContext()
        webContext.register(WebConfig::class.java)

        val dispatcher: ServletRegistration.Dynamic
                = servletContext.addServlet("DispatcherServlet", DispatcherServlet(webContext))
        dispatcher.setLoadOnStartup(1)
        dispatcher.addMapping("/*")
    }

    private fun registerCharacterEncodingFilter(servletContext: ServletContext) {
        val characterEncodingFilter: FilterRegistration.Dynamic
                = servletContext.addFilter("encodingFilter", CharacterEncodingFilter())
        characterEncodingFilter.setInitParameter("encoding", "UTF-8")
        characterEncodingFilter.setInitParameter("forceEncoding", "true")
        characterEncodingFilter.addMappingForServletNames(EnumSet.allOf(DispatcherType::class.java), true, "/*")
    }
}

/*
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
class WebConfig: AbstractAnnotationConfigDispatcherServletInitializer() {
    override fun getServletMappings(): Array<String> {
        return arrayOf("wordbook");
    }

    override fun getRootConfigClasses(): Array<Class<*>>? {
        return arrayOf(RootConfig::class.java)
    }

    override fun getServletConfigClasses(): Array<Class<*>>? {
        return arrayOf(ServletConfig::class.java)
    }
}*/

 AnnotationConfigWebApplicationContext() 문서를 읽어보면 @Configuration, @Component 등을 사용하는데 필요한 듯 하다. web.xml에서 기능하는 것을 사용할 수 있게 해준다고 써있다.
 그런데 이 글을 쓰는 도중에 반전이 있다는걸 알게되었다... 이건 나중에...

MemberController.kt

import com.dummy.wordbook.service.MemberService
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.RequestMapping

@Controller
class MemberController(private val memberService: MemberService) {
    @RequestMapping("/")
    public fun indexPage(m: Model): String {
        m.addAttribute("member", memberService.selectMemeberById(2));
        return "index"
    }
}

 의존성 주입 시 생성자 방식으로 하는 것이 대세라고 하더라. 그래서 생성자 방식으로 MemberService에 대한 의존성을 주입했다.
 get, post 상관 없이 "/"로 요청이 오면 index.jsp가 열리게 하고, Model을 통해 id가 2인 멤버의 정보를 jsp에 전달한다.

MemberDto.kt

import java.time.LocalDateTime

data class MemberDto(
        val id: Int,
        val memberId: String,
        val password: String,
        val email: String,
        val phone: String,
        val address: String?,
        val regDate: LocalDateTime) {
}

 data class로 선언. 자동으로 equals(), hashCode(), toString, copy(), componentN() 을 생성한다고 한다.
 찾아보니 Vo, Dto, Entity를 구분해서 쓴다는데, 아마 이건 Dto보단 Entity에 가깝지 않을까 생각된다. 이 부분은 좀 더 공부해봐야겠다.

MemberSQL.kt

class MemberSQL {
    public fun selectMemberById(id: Int): String {
        var query: StringBuilder = StringBuilder("")
        query.append("select * from MEMBER where id = #{id}")
        return query.toString()
    }
}

 나중에 조건 달아서 쿼리문을 바꿀 때를 대비해 StringBuilder로 선언했지만, 그냥 String으로 때려박아도 된다. 의외로 annotation을 쓰지 않는다.

MemberMapper.kt

import com.dummy.wordbook.dto.MemberDto
import org.apache.ibatis.annotations.Mapper
import org.apache.ibatis.annotations.SelectProvider

@Mapper
interface MemberMapper {
    @SelectProvider(type = MemberSQL::class, method = "selectMemberById")
    public fun selectMemberById(id: Int): MemberDto
}

 annotation 따라서 Mapper라고 이름지었지만, 흔히 말하는 DAO다. 위에서 만든 MemberSQL 클래스와 메서드를 annotation으로 불러와서 쓴다.

MemberService.kt

import com.dummy.wordbook.dto.MemberDto
import com.dummy.wordbook.mapper.MemberMapper
import org.springframework.stereotype.Service

@Service
class MemberService(private val memberMapper: MemberMapper){
    public fun selectMemeberById(id: Int): MemberDto {
        return memberMapper.selectMemberById(id)
    }
}

역시 생성자 의존성 주입 방식으로 했다.

application.properties

server.port=9082

spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe
spring.datasource.username=(username)
spring.datasource.password=(password)

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

server.servlet.context-path=/

server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true

 엄청 편리하다. 신세계네... (username)과 (password)는 지우고 본인에게 맞게 넣으면 된다.

 처음 써보는 언어와 개발 툴이라서 엄청 헤맸다. 그래서 쓸데없는 것에 시간을 많이 썼는데... 애시당초 스프링 부트는 web.xml, servlet-dispatcher.xml 관련 설정들을 자동으로 해주는데다, 나머지도 application.properties에서 다 설정이 가능해서 WebConfig.kt, RootConfig.kt 파일이 필요가 없었다!! 가장 시간을 많이 쓴 부분이 쓸데 없는 짓이었다는건 너무나도 슬픈 일인데, 대체 저걸 하면서 뭘 건드렸길래 저 두 파일을 만들기 전엔 안되던게 이젠 저 두 파일을 지워도 잘 되는걸까... 이게 개발이지 아ㅋㅋ 그래도 좋은 공부가 되었다고 믿자.
 여러분들은 부디 안된다고 허둥대지 말고 문서를 찬찬히 읽어보시길 바란다.물론 나는 안된다고 허둥된게 아니라 영어를 보고 허둥댔다

 점차 기능을 늘려가면서 프로젝트를 완성해갈 예정. 자동 번역 단어장을 만들 예정이다. 과연 파파고 API를 무사히 적용시킬 수 있을 것인가?

참고 글

profile
연습용더미1

0개의 댓글