newlec Spring MVC
31.문서 출력방법 4가지 @ResponseBody
url매핑을 했는데 이제 출력하는 것이 필요하다.
문서를 출력하는 방법은 4가지가 있다.
1.서블릿객체를 얻어서 문자열 출력하기
2.@ResponseBody 설정을 통한 문자열출력하기
3.ResourceViewResolver를 통해 문서출력하기
4.TilesViewResolver를 이용한 문서출력하기
어떤것을 이용하든 선택적으로 사용할 수 있어야한다.
String으로 출력하는게 아니라 void로 해보자
서블릿이 가지고있는 httpservlet이없는데 어떻게 사용할 수 있나.?
front controller가 요청이들어오면 컨트롤러의 함수를 실행한다.
컨트롤러에서 매개값을 주면 어떤 매개값이 잇는지 알아보고 가지고있는 것을 참조해서 전달한다.
front controller가 invoke해서 함수를 실행시킨다.
과거 서블릿을 직접이용해서 출력햇듯이 변수를 선언만하면 front controller가 알아서 넘겨줘서 참조하고 출력을 하게 된다.
컨트롤러에서 response 객체를 사용하면 front controller가 같은 response객체를 참조해서 출력하게 된다.
@RequestMapping("index")
public void index(HttpServletResponse response) {
PrintWriter out;
try {
out = response.getWriter();
out.println("Hello Index");
// return "root.index";
} catch (IOException e) {
e.printStackTrace();
}
}페이지를 준비하지않고 직접출력하려면 그냥 이렇게 사용하면된다.
이것을 더 편하게 작성하는 것을 알려주기도 한다.
문서로 사용하고 싶지 않고 그냥 String을 출력하고 싶다면 @ResponseBody어노테이션을 사용하면된다.
그냥 Stirng 타입으로 반환하면된다.
문자열을 그냥 반환하면 view라고 생각하고 view에 연관해서 하고자 한다.
그래서 그냥 문자열임을 알려주기 위해서는 @ResponseBody을 붙이면 된다.
@RequestMapping("index")
@ResponseBody
public String index() {
return "Hello Index";
}response객체를 직접 만들기
@ResponseBody사용
view resolver사용 중 필요한것을 그때그때 활용하면된다.
32. @RestController와 한글출력 설정
@RestController은 문서를 만들어서 제공하는 것이아니라 주로 데이터를 제공할때 주된역할을 한다.
한글출력에 문제를 발생한다.
프로젝트를 하다보면 문서를 줄게아니라 데이터를 줄 때가 있다.
데이터를제공하는 api로 사용하는 것이다.
그냥 컨트롤러는 문서를 기반으로 반환하는 것이다.
@RestController를 붙이면 특화된 컨트롤러이다.
Rest풀 그런 형태의 결과물을 반환하는 클래스이다.
문서를 반환하는게 아니라 @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> <!-- @ResonseBody로 한글처리할때 사용하기 -->
<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 출력하기
스트링이 아닌 객체를 반환하고자한다.
사실 객체를 반환하는건 말이안된다.
클라이언트 브라우저가 서버쪽에서 제공하는 백엔드의 메모리를 공유하는 것이 아니기때문이다.
서버쪽에서 객체를 반환하면 클라이언트에서 객체를 받을 수 있게하는 것이다.
문자열로 반환해서 받는 것이다.
데이터구조는 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")
@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도구를 response 출력때처럼 그냥 변수를 넣어주면된다.
스프링이 제공하는 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 : 변수명 별칭과 기본 값처리
request 값이 전달되지 않앗을경우 기본값을 제공할 수 있다.
p를 좀더 살려서 페이지 변수로 사용하고 싶다.
Stirng page로 담으면 보내고자하는 것과 일치하지 않아서 심어질수가 없다.
page가 아니라 스프링 컨트롤러에게 이것은 p라는 것을 전달해야한다.
@RequestParam어노테이션을 사용해서 @RequestParam("p")로 표시해주면된다.
url을 짧게만들고 싶다면 줄이고 아니라풀네임으로 쓰거나 다른이름으로 작성할 수 있게 된다.
@RequestMapping("list")
public String list(@RequestParam("p") String page) throws ClassNotFoundException, SQLException {
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 Exception {
List<Notice> list = noticeService.getList(1, "TITLE", "");
return "notice.list";
}어노테이션에 여러가지 속성이 있는데 이것을 지정해주면 된다.
파라미터값은 모두 파라미터이니까 잘 작성하자.
정수형으로 하고싶다면 그냥 int page로 하면되는데 스프링이 알아서 변환해준다.
null값이오면 "1"을 제공하기 때문에 상관이없다.
인티저를 이용해서 데이터 쿼리만 하면되는 것이다.
37.@RequestParam Optional 속성
@RequestParam이 가진 옵션에 대해서 알아보자.
@RequestParam spring io을 검색하는 스프링이 제공하는 api를 볼 수 있다.
@RequestParam에는 4가지 속성이 있다.
1.name 파라미터 이름
2.defaultValue 기본값
3.required
4.value
p 값이 안오면 오류가 난다.
기본값을 주지 않고 전달하는 값이 전달되지 않앗을때
int page가 Null값을 받을 수없으니 Integer로 바꿔준다.
required를 true로하면 값을 전달하지 않앗다고 오류가 난다.
기본값도 true이다.
@RequestParam(name = "p", required = false)
그냥 required를 false로하면 오류가 발생한다.
null값을 처리하기 위해서는 기본값이 아니라 포장클래스를 넣어줘야한다.
결론적으로 required의 기본값은 true이기때문에
1.null값을 처리할 수 잇게 해주거나
2.기본값을 전달하거나
3.false로해서 필수값이 전달안받아도되게 하거나로 해줘야한다.
@RequestMapping("list")
public String list(@RequestParam(name = "p", required = false) Integer page)
throws Exception {
List<Notice> list = noticeService.getList(1, "TITLE", "");
return "notice.list";
}마지막 value는 name을 대신할 수있는데 같이 사용하면 안된다.
완전히 똑같은 기능을한다.
그럼 별칭을 왜만들엇을까? value 강사님도 알수없다?
이름을 잘못만들어서 중간단계의 별칭을 만든게 아닐까 싶다.
38. POST 입력을 위한 Admin 컨트롤러 추가하기
post 입력을 위해서 관리자페이지의 reg페이지를 등록하고자한다.
customer페이지는 보는 것 만 할 수 있어서 등록버튼이 없다.
com.newlecture.web.controller.admin.board 에서 관리자가 공지사항 등록 등 여러가지를 할 수 있도록 클래스를 생성해주자.
noticeController가 이름이 같아서 충돌되니 별칭을 지어주자.
그리고 일단은 간단하게 실행되는지 보기 위해 @ResponseBody어노테이션을 사용하자.
@Controller("adminNoticeController")
@RequestMapping("/admin/board/notice/")
public class NoticeController {
@RequestMapping("reg")
@ResponseBody
public String reg() {
return "reg";
}
}39. POST 입력방법
사용자 입력데이터를 어떻게 받는지에 대해서 알아보자.
reg.html을 그냥 불러와보도록하자. 404에러
<mvc:resources location="/static/" mapping="/**"/> 옛날에 static파일이 나오는 리소스를 매핑햇엇던 것 그대로 html이 불러지게 되는 것이다.
static안에 있는 것을 요청해봐라 해놔서 static을 지우면 매핑이되서 컨트롤러의 reg가 나와버린다.
url패턴이 맞기 때문에 그렇다. 컨트롤러가 우선되서 안나온다.
정확하게 문서를 요청하는게 아니라 패턴에 맞는 것이 나오는것이다.
reg1.html로 일단 변경하면 등록폼이 나온다.
<form action="reg" method="post">
action에 reg를 지정해주면 알아서 컨트롤러에 연동이된다.
db에 값을 받아와서 등록할 수도 있지만 간단하게 출력하는 폼을 작성해보자.
@RequestMapping("reg")
@ResponseBody
public String reg(String title, String content) {
return String.format("title:%s<br>contentL%s<br>", title, content);
}40. POST 입력 #2 (콤보박스 값 입력)
POST입력을 위해 사용지입력을 받을 것이다.
콤보박스 체크박스 등등 간단하게 입력받아봣다.
실제 입력받을때는 다양한 컨트롤러가 사용된다.
<th>카테고리</th>
<td class="text-align-left text-indent text-strong text-orange" colspan="3">
<select name="category">
<option value="1">카테고리1</option>
<option value="2">카테고리2</option>
<option value="3">카테고리3</option>
<option value="4">카테고리4</option>
</select>카테고리에 있는 데이터베이스에 있다면 컨텐츠 이름이 있을뿐이고 식별자를 전달받아야한다.
콤보박스를 사용하면 option의 value값이 전달되게 된다.
41. POST 입력 #3 (체크박스, 라디오버튼 입력)
<th>좋아하는 음식</th>
<td class="text-align-left text-indent text-strong text-orange" colspan="3">
<label for="ch1">짜장면</label>
<input type="checkbox" value="1" id="ch1">
<label for="ch2">짬뽕</label>
<input type="checkbox" value="2" id="ch2">
<label for="ch3">볶음밥</label>
<input type="checkbox" value="3" id="ch3">
<label for="ch4">탕수육</label>
<input type="checkbox" value="4" id="ch4">
</td>체크박스의 체크 내용이 어떻게 전달되는가
name = food1 food2 이런식으로 각각이름을 전달할 수도 있다.
그런데 이러면 파라미터를 하나씩 넣어야해서 불편해진다.
name = foods 하나로해서 배열로 받아오면된다.
@RequestMapping("reg")
@ResponseBody
public String reg(String title, String content, String category, String[] foods) {
System.out.println(category);
for(String food : foods) {
System.out.println(food);
}
return String.format("title:%s<br>contentL%s<br>categroy:%s", title, content, category);
}콘솔창에 출력해보면 어떤 번호를 출력했는지 알 수 있다.
radio타입으로 버튼을 만들면 다중선택이 불가능하다.
name이 다 다르면 각각 선택할 수 있고 이름이 같으면 하나의 그룹이 되서 한가지만 선택할 수 있게 된다.
ex) food1 food1이면 둘중하나만 선택가능하고 food1 food1 food2 food2가 잇다면 두개 선택가능하게 된다.
<th>좋아하는 음식2</th>
<td class="text-align-left text-indent text-strong text-orange" colspan="3">
<label for="ch1">짜장면</label>
<input type="radio" value="1" id="ch1" name = "food">
<label for="ch2">짬뽕</label>
<input type="radio" value="2" id="ch2" name = "food">
<label for="ch3">볶음밥</label>
<input type="radio" value="3" id="ch3" name = "food">
<label for="ch4">탕수육</label>
<input type="radio" value="4" id="ch4" name = "food">
</td>체크박스와 콤보박스를 사용했는데 일반적인 텍스트박스일때도 이름이 같다면 배열로 받을 수 있다.
42.POST 입력 #4 (한글 입력이 깨지는 문제)
브라우저에서 네트워크로 한글을 보내는데 한글이 전달되는 것을 서버에서 받지 못해서 발생하는 것이다.
utf-8로 2바이트씩 보내더라도 서버에서 기본형태가 ISO-8859-1을 받는다면 1바이트씩 받게 되거나 다른 코드표를 받을 수 있다.
request.setCharachterEncoding("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>필터하나만 설정하니까 해결이 됬다.
서블릿에서는 직접 필터를 만들어 사용했다.
그때처럼 우리가 직접 필터를 만들 수도 있지만 스프링이 미리 만들어놓은 것을 사용한것이다.
인코딩 필터의 경우 모든 프로젝트에서 사용가는 공통적인 기능이므로 스프링에서 번거로움을 피하기 위해 제공하는것 같다.
43. POST 입력 #5(파일 업로드 설정하기)
브라우저에서 폼태그를 특별한 인코딩방식을 지정해야한다.
인코딩방식을 지정하지 않는다면 &로 구분해서 URL처럼 키는 무엇이고 값은 무엇이고로 전달이된다.
여기에는 binary를 보내기 힘들다.
파일 보낼때는 인코딩방식을 바꿔야한다.
multipart/form-data로 보내야한다.
multipart 는 문자열, 문자열, 바이너리 데이터가 가게된다.
인코딩박식에 따라 수납방법이 달라진다. &로 구분된 방식이아니라면 이해를 하지못해 서버에서 값을 받을때는 또다른 장치를 해야한다.
서버의 기본 방식이 appl~이기때문에 서버도 multipart/form-data로 받아서 인코딩할 수 있도록 설정을 변경해줘야한다.
서블릿이기 때문에 servelt-context에 설정해줘야한다.
multipartResolver를 사용해서 처리하는 것이다.
https://chunpinge.tistory.com/117 jsp의 97번강의
jsp때의 파일 설정 을 보고 처리할 수 있다.
https://docs.spring.io/spring-framework/docs/1.2.8/javadoc-api/org/springframework/web/multipart/commons/CommonsMultipartResolver.html
파일하나의 사이즈 파일당 전체 얼마고 등등을 설정할 수 있다.
디렉토리는 톰캣이 알아서 설정하도록 냅두는게 좋다.
그래서 사이즈만 정해주면 된다.
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="314572800" />
</bean>jsp 때는 10241024300 300메가로 넣었는데 XML은 계산이안되서 값을 직접 넣어준 것이다.
전체에 대한 크기 설정 전체가 얼만지 알수잇다.
<form action="reg" method="post" enctype="multipart/form-data" >폼에가서 인코딩타입을 설정해주어야한다.
컨트롤러에서는 RequestParam으로 MultipartFile객체를 받아서 저장만 하면된다.
file로부터 이름과 사이즈를 얻어서 출력할 수 있다.
@RequestMapping("reg")
@ResponseBody
public String reg(String title, String content, MultipartFile file, String category, String[] foods) {
String fileName = file.getOriginalFilename();
long size = file.getSize();
System.out.printf("fileName:%s. fileSixe:%d\n", fileName, size);
for (String food : foods) {
System.out.println(food);
}
return String.format("title:%s<br>contentL%s<br>categroy:%s", title, content, category);
}다른 파라미터를 받듯이 넣어주면된다.
MultipartFile형식으로 받고 잘 왔는지 확인하기 위해 이름과 사이즈를 확인해보자.
아파치가 제공하는 파일업로드기능이 없다고 오류가 나서 라이브러리를 추가해줘야한다.
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>44. POST 입력 #6(물리경로 얻기와 파일 저장하기)
내가 원하는 위치에 올린 파일을 다운받고자하자.
File saveFile = new File(realPath);
multipartFile.transferTo(saceFile);
전달받은 파일을 단순하게 저장할때는 이렇게 하면된다.
그런데 이 경로를 얻는게 힘들다
이 홈디렉토리가 어디에 있는지 알 수없다.
개발하는 과정에서 이 개발경로를 지정하면 안된다.
개발하지만 배포할때는 다른컴퓨터에 배포하기때문이고 이경로가 아닐 확률이 높기때문이다.
즉 배포상의 서버경로는 이 경로가 아닐 경우가 많다.
동적으로 알아야되는 작업이 필요하다.
홈디렉토리가 물리적으로 어디에 잇는지 알아내야한다.
1.ServletContext 얻기
ServletContext를 얻어서 context에서 절대경로를 얻어야한다.
request를 통해서 얻을 수도 있다.
유연하게 사용하면된다.
2.절대경로 얻기
String realPath = ctx.getRealPath("/upload");
String realPath = request.getServletContext().getRealPath("/upload");3.파일전송
위 절대경로를 얻어서 파일을 전송하면된다.
File saveFile = new File(realPath);
multipartFile.transferTo(saveFile);먼저 타겟이 되는 루트가 필요하다.
static에 사용자에게 전달하기 위해서 파일을 모아 놓는 것이니 static에 upload파일로 경로를 지정하자.
실질적인 생성된 디렉토리는 F:~이다.
배포되서 사용할 수가 없기 때문에 경로를 복사해서 무턱대고 넣으면안된다.
Context를 얻기 위해서
이함수에서만 사용되는 것이라면 매개변수로 HttpServletRequest request를 넣고
ServletContext ctx = request.getServletContext();전역적 사용이라면 멤버에 두고 사용하면된다.
@Autowired
private ServletContext ctx;이 Context객체는 스프링에서 IOC컨테이너에 담아놓고 있어서 Autowired하면 객체가 바인딩된다.
String webPath = "/static/upload";
String realPath = ctx.getRealPath(webPath);
F:\study\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\webprj\uploadmetadata에 plugins에 저장된다.
개발경로와 완전히 다르고 배포가되고 임시디렉토리에 저장이된다.
출력을 어디에할건가 리얼패스에 저장해야한다.
실제 풀경로 + 파일구분자 + 파일일명을 얹어야한다.
realPath + File.separator +fileName에 파일명을 얹어야한다.
업로드 파일이 있는지 없는지 확인해주고 없으면 파일을 만들어주게 만들어줘야한다.
@RequestMapping("reg")
@ResponseBody
public String reg(String title, String content, MultipartFile file, String category, String[] foods)
throws IllegalStateException, IOException {
String webPath = "/static/upload";
String realPath = ctx.getRealPath("/upload");
//업로드하기 위한경로가 없을경우 확인하고 생성
File savepath = new File(realPath);
if (!savepath.exists()) {
savepath.mkdirs();
}
realPath += File.separator + fileName;
File saveFile = new File(realPath);
file.transferTo(saveFile);
for (String food : foods) {
System.out.println(food);
}
return String.format("title:%s<br>contentL%s<br>categroy:%s", title, content, category);
}45. POST 입력 #7(다중파일 업로드)
같은이름으로 제공하기 때문에 배열이된다.
배열이니 배열스럽게 이름을 바꿔주자.
<tr>
<th>첨부파일</th>
<td colspan="3" class="text-align-left text-indent"><input type="file"
name="files" /> </td>
</tr>
<tr>
<th>첨부파일</th>
<td colspan="3" class="text-align-left text-indent"><input type="file"
name="files" /> </td>
</tr>MultipartFile[] files 배열로 받으면된다.
for(MultipartFile file : files) { 코 드 }
위에작성한내용을 괄호에 넣어주기만 하면된다.
그냥두면 파일을 하나만 넣으면 오류가 나기때문에 file사이즈가 0이라면 for문을 건너뛰게 해주자.
@RequestMapping("reg")
@ResponseBody
public String reg(String title, String content, MultipartFile[] files, String category, String[] foods)
throws IllegalStateException, IOException {
for(MultipartFile file : files) {
if(file.getSize() == 0) {
continue;
}
String webPath = "/static/upload";
String realPath = ctx.getRealPath("/upload");
//업로드하기 위한경로가 없을경우 확인하고 생성
File savepath = new File(realPath);
if (!savepath.exists()) {
savepath.mkdirs();
}
realPath += File.separator + fileName;
File saveFile = new File(realPath);
file.transferTo(saveFile);
}
for (String food : foods) {
System.out.println(food);
}
return String.format("title:%s<br>contentL%s<br>categroy:%s", title, content, category);
}46. 공지사항 관리자 페이지 준비하기
사용자로부터 값을 전달받을 수 있고 할 수 있게 되었다. 사용자 페이지는 추가했엇고 관리자 페이지가 필요하다.
customer라는 공통분모를 만들고 실제 페이지에서는 메인만 남겨놓았다.
amdin폴더를 추가하고 설정들을 나누자. tile설정으로 합쳐보도록하자.
만약어드민 board.event가 추가되엇을때를 감안하자. 모든 것을 패턴으로 커버하기 위해서 {1},{2}해주면된다.
<definition name="admin.*.*.*" template="/WEB-INF/view/admin/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/admin/inc/visual.jsp" />
<put-attribute name="aside" value="/WEB-INF/view/admin/inc/aside.jsp" />
<put-attribute name="body" value="/WEB-INF/view/admin/{1}/{2}/{3}.jsp" />
<put-attribute name="footer" value="/WEB-INF/view/inc/footer.jsp" />
</definition>reg는 안된다. files를 사용하고 있는데 이것이 null값이 오면안되서 그렇다.
get요청에서는 files가 필요가 없다.
겟요청과 포스트요청을 구분해야한다.
47. POST 매핑과 Redirection
목록과 같은 경우는 get요청으로 불러온다.
글쓰기를 누를때 reg페이지가 와야하는데 post요청이 발생해서 문제가 발생한다.
서블릿할때 doGet과 doPost을 사용했던 것과 비슷하다.
글쓰기 누를때 reg가 불러와줘야한다. get과 post를 구분하기위해서
3.0때는 @RequestMapping에 밸류값과 METHOD를 사용해서 매핑을 햇엇다.
4.0으로 넘어가며 @getMapping이 생겨서 편해졋다.
get요청과post요청을 처리하기위해 매핑을 다르게 하면된다.
@GetMapping("reg")
public String reg() {
return "admin.board.notice.reg";
}
@PostMapping("reg")
public String reg(String title, String content, MultipartFile[] files, String category, String[] foods)
throws IllegalStateException, IOException {
//post처리
}Spring - JDBC
1. Service와 Dao 레이어의 이해
https://youtu.be/Dkygu8Lclno
사용자 입출력 MVC를 배웟다.
서비스와 데이터 레이어를 구분해야한다.
사용자로부터 입력받는 GET POST를 햇다.
Sevice와 Dao가 있다. 연결하기 위해 DI를 사용햇다.
Service 인터페이스 -> sql -> dao인터페이스
만약 서비스를 구현할때 인터페이스를 두는이유는 각각 나눠서 일을 한다는 전제조건잇을때 인터페이스를 사용하면 협업하기 쉽다.
구현체를 만들어서 그때 그때 꽃아넣기만하면된다.
컨트롤러는 서비스를 이용하고 서비스는 DAO를 사용한다.
그럼 서비스와 DAO의 역할이 무엇인가
원래는 서비스에서 sql을 직접 작성하는데 이 sql을 미루어서 dao를 사용한다는 것이다.
dao가 제공하는 sql기능을 사용한다는 것이다.
업무보다는 sql이 가지고잇는것을 dao가 가지고 있는 것이다.
장점은 서비스를 구현하는사람이 sql을 몰라도 된다는 것이다.
java만 사용할줄알아도 가능하다.내부적으로 어떤 저장소를 쓰는지도 몰라도된다.
자바만 알면 업무를 할 수 있다.
각각 전문가들이 업무를 나눠서할 수 있게 된것이다.
dao는 sql이가진것을 다루면되고 서비스에서는 사용자입력을 다루는 서비스를 제공하면된다.
규모가작으면 애매하다. 거의 같은 역할일 수도 있다.
비효율적이라고 생각되더라도 원론적인 것을 알아둘 필요가 있다.
서비스레이어와 DAO를 나눠서만들때 1대1의 관계를 가지는 사람이 있다.
근데 이럴거면 나눌 필요가 없다.
Dao는 sql 이라고 생각하면된다.
컨트롤러 입장에선 서비스는 하나의 총판이다.
notice라는 서비스를 만들때 이 주인공을 두고 곁다리를 관리하는 것이다.
카테고리가 필요하다면 CategoryDao에게 게시판 내용이 필요하면 NoticeDao에게 달라고 하면된다.
각각의 DAO에게 데이터를 가져다 달라고 하는 것이다.
DAO는 테이블과 각각 1대1로 관리하는 것이다.
페이지들을 NoticeService eventService 등 각각 나눠서만드는 일이잇다.
dao를 구성할때는 단조롭고 반복적이며 코드량이 많은데
이것을 스프링 jdbc, 마이바티스, JPA가 사용하기 내용을 줄어들게 해준다.
2. JdbcTemplate을 이용해 getList 메소드 구현하기
jdbc원래 할때 sql작성 db연결/실행 결과집합 매핑 db연결종료의 일을 햇다.
그런데 sql을 제외하고는 모든 내용이 같다. 인자부분이 다를 수있긴한데 이부분은 라이브러리가 알아서 할 수 잇을 것같다.
대부분 이런게 라이브러리로 만들어질 수 잇다고 하면 필요가 없을 수 잇다.
JDBCNoticeService코드를 이용해보자.
모델을 만들어서 이것을 뷰로 전달하고싶다.
스프링이 그냥 Model객체를 제공하고 있다.
전달하는 방법은 스프링 MVC강의 20,21번확인하지
@RequestMapping("list"
public String list(@RequestParam(value = "p", required = false) Integer page, Model model) throws ClassNotFoundException, SQLException {
System.out.println(page);
List<Notice> list = noticeService.getList(1, "TITLE", "");
model.addAttribute("list", list);
return "notice.list";
}원래 jdbc부분의 커넥션하는 부분을 전부 지우고
JdbcTemplate template = new JdbcTemplate();을 추가해주자.
Jdbc템플릿은 이름부터 틀을 제공한다는 것을 알 수 잇다.
여러가지 오버로드된것을 알수잇다.
template.query목록반환 queryforlist매핑이 다르다.
옛날것임 왠만하면사용x
queryforobject는 로우하나값 반환할때 사용한다.
sql과 rowmapper을 달라는 것을 사용할것이다.
서버쪽의 데이터베이스 컬럼과 이것을 데이터에 담을건데 매핑해달라는 것이다.
로우매퍼도 알아서 해주는 클래스가 잇다.
db가어디에잇는지 아이디 패스워드가 무엇인지를 알려주는 것도 탬플릿을 이용할 수 잇다.
public List<Notice> getList(int page, String field, String query) throws ClassNotFoundException, SQLException{
int start = 1 + (page-1)*10; // 1, 11, 21, 31, ..
int end = 10*page; // 10, 20, 30, 40...
String sql = "SELECT * FROM NOTICE"; //_VIEW2 WHERE "+field+" LIKE ? AND NUM BETWEEN ? AND ?";
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(dataSource);
List<Notice> list = template.query(sql, new BeanPropertyRowMapper(Notice.class) );
return list;
}JDBC 단에서 하기보다는 스프링 프레임워크로 하는 경우가 많아서 더이상 배울 필요는 없다.
2023.04.22
defualtValue = "" null이나 빈문자열을 올때 설정해주기
혼자 공부시 if로 처리햇엇는데 그러면 안된다.
한글깨지는 것처리 알아두자.
'기초단계 > SPRING' 카테고리의 다른 글
| 2023.04.27 Spring (0) | 2023.04.28 |
|---|---|
| 2023.04.24 Spring (0) | 2023.04.24 |
| 2023.04.20 Spring (0) | 2023.04.20 |
| 2023.04.18 Spring (0) | 2023.04.18 |
| 2023.04.16 Spring (0) | 2023.04.17 |