본문 바로가기

Spring/Servlet

뉴렉처 스프링 MVC

톰캣을 가지고 웹개발 가능

Tomcat (Tomcat Configuration)

- web.xml

       - 사용할 라이브러리 인스턴스화(설정)

           Mybatis, Tiles, ...

         -Spring Dispatcher : front Controller를 스프링이 제공

 

 

Spring Web (Spring Configuration)

- web.xml에 Spring Dispatcher을 올리면 Spring이 모든 것을 관할하려고 함 (라이브러리 기타 등등)

   + 사용할 라이브러리 인스턴스화(설정)

           Mybatis, Tiles, ...

         -Spring Dispatcher : front Controller를 스프링이 제공

    - Servlet | Service | Security | Mybatis | Tiles ...

    - XML에 Annotation이 얹혀지는 방식으로 개발이 이루어지다가 버전이 업그레이드 되면서 최근엔 거의 Java가 전담 가능

       따라서 사람마다 설정 방식이 다르고 혼잡한데, 이를 돕기위해 Spring Boot(설정을 모아 편리하게 하는 프레임워크)가 나옴

 

Spring Boot (Starter Configuration)

- application.properties 혹은 YAML이라는 설정 파일 하나만 둠

 

여기서는 Spring Boot 이전 Spring Web을 기준으로 학습 [여전히 혼용이 많이 되기 때문에]

XML → Annotation → Java로 발전해온 방향 따라가면서 학습할 예정

 

 

 

 


 

Spring MVC

mvc1 : 모든 Controller(서블릿)가 각각의 Dispatcher가지고 있고 jsp(뷰단) 분리됨

mvc2 : 결합력을 낮추는 것이 핵심

              Controller 순수하게 Controller기능만, 서블릿이 아닌 일반적인 자바 클래스(POJO : Plain Old Java Object)로 구현됨

              dispatcher는 forwarding만 하니까 거의 유사하기 때문에 하나만 만듦

                - 사용자의 요청을 Controller에게 전달 후 결과물 반환한 값을 받아서 뷰에 전달 (중간 매개)

                - 단순 요청을 라우팅할뿐만 아니라 사용자 입력값을 사용하기 쉽게 포장

                - 서블릿의 입력도구를 이용해서 포장 후 자바 데이터형식(혹은 엔터티로)으로 전달

                - 이로인해 Controller에서는 Servlet 라이브러리 하나도 사용하지 않고 입력을 처리할 수 있게 됨

               Controller는 디스패처 기능이 없어 리턴할 때 어떤 뷰페이지가 필요할지에 대한 정보를 제공해서 Dispatcher에서 뷰단 호출

 

- 스프링이 Dispatcher Servlet기능을 제공 (Front Controller)

  모든 url요청이 들어오면 어떤 컨트롤러가 처리할지를 결정, 이에 대한 설정파일[*-servlet.xml]이 필요

  [*-servlet.xml] : <url-mapping> 매핑정보 포함, 기존에 web.xml에 설정했던 내용을 여기서 설정

  POJO클래스인 Controller가 model&view를 반환정보로 전달하면 view를 찾아 관련 데이터(model)을 전달

 

 

 


웹프로젝트 생성과 기본설정

메이븐을 이용한 기본 웹 프로젝트 생성

Create a Java project

Create new Spring Starter Project - 스프링부트 (나중에)

우측에 perspective view - java EE로 변경

 

1. Create a Maven Project - Create a simple project 체크 (skip archetype selection) : 비어있는 아키타입을 쓴다 -

  • Group id : com.newlecture
  • Artifact id : webprj
  • Packaging : war

2. pom.xml -- web.xml 만들어야 에러 없어짐

src-main-webapp에 web.xml파일 만듦 ( 톰캣/webapps/ROOT/WEB-INF에서 복붙해옴)

 

3. Libraries - JRE System Library 보면 JDK가 1.5인데 수정

pom.xml - Overview에서 add Property [소스는 1.8버전, 대상도 1.8버전임을 명시]

                                        name = "maven.compiler.source" value="1.8"

                                        name = "maven.compiler.target" value="1.8"

 

4. 변경사항 반영하기 위해서는 프로젝트 우클릭 -Maven - Update Proejct

5. 기본 인코딩 방식 utf-8로 변경 : Window - Preferences - Web -JSP files/HTML/CSS - Encoding : utf-8

                                       프로젝트 자체 속성 (Properties) - Resource - Other: utf-8

                                      이제 jsp파일 utf-8로 기본적으로 생성됨

6. pom.xml에서 jsp파일 관련 라이브러리 설정

                                       "maven tomcat api 구글링 - 자신의 톰캣버전 클릭해 Dependency 카피"

7. Run On Server - Apache - Tomcat v9.0 - 디렉토리 설정

   [ 톰캣 압축을 풀었던 홈 디렉토리 "apache-tomcat-9.1.2"] : bin, conf, lib 등의 파일이 있는 디렉토리가 홈 디렉토리

 

8. Window - Web Browser 선택

 

 


Dispatcher Servlet 라이브러리 설정하기 [04]

Maven - show view - other:Maven - Maven Repository

Global Repositories - central - 우클릭 Rebuild Index

다운로드 하면 쉽게 라이브러리 sts에서 검색가능

pom.xml - Dependencies  - Add - 바로 검색 가능

[ 원격에서 가져오는 목록이 아니라 인덱싱한 목록 출력]

 

Maven Repository - spring framework 중 Spring Web MVC만 최신버전으로 카피

Dispatcher Servlet클래스

프론트 컨트롤러 역할을 하기 위한 서블릿 클래스

 

05 dispatcher-servlet.xml파일

index.jsp가 dispatcher로 부터 model을 받아 ${data}한다고 가정

com.newlecture.web.controller.IndexController에서 extends httpServlet하지 않고

이 기능을 스프링(Dispatcher Servlet, 이미 만들어진 클래스)이 대신함

 

스프링으로 오면서 dispatcher기능은 의존성에서 대신한다.

이에 따라 Controller에서 extends HttpServlet을 하지 않는다

또한 스프링의 디스패처 서블릿을 사용하다보니 소스를 직접가지고 있지 않아

url매핑 또한 어노테이션으로 할 수 없어 xml파일을 사용해서 매핑이 필요하다.

 

 

1. web.xml 설정 [ 모든 URL ]

Maven Dependencies - spring-webmvc-5.3.7.jar - org.springframework.web.servlet

     - 스프링이 지원하는 mvc 라이브러리

     -  DispatcherServlet.class를 copy Qualified Name

 

  서블릿 매핑

 - 복사한 name 지우되, 클래스명까지만 남겨두고 확장자(.class)는 지운다.

- frontController는 기본적으로 모든 요청을 받아 분배해주기 때문에 모든 요청 "/*"로 매핑

- "매핑한 주소"만 걸러서 예외없이 요청을 받으려면 "/"가 아닌 "/*" 

- 하지만 여전히 에러 발생

 

 

web.xml의 매핑 url pattern에 따른 index로 요청했을 때 처리되는 순서

- /* : 기본적으로 *-servlet.xml이라는 파일이 WEB-INF에 존재해야 에러가 안남

         그러나 파일이 존재해도 여전히 문제

         /*는 forwarding이든 get요청이든 *-servlet파일을 거쳐서 가게 됨

         web.xml로 가서 서블릿에 걸려 dispatcher-servlet으로 이동 -  index라는 id의 빈이 있기 때문에 해당 컨트롤러로 이동

         IndexController에서 mv를 만들고 이제 뷰단으로 요청("index.jsp"라는 이름)을 하는데,

         web.xml에서 /*로 모든 요청을 받을 경우 또다시 걸려 dispatcher-servlet으로 재이동하는데, index.jsp라는 이름의 빈이 없음

         따라서 에러 발

- / :  마찬가지로 같은 경로를 거쳐 dispatcher-servlet으로 이동하나,

        /로 설정했을 경우 index.jsp처럼 없는 것들은 직접 resource에 요청 따라서 에러없이 작동.

        하지만 여전히 문제 : 외부에서 사용자가 view단을 직접 요청 가능하다는 것, 뷰단은 절대로 클라이언트가 직접 요청해선 안됨

         >   WEB-INF에 view 폴더를 만들고 jsp(뷰를)파일들을 모아 막는다. 

        이제 뷰단은 컨트롤러를 통해서만 요청이 가능하다.

 

<!-- 서블릿 만들기 -->
<servlet>
	<servlet-name>dispatcher</servlet-name> <!--사용자정의 이름-->
   	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<!-- 사용자가 요청할 수 있게하는 주소 url만들기 [매핑] -->
<servlet-mapping>
	<servlet-name>dispatcher</servlet-name>
   	<url-pattern>/</url-pattern> <!--루트로 시작하는 모든 url 다 받겠다 -->
</servlet-mapping>

 

 


 

2. dispatcher-servlet.xml 만들기  [ URL-mapping ]

- 디스패처 서블릿이 약속된 위치(WEB-INF)에 *-servlet라는 이름으로 있어야 함

- servlet은 예약어, *은 우리가 지정한 서블릿 이름 여기서는 dispatcher로 함

- 스프링 프레임워크 docs.spring.io/spring-framework/docs/5.2.x(버전)/spring-framework-reference/core에서 소스 가져옴

  카테고리 중 Ioc Container에서 스크롤 좀 내리면 xml 파일 설정 내용이 존재 - 복붙

- bean id : 요청명 | class명 : 패키지명.컨트롤러명

- 빈이 생성되면 IoC Container에 담김

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
	<!--"/index" url 요청이 오면 컨트롤러 클래스를 객체화해 호출-->
    <bean id="/index"      //빈을 만들면 객체화해주는데, 스프링 mvc에선 좀 다르게 id에 url
        class="com.newlecture.controller.IndexController"> //f2 이용 : 패키지명 복붙
    </bean>

</beans>

 


3. IndexController 메소드 추가

- IoC Container에 담겨진 POJO Controller들, 빈자체는 함수를 담는 껍데기이기 때문에 그 요청을 수반할 수 있는 메소드가 필요

- 남이 만든 디스패처를 쓰다보니 모든 컨트롤러는 정해진 메소드를 씀 : handleRequest

- 모든 Controller는 dispatcher가 약속하고 있는 함수 (handleRequest)를 쓰고

   Controller라는 interface 구현해야함

   과거에 Servlet을 상속받아 썼다면 이제는 Controller를 구현해서 Controller에 종속됨

 

- handleRequest() 오버라이드

- 디스패처 기능 자체는 스프링이 하고 있으니까 무엇을 디스패처 할건지 반환할 내용만 정해줌 : modelAndView

   ModelAndView 객체 만들고, addObject로 model에 데이터 담기

   setView(뷰라는 객체를 만들어 담거나 new ModelAndView("경로")하거나

   setViewName(뷰의 이름으로 넣거나 setViewName("경로") - 처음에는 index.jsp를 넣어줬지만 매핑 후 확장자 삭제)

- model&view가 Controller로부터 반환되면 Dispatcher가 받아 forwarding

 

public class IndexController implements Controller{
	@Override
	public modelAndView handleRequest(HttpServletRequest request, HttpServletResponse response){
    	ModelAndView mv = new ModelAndView();
        mv.addObject("data", "Hello Spring MVC"); //data란 이름으로 model담음
        //mv.setViewName("/WEB-INF/view/index.jsp"); //index.jps란 이름을 가진 뷰로 연결
        mv.setViewName("index"); //index.jps란 이름을 가진 뷰로 연결, View Resolver설정 필요(없으면 500무한루프)
        return mv;
    }
}

 

 

 


 

Context명 지우기

project- properties - Web Project Settings - "/"
컨텍스트명 변경 시 서버 아래 지우고 다시 추가

 

 


절대경로와 상대경로

IndexController

mv.setViewName("/WEB-INF/view/index.jsp");

※ bean id="/aa/index",     루트/다른폴더/index라고 가정
     뷰이름은 "WEB-INF/view/index.jsp"(상대경로) 로 쓴다고 가정

WEB-INF/view/index.jsp로 작성한다면
*-servlet.xml파일의 bean id를 기준으로
매핑한 url(http:// ..../aa/index)과 같은 위치에 있다고 가정해 
전혀 다른 주소로 요청한다.
/aa/Web-INF/view/index.jsp로 요청한다 (404에러 발생)
따라서 이런 문제를 신경쓰고 싶지 않다면 절대경로로 쓰는 게 좋다.
bean에서 지정한 url에 폴더와는 상관없이
항상 Root /에서 WEB-INF를 찾게 된다.


*-servlet.xml

<bean id="/index" ~> 무조건 루트에서부터 절대경로로 써야한다.
여기서 index는 사용자 지정 이름으로
실제로 존재하는 파일이 아닌 개발자가 매핑해준 요청명에 불과함


 

 

간단하게 페이지 요청하기

실행은 항상 Controller에서 해야하지만 url 매핑 이후 불가

매핑한 url은 "/index"인데, 단순히 Ctrl+F11로 실행하지 않고 직접 요청명을 작성하는 건 번거로운 일이다.

이를 해결하기 위한 꼼수가 존재한다.

 src- main - webapp에 매핑 주소와 똑같은 이름의 빈 파일을 만든다. [여기선 index] 

그럼 요청이 아닌 실제 빈 파일이 출력되지 않을까? 할 수 있는데, 선택한 파일 Ctrl+F11에 따라 주소가 자동 입력될 때

실제 파일보다도 Controller 요청의 우선순위가 높아 같은 이름의 파일이 있어도 매핑한 요청이 실행된다.

따라서 우리는 해당 요청을 실행하기 위해 빈파일을 간단하게 Ctrl+F11로 실행할 수 있다.

 

 


 

 

 

요청명 index로 단순화시키기 [ View Resolver ]

new ModelAndView("/WEB-INF/view/index.jsp")를 간단하게 요청할 수 있게 하기

dispatcher-servlet.xml 파일에 빈 추가

 

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/WEB-INF/view/"/>
 	<property name="suffix" value=".jsp"/>
</bean>

org.~~~를 복붙하려면 컨트롤러에서 internalResourceViewResolver쓰고 import 부분 복사해서 가져다쓰기

다음부터는 그냥 설정 파일을 복붙하면 됨

이제 new ModelAndView("/index")로 바로 요청 가능

 

 

 


 

정적 파일 서비스하기

스프링이 기본적으로 정적인 파일을 제공하지 않도록 막고 있음(HTML을 제외한 이미지, css, js)

 

이미지,css, javascript 등 소스파일 출력하기 [*-servlet.xml 수정]

 

더보기

< mvc:resources location="/images/" mapping="/images/**" />

< mvc:resources location="/js/" mapping="/js/**" />

< mvc:resources location="/css/" mapping="/css/**" />

사용자가 images[js, css]로 시작하는 url을 요청하면 /images[js, css]로 시작하는 디렉토리에서 찾아주겠다는 의미

 

< mvc:resources location="/resource/" mapping="resource/**" />

실제로는 정적 파일들을 resource라는 폴더 안에 넣어두고, 요청명을 resource로 매핑해서 사용한다.

 

요즘엔 이마저도 static으로 바뀜

 

 

 

xml파일에서 스키마란 태그로 이루어진 명령어집합으로, 즉  페이지 내에서 쓸 수 있는 태그를 정의한다.

 beans 뿐만 아니라 mvc 관련 태그를 사용하기 위해서는 mvc를 처리할 수 있는 처리기를 추가한다.

자세한 수정 방법은 더보기에서 확인 가능하다.

 

 

mvc 처리기 추가 : 기존파일 및 수정방법

 

더보기

1. 둘째줄 복붙 후 xmlns:mvc 추가 |  beans >mvc로 변경

2. 4~5줄에서 " "안의 내용만 문자열 안에 복붙 후 beans >mvc로 변경

    이제 mvc태그를 처리할 수 있다.

3.<mvc:resources location="실제 정적인 파일 모아둔 폴더" mapping="/**"/> 추가

 

1. 둘째줄 복붙 후 xmlns:mvc 추가 |  beans >mvc로 변경

2. 4~5줄에서 " "안의 내용만 복붙 후 beans >mvc로 변경

    이제 mvc태그를 처리할 수 있다.

3.<mvc:resources location="실제 정적인 파일 모아둔 폴더" mapping="/**"/> 추가

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="/index" class="com.newlecture.controller.IndexController"/>  
        <!-- collaborators and configuration for this bean go here -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    	<property name="prefix" value="/WEB-INF/view/"></property>
    	<property name="suffix" value=".jsp"></property>
    </bean>    
</beans>

 

 

mvc 처리기 추가된 -servlet.xml 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">
       <!--3행의 mvc라는 네임스페이스 추가해서 사용 가능한 태그를 확장시킨다-->
       <!--이름(주소처럼 보이지만 중복을 피하기 위해 도메인주소로 쓰고 있음) + 실제소스의 조합-->
       <!--해당 스키마를 식별하는 네임스페이스로 태그를 간략하게 줄여쓸수 있음-->
        
    <bean id="/index" class="com.newlecture.controller.IndexController"/>  
        <!-- collaborators and configuration for this bean go here -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    	<property name="prefix" value="/WEB-INF/view/"></property>
    	<property name="suffix" value=".jsp"></property>
    </bean>
    <!-- static파일을 모아두는 게 좋으니, static or public -->
    <mvc:resources location="/static/" mapping="/**"></mvc:resources>    
</beans>

 

 

web.xml파일에서 url-pattern 매핑 의미

/* :  jsp까지 막겠다
/ :  jsp 제외 정적인 파일 막겠다

** : 하위폴더 포함

 

<mvc:resources location="/static[혹은 public]/" mapping="/resource/**">

사용자가 resource/로 시작하는 요청을 한다면  static폴더안에서 찾음

webapp 아래 static 폴더 있고 그 안에 파일이 있을 때 바로 출력 가능

한번 나온 이미지는 해당 설정을 주석처리해도 여전히 뜨는데, 이는 이미지파일이 static으로 캐쉬에 저장되기 때문에 새로고침 필요

우리가 실제로 사용할 때 <img src="/resource/images/logo.png"> 보다는 

<img src="/images/logo.png">로 더 간단하게 쓰기 위해서 mapping명을 resource없이 "/**"로 간다.

<mvc:resources location="/static[혹은 public]/" mapping="/**">

 

 

이미지, css 등 그 외 resource파일들을 출력하기 [정리]

1. resource 폴더를 src-main-webapp 안에 만들어 둔 후 그 안에 정적파일들을 모아넣는다

     - 이 때 resource를 뷰단에서 모든 파일 경로에 추가해야하는 상황 발생 resource/imgaes/파일명

 

2. 요즘 스프링 부트에서 static 혹은 public을 많이 쓰는 경향이 있음. 따라서 static으로 폴더 이름을 주고, 다음과 같이 매핑한다

    *-servlet.xml                                                                                  

    <mvc:resources location="/static/" mapping="/**">

    서버가 아닌 클라이언트로 바로 전달할 것들(정적 파일들)을 webapp/static이란 폴더 안에 모아두고

   root(webapp)에서 static파일이 요청되면 static이란 폴더 안에서 찾도록 매핑한 것이다. 

   경로에서 이미지 클릭 후 Ctrl+F11하면 404에러

                                                                (왜? 매핑을 루트로 했기 때문에 localhost:8100/static/images/파일명으로 요청 필요 )

   /static/ : static 폴더 내에서 하위폴더까지 모두 탐색

   이로 인해 우리는 localhost:8100/images/파일명으로 바로 요청해도 이미지를 불러올 수 있고,

   파일 경로에도 images/로 바로 시작 가능

                                                                                                                                                                                             

 

 

 


공지사항 컨트롤러 추가

① 뷰단 : WEB-INF/view/notice/list.jsp추가

② 컨트롤러 : com.newlecture.web.controller.notice : ListController.java추가

     implements Controller & ModelAndView handleRequest(~~) 오버라이드

 

public class ListController implements Controller {

           @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {                              ModelAndView mv = new ModelAndView("notice.list");         // notice/list에서 슬래쉬를 .으로 변경

                        return mv; }

}

 

③ *-servlet.xml에 url매핑  

        <bean id="/notice/list" class="com.newlecture.web.controller.notice.ListController"/>  

 

④ view단 링크에서 .html 삭제(요청명만 써줌)

 

 

 


 

jsp:inclue를 쓰지 않는다.

why? 페이지를 만들 때마다 넣어주는 것 자체도 번거로움

이마저도 집중할 수 있는 외부 라이브러리 Tiles를 쓸 것 .

inclue하는 내용마저도 레이아웃으로 따로 만들고 그 안의 다른 내용 부분만 실제로 넣을 수 있게

 

모듈화

페이지 지시자 빼고 아래 내용만 남김

1. 뷰전체에서 사용 : view/inc안에 header.jsp, footer.jsp 만들고

상세 페이지 jsp에는 해당 내용을 삭제 2. view/customer[visual+aside를 포함, 몇몇 페이지에서만 씀]/inc/visual.jsp, aside.jsp, layout.jsp[레이아웃만, 내용X]3. view/customer/로 상세페이지 notice, event, faq 이동하고, 상세페이지에는 모든 레이아웃 제외 내용(main)만 남음

 

레이아웃 페이지 만들기와 Tiles라이브러리

 

다음과 같이 공통된 부분의 모듈화 작업이 선행되어야 한다.

view아래 customer, inc 폴더가 존재한다

 

WEB-INF/view/customer [사용자 지정 이름]

해당 웹사이트에서 aside+visual을 포함하는 영역인데 고객센터와 관련이 있기 때문에 customer로 이름지어졌다.

customer 안에서 공통적으로 쓰이는 aisde, layout, visual은 inc(include)폴더에 들어가고, 공지사항 게시판의 컨텐트를 보여주는 main은 notice 폴더 안 각각의 파일에 들어있다.

 

WEB-INF/view/inc

웹사이트 내 모든 페이지에서 공통적으로 쓰이는 문서 footer, header이 들어있다.

 

layout.jsp는 기존 페이지에서 고유한 content 영역을 제외한 header, visual, aside, footer의 을 포함하며 여기에 tiles를 통해 include할 예정이다.

 

 

 

 

요즘엔 프론트에서 페이지를 조작하면서

페이지를 합치는 작업이 프론트에서 일어나는 경우 많음

타일즈 2017에 나오고 더이상 나오지 않음

 


기존
1. 컨트롤러가 notice/list 반환 //여기서 notice/list는 실제 파일명에 가까움
2. spring에서는 resolver를 이용해 /WEB-INF/view/notice/list.jsp 결과물 찾아 반환

 


tiles 적용 후
1. 컨트롤러가 notice.list로 값 반환

     - Tiles 적용 후는 notice.list를 우선적으로 탐색 후 없으면 notice/list.jsp찾는 방식으로 작동됨

     - Controller에서 "notice.list"로 뷰이름 매핑


2. Tiles지시서 
[tiles.xml] : "어떤 이름으로 올 경우 어떤 조합을 붙일건지, 어떤 페이지가 필요한지에 대한 목록" 
 ① "tiles.apahce.org" - Documentation - Tutorial - Creating Tiles Pages - 두번째 블럭 xml파일 복붙
     WEB-INF - tiles.xml에 추가 (위치는 다르게 해도 되지만 일반적인 경로)


 ② tiles-definitions - definition의 name은 컨트롤러가 반환하는 요청명과 일치해야함
     따라서 notice.list | notice.detail로 맞춰준다 [여기서 이름은 notice-list등 다양한 패턴 가능하나 일반적으로 .사용]


 ③ template, value를 경로를 보며 작성한다 프로젝트 루트(webapp)에서 "/WEB-INF/"로 시작함, 이때 오타주의

 

 ④ 반복을 피하기 위해 definition의 name을 적을 때 패턴 문자[와일드 카드]를 사용할 수 있다.
      tiles.apache.org - tutorial - 4.Advanced Topics - wildcard에서 관련 사용법을 익힐 수 있다.

    *이나 . /(슬래쉬는 viewResolver과 충돌 여지)를 사용하여 다양한 패턴을 만들 수 있고, 정규식도 사용할 수 있지만
    실제로 사용할 때는 notice.*.* 이정도만 사용함  ex.
첫번째 와일드 카드 {1} 두번째 와일드카드{2}

 

 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
       "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
  <definition name="notice.*" template="/WEB-INF/view/customer/inc/layout.jsp">
    <put-attribute name="title" value="공지사항" /> <!--문자열로 넣을 때-->
    <put-attribute name="header" value="/WEB-INF/view/inc/header.jsp" />
    <put-attribute name="visual" value="/WEB-INF/view/customer/inc/visual.jsp" />
    <put-attribute name="aside" value="/WEB-INF/view/customer/inc/aside.jsp" />
    <put-attribute name="body" value="/WEB-INF/view/customer/notice/{1}.jsp" />
    <put-attribute name="footer" value="/WEB-INF/view/inc/footer.jsp" />
  </definition>
  <!--notice.list가 오면 {1}이 list가 들어가고 detail이 오면 detail들어감-->
  <!-- name="notice.*.*"로 한다면 notice/{1}/{2}.jsp식으로 사용가능-->
  
<!--  <definition name="notice.detail" template="/WEB-INF/view/customer/inc/layout.jsp">
    <put-attribute name="title" value="공지사항" /> <!--문자열로 넣을 때-->
    <put-attribute name="header" value="/WEB-INF/view/inc/header.jsp" />
    <put-attribute name="visual" value="/WEB-INF/view/customer/inc/visual.jsp" />
    <put-attribute name="aside" value="/WEB-INF/view/customer/inc/aside.jsp" />
    <put-attribute name="body" value="/WEB-INF/view/customer/notice/detail.jsp" />
    <put-attribute name="footer" value="/WEB-INF/view/inc/footer.jsp" />
  </definition> -->
</tiles-definitions>

 


"어떤 위치에 붙일것인지"
 ① [pom.xml]에 의존성 주입해야함 [org.apache.tiles tiles-jsp 3.0.8버전 추가]

      추가하기 방법1 maven repository에서 검색 후 복붙

      방법2 maven-Global Repositories - cetnral - Rebulid Index로 라이브러리 다운로드 하면

                  pom.xml dependencies에서 add 후 바로 검색 가능
 ② [layout.jsp]에 태그라이브러리 추가
     <%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
 ③ [layout.jsp]각각의 모듈 추가

     <tiles:insertAttribute name="header"/> 
     head-title태그안에 문자열<tiles:getStringAs name="title"/>
     layout.jsp페이지에 tiles를 포함시킨다 name값이 일치해야함 

 

여기서 노란 줄과 함께 에러 경고가 뜬다면 태그 의존성 주입이 되지 않았거나, xml파일에 오타가 있을 확률이 높다.

 

 


3. Tiles ViewResolver

     tiles 호출 : 형태에 맞는 페이지 조각 찾아서 반환 [dispatcher-servlet.xml]
- notice/list로 반환하면 .jsp붙여 처리

   notice.list로 반환하면 타일즈 호출해서 페이지를 합침,

   지금까지 어떤 목록의 모듈이 필요하고 어떤 위치에 붙일건지만 설정했다면

   notice.list에 해당하는 페이지 찾아 합쳐주는 역할(두 설정을 연결하는 역할)을 viewResolver가함

     
 ① <bean class="org.~~.UrlBasedViewResolver"> 추가
     <property name="order" value="1"> 우선순위 설정 (notice.list 요청)
     <bean class="~~.InternalResourceViewResolver"> 우선순위 2 설정 (notice/list 요청)
 ② TilesConfigurer : 지시서

 

 

dispatcher-servlet.xml 추가하거나 수정될 내용

 

<!-- tiles 리졸버 : notice.list식의 요청 -->
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
	<property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView" />
	<property name="order" value="1" />
</bean>

<!--위의 요청이 들어오면 지시서를 찾아서 일을 해야하는데 지시서의 위치를 알려줌-->
<bean class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
	<property name="definitions" value="/WEB-INF/tiles.xml" />
</bean>

<!--기본 리졸버 : notice/list식의 요청-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/WEB-INF/view/"></property>
	<property name="suffix" value=".jsp"></property>
	<property name="order" value="2" />
</bean>

 


4.JSTL 라이브러리 추가

현재 링크를 걸어두지 않았으므로 localhost/8100/notice/list로 직접 요청해서 확인해야 한다

이때, NoClassDefFoundError가 발생할텐데 jstl 라이브러리를 추가해서 해결한다.

pom.xml에 javax.servlet jstl 의존성 주입

 

오타도 없고 라이브러리 제대로 넣었는데 에러가 난다면 시도할 방법 maven-update proejct | 이클립스 껐다키기 | 서버 지우고 실행

 

 

 

루트에 있는 페이지에서 사용할 수 있는 Layout페이지 만들기

① WEB-INF/view/inc/index.jsp복사해 layout.jsp로 변경

② layout.jsp : header | body | footer 뼈대 남기고 body은 잘라옴

     타일즈 라이브러리 추가

    <tiles:insertAttribute name="header[body/footer]"/>

③ 루트의 index.jsp페이지에 지시자 아래 body 붙이기

④ tiles.xml 설정 추가 

      <definition name="root.*" template="WEB-INF/view/inc/layout.jsp">

  <!-- root에서 시작하는 페이지 : ex.index.jsp | notice.*부터 검사하고 해당없는 거는 root.*에서 찾음 그냥 *로 쓸 경우 무한루트-->
  <definition name="root.*" template="/WEB-INF/view/inc/layout.jsp">
    <put-attribute name="header" value="/WEB-INF/view/inc/header.jsp" />
    <put-attribute name="body" value="/WEB-INF/view/{1}.jsp" />
    <put-attribute name="footer" value="/WEB-INF/view/inc/footer.jsp" />
  </definition>

 

⑤ IndexController 수정

     package com.newlecture.web.controller

    viewName을 "root.index"로 설정

    "index"일때는 헤더, 푸터가 없지만 "root.index"일때는 타일즈 요청으로 들어가 헤더,푸터까지 결합해줌 (주소창엔 /index로 뜸)


데이터 서비스 클래스(NoticeService)

1. pom.xml 설정

① 데이터베이스 연결 위해 pom.xml에 jdbc추가

oracle로 검색해 com.oracle.database.jdbc ojdbc10 최신버전으로 추가

 

<bean id="/index" class="NoticeController"/>

IoC에 담김 → 우리가 사용해도 되고 스프링(프론트 컨트롤러= 디스패처)이 요청이 들어오면 사용

 

<bean class="NoticeService"/>

마찬가지로 빈이 생성되면 IoC에 담김 → 전역적으로 사용 가능

 

 

원래는 ListController에서 객체를 만들어야 하지만(Annotation) xml부터 배워보기 위해 dispatcher-servlet으로 갈것

 

 

 

2. dispatcher-servlet.xml [ DI 담당 ]

DB로부터 데이터를 가져오는 noticeService 클래스 객체를 생성

    <bean id="noticeService" class="com.newlecture.web.service.NoticeService"/> [21강]

      인터페이스로 자료형 바꾸고 수정된 버전 [22강]

    <bean id="noticeService" class="com.newlecture.web.service.jdbc.JDBCNoticeService"/>

 

ListController에 noticeService를 set

    <bean id="/notice/list" class="com.newlecture.web.controller.notice.ListController">
             <property name="noticeService" ref="noticeService"/>
    </bean> 

  • 첫번째 noticeService는 listController에 setter하는 것 setNoticeService()를 xml식으로 쓴 것
  • 두번째 noticeService는 앞서 빈으로 생성한 noticeService객체를 의미

 

 

3. ListController

  • ListController 클래스에 NoticeService 필드와 setter 선언
  • getList(1,"title","") 같은 기본값으로 가져옴
    • getList(int page:페이지번호, String field:카테고리, String query:검색어)
  • model&View에 1.view:notice.list 2.model:list 담고 mv 리턴
    • 디스패처가 해당 정보 받아 뷰 호출

 

 

4. 서비스 객체

src/main/java/service/NoticeService 인터페이스

src/main/java/service/jdbc/JDBCNoticeService implements NoticeService

 

JDBCService를 쓰다가 Mybatis를 쓰거나 하면 Controller가 직접 서비스객체와 닿아 있을 때 결합력으로 인해 문제가 된다.

따라서 자료형을 인터페이스로 쓰고, 서비스 객체는 인터페이스 구현하게 한다. 

이제 사용하는 DB가 달라져도 xml설정만 간단하게 변경할 수 있다.

 

public interface NoticeService {
	List<Notice> getList(int page, String field, String query) throws ClassNotFoundException, SQLException;
	int getCount() throws ClassNotFoundException, SQLException;
	int insert(Notice notice) throws SQLException, ClassNotFoundException;
	int update(Notice notice) throws SQLException, ClassNotFoundException;
	int delete(int id) throws ClassNotFoundException, SQLException;
}

 

 

사실 데이터베이스는 서비스와 분리되어 DAO라는 계층에서 따로 구현해야 하지만 현재는 분리하지 않고 진행. 뒤에서 변경할 것

 

 

 

5. view단

  • 태그라이브러리 설정
    • <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    • forEach문으로 list 꺼내 사용
  • EL태그의 표현식이 마치 속성 꺼내는 듯 하지만, 실제로는 getter[n.getId()]
  •  Notice객체에 getter이 있어야 데이터 꺼낼 수 있음

 

Servlet에서 스프링 강의로 옮겨오면서 쿼리에 변화가 있었다.

NOTICE 테이블은 그대로 사용 가능한데, Notice_view가 다르기 때문에 NOTICE_VIEW를 만드는 쿼리문을 추가한다.

NOTICE_VIEW
create view notice_view

as select * from (select rownum num, N.* from (
            select * from notice order by regdate desc ) N );

 

 

 

 

6. 데이터 연결정보 분리하기

DB의 위치가 달라지거나 비밀번호가 달라진다고 이미 배포된 프로그램의 소스코드를 변경할수 없다.

따라서 이러한 변경에 대한 내용을 따로 빼서 xml에 둔다.

  • JDBCNoticeService.java : DataSource 인터페이스 사용
    • private DataSource dataSource 및 setter 선언
    • 기존의 driver, url, username, password 정보지움
    • 기존의 연결을 위한 명령어는 주석처리하고,
      Connection con = dataSource.getConnection();
      만 남긴다.

  • pom.xml에서 spring-jdbc 추가
  • dispatcher-servlet.xml에서 dataSource객체 생성
    • dataSource 객체를 빈으로 생성하면서 setter 설정
    • DriverManagerDataSource 클래스는 남이 만들어준 클래스로, setter의 이름들이 정해져있다.
      • driverClassName | url | username | password

 

 

7.Spring 설정 파일 분리하기

지금까지 -servlet.xml로 정해진 이름을 WEB-INF아래 두었는데 사실 스프링 설정 파일은 원하는 위치와 파일명으로 만들 수 있다.

  • 일반적으로 원하는 위치에 원하는 파일로 분리해서 쓴다.
  • 분리해서 쓰면서 동기화(기다리는 작업) 축소
  • 이름은 여기서 그냥 정해준 이름, 변경 가능, 필요에 따라 더 나누면 됨
    • security-context.xml :로그인, 보안
    • service-context.xml : 데이터 서비스
    • servlet-context.xml : 뷰단

WEB-INF안에 spring이라는 폴더를 두고 그안에 dispatcher-servlet파일을 분리해둠

기존의 dispatcher-servlet.xml은 backup으로 확장자명을 바꿔 설정파일 역할을 막음

 

 

 

web.xml에서 설정 파일을 연결한다.

① servlet-context.xml 연결

  • 기존의 servlet 태그 설정에서 <init-param> 태그를 추가하여 기본적인 서블릿 설정 파일을 연결
  • load-on-startup
    • 서블릿이 메모리에 올라가는 시점은 첫번째 요청이 올 때이다. 아무런 설정없이 둔다면 첫 요청은 속도가 느릴 수밖에 없다.
      load-on-startup은 이를 방지하기 위해 톰캣이 시작될 때 서블릿이 미리 올라가게 할 수 있는 설정으로, 우선순위를 넣는다.
  • async-supported
    • 줄서서 처리하는 동기적 처리방식 말고, 비동기적으로 서블릿이 로드되는 것을 원한다면 true로 설정

 

② service-context.xml, security-context.xml 등 기타 스프링 설정 파일 연결

  • 위와 같이 서블릿 기본 설정은 하나밖에 불가능하기 때문에 리스너를 사용해야 한다.
  • listener : 톰캣이 시작, 끝날때 세션이 시작되고 끝날때 등 이벤트 처리

 

 

 


자바APP을 xml 혹은 어노테이션으로 설정 가능

가장 최근에는 경량화된 xml파일을 사용해서 설정하기도 함

 

객체 DI를 Annotation으로 변경

servlet-context.xml

  • <property name="noticeService" ref="noticeService"/> 주석처리
  • Context 처리기 추가 후 <context:annotation-config/>

ListController에 @Autowired

  • setter위 : Autowired될 시점에 메소드안에 코드함으로써 무언가 수행할 수 있는 여지 있음
  • 필드 위 : 그게 아니면(세팅으로 끝난다면) 필드에 해서 좀 더 간결한 코드, setter를 지울 수 있다

서비스 객체를 어노테이션으로 생성

service-context.xml

  • noticeService 빈 주석처리
  • 스캔 범주 설정 : <context:component-scan base-package="com.newlecture.web.service"/>
    • <context:annotation-config/>를 포함하는 개념
    • jdbc까지 안쓰고 service까지만 써줬음

JDBCNoticeService

  • @Service 어노테이션
  • @Component보다 세분화된 어노테이션

Annotation으로 URL매핑

 

왜 com.newlecutre.web으로 스캔할 범주를 크게 두지 않을까?

- 그 안에 entity 등 다양한 패키지가 존재하는데 몇몇은 IoC에 담아야할 객체가 아니기 때문에

 

servlet-context.xml

  • 스캔범주설정
    • <context:component-scan base-package="com.newlecture.web.controller"/>
  • 추가 필수 설정
    • <mvc:annotation-driven/> 매핑을 위한 필수요소


xml을 어노테이션으로 바꿀 때의 장점
스캔 범주안의 클래스 내에서 어노테이션 찾고 처리
xml파일 설정을 통해 할 때는 해당 객체를 객체화하기 위해서 인터페이스 규약을 따랐어야 함
그러나 어노테이션을 통해 설정함으로써 더이상 Controller 클래스 implements하지 않아도 됨

 

 

IndexController.java

  • implements Controller & handleRequest 오버라이드 주석처리
  • 메모리 로드 : @Controller
  • url 매핑 : @RequestMapping("요청명")
  • Ctrl+Shift+O(import 정리)
  • import org.springframework.web.servlet.mvc.Controller는 직접 삭제
  • 다시 import정리하면 에러 사라짐
  • 설령 뷰 정보를 전달하지 않아도 url만으로 리졸빙을 한다.
    • servlet-context.xml설정에 url 매핑과 관련하여 tiles설정과 기본 설정이 존재
    • tiles 요청명에 해당하지 않으므로 그 다음 우선순위인 기본설정에 따라 요청명.jsp로 요청한다
  • 더이상 url이 클래스에 매핑되지 않는다. 매핑되는 것은 클래스가 아닌 함수
    • 매핑하는 함수가 여러개 올 수 있다. 즉 하나의 클래스 안에서 여러 개의 컨트롤러를 포함할 수 있다.
    • IndexController는 폴더 개념으로 바뀜. root 폴더에 있는 컨트롤러를 담는 그릇으로 이름은 HomeController로 변경했다.
    • 하나의 클래스 안에 여러 개의 뷰단을 연결하는 메소드가 존재한다면, 각 요청명들의 공통된 부분은 클래스 매핑으로 뺄 수 있다.
    • 함수의 반환타입은 void도 가능하다. 반환값이 없을 때 스프링은 뷰리졸버 기본 설정에 따른 주소를 요청한다.
      • 리졸버가 없으면 500에러 (컨트롤러는 실행을 하나, 페이지를 찾지 못할뿐)
    • 명시적으로 반환하여 특정 view페이지를 호출하고 싶다면 String으로 두고 반환값으로 아래처럼 특정 페이지의 이름을 반환해야한다. 타일즈 리졸버를 사용할 경우 그 매핑값을 전달해야 한다.

 

HomeController | NoticeController



현재 model을 만드는 부분은 처리되지 않았다.

 

 

컨트롤러 정리하기

파일 구조와 url구조를 맞추는 편이 좋다.

이제 컨트롤러 클래스는 기능 집합으로, 일종의 패키지처럼 여러 컨트롤러를 담당하는 함수들을 포함한다. 일관된 기능단위(공지사항)로 클래스를 만들고 그 안에 view단위로 함수들(detail, list)를 넣는 방식이다. 

 

 

 

 

 

현재까지 @RequestMapping을 통해 url mapping을 하고 있지만, 후에는 최신의 getMapping() 등을 사용할 예정

 

 

 

 

notice 폴더 내용 삭제전 백업

더보기
package com.newlecture.web.controller.notice;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class DetailController implements Controller {

	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		ModelAndView mv = new ModelAndView("notice.detail"); //notice-detail 등 패턴 쓰기 나름
//		mv.setViewName("/WEB-INF/view/notice/list.jsp");
		return mv;
	}
}
package com.newlecture.web.controller.notice;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import com.newlecture.web.entity.Notice;
import com.newlecture.web.service.NoticeService;
import com.newlecture.web.service.jdbc.JDBCNoticeService;

public class ListController implements Controller {
	@Autowired
	private NoticeService noticeService;
	
	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		
		ModelAndView mv = new ModelAndView("notice.list"); // notice/list에서 슬래쉬를 .으로 변경
		List<Notice> list = noticeService.getList(1, "TITLE", ""); //DB에서 entity 꺼내오기
//		mv.setViewName("/WEB-INF/view/notice/list.jsp");
		mv.addObject("list", list); //모델로 담기
		return mv;
	}
	
	/*
	 * public void setNoticeService(NoticeService noticeService) {
	 * this.noticeService = noticeService; }
	 */	
}

 

servlet-context.xml  에서 삭제된 내용

 <!-- <bean id="/index" class="com.newlecture.web.controller.IndexController"/> -->  
    <!-- <bean id="/notice/list" class="com.newlecture.web.controller.notice.ListController">
    	<property name="noticeService" ref="noticeService"/>
    </bean>  
    <bean id="/notice/detail" class="com.newlecture.web.controller.notice.DetailController"/> -->

 

 


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

 

스프링의 디스패처가 사용자 요청이 들어오면 invoke() 호출해 우리가 만든 컨트롤러를 호출한다.그런데, 설정이 없거나 달라 url 매핑해둔 클래스를 찾을 수 없다면 컨트롤러가 없다는 뜻에서 404 에러를 반환한다.한편 컨트롤러가 있어도, 반환값이 없고 뷰리졸버 설정도 되어있지 않다면 (즉 뷰를 제공하지 않으면) 또 404에러를 반환하게 된다.

 

 


 

문서 출력 방법 4가지

 

1. 서블릿 객체를 얻어서 문자열 출력

함수의 반환 타입을 void로 둔다면, 리졸버를 쓰지 않게 된다.

프론트 컨트롤러가 invoke 함수를 호출할 때(즉 컨트롤러가 호출될 때) 컨트롤러의 어노테이션이 붙어있는 메소드가 매개값을 가지는지를 먼저 체크한다. 매개값이 없다면 일반적인 방식을 수행하고, 매개값이 존재한다면(즉 컨트롤러에서 디스패처에 요구한다면) response같은 서블릿 객체를 함께 컨트롤러에 전달한다. 따라서 매개값으로 받아 서블릿 객체를 이용할 수 있다.

 

 

2. @ResponseBody 설정을 통한 문자열 출력

리졸버를 쓰지 않고 사용자에게 리턴값을 문자열로 전달

서블릿 객체를 사용하지 않아도 리졸버를 호출하지 않고 문자열을 전달할 수 있는데, @ResponseBody를 이용하면 된다.

좀 더 진보된 방식이다.

 

 

3. ResourceViewResolver를 이용해 문서 출력

4. TileViewResolver를 이용해 문서 출력

 

 

 

 

 

 

 


 

@RestController

데이터 제공해주는 것이 주된 역할

자바스크립트 개발자에게 데이터를 제공할 때 이런 방식으로 제공

  • @RestController 설정시 @ResponseBody를 하지 않아도 기본적으로 문자열 반환한다.
    • 이때, return값이 한글을 포함한다면 제대로 출력하기 위해 따로 설정이 필요하다.
  • servlet-context.xml 에서 <mvc:annotation-driven>이라는 어노테이션 관련 태그를 열어 수정한다.
    • <mvc:message-converters>
      어노테이션 방식으로 url 매핑이 이루어졌을 때, @ResponseBody 형식의 어노테이션이 존재한다면 그 요청을 가로채서 문자열을 반환해주는 역할을 한다.

 

리턴 문자열 인코딩 [ servlet-context.xml ]

	<mvc:annotation-driven>
		<mvc:message-converters> <!-- @ResponseBody로 String 처리할때 한글처리 -->
			<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>

 

 

※IoC에 객체를 담을 때 이름 생성 규칙

이름을 명시적으로 지정하지 않는다면

자동으로 클래스 이름을 바탕으로 bean의 이름을 만든다.

이때 패키지가 달라도 같은 이름으로 생성하려 하기 때문에

같은 이름의 클래스가 이미 객체화되어 존재한다면 에러가 난다.

따라서 두번째부터 명시적으로 이름을 지정해야 한다.

 @RestController("apiNoticeController")

    NoticeController noticeController = new NoticeController

    <bean id="noticeController" class=".....NoticeController/>

 


JSON 출력하기

서버에서 객체를 반환하면 클라이언트에서 객체 값을 문자열로 변환해 받을 수 있게 함

1. pom.xml에 다음 라이브러리 추가

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.11.2</version>
</dependency>

 

2. Controller에서 객체로 던지기

 

기존
공지사항 데이터를 목록으로 가져와서 컬렉션을 forEach문으로 꺼내 문자열을 포맷팅 -> 귀찮음

가장 많이 쓰는 데이터 구조
CSV(컴마로 구분된 문자열) : 데이터를 중첩적으로 구분하기가 어려움

XML (메타데이터) : 아무리 중첩이 깊어져도 단일한 태그 방식으로 중첩이 가능해서 용이했음
    - 느리고 비대하다는 한계

 <notices>
       <notice id="1" title="hello" />
       <notice id="2" title="hi" />
</notices>
     

JSON (메타데이터+경량화된 데이터)
원래는 자바 스크립트의 표기법인데, 백엔드에서도 사용됨
자바스크립트에게 데이터를 포장해서 보내기 위해 가장 좋은 방식
스프링 없이는 제이슨으로 변환해서 던져야 하나
스프링에서는 객체나 리스트를 던지면 알아서 제이슨 형식으로 변환해 전달한다.

 

 


입력

매핑, 출력을 다뤘고 DAO, 서비스와 관련해선 앞서 DI, AOP에서 다루었음

이제 스프링에서 다룰 것은 입력만 남음

 

QueryString URL로 전달되는 문자열

"/list?p=1" Get요청

  • 메소드가 매개변수를 가지고 있다면 Front Controller에서 Controller를 호출하기 전에 스캔해서 필요한 매개값을 보내준다. request, response객체, get요청으로 넘어온 값들을 전달받을 수 있다.
  • 뷰단의 폼태그 안에 name값을 그대로 변수로 받을 수 있다.
     

@RequestParam

  • 변수명 별칭 주기 : 메소드명(@RequestParam("p") int page)
    • name값으로 받아온 변수의 별칭을 따로 설정할 수 있고, 형변환 또한 자유롭다 (스프링에서 제공)
  • defaultValue 기본 값 처리 : null값을 처리할 수 없는 자료형으로 받기 위해 기본값을 설정할 수 있다.
  • required : 기본값은 true로 null값이 오면 오류가 난다. false로 할 경우 null값이 올 수 있다.
  • value (=name) 서로 대체해서 쓰면 됨

 

 

 


 

POST 사용자의 입력으로 전달되는 문자열 

 

한글 깨짐 방지 필터

톰캣을 손대거나 request.setCharacterEncoding("utf-8")함으로써 utf-8으로 서버쪽에서 문서를 출력할 때도 인코딩 방식을 지정할 수 있으나 매우 번거로운 일이다.스프링에서 제공하는 필터를 이용해서 한글 깨짐을 방지한다.

 

web.xml 필터설정

 

	<!-- 한글깨짐 방지 필터 -->
	<filter>
		<filter-name>charaterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>charaterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

 

 

파일 업로드 설정하기

application/x-www-form-urlencoded : post방식의 기본 인코딩 방식

  • key=value&key=value와 같은 방식으로 전달됨
  • url에 사용되는 쿼리스트링과 거의 일치하는 방식
  • 문자열만 보낼 때는 문제가 없지만 바이너리를 포함시키기 어렵다

multipart/form-data

  • 각각의 key와 값이 파트가 나누어져 전송된다
  • 파일 파트의 경우 name값, 바이너리 데이터와 파일명이 함께 간다.
  • servlet-context 설정
    • maxUploadSize : 최대 총사이즈
      • 300mb=300*1024*1024 [xml에서 계산을 할 수 없어 계산된 값을 넣어줌]
    • maxUploadSizePerFile : 파일당 최대사이즈
    •  maxInMemorySize : 파일이 업로드되면 메모리에 들어간다. 메모리 공간을 넘어서면 임시공간에 저장.
                                                지정하지 않고 톰캣이 운영체제에 맞게 관리하도록 둔다.
    • defaultEncoding : 우리는 이미 필터를 통해 인코딩 방식을 지정했기 때문에 스킵
  • pom.xml 설정
    • commons-fileupload 추가
    • commons-io 추가
    <bean id="multipartResolver"
    	class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    	<property name="maxUploadSize" value="314572800"/>
    </bean>

 

물리경로 얻기와 파일 저장하기

 

나중에 서비스, 배포할 때를 위해 동적으로 해당디렉토리가 시스템상에서 어디에 있는지 알아내야함

 

① 저장 경로 얻기 : 택1

  • @Autowired
    ServletContext ctx;
    • 전역적으로 사용

  • @Autowired
    private HttpServletRequest request;
  • 매개변수에 ServletContext얻기
    • 해당 함수에서만 사용될 때 

② 절대경로얻기

String realPath = ctx.getRealPath("/upload");

String realPath = request.getServletContext().getRealPath("/upload");

  • 콘솔에 찍어 보면 개발환경에서의 경로와 전혀 다르다는 걸 알 수 있음
  • 개발 환경에서의 경로
  • 배포 이후의 경로

  • 경로가 물리적으로 유효한지 체크하기 위해 파일명을 붙이기 이전 (~/upload)까지를 바탕으로 File 객체를 생성한 후
    exists() 함수를 사용해 조건처리한다.

    • mkdirs() : 업로드하기 위한 폴더가 지정한 Path에 존재하지 않을 경우, 폴더(들)을 자동 생성

③ 파일 저장

File saveFile = new File(realPath); //저장 디렉토리 지정

multipartFile.transferTo(saveFile); //전송

  • 위에서 얻은 절대경로와 파일이름을 합쳐야하는데, 둘 사이의 구분자는 OS에 따라 다르다
    • windows에선 \\이지만 다른 OS에서는 path에 /가 사용된다.
    • File.separator : 현재 시스템에 맞는 구분자를 생성해준다.

 

다중파일 업로드

① 뷰단의 input태그 name="file"에서 name="files"로 변경

 

② 컨트롤러에서 MultipartFile[] files로 배열로 변경 및 변수명 수정

③ 반복문으로 처리 for(MultiparFile file :files) {  .... }

 


POST요청과 GET요청의 분리

reg 요청은 글을 입력하기 위한 페이지 GET요청(인자X)과 입력 후 등록을 위한 POST요청으로 나뉜다.

단순 페이지 요청의 경우 인자가 없어 에러가 난다

해결 방법

① 방법1 : if(request.getMethod().equals("POST")){ ..... }

② 방법2 : GET요청은 forward로, POST는 redirect로 처리한다.

 

Path 경로로 전달되는 문자열

 

 

Cookie 브라우저에 보관하고 있던 문자열

Header 요청 헤더로 전달되는 문자열