'Spring Framework'에 해당하는 글 5건

Spring MVC 에서는 기본 error 페이지를 제공하지 않아 SimpleMappingExceptionResolver 를 사용해야 하지만, Spring Boot 는 기본적으로 BasicErrorController 를 설정하여 모든 error 를 처리하는 /error 매핑을 제공하며, 서블릿 컨테이너에 전역 error 페이지로 등록된다. 예로, Thymeleaf 를 사용할 경우 /error 와 매핑되는 페이지는 src/main/resources/templates/error.html 가 될 것이다.


시스템 클라이언트의 경우, HTTP 상태, error, 메시지를 담은 JSON 형식으로 응답한다. 브라우저 클라이언트의 경우, 위와 동일한 데이터를 HTML 형식으로 렌더링한 /error 페이지로 응답할 것이며, 존재하지 않을 경우 Spring boot 에서 제공하는 "whitelabel" error 페이지로 응답한다. whitelabel 에는 exception 정보가 명시되므로 다른 error 페이지로 대체하는 것이 좋다. (properties 파일에서 설정이 가능하다. server.error.whitelabel.enabled=false)


error 를 처리할 뷰를 추가하여 커스터마이징 할 수 있는데, @ExceptionHandler 와 @ControllerAdvice 같은 일반적인 Spring MVC 기능을 사용할 수 있다.

@ExceptionHandler 는 단일 컨트롤러 등에서, @ControllerAdvice 는 전역에서 exception 처리를 한다.



@ResponseStatus 과 HTTP 상태 코드 사용


일반적으로 웹 요청을 Spring boot 에서 처리할 때 정의되지 않은 exception 이 발생하면 서버는 HTTP 500 응답을 반환한다. @ResponseStatus 어노테이션을 메소드나 exception 클래스에 사용하면 지정한 상태 코드와 메시지를 반환할 수 있다. 아래는 404 exception 클래스를 만들고, 컨트롤러에 전달된 주문번호(id)가 존재하지 않으면 해당 exception 처리를 하는 예이다.


@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="No such Order")  // 404
public class OrderNotFoundException extends RuntimeException {
    // Process Exception
}
 
@RequestMapping(value="/orders/{id}", method=GET)
public String showOrder(@PathVariable("id"long id, Model model) {
    Order order = orderRepository.findOrderById(id);
 
    // Call Exception
    if (order == nullthrow new OrderNotFoundException(id);
 
    model.addAttribute(order);
    return "orderDetail";
}
cs


이렇게 반환되는 에러 정보는 콘솔이나 /error 에 매핑되는 페이지에서 확인할 수 있으며, 없다면 whitelabel 페이지에서 확인할 수 있다.

RESTful 요청의 경우 다음과 같이 error 정보를 json 형식의 응답으로 보여준다.


{
    "timestamp""2018-11-08T05:22:05.440+0000",
    "status"404,
    "error""Not Found",
    "message""No such page",
    "path""/orders"
}
cs



@ExceptionHandler 사용


@RequestMapping 어노테이션을 가진 메소드에서 exception 을 처리하기 위해 동일한 컨트롤러에 @ExceptionHandler 메소드를 추가하여 사용할 수 있다. @ExceptionHandler 을 사용하면, @ResponseStatus 어노테이션이 없는 exception 도 다룰 수 있고, 별로도 작성한 error 페이지로 사용자를 리디렉션 시킬 수 있다. @ExceptionHandler 는 HttpServletRequest, HttpSession, WebRequest, Locale, InputStream, OutputStream, Empty Model, ... 등의 여러가지 파라미터 type 을 가질 수 있다. 또한 ModelAndView, Model, Map, View, String, @ResponseBody, HttpEntiry<?>, ResponseEntity<?>, void, ... 등의 type 을 반환할 수 있다.


다음은 DataIntegrityViolationException 라는 미리 작성한 exception 클래스를 호출시켜 핸들러에서 처리하는 예제이다.


@Controller
public class ExceptionHandlingController {
    ...
    @RequestMapping("/dataIntegrityViolation")
    String throwDataIntegrityViolationException() throws SQLException {
        throw new DataIntegrityViolationException("Duplicate id");
    }
 
    @ResponseStatus(value = HttpStatus.CONFLICT, reason = "Data integrity violation"// 409
    @ExceptionHandler(DataIntegrityViolationException.class)
    public void conflict() {
        // Nothing to do
    }
}
cs



@ControllerAdvice 사용


@ControllerAdvice 를 사용하면 개별 컨트롤러뿐만 아니라 전체 어플리케이션에서 exception 처리를 할 수 있다.

모든 exception 의 기본 핸들러의 코드는 다음과 같다.


@ControllerAdvice
class GlobalDefaultExceptionHandler {
    public static final String DEFAULT_ERROR_VIEW = "error";
 
    @ExceptionHandler(value = Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        // exception 에 @ResponseStatus 를 가진 경우 프레임워크가 처리하도록 한다.
        if (AnnotationUtils.findAnnotation (e.getClass(), ResponseStatus.class!= null)
            throw e;
 
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", req.getRequestURL());
        mav.setViewName(DEFAULT_ERROR_VIEW);
        return mav;
    }
}
cs




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

개발자들은 어플리케이션의 중심부에 통합 로깅을 구성하여, 개발시 문제점 추적이나 필요한 부분을 모니터링 하려고 할 것이다.

System.out.println() 을 열심히 붙여 넣으면 로깅이 가능하다.

조금더 진보적인 방법으로는 Logging Framework 를 사용하는 방법이 있다.


Logging Framework 의 종류는 매우 많겠지만, 대부분 사용하는 것은 정해져 있는 듯 하다.

JUL, Log4j, Logback 정도...


Spring Framework 은 기본적으로 spring-core 모듈이 JCL(Jakarta Commons Logging) API 에 의존성을 가지고 있다.

commons-logging 의 역할은 지정된 클래스패스에서 다른 Logging Framework 를 찾아 동일한 API 로 로깅을 구현할 수 있게 한다.

예를 들면 Log4j 같은 Logging Framework 와 연동시켜 logging 을 가능하게 한다.

사용 가능한 Logging Framework 가 없다면 JDK 의 JUL(java.util.logging) 을 기본으로 사용한다.


문제는 commons-logging 이 slf4j 에 비해 비효율적이라는데 있다.

slf4j 는 commons-logging 처럼 다른 Logging Framework 를 찾는 작업을 런타임시 하지 않고, 컴파일 시점에 바인딩하기 때문에 더 효율적이다.

이러한 효율성이 얼마나 체감 가능한지는 모르겠지만 아무튼 그러한 이유로 slf4j 를 많이 사용한다고 한다.

Log4j 도 런타임 오버헤드와 코드 사이즈가 증가하는 단점으로 인해, slf4j API 를 구현하는 Logback 을 주로 사용한다.

JUL, JCL, Log4j 등을 호출하는 외부 라이브러리 이용시에는 slf4j 의 extend 라이브러리(jcl-over-slf4j 등) 를 사용하면 slf4j 로 통합된다.


정리하자면, commons-logging 이나 slf4j 같은 Logging API 를 사용하는 이유는,

JUL 이든 Log4j 든 Logback 이든 개발 도중 Logging Framework 가 바껴도 Logging 에 관련된 코드를 수정할 필요가 없도록 하기 위해서이다.

slf4j + logback 인 logging 을 위해 dependancy 에 추가할 것은 세가지 정도가 되겠다.


  1. slf4j Logging API
  2. logback Logging Framework
  3. Binding to slf4j : jul-over-slf4j / jcl-over-slf4j / log4j-over-slf4j / ... 


Gradle 을 예로 들면, 다음과 같은 형태가 되겠다.


dependencies {
    // logback-classic (slf4j-api, logback-core 포함)
    compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
    compile group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.25'
    compile group: 'org.slf4j', name: 'log4j-over-slf4j', version: '1.7.25'
}
cs


마지막으로 resources 디렉토리에 적당한 logback.xml 나 logback.groovy 파일을 생성/설정한다. ^^




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

Spring 의 어플리케이션에서 외부 라이브러리를 사용하려면 필요한 라이브러리(jar 파일) 를 다운받아 런타임시 그것들을 클래스 패스로 가져와야 한다.

Dependency 란 말이 어렵다면 필요한 외부 라이브러리라고 봐도 무방할 듯하다.

Dependency 관리는 외부 라이브러리가 있는 해당 리소스 위치에서 로컬에 라이브러리를 저장하고 클래스 패스에 추가하는 것이다.


Spring 을 사용하려면 필요한 Spring 모듈의 jar 라이브러리 사본을 얻어야 한다.

예를 들어, 웹 어플리케이션을 작성하지 않는다면, spring-web 모듈이 필요하지 않다.

Spring 라이브러리 모듈은 spring-core, spring-webmvc, spring-jms 처럼 spring-* 형식의 이름으로 각각 저장소에 게시된다.



Spring Framework 배포판(RELEASE) 다운로드


  • Maven Central
    Maven 에서 쿼리하는 기본 저장소이며 Spring 이 의존하고있는 많은 공통 라이브러리들도 Maven Central 에서 구할 수 있다. 이 곳에서 Spring 모듈인 jar 의 이름은 spring-*-<version>.jar 형식이며 Maven groupId 는 org.springframework 가 된다.

  • Spring Repogitory (https://repo.spring.io)
    이 저장소는 Spring 을 위해 특별히 호스팅 되는 공개 Maven 저장소이며, 최종 GA(General Available) 릴리즈 외에도 개발 snapshot 및 milestone 을 호스팅한다. jar 파일 이름이 Maven Central 과 같은 형식이기 때문에, Spring 의 개발 버전을 Maven Central 에서 배포된 다른 라이브러리와 함께 얻을 수 있는 유용한 장소이다. 이 저장소는 또한 번들로 묶인 모든 Spring jar 파일들을 포함하는 번들 zip 파일도 제공한다.


다운받는 실제 jar 파일 이름은 spring-core-4.3.6.RELEASE.jar 처럼 일반적으로 버전 번호와 연결된 모듈 이름이다.


가장 먼저 결정해야 할 것은 의존성을 관리하는 방법이다.

모든 Jar를 직접 다운로드하여 수동으로 작업할 수도 있지만, 일반적으로 Maven, Gradle 등의 빌드 자동화 툴을 사용하는 것이 좋다.



Maven 의존성 관리


Maven Central 저장소로부터 Spring 어플리케이션 컨텍스트를 만들기 위해 Maven dependencies 를 다음과 같이 구성할 수 있다.

Spring API에 대해 컴파일 할 필요가 없다면 적용범위(scope) 를 runtime 으로 선언한다.


<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.6.RELEASE</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>
cs


Spring Maven 저장소(예: milestones, 개발자 snapshots) 를 사용하려면 Maven 구성에서 저장소 위치를 지정해야 한다.


For releases:


<repositories>
    <repository>
        <id>io.spring.repo.maven.release</id>
        <url>http://repo.spring.io/release/</url>
        <snapshots><enabled>false</enabled></snapshots>
    </repository>
</repositories>
cs


For milestones:


<repositories>
    <repository>
        <id>io.spring.repo.maven.milestone</id>
        <url>http://repo.spring.io/milestone/</url>
        <snapshots><enabled>false</enabled></snapshots>
    </repository>
</repositories>
cs


For snapshots:


<repositories>
    <repository>
        <id>io.spring.repo.maven.snapshot</id>
        <url>http://repo.spring.io/snapshot/</url>
        <snapshots><enabled>true</enabled></snapshots>
    </repository>
</repositories>
cs



Maven BOM (Bill Of Materials) Dependency


Maven 을 사용하다 보면 다른 버전의 Spring JAR 파일들이 혼합될 수 있지만, BOM dependency 를 사용하면 이를 막을 수 있다.

모든 Spring 의존성을 동일한 버전으로 가져오도록 dependencyManagement 섹션에서 spring-framework-bom 을 import 하면 Spring Framework artifact 작성할 때 더 이상 <version> 속성을 지정할 필요가 없다.


<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-framework-bom</artifactId>
            <version>4.3.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
cs



Gradle 의존성 관리


Gradle 로 Spring 저장소를 사용하려면, repositories 섹션에 Maven Central 이나 특정 URL 을 포함시킨다.


repositories {
    mavenCentral()
    // and optionally...
    maven { url "http://repo.spring.io/release" }
}
cs


repositories URL 을 /release 에서 /mileestone 나 /snapshot 으로 변경하거나 더 추가할 수 있다. 

저장소가 설정되었으면, 일반적인 Gradle 방식으로 dependencies 를 선언할 수 있다.


dependencies {
    compile("org.springframework:spring-context:4.3.6.RELEASE")
    testCompile("org.springframework:spring-test:4.3.6.RELEASE")
}
cs




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,



Spring Framework 는 Core Container, Data Access/Integration, Web, AOP, Instrumentation, Messaging, Test 로 그룹화 된, 약 20 개의 모듈로 구성된다.



1. Core Container


Core 컨테이너는 spring-core, spring-beans, spring-context, spring-context-support, spring-expression (Spring Expression Language) 모듈로 구성된다.

모듈 이름은 의존성 관리 도구에 사용되는 artifact ID 가 된다. (spring-<module>:<version> 형식)


spring-corespring-beans 모듈은 IoC 및 DI 기능을 포함하여 프레임워크의 기본적인 부분을 제공한다. 

BeanFactory 는 factory 패턴을 정교하게 구현하여, 프로그래밍 방식의 singleton 패턴을 없애고 실제 프로그램 로직에서 종속성의 구성과 사양을 분리할 수 있다.


spring-context 모듈은 Core 와 Beans 모듈에서 제공하는 견고한 기반 위에 구축되어, JNDI 레지스트리와 비슷한 프레임워크 스타일 방식으로 객체에 액세스하는 수단이다. Beans 모듈에서 기능을 상속하며, 국제화 (예: resource bundles 사용), 이벤트 전달(propagation), 리소스 로드 및 서블릿 컨테이너 같은 컨텍스트의 생성과 EJB, JMX 및 기본 원격 처리을 지원한다.

spring-context-support 는 캐싱(EhCache, Guava, JCache), 메일링(JavaMail), 스케줄링(CommonJ, Quartz) 및 템플릿 엔진(FreeMarker, JasperReports, Velocity) 을 위한 Spring 어플리케이션 컨텍스트로 일반적인 third-party 라이브러리를 통합할 수 있게 한다.


spring-expression 모듈은 런타임시 객체 그래프(특정 시점의 객체들의 참조 관계) 를 쿼리하고 조작하기 위한 강력한 표현식 언어(EL: Expression Language) 로, JSP 2.1 사양에 명시된 대로 통합 표현식 언어 (unified EL) 를 확장하였다. EL 은 속성 값 설정, 가져오기, 할당 / 메소드 호출 / 배열, 콜렉션, 인덱서의 내용 / 논리 및 산술 연산자 / 변수 / Spring 의 IoC 컨테이너에서 이름으로 객체를 조회하고, 일반 리스트의 projection, selection, aggregations 를 지원한다.



2. AOP / Instrumentation


spring-aop 모듈은 AOP Alliance 와 호환되는 관점 지향 프로그래밍(AOP) 구현을 제공한다. 예를 들어 분리되어야 하는 기능 구현 코드를 완전히 분리하도록 메소드 interceptors 와 pointcut 을 정의할 수 있다.


spring-aspects 모듈로 AOP 프로그래밍인 AspectJ 와 통합할 수 있다.


spring-instrument 모듈은 JVM 부트스트랩핑을 위한 Instrumentation 에이전트로, 특정 애플리케이션 서버에서 사용되는 instrumentation 지원 및 클래스 로더 구현을 제공한다. 

spring-instrument-tomcat 모듈은 Tomcat 용 Spring instrumentation 에이전트를 포함한다.



3. Messaging


spring-messaging 모듈은 메시징 아키텍처와 프로토콜을 지원하며, 메시징 기반 어플리케이션에 사용되는 Message, MessageChannel, MessageHandler 등의 주요 추상화를 제공한다.



4. Data Access/Integration


Data Access/Integration 계층은 JDBC, ORM, OXM, JMS 및 Transaction 모듈로 구성된다.


spring-jdbc 모듈은 JDBC 코딩과 데이터베이스 벤더의 특정 에러 코드를 분석할 필요가 없도록 JDBC 추상화 계층을 제공한다.


spring-tx 모듈은 특별한 인터페이스를 구현하는 클래스와 모든 POJO 를 위한 프로그래밍 및 선언적 트랜잭션 관리를 지원한다.


spring-orm 모듈은 JPA, JDO 및 Hibernate를 포함하는 ORM(Object-Relational Mapping) API를 위한 integration 계층을 제공한다. 


spring-oxm 모듈은 JAXB, Castor, XMLBeans, JiBX, XStream 같은 Object/XML 매핑 구현을 지원하는 추상화 계층을 제공한다.


spring-jms 모듈은 JMS(Java Messaging Service) 엑세스와 메시지 리스너 컨테이너를 지원하며, spring-messaging 모듈과의 통합을 제공한다.



5. Web


Web 계층은 spring-web, spring-webmvc, spring-websocket, spring-webmvc-portlet 모듈로 구성된다.


spring-web 모듈은 서블릿 리스너와 웹 어플리케이션 컨텍스트를 이용한 IoC 컨테이너 초기화와 multipart 파일 업로드 기능 같은 기본적인 웹 지향 통합 기능을 제공한다. 또한 HTTP 클라이언트와 Spring 원격 지원의 웹 관련 부분을 지원한다.


spring-webmvc 모듈(Web-Servlet) 은 도메인 모델 코드와 웹 폼을 깔끔하게 분리하는 MVC(Model-View-Controller) 와 REST Web Service 구현을 지원한다.


spring-webmvc-portlet 모듈은 spring-webmvc 모듈의 기능을 반영하고, Portlet 환경에서 사용될 MVC 구현을 제공한다.



6. Test


spring-test 모듈은 JUnit 나 TestNG 를 사용하여, Spring 구성 요소의 유닛 테스트 및 통합 테스트를 지원하며, 코드를 독립적으로 테스트할 수 있는 모의(mock) 객체를 제공한다.




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,



가장 지루한 Spring Framework 서론.


Spring Framework 는 자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크로서, 동적인 웹 사이트를 개발하기 위한 여러 가지 서비스를 제공하고 있다.

모듈 별로 구분되어 있어서 모듈 전체를 가져오지 않고 않고 필요한 부분만 가져와 사용할 수 있기 때문에, 경량의 어플리케이션도 구축할 수 있다.

트랜잭션 관리, RMI 나 웹 서비스를 통한 로직으로의 원격 액세스, 데이터를 유지하기 위한 다양한 옵션도 지원한다. 



Spring Framework 특징


POJO (Plain Old Java Object)

일반적인 J2EE 프레임워크에 비해 특정한 인터페이스를 구현하거나 상속을 받을 필요가 없어 기존에 존재하는 라이브러리 등을 사용하기 편하고 객체가 가볍다.

POJO 어플리케이션을 빌드하는 것도 가능하고, POJO 에 비-침입적(non-invasively) 으로 엔터프라이즈 서비스를 적용하는 것도 가능하다.


DI (Dependency Injection)

각각의 계층이나 서비스들 간에 의존성이 존재할 경우 프레임워크가 서로 연결시켜준다.


IoC (Inversion of Control)

컨트롤의 제어권이 사용자가 아니라 프레임워크에 있어서, IoC 컨테이너가 각각의 객체 생성, 소멸과 같은 라이프 사이클을 관리하며 이 객체를 bean 이라고 한다.


AOP (Aspect-Oriented Programming)

로깅이나 보안, 트랜잭션 등 핵심적인 비즈니스 로직과 관련이 없으나 여러 곳에서 공통적으로 쓰이는 기능들을 따로 분리하여 개발하고 실행 시에 다시 조합할 수 있다.


Data Access

JDBC, iBATIS(MyBatis), Hibernate 등의 데이터베이스에 접속하고 자료를 저장 및 읽어오기 위한 인터페이스를 제공한다.


MVC Pattern

웹 프로그램밍 개발 시 거의 표준적인 방식인 Spring MVC라 불리는 모델-뷰-컨트롤러(MVC) 패턴을 사용한다. 

DispatcherServlet 이 Contoller 역할을 담당하여 각종 요청을 적절한 서비스에 분산, 이를 각 서비스들이 처리, 그 결과를 다양한 형식의 View 서비스로 화면에 표시할 수 있다.


Batch

Quartz 기반으로 특정 시간대에 실행하거나 대용량의 자료를 처리할 수 있다.



Spring Framework vs Spring Boot


Spring 을 처음 시작한다면, Spring Boot 기반의 어플리케이션을 생성하여 Spring Framework 를 사용할 수 있다.

Spring Boot CoC(Convention over Configuration: 설정 이상의 관례) 를 선호하며, 코딩/설정 등을 최소화 할 수 있고 가능한 빠르게 작동하도록 설계되었다.

https://spring.io/guides 에 Spring Boot 를 기반으로 한 많은 예제가 있으며, 특정 문제를 해결할 때 고려해야 할 Spring 의 다른 프로젝트들도 포함되어 있다.


Spring Boot 를 사용하는 것이 훨씬 편리하고 간단하다. 추세도 그러하다.

이 시점에 Spring Boot 를 사용하지 않고 Spring Framework 로 개발하는 것은 회칼을 옆에 두고 과도로 횟감을 뜨고 있는 모양새와 같을 수 있다.

그럼에도 불구하고 Spring Framework 를 계속 보고 있는 이유는, 

어찌됐든 Spring Framework 를 간편화 한 프로젝트이므로 그 근간을 모르고서는 방식/동작 등에 대해 이해가 부족할 것 같아서 이다.

그렇게 따지자면 java1 부터 공부를 해야 맞는 것이 겠지만; 선택은 하고 싶은 사람들 마음이다.

꼭 필요해서 한다면 Spring Boot 를 먼저 봐도 좋을 것이고, 알고 싶다면 Spring Framework 부터 봐도 좋을 것이고...




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,