이야기 - 자바 진영의 추운 겨울과 스프링의 탄생
2000년대 초반, 자바 진영의 표준 기술은 Enterprise Java Beans 였다. 이론이 좋은 종합 선물 세트였지만, 상당히 비싸고 복잡하며 성능이 좋지 못했다. 그러다보니, 순수한 Plain Old Java Object 로 돌아가자는 의견이 나오기 시작했다. 그렇게 등장한 것이, EJB 를 대체할 Spring 과 EJB 엔티티를 대체할 JPA 이다.
스프링 역사
2002년 로드 존슨이라는 사람이 EJB 의 문제점을 지적하는 책을 출판한다. 이 책에 BeanFactory, ApplicationContext, POJO, IoC, DI 등등 지금의 스프링 핵심 개념과 기반 코드가 들어가 있다. 스프링은 좋은 대신, 설정이 너무 힘들었는데, 이를 보완하여 나온 것이 스프링 부트이다.
스프링이란?
스프링 프레임워크
핵심 기술 - 스프링 DI 컨테이너, AOP, 이벤트 웹 기술 - 스프링 MVC, 스프링 WebFlux 데이터 접근 기술 - 트랜잭션, JDBC, ORM 기술 통합 - 캐시, 이메일, 원격접근, 스케줄링
스프링 부트
스프링을 편리하게 사용할 수 있도록 지원하는 것이 스프링 부트이다.
- 단독으로 실행할 수 있는 스프링 애플리케이션을 쉽게 생성할 수 있다.
- Tomcat 같은 웹 서버를 내장해서 별도의 웹 서버를 설치하지 않아도 된다.
- 손쉬운 빌드 구성을 위한 starter 종속성을 제공한다.
- 스프링과 외부 라이브러리를 자동으로 구성해준다.
- 메트릭, 상태 확인, 외부 구성 같은 프로덕션 준비 기능을 제공한다.
- 관례에 의한 간결한 설정을 지원한다.
스프링이라는 단어?
스프링이라는 단어는 문맥에 따라 다르게 사용된다.
- 스프링 DI 컨테이너 기술
- 스프링 프레임워크
- 스프링 부트, 스프링 프레임워크 등을 모두 포함한 스프링 생태계
스프링을 왜 만들었을까?
스프링은 자바 언어 기반의 프레임워크다. 자바 언어의 가장 큰 특징은 객체 지향 언어라는 것이다. 스프링은 객체 지향 언어가 가진 강력한 특징을 살려내는 프레임워크이다. 즉, 스프링은 좋은 객체 지향 애플리케이션을 개발할 수 있게 도와주는 프레임워크라는 것이다.
좋은 객체 지향 프로그래밍이란?
객체 지향의 특징
- 추상화
- 캡슐화
- 상속
- 다형성
객체 지향 프로그래밍
객체는 메시지를 주고 받으며 협력을 할 수 있기에 유연하고 변경에 용이하다는 장점이 있다. 이러한 특징 때문에 대규포 소프트웨어 개발에 많이 사용된다.
유연하고 변경에 용이하다?
레고 블록을 조립하듯이 컴포넌트를 쉽고 유연하게 변경하면서 개발할 수 있는 방법이 객체 지향의 핵심이다. 즉, 다형성이 객체 지향 프로그래밍의 가장 중요한 핵심이다.
역할과 구현을 분리
역할과 구현을 분리하면 세상이 단순해지고, 유연해지며 변경도 편리해진다. 이를 통한 장점으로는 아래와 같은 것들이 있다.
- 클라이언트는 대상의 역할(인터페이스)만 알면 된다.
- 클라이언트는 구현 대상의 내부 구조를 몰라도 된다.
- 클라이언트는 구현 대상의 내부 구조가 변경되어도 영향을 받지 않는다.
- 클라이언트는 구현 대상 자체를 변경해도 영향을 받지 않는다. 자바 언어의 다형성을 활용하여 이러한 이점을 프로그래밍 시에도 가져올 수 있다.
- 역할 = 인터페이스
- 구현 = 인터페이스를 구현한 클래스, 구현 객체 객체를 설계할 때 역할과 구현을 명확히 분리하여 역할을 먼저 부여하고, 그 역할을 수행하는 구현 객체를 만들어내는 것에 객체 지향 프로그래밍의 핵심 개념이다.
자바 언어의 다형성
오버라이딩을 떠올려보자. 다형성으로 인터페이스를 구현한 객체를 실행 시점에 유연하게 변경할 수 있다.
다형성의 본질
인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경할 수 있다. 다형성의 본질을 이해하려면 협력이라는 객체 사이의 관계에서 시작해야 한다. 클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있는 것이 다형성의 본질인 것이다.
역할과 구현을 분리 - 정리
실세계의 역할과 구현이라는 편리한 컨셉을 다형성을 통해 객체 세상으로 가져올 수 있다. 이는 유연하고, 변경이 용이하다는 장점을 가져다 주고, 이는 곧 확장 가능한 설계를 의미하기도 한다. 클라이언트에 영향을 주지 않고 변경을 가능하게 하는 것이 핵심이기에, 인터페이스를 안정적으로 잘 설계하는 것이 중요하다.
역할과 구현을 분리 - 한계
역할(인터페이스) 자체가 변하면, 클라이언트, 서버 모두에 큰 변경이 발생한다. 즉, 인터페이스를 안정적으로 잘 설계하는 것이 중요한 것이다.
스프링과 객체 지향
다형성이 가장 중요하다! 스프링은 다형성을 극대화해서 이용할 수 있게 도와준다. 스프링에서 이야기하는 IoC, DI 는 다형성을 활용해서 역할과 구현을 편리하게 다룰 수 있도록 지원한다. 스프링을 사용하면 마치 레고 블럭을 조립하듯이 구현을 편리하게 변경할 수 있다.
좋은 객체 지향 설계의 5가지 원칙(SOLID)
객체 지향 설계와 스프링
다시 스프링으로
스프링 이야기에 왜 자꾸 객체 지향 이야기가 나오는거지? 순수하게 자바로 OCP, DIP 원칙들을 지키면서 개발을 해보면, 결국 스프링 프레임워크를 만들게 된다. 정확히는 DI 컨테이너를 만들게 된다. 스프링은 다음 기술로 다형성 + OCP, DIP 를 가능하도록 지원한다.
- DI - 의존성 주입
- DI 컨테이너 제공 이는 클라이언트 코드의 변경없이 기능 확장을 가능하게 한다. 즉, 쉽게 부품을 교체하듯이 개발할 수 있게 도와준다는 것이다.
정리
모든 설계에 역할과 구현을 분리하자. 즉, 이상적으로는 모든 설계에 인터페이스를 부여하자는 의미다. 하지만, 인터페이스를 도입하면 추상화라는 비용이 발생한다. 기능을 확장할 가능성이 없다면, 구체 클래스를 직접 사용하고, 향후 꼭 필요할 때 리팩터링해서 인터페이스를 도입하는 것도 방법이라고 볼 수 있다.
스프링 웹 개발 기초
웹을 개발하는 3가지 방법
- 정적 컨텐츠
- MVC와 템플릿 엔진
- API
정적 컨텐츠
resources/static 경로에 정적 html 컨텐츠를 저장한다. resources/static/hello-static.html
이후 http://localhost:8080/hello-static.html 접속 시 저장한 컨텐츠에 접근할 수 있다.
MVC와 템플릿 엔진

@GetMapping("hello-mvc")
public String helloMvc(@RequestParam("name") String name, Model model) {
model.addAttribute("name", name);
return "hello-template";
}Controller 에 Mapping 된 경로를 통해 해당 요청을 수행한다.
model 에 요청 시 전달 받은 인자 RequestParam 을 추가하여 hello-template 에 적용할 수 있다.
API

@ResponseBody사용 시viewResolver대신,HttpMessageConverter가 동작한다.
@ResponseBody 문자 반환
@GetMapping("hello-string")
@ResponseBody
public String helloString(@RequestParam("name") String name) {
return "hello " + name;
}@ResponseBody를 사용하여 HTTP Body 에 문자 내용을 직접 반환한다.StringHttpMessageConverter가 기본으로 동작한다.
@ResponseBody 객체 반환
@GetMapping("hello-api")
@ResponseBody
public Hello helloApi(@RequestParam("name") String name) {
Hello hello = new Hello();
hello.setName(name);
return hello;
}@ResponseBody를 사용하여 객체를 반환 시, 객체가 JSON 으로 변환된다.MappingJackson2HttpMessageConverter가 기본으로 동작한다.