본문 바로가기

신세계 국비과정/신세계 - Spring 공부

15주차 배운점 - 퍼사드 패턴, 컨트롤러, Formatter, 예외처리, Mapper

728x90
반응형

 

퍼사드(Facade) 패턴

 

소프트웨어 설계 패턴 중 하나로, 복잡한 시스템에 대한 단순화된 인터페이스를 제공합니다. 이 패턴의 주요 목적은 서브시스템의 복잡성을 숨기고, 클라이언트가 사용하기 편리한 고수준의 인터페이스를 제공함으로써 클라이언트와 복잡한 서브시스템 간의 의존성을 줄이는 것입니다.

 

퍼사드 패턴 특징

 

단순화된 접근: 클라이언트는 복잡한 서브시스템을 직접 다루지 않고, 퍼사드를 통해 간접적으로 접근합니다. 이를 통해 사용법이 간단해지고, 구현 세부 사항에 대한 이해 없이도 서브시스템의 기능을 사용할 수 있습니다.

 

시스템 분리: 퍼사드는 시스템의 구성 요소를 분리하여, 서브시스템의 변경이 클라이언트에 미치는 영향을 최소화합니다. 이로 인해 시스템의 유지보수성과 확장성이 향상됩니다.

 

인터페이스 통합: 복잡한 서브시스템에 여러 인터페이스가 존재할 경우, 퍼사드는 이들 인터페이스를 통합하여 단일 인터페이스를 제공합니다. 이는 클라이언트가 서브시스템을 더 쉽게 사용할 수 있게 해줍니다.

 

Front Controller: 모든 요청을 처음 받는 중앙 진입점입니다. 이 컴포넌트는 요청을 적절한 핸들러나 컨트롤러로 전달하기 전에 필요한 사전 처리(인증 검사, 로깅, 요청 데이터의 사전 처리 등)를 수행합니다.

 

Handler Mapping: 요청 URL과 처리할 수 있는 컨트롤러 간의 매핑을 관리합니다. Front Controller는 이 매핑 정보를 사용하여 각 요청을 처리할 적절한 컨트롤러를 결정합니다.

 

Controller: 실제 비즈니스 로직을 처리하는 컴포넌트입니다. 요청에 따라 데이터를 처리하고, 처리 결과를 바탕으로 사용자에게 보여줄 뷰를 결정합니다.

 

View: 사용자에게 응답을 보여주는 역할을 합니다. 컨트롤러로부터 받은 모델 데이터를 사용하여 사용자가 볼 수 있는 형태로 렌더링합니다.

 

Dispatcher: Controller가 처리한 결과와 선택된 View를 연결하고, 최종적으로 사용자에게 응답을 보내는 역할을 합니다.

 

작동 방식

 

요청 수신: 사용자의 요청은 먼저 Front Controller에 도달합니다. 이는 대개 웹 애플리케이션에서 단일 서블릿(예: 스프링의 DispatcherServlet)이 이 역할을 수행합니다.

 

요청 분석 및 전처리: Front Controller는 요청의 URL, HTTP 메소드, 헤더 등을 분석하여 어떤 컨트롤러가 요청을 처리할 수 있는지 결정합니다. 필요한 경우, 인증 검사, 로깅, 요청 데이터의 사전 처리와 같은 전처리 작업을 수행합니다.

 

컨트롤러 실행: Handler Mapping을 사용하여 결정된 컨트롤러를 호출합니다. 컨트롤러는 비즈니스 로직을 실행하고, 결과 데이터를 모델에 저장한 후, 응답으로 사용할 뷰를 결정합니다.

 

뷰 렌더링: 결정된 뷰는 모델 데이터를 사용하여 최종 사용자 응답을 생성합니다. 이 과정에서 HTML, JSON, XML 등 다양한 형식의 응답을 준비할 수 있습니다.

 

응답 반환: 최종적으로 생성된 응답은 사용자에게 반환됩니다. 이 과정은 Dispatcher에 의해 수행될 수 있습니다.

 

@GetMapping

 

스프링 부트(Spring Boot)에서 @GetMapping 어노테이션은 프론트 컨트롤러 패턴의 일환으로 사용되며, HTTP GET 요청을 특정 메소드에 매핑하기 위해 사용됩니다. 스프링 MVC 프레임워크 내에서, @GetMapping은 @RequestMapping 어노테이션의 특수한 형태로, method = RequestMethod.GET을 기본값으로 사용하는 것과 동일합니다. 이를 통해 웹 애플리케이션의 특정 경로에 대한 GET 요청을 처리할 메소드를 지정할 수 있습니다.

 

프론트 컨트롤러와 @GetMapping

 

프론트 컨트롤러 패턴에서는 모든 요청이 단일 진입점을 통해 처리됩니다. 스프링 MVC에서는 DispatcherServlet이 이 역할을 수행하며, 모든 웹 요청을 적절한 핸들러(컨트롤러)에 전달합니다. @GetMapping을 사용하는 컨트롤러 메소드는 DispatcherServlet에 의해 관리되는 요청 처리 흐름의 일부입니다.

 

-> 원래는 기능별로 컨트롤러 jsp 파일 하나 이어붙였지만 그래서 이거 이용하면 이전과 다르게 하나의 컨트롤러에 다 작성가능하네

 

최종결론 프론트컨트롤러를 설명

 

스프링 MVC에서는 DispatcherServlet이 이 역할을 합니다. 클라이언트는 단순히 URL을 요청합니다. 그러면 프론트 컨트롤러(DispatcherServlet)는 요청의 유형(GET, POST 등)을 판단하고, 적절한 컨트롤러 메서드에 요청을 전달하며, 최종적으로 어떤 뷰를 보여줄지 결정합니다

 

스프링 웹 MVC에서 컨트롤러

 

상속이나 인터페이스를 구현하는 방식을 사용하지 않고 어노테이션만으로 처리가 가능
오버라이드 없이 필요한 메서드들을 정의
메서드의 파라미터를 기본자료형이나 객체자료형을 마음대로 지정
메서드의 리턴타입도 void, String,객체등 다양한 타입을 사용할 수 있음

 

@Controller// 컨트롤러 역할
@Log4j2
public class SampleController {

    @GetMapping("/hello") // doPost 이런거 안함 , GetMapping PostMaping 으로 
    // 스프링의 빈으로 처리되기 위해 어노테이션을 달았다.
    //hello()라는 기능을 가지고 있는데 get 방식으로 들어오는 hello라는 요청을 처리하는 기능이다.
    public void hello(){
        log.info("hello~~~~");
    }

}

 

컨트롤러 어노테이션이 된애들 빈으로 만들기 위해 sevelet-context.xml에 추가
해당경로에 컴포넌트 스캔해라!

 

<context:component-scan base-package="com.ssg.springex.controller"/>

 

jsp파일 만들기

<html>
<head>
    <title>Hello</title>
</head>
<body>
    <h1>Hello JSP !</h1>
</body>
</html>

 

/hello 로 접속

 

 

@RequestMapping과 파생 어노테이션들

 

특정경로를 지정하는 어노테이션
@RequestMapping은 경로를 처리하기 위한 용도로 사용
스프링 MVC의 경우 하나의 클래스로 여러 경로를 처리
4버전 이후 @GetMapping, @PostMapping 등이 추가

 

TodoContllor

 

@Controller
@RequestMapping("/todo") // 경로지정
@Log4j2
public class TodoController {

    @RequestMapping("/list") // /todo/list로 요청
    public void list(){
        log.info("todo list........")    ;
    }

    @RequestMapping(value = "/register", method = RequestMethod.GET)
    // 얘는 get방식
    public void register(){
        log.info("todo register.....");
    }
}

 

list.jsp

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello</title>
</head>
<body>
    <h1>todo List!</h1>
</body>
</html>

 

/todo/list 로 접속
/todo/register 로 접속

 

 

@RequestMapping 이용하면, 여러개의 컨트롤러를 하나의 클래스로 묶을 수 있고, 각 기능을 메소드 단위로 설계할 수 있다는 장점이 있다. 많은 양의 코드를 줄일 수 있다.

 

//@RequestMapping(value = "/register", method = RequestMethod.GET)
    @GetMapping("/register")
    public void register(){
        log.info("todo register.....");
    }

    @PostMapping("/register")
    public void registerPost(){
        log.info("post todo register ....");
    }

 

같은경로를 각각 get과 post로 요청받는다.

 

@GetMapping("ex1")
    public void ex1(String name, int age){
        log.info("ex1");
        log.info("name : "+name);
        log.info("age : "+age);
    }

 

/ex1?name=JKY&age=20 로 접속

 

 

파라미터의 자동 수집과 변환

 

DTO나 VO등으로 자동으로 HttpServletRequest의 파라미터 수집
기본자료형의 경우는 자동으로 형 변환처리가 가능
객체자료형의 경우는 setXXX( )의 동작을 통해서 처리
객체자료형의 경우 생성자가 없거나 파라미터가 없는 생성자가 필요

 

@RequestParam: 파라미터의 이름을 지정하거나 기본값(defaultValue)를 지정할 수 있음
@ModelAttribute를 이용해서 명시적으로 해당 파라미터를 view까지 전달하도록 구성할 수 있음

 

파라미터 기본값 지정:

 

@GetMapping("ex2")
    public void ex2(@RequestParam(name = "name", defaultValue = "SSG") String name,
                    @RequestParam(name = "age", defaultValue = "20") int age){ 
        // 디폴트밸류 만들어주기
        // 값 전달해 주지 않아도 디폴트 밸류로 처리
        log.info("ex1");
        log.info("name : "+name);
        log.info("age : "+age);
    }

 

/todo/ex2 로 접속, 전달값 없이 하니까 디폴트값 나옴

 

Formatter를 이용한 커스텀 처리

 

날짜나 형 변환을 커스터마이징 해야 하는 경우 주로 사용
컨트롤러는 다 String 사용

 

@GetMapping("ex3")
    public void ex3(LocalDate dueDate){
        log.info("ex3 ...");
        log.info("dueDate "+dueDate);
    }

 

/todo/ex3?dueDate=2024-03-10 접속하면 400에러뜸

 

 

Formatter 등록


날짜 변환

 

public class LocalDateFormatter implements Formatter<LocalDate> {

    @Override
    public LocalDate parse(String text, Locale locale) throws ParseException {
        return LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }

    @Override
    public String print(LocalDate object, Locale locale) {
        return DateTimeFormatter.ofPattern("yyyy-MM-dd").format(object);
    }
}

 

servlet-context.xml에 빈등록

 

//추가
<mvc:annotation-driven conversion-service="conversionService"/>

 

 

//추가
<bean id = "conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="formatters">
            <set>
                <bean class="com.ssg.springex.controller.formatter.LocalDateFormatter"></bean>
            </set>
        </property>
    </bean>

 

다시 접속 404 에러
이제 나온다.

 

 

객체 자료형 파라미터 수집

 

Java Beans 형식으로 만들어진 클래스 타입은 자동으로 객체가 생성되고 setXXX( )등을 자동으로 호출
Lombok의 @Setter 혹은 @Data를 활용

 

TodoDTO를 받는 포스트방식

 

@PostMapping("/register")
    public void registerPost(TodoDTO todoDTO){ 
        log.info("post todo register ....");
        log.info(todoDTO);
    }

 

register.jsp

 

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/todo/register" method="post">
    <div>
        Title: <input type="text" name="title" placeholder="INSERT TITLE">
    </div>
    <div>
        DueDate: <input type="date" name="dueDate" value="2024-03-01">
    </div>
    <div>
        Writer: <input type="text" name="writer">
    </div>
    <div>
        Finished: <input type="checkbox" name="finished">
    </div>

    <div>
        <button type="reset">RESET</button>
        <button type="submit">REGISTER</button>
    </div>
</form>

 

레지스터 버튼 누르면 콘솔 찍힘
포메터가 날짜 변환 해줌

 

 

Model이라는 특별한 파라미터

 

예전 서블릿에서 request.setAttribute( )로 처리했던 모델대신 사용
메소드의 파라미터에 Model을 선언하면 자동으로 객체 생성
addAttribute( ) 를 이용해서 view까지 전달할 객체 저장

@GetMapping("ex4")
    public void ex4(Model model){ // 파라미터를 다 모델로
        log.info("=======ex4=========");
        model.addAttribute("message","Hello Spring MVC");
    }

 

ex4.jsp

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
    <title>es4</title>
</head>
<body>
    <h1>${message}</h1>
    <h1><c:out value="${message}"> </c:out> </h1>
</body>
</html>

 

/todo/ex4로 접속 , 모델에 담아서 전달해줌

 

 

dto 라는 이름으로 todoDTO 전달

 

 @GetMapping("/ex4_1")
    public void ex4Extra(@ModelAttribute("dto") TodoDTO todoDTO, Model model){
        log.info(todoDTO);
    }

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
    <title>es4</title>
</head>
<body>
    <h1>${dto}</h1>
    <h1><c:out value="${dto}"> </c:out> </h1>
</body>
</html>

 

 

 

RedirectAttributes 와 리다이렉션

 

스프링 MVC의 경우 반환타입이 문자열이고 redirect: 로 시작하는 경우 리다이렉트 처리
RedirectAttributes는 리다이렉트시에 필요한 쿼리 스트링을 구성하기 위한 객체
addFlashAttribute( ): 일회성으로 전달되는 값 전달을 위해 사용
addAttribute( ): 리다이렉트시에 쿼리 스트링으로 작성되는 값

 

@GetMapping("/ex5")
    public String ex5(RedirectAttributes redirectAttributes){  // String으로 반환타입
        redirectAttributes.addAttribute("name","aaa");
        redirectAttributes.addFlashAttribute("result","success");
        return "redirect:/todo/ex6"; // 리다이렉트
    }

    @GetMapping("/ex6")
    public void ex6(){}

 

ex6.jsp

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
    <title>ex6 message print</title>
</head>
<body>
    <h1>${result}</h1>
    <h1><c:out value="${result}"> </c:out> </h1>
</body>
</html>

 

/todo/ex5 로 접속 -> /ex6?name=aaa 로 바뀜

 

 

새로고침하면 없어짐 1회성임

 

어노테이션 정리

 

컨트롤러 선언부에 사용하는 어노테이션
@Controller : 스프링 빈의 처리됨을 명시
@RestController : REST 방식의 처리를 위한 컨트롤러로 명시
@RequestMapping : 특정한 URL 패턴에 맞는 컨트롤러로 명시

 

메소드 선언부에 사용하는 어노테이션
@GetMapping / @PostMapping / @DeleteMapping / @PutMapping
@RequestMapping : Get/Post 방식 모두 지원하는 경우 사용
@ResponseBody : REST 방식에서 사용

 

메소드의 파라미터에 사용하는 어노테이션
@RequestParam
@ModelAttribute
@PathVariable
@SessionAttribute
@Valid
@RequestBody

 

다양한 리턴 타입

 

상속이나 인터페이스와 달리 다양한 리턴 타입을 사용할 수 있다.
void
String
사용자 정의 타입
배열/컬렉션
ResponseEntity 등

 

스프링 MVC의 예외처리

 

@ControllerAdvice를 이용해서 처리
예외에 따라서 @ExceptionHandler를 메서드에 활용

 

    @GetMapping("/ex7")
    public void ex7(String p1, int p2){
        log.info("p1"+p1);
        log.info("p2"+p2);

    }

http://localhost:8090/ex7?p1=AAAA&p2=BBB

 

p2는 int인데 String 전달 -> 400 에러

 

예외처리 클래스

 

@ControllerAdvice
@Log4j2
public class CommonExceptionAdvice {

    @ResponseBody  // 내가받은 문자열 브라우저에 그대로보냄.. /??
    @ExceptionHandler(NumberFormatException.class) //
    public String exceptionNumber(NumberFormatException numberFormatException){
        log.info("----------------");
        log.error(numberFormatException.getMessage());
        return "NUMBER FORMAT EXECEPTION";

    }
}

 

 

 

범용적인 예외처리

 

예외 발생시 상위 타입인 Exception을 이용해서 예외 메시지를 구성
디버깅 용도로 활용
이렇게 해주면 자세한 내용 다 나옴

 

@ControllerAdvice
@Log4j2
public class CommonExceptionAdvice {

    @ResponseBody  // 내가받은 문자열 브라우저에 그대로보냄.. /??
    @ExceptionHandler(Exception.class) //
    public String exceptionCommon(Exception exception){
        log.info("----------------");
        log.error(exception.getMessage());

        StringBuffer buffer = new StringBuffer("<ul>");
        buffer.append("<li>" + exception.getMessage()+"<li>");
        Arrays.stream(exception.getStackTrace()).forEach(stackTraceElement -> {
            buffer.append("<li>"+stackTraceElement+"</li>");
        });

        buffer.append("</ul>");

        return buffer.toString();

    }
}

 

 

 

404에러

 

예외처리 notFound 메서드

 

@ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)  // NOT_FOUND가 발생하면
    public String notFound(){  
        return "custom404";   // 여기로 이동하라
    }

 

custom404.jsp를 만들었다.

 

<%@ page contentType="text/html;charset=UTF-8"
language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1> 페이지를 찾을 수 없습니다!</h1>
</body>
</html>

 

web.xml에 추가해준다.

 

<init-param>
<param-name>throwExceptionIfNoHandlerFound</paramname>
<param-value>true</param-value>
</init-param>

 

아무거나 치면 다음과 같이 출력된다.

 

Model Mapper 사용

 

build.gradle에 추가

 

implementation group: 'org.modelmapper', name:'modelmapper' , version : '3.0.0'

  implementation group: 'org.hibernate', name : 'hibernate-validator' , version : '6.2.1.Final'

 

ModelMapperConfig

 

@Configuration  // 해당 클래스가 스프링 빈에 대한 설정을 (@Configuration) 명시하는 클래스
public class ModelMapperConfig {

    @Bean   // 해당메소드의 실행 결과로 반환 된 객체를 스프링의 빈으로 등록시키는 역할
    public ModelMapper getMapper() {
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.getConfiguration().setFieldMatchingEnabled(true)
                .setFieldAccessLevel(org.modelmapper.config.Configuration.AccessLevel.PRIVATE)
                .setMatchingStrategy(MatchingStrategies.STRICT);
        return modelMapper;
    }


}

 

root-context.xml에 추가


Mapper 빈으로 등록할 수 있도록

 

<context:component-scan base-package="com.ssg.todo.config" />

 

mybatis 이용하는 개발 단계
1.VO선언
2.Mapper 인터페이스 개발
3.XML 쿼리문
4.테스트 코드 개발(tdd)

TodoMapper

public interface TodoMapper {
String getTime();
}

 

보통 MyBatis나 Spring Data 등의 ORM 프레임워크에서 매퍼 인터페이스는 데이터베이스와 상호 작용하는 메소드를 정의하는 데 사용
데이터베이스에서 시간 정보를 가져오는 메소드인 getTime()을 선언

 

resources - mappers - TodoMapper.xml

 

<?xml version="1.0" encoding="UTF-8" ?>
http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace=“com.ssg.springex.mapper.TodoMapper">
<select id="getTime" resultType="string">
select now()
</select>
</mapper>

 

<select> 요소는 SQL 쿼리를 정의하는 데 사용됩니다. 여기서는 getTime이라는 메서드를 나타내며, 이는 TodoMapper 인터페이스의 getTime() 메서드와 매핑됩니다. SQL 쿼리는 select now()로 지정되어 있으며, 현재 시간을 가져오는 쿼리입니다.

 

<select> 요소의 resultType 속성은 결과를 매핑할 Java 타입을 지정합니다. 여기서는 문자열 타입으로 설정되어 있으므로 SQL 결과가 문자열로 반환될 것으로 예상됩니다.

 

선언은 이 XML이 사용하는 DTD(Document Type Definition)을 지정합니다. MyBatis의 매퍼 파일은 주로 이러한 DTD를 사용하여 유효성을 검사하고 XML 문서를 파싱합니다.

 

TodoMapeprTests

@Log4j2
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/root-context.xml")
public class TodoMapeprTests {
    @Autowired(required = false)
    private TodoMapper todoMapper;

    @Test
    public void testGetTimeNow(){
        log.info(todoMapper.getTime());
    }
}
반응형