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";
}
}
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);
}
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) {
}
}
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;
}
@Service를 붙인 BoardServiceImpl는 Spring이 자동으로 객체(Bean)로 등록합니다.BoardController에서는 private final BoardService boardService;를 선언하고, 생성자 주입으로 Spring이 BoardServiceImpl을 넣어줍니다.BoardService 타입으로 받아도, 안에는 BoardServiceImpl 객체가 들어있습니다./getBoardList로 요청을 보냅니다.BoardController의 getBoardList()가 실행됩니다.boardService.getBoardList()를 호출합니다.boardService 안에 BoardServiceImpl 객체를 주입했기 때문에,BoardServiceImpl의 getBoardList()가 실행됩니다.BoardRepository를 이용해서 데이터베이스에서 데이터를 가져옵니다.model에 담아서 View(JSP)로 넘깁니다.| 이유 | 설명 |
|---|---|
| 코드 확장성 | 나중에 서비스 로직이 바뀌어도, 컨트롤러는 수정할 필요가 없습니다. (ex: BoardServiceImplV2가 생겨도 Controller는 그대로) |
| 테스트 편리성 | 테스트할 때 가짜 구현체(Mock)를 쉽게 만들 수 있습니다. |
| 느슨한 결합 (Loose Coupling) | Controller가 구체적인 구현체에 의존하지 않고, "서비스"라는 추상 개념에만 의존합니다. |
| 스프링 기본 철학 | 스프링은 인터페이스 지향 프로그래밍을 권장합니다. (유연하고 테스트 가능한 코드 지향) |
인터페이스 (BoardService) | 구현 클래스 (BoardServiceImpl) |
|---|---|
| "나는 게시판 서비스를 제공할게!" 약속 | "좋아, 실제로 이렇게 동작할게!" 실천 |
Controller는
"나한테 게시판 서비스 하나 줘!" (BoardService) 라고 요구할 뿐
"BoardServiceImpl 줘!" 라고 특정 구현체를 요구하지 않습니다.
→ 실제 어떤 구현체가 들어오는지는 Spring이 결정합니다.
@Service 덕분에 BoardServiceImpl이 Spring에 등록된다.@Autowired 또는 생성자 주입 덕분에 Controller는 BoardServiceImpl을 자동으로 받는다.BoardService boardService로 호출하면, BoardServiceImpl이 실행된다.[Controller] --(BoardService 요청)--> [Spring Container] --(BoardServiceImpl 찾아서)--> [BoardServiceImpl 실행]
이런 흐름으로 이해하면 아주 완벽하게 정리됩니다! 🌟