[프레임워크] 스프링 프레임워크 개요
in Lecture on Spring, Java

스프링 프레임워크 개요
1. Spring Framework 개요
자바 엔터프라이즈 개발을 편하게 해주는 오픈소스 경량급 애플리케이션 프레임워크
1.1. EJB 아키텍처
- 비즈니스 로직의 복잡성과 기술적인 복잡성의 분리
- 선언적 트랜잭션, 보안
- 컨테이너를 통한 리모팅 기술의 적용
- 컴포넌트 단위의 배치
- JNDI를 이용한 서비스 검색
- 서비스 오브젝트의 풀링
- 컴포넌트 생명주기 관리
- But, EJB라는 환경과 스펙에 종속(침투적 기술)
- EJB 환경에 동작하기 위해 특정 인터페이스를 구현하고 특정 클래스를 상속
- 서버에 종속적인 서비스를 통해서만 접근하고 사용이 가능
- 객체지향적인 특성을 잃어버리는 결과
- 상속, 다형성 적용의 제한
- 대안은?
비침투적(non-invasive)
인 기술의 적용- 기술의 적용 사실이 코드에 직접 반영되지 않음
- 코드의 설계와 구현 방식을 제한하지 않음
1.2 스프링의 기원
- Rod Johnson, “Expert One-on-One J2EE Design and Development”, 2003
- “항상 프레임워크 기반으로 접근하라”
1.3 스프링의 전략
Lightweight
Container Architecture
객체지향 언어의 장점을 최대한 활용 => 스프링은 단지 거들 뿐이다.
- 개발의 복잡함을 제거하고 개발을 편하게 해주는 해결책을 제시
- 로우레벨의 트랜잭션이나 상태 관리, 멀티 스레딩, 리소스 풀링 …
- 비즈니스 로직을 빠르고 효과적으로 구현
서비스 추상화(Portable Service Abstraction)
- 로우레벨의 기술 구현 부분과 기술을 사용하는 인터페이스를 분리
- 환경과 세부 기술에 독립적인 접근 인터페이스를 제공
- 엔터프라이즈 개발에 사용되는 다양한 기술에 대한 서비스 추상화 기능을 제공
- 설정을 통해 어떤 종류의 기술을 사용할지 지정
- 트랜잭션 서비스 추상화
- JDBC TX: PlatformTransactionManager
- JTA TX: JtaTransactionManager
- 트랜잭션 서비스 추상화
AOP (Aspect Oriented Programming)
- 기술과 비즈니스 로직의 혼재로 발생하는 복잡함을 해결하기 위한 방법
- 애플리케이션 로직을 담당하는 코드에 남아 있는 기술 관련 코드를 분리하여 별도 모듈로 관리
- 객체지향의 언어의 장점을 살려서 비즈니스 로직의 효과적인 구현이 가능
- 유연한 설계
- 재사용성
핵심도구 : 객체지향과 DI
- 비즈니스 로직 자체의 복잡함은 객체지향 설계 기법이 더 중요
- 즉, 객체지향 설계 기법을 잘 적욯할 수 있는 구조를 잘 만들 수 있도록 도와주는 전략
- DI(Dependency Injection)
- 바뀔 수 있는 것을 오브젝트로 분리하고,
- 인터페이스를 도입하고,
- DI로 관계를 연결
POJO (Plain Old Java Object)
- Martin Fowler, 2000, EJB <-> POJO
간단한 자바 오브젝트
라는 의미- 특정 규약 및 환경에 종속되지 않는다.
- 자동화된 테스트에 유리하다.
- 스프링의 주요 기술은 애플리케이션을 POJO로 개발할 수 있게 해주는 가능 기술이다.
- IoC, DI, AOP …
1.4 스프링의 기술
1.4.1 제어의 역전(Inversion of Controll)
1.4.2 의존관계 주입(Dependency Injection)
- 인터페이스를 두고 느슨하게 연결한 뒤, 실제 사용할 대상은 DI를 이용해서 외부에서 지정
- 유연한 확장이 가능하게 하기 위함 (OCP) : A -> B
- 의존 대상의 구현을 변경 -> DAO
- 동적인 기능 변경 (다이내믹 라우팅 프록시, 프록시 오브젝트)
- 부가 기능의 추가 (데코레이터 패턴)
- 인터페이스의 변경 (어댑터 패턴) : A -> B -> C
- 어댑터 레이어
- 일관성 있는 서비스 추상화(PSA)
- 프록시
- 지연된 로딩(lazy loading)
- 원격 프록시(EJB, 웹서비스, REST, HTTP)
- 싱글톤과 오브젝트 스코프
- 대상 오브젝트를 컨테이너가 관리
- 오브젝트의 생성부터 관계 설정, 이용, 소멸
- 싱글톤이 기본 스코프
- 테스트
- 테스트 대상인 오브젝트의 기능에 충실하게 테스트 가능
- 의존 오브젝트를 대신해서 스텁 또는 목 오브젝트 활용이 용이
- 테스트용으로 설정을 별도록 만드는 방법도 가능
1.4.3 관점 지향 프로그래밍(Aspect Oriented Programming)
AOP 적용 기법
- 다이내믹 프록시 방식
- 기존코드에 영향을 주지 않고 부가기능을 적용하게 해주는 데코레이터 패턴을 응용한 것
- 부가 기능을 부여할 수 있는 곳은 메소드의 호출이 일어나는 지점 뿐이다.
(인터페이스와 DI를 활용)
- AspectJ
- 프록시 방식의 AOP에서는 불가능한 다양한 조인 포인트를 제공
- 메소드 호출, 인스턴스 생성, 필드 액세스 …
- 단점: AOP 컴파일러를 이용한 빌드 과정,
또는 클래스가 메모리로 로딩 될 때 바이트 코드를 조작하는 위빙 방법이 필요
AOP 적용 예
- 미리 준비된 AOP 이용
- 스프링이 제공하는 AOP 기능 적용
- 예> 트랜잭션
- 프로젝트 정책적으로 적용
- 공통 레벨에서 구현하여 적용
- 예> 보안, 로깅, 트레이싱, 성능 모니터링, …
1.5 Spring IoC Container
- instantialing
- Configuring
1.6 Spring Bean
- Annotation
- @Component annotation in class
- @BEan annotation in method
1.7 Dependency Management Tools
- Gradle
- DSL(Groovy or Kotlin)
- Maven
- XML
- Maven Repository
2. Hello World Example
Hello.java
public interface Hello {
String sayHello(String message);
}
HelloImpl.java
@Component
public class HelloImpl implements Hello {
@Override
public String sayHello(String message) {
return "Hello " + message;
}
}
HelloTest.java
@ContextConfiguration(classes = DbConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class HelloTest {
@Resource
Hello hello;
@Test
public void sayHello() {
String message = hello.sayHello("World");
assertEquals("Hello World", message);
}
}
컴포넌트 스캔을 위해서 WebConfig
클래스의 ComponentScan 패키지에 다음을 추가한다.
WebConfig.java
@ComponentScan(basePackages = { "kyun.framework.hello" })
3. Spring Web MVC
The request processing workflow in Spring Web MVC (high level)
사진출처: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html
MVC Architecture
Hello Web Application
Controller
HelloController.java
@Controller
public class HelloController {
@Resource
Hello hello;
@RequestMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello World!");
return "hello";
}
@RequestMapping("/hello/sayHello")
@ResponseBody
public String sayHello(String message) {
return hello.sayHello(message);
}
}
JSP
hello.jsp
<?xml version="1.0" encoding="UTF-8" ?>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
</head>
<body>
<H1>${message}</H1>
</body>
</html>
WEB Application Configuration 클래스
WebConfig.java
@EnableWebMvc
@Configuration
@ComponentScan(basePackages = { "kyun.framework.team.controller, kyun.framework.hello" })
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
}
WebInitializer.java
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { DbConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
Spring Web MVC 사용을 위한 라이브러리 의존성 추가
pom.xml
<!-- Spring Web MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.2.2.RELEASE</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<!-- JSTL -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
Tomcat Deploy & Test
http://mirror.navercorp.com/apache/tomcat/tomcat-8/v8.0.36/bin/apache-tomcat-8.0.36-windows-x64.zip
다운로드 받은 파일을 적당한 디렉토리에 압축을 푼다.
Eclipse Servers 뷰에서 New > Servers 를 클릭한다.
Apache > Tomcat v8.0 Server
를 선택하고Next
를 클릭한다.
Tomcat installation directory
를 선택하고Next
를 클릭한다.
basic
어플리케이션을 Add하고Finish
를 클릭한다.
테스트
http://localhost:8080l4.png/hello
http://localhost:8080/basic/hello/sayHello?message=World
4. REST API
REST 개요
REST는 웹의 창시자(HTTP) 중의 한 사람인 Roy Fielding의 2000년 논문에 의해서 소개되었다. 현재의 아키텍쳐가 웹의 본래 설계의 우수성을 많이 사용하지 못하고 있다고 판단했기 때문에, 웹의 장점을 최대한 활용할 수 있는 네트워크 기반의 아키텍쳐를 소개했는데 그것이 바로 Representational safe transfer (REST)이다.
REST 기본요소
리소스
REST는 리소스 지향 아키텍쳐 스타일이라는 정의 답게 모든 것을 리소스 즉 명사로 표현을 하며, 각 세부 리소스에는 id를 붙인다.http://localhost:8080/basic/api/team/1
메서드
Method | CRUD | Idempotent |
---|---|---|
POST | Create | No |
GET | Select | Yes |
PUT | Update | Yes |
DELETE | Delete | Yes |
- 메세지
요청/응답 데이터
[{"id":1,"teamName":"basic","rating":5}]
JSON 을 이용한 요청/응답
RestConfig.java
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "kyun.framework.team.controller" }, useDefaultFilters = false, includeFilters = {
@Filter(Controller.class) })
public class RestConfig extends WebMvcConfigurerAdapter {
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(mappingJackson2HttpMessageConverter());
}
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
return converter;
}
}
TeamService.java
public interface TeamService {
public void addTeam(Team team);
public void updateTeam(Team team);
public Team getTeam(int id);
public void deleteTeam(int id);
public List<Team> getTeams();
}
TeamService.java
@Service
@Transactional
public class TeamServiceImpl implements TeamService {
@Autowired
private TeamDao teamDao;
@Override
public void addTeam(Team team) {
teamDao.addTeam(team);
}
@Override
public void updateTeam(Team team) {
teamDao.updateTeam(team);
}
@Override
public Team getTeam(int id) {
return teamDao.getTeam(id);
}
}
TeamRestController.java
@Controller
@RequestMapping(value = "/api/team")
public class TeamRestController {
@Autowired
TeamService teamService;
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
public List<Team> getTeams(){
List<Team> teams = teamService.getTeams();
return teams;
}
@RequestMapping(value = "/add", method = RequestMethod.POST)
@ResponseBody
public String addTeam(@RequestBody Team team) {
teamService.addTeam(team);
return "OK";
}
}
다음과 같이 getServletConfigClasses()
에 RestConfig.class
를 추가한다.
WebInitializer.java
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class, RestConfig.class };
}
}
JSON 사용을 위한 라이브러리 의존성 추가
pom.xml
<!-- JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>
REST Test
Postman 설치
다음의 크롬 웹스토어에서 Postman
을 찾아 설치한다.
https://chrome.google.com/webstore/category/apps?utm_source=chrome-ntp-icon
크롬앱에서 Postman을 실행하고 다음과 같이 테스트를 수행한다.
- Method: POST 선택
- URL: http://localhost:8080/basic/api/team/add
- Body 탭에서
raw
타입을 선택하고JSON
포멧으로 변경한다. - 다음과 같이 요청 메세지를 입력한다.
{ "teamName":"basic", "rating":5 }
Send
버튼을 누르면 요청이 전송되고 다음과 같이 “OK” 응답이 돌아오면 정상 처리된 것이다.
- 다음 URL을 호출하여 정상 응답을 확인한다.
http://localhost:8080/basic/api/team/list
[{"id":1,"teamName":"basic","rating":5}]
5. Anotation vs XML
스프링에서 빈(Bean) 객체의 의존성 관리를 위한 설정 방법
5.1 Anotation
Service classes
@Resource
AaccountDao ccountDao
@Resource
ItemDao itemDao
DAO classes
@Repository
public class AaccountDao {
...
}
@Repository
public class ItemDao {
...
}
5.2 XML
service.xml
<!-- 서비스 -->
<bean id="petStore"
class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- 이 빈에 대한 추가적인 협력 객체나 설정은 여기에 작성한다 -->
</bean>
<!-- 서비스에 대한 추가적인 빈 정의는 여기에 작성한다 -->
dao.xml
<bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapAccountDao">
<!-- 이 빈에 대한 추가적인 협력 객체나 설정은 여기에 작성한다 -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapItemDao">
<!-- 이 빈에 대한 추가적인 협력 객체나 설정은 여기에 작성한다 -->
</bean>
<!-- 데이터 접근 객체에 대한 추가적인 빈 정의는 여기에 작성한다 -->
5.3 비교
5.3.1 Anotation
- 모든 정보가 소스코드에서만 관리되므로 가독성 측면에서 단순하고 명확
- 자바소스코드만 관리하면 되므로 관리가 용이
- 클래스가 변경되었을 경우 XML을 수정할 필요가 없음
- 생산성 측면으로도 XML 방식보다 더 유리
5.3.2 XML
- 코드와 설정의 명확한 분리
- 코드정보의 탐색 측면에서는 XML 바운더리만 검색하면 되므로 Anotation 보다 좀 더 용이
- XML 파일과 자바코드를 병행해서 트레이스 해야 하므로 가독성이 떨어짐
- XML 파일과 자바소스 코드를 모두 관리해야 함
오타로 인한 오류 발생 가능성이 더 높음
- 표로 정리하면 대략 다음과 같다.
구분 | Anotation | XML |
---|---|---|
Readability | O | X |
Productivity | O | X |
Flexibility | X | O |
Length | 짧다 | 길다 |
5.3.3 기타 의견
- 의존성 대상 객체의 추적은 IDE 툴에서 대부분 지원하는 기능임 따라서, XML, 어노테이션 모두 쉽게 트레이스 가능함
- 개발 편의성 측면에서는 위에서 언급한 바로 어노테이션 방식이 편리하다고 생각됨
- 소스코드의 양이 많을 수록 XML의 트레이싱은 더욱 어려워지고 Consistency 측면에서도 득이 될 것이 없음
- 유지보수성을 고려한다면 자산관리 및 거버넌스까지 고려되어야 장점을 찾을 수 있다고 생각됨 단순히 오류내역을 트레이스하고 소스코드를 찾기 위한 방법은 툴차원으로 해결 가능하다고 생각됨 (물론, 개발툴 없이 생각한다면 달라질 수 있음)