# **서비스 계층과 비즈니스 로직**  

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`)을 서비스 계층에서 수행**하여 데이터 무결성을 보장.  
✔ **테스트가 용이하도록 서비스 계층을 단독으로 분리**.  

서비스 계층을 잘 활용하면, **더 유지보수하기 좋은 코드**를 만들 수 있습니다.