이번엔 Spring MVC에서 Model과 관련된 Service Component에 대해서 알아보겠다.
흐름의 순서는 Controller -> Service -> Repository라서 Controller를 먼저 알아야 하지만
Controller와 관련된 개념을 최대한 제외하고 Service 자체를 먼저 알아보겠다.
우선 스프링부트에서 Component가 무엇을 뜻하는지 먼저 알아야한다.
이때 Component란?
구성요소라는 뜻으로 독립적인 단위 모듈을 뜻한다.
스프링 부트에선 IoC 컨테이너에 등록된 클래스를 뜻한다.
@Component 어노테이션을 사용해 직접 작성한 클래스를 IoC 컨테이너에 등록 할 수 있다.
이때, IoC 컨테이너에 등록되어 IoC가 관리하는 객체를 Bean이라고 한다.
이번에는 그 중 하나인 Service Component에 대해 알아 볼 것이다.
Service Component란?
Client의 요청에 대한 비즈니스 로직을 수행하는 Component이다.
Client의 요청 url에 따른 Controller가 호출되면,
Controller는 해당 비즈니스 로직을 수행하기 위한 Service를 호출한다.
Service는 로직 수행 후 결과를 Controller에게 반환한다.
이때 Service가 Service Component이다.
소스코드에서 Service Component는 @Service 어노테이션을 달고있다.
말그대로 이처럼 클래스 선언부에 @Service 어노테이션을 달고있다.
이때 @Service 어노테이션의 가장 큰 역할은
스프링에게 해당 클래스가 Service Component이라고 알려 주는 것이다.
작성한 클래스에 @Service 어노테이션을 달아주면
해당 클래스를 루트 컨테이너에 Bean 객체로 생성해준다.
당연한 말이지만 IoC 컨테이너에 Bean 객체로 등록하지 않으면 스프링 부트가 제공하는 기능들을 사용할 수 없다.
또한 @Service를 통해 Business Layer에서 사용할 Service임을 명시한다.
@Service어노테이션을 통해 루트 컨테이너에 Bean 객체로 등록하고
등록된 Bean을 통해스프링은 Controller가 어떤 Service를 호출 할 것인지 결정하는 것이다.
xml에 직접 등록해야하는 것을 대신 해주는 것이다.
지금까지 Service Component의 생성에 대해 알아보았다.
이제 Service Component는 어떻게 구현하는 것인지에 대해 알아보겠다.
Service Component 구현
Service는 Interface를 사용해 구현한다.
- Service Interface
- Service Interface Implement
Interface를 통해 비즈니스 로직의 틀을 잡고
Implement를 통해 비즈니스 로직을 구현한다.
- Interface를 사용하면 클래스간의 결합이 느슨해진다.
비즈니스 로직이 바뀌었을 경우 Implement만 갈아끼우면 된다. (코드 수정 1줄)
- 캐시를 사용 할 수 있게 된다.
내부 캐시를 통해 내부에 데이터가 존재하지 않는 경우에만 메소드가 실행 하도록 할 수 있다.
구현 방법은 아래와 같다.
현재 공부하며 게시판 서비스를 만드는 중인데 게시판 서비스를 통해 설명을 하겠다.
인터페이스에 createBoard라는 추상 메소드를 정의하였다.
해당 메소드는 CreateBoardReqDto 객체 하나를 인자로 받고 내부에서 에러가 생기면 Exception을 던진다.
createBoard는 게시판의 게시글 작성을, insert를 담당하는 메소드이다.
추가적으로 해당 인터페이스에는 어떠한 어노테이션이라도 달려있지 않다.
해당 클래스는 BoardService를 구현하고 있다.
우선 해당 클래스는 이름이 너무 기니 이제 BSI라고 하겠다. (BoardServiceImpl => BSI)
우선 가장 눈에 띄는 것은 @Service와 @RequiredArgsConstructor이다.
@Service
@Service 어노테이션을 통해 BSI는 IoC 컨테이너에 Bean으로 등록 되었다.
스프링 프레임워크에서 제공하는 어노테이션이다.
@RequiredArgsConstructor
@RequiredArgsConstructor 어노테이션은 lombok에서 제공하는 어노테이션이다.
해당 어노테이션은 final 키워드 또는 @NonNull이 붙은 필드들로 이루어진
생성자를 만들어준다.
스프링부트에서는 어떠한 Bean에 대한 생성자가 오직 하나만 있고 (BSI가 충족 o)
생성자의 파라메너 타입이 Bean으로 등록되어 있다면 (BoardService 이미 등록 o)
@Autowired 어노테이션 없이도 스프링부트가 자동으로 의존성 주입(DI)를 해준다.
@Autowired 어노테이션 자체도 IoC 컨테이너에 등록되어 있는 Bean을
클래스 생성시 해당 필드로 주입해주는데
스프링부트에서는 조건이 충족된다면 @Autowired 조차 안써도 된다.
그래서 @RequiredArgsConstructor 때문에 BSI 생성시
boardRepository에는 IoC 컨테이너에 등록된 Bean이 자동으로 주입되는 것이다.
이제 어노테이션에 대한 설명을 끝났다.
createBoard()
BSI의 createBoard 메소드는 CreateBoardReqDto 객체를 인자로 받는다. (CreateBoardReqDto => CBRD)
CBRD를 객체로 받아서 그냥 boardRepository의 save()를 호출한다.
물론 save 메소드를 호출 할 때 인수로 CBRD의 toEntity()의 리턴값을 넘겨준다.
save()는 호출되면 sql 쿼리문을 실행할 것이고 DB에 게시글을 등록 할 것이다.
등록 후 등록에 성공한 게시글의 수를 integer로 반환 할 것이다.
(해당 메소드는 @Repository와 관련이 있는 부분이기 때문에 나중에 자세히 설명하겠다.)
그래서 save()는 게시글 등록에 실패하면 0을, 성공하면 1을 반환할 것이다.
그리고 createBoard()는 save()의 반환 값이 0보다 큰 지를 확인한 후 결과를 반환하는 것이다.
createBoard()가 수행하는 일은 여기까지이다.
이제 createBoard()가 사용했던 CRBD가 무엇인지에 대해 알아야한다.
CreateBoardReqDto
CBRD는 사실 게시글 작성에 필요한 정보들을 담은 객체이다.
클라이언트가 게시글 작성 요청시 입력하는 정보들을 담는 객체이다.
특별한 기능은 없다.
용도는 Controller에서 Service까지의 데이터 전달용이다. (DTO)
Service에서 Repository로의 데이터 전달은 Board 클래스를 사용한다. (Entity)