'spring'에 해당되는 글 7건

  1. Spring, RestTemplate으로 REST 클라이언트 구현하기
  2. Spring Boot, @Async 비동기 실행 로직 구현하기
  3. Spring Boot, JUnit을 이용한 유닛 테스트 구현하기
  4. Spring MVC Controller에서 ApplicationContext에 접근하기
  5. [Spring] JavaMail API로 메일 발송 기능 구현하기
  6. [Spring] Spring 3에서 작업 스케줄러(Task Scheduler) 메써드 구현하기 (2)
  7. [Spring] Spring MVC 개발환경 구성: STS 설치

Spring, RestTemplate으로 REST 클라이언트 구현하기

개요

웹의 시대가 열린지는 오래되었지만 REST API가 본격적으로 유행한지는 얼마 되지 않았다. 안타깝게도 국내는 비즈니스 로직 구현에만 초점을 맞추어 GET, POST 메써드의 구분 없이 API 요청을 허용한다거나(심지어 POST 요청에 쿼리 스트링을 담기도 한다.) 상태 코드를 사용하지 않고 무조건 200 응답 후 바디에 독자적인 코드를 재정의하는 등 HTTP 스펙의 권고사항을 무시한 API 개발이 너무나도 흔한 상황이다. 그럼에도 REST는 가독성, 유지보수성 등을 고려했을 때 꼭 필요한 설계 철학이다.(세계 최대의 커뮤니티 reddit 또한 REST API를 제공한다.) 이번 글에서는 이러한 REST API를 이용(소비)하는 클라이언트로서 RestTemplate의 사용 예를 설명하고자 한다.

라이브러리 종속성 추가

dependencies {
    compile group: 'org.springframework', name: 'spring-web', version: '4.2.6.RELEASE'
    compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.7.4'
    compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.4'
    compile group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.7.4'
    compile group: 'org.projectlombok', name: 'lombok', version: '1.16.8'
}
  • RestTemplate를 사용하기 위해 spring-web 아티팩트를 추가하였다.

  • RestTemplatePOJO-JSON 상호 변환시 기본값으로 Jackson 라이브러리를 사용한다. Jackson이 정상적으로 동작하려면 jackson-core, jackson-databind, jackson-annotations 3개 아티팩트를 추가해야 한다.

  • 만약 Jackson을 추가하지 않고 Gson을 추가하면 RestTemplateGson을 대신 사용한다. 어떤 HttpMessageConverter 구현 클래스를 등록하느냐의 차이로 MappingJackson2HttpMessageConverter, GsonHttpMessageConverter가 이 역할을 수행하며 RestTemplate 오브젝트 생성시 생성자로 명시적인 전달이 가능하다.

  • 만약 Spring Boot 기반의 프로젝트로 spring-boot-starter-web 아티팩트를 추가한 상태라면 위에 열거한 아티팩트를 추가할 필요가 없다. 모두 포함되어 있기 때문이다.(Gson은 따로 추가해야 한다.)

  • lombok 아티팩트 추가는 필수가 아닌 선택이다. LombokPOJO 작성시 반복적이고 지루한 RequiredArgsConstructor, Getter, Setter, ToString 메써드 작성을 자동화 해준다.(적지 않은 수의 국내 개발자들 이 작업이 귀찮아 POJO 대신 Map을 사용하는데 이는 Java가 가진 많은 장점을 포기하는 것이다.)

HTML GET 요청

단순한 HTML 응답을 받는 예이다.

HttpHeaders header = new HttpHeaders();
header.add(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE);
ResponseEntity<String> response = new RestTemplate().exchange("http://www.naver.com/", HttpMethod.GET, new HttpEntity(header), String.class);

JSON GET 요청

에코 응답을 제공하는 JSON Test API를 사용하여 예를 작성해보겠다. JSON GET 요청에 앞서 응답을 받을 POJO 클래스를 구현한다. 아래 HeadersResponse 클래스는 JSON 응답을 담는 역할을 한다.

@Data
public class HeadersResponse {

    @JsonProperty("Accept-Language")
    private String acceptLanguage;

    @JsonProperty("Host")
    private String host;

    @JsonProperty("User-Agent")
    private String userAgent;

    @JsonProperty("Accept")
    private String accept;
}
  • 앞서 언급한 Lombok을 이용하여 POJO 구현시 반복적인 메써드의 작성을 제거했다. 클래스 레벨에 @Data를 명시하면 모든 것을 컴파일 시점에 대신 생성해준다.
  • @JsonPropertyPOJO-JSON 상호변환시 정확한 멤버 변수에 맵핑해주는 역할을 한다. Jackson이 제공하는 기능으로 Gson을 사용한다면 Gson이 제공하는 기능을 사용해야 한다.

작성된 POJO을 이용한 요청 예이다.

HttpHeaders header = new HttpHeaders();
header.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_UTF8_VALUE);
ResponseEntity<HeadersResponse> response = new RestTemplate().exchange("http://headers.jsontest.com/", HttpMethod.GET, new HttpEntity(header), HeadersResponse.class);

참고 글

저작자 표시 비영리 동일 조건 변경 허락
신고

Spring Boot, @Async 비동기 실행 로직 구현하기

먼저 읽어볼만한 글

@SpringBootApplication 클래스 작성

package com.jsonobject.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@SpringBootApplication
@EnableAutoConfiguration
@EnableAsync
public class Application {

    public static void main(String[] args) {

        ApplicationContext ctx = SpringApplication.run(Application.class, args);
    }

    @Bean
    public TaskExecutor taskExecutor() {

        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(10);
        taskExecutor.setMaxPoolSize(20);
        taskExecutor.setQueueCapacity(50);

        return taskExecutor;
    }
}
  • @EnableAsync를 명시함으로서 비동기로 작동할 @Async가 명시된 빈을 인식하도록 한다.

@Service 클래스 작성

package com.jsonobject.example;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncService {

    @Async
    public void doAsync() {
        // 비동기로 실행될 로직을 작성
    }
}
  • Spring Boot 기반의 웹 애플리케이션은 HTTP 요청이 들어왔을 때 내장된 서블릿 컨테이너(Tomcat)가 관리하는 독립적인 1개의 Worker 쓰레드에 의해 동기 방식으로 실행된다. 하지만 요청 처리 중 @Async가 명시된 메써드를 호출하면 앞서 등록한 ThreadPoolTaskExecutor 빈에 의해 관리되는 또 다른 독립적인 Worker 쓰레드로 실행된다. 별도의 쓰레드로 동작하기에 원래의 요청 쓰레드는 그대로 다음 문장을 실행하여 HTTP 응답을 마칠 수 있다.

  • @Async가 명시된 메써드는 반드시 public으로 선언되어야 한다. 또한 같은 클래스 내에서 해당 메써드를 호출할 경우 비동기로 작동하지 않는다.

참고 글

저작자 표시 비영리 동일 조건 변경 허락
신고

Spring Boot, JUnit을 이용한 유닛 테스트 구현하기

먼저 읽어볼만한 글

라이브러리 종속성 추가

/build.gradle 파일에 아래 내용을 추가한다.

dependencies {
    testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test'
}
  • spring-boot-starter-test Starter POM의 추가 만으로 Spring Test, JUnit, Hamcrest, Mockito를 모두 사용하여 테스트 클래스를 작성할 수 있다.

테스트 클래스 작성

/src/test/java/com.jsonobject.example/ExampleTest.java 파일을 아래와 같이 작성한다.

import com.jsonobject.example.Application;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebIntegrationTest
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@ActiveProfiles(profiles = "test")
public class ExampleTest {

    @Autowired
    @Qualifier("anotherDAO")
    private SomeDAO someDAO;

    @Resource(name = "anotherService")
    private SomeService someService;

    @Autowired
    private SomeController someController;

    @Test
    public void aTest() {
        // 테스트 코드 작성
    }

    @Test
    public void bTest() {
        // 테스트 코드 작성
    }
}
  • 클래스에 @SpringApplicationConfiguration를 명시하면 ApplicationContext를 선택하여 테스트를 할 수 있다. 이에 따라 클래스 멤버에 애플리케이션 컨텍스트의 빈을 주입하여 테스트에 사용할 수 있다. @Autowired, @Qualifier, @Resource 모두 사용 가능하다.

  • 클래스에 @WebIntegrationTest를 명시하면 실제 웹 애플리케이션이 구동되는 환경과 동일하게 HTTP 요청 테스트가 가능하다. TestRestTemplate 클래스를 사용한다.

  • 클래스에 @ActiveProfiles를 명시하면 테스트 실행시 적용될 프로파일을 적용할 수 있다.

  • 메써드에 @Test를 명시하면 테스트 메써드로 선언되어 메써드 단위 테스트가 가능해진다. 클래스 단위 테스트시 실행될 대상 메써드가 된다.

  • JUnit에서는 클래스 단위 테스트 실행시 메써드 간의 테스트 순서가 임의로 진행된다. 클래스에 @FixMethodOrder를 명시하면 테스트 메써드 간의 실행 순서를 결정할 수 있다. MethodSorters.NAME_ASCENDING은 메써드의 이름 순으로 실행 순서를 결정하겠다는 의미이다.

참고 글

저작자 표시 비영리 동일 조건 변경 허락
신고

Spring MVC Controller에서 ApplicationContext에 접근하기

Spring 프레임워크에 있어서 ApplicationContext 오브젝트는 Bean의 초기화와 Life Cycle을 관리하는 중요한 오브젝트이다. Spring MVC 기반의 웹 애플리케이션을 개발하다보면 ApplicationContext에 직접 접근할 일이 거의 없지만 Bean 오브젝트를 얻기 위해 필요할 때가 있다.

ApllicationContext 오브젝트를 획득하는 방법은?


방법은 간단하다. Controller 클래스를 예로 들면 BeanFactoryAware 인터페이스를 구현하고 아래와 같이 setBeanFactory() 메써드를 오버라이드하여 작성한다. 현재 활성화된 특정 Bean 오브젝트를 획득할 수 있다.


@Controller
public class SomeController implements BeanFactoryAware {
  private SomeBean someBean;

  @Override
  public void setBeanFactory(BeanFactory context) {
    someBean = (SomeBean) context.getBean("someBean");
  }
}

참고 글



저작자 표시 비영리 동일 조건 변경 허락
신고

[Spring] JavaMail API로 메일 발송 기능 구현하기

* Spring 3 환경에서 메일을 발송하는 방법을 조사해봤다. 간단하게 Oracle에서 제공하는 JavaMail API를 사용하면 된다. 현재 최신 버전은 1.4.7이며 maven javamail로 구글링하면 mail-1.4.7.jar 파일을 다운로드받을 수 있다.


* 다운로드받은 mail-1.4.7.jar 파일을 프로젝트의 CLASSPATH에 추가한다. 웹 애플리케이션의 경우 /WebContent/WEB-INF/lib 디렉토리에 복사하면 간단하게 끝난다.


* 메일 발송 정보를 저장할 mail.xml 파일을 작성한다.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bean="http://www.springframework.org/schema/bean"

xsi:schemaLocation="

        http://www.springframework.org/schema/beans 

        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

<bean id="mailSender"

class="org.springframework.mail.javamail.JavaMailSenderImpl">

<property name="host" value="smtp.somemailhost.com" />

<property name="port" value="25" />

<property name="username" value="someuser" />

<property name="password" value="somepassword" />

<prop key="mail.transport.protocol">smtp</prop>

<prop key="mail.smtp.auth">true</prop>

<prop key="mail.smtp.starttls.enable">true</prop>

<prop key="mail.debug">true</prop>

</property>

</bean>

</beans>


* DispatcherServlet 역할을 하는 XML 파일에 아래 내용을 추가한다.

<import resource="mail.xml" />


* 애플리케이션의 @Service 오브젝트에서 사용될 MailService 클래스를 작성한다. 아래는 간단한 예로 첨부 파일 발송, HTML 템플릿 등을 적용하려면 살을 더 붙여야 한다.

@Service("mailService")

public class MailService {

  @Autowired

  private MailSender mailSender;

  

  public void sendMail(String from, String to, String subject, String text) {

    SimpleMailMessage message = new SimpleMailMessage();

    message.setFrom(from);

    message.setTo(to);

    messate.setSubject(subject);

    message.setText(text);

    mailSender.send(message);

  }  

}


* 메일 발송 비즈니스 로직을 수행할 @Service 클래스에서는 아래와 같이 작성한다.

@Service("someService")

public class someService {

  @Autowired

  private MailService mailService;

  

  public void sendMail() {

    mailService.sendMail("from@MailAddress.com", "to@MailAddress.com", "someSubject", "someText");

  }

}


<참고자료>

Send Mail with Spring : JavaMailSenderImpl Example (by Lokesh Gupta)

http://howtodoinjava.com/2013/05/27/send-email-with-spring-javamailsenderimpl-example/


Spring Framework 3.2 Reference Documentation: 26. Email

http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/mail.html


저작자 표시 비영리 동일 조건 변경 허락
신고

[Spring] Spring 3에서 작업 스케줄러(Task Scheduler) 메써드 구현하기

* 일반적으로 전산실 환경에서 주기적인 디비 백업이나 메일 발송 기능은 유닉스 또는 리눅스 상에서의 cron을 이용하여 쉘 스크립트를 실행하는 형태로 구현한다. 현재 Spring Web MVC 3를 기반으로 개발 중인 사내 웹 애플리케이션에도 이런 형태의 스케줄 작업을 구현할 필요가 생겼는데 웹 애플리케이션 내부에서 구현해보자는 생각에 Spring 3 환경에서 Task Scheduler 메써드를 구현하는 방법을 조사해봤다.


* Spring 3에서는 어노테이션 기반으로 cron만큼 편리하고 직관적인 작업 스케줄러 기능을 지원한다.


* 적용할 Spring Web MVC 3 프로젝트가 XML 기반의 ApplicationContext로 구성되었다는 가정 하에 아래와 같이 task.xml 파일을 작성한다.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" 

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 

xmlns:task="http://www.springframework.org/schema/task" 

xsi:schemaLocation="

        http://www.springframework.org/schema/beans 

        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 

        http://www.springframework.org/schema/task 

        http://www.springframework.org/schema/task/spring-task-3.1.xsd">

<task:annotation-driven executor="taskExecutor" scheduler="taskScheduler" />

<task:executor id="taskExecutor" pool-size="1" />

<task:scheduler id="taskScheduler" />

</beans>


* DispatcherServlet 역할을 하는 XML 파일에 아래 내용을 추가한다.

<import resource="task.xml" />


* 작업 스케줄러 메써드를 구현하기 위한 모든 준비가 끝났다. 이제 구현할 메써드를 @Service 클래스에 작성하면 된다. 방법은 간단하다. 특정 주기로 실행될 메써드 앞에 @Scheduled 어노테이션을 작성하면 된다.

@Scheduled(cron = "* * 1 * * ?") // 매일 오전 1시에 실행한다.

public void doSomething() {

  // doSomething

}


* 아래와 같이 다양한 주기 설정이 가능하다.

@Scheduled(fixedDelay = 3600000) // 1000 * 60 * 60, 즉 1시간마다 실행한다.


<참고자료>

Scheduled Jobs in Spring 3 (by Matt Young)

http://www.vodori.com/blog/spring3scheduler.html


Spring Framework 3.2 Reference Documentation: 27. Task Execution and Scheduling

http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/scheduling.html


저작자 표시 비영리 동일 조건 변경 허락
신고

[Spring] Spring MVC 개발환경 구성: STS 설치

* Spring 기반의 프로젝트를 진행한다면 Eclipse보다는 Spring에 특화된 다양한 기능을 지원하는 Spring Tool Suite(이하 STS)를 IDE로 사용하는 것이 정신건강에 좋다.


<Spring Framework 다운로드>

- SPRING FRAMEWORK (spring framework로 구글링 후 접속) > DOWNLOAD > take me to the download page >
spring-framework-3.2.1.RELEASE-dist.zip (다운로드)


<STS 다운로드>

- SPRING TOOL SUITE (sts로 구글링 후 접속) > DOWNLOAD > take me to the download page > spring-tool-suite-3.1.0.RELEASE-e3.8-win32-x86_64-installer.exe (다운로드 후 실행) >

- Select the installation path: C:\Java\STS (STS 설치경로 지정) > Select the JDK path: C:\Java\jdk1.7.0_03 (JDK 설치경로 지정) >

- (STS 실행) > Workspace: C:\Workspace (프로젝트 경로 지정)


<컬러 테마 설정>

- Help > Eclipse Marketplace > Find: Eclipse Color Theme > Install >

- Window > Preferences > General > Appearance > Color Theme > Color Theme: Obsidian > Apply >

- General > Appearance > Colors and Fonts > Basic > Text Font > Edit > Consolas/보통/12 > 확인 > OK


* 영문 글꼴에서 소스 코드로 가장 가독성이 좋은 글꼴은 Consolas가 유명하다. 하지만 한글 글꼴이 제공되지 않으므로 맑은 고딕이 추가된 트윅된 글꼴을 추천한다. 아래 블로그에서 다운로드 가능하다.

http://warmz.tistory.com/648


<Spring MVC 프로젝트 생성>

- File > New > Dynamic Web Project > Project name: (프로젝트명 입력) > New Runtime >

- Apache > Apache Tomcat v7.0 > Create a new local server (체크) >

- Download and Install > Tomcat installation directory: C:\Java\apache-tomcat-7.0.12 > JRE: jdk1.7.0_03

- 다운로드한 Spring Framework 압축해제 후 \libs\*.RELEASE.jar 파일들을 프로젝트의 /WebContent/WEB-INF/lib 디렉토리에 복사


* Spring Framework는 몇가지 라이브러리에 종속성을 가지는데 대표적으로 Apache Commons Logging 라이브러리가 있다. maven commons logging으로 구글링하여 라이브러리를 다운로드하고 앞과 같은 방법으로 복사한다.


<web.xml 작성>

- 프로젝트의 /WebContent/WEB-INF/web.xml 파일을 아래와 같이 작성한다.

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

<display-name>Spring MVC Sample Application</display-name>

<servlet>

<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextClass</param-name>

<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>

</init-param>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>com.leeth.mvc.config.SpringConfig</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>

</web-app>


* XML 파일이 아닌 Java 클래스로 WebApplicationContextConfig 파일을 작성할 수 있도록 했다. Spring Framework v3.1부터 추가된 기능이다.


<Config 클래스 작성>
package com.leeth.mvc.config;

import org.springframework.context.annotation.Bean;
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.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan("com.leeth.mvc.*")
public class SpringConfig {
@Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}

<Controller 클래스 작성>
package com.leeth.mvc.controller;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {
private static final Log log = LogFactory.getLog(HomeController.class);

@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Model model) {
log.info("home");
return "home";
}
}

<.WAR 임포트>
* .WAR로 익스포트한 프로젝트 파일을 다시 STS에서 임포트하는 방법은 아래와 같다.
- File > Import > Web > WAR file >
- WAR file: (임포트할 .WAR 파일 지정) > Web project: (프로젝트명 입력) > Target runtime: Apache Tomcat v7.0 >
- (임포트 후 프로젝트명 우클릭) > Properties > Resource > Text file encoding > Other: UTF-8 >
- JavaScript > Include Path > Source > Edit > Exclusion patterns > Add > **/*.js

저작자 표시 비영리 동일 조건 변경 허락
신고