본문 바로가기
-- 오늘 있었던 개발 일기

spring MVC에 대해서

by code study 2026. 1. 16.

MVC 패턴, 왜 필요한가?

웹 애플리케이션을 처음 만들 때는 모든 코드를 한 파일에 작성하기 쉽습니다. 하지만 프로젝트가 커지면 문제가 생깁니다.

문제 상황

// 모든 것이 섞여있는 코드
public void processRequest() {
    // DB 연결 코드 - 데이터베이스 접속 설정
    // 비즈니스 로직 - 회원가입, 로그인 등의 실제 기능
    // HTML 생성 코드 - 사용자에게 보여줄 화면 만들기
    // 다 섞여있어서 수정이 어려움!
    // 예: HTML만 수정하려는데 DB 코드까지 건드려야 함
}

MVC로 분리하면?

세 가지 역할로 명확하게 분리됩니다:

  • Model: 데이터와 비즈니스 로직만 담당
  • View: 화면 표시만 담당
  • Controller: 요청을 받아서 Model과 View를 연결

결과: 각자 역할이 명확하니 수정할 때 해당 부분만 찾아서 고치면 됩니다!


Spring MVC란?

Spring Framework에서 제공하는 웹 애플리케이션 개발 프레임워크입니다.
MVC 패턴을 쉽게 구현할 수 있도록 다양한 기능을 제공합니다.

핵심 특징 3가지

  1. 어노테이션 기반으로 간단한 설정
  2. 자동으로 요청을 적절한 메서드로 연결
  3. 데이터 처리, 검증 등 편리한 기능 제공

Spring MVC 동작 흐름 (7단계)

① 사용자가 URL 요청 (예: /member/login)
    ↓
② DispatcherServlet이 요청을 받음 (프론트 컨트롤러)
    ↓
③ HandlerMapping이 적절한 Controller 찾음
    ↓
④ Controller가 비즈니스 로직 수행
    ↓
⑤ Model에 데이터 담아서 View 이름 반환
    ↓
⑥ ViewResolver가 실제 View 파일 찾음
    ↓
⑦ View가 최종 HTML 생성해서 사용자에게 응답

DispatcherServlet이 뭔가요?

모든 요청의 입구 역할을 하는 프론트 컨트롤러입니다.

  • 예전: URL마다 서블릿을 따로 만들어야 했음
  • 지금: DispatcherServlet 하나가 모든 요청을 받아서 적절한 Controller로 분배

 

만약 Spring MVC에서 Rest API를 적용한다면

① 사용자가 URL 요청 (예: /member/login)
    ↓
② DispatcherServlet이 요청을 받음 (프론트 컨트롤러)
    ↓
③ HandlerMapping이 적절한 Controller 찾음
    ↓
④ RestController가 비즈니스 로직 수행
    ↓
⑤ 객체를 직접 반환
    ↓
⑥ MessageConverter가 객체를 JSON으로 변환
    ↓
⑦ JSON 데이터 응답 (View 없음!)

 


실전 코드 예제

1. Controller 작성

// @Controller: 이 클래스가 컨트롤러임을 Spring에게 알려줌
// Spring이 자동으로 이 클래스를 찾아서 Bean으로 등록
@Controller
// @RequestMapping: 이 컨트롤러가 처리할 기본 URL 경로 지정
// /member로 시작하는 모든 요청은 이 컨트롤러가 담당
@RequestMapping("/member")
public class MemberController {

    // @Autowired: Spring이 자동으로 MemberService 객체를 주입해줌
    // 개발자가 직접 new MemberService()를 하지 않아도 됨
    @Autowired
    private MemberService memberService;

    // 회원 목록 조회
    // @GetMapping: GET 방식의 /member/list 요청을 이 메서드가 처리
    // 즉, 최종 URL은 /member/list가 됨
    @GetMapping("/list")
    public String memberList(Model model) {
        // Service에서 모든 회원 정보를 가져옴
        List<Member> members = memberService.getAllMembers();

        // Model에 데이터를 담음 - JSP에서 ${members}로 사용 가능
        // "members"는 JSP에서 사용할 변수명
        model.addAttribute("members", members);

        // "member/list"를 반환 - ViewResolver가 member/list.jsp를 찾아줌
        return "member/list";
    }

    // 회원 가입 폼 보여주기
    // GET /member/join 요청 시 실행
    @GetMapping("/join")
    public String joinForm() {
        // 단순히 회원가입 폼 화면만 보여줌
        // member/joinForm.jsp로 이동
        return "member/joinForm";
    }

    // 회원 가입 처리
    // @PostMapping: POST 방식의 /member/join 요청을 처리
    // 폼 제출(submit) 시 실행됨
    @PostMapping("/join")
    public String join(@ModelAttribute Member member) {
        // @ModelAttribute: 폼의 input 데이터를 Member 객체로 자동 변환
        // 예: <input name="name"> → member.setName(값)
        //     <input name="email"> → member.setEmail(값)
        // 개발자가 일일이 request.getParameter() 할 필요 없음!

        // 실제 회원가입 로직 수행 (DB에 저장)
        memberService.registerMember(member);

        // redirect: 는 다른 URL로 재요청하라는 의미
        // 회원가입 후 목록 페이지로 이동
        // 주의: "redirect:" 없이 그냥 반환하면 뷰 이름으로 인식됨
        return "redirect:/member/list";
    }

    // 회원 상세 조회
    // @GetMapping에서 {id}는 변수를 의미
    // 예: /member/detail/123 → id에 123이 들어감
    @GetMapping("/detail/{id}")
    public String memberDetail(@PathVariable Long id, Model model) {
        // @PathVariable: URL 경로의 {id} 값을 파라미터로 받음
        // /member/detail/123 요청 시 → id = 123

        // 특정 회원 정보를 id로 조회
        Member member = memberService.getMemberById(id);

        // 조회한 회원 정보를 Model에 담음
        model.addAttribute("member", member);

        // member/detail.jsp로 이동
        return "member/detail";
    }
}

코드 설명 정리

어노테이션 설명
@Controller 이 클래스가 Controller 역할임을 Spring에게 알림
@RequestMapping("/member") /member로 시작하는 URL 처리
@GetMapping, @PostMapping HTTP 메서드별 매핑
@ModelAttribute 폼 데이터를 객체로 자동 변환
(예: 이름, 이메일 → Member 객체)
@PathVariable URL 경로의 값을 파라미터로 받음
(예: /detail/123 → id=123)
Model View로 전달할 데이터를 담는 객체

2. Service 계층

// @Service: 이 클래스가 비즈니스 로직을 담당하는 Service임을 표시
// Spring이 자동으로 Bean으로 등록
@Service
public class MemberService {

    // @Autowired: Repository를 자동으로 주입받음
    // Repository는 실제 DB 작업을 수행하는 계층
    @Autowired
    private MemberRepository memberRepository;

    // 모든 회원 조회
    public List<Member> getAllMembers() {
        // Repository의 findAll() 메서드로 DB에서 모든 회원 조회
        return memberRepository.findAll();
    }

    // 회원 가입 처리
    public void registerMember(Member member) {
        // 비즈니스 로직: 이메일 중복 검사
        // DB에 이미 같은 이메일이 있는지 확인
        if (memberRepository.existsByEmail(member.getEmail())) {
            // 중복이면 예외 발생 - 회원가입 실패
            throw new RuntimeException("이미 존재하는 이메일입니다.");
        }

        // 중복이 아니면 DB에 회원 정보 저장
        memberRepository.save(member);
    }

    // 특정 회원 조회
    public Member getMemberById(Long id) {
        // findById()는 Optional<Member>를 반환
        // orElseThrow(): 값이 없으면 예외 발생, 있으면 Member 반환
        return memberRepository.findById(id)
            .orElseThrow(() -> new RuntimeException("회원을 찾을 수 없습니다."));
    }
}

3. View (JSP 예시)

<!-- member/list.jsp -->
<!-- JSTL 태그 라이브러리 사용 선언 -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<h1>회원 목록</h1>
<table>
    <thead>
        <tr>
            <th>번호</th>
            <th>이름</th>
            <th>이메일</th>
        </tr>
    </thead>
    <tbody>
        
        <c:forEach items="${members}" var="member">
            <tr>
                <td>${member.id}</td>
                <td>${member.name}</td>
                <td>${member.email}</td>
            </tr>
        </c:forEach>
    </tbody>
</table>

주요 어노테이션 정리

어노테이션 역할
@Controller 해당 클래스를 Controller로 등록
@RestController REST API용 Controller (JSON 반환)
@RequestMapping URL과 메서드를 매핑
@GetMapping GET 요청 처리
@PostMapping POST 요청 처리
@RequestParam 쿼리 파라미터 받기 (?name=value)
@PathVariable URL 경로 변수 받기 (/member/{id})
@ModelAttribute 폼 데이터를 객체로 변환
@ResponseBody 반환값을 HTTP 응답 본문으로 전달

Spring MVC vs 일반 Servlet

비교: 같은 기능 구현하기

일반 Servlet 방식

// @WebServlet: 이 Servlet이 /member/list URL을 처리
@WebServlet("/member/list")
public class MemberListServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, 
                        HttpServletResponse response) {
        // 1. 파라미터를 직접 추출해야 함
        String page = request.getParameter("page");

        // 2. null 체크, 타입 변환 등을 직접 해야 함
        int pageNum = 1;
        if (page != null) {
            pageNum = Integer.parseInt(page);
        }

        // 3. 비즈니스 로직을 직접 호출
        List<Member> members = // DB 조회

        // 4. request 객체에 직접 데이터를 설정
        request.setAttribute("members", members);

        // 5. JSP로 forward를 직접 수행
        request.getRequestDispatcher("/WEB-INF/views/list.jsp")
               .forward(request, response);
    }
}

Spring MVC 방식

@Controller
public class MemberController {
    @GetMapping("/member/list")
    public String list(
            // @RequestParam: 파라미터 자동 추출 및 타입 변환
            // defaultValue: page 파라미터가 없으면 기본값 1 사용
            @RequestParam(defaultValue="1") int page, 
            // Model: 자동으로 생성되어 주입됨
            Model model) {

        // Service를 통해 데이터 조회 (의존성 주입으로 간단)
        List<Member> members = memberService.getMembers(page);

        // Model에 데이터 추가 - request.setAttribute() 대신
        model.addAttribute("members", members);

        // View 이름만 반환 - ViewResolver가 자동으로 JSP 찾아줌
        // "list" → /WEB-INF/views/list.jsp
        return "list";
    }
}

무엇이 달라졌나?

항목 일반 Servlet Spring MVC
파라미터 처리 직접 추출, null 체크, 타입 변환 필요 자동으로 메서드 인자에 매칭 및 변환
데이터 전달 request.setAttribute() Model 객체 사용
View 연결 forward() 직접 호출, 전체 경로 필요 View 이름만 반환
URL 매핑 @WebServlet 또는 web.xml 어노테이션으로 간단히 설정

Spring MVC의 4가지 장점

1. 생산성 향상

반복적인 코드를 Spring이 자동으로 처리 → 비즈니스 로직에 집중 가능

2. 유지보수 용이

계층이 명확히 분리 → 수정할 때 해당 부분만 찾아서 수정

3. 테스트 편의성

각 계층을 독립적으로 테스트 → 단위 테스트가 쉬움

4. 유연한 구조

Interceptor, Exception Handler 등 다양한 확장 포인트 제공


정리 

Spring MVC는?

웹 애플리케이션을 MVC 패턴으로 쉽게 개발할 수 있게 해주는 프레임워크

핵심 4가지

  1. DispatcherServlet이 모든 요청을 받아서 처리
  2. Controller가 요청을 처리하고 Model에 데이터를 담음
  3. ViewResolver가 View를 찾아서 응답 생성
  4. 어노테이션으로 간단하게 설정 가능

'-- 오늘 있었던 개발 일기' 카테고리의 다른 글

더미 데이터에 시퀀스?  (1) 2026.01.29
테이블 설계...  (0) 2026.01.28
aws에 대해하여!  (0) 2026.01.06
2026년도 새해가 왔다  (0) 2026.01.05
RESTful Api란?  (0) 2026.01.04