Spring
1. Spring?
- 스프링은 자바 언어 기반의 프레임워크
- 자바 언어의 가장 큰 특징 - 객체 지향 언어
- 스프링은 객체 지향 언어가 가진 강력한 특징을 살려내는 프레임워크
- 스프링은 좋은 객체 지향 애플리케이션을 개발할 수 있게 도와주는 프레임워크
로드 존슨이 EJB 불편함을 극복하기 위해 만든 프레임워크입니다.
2. 스프링의 주요 목표
- 경량화: 필요 없는 기능을 최소화하여 가볍게 동작
- POJO 기반 개발: 일반적인 자바 객체(Plain Old Java Object)를 기반으로 개발
- DI(의존성 주입): 객체 간 의존성을 설정 파일이나 주석(annotation)을 통해 관리
- AOP(관점 지향 프로그래밍): 로깅, 보안 등과 같은 부가 기능을 쉽게 추가
- 모듈화: 필요에 따라 원하는 기능만 사용할 수 있는 구조
3. 스프링의 주요 특징
스프링은 다양한 특징을 통해 개발자가 생산성을 높이고 효율적으로 코드를 작성할 수 있도록 돕습니다.
3.1. DI (Dependency Injection) - 의존성 주입
- 객체 간의 의존 관계를 코드가 아닌 설정으로 관리합니다.
- 설정 파일(XML 또는 Java Config)이나 어노테이션(@Autowired)을 사용하여 객체 간의 결합을 줄입니다.
@Component
public class Car {
private Engine engine;
@Autowired
public Car(Engine engine) {
this.engine = engine;
}
}
3.2. AOP (Aspect Oriented Programming) - 관점 지향 프로그래밍
핵심 비즈니스 로직과 부가 기능(로깅, 보안, 트랜잭션)을 분리하여 관리합니다.
Advice(어드바이스)와 Pointcut(포인트컷)을 통해 코드의 중복을 줄입니다.
3.3. 트랜잭션 관리
데이터베이스 트랜잭션을 쉽게 관리할 수 있도록 지원합니다.
@Transactional 어노테이션을 통해 코드에 최소한의 설정만으로 트랜잭션을 처리할 수 있습니다.
3.4. 테스트 용이성
테스트 코드 작성을 쉽게 할 수 있도록 DI를 기반으로 유닛 테스트를 지원합니다.
Mock 객체를 쉽게 사용할 수 있습니다.
3.5. 모듈화와 확장성
스프링은 여러 모듈로 구성되어 있습니다. 원하는 기능만 골라 사용할 수 있습니다.
4. 스프링의 주요 구성 요소
스프링은 여러 모듈로 나뉘어 있으며, 각 모듈은 특정 기능을 담당합니다.
4.1. 스프링 코어(Core)
- DI와 IoC(Inversion of Control) 컨테이너를 제공
- 객체 생성 및 의존성 관리를 담당
4.2. 스프링 AOP
- 관점 지향 프로그래밍 기능을 지원
4.3. 스프링 데이터(Spring Data)
- 데이터 접근 계층을 쉽게 구현할 수 있는 도구 제공
- JPA, MongoDB, Redis 등과의 통합 지원
4.4. 스프링 웹(Spring Web)
- 웹 애플리케이션 개발에 필요한 기능 제공
- HTTP 요청/응답 처리 및 MVC 패턴을 지원
4.5. 스프링 부트(Spring Boot)
- 스프링 애플리케이션을 더 간단하고 빠르게 개발할 수 있는 도구
- 내장 웹 서버(Tomcat, Jetty 등)와 설정 자동화 기능 제공
5. 스프링 부트 (Spring Boot)
스프링 부트는 스프링 프레임워크를 더 쉽고 간단하게 사용할 수 있도록 만든 확장 프레임워크입니다.
스프링 부트의 장점
- 내장된 웹 서버 제공 (Tomcat, Jetty 등)
- 복잡한 설정 없이 기본값으로 빠르게 실행 가능
- Spring Initializr (스프링 스타터)을 통해 프로젝트 초기 설정을 간소화
@SpringBootApplication
public class SpringBootExample {
public static void main(String[] args) {
SpringApplication.run(SpringBootExample.class, args);
}
}
6. 스프링 MVC 패턴
스프링은 MVC(Model-View-Controller) 아키텍처를 지원하여 웹 애플리케이션 개발을 쉽게 만듭니다.
- Model : 데이터를 관리하는 계층 (Service, DAO 등)
- View : 사용자에게 보여지는 화면 (HTML, JSP 등)
- Controller : 요청을 처리하고 적절한 응답을 생성
@Controller
public class MyController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello, Spring!");
return "hello"; // View 이름
}
}
좋은 객체 지향 프로그래밍이란?
1. 객체 지향 특징
- 추상화
- 캡슐화
- 상속
- 다형성
2. 객체 지향 프로그래밍
- 객체 지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위,
즉 "객체"들의 모임으로 파악하고자 합니다.
- 각각의 객체는 메시지를 주고 받고, 데이터를 처리할 수 있습니다.
- 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용됩니다.
한마디로, 컴포넌트를 쉽고 유연하게 변경하면서 개발할 수 있습니다.
3. 다형성의 실세계 비유 예시
- 운전자 - 자동차
- 공연 무대
- 키보드, 마우스, 세상의 표준 인터페이스들
- 정렬 알고리즘
- 할인 정책 로직
4. 역할과 구현을 분리
역할과 구현으로 구분하면 세상이 단순해지고, 유연해지며 변경도 편리해집니다.
- 실세계의 역할과 구현이라는 편리한 컨셉을 다형성을 통해 객체 세상으로 가져올 수 있음
- 유연하고 변경이 용이
- 확장 가능한 설계
- 클라이언트에 영향을 주지 않는 변경 가능
- 인터페이스를 안정적으로 잘 설계하는 것이 중요
자바 언어
자바 언어의 다형성을 활용
> 역할 = 인터페이스
> 구현 = 인퍼테이스를 구현한 클래스, 구현 객체
객체를 설계할 때 역할과 구현을 명확히 분리
객체 설계시 역할(인터페이스)을 먼저 부여하고, 그 역할을 수행하는 구현 객체 만들기
5. 다형성의 본질
- 인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경할 수 있습니다.
- 다형성의 본질을 이해하려면 협력이라는 객체 사이의 관계에서 시작해야합니다.
- 클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있습니다.
6. 스프링과 객체 지향
- 다형성이 가장 중요합니다.
- 스프링은 다형성을 극대화해서 이용할 수 있게 도와줍니다.
- 스프링에서 이야기하는 제어의 역전(IoC), 의존관계 주입(DI)은 다형성을 활용해서 역할과 구현을 편리하게 다룰 수 있도록 지원합니다.
- 스프링을 사용하면 구현을 편리하게 변경할 수 있습니다.
좋은 객체 지향 설계의 5가지 원칙(SOLID)
1. SOLID
- SRP : 단일 책임 원칙 (single responsibility principle)
- OCP : 개방-폐쇄 원칙 (Open/closed principle)
- LSP : 리스코프 치환 원칙 (Liskov substitution principle)
- ISP : 인터페이스 분리 원칙 (Interface segregation principle)
- DIP : 의존관계 역전 원칙 (Dependency inversion principle)
1.1 SRP 단일 책임 원칙
- 한 클래스는 하나의 책임만 가져야 합니다.
- 하나의 책임이라는 것은 모호합니다.
- 클 수 있고, 작을 수 있습니다.
- 문맥과 상황에 따라 다릅니다.
- 중요한 기준은 변경입니다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것입니다.
1.2 OCP 개방-폐쇄 원칙
- 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 합니다.
- 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구협합니다.
1.3 LSP 리스코프 치환 원칙
- 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 합니다.
- 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 합니다.
- 다형성을 지원하기 위한 원칙, 인터페이스를 구현한 구현체를 믿고 사용하려면 이 원칙이 필요합니다.
- 단순히 컴파일에 성공하는 것을 넘어서는 이야기
1.4 ISP 인터페이스 분리 원칙
- 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫습니다.
- 자동차 인터페이스 -> 운전 인터페이스, 정비 인터페이스로 분리
- 사용자 클라이언트 -> 운전자 클라이언트, 정비사 클라이언트로 분리
- 분리하면 정비 인터페이스 자체가 변해도 운전자 클라이언트에 영향을 주지 않습니다.
- 인터페이스가 명확해지고, 대체 가능성이 높아집니다.
1.5 DIP 의존관계 역전 원칙
- 프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안된다." 의존성 주입은 이 원칙을 따르는 방법 중 하나입니다.
- 쉽게 이야기해서 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻
- 역할에 의존하게 해야 한다는 것과 같습니다.
객체 세상도 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있습니다.
구현체에 의존하게 되면 변경이 아주 어려워집니다.
정리
- 객체 지향의 핵심은 다형성 !!
- 다형성 만으로는 쉽게 부품을 갈아 끼우듯이 개발할 수 없습니다.
- 다형성 만으로는 구현 객체를 변경할 때 클라이언트 코드도 함께 변경됩니다.
- 다형성 만으로는 OCP, DIP를 지킬 수 없습니다.
객체 지향 설계와 스프링
스프링 이야기에 왜 객체 지향 이야기가 나올까?
스프링(Spring) 프레임워크를 이해하다 보면 객체 지향 프로그래밍(OOP)에 대한 이야기가 빠지지 않고 등장합니다.
그 이유는 스프링이 객체 지향 설계의 원칙을 기반으로 설계되었기 때문입니다.
객체 지향의 강점을 활용하여 개발자가 더 유연하고 확장 가능한 코드를 작성할 수 있도록 돕는 것이 스프링의 주요 목표 중 하나입니다.
아래에서 스프링과 객체 지향 설계가 어떻게 연결되는지, 그리고 스프링이 이를 어떻게 지원하는지 자세히 설명하겠습니다.
1. 객체 지향 설계의 핵심: OCP와 DIP
객체 지향 설계의 주요 원칙 중 Open-Closed Principle(OCP, 개방-폐쇄 원칙)과 Dependency Inversion Principle(DIP, 의존성 역전 원칙)은 스프링이 지향하는 설계 철학과 깊이 연관되어 있습니다.
OCP (Open-Closed Principle)
- 정의: 소프트웨어 구성 요소는 "확장에는 열려 있고, 변경에는 닫혀 있어야 한다."
- 설명: 기존 코드를 수정하지 않고도 애플리케이션의 기능을 확장할 수 있어야 합니다.
- 스프링과의 연결: 스프링은 다형성과 의존성 주입(DI)을 활용해 OCP를 실현합니다. 새로운 기능을 추가하거나 기존 기능을 변경할 때 클라이언트 코드를 수정하지 않아도 되는 구조를 제공합니다.
DIP (Dependency Inversion Principle)
- 정의: 고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 둘 다 추상화에 의존해야 한다.
- 설명: 구현체가 아닌 인터페이스에 의존함으로써 코드의 결합도를 낮추고 유연성을 높이는 원칙입니다.
- 스프링과의 연결: 스프링은 DI(Dependency Injection)를 통해 DIP를 자연스럽게 구현할 수 있도록 돕습니다. 구체적인 구현 클래스가 아닌 추상화된 인터페이스를 기반으로 의존성을 주입받도록 설계할 수 있습니다.
2. 스프링의 기술: 다형성과 OCP, DIP의 실현
스프링은 아래와 같은 기술을 통해 객체 지향 설계 원칙을 효과적으로 지원합니다.
2.1 DI(Dependency Injection): 의존성 주입
DI는 객체 간의 의존 관계를 코드가 아닌 설정을 통해 외부에서 주입하는 방식입니다. 이를 통해 다음과 같은 효과를 얻을 수 있습니다
- 결합도 감소: 객체가 스스로 의존 객체를 생성하지 않으므로, 구현체를 변경해도 클라이언트 코드를 수정할 필요가 없습니다.
- 유연한 확장: 새로운 구현체를 쉽게 교체하거나 추가할 수 있습니다.
예를 들어,DI 없이 객체를 생성하면 다음과 같습니다
public class OrderService {
private PaymentService paymentService = new CreditCardPaymentService();
}
위 코드는 CreditCardPaymentService에 강하게 결합되어 있기 때문에 다른 결제 방식을 추가하려면 코드를 수정해야 합니다.
DI를 사용하면 다음과 같이 작성할 수 있습니다.
@Component
public class OrderService {
private final PaymentService paymentService;
@Autowired
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
위 코드에서는 PaymentService 인터페이스를 사용하므로, 구현체가 무엇인지 상관없이 동작합니다. 이를 통해 OCP와 DIP를 실현할 수 있습니다.
2.2 DI 컨테이너 제공
스프링은 DI 컨테이너(ApplicationContext)를 통해 객체의 생성과 의존성 주입을 관리합니다. 개발자는 객체 생성과 주입을 컨테이너에 위임함으로써 복잡한 객체 관계를 손쉽게 관리할 수 있습니다.
2.3 쉽게 부품을 교체하듯이 개발
- 유연한 구조: DI와 스프링 컨테이너를 통해 구현체를 간단히 교체하거나 추가할 수 있습니다.
- 테스트 용이성: Mock 객체를 주입하여 유닛 테스트를 쉽게 작성할 수 있습니다.
정리
- OCP와 DIP를 통해 확장 가능하고 유지 보수가 쉬운 코드를 작성하도록 돕습니다.
- DI와 AOP 같은 기술로 객체 지향의 강점을 극대화합니다.
- 클라이언트 코드의 변경 없이 새로운 기능을 추가하거나 기존 기능을 대체할 수 있는 유연성을 제공합니다.
전체 요약
스프링 프레임워크는 자바의 객체 지향적 특성을 최대한 활용하여 유연하고 확장 가능한 애플리케이션 개발을 가능하게 합니다.
DI와 AOP와 같은 핵심 기능을 통해 SOLID 원칙, 특히 개방-폐쇄 원칙(OCP)과 의존관계 역전 원칙(DIP)을 자연스럽게 구현할 수 있어 코드의 유지 보수성과 재사용성을 크게 향상시킵니다.
스프링 부트와 같은 도구들은 개발 과정을 더욱 단순화하고 효율적으로 만들어, 개발자가 비즈니스 로직에 집중할 수 있도록 돕습니다.
객체 지향 설계의 강점을 바탕으로 스프링을 활용하면, 변화하는 요구사항에도 유연하게 대응할 수 있는 견고한 애플리케이션을 구축할 수 있습니다.
'개발일지 > Spring' 카테고리의 다른 글
객체지향원칙을 고려한 Spring 테스트 설계 전략 (JUnit) (0) | 2025.02.06 |
---|---|
Spring 테스트 케이스 작성 방법과 팁 (JUnit) (2) | 2025.02.06 |
Thymeleaf(타임리프) 템플릿엔진 ? (0) | 2025.02.06 |
Spring 중요한 라이브러리 (0) | 2025.02.06 |
Spring 프로젝트 기본 환경 설정 - Intellij (0) | 2025.02.06 |