백엔드/Spring

[Spring] 스프링 MVC - 구조 이해

happy_life 2022. 7. 11. 11:02

목차

1. 스프링 구조

2. 컨트롤러 통합하기

3.실용적인 방식

 

 

 

 

 

1. 스프링MVC 구조

1) 개요

스프링의 구조

 

스프링은 위의 사진처럼 동작한다

동작 순서

1.핸들러 조회: 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회한다.

2. 핸들러 어댑터 조회: 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다. 

3.핸들러 어댑터 실행

4. 핸들러(컨트롤러) 실행

5. ModelView 반환: 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelView로 변환 후 반환한다.

6. viewResolver호출: 뷰 리졸버를 찾고 실행한다.

7. view 반환: 뷰 리졸버는 뷰의 논리 이름을 물리이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환한다

8. 뷰 렌더링: 뷰를 렌더링한다. 

 

 

현재의 스프링은 어노테이션 기반으로 동작한다. 기존 코드와 같이 보고 차이를 느껴보자.

 

기존 코드

직접 구현했던 코드

 

 

스프링 코드

스프링 코드

 

 

위의 코드는 어노테이션 기반으로 어떻게 동작할까? 아래의 순서를 보고 이해하자.

 

1. @Controller 어노테이션이 있는 클래스는 스프링 빈으로 등록된다.

2. 등록된 스프링 빈에서 Controller를 찾는다.

3. process()가 동작하여 ModelView를 반환한다.

4. ModelView가 반환되면 기존의 단계를 쭉 스프링이 실행해주는 것이다.

 

 

 

2. 컨트롤러 통합하기

1) 개요

하지만 이렇게 코드를 작성하면 각각의 컨트롤러마다, 클래스를 생성해줘야한다.

 

 

 

이렇게 컨트롤러를 각각 만드는 것은 여간 귀찮은 것이 아니다. 다행히 우리는 하나의 컨트롤러에 관련된 동작을 수행하는 메서드를 통합해 사용할 수 있다.

 

코드 예제

@Controller
@RequestMapping("/union")
public class UnionMemberController {
    private final MemberRepository memberRepository = MemberRepository.getInstance();

    @RequestMapping("/form")
    public ModelAndView newForm() {
        return new ModelAndView("new-form");
    }

    @RequestMapping("/save")
    public ModelAndView save(HttpServletRequest request, HttpServletResponse response) {
        String name = request.getParameter("name");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(name, age);
        memberRepository.save(member);

        ModelAndView mv = new ModelAndView("member-save");
        mv.addObject("member", member);
        return mv;
    }

    @RequestMapping("/list")
    public ModelAndView members() {
        List<Member> members = memberRepository.findAll();
        ModelAndView mv = new ModelAndView("member-list");

        mv.addObject("members", members);
        return mv;
    }
}

 

한편, 클래스 단위의 @RequestMapping으로 중복된 코드 또한 지워주었다.

예를 들어 메서드의 RequestMapping("/form")은  클래스의 "/union" + "/form" -> "/union/form"으로 인식될 수 있다.

 

3. 실용적인 방식

하나의 클래스 내에서 동작하게 했음에도 불구하고, ModelView를 반환하는 코드도 길고, save같은 경우는 request,response로 받은 정보를 Integer로 파싱하는 등 번거로운 부분이 아직 남아있다. 스프링은 이런 귀찮음 또한 해결하였는데, 이를 간단히 실용적인 방식이라고 표현해보았다.

 

기존 코드

@RequestMapping("/save")
    public ModelAndView save(HttpServletRequest request, HttpServletResponse response) {
        String name = request.getParameter("name");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(name, age);
        memberRepository.save(member);

        ModelAndView mv = new ModelAndView("member-save");
        mv.addObject("member", member);
        return mv;
    }

위의 기존 코드를 실용적인 방식으로 수정해보자.

 

수정 코드

    @RequestMapping("/save")
    public String save(
            @RequestParam("name") String name,
            @RequestParam("age") int age,
            Model model) {
        
        Member member = new Member(name, age);
        memberRepository.save(member);

        model.addAttribute("member", member);
        return "member-save";
    }

 

변경점

1. @RequestParam

스프링에서는 request, response도 받을 수 있지만, 매개변수를 직접 @RequestParam을 통해 받을 수 있다. 받으면서 형을 자동으로 변환해준다.  

 

2. return 타입

스프링은 유연하게 설계되어 있기 때문에, ModelView를 직접 반환하지 않고 이름만 반환해도 동작한다.

 

3. ModelView -> Model

그저 Model이라는 클래스의 객체에 담으면 된다.

 

한편, 이 코드는 GET, POST 등 통신방식을 고려하지 않은 것이다. 이런 방식을 지정해주려면

@RequstMapping 대신 @GetMapping, @PostMapping을 사용해야 한다.

 

 

 

본 포스팅은 김영한님 인프런 강의내용을 바탕으로 복습을 위해 작성하였습니다. 강의를 통해 배운 개념을 바탕으로 추가적으로 공부한 부분과 간단한 코드 예제를 작성하였습니다. 코드 전체를 복사한 것이 아니라 임의로 수정하거나 생략하였습니다.