
이번 시간에는 백엔드의 기반이 될 스프링에 대해 배워봅시다!!
스프링은 Java로 애플리케이션을 쉽게 개발할 수 있도록 도와주는 프레임워크입니다.
즉, 개발자가 복잡한 작업에 집중하지 않고 필요한 기능을 빠르게 구현할 수 있도록 도구와 규칙을 제공하는 틀입니다.
1️⃣ 종합 요리 키트
2️⃣ 블록 조립 키트
1️⃣ 복잡한 작업을 자동화
2️⃣ 코드 간소화
3️⃣ 유연성과 확장성
4️⃣ 표준화된 설계 방식
1️⃣ IoC (Inversion of Control)
2️⃣ DI (Dependency Injection)
3️⃣ MVC 패턴 지원
4️⃣ AOP (Aspect-Oriented Programming)
UserService userService = new UserService();
DatabaseConnection dbConnection = new DatabaseConnection();
userService.setDatabaseConnection(dbConnection);
userService.processRequest(request);
@Autowired
private UserService userService;
userService.processRequest(request);
프레임워크와 API는 비슷하게 들리지만, 사용 방식과 역할에서 차이가 있습니다.
| 구분 | 프레임워크 | API |
|---|---|---|
| 정의 | 코드 구조와 흐름을 미리 정의하고, 개발자가 이를 따라 작업하도록 제공되는 틀 | 외부 소프트웨어와의 상호작용을 가능하게 하는 인터페이스 |
| 제어 흐름 | 제어 흐름이 프레임워크에 의해 거꾸로(역전) 적용됨 (IoC) | 개발자가 필요한 기능을 호출 (제어 흐름은 개발자에게 있음) |
| 예시 | 스프링(Spring), 장고(Django) | Java의 HttpClient API, JDBC API |
그러면 각각의 스프링의 기능에 대해 더 자세히 알아봅시다.
IoC(Inversion of Control)는 제어의 흐름을 개발자가 아닌 스프링 컨테이너가 관리하도록 바꾸는 개념입니다.
즉, 객체를 만들고, 연결하고, 없애는 일을 스프링 컨테이너에게 맡긴다고 생각하면 됩니다.
개발자가 직접 객체를 생성하고, 필요한 다른 객체와 연결하며, 객체를 없애는 과정까지 전부 관리해야 했습니다.
스프링에서는 객체를 직접 생성하거나 연결하지 않아도 됩니다.
1️⃣ 클래스 정의
public class OrderService {
private PaymentService paymentService;
}2️⃣ 의존성 설정
@Autowired
private PaymentService paymentService; // OrderService에서 PaymentService가 필요!3️⃣ 컨테이너 관리
OrderService를 바로 사용할 수 있습니다. @Autowired
private OrderService orderService;public class OrderService {
private PaymentService paymentService;
public OrderService() {
this.paymentService = new PaymentService(); // 직접 객체 생성
}
public void processOrder() {
paymentService.pay();
}
}
@Component
public class OrderService {
@Autowired
private PaymentService paymentService; // 스프링이 자동으로 주입
public void processOrder() {
paymentService.pay();
}
}
다음으로는 의존성 주입(DI)에 대해 살펴보도록 하겠습니다.
DI(Dependency Injection)는 객체 간의 의존성을 외부에서 주입하는 방식입니다.
즉, 객체가 필요한 다른 객체(의존성)를 스스로 생성하지 않고, 스프링이 대신 연결해 주는 것을 의미합니다.
의존성 주입의 방식에는 대표적으로 생성자 주입, Setter 주입, 필드 주입 총 3가지 있습니다. 하나씩 살펴봅시다.
1️⃣ 생성자 주입
@Component
public class OrderService {
private final PaymentService paymentService;
@Autowired
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService; // 생성자로 주입
}
}
2️⃣ Setter 주입
@Component
public class OrderService {
private PaymentService paymentService;
@Autowired
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService; // Setter로 주입
}
}
3️⃣ 필드 주입
@Component
public class OrderService {
@Autowired
private PaymentService paymentService; // 필드로 바로 주입
}
결론
쉽게 비유하자면,
DI는 객체를 스스로 생성하지 않고, 스프링이 필요한 객체를 대신 연결해 주어 결합도를 낮추고, 코드 유지보수를 쉽게 만들어 줍니다.
생성자 주입은 객체 생성 시 필요한 모든 의존성을 반드시 제공하도록 강제하기 때문에, 의존성 누락으로 생기는 문제를 컴파일 시점에 방지해줍니다. 따라서, 안전성과 테스트 용이성 측면에서 가장 권장되는 방식입니다!
스프링 빈(Bean)은 스프링 컨테이너에서 관리하는 객체를 의미합니다.
1️⃣ 빈 정의: 스프링 설정 파일(XML, Java Config)이나 어노테이션(@Component, @Bean)으로 정의.
2️⃣ 빈 등록: 컨테이너가 빈을 읽어 생성하고 등록.
3️⃣ 빈 사용: 컨테이너가 요청에 따라 빈을 반환하거나, 자동으로 주입.
스프링에서는 빈을 등록하는 방식이 자동 등록과 명시적 등록으로 나뉩니다.
@Component, @Autowired)스프링이 자동으로 빈을 스캔하고 등록하는 방식입니다.
@Component, @Service, @Repository, @Controller를 붙이면 스프링이 빈으로 등록. @Autowired로 주입.@Component // 빈 자동 등록
public class MyService {
@Autowired // 빈 의존성 자동 주입
private MyRepository repository;
public void execute() {
repository.save();
}
}
🌟 묵시적 빈 정의의 장점
@Configuration, @Bean)개발자가 빈의 등록 과정을 직접 제어하는 방식입니다.
@Configuration을 붙이고, 메서드에 @Bean을 사용하여 빈을 등록. @Configuration // 설정 클래스
public class AppConfig {
@Bean // 명시적 빈 등록
public MyService myService() {
return new MyService(myRepository());
}
@Bean
public MyRepository myRepository() {
return new MyRepository();
}
}
🌟명시적 빈 정의의 장점
| 구분 | 묵시적 빈 정의 (@Component → @Autowired) | 명시적 빈 정의 (`@Configuration → @Bean) |
|---|---|---|
| 사용 방법 | 클래스에 어노테이션(@Component, @Service) 추가 | @Configuration 클래스에서 @Bean 메서드 사용 |
| 장점 | 구조가 간단하고 반복 작업 감소 | 생성 과정을 세밀하게 제어 가능 |
| 단점 | 복잡한 구성에는 적합하지 않음 | 코드가 길어질 수 있음 |
| 추천 상황 | 간단한 서비스, 레포지토리 등록에 적합 | 의존성 설정이나 초기화 작업이 필요한 경우 적합 |
1️⃣ 스프링 빈은 애플리케이션의 주요 구성 요소이며, 컨테이너에서 객체 생성 및 생명 주기를 관리합니다.
2️⃣ 자동 등록은 간단하고 반복적인 작업에 적합하며, 명시적 등록은 복잡한 작업에 적합합니다.
3️⃣ DI와 결합되어, 스프링 빈은 유연성과 유지보수성을 극대화합니다.
서블릿(Servlet)은 웹 애플리케이션에서 클라이언트의 요청(HTTP Request)을 처리하고 응답(HTTP Response)을 생성하는 자바 기반의 컴포넌트입니다.
서블릿은 동적인 웹 페이지를 생성하는 데 사용되며, 클라이언트 요청에 따라 결과를 반환하는 역할을 합니다.
웹 애플리케이션에서 서블릿은 다음과 같은 역할을 합니다:
1. HTTP 요청 수신: 클라이언트가 입력하거나 클릭한 요청을 서버로 전달.
2. 비즈니스 로직 처리: 요청 데이터를 분석하고 필요한 작업 수행.
3. 응답 생성: 처리 결과를 HTML, JSON, XML 등의 형식으로 클라이언트에 반환.

doGet(), doPost() 메서드를 실행. 
Servlet Container(서블릿 컨테이너)는 서블릿을 관리하고 실행하는 환경을 제공합니다. Servlet이 어떤 역할을 수행하는 메뉴얼이라면, Servlet 컨테이너는 해당 메뉴얼을 보고 직접 핸들링한다고 생각하시면 됩니다.
서블릿 컨테이너는 클라이언트의 요청(Request)를 받아주고 응답(Response)할 수 있게, 웹 서버와 Socket으로 통신합니다.
대표적인 서블릿 컨테이너로는 Apache Tomcat이 있으며, 다음과 같은 역할을 수행합니다:
스프링에서 가장 중요한 서블릿은 DispatcherServlet입니다.
DispatcherServlet은 스프링 MVC의 중심으로, 클라이언트의 모든 HTTP 요청을 받아 적절한 Controller로 전달하고 처리 결과를 반환합니다. 이는 Front Controller 패턴을 구현한 서블릿으로, 모든 HTTP 요청을 받는 녀석이에요. 이후, 요청을 적절한 Controller로 전달하고, 로직을 실행한 후 응답을 생성해줍니다.
@Controller
public class MyController {
@GetMapping("/umc")
public String hello(Model model) {
model.addAttribute("message", "UMC Spring Fighting!");
return "greeting";
}
}
/umc URL을 요청하면, DispatcherServlet이 요청을 수신. MyController)를 찾아 hello() 메서드를 호출. greeting)으로 반환. 전체적인 흐름을 이해하기 위해, 서블릿과 스프링 빈 어떤 관계인지 살펴보고 마무리합시다!
서블릿 수신
클라이언트가 보낸 HTTP 요청은 서블릿 컨테이너(Tomcat 등)로 전달되고, 해당 요청이 DispatcherServlet으로 처리됩니다.
DispatcherServlet 역할
요청을 분석하여 적절한 스프링 빈(Controller)을 호출.
스프링 빈 처리
Controller, Service, Repository 계층에서 비즈니스 로직을 수행하고 결과를 반환.
결과 반환
DispatcherServlet이 처리 결과를 클라이언트에 응답.
서블릿: 식당의 매니저
스프링 빈: 식당의 직원
정리하자면,
둘은 역할이 다르지만, 협력하여 클라이언트 요청을 처리하고 애플리케이션의 동작을 구현합니다.
오늘 배운 내용 중 핵심 키워드를 정리해보겠습니다.
스프링의 DI, IoC, DispatcherServlet 개념은 애플리케이션 설계의 복잡성을 줄이고 유지보수를 쉽게 만드는 핵심입니다. 이 기본 개념을 통해, 스프링 기반의 프로젝트를 체계적으로 설계할 수 있습니다.
다음 시간부터는 오늘 배운 스프링부트를 본격적으로 학습하면서 JPA에 대해 배워보도록 하겠습니다.😊