Spring - MVC

25. 객체 DI를 Annotaion으로 변경하기

xml설정을 어노테이션으로 하나씩 바꿔나가려고한다.
자바 APP .XML과 @ 두가지로 설정한다. 둘다 장단점이있는데 스프링은 어노테이션으로 가고 있다.
프로퍼티(외부 텍스트파일) 자바컨피그 어노테이션으로 바꿔나가고 있다.

<property name ="noticeService" ref="noticeService"/>
가장 먼저servlet-context di부분을 어노테이션으로 하도록 하자.

listcontroller에 setter에 @Autowired해준다. 그런데 이것은 소스코드를 손댈수 잇기때문임
소스코드를 손댈수없다면 xml을 설정해야한다.

네임스페이스에 xmlns:context를 설정해주고 <context:annotation-config/>을 추개해줘야 어노테이션을 사용할 수 잇다.

@Autowired
public void setNoticeService(NoticeService noticeService) {
    this.noticeService = noticeService;
}

세터에 @Autowired 해준다.

26. Annotation으로 서비스 객체 생성하기

객체 생성 자체를 어노테이션으로 하자. 크래스에 @Component을 달아주면된다.

<context:component-scan base-package="com.newlecture.web.service"/>
객체를 먼저 찾아보도록한다.
<context:annotation-config/>은 필요없게 되니 지워준다.

그런데 문제점은 @Component은 관념적으로 사용하는 것이다.
@Controller @Service @Repository 가 의미적으로 더 가지고 있다. 역할에 맞게 작성해주자.

27. Annotation으로 URL 매핑하기

컨트롤러 객체를 어노테이션으로 생성하고 url매핑 한것도 어노테이션으로 할 수잇다.
<context:component-scan base-package="com.newlecture.web.controller"/> xml에 가서 설정해준다.
그냥 com.newlecture.web만 하면되는거아니냐? 그런데 이 패키지의 모든 것들을 IOC컨테이너에 담을 필요가 없다.

@Controller
public class IndexController {}
어노테이션을 붙였으면 그냥 해야할 행위가 있는 것이기 때문에 참조하는 인터페이스가 있어야했는데 이제 필요가 없다. 메소드도 바꿔줘야한다.
컨트롤러는 반드시 url을 매핑하는 것이라 추가적으로 설정을 해줘야한다.
리턴할때 아무설정을 안해도 @RequestMapping("/index")의 url을 기반으로 페이지를 찾게된다.
객체를 만들어도 매핑을 하기 위해서는 xml에 mvc:annotation-driven/을 추가해줘야한다.

@Controller
public class IndexController {

@RequestMapping("/index")
public void aaaa() {
}

28. HomController 만들기

어노테이션 적용하는 방법에 포커스엿는데 컨트롤러가 컨트롤러 형태가 아니게 되엇다.

@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    ModelAndView mv = new ModelAndView("root.index");
    return mv;
}

원래 이녀석이 가지고 있던 기능을 구현하도록 해줘야한다.
url로 묵시적인 view를 찾았는데 더이상 url이 클래스와 매핑되는게 아니다.
전에는 클래스 하나에 함수가 잇엇고 함수가 결국 url과 매핑됫는데 이제는 그렇지 않앗다.
이제는 함수가 여러개 올 수 있다.url에 매핑되는 것이 여러개가 될 수 잇다. 더이상 그냥 컨트롤러가 아닌 여러개를 담을 수 있는 더 큰 그릇이 될 수 있게되는 것이다.
그래서 이제 index로만 한정하면안된다. root에 view단을 담당하는게 여러개 있다면? 폴더개념으로 바꾸는 것이 중요하다.

컨트롤러 이름을 HomeController으로 변경해주자. 공지사항도 NoticeController를 만들고 함수를 따로 만들어주자.
컨트롤러를 담는 컨테이너가 되는 것이다. 뷰페이지를 보여주는 방식으로 간단하게 문자열을 반환하면된다.

@Controller
public class HomeController {

@RequestMapping("/index")
public String index() {
    return "root.index";
}

29. 컨트롤러 정리하기


ListController와 DetailController 두개의 컨트롤러를 하나로 합치고 함수를 어떻게 정리를 해야할까?
구체적으로 어떻게 나눌까 이것은 컨트롤러나 스프링이아니라 어떻게 만들건지 파일구조를 어떻게 만들건지 이다.
notice의 목록을 보여주기 ,자세한내용보여주기 등 기능으로 묶는 것이 좋다. customer는 대상보다 더 큰 대상이니 클래스가 아닌 패키지로 만들어야한다.
com.newlecture.web.controller.customer 패키지를 만들고 밑에 NoticeController를 만들어주자.
ListController와 DetailController가 클래스였는데 이제 함수만 남으면 되게 된것이다.
각 컨트롤러에서 사용했던 필드 등을 가져와서 그대로 사용하면된다.

@Controller
public class NoticeController {

@Autowired
private NoticeService noticeService;

@RequestMapping("/customer/notice/list")
public String list() throws ClassNotFoundException, SQLException {
    List<Notice> list = noticeService.getList(1, "TITLE", "");
    return "notice.list";
}

@RequestMapping("/customer/notice/detail")
public String detail() {
    return "notice.detail";
}}

ListController와 DetailController클래스들은 이제 더 이상 사용하지 않으니 지워버린다.
컨트롤러가 매우 가벼워졋다.

"/customer/notice/" 매핑이 이부분까지는 동일 하기때문에 클래스에 매핑을 해줌으로써 정리를 해주자.

@Controller
@RequestMapping("/customer/notice/")
public class NoticeController {

    @Autowired
    private NoticeService noticeService;

    @RequestMapping("list")
    public String list() throws ClassNotFoundException, SQLException {
        List<Notice> list = noticeService.getList(1, "TITLE", "");
        return "notice.list";
    }

    @RequestMapping("detail")
    public String detail() {
        return "notice.detail";
    }
}

30. 컨트롤러를 위한 Annotation 개념정리

https://youtu.be/jyLjTKVbwvY
원래는 매핑과 객체생성을 XML에 하나씩 설정해주었다.
MVC가 스캔을 해서 이름과 상관없이 URL에 맞는 함수를 호출 하는 것으로 하게 된다. 인터페이스라는 제약도 없고 클래스당 개별적으로 만들필요도 없어졋다.
<context:component-scan base-package="com.newlecture.web.service"/>
대신 컨트롤러를 찾아가도록 설정을 했다.
mvc:annotation-driven/추갖거으로 어노테이션 매핑을 사용하기 위해서 작성했다.
사용자 요쳥이 들어오면 함수를 호출하기 때문에 이름이 별로 중요하지도 않았다. 그러나 매핑에 따라 해주는게 구분하기 쉽다.
반환타입은 아무렇게나 해도된다. 앞뒤에 붙여야하는데 찾을 수없어서 void는 좋지는 않다. resolver가 찾지못함.
Resolver를 없애면 아예 뷰정보가 없어져서 500에러가 발생하게 된다.
프론트 컨트롤러가 하고 있는 것은 재호출이다. 서블릿이기때문에 우리가 만들어놓은 method를 찾아낸다. 없으면 404에러를 뱉는다.
tiles를 사용하고 있으니 String타입으로 만드는 것이 좋다.

31.문서 출력방법 4가지 @ResponseBody

url매핑을 했는데 이제 출력하는 것이 필요하다.
문서를 출력하는 방법은 4가지가 있다.
1.서블릿객체를 얻어서 문자열 출력하기 2.@ResponseBody 설정을 통한 문자열출력하기
3.ResourceViewResolver를 통해 문서출력하기 4.TilesViewResolver를 이용한 문서출력하기

어떤것을 이용하든 선택적으로 사용할 수 있어야한다.
String으로 출력하는게 아니라 void로 해보자
서블릿이 가지고있는 httpservlet이없는대 어떻게 사용할 수 있나.?
front controller가 요청이들어오면 컨트롤러의 함수를 실행한다. 컨트롤러에서 매개값을 주면 어떤 매개값이 잇는지 알아보고 가지고있는 것을 참조해서 전달한다.

@RequestMapping("index")
public void index(HttpServletResponse response) {
    PrintWriter out;
    try {
        out = response.getWriter();
        out.println("Hello Index");
    } catch (IOException e) {    
        e.printStackTrace();
    }
}

과거 서블릿을 직접이용해서 출력햇듯이 변수를 선언만하면 front controller가 알아서 넘겨줘서 참조하고 출력을 하게 된다.
페이지를 준비하지않고 직접출력하려면 그냥 이렇게 사용하면된다.
이것을 더 편하게 작성하는 것을 알려주기도 한다. 그냥 Stirng 타입으로 반환하면된다.
그런데 문자열을 그냥 반환하면 view라고 생각하고 view에 연관해서 하고자 한다.
그래서 그냥 문자열임을 알려주기 위해서는 @ResponseBody을 붙이면된다.

@RequestMapping("index")
    @ResponseBody
    public String index() {
        return "Hello Index";
}

직접텍스트만 출력하자고 하면 이렇게 하면된다.들어야한다.
com.newlecture.web.controller.customer 패키지를 만들고 밑에 NoticeController를 만들어주자.
ListController와 DetailController가 클래스였는데 이제 함수만 남으면 되게 된것이다.
각 컨트롤러에서 사용했던 필드 등을 가져와서 그대로 사용하면된다.

@Controller
public class NoticeController {

@Autowired
private NoticeService noticeService;

@RequestMapping("/customer/notice/list")
public String list() throws ClassNotFoundException, SQLException {
    List<Notice> list = noticeService.getList(1, "TITLE", "");
    return "notice.list";
}

@RequestMapping("/customer/notice/detail")
public String detail() {
    return "notice.detail";
}
}

ListController와 DetailController클래스들은 이제 더 이상 사용하지 않으니 지워버린다.
컨트롤러가 매우 가벼워졋다.

"/customer/notice/" 매핑이 이부분까지는 동일 하기때문에 클래스에 매핑을 해줌으로써 정리를 해주자.

@Controller
@RequestMapping("/customer/notice/")
public class NoticeController {

@Autowired
private NoticeService noticeService;

@RequestMapping("list")
public String list() throws ClassNotFoundException, SQLException {
    List<Notice> list = noticeService.getList(1, "TITLE", "");
    return "notice.list";
}

@RequestMapping("detail")
public String detail() {
    return "notice.detail";
}
}

32. @RestController와 한글출력 설정

@RestController은 문서를 만들어서 제공하는 것이아니라 주로 데이터를 제공할때 주된역할을 한다.
그런데 한글출력에 문제를 발생한다. 문서를 줄게아니라 데이터를 줄 때가 있다.
com.newlecture.web.controller.api 패키지를 생성하고 NoticeController 컨트롤러를 만들어주자.

@RestController를 붙이면 특화된 컨트롤러이다. Rest풀 그런 형태의 결과물을 반환하는 클래스이다.
url자체를 따로줘서 api것을 시작해서 노티스에 대한 데이터 목록을 주는 것으로 하느 ㄴ것이다.

매핑하기 위한 어노테이션도 뒤에선 다른 것을 사용한다.
@RestController을 하면 문서를 뱉는게 아니라 마치 @ResponseBody를 주는 것처럼 보인다. 무조건 글자로 반환하게된다.
에러발생 -> IOC컨테이너에 담는데 NoticeController를 담는데 뽑아올때
NoticeController noticeController = new NoticeController
xml에서는 이렇게
<beam id="noticeController" class="...NoticeController">
저렇게 뽑아오는데 이미 같은 이름이 잇어서 뽑아올때 뭘뽑아올지 모르겠다고 에러가 발생하는 것이다.
그러면 그 식별자를 정할 필요가 있다.
@RestController("apiNoticeController")에 부여하면 묵시적으로 부여한 것이기때문에 충돌하지 않는다.

@RestController("apiNoticeController")
@RequestMapping("/api/notice/")
public class NoticeController {

@RequestMapping("list")
public String list() {

     return"notice list";
 }
}

자바 스크립트개발자들에게 데이터를 제공할때 이런식으로 제공해야한다. 한글로 하면 한글이 깨진다.
servlet-context.xml로 가서 설정을 해준다.

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name = "supportedMediaTypes">
                <list>
                    <value>text/html;charset=UTF-8</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

어노테이션이 요청을하고 만약 ResonseBody로 String을 처리할때 결과물을 줄때 이것을 가로채고 메시지를 변환하는 과정을 거치게 해주는 것이다.
향후 인코딩말고 다른 변화를 원한다면 하나씩 꽃을 수 있다.

RestController를 활요해서 프론트 개발을 할때 데이터를 제공하는 것이다.

33. JSON 출력하기

데이터목록을 JSON형태로 반환하도록하자.
스트링이 아닌 객체를 반환하고자한다. 사실객체를 반환하는건 말이안된다. 브라우저가 db를 공유하는 것이 아니기때문임.
서버쪽에서 객체를 반환하면 클라이언트에서 객체를 받을 수 있게하는 것이다. 문자열로 반환해서 받는 것이다.
데이터구조는 xml csv json이다.
1, hello json 이렇게 csv기반으로 보내면 어떤 것이 어떤값이 제목인지 구분을 못한다. 데이터를 중첩해서 그루핑할때 구분할때 큰 구분을 하기 힘들다.
보완하기 위해 <notices> <notice id ="1" title="hello json"/>이런식으로 보냈다. 데이터의 데이터 메타데이터를 보내서 헷갈리지 않게 했엇다.
중첩해서 하더라도 태그로 단일 화할 수가 잇엇다. 그런데 이것도 문제가 있엇다 태그가 너무 비대고 성능이 느리다..
그래서 JSON을 사용하게 되엇다.
[
{"id":1, "title":"hello json"}
]
원래 JSON은 자바스크립트가 사용하는 객체 표기법인데 이제 어디서든 사용하게 되엇다.
공지사항 데이터를 불러와서 문자열을 만들기위해서 목록화한다면 일일히 FOREACH로 하기에 귀찮다.
객체를 얻어와서 바인딩한후 getlist기능으로 가져오고 목록을 그냥 객체를 반환해버릴것이다. 문자열로 포맷안할것임.

객체가 가지고잇는 문자열을 변환해야하는데 문자열대신 객체르 통채로 던저보자
라이브러리가 필요하다.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
</dependency>

@RestController("apiNoticeController")
@RequestMapping("/api/notice/")
public class NoticeController {

@Autowired
private NoticeService service;

@RequestMapping("list")
public List<Notice> list() throws ClassNotFoundException, SQLException {

    List<Notice> list = service.getList(1, "TITLE", "");

    return list;
 }
 }


JSON으로 목록이 출력되게 되었다.
데이터를 제공할때 이런식으로 api를 제공하는게 일반적이다. 객체를 JSON으로 바꿔서 제공하는게 일반적이다. 객체를 뱉을 일이잇다면 그냥 객체를 반환해버려서 JSON으로 반환하게 하자.

34. 지금까지 다루었던 내용과 이번에 다루게 될 사용자 입력 5가지

SPIRNG이아니라 Spirng MVC를 다루고잇다. Front Controller가 제공하는 기능,라이브러리를 사용하겟다는 것이다.
그래서 매핑하기 위한 매핑방법을 배웠다. xml매핑과 어노테이션 매핑을 배웠다. 컨트롤러가 출력하기 위해 문자열 그냥출력 viewResolver로 보여주거나 tiles로 보여주기도 했다.
이제는 입력을 하는 방법을 알고자 하자.


그런데 여기까지는 MVC의 기능이다. 이제 웹개발할때는 Service Dao를 사용해서 DI를 사용하거나 트랜잭션기능을 처리하는데 이것은 스프링의 원래 기능이다.
트랜젝션은 스프링 AOP를 사용한 기능 이다.

이제 MVC에서는 입력만 남았다. 입력을 다룰때 값을 다루는 방법은 5가지가 있다.
스프링이 우리에게 어떤 도움을 줄수있는지 알아보고 싶다.

35. QueryString 입력 #1

QueryString으로 전달되는 값을 스프링이 어떻게 받는 지 알아보자.
사용자가 url에 쿼리값을 보낸다. request "/list?p=1"
Get요청이고 추가적으로 파리미터를 전달해서 옵션을 해서 달라고 하는 것이다.
과거서블릿이엇다면 request로 도구를 얻어서 사요햇엇는데 이 request도구를 어떻게 사용하나? 출력때처럼 그냥 변수를 넣어주면된다.
스프링이 제공하는 Front Controller덕분에 사용할 수 있게 되엇다. 그런데 과거랑 같아서 좋다고 하기에 말할 수없다.
더많은 기능을 제공한다.이름과같은 하나만 선언할수도잇다. String p만 제공하면

규격이 정해져잇지않고 필요하면 넣고 아니면 빼면된다.

@RequestMapping("list")
public String list(HttpServletRequest request) throws ClassNotFoundException, SQLException {
    String p = request.getParameter("p");
    System.out.println(p);

    List<Notice> list = noticeService.getList(1, "TITLE", "");
    return "notice.list";
}

@RequestMapping("list")
    public String list(String p) throws ClassNotFoundException, SQLException {
        System.out.println(p);

        List<Notice> list = noticeService.getList(1, "TITLE", "");
        return "notice.list";
}

이렇게 줘도 받을 수 있게 된다.

36. QueryString 입력 #2 : 변수명 별칭과 기본 값처리


입력받을때 변수명을 다른것으로 할 수없을까? 값이 전달되지 않앗을경우 기본값을 어떻게 제공할까
p를 좀더 살려서 페이지 변수로 사용하고 싶다. Stirng page로 담으면 보내고자하는 것과 일치하지 않아서 심어질수가 없다.
page가 아니라 스프링컨트롤러에게 이것은 p라는 것을 전달해야한다. @RequestParam("p")로 표시해주면된다.
풀네임으로 쓰거나 다른이름으로 작성할 수 있게 된다.

@RequestMapping("list")
public String list(@RequestParam("p") String page) throws ClassNotFoundException, SQLException {
    System.out.println(p);

    List<Notice> list = noticeService.getList(1, "TITLE", "");
    return "notice.list";
}

때로는 p에 파라미터를 전달햇다. p값이 전달이 안된다면 기본값을 어떻게 지정해야하는가?
스프링이 기본값을 제공하는 것을 지원한다.

@RequestMapping("list")
public String list(@RequestParam(name = "p", defaultValue = "1") int page) throws ClassNotFoundException, SQLException {
    System.out.println(page);

    List<Notice> list = noticeService.getList(1, "TITLE", "");
    return "notice.list";
}

어노테이션에 여러가지 속성이 있는데 이것을 지정해주면 된다. 파라미터값은 모두 파라미터이니까 잘 작성하자. 정수형으로 하고싶다면 그냥 int page로 하면되는데 스프링이 알아서 변환해준다.
null값이오면 "1"을 제공하기 때문에 상관이없다. 인티저를 이용해서 데이터 쿼리만 하면되는 것이다.

37. @RequestParam Optional 속성

나머지 속성을 알아보고자 한다.

구글에 @RequestParam Spring io를 검색해보자.
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html
어노테이션을 정의하는 문서를 볼 수 있다.

p 값이 안오면 오류가 난다. 기본값을 주지 않고
@RequestParam(name = "p", required = false)로하면? 오류가 발생한다 전달하는 값이 전달되지 않는데 int page가 Null값을 받을 수없으니 Intger로 바꿔준다.
true로하면 값을 전달하지 않앗다고 오류가 난다.
1.null값을 처리할 수 잇게 해주거나 2.기본값을 전달하거나 3.false로해서 필수값이 전달안받아도되게 하거나 로 해줘야한다.
value는 name을 대신할 수있는데 같이 사용하면 안된다.
그럼 별칭을 왜만들엇을까? value 강사님도 알수없다? 이름을 잘못만들어서 중간단계의 별칭을 만든게 아닐까 싶다.

@RequestMapping("list")
public String list(@RequestParam(value = "p", required = false) Integer page) throws ClassNotFoundException, SQLException {

System.out.println(page);

List<Notice> list = noticeService.getList(1, "TITLE", "");
return "notice.list";
}

2023.01.19 후기

스프링에 스프링 배우는 중 ㅎㅎ;
중요한 것은 꺾이지 않는 마음

'기초단계 > SPRING' 카테고리의 다른 글

2023.01.21 Spring  (0) 2023.01.22
2023.01.19 Spring  (0) 2023.01.20
2023.01.17 Spring  (0) 2023.01.17
2023.01.16-2 Spring  (1) 2023.01.16
2023.01.16-1 Spring  (0) 2023.01.16

+ Recent posts