기술과 산업/언어 및 프레임워크
Spring Framework 시리즈 10화 – 실무 예제: 간단한 DI 기반 계산기 시스템 구현
B컷개발자
2025. 5. 29. 16:30
728x90
스프링 핵심 개념인 @Component, @Autowired, @Value, @EventListener 등을 실제 프로젝트에 통합하여 DI 기반 계산기 시스템을 구현해봅니다. 학습한 이론을 코드로 연결하고 구조적으로 정리합니다.
프로젝트 개요
우리는 간단한 계산기 시스템을 만들 겁니다.
사용자가 두 수와 연산자(+, -, *, /)를 입력하면, 계산을 수행하고 결과를 출력하며, 이벤트 리스너를 통해 로그 또는 통계 처리를 따로 분리합니다.
📁 프로젝트 구조
src/
└── main/
├── java/
│ └── com.example.calculator/
│ ├── AppConfig.java
│ ├── Calculator.java
│ ├── OperatorService.java
│ ├── ResultPrinter.java
│ ├── CalculationEvent.java
│ ├── CalculationEventListener.java
│ └── CalculatorApp.java
└── resources/
└── application.properties
1. 설정값 주입 – application.properties
calculator.name=스프링 계산기
2. OperatorService – 연산 수행 컴포넌트
@Component
public class OperatorService {
public double calculate(double a, double b, String op) {
return switch (op) {
case "+" -> a + b;
case "-" -> a - b;
case "*" -> a * b;
case "/" -> b != 0 ? a / b : 0;
default -> throw new IllegalArgumentException("지원하지 않는 연산자입니다.");
};
}
}
3. CalculationEvent – 이벤트 객체
public class CalculationEvent {
private final double a;
private final double b;
private final String operator;
private final double result;
public CalculationEvent(double a, double b, String operator, double result) {
this.a = a;
this.b = b;
this.operator = operator;
this.result = result;
}
// Getter 생략
}
4. Calculator – 주입 구조 구성
@Component
public class Calculator {
private final OperatorService operatorService;
private final ApplicationEventPublisher publisher;
@Value("${calculator.name}")
private String name;
public Calculator(OperatorService operatorService, ApplicationEventPublisher publisher) {
this.operatorService = operatorService;
this.publisher = publisher;
}
public void calculate(double a, double b, String op) {
System.out.println("[" + name + "] 연산 수행: " + a + " " + op + " " + b);
double result = operatorService.calculate(a, b, op);
publisher.publishEvent(new CalculationEvent(a, b, op, result));
}
}
5. ResultPrinter – 이벤트 리스너를 통한 결과 처리
@Component
public class ResultPrinter {
@EventListener
public void handleResult(CalculationEvent event) {
System.out.println("결과: " + event.getResult());
}
}
6. AppConfig – 설정 클래스
@Configuration
@ComponentScan(basePackages = "com.example.calculator")
public class AppConfig {}
7. CalculatorApp – 실행 메인 클래스
public class CalculatorApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Calculator calculator = context.getBean(Calculator.class);
calculator.calculate(5, 3, "+");
calculator.calculate(10, 2, "/");
calculator.calculate(7, 8, "*");
}
}
실행 결과 예시
[스프링 계산기] 연산 수행: 5.0 + 3.0
결과: 8.0
[스프링 계산기] 연산 수행: 10.0 / 2.0
결과: 5.0
[스프링 계산기] 연산 수행: 7.0 * 8.0
결과: 56.0
이 예제를 통해 확인한 개념 정리
적용 기능 사용 위치
DI 및 컴포넌트 주입 | @Component, @Autowired, 생성자 주입 |
외부 설정값 주입 | @Value, application.properties |
ApplicationContext 구성 | AnnotationConfigApplicationContext |
이벤트 퍼블리싱 | ApplicationEventPublisher 사용 |
이벤트 리스닝 | @EventListener 로 결과 처리 |
마무리 – 실전은 연결이다
이전 회차까지 개별적으로 배운 기능들을 하나의 프로젝트로 통합해 보았습니다.
의존성 주입, 설정 분리, 이벤트 처리 등은 개념 그 자체보다 서로 어떻게 연결되고 설계되는지가 실무 핵심입니다.
이 계산기 프로젝트처럼 작고 명확한 구조를 설계하면서 핵심 요소를 조립해보면, 더 복잡한 서비스에서도 유사한 패턴으로 확장할 수 있습니다.
다음 11화에서는 테스트 가능한 구조 만들기를 주제로, 이 프로젝트를 단위 테스트 및 통합 테스트 가능한 구조로 리팩토링하고, @TestConfiguration, @MockBean 등을 활용해 실전 테스트 전략을 소개할 예정입니다.
728x90