겉바속촉

[점프투스프링부트] 2-09. 서비스 본문

IT 일기 (상반기)/SPRING 기초

[점프투스프링부트] 2-09. 서비스

겉바속촉 2023. 2. 27. 14:07
728x90
반응형

 

점프투스프링부트 2-09

 

 

 

목표 : 서비스 만들기

 

 

 

이제 질문 목록의 제목 링크를 누르면 질문 상세 화면이 보이게 할 것이다.

하지만 기능을 추가하기 전에 잠시 생각할 것이 있다.

 

우리는 QuestionController에서 QuestionRepository를 직접 사용하여 질문 목록 데이터를 조회했다.

 

하지만 대부분의 규모있는 스프링부트 프로젝트는 컨트롤러에서 리포지터리를 직접 호출하지 않고 중간에 서비스(Service)를 두어 데이터를 처리한다.

서비스는 스프링에서 데이터 처리를 위해 작성하는 클래스이다.

 
 

 
 

1.  서비스가 필요한 이유

 

서비스가 필요한 이유는 무엇일까?

 

- 모듈화

예를들어 어떤 컨트롤러가 여러개의 리포지터리를 사용하여 데이터를 조회한후 가공하여 리턴한다고 가정해 보자.

이러한 기능을 서비스로 만들어 두면 컨트롤러에서는 해당 서비스를 호출하여 사용하면 된다.

하지만 서비스로 만들지 않고 컨트롤러에서 구현하려 한다면 해당 기능을 필요로 하는 모든 컨트롤러가 동일한 기능을 중복으로 구현해야 한다. 이러한 이유로 서비스는 모듈화를 위해서 필요하다.

 

- 보안

컨트롤러는 리포지터리 없이 서비스를 통해서만 데이터베이스에 접근하도록 구현하는 것이 보안상 안전하다.

이렇게 하면 어떤 해커가 해킹을 통해 컨트롤러를 제어할 수 있게 되더라도 리포지터리에 직접 접근할 수는 없게 된다.

 

- 엔티티 객체와 DTO 객체의 변환

우리가 작성한 Question, Answer 클래스는 엔티티(Entity) 클래스이다.

엔티티 클래스는 데이터베이스와 직접 맞닿아 있는 클래스이기 때문에 컨트롤러나 타임리프 같은 템플릿 엔진에 전달하여 사용하는 것은 좋지 않다. 컨트롤러나 타임리프에서 사용하는 데이터 객체는 속성을 변경하여 비즈니스적인 요구를 처리해야 하는 경우가 많은데 엔티티를 직접 사용하여 속성을 변경한다면 테이블 컬럼이 변경되어 엉망이 될수도 있기 때문이다.

이러한 이유로 Question, Answer 같은 엔티티 클래스는 컨트롤러에서 사용할수 없게끔 설계하는 것이 좋다.

그러기 위해서는 Question, Answer 대신 사용할 DTO(Data Transfer Object) 클래스가 필요하다.

그리고 Question, Answer 등의 엔티티 객체를 DTO 객체로 변환하는 작업도 필요하다.

그러면 엔티티 객체를 DTO 객체로 변환하는 일은 어디서 처리해야 할까? 그렇다. 바로 서비스이다.

서비스는 컨트롤러와 리포지터리의 중간자적인 입장에서 엔티티 객체와 DTO 객체를 서로 변환하여 양방향에 전달하는 역할을 한다.

 
 
 

2. QuestionService

다음과 같이 QuestionService를 작성하자.

[파일명:/sbb/src/main/java/com/mysite/sbb/question/QuestionService.java]

package com.mysite.sbb.question;

import java.util.List;

import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class QuestionService {

    private final QuestionRepository questionRepository;

    public List<Question> getList() {
        return this.questionRepository.findAll();
    }
}

 

스프링의 서비스로 만들기 위해서는 위와 같이 클래스명 위에 @Service 애너테이션을 붙이면 된다.

@Controller, @Entity 등과 마찬가지로 스프링부트는 @Service 애너테이션이 붙은 클래스는 서비스로 인식한다.

questionRepository 객체는 생성자 방식으로 DI 규칙에 의해 주입된다.

그리고 질문 목록을 조회하여 리턴하는 getList 메서드를 추가했다. 이전 컨트롤러에서 리포지터리를 사용했던 부분을 그대로 옮긴 것이다.

 

 

***또 까먹은 사람(나)을 위해***
@RequiredArgsConstructor
Lombok으로 스프링에서 DI(의존성 주입)의 방법 중에 생성자 주입을 임의의 코드없이 자동으로 설정해주는 어노테이션

 

 

 

 

3. QuestionController

그리고 QuestionController는 리포지터리 대신 서비스를 사용하도록 다음처럼 수정하자.

 

[파일명:/sbb/src/main/java/com/mysite/sbb/question/QuestionController.java]

(... 생략 ...)
public class QuestionController {

    private final QuestionService questionService;

    @GetMapping("/question/list")
    public String list(Model model) {
        List<Question> questionList = this.questionService.getList();
        model.addAttribute("questionList", questionList);
        return "question_list";
    }
}

questionService 객체는 생성자 방식으로 DI 규칙에 의해 주입된다.

브라우저로 http://localhost:8080/question/list 페이지에 접속하면 이전과 동일한 화면을 볼수 있다.

앞으로 작성할 컨트롤러들도 리포지터리를 직접 사용하지 않고 Controller -> Service -> Repository 구조로 데이터를 처리할 것이다.

 

 

AS-IS : Controller에서 Repository 직접 사용

 

TO-BE : Controller에서 Service를 사용하고 그 Service는 Repository에서 파생된 것

 

 

 

 

728x90
반응형