2025-04-08T19:56:24

This commit is contained in:
2025-04-08 19:56:24 +09:00
parent a75a1dbd0f
commit eef061c1c9
100 changed files with 18639 additions and 0 deletions

View File

@@ -0,0 +1,213 @@
# **서비스 계층과 비즈니스 로직**
Spring Boot 애플리케이션을 설계할 때, 코드의 **재사용성, 유지보수성, 테스트 용이성**을 높이려면 **비즈니스 로직을 서비스 계층(Service Layer)에 분리**하는 것이 중요합니다.
이 글에서는 **서비스 계층의 역할, 설계 방식, 그리고 예제 코드**를 통해 비즈니스 로직을 어떻게 다루어야 하는지 설명합니다.
---
## **1. 서비스 계층이란?**
Spring Boot 애플리케이션은 일반적으로 **MVC (Model-View-Controller) 구조**를 따릅니다.
이때 **서비스 계층은 컨트롤러와 데이터 계층(Repository) 사이에서 비즈니스 로직을 처리하는 역할**을 합니다.
### **📌 계층별 역할**
| 계층 | 역할 |
|------|------|
| **Controller (컨트롤러 계층)** | 사용자 요청을 받아 서비스 계층에 전달 |
| **Service (서비스 계층)** | 비즈니스 로직을 처리하고 트랜잭션을 관리 |
| **Repository (데이터 계층)** | 데이터베이스와 직접적인 통신을 담당 |
---
## **2. 서비스 계층의 필요성**
### **📌 컨트롤러에서 직접 로직을 처리하는 문제점**
```java
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserRepository userRepository; // 데이터 계층 직접 접근
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
if (user.getName() == null || user.getAge() < 0) {
throw new IllegalArgumentException("Invalid user data");
}
return ResponseEntity.ok(userRepository.save(user)); // 비즈니스 로직이 컨트롤러에 있음
}
}
```
**🚨 문제점:**
1. **컨트롤러가 너무 많은 책임을 가짐** → 가독성과 유지보수성이 떨어짐.
2. **비즈니스 로직이 중복될 가능성 증가** → 여러 컨트롤러에서 같은 로직을 작성할 가능성이 큼.
3. **테스트가 어려움** → 서비스 계층이 없으면 컨트롤러 단위 테스트가 복잡해짐.
---
## **3. 서비스 계층을 활용한 개선된 설계**
### **📌 1) Entity (데이터 모델)**
```java
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
// 기본 생성자 및 getter, setter
public User() {}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
```
### **📌 2) Repository (데이터 계층)**
```java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
```
**설명:**
- `JpaRepository<User, Long>``User` 엔티티를 다루는 JPA 리포지토리.
- `findById(id)`, `save(entity)`, `deleteById(id)` 등 기본적인 DB 연산 제공.
---
### **📌 3) Service (비즈니스 로직 계층)**
```java
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public User createUser(User user) {
if (user.getName() == null || user.getAge() < 0) {
throw new IllegalArgumentException("Invalid user data");
}
return userRepository.save(user);
}
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
}
```
**🚀 개선된 점:**
**컨트롤러에서 비즈니스 로직이 제거됨** → 역할이 명확해짐.
**비즈니스 로직이 서비스 계층에 집중됨** → 재사용성 증가.
**트랜잭션 관리 가능**`@Transactional`을 활용하여 데이터 일관성 유지.
---
### **📌 4) Controller (컨트롤러 계층)**
```java
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
return ResponseEntity.ok(userService.createUser(user));
}
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
}
```
**🚀 컨트롤러가 깔끔해짐!**
✔ 비즈니스 로직을 서비스 계층에 위임.
✔ 컨트롤러는 단순히 요청을 받고 응답을 반환하는 역할만 수행.
---
## **4. 서비스 계층에서 트랜잭션 관리**
### **📌 트랜잭션 (`@Transactional`)이란?**
- 여러 개의 DB 연산을 하나의 단위로 묶어 **"모두 성공하거나, 하나라도 실패하면 롤백"**하도록 보장.
- `@Transactional`을 붙이면, **예외가 발생할 경우 자동으로 롤백**됨.
### **📌 예제: 트랜잭션을 적용한 서비스**
```java
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void registerUsers(List<User> users) {
for (User user : users) {
if (user.getAge() < 0) {
throw new RuntimeException("Invalid age");
}
userRepository.save(user);
}
}
}
```
**🚀 특징:**
- 만약 `user.getAge() < 0`인 사용자가 있다면, **이전까지 저장된 사용자도 롤백됨**.
- 데이터 무결성을 보장할 수 있음.
---
## **5. 서비스 계층을 활용한 테스트**
### **📌 JUnit을 활용한 서비스 계층 테스트**
```java
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testCreateUser() {
User user = new User("Alice", 25);
User savedUser = userService.createUser(user);
assertNotNull(savedUser);
assertEquals("Alice", savedUser.getName());
}
}
```
**🚀 서비스 계층이 분리되어 있어, 테스트가 쉬워짐!**
`UserService`만 단독으로 테스트 가능.
`UserRepository`가 직접 드러나지 않으므로, Mocking(가짜 데이터)도 쉽게 적용 가능.
---
## **6. 정리**
| 계층 | 설명 |
|------|------|
| **Controller** | 요청을 받고 응답을 반환 (비즈니스 로직 X) |
| **Service** | 비즈니스 로직 처리 및 트랜잭션 관리 |
| **Repository** | 데이터베이스와 직접 통신 |
### **✅ 좋은 서비스 계층 설계 원칙**
✔ **컨트롤러는 서비스에 로직을 위임**하고, 직접 처리하지 않음.
✔ **비즈니스 로직은 서비스 계층에서 일괄적으로 관리**하여 재사용성을 높임.
✔ **트랜잭션 관리 (`@Transactional`)을 서비스 계층에서 수행**하여 데이터 무결성을 보장.
**테스트가 용이하도록 서비스 계층을 단독으로 분리**.
서비스 계층을 잘 활용하면, **더 유지보수하기 좋은 코드**를 만들 수 있습니다.