◇ PART · MVC

6 계층 한눈에

DispatcherServlet ~ View 의 분업 지도

학습 목표

  • Spring MVC 의 6 계층이 무엇인지 안다
  • 각 계층의 단 하나의 책임을 입으로 말할 수 있다
  • 하나의 요청이 6 계층을 어떻게 통과하는지 그릴 수 있다
  • 이 분업이 왜 코드를 안전하고 유연하게 만드는지 안다

⚠️ 분업 없는 코드의 한계

한 메서드에 다 박힌 코드

@RequestMapping("/board/list")
public String list() {
    // ① DB 직접 연결
    Connection c = DriverManager.getConnection(...);
    // ② SQL 실행
    PreparedStatement ps = c.prepareStatement("SELECT...");
    // ③ 결과 가공
    while(rs.next()) { ... }
    // ④ HTML 조립
    StringBuilder html = new StringBuilder("<html>...");
    // 한 메서드 안에 4 가지 일이 다 있다
}

화면 디자인 한 번 바꾸려고 SQL 도 손대야 한다면? 부서지기 쉽고, 테스트 불가능, 새 사람이 와서 손대기 어려움.

🛠️ 6 계층 분업 — 식당으로 비유

각자의 일이 다르다

큰 식당이 손님 한 명을 응대하는 흐름과 정확히 같습니다. 각 사람은 자기 일만 하고, 다른 사람의 일은 알지 못해도 됩니다.

① DispatcherServlet → 안내데스크 (입구 통제) ② Controller → 종업원 (주문 받기) ③ Service → 메인 셰프 (실제 요리) ④ DAO / Repository → 식재료 창고 관리자 (DB 접근) ⑤ DTO / VO → 그릇 (데이터 운반) ⑥ View → 식탁·메뉴판 (화면)

전체 흐름 — 요청이 들어가는 길

브라우저 GET /board/view?id=3 ↓ ① DispatcherServlet "이 URL 누구한테 보낼까?" ↓ ② BoardController.view(3) "id=3 받음, 셰프에게 부탁하자" ↓ ③ BoardService.findById(3) "비즈니스 로직 처리" ↓ ④ BoardMapper.findById(3) "DB 에 SQL 실행" ↓ DB

전체 흐름 — 결과가 돌아오는 길

DB → ⑤ Board (DTO) 한 개 반환 ↑ ④ Mapper 가 DTO 로 받아 Service 로 ↑ ③ Service 가 결과를 Controller 로 ↑ ② Controller 가 model 에 담고 ViewName 반환 ↓ ⑥ View (JSP) — 화면 그림 ↓ 브라우저 → 화면 표시

① DispatcherServlet — 안내데스크

책임: 모든 HTTP 요청을 가장 먼저 받는다. 어느 컨트롤러가 처리할지 결정해서 넘긴다.

  • Spring 이 만들어주는 부품 — 우리가 직접 짜지 않음
  • web.xml 에 한 번만 등록
  • Front Controller 패턴의 구현체
  • URL 보고 적절한 컨트롤러 메서드를 찾음 (HandlerMapping 도움)
web.xml 에서: <servlet> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> </servlet>

② Controller — 종업원

책임: 요청을 받아 검증하고, 적절한 Service 에게 일을 시킨다. 결과를 모델에 담아 View 로 보낸다.


@Controller
public class BoardController {

    @Autowired
    private BoardService service;          // 셰프

    @GetMapping("/board/view")
    public String view(@RequestParam int id, Model model) {
        Board b = service.findById(id);     // 셰프에게 부탁
        model.addAttribute("board", b);     // 그릇에 담아
        return "board/view";                // 식탁(JSP)으로
    }
}

👉 컨트롤러는 「중개자」 — SQL 도 모르고, HTML 도 안 만든다.

③ Service — 메인 셰프

책임: 실제 요리(비즈니스 로직)를 한다. 트랜잭션도 책임진다.


@Service
public class BoardService {

    @Autowired
    private BoardMapper mapper;            // 창고 관리자

    public Board findById(int id) {
        Board b = mapper.findById(id);
        if (b == null) {
            throw new IllegalArgumentException("글이 없습니다");
        }
        return b;
    }
}

③ Service — 트랜잭션 예시


@Service
public class BoardService {

    @Autowired private BoardMapper mapper;
    @Autowired private FileService fileService;

    @Transactional
    public void writeWithFile(Board b, MultipartFile file) {
        mapper.insert(b);                   // 글 저장
        fileService.save(file);             // 파일 저장
        // 둘 중 하나 실패하면 둘 다 롤백
    }
}

③ Service 가 진짜로 하는 일

  • 비즈니스 규칙 적용 — "글이 없으면 예외" 같은 도메인 규칙
  • 여러 Mapper 호출 조합 — 글 + 파일 + 알림 등 여러 DB 작업
  • 트랜잭션 경계@Transactional 로 원자성 보장
  • 외부 시스템 호출 — 결제 API, 메일 발송 등

Service 가 「두툼해야」 한다 — Controller 와 Mapper 는 얇게.

④ DAO / Repository — 창고 관리자

책임: DB 접근 SQL 만 작성. 비즈니스 로직 없음.


// 인터페이스 — 자바
@Mapper
public interface BoardMapper {
    Board findById(int id);
    List<Board> findAll();
    void insert(Board b);
    void update(Board b);
    void delete(int id);
}

// SQL — XML
<mapper namespace="...BoardMapper">
    <select id="findById" resultType="Board">
        SELECT * FROM board WHERE id = #{id}
    </select>
</mapper>

👉 「DAO」 와 「Repository」 는 사실 같은 일. Spring 권장은 @Repository.

⑤ DTO / VO — 그릇

책임: 계층 사이 데이터 운반. 로직은 거의 없는 「데이터 그릇」.


public class Board {
    private int id;
    private String title;
    private String content;
    private int writerId;
    private LocalDateTime createdAt;

    // getter / setter
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    // ...
}

👉 DB 한 행 ↔ JSP 의 ${board.title} 사이를 오가는 그릇.

⑤ DTO · VO · Entity — 미세한 차이

이름주된 의미특징
DTOData Transfer Object계층 간 운반. 보통 getter/setter
VOValue Object「값」으로서 의미. 불변(immutable) 권장
EntityDB 의 한 행을 표현JPA 등에서. 본 과정에서는 미사용

👉 본 과정에선 「DTO 와 VO 는 거의 같다」 정도로 받아들여도 OK. 정확한 구분은 Part 3 의 ◇ DTO·VO·Model 차시에서.

⑥ View — 식탁·메뉴판

책임: 모델 데이터를 받아 사용자에게 보여줄 화면을 그린다.


<!-- /WEB-INF/views/board/view.jsp -->
<%@ page contentType="text/html; charset=UTF-8" %>
<html>
<body>
    <h1>${board.title}</h1>
    <p>작성자: ${board.writerName}</p>
    <div>${board.content}</div>
</body>
</html>

👉 View 는 자바 코드를 거의 안 씀. 표현(${...})만.

각 계층의 「단 하나의 책임」

계층한 줄 책임이런 코드는 여기 없음
DispatcherServlet요청 분배(우리가 안 짬)
Controller요청·응답 중개SQL, 비즈니스 규칙
Service비즈니스 로직 + 트랜잭션HTML, 직접 SQL
DAO/RepositorySQL 만비즈니스 규칙
DTO/VO데이터 운반비즈니스 메서드
View화면 표시SQL, 비즈니스 처리

의존 방향 — 위에서 아래로만

Controller ↓ 호출 Service ↓ 호출 DAO / Repository ↓ 호출 DB ※ 거꾸로는 절대 안 됨 - Service 가 Controller 를 호출 X - DAO 가 Service 를 호출 X

👉 화살표가 한 방향이라 의존 관계가 단순. 한 계층 변경이 위로만 영향.

분업이 만드는 5 가지 효과

  • ① 변경 격리 — 화면 디자인 변경 = View 만, DB 변경 = DAO 만
  • ② 테스트 용이 — Service 만 단위 테스트 가능 (Controller·DB 없이)
  • ③ 재사용 — 같은 Service 를 웹 / API / 배치에서 모두 사용
  • ④ 협업 — 백엔드는 Service 작성, 프론트는 View 작성
  • ⑤ 코드 추적 — 버그 위치를 계층으로 좁힘

실습 환경의 폴더 구조

src/main/java/com/example/demo/ ├─ controller/ ← @Controller 들 │ └─ BoardController.java ├─ service/ ← @Service 들 │ └─ BoardService.java ├─ mapper/ ← @Mapper 들 │ └─ BoardMapper.java └─ domain/ ← DTO/VO 들 └─ Board.java src/main/webapp/WEB-INF/views/ └─ board/ ← View(JSP) 들 ├─ list.jsp └─ view.jsp src/main/resources/mappers/ └─ BoardMapper.xml ← SQL

실수 대처 가이드

증상의심할 계층
404 Not FoundDispatcherServlet 매핑 / Controller URL
"No qualifying bean of type 'XService'"Service 의 @Service 누락
NullPointerException at mapperDAO 의 @Autowired 누락
SELECT 결과 모두 nullDTO 필드 매핑 (mapUnderscoreToCamelCase)
"Could not resolve view"ViewName 오타 또는 JSP 위치
한글 깨짐web.xml CharacterEncodingFilter

다음 차시들 미리보기

  • 다음 차시 — DispatcherServlet 깊이 보기
  • Controller 해부 (어노테이션, Model)
  • Service 계층 (트랜잭션 자리)
  • DAO / Repository 계층
  • DTO · VO · Model 의 정확한 차이
  • View · ViewResolver
  • 설정 파일 3 종 해부 (XML 공포증 해소)
  • ★ v0.5 첫 종단간 흐름

🔄 Before / After

Part 3 시작

"분업하라" 는 큰 그림은 안다. 6 계층의 이름은 처음 들어봄.

이번 차시 끝

6 계층의 이름·책임·의존 방향을 한 그림으로 외운다. 다음 차시들에서 각 계층을 깊이 파고들 토대 완성.

📊 한 그림 정리

이번 차시의 데이터 흐름

DispatcherServlet
Controller
Service
DAO
DB
4 개 계층 박스가 한 번에 등장 — Part 3 의 토대

정리

오늘 들고 가는 것

  • 6 계층 = DispatcherServlet · Controller · Service · DAO · DTO · View
  • 각 계층은 단 하나의 책임
  • 의존 방향은 위 → 아래 한 방향
  • 분업이 만드는 5 가지 효과 (변경 격리·테스트·재사용·협업·추적)
  • 다음 차시들에서 각 계층을 깊이

다음: DispatcherServlet 깊이 보기 — URL 분배의 마법.