MVC 패턴이란 ? 

 

모델-뷰-컨트롤러 - 위키백과, 우리 모두의 백과사전

모델, 뷰, 컨트롤러의 관계를 묘사하는 간단한 다이어그램. 웹 애플리케이션에서 일반적인 MVC 구성요소 다이어그램 모델-뷰-컨트롤러(model–view–controller, MVC)는 소프트웨어 공학에서 사용되는

ko.wikipedia.org

 

MVC 패턴의 구조는 Model, View, Controller 세 가지로 나누어진다.

  • 모델은 어떠한 동작을 수행하는 코드를 이야기한다. 우리가 무엇을 할지를 정하고, 비즈니스 로직과 DB 등에 대한 처리를 수행한다.
  • 컨트롤러는 뷰와 모델을 이어주는 역할을 맡는다. 뷰의 요청에 따라 모델의 상태를 바꾸고, 이를 다시 뷰에 전달한다.
  • 뷰는 사용자에게 보이는 영역이다. 컨트롤러를 통해 모델에 질의를 보내고, 그 값을 사용자에게 적절하게 보여준다.

스프링의 웹 계층

위의 이미지에서 보듯, 스프링에는 5가지 요소가 존재한다.

이에 대한 설명은 다음과 같다.

  1. Web Layer
    • 컨트롤러(@Controller)가 대표적이고, 이외에도 필터(@filter), 인터셉터, 컨트롤러 어드바이스 등이 포함된다.
    • 외부 요청과 응답에 대한 전반적인 영역을 의미한다.
  2. Service Layer
    • 말 그대로 서비스(@Service)이다.
    • 일반적으로 컨트롤러와 저장소(Repository, Dao)의 중간에 위치한다.
    • 트랜잭션(@Transactional)과 도메인 간의 연산 순서를 보장해 준다.
  3. Repository Layer
    • DB와 같은 데이터 저장소에 접근하는 영역이다.
    • JPA를 사용한다면 @Repository를 생각하면 된다.
  4. DTOs
    • DTO(Data Transfer Object)는 계층 간의 데이터 교환을 위한 객체를 이야기한다.
  5. Domain model
    • 개발 대상, 즉 도메인을 모든 사람이 동일한 관점에서 이해할 수 있고 공유할 수 있도록 단순화한 것을 도메인 모델이라고 한다.
    • 비즈니스 로직을 처리하는 영역이다.
    • JPA를 사용한다면, @Entity가 사용되는 영역 역시 도메인 영역이라고 생각할 수 있다.

이제 위의 MVC 구조로 돌아가 보자. 나는 이렇게 MVC를 구현하고 있다.

  • 뷰 : 프론트엔드에서 담당한다. 요청을 보내고, 응답을 받아 이를 보여준다.
  • 컨트롤러 : 컨트롤러에서 담당한다. 들어온 요청에 따라 서비스의 적절한 연산을 수행하고, 결괏값을 응답으로 가공하여 보내준다. 또한 인터셉터, 예외 핸들러, 컨트롤러 어드바이스 등 통신 과정에 영향을 주는 다른 클래스들을 포함한다.
  • 모델 : 도메인 모델이 비즈니스 로직과 상태를 가지고 있는다. 서비스는 이러한 도메인 모델의 생성과 연산을 적절히 수행하며, 그 과정에서 트랜잭션을 보장한다.
  • 기타 : DB의 경우 외부와 통신을 해야 하고, Clean Architecture에서도 컨트롤러 바깥의 레이어로 둔다. 다만 Spring Data JPA를 쓰고 있으므로, Repository Layer에서의 연산 수행에 집중하고 통신 부분에 대해서는 추후 더 고민해보려고 한다.

이제 컨트롤러로 가보자.

@GetMapping("/api/books")
public ResponseEntity<List<BookResponse>> list() {
    return ResponseEntity.ok()
            .body(bookService.list());
}

컨트롤러는 뷰와 모델을 연결해준다.

먼저 들어온 요청을 받고, 이에 대해 올바른 응답을 돌려준다.

그리고 그 과정에서, 필요한 정보를 서비스에 요청한다.

 

이제 서비스를 한 번 보자.

@Transactional(readOnly = true)
public List<BookResponse> list() {
    return bookRepository.findAll()
            .stream()
            .map(BookResponse::new)
            .collect(Collectors.toList());
}

서비스는 비즈니스 로직을 처리하는 곳이 아니다.

서비스는 도메인 모델의 비즈니스 로직을 순서대로 호출해 주고,

또한 트랜잭션을 보장하면서 Repository Layer를 통해 DB와의 연산을 수행하는 곳이다.

위의 코드의 경우 조회를 수행하는 곳이기에 비즈니스 로직이 특별히 나오지는 않았다.

(여기서 비즈니스 로직은, 책의 수정이나 대출 처리 등을 예시로 생각하고 기재했다)

 

그렇다면 도메인은 어떨까? 대략 이런 형태를 띌 것이다.

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String content;
    private Boolean isLend
    
    public void loanBook() { ... }
}

도서 대출과 같은 비즈니스 로직 처리를 하고,

한편으로는 필요한 상태들을 저장한다.

이를 통해 도메인 모델을 명확히 하는 것이다.

물론 컨트롤러 등과 통신 시에는 DTO로 변환하여 보낸다.

 

Repository의 경우, JPA를 쓰고 있어서...그냥 인터페이스니까 따로 예시 코드를 넣지는 않았다.

다만 Repository Interface가 되었건 Dao가 되었건, 도메인 모델과 DB를 이어준다는 점만 주목하면 된다.

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기