# **서비스 계층과 비즈니스 로직** 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 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 { } ``` **설명:** - `JpaRepository` → `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 createUser(@RequestBody User user) { return ResponseEntity.ok(userService.createUser(user)); } @GetMapping("/{id}") public ResponseEntity 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 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`)을 서비스 계층에서 수행**하여 데이터 무결성을 보장. ✔ **테스트가 용이하도록 서비스 계층을 단독으로 분리**. 서비스 계층을 잘 활용하면, **더 유지보수하기 좋은 코드**를 만들 수 있습니다.