# 스프링이란?
스프링(Spring)은 자바 기반의 오픈소스 애플리케이션 프레임워크로, 엔터프라이즈급 애플리케이션 개발을 단순화하기 위해 만들어졌다.
- 경량 컨테이너: 객체(Bean)의 생명주기를 직접 관리
- IoC(Inversion of Control): 객체 생성 및 의존관계 제어권을 프레임워크가 담당
- DI(Dependency Injection): 의존성을 외부에서 주입하여 결합도를 낮춤
- AOP(Aspect Oriented Programming): 공통 관심사를 핵심 로직과 분리
# 스프링 생태계
스프링은 단일 프레임워크가 아닌 다양한 모듈로 구성된 생태계다.
- Spring Framework: 핵심 DI/IoC 컨테이너
- Spring Boot: 설정 자동화, 내장 서버 포함 — 실무에서 가장 많이 사용
- Spring MVC: 웹 애플리케이션을 위한 MVC 패턴 구현
- Spring Data: JPA, MongoDB 등 데이터 접근 추상화
- Spring Security: 인증/인가 처리
# 스프링 부트(Spring Boot)
스프링 부트는 스프링을 더 쉽게 사용할 수 있도록 만든 도구다. 복잡한 XML 설정 없이 빠르게 프로젝트를 시작할 수 있다.
- 자동 설정(Auto Configuration):
@SpringBootApplication어노테이션 하나로 필요한 빈을 자동 등록 - 내장 서버: Tomcat이 내장되어 있어 별도 서버 설치 없이 실행 가능
- 스타터 의존성(Starter):
spring-boot-starter-web등 묶음 의존성으로 설정 간소화
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
# 웹 요청 처리 흐름
스프링 부트 애플리케이션이 웹 요청을 처리하는 방식은 콘텐츠 종류에 따라 달라진다.
# 정적 컨텐츠 (Static Content)
정적 컨텐츠는 서버에서 별도 가공 없이 파일 그대로 응답하는 방식이다. HTML, CSS, JS, 이미지 파일 등이 해당된다.
웹 브라우저
│
│ GET /hello.html
▼
내장 톰캣 서버 (Embedded Tomcat)
│
│ 스프링 컨테이너에서 hello 관련 컨트롤러 탐색
▼
스프링 컨테이너
│
│ 매핑된 컨트롤러 없음 → 정적 리소스 탐색
▼
resources/static/hello.html 파일 반환
│
▼
웹 브라우저
핵심 흐름:
- 브라우저가 톰캣 서버로 HTTP 요청을 보낸다
- 톰캣이 스프링 컨테이너에 해당 URL을 처리할 컨트롤러가 있는지 먼저 확인한다
- 매핑된 컨트롤러가 없으면
resources/static/또는resources/public/폴더에서 동일한 이름의 파일을 찾는다 - 파일이 있으면 그대로 응답한다
정적 컨텐츠는 스프링 컨테이너의 비즈니스 로직을 거치지 않고 파일을 직접 서빙한다.
# DispatcherServlet
DispatcherServlet은 스프링 MVC의 **프론트 컨트롤러(Front Controller)**다. 톰캣으로 들어오는 모든 HTTP 요청을 가장 먼저 받아 적절한 컨트롤러에게 위임하는 역할을 한다.
스프링 부트를 사용하면 DispatcherServlet이 자동으로 등록되며, 기본적으로 /* 경로의 모든 요청을 처리한다.
DispatcherServlet의 책임:
- 요청 URL을 분석해 처리할 컨트롤러를 핸들러 매핑으로 탐색
핸들러 매핑(Handler Mapping)이란?
요청 URL과 처리할 컨트롤러 메서드를 연결해주는 일종의 지도다. 스프링 부트에서는 @GetMapping, @PostMapping 등의 어노테이션을 기준으로 자동으로 매핑 정보가 등록된다. DispatcherServlet이 요청을 받으면 핸들러 매핑에 "이 URL 담당자가 누구야?"라고 물어보고, 반환된 컨트롤러를 호출한다.
- 컨트롤러 호출 후 반환된 뷰 이름을 ViewResolver에 전달
ViewResolver란?
컨트롤러가 반환한 뷰 이름(문자열)을 실제 템플릿 파일 경로로 변환해주는 컴포넌트다. 예를 들어 컨트롤러가 "hello"를 반환하면, ViewResolver가 resources/templates/hello.html로 해석해 템플릿 엔진에 전달한다. 스프링 부트에서 Thymeleaf를 사용하면 ThymeleafViewResolver가 자동으로 등록되어 resources/templates/ 경로와 .html 확장자를 자동으로 붙여준다.
- 예외 발생 시 HandlerExceptionResolver에 위임
프론트 컨트롤러 패턴을 사용하기 전에는 각 URL마다 별도의 서블릿을 등록해야 했다. DispatcherServlet이 창구를 하나로 통일함으로써 공통 처리(인증, 로깅 등)를 중앙에서 관리할 수 있게 된다.
요청 1: GET /hello ─┐
요청 2: GET /bye ─┼──▶ DispatcherServlet ──▶ 각 컨트롤러로 분기
요청 3: POST /login ─┘
# MVC와 템플릿 엔진
MVC(Model-View-Controller) 패턴은 서버에서 동적으로 HTML을 생성하여 응답하는 방식이다. Thymeleaf 같은 템플릿 엔진이 View 역할을 담당한다.
웹 브라우저
│
│ GET /hello?name=spring
▼
내장 톰캣 서버 (Embedded Tomcat)
│
│ HTTP 요청을 스프링에 전달
▼
스프링 컨테이너
├─ DispatcherServlet (프론트 컨트롤러)
│ │
│ │ 핸들러 매핑 조회 → @GetMapping("/hello") 탐색
│ ▼
│ Controller (@Controller)
│ │
│ │ 비즈니스 로직 수행, Model에 데이터 저장
│ │ return "hello"; (뷰 이름 반환)
│ ▼
│ ViewResolver
│ │
│ │ "hello" → resources/templates/hello.html 매핑
│ ▼
│ 템플릿 엔진 (Thymeleaf)
│ │
│ │ Model 데이터를 HTML에 삽입하여 렌더링
│ ▼
│ 완성된 HTML
│
▼
웹 브라우저
핵심 흐름:
- 브라우저가 톰캣 서버로 HTTP 요청을 보낸다
- 톰캣이 요청을 스프링의 DispatcherServlet으로 전달한다
- DispatcherServlet이 핸들러 매핑을 통해 URL에 맞는 컨트롤러를 찾는다
- 컨트롤러가 로직을 수행하고 Model에 데이터를 담아 뷰 이름을 반환한다
- ViewResolver가 뷰 이름을 실제 템플릿 파일 경로로 변환한다 (
resources/templates/{뷰이름}.html) - 템플릿 엔진이 템플릿에 Model 데이터를 채워 최종 HTML을 생성한다
- 완성된 HTML이 브라우저에 응답된다
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("name", "spring");
return "hello"; // ViewResolver가 resources/templates/hello.html을 찾음
}
}
<!-- resources/templates/hello.html -->
<html xmlns:th="http://www.thymeleaf.org">
<body>
<p th:text="'안녕하세요, ' + ${name}">기본 텍스트</p>
</body>
</html>
# @ResponseBody를 활용한 API 방식
@ResponseBody는 컨트롤러의 반환값을 뷰가 아닌 HTTP 응답 바디에 직접 쓰는 방식이다. ViewResolver를 거치지 않고 데이터(주로 JSON)를 그대로 응답한다.
웹 브라우저
│
│ GET /api/hello?name=spring
▼
내장 톰캣 서버 (Embedded Tomcat)
│
│ HTTP 요청을 스프링에 전달
▼
스프링 컨테이너
├─ DispatcherServlet
│ │
│ │ 핸들러 매핑 조회 → @GetMapping("/api/hello") 탐색
│ ▼
│ Controller (@ResponseBody 포함)
│ │
│ │ 비즈니스 로직 수행, 객체 반환
│ ▼
│ HttpMessageConverter
│ │
│ │ 객체 → JSON 변환 (MappingJackson2HttpMessageConverter)
│ │ 문자열 → 그대로 (StringHttpMessageConverter)
│ ▼
│ HTTP 응답 바디에 직접 삽입
│
▼
웹 브라우저 (JSON 데이터 수신)
핵심 흐름:
- 브라우저가 톰캣 서버로 HTTP 요청을 보낸다
- DispatcherServlet이 핸들러 매핑으로 컨트롤러를 찾는다
@ResponseBody가 붙은 메서드는 반환값을 뷰 이름이 아닌 데이터로 취급한다- HttpMessageConverter가 반환값의 타입에 따라 변환 방식을 결정한다
- 변환된 데이터가 HTTP 응답 바디에 직접 작성된다
HttpMessageConverter란?
@ResponseBody 사용 시 객체를 HTTP 응답으로 변환해주는 컴포넌트다. 반환 타입이 String이면 StringHttpMessageConverter가, 객체(클래스)이면 MappingJackson2HttpMessageConverter가 JSON으로 직렬화한다. 스프링 부트는 Jackson 라이브러리를 기본으로 포함하므로 별도 설정 없이 객체를 JSON으로 변환할 수 있다.
@Controller
public class HelloApiController {
@GetMapping("/api/hello")
@ResponseBody
public Hello helloApi(@RequestParam("name") String name) {
Hello hello = new Hello();
hello.setName(name);
return hello; // 객체를 반환 → JSON으로 변환되어 응답
}
static class Hello {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
}
응답 결과:
{ "name": "spring" }
@RestController는 @Controller + @ResponseBody를 합친 어노테이션으로, REST API 컨트롤러를 만들 때 주로 사용한다.
# 정적 컨텐츠 vs MVC 비교
| 구분 | 정적 컨텐츠 | MVC + 템플릿 엔진 |
|---|---|---|
| 처리 주체 | 톰캣이 파일 직접 서빙 | 스프링 컨테이너 전체 관여 |
| 컨트롤러 | 없음 | 필수 |
| 데이터 가공 | 불가 | 가능 |
| 응답 형태 | 파일 그대로 | 동적 생성 HTML |
| 파일 위치 | resources/static/ | resources/templates/ |
# 스프링 빈과 의존관계
- 스프링 빈은 스프링 IoC 컨테이너가 생성하고 관리하는 객체이다.
- IoC(Inversion of Control): 제어의 역전
- 객체 생성/소멸/의존성 연결을 내가 아니라 Spring이 책임
- 기존에는 개발자가 직접 의존 관계를 코드로 정의했지만 스프링 빈,
@Autowired와 같은 구문들을 사용하면 스프링에 의해 이러한 의존 관계들이 관리된다. - 스프링 등록 방법
- 어노테이션을 붙여 컴포넌트 스캔을 수행하고 Bean으로 등록한 뒤, 자동 의존관계 설정(Autowiring)을 통해 Bean들 사이의 의존관계가 확립
@Component애노테이션이 있으면 스프링 빈으로 자동 등록됨.- 위 애노테이션을 포함하는
@Controller,@Service등의 애노테이션들도 빈으로 자동 등록
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
- 위와 같은 코드의 경우
MemberRepository타입으로 등록된 스프링 빈을 찾아 서비스 생성자 호출 시점에 의존성을 주입해준다.
# AOP (Aspect-Oriented Programming)
AOP는 핵심 비즈니스 로직과 부가 기능(로깅, 트랜잭션, 보안 등)을 분리하는 프로그래밍 패러다임이다.
여러 곳에 반복되는 공통 기능을 **횡단 관심사(Cross-cutting Concerns)**라고 하는데, AOP 없이는 이를 매 메서드마다 직접 작성해야 한다.
// AOP 없이 모든 메서드에 반복 작성
public void createOrder() {
log.info("start"); // 반복
startTransaction(); // 반복
// 핵심 비즈니스 로직
commitTransaction(); // 반복
log.info("end"); // 반복
}
AOP를 사용하면 공통 로직을 한 곳에 모아 관리할 수 있다.
@Aspect
@Component
public class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logging(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("start: {}", joinPoint.getSignature());
Object result = joinPoint.proceed(); // 핵심 로직 실행
log.info("end");
return result;
}
}
주요 용어:
| 용어 | 설명 |
|---|---|
| Aspect | 부가 기능을 모아둔 모듈 |
| Advice | 실제 부가 기능 코드 (@Before, @After, @Around) |
| Pointcut | Advice를 적용할 메서드 지정 |
| JoinPoint | Advice가 끼어들 수 있는 실행 지점 |
스프링 AOP는 프록시 패턴 기반으로 동작한다. 런타임에 프록시 객체를 생성해 실제 메서드 호출 전후에 Advice를 끼워 넣는다.
자바 기초 →