[TIL] 자바 Controller, Service, Repository 구조 (1)

냠냠빈·2024년 11월 26일

자바의 Controller, Service, Repository 구조에 대한 이해

Java 기반 애플리케이션 개발에서 Controller-Service-Repository 구조는 레이어드 아키텍처(Layered Architecture)의 대표적인 형태다. 이 구조는 각 계층의 역할을 명확히 구분하여 유지보수성을 향상시키고 코드의 가독성을 높이는 데 목적이 있다.

  • 레이어드 아키텍쳐 : 애플리케이션을 역할별로 계층화하여 관리하는 소프트웨어 설계 방식


Controller, Service, Repository 구조의 비유

  1. Controller (점원)
  • 사용자의 요청을 받아 적절한 서비스를 호출한다.
  • 요청(Request)과 응답(Response) 처리를 담당한다.
  • 사용자와 애플리케이션 간의 접점을 관리한다.
  1. Service (요리사)
  • 순수 비즈니스 로직을 처리한다.
  • Controller에서 받은 요청을 분석하여 필요한 작업을 수행한다.
  • 데이터를 직접 다루지 않고 Repository를 통해 필요한 데이터를 가져온다.
  1. Repository (재료담당 직원)
  • 데이터베이스(DB)나 파일 시스템 등과 상호작용한다.
  • 데이터를 생성, 조회, 수정, 삭제하는 작업을 수행한다.

1. Controller

역할:

  • 사용자 요청을 받아 서비스 계층에 전달.
  • 서비스 계층에서 반환된 결과를 사용자가 이해할 수 있는 형태로 가공.
  • 일반적으로 HTTP 요청/응답과 관련된 작업을 수행.

작성 방법:

  • Spring MVC 기준으로 @RestController 또는 @Controller 애노테이션 사용.

  • 요청 URL과 메서드를 매핑하기 위해 @RequestMapping 또는 @GetMapping, @PostMapping 등을 사용.

    @RestController
    @RequestMapping("/orders")
    public class OrderController {
    
        private final OrderService orderService;
    
        public OrderController(OrderService orderService) {
            this.orderService = orderService;
        }
    
        @PostMapping
        public ResponseEntity<String> createOrder(@RequestBody OrderRequest orderRequest) {
            orderService.createOrder(orderRequest);
            return ResponseEntity.ok("Order created successfully!");
        }
    
        @GetMapping("/{id}")
        public ResponseEntity<OrderResponse> getOrder(@PathVariable Long id) {
            OrderResponse order = orderService.getOrderById(id);
            return ResponseEntity.ok(order);
        }
    }

특징:

  • 사용자 입력 검증 (e.g., @RequestParam, @RequestBody 사용).
  • 입력 데이터를 서비스로 전달하고 응답 데이터를 사용자에게 반환.
  • 비즈니스 로직은 작성하지 않음.

주의사항:

  • 비즈니스 로직을 포함하지 말고, 서비스 계층으로 분리.
  • 컨트롤러는 단순히 "요청-응답"의 연결 고리 역할만 수행해야 함.

2. Service

역할:

  • 비즈니스 로직을 처리하는 핵심 계층.
  • Controller에서 받은 데이터를 기반으로 작업을 수행하고 결과를 반환.
  • 데이터베이스와 직접 통신하지 않고 Repository 계층을 사용하여 데이터를 조회하거나 조작.

작성 방법:

  • Spring에서는 @Service 애노테이션을 사용.

  • 데이터를 처리하는 로직을 작성하고, Repository를 호출하여 데이터를 저장하거나 불러옴.

    @Service
    public class OrderService {
    
        private final OrderRepository orderRepository;
    
        public OrderService(OrderRepository orderRepository) {
            this.orderRepository = orderRepository;
        }
    
        public void createOrder(OrderRequest orderRequest) {
            Order order = new Order(orderRequest.getItemName(), orderRequest.getQuantity());
            orderRepository.save(order); // Repository를 통해 DB에 저장
        }
    
        public OrderResponse getOrderById(Long id) {
            Order order = orderRepository.findById(id)
                                         .orElseThrow(() -> new RuntimeException("Order not found"));
            return new OrderResponse(order.getId(), order.getItemName(), order.getQuantity());
        }
    }

특징:

  • 데이터 조작, 계산, 처리 등 비즈니스 로직을 담당.
  • Repository를 호출하여 필요한 데이터를 처리.
  • 입력/출력 관련 작업은 하지 않음.

주의사항:

  • 단일 책임 원칙(SRP)을 지켜야 함: 각 메서드는 하나의 작업만 처리.
  • 컨트롤러에서 서비스 계층을 호출할 때 의존성 주입(DI)을 활용.

3. Repository

역할:

  • 데이터베이스와 직접 통신하여 데이터를 생성, 조회, 수정, 삭제.
  • JPA, MyBatis, JDBC 등과 같은 데이터 접근 기술을 사용.

작성 방법:

  • Spring Data JPA 기준으로 @Repository 애노테이션을 사용.
  • Spring Boot에서는 자동으로 인터페이스만 작성하면 구현체를 생성해줌.
    @Repository
    public interface OrderRepository extends JpaRepository<Order, Long> {
        // JpaRepository를 상속받아 기본적인 CRUD 메서드 사용 가능
    }

특징:

  • 데이터와 직접 상호작용.
  • 비즈니스 로직은 포함하지 않음.
  • 필요한 데이터만 반환.

주의사항:

  • 데이터베이스 관련 로직을 제외한 비즈니스 로직은 작성하지 말 것.
  • 복잡한 쿼리가 필요한 경우 쿼리 메서드 또는 @Query 애노테이션 활용.

개발 순서

1. Repository 작성:

  • 데이터를 다룰 인터페이스를 작성하고, 기본적인 CRUD 작업이 가능하도록 설정.

2. Service 작성:

  • 비즈니스 로직을 구현하고, 필요한 경우 Repository를 호출하여 데이터를 처리.

3. Controller 작성:

  • 사용자 요청을 받고, Service를 호출하여 처리 결과를 반환.

Controller-Service-Repository 구조의 특징과 장점

  1. 역할 분리:
    • 각 계층이 맡은 역할이 명확하므로 코드가 깔끔하고 유지보수가 용이.
  2. 확장성:
    • 한 계층의 코드를 수정해도 다른 계층에는 영향을 미치지 않음.
  3. 테스트 용이성:
    • 각 계층을 독립적으로 테스트할 수 있음.
  4. 재사용성:
    • Service와 Repository 계층의 로직은 다른 Controller에서도 쉽게 재사용 가능.

실제 예제: 주문 시스템

점원의 역할 (Controller):

@PostMapping
public ResponseEntity<String> createOrder(@RequestBody OrderRequest orderRequest) {
    orderService.createOrder(orderRequest); // 요리사에게 요청
    return ResponseEntity.ok("Order created successfully!");
}

요리사의 역할 (Service):

public void createOrder(OrderRequest orderRequest) {
    Order order = new Order(orderRequest.getItemName(), orderRequest.getQuantity());
    orderRepository.save(order); // 재료담당 직원에게 요청
}

재료담당 직원의 역할 (Repository):

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {}

작성 시 주의할 점

  1. 역할 구분을 엄격히 유지:

    • Controller: 요청과 응답만.
    • Service: 비즈니스 로직만.
    • Repository: 데이터 처리만.
  2. 의존성 주입(DI) 사용:

    • 각 계층 간 의존성을 생성자 주입 방식으로 관리.
  3. 비즈니스 로직 중복 방지:

    • Service 계층에 모든 로직을 작성해 Controller나 Repository에서 중복되지 않도록 함.

여기서 레이어드 아키택쳐나 Spring MVC 애노테이션, 사용자 입력 검증 등에 대해 잘 모르겠으니 2탄에서 이어서 공부해 보겠다.

profile
다 먹어버릴거야!

0개의 댓글