제가 생각하는 가장 베스트 프랙티스는 항상 책임의 분리(Separation of Concerns)를 고려하고, 코드가 각 계층의 역할을 명확하게 수행하는지 확인하는 것입니다. 그래서 이 질문을 좀 더 깊이 파고들어 실제로 최적의 구현 방법을 설명드리겠습니다.
컨트롤러는 기본적으로 요청을 받아 해당 요청에 대한 적절한 서비스 호출을 수행하고, 그 결과를 응답으로 반환하는 역할을 합니다. 컨트롤러는 비즈니스 로직에 대해 많은 책임을 지지 않아야 하며, 이는 서비스 레이어에서 처리해야 합니다.
서비스는 애플리케이션의 비즈니스 로직을 처리하는 핵심 레이어입니다. 여기서 사용자 정보를 조회하고, 투두와 관련된 복잡한 비즈니스 로직을 처리하는 것이 맞습니다. 서비스는 컨트롤러에 의해 호출되며, 각종 데이터베이스 작업과 비즈니스 로직이 여기서 처리되어야 합니다.
userService.getUserById() 같은 호출이 서비스 안에서 이루어져야 하는 이유는 서비스는 비즈니스 로직을 처리하는 곳이고, 컨트롤러는 요청을 위임하는 역할에 충실해야 하기 때문입니다. 이를 통해 중복된 코드와 복잡성을 줄일 수 있습니다.
@Service
public class TodoService {
@Autowired
private TodoRepository todoRepository;
@Autowired
private UserService userService; // 사용자 정보를 조회하는 로직은 서비스에서 처리
// 현재 사용자를 기반으로 Todo 생성
public Todo createTodoForCurrentUser(Todo todo) {
Long userId = SecurityUtil.getCurrentUserId(); // SecurityUtil을 통해 현재 사용자 ID 가져오기
User user = userService.getUserById(userId); // UserService를 통해 사용자 정보 조회
todo.setUser(user); // 생성된 Todo에 User 설정
return todoRepository.save(todo); // 저장
}
// 특정 사용자의 Todo 리스트를 가져오기
public List<Todo> getTodosByUser(Long userId) {
return todoRepository.findByUserId(userId); // 사용자의 Todo를 조회
}
}
@RestController
@RequestMapping("/api/todos")
public class TodoController {
@Autowired
private TodoService todoService;
// 사용자의 Todo 리스트를 조회
@GetMapping
public List<Todo> getUserTodos() {
Long userId = SecurityUtil.getCurrentUserId(); // SecurityUtil을 통해 사용자 ID 가져오기
return todoService.getTodosByUser(userId); // 서비스에 위임하여 Todo 리스트 조회
}
// 새로운 Todo 항목을 생성
@PostMapping
public ResponseEntity<Todo> createTodo(@RequestBody Todo todo) {
Todo createdTodo = todoService.createTodoForCurrentUser(todo); // 서비스에 위임하여 Todo 생성
return new ResponseEntity<>(createdTodo, HttpStatus.CREATED); // 생성된 Todo 반환
}
}
컨트롤러는 최소한의 로직만 처리: 컨트롤러는 단지 요청을 받아 적절한 서비스에 위임하고, 그 결과를 반환하는 역할을 합니다. 이렇게 하면 컨트롤러는 테스트하기 쉬워지고, 코드가 간결해집니다.
서비스가 비즈니스 로직을 담당: 비즈니스 로직은 서비스 레이어에 집중되어야 합니다. 여기서는 사용자 정보를 조회하고 투두 리스트를 처리하는 등 로직을 한 곳에서 처리하여, 비즈니스 로직의 변경이나 확장이 쉬워집니다.
재사용성과 유지보수성 향상: 이 구조는 서비스 레이어에서의 코드 재사용성을 높이고, 각 계층의 책임을 분명히 함으로써 유지보수와 확장성이 좋아집니다. 새로운 기능이 추가되거나 변경될 때, 해당 레이어만 수정하면 되므로 다른 부분에 미치는 영향을 최소화할 수 있습니다.
컨트롤러는 요청을 처리하고 응답을 반환하는 데 집중해야 하며, 비즈니스 로직은 서비스 레이어에서 처리하는 것이 베스트 프랙티스입니다. 이를 통해 코드는 더 읽기 쉽고 유지보수하기 쉬운 구조가 됩니다.