BoardServiceImpl 와 구분하여 BoardService를 굳이 인터페이스로 사용하는 이유

Yeeun·2025년 4월 29일
0

SpringBoot

목록 보기
26/46

✨ Spring MVC 구조 정리: BoardService와 인터페이스 사용 이유

1. 전체 코드 구성

(1) Controller

package ruby.paper.controller;

import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import ruby.paper.domain.Board;
import ruby.paper.service.BoardService;

@Controller
public class BoardController {

    private final BoardService boardService;

    public BoardController(BoardService boardService) {
        this.boardService = boardService;
    }

    @GetMapping("/getBoardList")
    public String getBoardList(Model model) {
        List<Board> boardList = boardService.getBoardList();
        model.addAttribute("boardList", boardList);
        return "getBoardList";
    }
}

(2) Service Interface

package ruby.paper.service;

import java.util.List;
import ruby.paper.domain.Board;

public interface BoardService {

    List<Board> getBoardList();

    Board getBoard(Board board);

    void insertBoard(Board board);

    void updateBoard(Board board);

    void deleteBoard(Board board);

}

(3) Service Implementation

package ruby.paper.service;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ruby.paper.domain.Board;
import ruby.paper.persistence.BoardRepository;

@Service
public class BoardServiceImpl implements BoardService {

    @Autowired
    private BoardRepository boardRepo;

    @Override
    public List<Board> getBoardList() {
        return boardRepo.findAll();
    }

    @Override
    public Board getBoard(Board board) {
        return null; // 미구현
    }

    @Override
    public void insertBoard(Board board) {
    }

    @Override
    public void updateBoard(Board board) {
    }

    @Override
    public void deleteBoard(Board board) {
    }
}

(4) Domain (Entity 아님)

package ruby.paper.domain;

import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Getter
@Setter
public class Board {

    private Long seq;
    private String title;
    private String content;

    @Builder.Default
    private Date createDate = new Date();

    @Builder.Default
    private Long cnt = 0L;
    
    private String writer;
}

2. 서비스 계층이 동작하는 구조

(1) 의존성 주입 (Dependency Injection)

  • @Service를 붙인 BoardServiceImplSpring이 자동으로 객체(Bean)로 등록합니다.
  • BoardController에서는 private final BoardService boardService;를 선언하고, 생성자 주입으로 Spring이 BoardServiceImpl을 넣어줍니다.
  • 실제로는 BoardService 타입으로 받아도, 안에는 BoardServiceImpl 객체가 들어있습니다.

(2) 호출 흐름

  1. 브라우저가 /getBoardList로 요청을 보냅니다.
  2. BoardControllergetBoardList()가 실행됩니다.
  3. 이때, boardService.getBoardList()를 호출합니다.
  4. Spring은 이 boardService 안에 BoardServiceImpl 객체를 주입했기 때문에,
  5. 결국 BoardServiceImplgetBoardList()가 실행됩니다.
  6. BoardRepository를 이용해서 데이터베이스에서 데이터를 가져옵니다.
  7. 가져온 데이터를 model에 담아서 View(JSP)로 넘깁니다.

3. 그럼... 왜 굳이 인터페이스를 쓸까?

이유설명
코드 확장성나중에 서비스 로직이 바뀌어도, 컨트롤러는 수정할 필요가 없습니다. (ex: BoardServiceImplV2가 생겨도 Controller는 그대로)
테스트 편리성테스트할 때 가짜 구현체(Mock)를 쉽게 만들 수 있습니다.
느슨한 결합 (Loose Coupling)Controller가 구체적인 구현체에 의존하지 않고, "서비스"라는 추상 개념에만 의존합니다.
스프링 기본 철학스프링은 인터페이스 지향 프로그래밍을 권장합니다. (유연하고 테스트 가능한 코드 지향)

4. 쉽게 비유하자면

인터페이스 (BoardService)구현 클래스 (BoardServiceImpl)
"나는 게시판 서비스를 제공할게!" 약속"좋아, 실제로 이렇게 동작할게!" 실천

Controller는
"나한테 게시판 서비스 하나 줘!" (BoardService) 라고 요구할 뿐
"BoardServiceImpl 줘!" 라고 특정 구현체를 요구하지 않습니다.

→ 실제 어떤 구현체가 들어오는지는 Spring이 결정합니다.


✨ 결론 정리

  • @Service 덕분에 BoardServiceImpl이 Spring에 등록된다.
  • @Autowired 또는 생성자 주입 덕분에 Controller는 BoardServiceImpl을 자동으로 받는다.
  • BoardService라는 인터페이스는 코드 유연성, 확장성, 테스트를 위해 꼭 필요하다.
  • 결국, BoardService boardService로 호출하면, BoardServiceImpl이 실행된다.

📋 그림으로 한번에 정리 (비주얼)

[Controller] --(BoardService 요청)--> [Spring Container] --(BoardServiceImpl 찾아서)--> [BoardServiceImpl 실행]

이런 흐름으로 이해하면 아주 완벽하게 정리됩니다! 🌟

0개의 댓글