1. Spring Web MVC 한 번에 감 잡기
1-1. Spring Web MVC란?
Spring Web MVC는 말 그대로 “웹용 스프링 프레임워크”라고 보면 된다.
- 클라이언트가 HTTP 요청을 보낸다.
- 스프링의 Controller가 요청을 받고 비즈니스 로직(Service)을 호출한다.
- 필요한 데이터는 DAO를 통해 DB에서 가져온다.
- 가공된 데이터를 Model에 담아서 View(HTML, 템플릿)에 넘긴다.
- ViewResolver가 어떤 화면을 렌더링할지 결정하고, 최종 응답을 만든다.
핵심 포인트는 두 가지다.
- MVC 2 패턴 구조로 웹 애플리케이션을 만든다.
- Controller, Service, DAO 같은 객체들의 의존 관계를 스프링 IoC 컨테이너가 관리해 준다.
그래서 개발자는 “객체를 언제 new 해서 연결하지?” 이런 고민보다
“어떤 책임을 어디에 둘까?”에 더 집중할 수 있다.
1-1-1. Spring에서 말하는 MVC 계층
Spring Web MVC에서 이야기를 할 때 보통 이렇게 세 계층으로 나눈다.
- Controller
- Service
- DAO(Repository)
각자 역할을 간단히 보면:
- Controller
- 클라이언트 요청을 받는다.
- 파라미터를 받고, 검증하고, 필요한 값 가공.
- Service를 호출해서 비즈니스 로직 실행.
- 결과 데이터를 Model에 담고, 어떤 View를 보여줄지 결정해서 리턴.
- Service
- “비즈니스 로직”의 중심.
- 여러 DAO 호출 결과를 조합해서
- 트랜잭션 처리
- 로직 분기
- 계산, 검증 등을 수행한다.
- Controller에서 받은 입력값을 규칙에 맞게 처리하고, DAO와 통신한 뒤 결과를 반환.
- DAO (Data Access Object)
- DB와 직접 이야기하는 계층.
- SQL 실행, 데이터 조회/삽입/수정/삭제.
- JDBC, MyBatis, JPA 등 어떤 기술을 쓰든 “DB 접근 로직”을 여기에 모아둔다.
- Service는 최대한 “비즈니스 중심 로직”만 남기고, SQL 같은 건 DAO로 빼는 것이 목적.
1-1-2. MVC Architecture가 등장한 이유
초창기(원시적인) 웹 애플리케이션 구조를 떠올려보면 이랬다.
- 하나의 파일(또는 클래스) 안에서:
- 화면 출력 HTML
- 로직 처리 코드
- DB 조회/수정 SQL 전부 다 섞여 있다.
이 구조의 문제점:
-
중복 코드 폭발
비슷한 기능이 여기저기 흩어져서
“이 쿼리랑 비슷한 거 저기에도 또 있음…” 이런 상황이 생긴다.
-
수정 영향 범위가 너무 넓다
쿼리 하나, 로직 하나 바꾸려고 해도
여러 파일을 다 뒤져서 바꿔야 할 수도 있다.
그래서 나온 생각이:
“역할별로 코드를 나누자”
→ 비즈니스 로직 / 화면 / DB 접근을 분리해 관리하자.
이게 바로 MVC 아키텍처의 시작이다.
1-1-3. DAO 계층: DB 접근을 따로 뽑자
먼저 DB 쿼리를 하나의 클래스로 모은 것이 DAO이다.
- DAO는 “쿼리 단위”로 메서드를 나눈다.
- insertSomething()
- selectSomething()
- updateSomething()
- deleteSomething()
이렇게 잘게 쪼개두면 장점은:
- 재사용성이 높다 블록 장난감처럼, 잘게 잘려 있을수록 여러 곳에 갖다 쓰기 쉬운 것처럼, 쿼리도 기능 단위로 쪼개둘수록 여기저기에서 재활용하기 좋다.
- 유지보수성이 좋다 특정 쿼리가 잘못되었을 때, 그 기능은 DAO 클래스 안에서 고치면 된다. (이 파일 저 파일 다 뒤지지 않아도 된다.)
그리고 또 중요한 개념 하나:
- 논리적인 “하나의 작업(트랜잭션)”은
- 하나의 Connection 안에서 처리되어야 한다.
- 그래서 보통 “Service 쪽에서 Connection을 관리”하고, DAO는 그 Connection을 넘겨받아 쓰는 구조를 취한다.
- ResultSet은 java.sql에 강하게 의존하므로,
- 이 의존성을 최대한 DAO 안에 가둬두고
- Service나 Controller 쪽에는 도메인 객체/DTO로만 넘겨주는 것이 일반적이다.
1-1-4. Service 계층: 비즈니스 로직 + 트랜잭션의 중심
DAO까지 분리했는데도 아직 이런 문제가 남아 있다.
- 화면(뷰) 코드 안에서 DAO를 직접 부르는 구조라면,
- 화면이 바뀔 때마다 로직이 같이 엮인다.
- 같은 로직을 사용하는 다른 화면이 생기면, 또 코드를 복사/붙여넣기 하는 상황이 생긴다.
- 프론트 개발자와 백엔드 개발자가 같은 파일을 건드려야 해서 충돌도 잦다.
그래서 등장한 것이 Service 계층이다.
Service의 역할은 크게 두 가지다.
- 비즈니스 로직 처리
- 요청 요구사항에 맞는 로직 흐름을 Service에 모은다.
- 여러 DAO 호출을 조합한다.
- 값 검증, 도메인 규칙 적용, 상태 변경 등.
- 트랜잭션 + Connection 라이프사이클 관리
- Connection 열기 / 닫기
- autoCommit(false) 설정 후
- 중간에 문제 없으면 commit
- 예외 발생 시 rollback
- 이런 “하나의 논리적인 작업 단위”를 Service에서 책임진다.
그래서 “Model” 계층 중에서도 실제 핵심은 Service라고 말하기도 한다.
이 철학이 더 확장된 게 바로 “SOA(Service Oriented Architecture, 서비스 지향 아키텍처)” 개념이다.
1-1-5. Controller 계층: View와 Service 사이의 중간 다리
이제 Model(Service/DAO) 쪽은 잘 정리했는데,
여전히 이런 문제가 남아 있다.
- View에서 Service를 직접 호출하고 있다면:
- Service 이름이 바뀌거나 메서드 시그니처가 바뀌면 View도 전부 수정해야 한다.
- Web, Android, iOS 등 “표현 방식(View)”은 다른데 로직(Service)은 같은 경우, 각 View마다 Service 의존 코드가 들어가 있으니 관리가 힘들다.
- View에 따라 필요한 데이터 가공 방식이 조금씩 달라지는 경우도 있어서, 비즈니스 로직이 오염되기 쉽다.
그래서 나온 해결책이 Controller이다.
Controller의 역할:
- 요청 파라미터 수집 + 1차 가공
- 클라이언트에서 넘겨준 값들(request param, path variable, body 등)을 받는다.
- 단순 검증 및 타입 변환을 한다.
- 보안상/논리상 프론트가 넘기면 안 되는 값(예: 서버 시간)은 Controller에서 채운다.
- Service 호출
- 정리된 파라미터를 Service에 전달한다.
- Service는 비즈니스 로직, DB 접근 등을 모두 처리한다.
- 결과 받아서 View로 전달
- Service가 돌려준 결과를 Model에 담는다.
- 어떤 View를 렌더링할지 결정한다.
- JSON 응답, 템플릿, 리다이렉트 등 응답 방식을 고른다.
이렇게 되면 구조가 깔끔해진다.
- View ↔ Controller
- Controller ↔ Service
- Service ↔ DAO
각 계층이 “바로 아래 계층”만 의존하도록 만들어 결합도를 낮춘다.
1-1-6. MVC Architecture 흐름 정리
최종적으로 MVC 구조를 흐름으로 보면:
- 사용자가 화면(View)에서 요청을 보낸다.
- Controller가 요청을 받고, 파라미터를 처리한다.
- Controller는 Service에게 “이 작업 해줘”라고 요청한다.
- Service는
- 필요하면 여러 DAO를 사용해서 DB 작업을 하고
- 하나의 트랜잭션 단위로 commit/rollback을 관리한다.
- Service 결과를 Controller에 반환한다.
- Controller는 결과를 Model에 담고 적절한 View를 선택한다.
- View는 Model 데이터를 이용해서 화면을 렌더링한다.
이걸 계층 구조로 그리면:
- View (UI)
- Controller (요청/응답 조율)
- Service (비즈니스 로직 + 트랜잭션)
- DAO/Repository (DB 접근)
스프링 MVC는 이 구조를 프레임워크 수준에서 지원해주는 것이다.