새소식

Spring

[Spring] Spring MVC 구조

  • -

 

 

MVC 패턴

 

1)    MVC 패턴의 개념

 

MVC는 소프트웨어 아키텍처 패턴으로, 애플리케이션을 Model, View, Controller 세 가지 주요 구성 요소로 분리합니다.

 

MVC 패턴은 유연성, 재사용성, 유지보수 용이성 등의 이점을 제공하지만,

복잡성과 추가적인 코드량이 증가할 수 있는 단점도 있습니다.

 

 

 

2)    MVC 패턴의 구성 요소

 

 

① Model

Model은 애플리케이션의 데이터와 비즈니스 로직을 담당합니다.

 

데이터베이스, 파일 시스템, 외부 API 등을 통해 데이터를 저장하고 관리하며,

데이터의 유효성 검사, 업데이트, 검색 등을 수행합니다.

 

Model은 보통 독립적으로 설계되어 재사용 가능하며,

데이터 변경이 발생하면 이를 View와 Controller에 알리는 역할을 수행합니다.

 

● 예) 사용자 정보, 상품 데이터, 주문 처리 로직 등

 

 

 

② View

View는 사용자의 입력을 받아들이지 않고, 오로지 데이터의 표현과 출력을 담당합니다.

 

데이터를 시각적으로 표현하고 사용자 인터페이스를 구성합니다.

(주로 웹 애플리케이션에서는 HTML, CSS, JavaScript를 사용)

 

● 예) 웹 애플리케이션에서의 UI, 모바일 앱의 화면 등

 

 

 

③ Controller

Controller는 Model과 View 간의 조정과 상호작용을 담당합니다.

사용자 입력을 받아 Model을 업데이트하거나, Model의 변경을 View에 반영합니다.

 

● 예) 웹 애플리케이션에서 URL 라우팅, 사용자 입력 처리, 비즈니스 로직 호출 등

 

 

 

④ ModelAndView

● 데이터와 뷰 연결

데이터는 Model 객체에 저장되고, 뷰는 화면에 해당 데이터를 표시합니다.

ModelAndView 객체는 컨트롤러가 처리한 결과 데이터를 담고, 이를 뷰에 전달하여 화면에 표시합니다.

 

 

 

 

 

 

3)    MVC 패턴에 지켜져야할 점!

 

① Model은 View와 Controller에 의존되지 않아야한다.

Model 내부에 View와 Controller와 관련된 내용이 있으면 안됩니다.

 

 

② View는 Model에만 의존하고, Controller에는 의존하면 안된다.

View 내부에는 Model 코드만 있을 수 있고, Controller의 코드가 있으면 안됩니다.

 

 

③ View가 Model로부터 데이터를 받을 때는, 사용자에게 다르게 보여줘야하는 데이터만 받아야한다.

 

 

 

View가 Model로부터 데이터를 받을 때는, 반드시 Controller에서 받아야한다.

View 내부에는 Model 코드만 있을 수 있고, Controller의 코드가 있으면 안됩니다.

 

 

⑤ Controller는 Model과 View에 의존하여도 된다.

즉, Controller 내부에는 Model과 View 코드가 있을 수 있습니다.

 

 

 

 

 

 

 

4)    MVC 1

 

MVC1

 

① MVC1의 개념

Model-View-Controller 1의 약자로, 초기의 MVC 패턴 구현 방식을 의미합니다.

 

Model은 데이터와 비즈니스 로직을 담당하고,

View는 데이터의 시각적 표현을 담당하며,

Controller는 사용자 입력을 처리하고 Model과 View 간의 흐름을 조절합니다.

 

 

 

② MVC1의 특징

 

● 간단한 구조

단순하고 직관적인 구조로, 쉽게 이해하고 적용할 수 있다.

 

View와 Controller의 결합

View와 Controller가 밀접하게 결합되어 작동합니다. View는 Controller에 직접 접근하여 데이터나 이벤트를 처리합니다.

 

● 사용자 중심 설계

사용자 입력을 처리하고 결과를 즉시 보여주는 데 강점을 가지고 있습니다.

 

 

③ MVC1의 예시

 

Model

Pokemon.java

public class Pokemon {
    private String name;
    private int level;
    private String skill;

    public Pokemon(String name, int level, String skill) {
        this.name = name;
        this.level = level;
        this.skill = skill;
    }

    public String getName() {
        return name;
    }

    public int getLevel() {
        return level;
    }

    public String getSkill() {
        return skill;
    }
}

 

 

View

Pokedex.java

public class Pokedex {
    public void displayPokemonInfo(Pokemon pokemon) {
        // 포켓몬 정보 시각적으로 표시
        System.out.println("Pokemon Info");
        System.out.println("Name: " + pokemon.getName());
        System.out.println("Level: " + pokemon.getLevel());
        System.out.println("Skill: " + pokemon.getSkill());
    }
}

 

 

Controller

PokemonController.java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class PokemonController {
    private Pokedex pokedex;

    public PokemonController() {
        pokedex = new Pokedex();
    }

    public void handlePokemonDetailsRequest(String pokemonName) {
        // 포켓몬 데이터 조회 및 요청 처리
        Pokemon pokemon = fetchPokemonData(pokemonName);

        // View에 데이터 전달
        pokedex.displayPokemonInfo(pokemon);
    }

    private Pokemon fetchPokemonData(String pokemonName) {
        // 데이터베이스 연결 정보 설정
        String url = "jdbc:mysql://localhost:3306/pokemon_db";
        String username = "username";
        String password = "password";

        // 데이터베이스 연결 객체 생성
        try (Connection connection = DriverManager.getConnection(url, username, password)) {
            // SQL 쿼리 준비
            String query = "SELECT * FROM pokemon WHERE name = ?";
            PreparedStatement statement = connection.prepareStatement(query);
            statement.setString(1, pokemonName);

            // 쿼리 실행 및 결과 조회
            ResultSet resultSet = statement.executeQuery();
            if (resultSet.next()) {
                // 조회 결과로부터 포켓몬 정보 추출
                String fetchedName = resultSet.getString("name");
                int fetchedLevel = resultSet.getInt("level");
                String fetchedSkill = resultSet.getString("skill");

                // 포켓몬 객체 생성 및 반환
                return new Pokemon(fetchedName, fetchedLevel, fetchedSkill);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        // 데이터베이스 조회 실패 시, 빈 포켓몬 객체 반환
        return new Pokemon("", 0, "");
    }
}

 

PokemonController는 사용자의 요청을 처리하고 데이터를 조회한 후,

PokedexdisplayPokemonInfo 메서드를 호출하여 데이터를 전달합니다.

 

이로써 View와 Controller가 결합되어 있는 MVC1 패턴의 구조를 갖추게 됩니다.

 

 

 

 

 

 

 

 

5)    MVC 2

 

MVC2

 

① MVC2의 개념

Model-View-Controller 2의 약자로, 초기의 MVC 패턴보다 발전된 구현 방식

 

View와 Controller의 분리로 유연한 UI 디자인과 테스트 용이성을 제공합니다.

그러나 복잡한 애플리케이션에서는 구조의 복잡성, 초기개발비용의 증가를 초래하였습니다.

 

Model은 데이터와 비즈니스 로직을 담당하고, 

View는 데이터의 시각적 표현을 담당하며, 

Controller는 사용자 입력을 처리하고 Model과 View 간의 흐름을 조절합니다.

 

 

② MVC2의 특징

 

View와 Controller 분리

MVC2에서는 View와 Controller가 분리되어 작동합니다. 

View는 직접적으로 Model에 접근하지 않습니다. 대신, Controller를 통해 Model과 통신합니다.

 

 

③ MVC2의 예시

 

Model

Pokemon.java

public class Pokemon {
    private String name;
    private int level;
    private String skill;

    public Pokemon(String name, int level, String skill) {
        this.name = name;
        this.level = level;
        this.skill = skill;
    }

    public String getName() {
        return name;
    }

    public int getLevel() {
        return level;
    }

    public String getSkill() {
        return skill;
    }
}

 

 

View

Pokedex.java

public class Pokedex {
    public void displayPokemonInfo(Pokemon pokemon) {
        // 포켓몬 정보 시각적으로 표시
        System.out.println("Pokemon Info");
        System.out.println("Name: " + pokemon.getName());
        System.out.println("Level: " + pokemon.getLevel());
        System.out.println("Skill: " + pokemon.getSkill());
    }
}

 

 

Controller

PokemonController.java

public class PokemonController {
    private Pokedex pokedex;
    private PokemonService pokemonService;

    public PokemonController() {
        pokedex = new Pokedex();
        pokemonService = new PokemonService();
    }

    public void handlePokemonDetailsRequest(String pokemonName) {
        // 포켓몬 데이터 조회 및 요청 처리
        Pokemon pokemon = pokemonService.fetchPokemonData(pokemonName);

        // View에 데이터 전달
        pokedex.displayPokemonInfo(pokemon);
    }
}

 

 

Service

PokemonController.java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class PokemonService {
    public Pokemon fetchPokemonData(String pokemonName) {
        // 데이터베이스 연결 정보 설정
        String url = "jdbc:mysql://localhost:3306/pokemon_db";
        String username = "username";
        String password = "password";

        // 데이터베이스 연결 객체 생성
        try (Connection connection = DriverManager.getConnection(url, username, password)) {
            // SQL 쿼리 준비
            String query = "SELECT * FROM pokemon WHERE name = ?";
            PreparedStatement statement = connection.prepareStatement(query);
            statement.setString(1, pokemonName);

            // 쿼리 실행 및 결과 조회
            ResultSet resultSet = statement.executeQuery();
            if (resultSet.next()) {
                // 조회 결과로부터 포켓몬 정보 추출
                String fetchedName = resultSet.getString("name");
                int fetchedLevel = resultSet.getInt("level");
                String fetchedSkill = resultSet.getString("skill");

                // 포켓몬 객체 생성 및 반환
                return new Pokemon(fetchedName, fetchedLevel, fetchedSkill);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        // 데이터베이스 조회 실패 시, 빈 포켓몬 객체 반환
        return new Pokemon("", 0, "");
    }
}

 

포켓몬 데이터 조회 로직은 Service 계층에 해당하는 PokemonService 클래스에 구현되었습니다.

 

 

 

 

 

 

 

 

6)    Spring MVC 패턴

 

 

SPRING MVC PATTERN

 

① Spring MVC 패턴의 개념

웹 응용 프로그램 개발을 위한 MVC(Model-View-Controller) 아키텍처 패턴을 구현하는 프레임워크

( 일반적으로 MVC2 패턴을 기반으로 합니다)

 

 

 

② Spring MVC 패턴의 장점

● 코드의 재사용성 및 유지보수성이 향상
● 뷰와 모델의 분리 → 사용자 인터페이스 변경이 용이
● 테스트의 용이
● Spring 프레임워크의 다른 기능과 통합 되어 사용가능 

 

 

 

③ Spring MVC와 기존 MVC2의 차이

의존성 주입

의존성 주입(Denpendency Injection) 기능을 통해 객체 간의 의존성을 관리합니다. DI를 통해 컴포넌트들을 연결하고 필요한 객체를 주입받을 수 있으며, 이는 유연하고 테스트 가능한 코드를 작성할 수 있게 해줍니다.

 

강력한 설정 및 확장 가능성

XML 또는 Java Config를 통해 세부적인 설정을 제공하며, 다양한 확장 가능성을 제공합니다.

 

AOP

AOP(Aspect Oriented Programming)를 지원하여, 애플리케이션에서의 공통 관심 사항(Cross-cutting Concerns)을 분리하여 모듈화할 수 있습니다.

 

 

 

 

 

④ Spring MVC의 주요 Component

MVC (Model, View, Controller) 패턴에 Spring MVC에서는 추가적인 컴포넌트들이 개입되어 기능을 확장하고 보완합니다.

 

 

Component 설명
DispatcherServlet    클라이언트의 요청을 받고, 요청을 처리할 컨트롤러를 찾아서 호출하고, 응답을 반환하는 역할을 합니다.
HandlerMapping    요청 URL과 컨트롤러 간의 매핑을 담당합니다.
   클라이언트의 요청이 어떤 컨트롤러로 전달되어야 하는지 결정합니다.
Controller    요청을 처리하고, 비즈니스 로직을 수행하는 역할을 합니다.
   요청을 받아서 처리한 뒤에 응답 데이터를 생성합니다.
ModelAndView    컨트롤러에서 처리된 데이터와 뷰의 이름을 담아서 반환하는 객체입니다.
   데이터와 뷰 정보를 함께 전달할 수 있습니다.
ViewResolver    뷰의 이름을 기반으로 실제 뷰 객체를 찾아내는 역할을 합니다.
   뷰의 이름을 물리적인 뷰 파일과 매핑시킵니다.
View    클라이언트에게 응답을 보여주는 뷰를 나타냅니다.
   HTML, JSON, XML 등 형태로 데이터를 표현하여 클라이언트에게 보여줍니다.

 

 

 

 

⑤ Spring MVC의 예시1

 

 

Model

Pokemon.java

public class Pokemon {
    private String name;
    private int level;
    private String skill;
    // 포켓몬에 대한 변수 선언
    

    public Pokemon(String name, int level, String skill) {
        this.name = name;
        this.level = level;
        this.skill = skill;
    }
    // 생성자를 이용해 생성
    

    // Getters and Setters 생략

    
    @Override
    public String toString() {
        return "Pokemon [name=" + name + ", level=" + level + ", skill=" + skill + "]";
    }
}

 

● @Override

Pokemon 클래스에 toString() 메서드를 오버라이딩한 부분

System.out.println(pokemon)를 통해 출력할 경우, pokemonPokemon 객체를 나타내며,

System.out.println() 메서드는 객체의 toString() 메서드를 자동으로 호출하여 문자열로 변환한 뒤 출력합니다.

 

Pokemon 객체의 이름이 "Pikachu", 레벨이 10, 스킬이 "Thunderbolt"인 경우

toString() 메서드는 "Pokemon [name=Pikachu, level=10, skill=Thunderbolt]"와 같은 문자열을 반환할 것입니다.

 

 

Service

PokemonService.java

import java.util.HashMap;
import java.util.Map;

public class PokemonService {
    private Map<String, Pokemon> pokemonMap;
    // Map 타입의 pokemonMap이라는 인스턴스변수 선언


    public PokemonService() {
    // PokemonService 생성자
        pokemonMap = new HashMap<>();
        // 임의의 포켓몬 데이터 추가 (PokemonMap 변수를 HashMap 클래스 인스턴스로 초기화)
        // 즉 HashMap 객체를 생성하여 pokemonMap 변수에 할당함
        pokemonMap.put("Pikachu", new Pokemon("Pikachu", 10, "Thunderbolt"));
        pokemonMap.put("Charizard", new Pokemon("Charizard", 30, "Fire Blast"));
    }

    public Pokemon getPokemonByName(String name) {
    	// name 매개변수를 받고 Pokemon 객체를 반환하는 메소드입니다. 
        return pokemonMap.get(name);
    }

    public void trainPokemon(Pokemon pokemon) {
        // 훈련 작업 수행
        // 예: 포켓몬의 레벨 상승, 스킬 배우기 등
        pokemon.setLevel(pokemon.getLevel() + 1);
        pokemon.setSkill("New Skill");
    }
}

 

 

Controller

PokemonController.java

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

@Controller
public class PokemonController {
    private PokemonService pokemonService;
    // PokemonService 타입의 pokemonService 멤버 변수를 선언합니다.

    public PokemonController(PokemonService pokemonService) {
        this.pokemonService = pokemonService;
    }
    // PokemonController의 생성자입니다. 
    // PokemonService 객체를 매개변수로 받아 멤버 변수 pokemonService에 할당합니다.


    @GetMapping("/pokemon/{name}")
    // GET 요청의 URL 경로가 "/pokemon/{name}"인 경우 이 메서드를 실행합니다.
    public String getPokemonDetails(@PathVariable String name, Model model) {
    // PathVariable : URL 경로에서 변수 값을 추출하는데 사용되는 어노테이션
    // name이라는 경로 변수와 Model 객체를 매개변수로 받는 getPokemonDetails 메서드
        Pokemon pokemon = pokemonService.getPokemonByName(name);
        model.addAttribute("pokemon", pokemon);
    // Model 객체에 pokemon을 "pokemon"이라는 이름으로 추가합니다.
        return "pokemon-details";
    }

    @PostMapping("/pokemon/train/{name}")
    // POST 요청의 URL 경로가 "/pokemon/train/{name}"인 경우 이 메서드를 실행합니다.
    public String trainPokemon(@PathVariable String name, Model model) {
	// name이라는 경로 변수와 Model 객체를 매개변수로 받는 trainPokemon 메서드
        Pokemon pokemon = pokemonService.getPokemonByName(name);
        pokemonService.trainPokemon(pokemon);
        model.addAttribute("pokemon", pokemon);
        return "pokemon-details";
    }
}

 

 

View

pokemon-details.jsp

<!DOCTYPE html>
<html>
<head>
    <title>Pokemon Details</title>
</head>
<body>
    <h1>Pokemon Details</h1>
    <h3>Name: ${pokemon.name}</h3>
    <p>Level: ${pokemon.level}</p>
    <p>Skill: ${pokemon.skill}</p>
    <form action="/pokemon/train/${pokemon.name}" method="post">
    // 포켓몬을 훈련하기 위한 폼을 생성합니다. action 속성은 폼을 제출할 URL을 설정합니다. 
        <button type="submit">Train Pokemon</button>
    </form>
</body>
</html>

 

$

${pokemon.name}과 같은 EL (Expression Language) 표현식은 해당 정보를 가져오는 데 사용됩니다.

 

Spring MVC에서 Thymeleaf나 JSP와 같은 서버 측 템플릿 엔진에서 사용되며, 서버에서 전달된 데이터를 동적으로 표시하고 처리하는 용도로 활용됩니다.

 

${pokemon.name}pokemon 객체의 name 속성 값을 가져와서 HTML에 표시하는 역할을 합니다.

이를 통해 동적인 데이터를 서버 측에서 처리하여 웹 페이지에 반영할 수 있습니다.

 

 

 

 

 

 

⑤ Spring MVC의 예시2 (ModelAndView 이용)

 

Model

Pokemon.java

public class Pokemon {
    private String name;
    private int level;
    private String skill;

    public Pokemon(String name, int level, String skill) {
        this.name = name;
        this.level = level;
        this.skill = skill;
    }

    // Getters and Setters

    // Override toString() method for printing the Pokemon object
    @Override
    public String toString() {
        return "Pokemon [name=" + name + ", level=" + level + ", skill=" + skill + "]";
    }
}

 

 

Controller

PokemonController.java

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

@Controller
public class PokemonController {
    private PokemonService pokemonService;

    public PokemonController(PokemonService pokemonService) {
        this.pokemonService = pokemonService;
    }

    @GetMapping("/pokemon")
    public ModelAndView getPokemonDetails() {
        ModelAndView modelAndView = new ModelAndView("pokemon-details");
        modelAndView.addObject("pokemon", pokemonService.getPokemon());
        return modelAndView;
    }

    @PostMapping("/pokemon/train")
    public ModelAndView trainPokemon() {
        ModelAndView modelAndView = new ModelAndView("pokemon-details");
        Pokemon pokemon = pokemonService.getPokemon();
        pokemonService.trainPokemon(pokemon);
        modelAndView.addObject("pokemon", pokemon);
        return modelAndView;
    }
}

 

 

Service

PokemonService.java

import org.springframework.stereotype.Service;

@Service
public class PokemonService {
    private Pokemon pokemon;

    public PokemonService() {
        // 임의의 포켓몬 데이터 생성
        pokemon = new Pokemon("Pikachu", 10, "Thunderbolt");
    }

    public Pokemon getPokemon() {
        return pokemon;
    }

    public void trainPokemon(Pokemon pokemon) {
        // 포켓몬 훈련 작업 수행
        // 예: 레벨 증가, 새로운 스킬 배우기 등
        pokemon.setLevel(pokemon.getLevel() + 1);
        pokemon.setSkill("New Skill");
    }
}

 

 

View

pokemon-details.jsp

<!DOCTYPE html>
<html>
<head>
    <title>Pokemon Details</title>
</head>
<body>
    <h1>Pokemon Details</h1>
    <h3>Name: ${pokemon.name}</h3>
    <p>Level: ${pokemon.level}</p>
    <p>Skill: ${pokemon.skill}</p>

    <form action="/pokemon/train" method="post">
        <button type="submit">Train Pokemon</button>
    </form>
</body>
</html>

 

 

 

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.