tools & libs/IDES

[intellij]Community Edition에서 스프링 부트 개발하기

  • -

가난한 개발자를 위한 Intellij의 community edition은 많은 기능이 제한되어있지만 그렇다고 개발에 많은 어려움이 있지는 않다.

intellij의 community edition은 springboot를 위한 메뉴가 없다.

이번 포스트에서는 Intellij의 community edition을 이용해서 spring boot 앱을 개발하는 절차에 대해 알아보자.

 

 

프로젝트 구성

이번 포스트는 gradle을 빌드 툴로하고 mustache를 template engine으로 사용할 한다. 옵션으로 단위테스트는 groovy 기반의 spock을 사용한다.

프로젝트 생성

New Project에서 다음과 같이 Build tool에서 Gradle을, Additional Libraries and Frameworks에서 Java와 Groovy를 선택한다.

Next를 누르고 Name, Location 등의 정보를 입력 및 확인하고 finish를 클릭한다.

 

build.gradle 편집

build tool인 gradle의 설정을 위해 build.gradle을 편집한다.

plugins

plugins 항목에 spring boot의 버전을 나타내는 org.springframework.boot와 io.spring.dependency-management를 추가한다.

plugins {
    id 'groovy'
    id 'java'
    id 'org.springframework.boot' version '2.4.3'
    id('io.spring.dependency-management') version('1.0.11.RELEASE')
}

참고로 build.gradle을 작성하는 groovy DSL에서는 id나 value 같은 함수를 사용할 때 마지막 라인처럼 () 를 생략할 수 있다.(대부분 생략하고 쓴다.)

dependencies 

의존성 부분은 spring boot에서 관리하는 부분과 그렇지 않은 부분으로 나눠서 생각할 수 있는데 전자는 spring boot에서 io.spring.dependency-management에서 버전을 관리하기 때문에 따로 버전을 명시하지 않는다. org.springframework.boot의 버전에 맞춰서 호환되는 버전을 자동으로 사용하기 때문이다. 하지만 그렇지 않은 경우는 당연히 버전을 명시해줘야 한다. 아래의 org.spockframework:spock-core와 net.bytebuddy가 그러하다.

dependencies {
    // spring boot 관련된 library 들은 io.spring.depeneency-management에 의해 자동으로 버전 관리가 이뤄진다.
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-devtools'
    
    // mustache 사용을 위해 추가한다.
    implementation 'org.springframework.boot:spring-boot-starter-mustache'
    // 단위테스트를 위해 추가한다.
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    // spock 단위테스트를 사용하기 위해 추가한다.
    testImplementation 'org.spockframework:spock-core:1.3-groovy-2.5'
    testImplementation 'org.spockframework:spock-spring:1.3-groovy-2.5'    
    // spock에서 클래스 기반의 Mock을 생성하기 위해 추가한다.
    testImplementation 'net.bytebuddy:byte-buddy:1.10.20'
}

라이브러리를 등록할 때 intellij에서 자동완성이 되기도 하지만 mvnrepository.com에서 조회해서 등록하는 것이 더 편한것 같다.

 

기타

compiler 버전 설정을 위해 sourceCompatibility를 사용한다. 그런데 주의할 점은 다른 속성들과 달리 이 속성은 =으로 값을 할당해야 한다. 

group 'com.quietjun'
version '1.0-SNAPSHOT'
sourceCompatibility = "1.8"

 

전체 코드

위 내용들을 반영한 전체 코드는 아래와 같다.

plugins {
    id 'groovy'
    id 'java'
    id 'org.springframework.boot' version '2.4.3'
    id('io.spring.dependency-management') version('1.0.11.RELEASE')
}

group 'com.quietjun'
version '1.0-SNAPSHOT'
sourceCompatibility = "1.8"

repositories {
    mavenCentral()
}

dependencies {
    // spring boot 관련된 library 들은 io.spring.depeneency-management에 의해 자동으로 버전 관리가 이뤄진다.
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-devtools'
    
    // DTO를 손쉽게 만들기 위해 lombok을 추가한다.
    implementation 'org.projectlombok:lombok'
    // mustache 사용을 위해 추가한다.
    implementation 'org.springframework.boot:spring-boot-starter-mustache'
    // 단위테스트를 위해 추가한다.
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    // spock 단위테스트를 사용하기 위해 추가한다.
    testImplementation 'org.spockframework:spock-core:1.3-groovy-2.5'
    testImplementation 'org.spockframework:spock-spring:1.3-groovy-2.5'    
    // spock에서 클래스 기반의 Mock을 생성하기 위해 추가한다.
    testImplementation 'net.bytebuddy:byte-buddy:1.10.20'
}

test {
    useJUnitPlatform()
}

 

프로젝트에 반영

이렇게 build.gradle을 수정하면 우측에 아래와 같은 아이콘이 표시되고 코끼리를 클릭해주면 sync가 build.gradle을 이용해서 project의 sync를 맞추게 된다.


코드 작성


Application.java

가장 먼저 Spring boot 애플리케이션의 시작점인 @SpringBootApplication 클래스를 작성해주자.

package com.quietjun.intellij;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

여기까지만 작성하고 클래스를 실행시켜도 정상적으로 동작함을 확인할 수 있다. 로그를 살펴보면 tomcat이 8080포트에서 대기하고 있음을 알 수 있다.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.3)

2021-02-22 10:50:32.320  INFO 21020 --- [           main] com.quietjun.intellij.Application        : Starting Application using Java 1.8.0_192 on andy-gram14 with PID 21020 (D:\ws_intellij\intellijcommunity\build\classes\java\main started by itsmeyjc in D:\ws_intellij\intellijcommunity)
2021-02-22 10:50:32.320  INFO 21020 --- [           main] com.quietjun.intellij.Application        : No active profile set, falling back to default profiles: default
2021-02-22 10:50:33.328  INFO 21020 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-02-22 10:50:33.339  INFO 21020 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-02-22 10:50:33.339  INFO 21020 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.43]
2021-02-22 10:50:33.418  INFO 21020 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-02-22 10:50:33.418  INFO 21020 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1051 ms
2021-02-22 10:50:33.605  INFO 21020 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-02-22 10:50:33.764  WARN 21020 --- [           main] o.s.b.a.m.MustacheAutoConfiguration      : Cannot find template location: classpath:/templates/ (please add some templates, check your Mustache configuration, or set spring.mustache.check-template-location=false)
2021-02-22 10:50:33.840  INFO 21020 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-02-22 10:50:33.851  INFO 21020 --- [           main] com.quietjun.intellij.Application        : Started Application in 1.865 seconds (JVM running for 2.204)

 


HelloRestController.java 작성

rest 서비스의 동작을 테스트 하기 위해 HelloRestController를 추가해보자.

package com.quietjun.intellij.controller;

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

@RestController
public class HelloRestController {

    @GetMapping("/hello")
    public String hello(){
        return "Hello Spring";
    }
}

 작성 후 hello를 요청하면 Hello Spring이 잘 반환됨을 알 수 있다.

 


HelloController.java 작성

다음으로 일반 web page의 동작을 살펴보기 위해 HelloController를 작성해보자.

package com.quietjun.intellij.controller;

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

@Controller
public class HelloController {
    @GetMapping("/hi")
    public String sayHi(Model model){
        model.addAttribute("message", "Hello");
        return "hitemplate";
    }
}

 위 코드는 /hi로 요청이 들어왔을 때 hitemplate라는 이름으로 등록된 template을 이용한다. 이때 message를 key로 Hello를 전달하고 있다.

우리는 template engine으로 mustache를 설정했기 때문에 이를 이용해서 웹 페이지를 작성해준다. 


웹 클라이언트를 위한 부분 작성

웹 클라이언트를 위한 부분은 크게 정적인 서비스를 하는 부분과 동적인 서비스를 하는 부분으로 나뉜다. 정적인 리소스(html, css, js)를 위해서 resources 아래에 static 폴더를 만들고 common.css를 등록해보자. templates에는 mustache로 작성된 hitemplate.mustache를 작성해보자.

.title{
    color: blue;
}

 

<!doctype html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link href="/common.css" rel="stylesheet">
</head>
<body>
    <h1 class="title">Hello SpringBoot</h1>
    <p>msssage: {{message}}</p>
</body>
</html>

 

이제 /hi를 요청하면 아래 처럼 title이 css 설정대로 blue로 표시되고 message에 Hello가 잘 할당된 것을 확인할 수 있다.

 

테스트 코드 작성하기

마지막으로 Spock을 이용해서 우리가 만든 기능들을 테스트 해보자. 사실 브라우저를 통해서 페이지를 요청해보기 전에 하는것이 좋지만 아직 익숙하지 않은 상태라고 가정하고 테스트를 나중에 실행해보았다.

실행 설정

먼저 테스트를 정상적으로 실행하기 위해서는 Gradle의 test 실행을 IntelliJ IDEA로 변경해야 한다. 이를 위해 Settings > Build, Execution, Develoyment > Build Tools > Gradle에서 Run tests using 부분을 IntelliJ IDEA로 변경한다.


빈은 잘 생성되었나?

앞서 생성한 HelloController나 HelloRestController가 잘 생성되었는지 테스트해보자. 

src>test에서 New 선택 후 Spock Specification을 선택하고 클래스 이름으로 BeanTest라고 입력하자.

이때 package 이름은 @SpringBootApplication이 있는 하위에 구성되어야 함을 명심하자.

package com.quietjun.intellij


import com.quietjun.intellij.controller.HelloController
import com.quietjun.intellij.controller.HelloRestController
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import spock.lang.Specification

@SpringBootTest
class BeanTest extends Specification {

    @Autowired
    HelloController hello;

    @Autowired
    HelloRestController helloRest;

    def "빈들은 잘 생성되었는가?"() {
        expect:
        hello !=null
        helloRest!=null
    }
}

 

자바인듯 자바 아닌 Groovy 기반의 Spock이 자바와 어울려서 잘 동작하는 것을 확인할 수 있다.

package com.quietjun.intellij

import com.quietjun.intellij.controller.HelloController
import com.quietjun.intellij.controller.HelloRestController
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.context.SpringBootTest
import spock.lang.Specification

@SpringBootTest
class BeanTest extends Specification {

    @Autowired
    HelloController hello;

    @Autowired
    HelloRestController helloRest;

    def "빈들은 잘 생성되었는가?"() {
        expect:
        hello !=null
        helloRest!=null
    }
}

위의 예에서 처럼 웹과 무관한 동작을 테스트 할 때는 @SpringBootTest로 충분하다.


웹페이지와 REST는 잘 동작하는가?

웹과 관련된 동작을 처리할 때는 MockMvc를 이용한다. 이를 위해 @SpringBootTest 대신 @AutoConfigureMockMvc와 @WebMvcTest를 이용한다.

package com.quietjun.intellij

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import spock.lang.Specification

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@AutoConfigureMockMvc
@WebMvcTest
class ControllerTest extends Specification {
    @Autowired
    MockMvc mvc;

    def "/hello라고 요청했을 때 200 상태를 수신 받고 이때 content는 Hello Spring이어야 한다."(){
        expect:
        mvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andReturn()
                .response
                .contentAsString=="Hello Spring"
    }

    def "/h1라고 요청하면 200 상태를 수신 받고 request 영역에 message를 키로 Hello가 있어야 하며 리턴 템플릿은 hitemplate"(){
        expect:
        mvc.perform(get("/hi"))
        .andExpect(status().isOk())
        .andExpect(model().attribute("message", "Hello"))
        .andExpect(MockMvcResultMatchers.view().name("hitemplate"))
    }
}

 

위 3개의 단위 테스트가 잘 동작했다면 intellij community edition + SpringBoot + Gradle + Spock가 멋지게 연동된 것이라 볼 수 있다.

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.