삶 가운데 남긴 기록 AACII.TISTORY.COM

Spring MVC에서 Model과 ModelAndView 그리고 CRUD 본문

DEV&OPS/Java

Spring MVC에서 Model과 ModelAndView 그리고 CRUD

ALEPH.GEM 2026. 7. 16. 19:08
728x90

 

Model과 ModelAndView의 차이점

특징 Model ModelAndView
역할 뷰에 전달할 데이터(속성)만 담는 역할 (인터페이스) 데이터(Model)와 뷰 이름(View)을 모두 담는 컨테이너 (클래스)
뷰 지정 컨트롤러 메서드의 반환 값(String)으로 뷰 이름을 지정 객체 내부의 setViewName() 메서드로 뷰 이름을 지정
주요 사용처 최근 Spring 개발 트렌드에서 주로 사용
(String 리턴 타입과 함께)
Spring 초기에 자주 사용되었으나, 최근에는 사용 빈도가 감소
반환 타입 컨트롤러 메서드의 파라미터로 받으며, 메서드는 보통 뷰 이름(String)을 반환 컨트롤러 메서드의 반환 타입이 됨

 

 

Model

Model 객체는 컨트롤러 메서드의 파라미터로 주입받아 사용하며, 데이터를 추가합니다. 메서드는 뷰의 논리적 이름(String)을 반환합니다.

컨트롤러 예시:

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class MyController {

    @GetMapping("/model-example")
    public String useModel(Model model) {
        // Model에 'message'라는 이름으로 데이터 추가
        model.addAttribute("message", "Model 객체를 사용한 데이터입니다.");
        
        // 뷰의 논리적 이름 반환 (ViewResolver가 'modelView.jsp' 또는 'modelView.html' 등을 찾음)
        return "modelView"; 
    }
}

 

뷰(modelView.jsp) 예시:

<body>
    <h1>Model 예제</h1>
    <p>받은 메시지: ${message}</p>
</body>

 

 

ModelAndView

ModelAndView 객체를 생성하여 데이터를 추가하고 뷰 이름까지 설정한 후, 객체 자체를 반환합니다.

컨트롤러 예시:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MyController {

    @GetMapping("/mav-example")
    public ModelAndView useModelAndView() {
        // ModelAndView 객체 생성 및 뷰 이름 설정
        ModelAndView mav = new ModelAndView("mavView"); 
        
        // addObject()를 사용하여 데이터 추가
        mav.addObject("greeting", "ModelAndView를 사용한 환영 메시지입니다.");
        
        // ModelAndView 객체 자체를 반환
        return mav;
    }
}

 

뷰(mavView.jsp) 예시:

<body>
    <h1>ModelAndView 예제</h1>
    <p>받은 인사: ${greeting}</p>
</body>

 

 

@ModelAttribute

@ModelAttribute 어노테이션은 Spring MVC에서 HTTP 요청 파라미터와 Model 객체를 처리하는데 사용됩니다.

1. 컨트롤러 메서드의 파라미터에서 사용될 때 (데이터 바인딩 및 Model에 추가)

요청 데이터를 객체에 자동으로 매핑하고 이 객체를 뷰에 전달 합니다.

@ModelAttribute가 컨트롤러 메서드의 파라미터에 사용될 때, Spring은 다음 세 가지 작업을 수행합니다.

  1. 객체 생성: 파라미터 타입에 해당하는 객체(DTO 또는 VO)를 자동으로 생성합니다.
  2. 데이터 바인딩 (Data Binding): HTTP 요청(예: 폼 데이터, 쿼리 파라미터)에서 넘어온 값들을 자동으로 새로 생성된 객체의 필드에 바인딩(매핑)합니다. 이 과정을 통해 개발자가 일일이 request.getParameter()를 호출할 필요가 없습니다.
  3. Model에 추가: 바인딩이 완료된 객체를 자동으로 Model 객체에 추가하여 뷰(View)에서 사용할 수 있도록 합니다. 이때 Model 속성의 이름은 기본적으로 해당 클래스의 이름(소문자로 시작)을 따릅니다.
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;

// Student DTO/VO 클래스가 있다고 가정
public class Student {
    private String name;
    private int age;
    // getter, setter 생략
}

@Controller
public class FormController {
    
    // HTTP 요청 파라미터 (예: name=John&age=20)가 Student 객체에 자동 바인딩됨
    @PostMapping("/register")
    // @ModelAttribute("studentInfo") Student student
    //  -> student라는 객체를 Model에 "studentInfo"라는 이름으로 추가 
    public String register(@ModelAttribute("studentInfo") Student student) {
        // student 객체에는 이미 요청 파라미터로 넘어온 name과 age 값이 설정되어 있음
        System.out.println("등록된 학생 이름: " + student.getName()); 
        
        // Model에도 "studentInfo"라는 이름으로 student 객체가 추가되어 뷰로 전달됨
        return "registrationResult"; 
    }
}

 

2. 컨트롤러 메서드의 반환 값 (공통 참조 데이터 추가)

해당 컨트롤러의 모든 요청 처리 전에 실행되어, 공통 데이터를 Model에 미리 설정합니다.

@ModelAttribute가 컨트롤러 클래스 내의 특정 메서드에 사용될 때, 이 메서드가 반환하는 객체를 @RequestMapping 등으로 매핑된 요청 핸들러 메서드가 사용하기 전에 Model에 자동으로 추가하도록 지정합니다.

이는 해당 컨트롤러 내의 여러 뷰에서 공통적으로 필요한 드롭다운 목록이나 사이트 메뉴 등을 Model에 미리 담아두는 데 유용합니다.

@Controller
public class CommonController {

    // 이 컨트롤러의 어떤 @RequestMapping 메서드가 호출되든,
    // 이 메서드가 먼저 실행되고 반환 값이 Model에 "commonData"라는 이름으로 추가됨
    @ModelAttribute("commonData")
    public List<String> getCommonData() {
        return Arrays.asList("메뉴1", "메뉴2", "메뉴3");
    }

    @GetMapping("/home")
    public String home() {
        // home.jsp에서 ${commonData}를 사용할 수 있음
        return "home";
    }

    @GetMapping("/about")
    public String about() {
        // about.jsp에서도 ${commonData}를 사용할 수 있음
        return "about";
    }
}

 

 

Spring MVC 게시판 CRUD 예제

1. Model(데이터 객체): 

// src/main/java/.../model/Board.java
public class Board {
    private Long id;
    private String title;
    private String content;

    // 생성자 (Constructor)
    public Board() {}

    public Board(Long id, String title, String content) {
        this.id = id;
        this.title = title;
        this.content = content;
    }

    // Getter와 Setter 생략 
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
}

 

2. Service(비지니스 로직)

// src/main/java/.../service/BoardService.java
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

@Service
public class BoardService {
    // 메모리 저장소 (실제로는 DB 연동)
    private final List<Board> boards = new ArrayList<>();
    private final AtomicLong counter = new AtomicLong(0); // ID 생성을 위한 카운터

    // 초기 데이터 설정
    public BoardService() {
        save(new Board(null, "첫 번째 글", "내용입니다."));
        save(new Board(null, "두 번째 글", "더 긴 내용입니다."));
    }

    // 1. Create: 새 글 저장
    public Board save(Board board) {
        board.setId(counter.incrementAndGet()); // 새 ID 할당
        boards.add(board);
        return board;
    }

    // 2. Read All: 모든 글 목록 조회
    public List<Board> findAll() {
        return boards;
    }

    // 2. Read One: 특정 ID의 글 조회
    public Board findById(Long id) {
        return boards.stream()
                .filter(board -> board.getId().equals(id))
                .findFirst()
                .orElse(null);
    }

    // 3. Update: 글 수정
    public Board update(Long id, Board updatedBoard) {
        Board existingBoard = findById(id);
        if (existingBoard != null) {
            existingBoard.setTitle(updatedBoard.getTitle());
            existingBoard.setContent(updatedBoard.getContent());
            return existingBoard;
        }
        return null; // 수정할 글이 없는 경우
    }

    // 4. Delete: 글 삭제
    public boolean delete(Long id) {
        return boards.removeIf(board -> board.getId().equals(id));
    }
}

 

3. Controller (요청 제어 및 View 전달)

HTTP 요청을 받아 서비스를 호출하고 결과를 Model에 담아 View 이름으로 반환합니다.

// src/main/java/.../controller/BoardController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/boards")
public class BoardController {

    private final BoardService boardService;

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

    // **1. Read All (목록 조회)**: GET /boards
    @GetMapping
    public String listBoards(Model model) {
        model.addAttribute("boards", boardService.findAll());
        return "boardList"; // boardList.jsp 또는 boardList.html로 이동
    }

    // **2. Create (등록 폼)**: GET /boards/new
    @GetMapping("/new")
    public String showCreateForm(Model model) {
        model.addAttribute("board", new Board()); // 빈 Board 객체를 모델에 담아 폼에 전달
        return "boardForm";
    }

    // **3. Create (등록 처리)**: POST /boards
    @PostMapping
    public String createBoard(@ModelAttribute Board board) {
        boardService.save(board);
        return "redirect:/boards"; // 목록 페이지로 리다이렉트
    }

    // **4. Read One (상세 조회)**: GET /boards/{id}
    @GetMapping("/{id}")
    public String viewBoard(@PathVariable Long id, Model model) {
        Board board = boardService.findById(id);
        if (board == null) {
            return "redirect:/boards"; // 글이 없으면 목록으로
        }
        model.addAttribute("board", board);
        return "boardDetail"; // boardDetail.jsp로 이동
    }

    // **5. Update (수정 폼)**: GET /boards/{id}/edit
    @GetMapping("/{id}/edit")
    public String showEditForm(@PathVariable Long id, Model model) {
        Board board = boardService.findById(id);
        if (board == null) {
            return "redirect:/boards";
        }
        model.addAttribute("board", board);
        return "boardForm"; // 등록 폼과 동일한 뷰 사용
    }

    // **6. Update (수정 처리)**: POST /boards/{id} (PUT/PATCH는 폼에서 지원하기 어려워 POST 사용)
    @PostMapping("/{id}")
    public String updateBoard(@PathVariable Long id, @ModelAttribute Board board) {
        boardService.update(id, board);
        return "redirect:/boards/" + id; // 상세 페이지로 리다이렉트
    }

    // **7. Delete (삭제 처리)**: POST /boards/{id}/delete
    @PostMapping("/{id}/delete")
    public String deleteBoard(@PathVariable Long id) {
        boardService.delete(id);
        return "redirect:/boards"; // 목록 페이지로 리다이렉트
    }
}

 

4. View(jsp / Thymeleaf)

여기서는 jsp를 예시로 듭니다.

boardList.jsp 목록 조회

<h2>게시글 목록</h2>
<p><a href="/boards/new">새 글 등록</a></p>
<table border="1">
    <tr>
        <th>ID</th>
        <th>제목</th>
        <th>관리</th>
    </tr>
    <c:forEach var="board" items="${boards}">
    <tr>
        <td>${board.id}</td>
        <td><a href="/boards/${board.id}">${board.title}</a></td>
        <td>
            <a href="/boards/${board.id}/edit">수정</a>
        </td>
    </tr>
    </c:forEach>
</table>

 

boardForm.jsp(등록 및 수정 폼)

<h2>${board.id == null ? '새 글 등록' : '글 수정'}</h2>

<form action="/boards${board.id != null ? '/' : ''}${board.id != null ? board.id : ''}" method="POST">
    <p>
        제목: <input type="text" name="title" value="${board.title}" required/>
    </p>
    <p>
        내용: <textarea name="content" required>${board.content}</textarea>
    </p>
    <p>
        <button type="submit">${board.id == null ? '등록' : '수정'}</button>
        <a href="/boards">목록으로</a>
    </p>
</form>

 

boardDetail.jsp(상세조회)

<h2>${board.title}</h2>
<p>
    ID: ${board.id}
</p>
<p>
    내용: ${board.content}
</p>
<p>
    <a href="/boards/${board.id}/edit">수정</a>

    <form action="/boards/${board.id}/delete" method="POST" style="display:inline;">
        <button type="submit" onclick="return confirm('정말로 삭제하시겠습니까?');">삭제</button>
    </form>

    <a href="/boards">목록으로</a>
</p>

 

RUD 동작 HTTP 메서드 URI 패턴 Controller 메서드 View
Create (등록 폼) GET /boards/new showCreateForm boardForm
Create (처리) POST /boards createBoard redirect:/boards
Read (목록) GET /boards listBoards boardList
Read (상세) GET /boards/\{id\} viewBoard boardDetail
Update (수정 폼) GET /boards/\{id\}/edit showEditForm boardForm
Update (처리) POST /boards/\{id\} updateBoard redirect:/boards/\{id\}
Delete (처리) POST /boards/\{id\}/delete deleteBoard redirect:/boards

 

 

 

 

 

 

 

 

 

 

 

728x90

'DEV&OPS > Java' 카테고리의 다른 글

Spring MVC PostgreSQL CRUD 예제  (0) 2026.07.16
JWT 인증  (0) 2026.07.16
스프링 부트 웹 애플리케이션에서 카카오 로그인 처리  (0) 2026.07.16
JPA 연관 관계  (0) 2026.07.11
인텔리제이 주석 템플릿 설정  (0) 2026.07.06