issue1 ~ issue21

1. 통합테스트 애노테이션 정리

Spring

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class) 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/app-config.xml")

@ContextConfiguration은 통합 테스트에서 클래스 레벨 메타데이터(xml 파일 or javaConfig 파일)를 정의한다. 다시 말해, context를 로드하는데 사용되는 annotated class(@Configuration 클래스)나 application context resource locations(classpath에 위치한 XML 설정 파일)들을 선언한다.

또한 @ContextConfiguration은 ContextLoader 전략을 사용할 수 있다. 하지만 일반적으로 로더를 직접 명시할 필요는 없다. default loader가 initializers 뿐만 아니라 resource locations 또는 annotated classes를 지원하기 때문이다.

문제 발생

Spring Boot에서 @ContextConfiguration(classes = Application.class)만 설정했더니

[main] DEBUG org.springframework.core.type.classreading.AnnotationAttributesReadingVisitor -
Failed to class-load type while reading annotation metadata. 
This is a non-fatal error, but certain annotation metadata may be unavailable.

java.lang.ClassNotFoundException:
org.springframework.data.web.config.EnableSpringDataWebSupport
...
java.lang.ClassNotFoundException:
org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
...

위와 같은 에러뿐만 아니라 기본적인 Autowired 설정도 안되고, BeanCreationException도 발생했다. 원인은 애노테이션이 classpath에 없기 때문에 클래스가 로드될 때 JVM이 drop 시켜서 발생한 문제였다.

해결 방법1

Spring Boot에서는 기존의 @ContextConfiguration 대신 @SpringApplicationConfiguration을 제공한다. ApplicationContext 설정을 @SpringApplicationConfiguration으로 사용하면 SpringApplication 으로 생성되고 추가적인 Spring Boot feature들을 얻을 수 있다.

해결 방법2

그런데 1.4부터 SpringApplicationContextLoader가 Deprecated 되었다.

해결 방법3

ConfigFileApplicationContextInitializer는 Spring Boot application.properties파일을 로드해 테스트 코드에 적용한다. @SpringApplicationConfiguration가 제공하는 full feature들이 필요 없을 때 사용된다.

cf) spring boot 1.4

직접적인 Configuration 설정 없이도 @*Test 애노테이션이 자동으로 primary configuration을 찾는다(테스트가 포함된 패키지로부터 @SpringBootApplication또는 @SpringBootConfiguration 애노테이션 클래스를 찾는다).

Spring Boot

cf) 방법4에서 @SpringBootTest에서 classes 속성을 생략하면 inner-classes에서 @Configuration을 제일 먼저 로드하려 시도하고, 없으면 @SpringBootApplication class를 찾는다.

@WebApplicationContext

WebApplicationContext을 생성할 수 있게 해준다.

2. dataSource

메이븐 pom.xml에서 에서 spring-boot-starter-data-jpa 를 추가하면 그안에 spring-boot-starter-jdbc가 있고 그 안에 tomcat-jdbc가 있다. Spring Boot에서는 DataSource 관리를 위한 구현체로써 tomcat-jdbc(The Tomcat JDBC Pool) 을 default로 제공한다.

근데 실서버에 배포 했을 때 에러가 반복해서 발생했고 그 주기도 일정치 않아서 재연이 쉽지 않았다. validationQuery: select 1을 설정했음에도 connection이 자꾸 닫히는 문제가 발생했다.

그래서 해결책으로 hikari cp를 사용했다.

그러나 설정파일에 hikari 의존이 생기는 건 좋지 않다.

위와 같이 tomcat에서 제공하는 기본 dataSource를 지우면 boot에서 자동으로 hikari cp로 설정된다. 따라서 JpaConfig 파일도 필요 없게 되고 yml에서 hikari를 지워도 된다.

2016.08.12 추가

Spring Boot 1.4부터는 db 프로파일에 spring.datasource.hikari를 명시적으로 작성할 수 있게 됐다.

3. request url에 문자열 ".t"을 확장자로 인식

위와 같이 정규식으로 모든 문자열을 (. 포함) 받겠다고 명시했음에도 .t는 인식이 안되는 문제가 발생했다.

이유는 .t라는 확장자가 있기 때문에 아래와 같이 설정에서 mediaType을 json으로 직접 명시해주면 해결이 가능하다.

4. Spring MVC Test

Spring Framework 4.2.6에서 Spring MVC Test를 처음 생성하다 다음과 같은 에러가 발생했다.

문제는 org.springframework.mock.web에 있는 mock set들은 Servlet 3.0 API를 기반으로 동작하는데 현재 프로젝트 Servlet 버전은 2.5였다.

The Spring 4.0.1 reference documentation is now more clear about the Mocks: Servlet 3.0+ is strongly recommended and a prerequisite in Spring's test and mock packages for test setups in development environments.

5. 날짜

6. Mockito

verify

never() : 어떤 조건에 따라 호출되면 안되는 경우, 진짜 호출이 안되는지 확인.

<T> T any(Class<T> clazz): anyObject와는 다르게 클래스를 지정할 수 있다.

spy 해당 서비스안에 있는 일반 메서드를 when().thenReturn()을 통해 제어하고 싶을 때 서비스를 spy로 만들어서 할 수 있다.

ArgumentCaptor 참고 : http://stackoverflow.com/questions/12295891/how-to-use-argumentcaptor-for-stubbing

stub을 만들지 않고, 메서드가 호출됐는지 확인하는 것과 인자가 정확한지 확인하고 싶을 때 ArgumentCaptor방식을 사용할 수 있다.

7. YAML

기존 spring 프로젝트에서 yml 설정을 하기 위해서 먼저 의존성을 추가해준다.

그리고 서블릿 컨텍스트에 yaml 설정을 추가해준다.

cf) spring boot에서는 classpath 에 application.yml 을 추가 하면 자동으로 boot가 스캔하기 때문에 따로 설정이 필요없다.

application.yml 작성 예

8. 기존 xml 설정을 JavaConfig로 변경하기

먼저 web.xml에서 contextConfigLocation을 xml 파일 위치 대신 AppConfig 위치로 변경해준다.

그리고 ContextLoaderListener가 자동으로 생성하는 컨텍스트의 클래스는 기본적으로 XmlWebApplicationContext다. 이를 다른 애플리케이션 컨텍스트 구현 클래스로 변경하고 싶으면 contextClass 파라미터를 이용해 지정해주면 된다.

두번째로 AppConfig 파일에 기존 xml에 있던 설정들을 옮긴다.

9. Mybatis db 언더바 사용된 컬럼과 자바 카멜케이스 변수 자동 매핑

방법1 : application.yml

방법2 : mybatis-config.xml

cf) 다른 설정정보 참고 : http://www.mybatis.org/mybatis-3/ko/configuration.html

10. @RequestParam값이 Optional일때 DefaultValue 적용

int형이기 때문에 값이 없을때 null일 수 없다. 그래서 defaultValue를 미리 지정해주는게 좋다.

11. 세션 스코프 빈 사용

컨트롤러에서 세션 스코프 빈을 사용할 일이 있었고, 컨트롤러는 싱글톤 빈이기 때문에 일반적으로 DI방식을 이용해 주입해서는 방법이 없다. DL 방식을 이용하는 방법도 있지만 애플리케이션 로직에 스프링 코드가 들어간다는 단점이 있다.

그래서 프록시 DI 방식을 택했다.

@Scope 애노테이션으로 스코프를 지정했다면 proxyMode 엘리먼트를 이용해서 프록시를 이용한 DI가 되도록 지정할 수 있다. 클라이언트(여기선 컨트롤러 클래스)는 스코프 프록시 오브젝트를 실제 스코프 빈처럼 사용하면 프록시에서 현재 스코프에 맞는 실제 빈 오브젝트로 작업을 위임해준다.

스코프 프록시는 각 요청에 연결된 HTTP 세션정보를 참고해서 사용자마다 다른 Work 오브젝트를 사용하게 해준다. 클라이언트인 컨트롤러 입장에서는 모두 같은 오브젝트를 사용하는 것처럼 보이지만, 실제로는 그 뒤에 사용자별로 만들어진 여러 개의 Work가 존재하고, 스코프 프록시는 실제 Work 오브젝트로 클라이언트의 호출을 위임해주는 역할을 해줄 뿐이다.

프록시 빈이 인터페이스를 구현하고 있고, 클라이언트에서 인터페이스로 DI 받는다면 proxyMode를 ScopedProxyMode.INTERFACES로 지정해주고, 프록시 빈 클래스를 직접 DI 한다면 ScopedProxyMode.TARGET_CLASS로 지정하면 된다(여기서는 Work 클래스로 직접 DI 할 것이므로 ScopedProxyMode.TARGET_CLASS).

cf) XML 설정방식

DI 받을 때 클래스를 이용한다면 proxy-target-class를 true로 설정하고, 인터페이스를 이용한다면 false로 하거나 아예 생략하면 된다.

12. Spring Boot war 배포로 변경하기

  1. pom.xml

  2. configuration

13. Spring Boot CORS 처리

대부분의 웹 브라우저는 보안상의 이유로 다른 도메인의 URL을 호출해서 데이터를 가져오는 것을 금지하고 있다. 우리 웹 서비스에서만 사용하기 위해 다른 서브 도메인을 가진 API 서버를 구축했는데, 다른 웹 서비스에서 마음대로 접근해서 사용하면 문제가 되기 때문이다.

그런데 하나의 도메인을 가진 웹 서버에서 모든 처리를 하기에는 효율성이나 성능 등 여러 문제로 각 기능별로 여러 서버를 두는 경우가 많다(API 서버, WAS 서버, 파일 서버 등등). 물리적으로 분리된 서버이고, 다른 용도로 구축된 서버이니 당연히 각각 다른 도메인을 가진 서버들일 텐데, 서로간에 Ajax 통신을 할 수 없는 것일까? 즉 서로 다른 도메인 간의 호출을 의미하는 크로스 도메인 문제를 해결할 수는 없는 것일까?

CORS(Cross Origin Resource Sharing)은 외부 도메인에서의 요청(접근)을 허용해주는 메커니즘이다.

원문 : http://ooz.co.kr/232

위와 같이 필터 클래스를 만들고

빈으로 등록만 해주면 url 패턴에 따라 필터가 작동된다.

CORS support in Spring Framework

http://spring.io/blog/2015/06/08/cors-support-in-spring-framework

@CrossOrigin 애노테이션을 추가해줌으로써 CORS를 가능하게 해준다.

컨트롤러 전체에 적용도 가능하다.

Java Config

XML namespace

14. @JsonInclude @Pattern

@JsonInclude(JsonInclude.Include.NON_NULL) 객체를 Json으로 변환할 때, null값 필드는 아예 Json으로 매핑 안되게 해주는 설정이다.

15. Swagger를 조회하는 URL 바꾸고 싶을 때

16. WebApplicationInitializer를 이용한 컨텍스트 등록

서블릿 3.0 이상부터 사용 가능하다. web.xml 파일만을 단독으로 사용하던 것에서 탈피해, 설정 방식을 모듈화해서 관리하는 방법을 도입했다.

17. maven-clean-plugin

mvn clean을 수행하면 기본적으로 target 디렉토리 밑에 있는 내용을 삭제한다. 그런데 target 디렉토리 외에 다른 디렉토리도 지우고 싶다면, pom.xml에 직접 설정할 수도 있다.

18. maven profiles

19. HandlerMethodArgumentResolver를 이용한 사용자 검증

HandlerMethodArgumentResolver 인터페이스를 구현하여 컨트롤러의 메서드 파라미터를 검증, 수정 할 수 있다.

쿠키로 인증 구현 예

설정은 다음과 같이 한다.

20. @JsonView

출처 url : http://www.concretepage.com/spring-4/spring-mvc-4-rest-jackson-jsonview-annotation-integration-example

결과 /app/publicprofile

결과 app/friendprofile

결과 app/familyprofile

21. Spring Boot Embedded Tomcat AJP 연동

Spring Boot의 Embeded WAS로 Tomcat을 쓸 경우 아래와 같은 설정으로 AJP 연동을 할 수 있다.

참고로 AJP를 쓸 경우 HTTP 메서드 등 PATCH를 쓸 수 없다는 제약이 있다.

Last updated

Was this helpful?