기초단계/SPRING

2023.01.19 Spring

춘핑이 2023. 1. 20. 10:57

Spring - MVC

38. POST 입력을 위한 Admin 컨트롤러 추가하기

customer페이지는 보는 것 만 할 수 있어서 등록버튼이 없다.
com.newlecture.web.controller.admin.board 에서 관리자가 공지사항 등록 등 여러가지를 할 수 있도록 클래스를 생성해주자.
@RequestMapping("reg")
public String reg() {
return "reg";
}

이번엔 reg를 추가할 것이다 출력만 위해 reg를 추가해보자.

39. POST 입력방법

사용자 입력데이터를 어떻게 받는지.
reg.html을 그냥 불러와보도록하자. 404에러
<mvc:resources location="/static/" mapping="/**"/> 옛날에 리소스를 매핑햇엇다.
static안에 있는 것을 요청해봐라 해놔서
매핑이되서 컨트롤러의 reg가 나와버린다. url패턴이 맞기 때문에 그렇다. 컨트롤러가 우선되서 안나옴.
reg1.html로 일 단 변경해보자. 등록폼이 나온다.
<form action="reg" method="post" enctype="multipart/form-data">
action에 reg를 지정해주면 알아서 컨트롤러에 연동이된다.

@RequestMapping("reg")
@ResponseBody
public String reg(String title, String content) {
        return String.format("title:%s<br>contentL%s<br>", title, content);
}

@RequestMapping("reg")
@ResponseBody
public String reg(Httpservlet request) {

}

둘다 가능하지만 변수명으로 하는게 보기 쉬우니 하자 잘 전달되는 것을 알 수 있다.
title:sdsds
contentLsdsdsdds

40. POST 입력 #2 (콤보박스 값 입력)

POST입력을 위해 사용지입력을 받을 것이다. 콤보박스 체크박스 등등
간단하게 입력받아봣다. 실제 입력받을때는 다양한 컨트롤러가 사용된다.

<th>카테고리</th>
<td class="text-align-left text-indent text-strong text-orange" colspan="3">
<select name="category">
<option>카테고리1</option>
<option>카테고리2</option>
<option>카테고리3</option>
<option>카테고리4</option>
</select>

콤보박스를 추가해보았다. 그런데 사실 선택된 라벨이 전달되는 것이 필요로 하지 않다.
카테고리에 있는 데이터베이스에 있다면 컨텐츠 이름이 있을뿐이고 식별자를 전달받을 것이다.
value="1" 를 넣어주면 밸류값이 전달된다.

@RequestMapping("reg")
@ResponseBody
public String reg(String title, String content, String category) {
    return String.format("title:%s<br>contentL%s<br>categroy:%s", title, content, category);
}

title:sss
contentLaaa
categroy:3

41. POST 입력 #3 (체크박스, 라디오버튼 입력)

<th>좋아하는 음식</th>
<td class="text-align-left text-indent text-strong text-orange" colspan="3">
<input type="checkbox" value="1" id="ch1"><label for="ch1">자장면</label>
<input type="checkbox" value="2" id="ch2"><label for="ch2">짬뽕</label>
<input type="checkbox" value="3" id="ch3"><label for="ch3">볶음밥</label>
<input type="checkbox" value="4" id="ch4"><label for="ch4">탕수육</label>

체크박스를 만들어보았다. 이것이 서버에 어떻게 전달이 되는가?
name= food1 food2이런식으로 할수도있지만 이러면 파라미터를 하나씩 넣어야해서 불편해진다.
nmae = food하나로해서 배열로 받아 올수 있도록 하자.

@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이면 둘중하나만 선택가능하다.
체크박스와 콤보박스를 봤는데 일반적인 텍스트박스일때도 이름이 같다면 배열로 받을 수 있다.

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 는 문자열, 문자열, 바이너리 데이터가 가게된다.

인코딩박식에 따라 수납방법이 달라진다. &로 구분된 방식이아니라면 이해를 하지못해 서버에서 값을 받을때는 또다른 장치를 해야한다.
서블릿이기 때문에 servelt-context로 와서 설정하자.

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="314572800" />
</bean>

맥스 사이즈를 300메가로 했다. 서블릿jsp때는 30010241024로 알아보기 쉽게 넣었는데 xml에서는 그게 불가능해서 값을 넣어줘야한다.
https://docs.spring.io/spring-framework/docs/1.2.8/javadoc-api/org/springframework/web/multipart/commons/CommonsMultipartResolver.html
어떤일을 하는지는 가서보자.
https://chunpinge.tistory.com/117 jsp의 97번강의
전체에 대한 크기 설정 전체가 얼만지 알수잇다.
<form action="reg" method="post" enctype="multipart/form-data" >
폼에가서 인코딩타입을 설정해주어야한다.

@RequestMapping("reg")
@ResponseBody
public String reg(String title, String content, MultipartFile file, String category, String[] foods, String food) {

String fileName = file.getOriginalFilename();
long size = file.getSize();
System.out.printf("fileName:%s. fileSixe:%d\n", fileName, size);

System.out.println(category);
for(String f : foods) {
    System.out.println(f);
}
return String.format("title:%s<br>contentL%s<br>categroy:%s", title, content, category);
}

다른 파라미터를 받듯이 넣어주면된다. MultipartFile형식으로 받고 잘 왔는지 확인하기 위해 이름과 사이즈를 확인해보자.
그런데 아파치가 제공하는 파일업로드기능이 없다고 오류가 난다.
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
폼파일에 작성해서 라이브러리를 추가해주자
fileName:43.1.1 ㅇㅇ.png. fileSixe:88710 실제로 된게 확인이된다.

44. POST 입력 #6(물리경로 얻기와 파일 저장하기)

내가 원하는 위치에 올린 파일을 다운받고자하자.
File saveFile = new File(realPath);
multipartFile.transferTo(saceFile);
전달받은 파일을 단순하게 저장할때는 이렇게 하면된다. 그런데 이 경로를 얻는게 힘들다
이 홈디렉토리가 어디에 있는지 알 수없다. 그런데 개발하는 과정에서 이 개발경로를 지정하면 안된다. 나중에 이것을 배포하면 다른컴퓨터에서 하기 때문임
배포상의 서버경로는 이 경로가 아닐 경우가 많다. 동적으로 알아야되는 작업이 필요하다.
홈디렉토리가 물리적으로 어디에 잇는지 알아내야한다.


어떤걸로 얻든 유연하게 사용하면된다.
static에 사용자에게 전달하기 위해서 파일을 모아 놓는 것이니 static에 upload파일로 경로를 지정하자.
실질적인 생성된 디렉토리는 F:~이다. 이 경로를 복사해서 무턱대고 넣으면안된다.
배포되서 사용할 수가 없기 때문이다.
이함수에서만 사용되는 것이라면 매개변수로 HttpServletRequest request를 넣고
ServletContext ctx = request.getServletContext();
전역적 사용이라면 멤버에 두고 사용하자.
@Autowired
private ServletContext ctx;
이 Context객체를 스프링에서 IOC컨테이너에 담아놓고 있어서 Autowired하면 객체가 바인딩된다.

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

File saveFile = new File(realPath);
multipartFile.transferTo(saceFile);

metadata에 plugins에 저장된다. 개발경로와 완전히 다른것이다. 배포가되고 임시디렉토리에 저장이된다. 그경로가 나오는 것이다.
출력을 어디에할건가? 리얼패스에 저장해야한다. 실제 풀경로를 얻어야한다.
realPath + File.separator +fileName에 파일명을 얹어야한다.

String fileName = file.getOriginalFilename();
long size = file.getSize();
System.out.printf("fileName:%s. fileSixe:%d\n", fileName, size);

//ServletContext ctx = request.getServletContext();
String webPath = "/static/upload";
String realPath = ctx.getRealPath(webPath);
System.out.println(realPath);
File savePath = new File(realPath); //upload가지만 경로
//업로드하기 위한경로가 없을경우 확인하고 생성
if(!savePath.exists()) {
savePath.mkdirs();
}
realPath += File.separator + fileName;

File saveFile = new File(realPath);
file.transferTo(saveFile);
기본 서블릿때는 파일을 만들어내고 인풋스트림 아웃풋스트림등을 했던것 같은데 스프링에서 알아서 해준다.

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) { 코 드 }위에작성한내용을 괄호에 넣어주기만 하면된다.

46. 공지사항 관리자 페이지 준비하기

사용자로부터 값을 전달받을 수 있고 할 수 있게 되었다. 사용자 페이지는 추가했엇고 관리자 페이지가 필요하다.
customer라는 공통분모를 만들고 실제 페이지에서는 메인만 남겨놓았다.
amdin폴더를 추가하고 설정들을 나누자. tile설정으로 합쳐보도록하자.
만약어드민 board.event가 추가되엇을때를 감안하자. 모든 것을 패턴으로 커버하기 위해서 {1},{2}해주면된다.

<definition name="admin.board.*.*" 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/board/{1}/{2}.jsp" />
    <put-attribute name="footer" value="/WEB-INF/view/inc/footer.jsp" /> 
</definition>

reg는 안된다. files를 사용하고 있는데 이것이 null값이 오면안되서 그렇다.
get요청에서는 files가 필요가 없다. 겟요청과 포스트요청을 구분해보자.

47. POST 매핑과 Redirection

글쓰기 누를때 reg가 불러와줘야한다. get과 post를 구분하기위해서 서블릿할때 doGet과 doPost을 사용햇엇다.
3.0때는 @RequestMapping에 밸류값과 METHOD를 사용해서 매핑을 햇엇다.


4.0으로 넘어가며 getMapping이 생겨서 편해졋다.

Spring - JDBC

1. Service와 Dao 레이어의 이해

https://youtu.be/Dkygu8Lclno
사용자 입출력 MVC를 배웟다. 그 구조를 알아보자.
서비스와 데이터 레이어를 구분해야한다. 사용자로부터 입력받는 GET POST를 햇다.
Sevice와 Dao가 있다. 연결하기 위해 DI를 사용햇다.
Service 인터페이스 -> sql -> dao인터페이스
만약 서비스를 구현할때 인터페이스를 두눈이유는 각각 나눠서 일을 한다는 전제조건잇을때 인터페이스를 사용하면 협업하기 쉽다.
구현체를 만들어서 그때그때 꽃아넣기만하면된다.
컨트롤러는 서비스를 이용하고 서비스는 다오를 사용한다. 그럼 서비스와 다오의 역할이 무엇인가
원래는 서비스에서 sql을 직저버 작성하는데 이 sql을 미룬다는 것이고 dao를 사용한다는 것이다.
dao가 제공하는 sql기능을 사용한다는 것이다. 업무보다는 sql이 가지고잇는것을 dao가 가지고 있는 것이다.
장점은 서비스를 구현하는사람이 sql을 몰라도 된다는 것이다. java만 사용할줄알아도 가능하다.내부적으로 어떤 저장소를 쓰는지도 몰라도된다.
자바만 알면 업무를 할 수 있다. 각각 전문가들이 업무를 나눠서할 수 있게 된것이다.
dao는 sql이가진것을 다루면되고 서비스에서는 사용자입력을 다루는 서비스를 제공하면된다.
규모가작으면 애매하다. 거의 같은 역할일 수도 있다. 비효율적이지라고 생각한다면 원론적인것을 알면좋다.


서비스레이어와 DAO를 나눠서만들때 1대1의 관계를 가지는 사람이 있다.
근데 이럴거면 나눌 필요가 없다. Dao는 sql 이라고 생각하면된다.
컨트롤러 입장에선 서비스는 하나의 총판이다. dao들을 연결해서 가져오도록 하는 것이다. 곁다리 데이터들을 달라고 한다.

페이지들을 NoticeSerive eventSecice 등 각각 나눠서만드는 일이잇다.
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;
}

정말 긴내용이 단 3줄로 끝나게 되엇다.
다음 강의는 강사님이 아파서 올리시지 않았다고 한다...
JDBC를 사용하기 위한 개념은 잡은 상태라고 한다..
스프링부트로 넘어가보자.

Springboot

1. 스프링 부트란?

https://youtu.be/4-0scAf5tpU
스프링을 사용하기 위해서는 was 서블릿/jsp 그위에 스프링 di트랜잭션 mvc등을 하면서 설정을 햇다.
라이브러리도 여러개가 잇어서 사용하기 위한 사전작업을 많이햇다.
어플리케이션을 쾌적하게 만들기위해 어려움을 겪던것을 합쳐버려서 스프링 부트로 만들어버렷다. 톰캣 다운받지마! 서블릿jsp알아서 하겟다 루트 알아서 만들겟다 등등
스프링 어플리케이션을 만들때 그냥 만들어라. 전부 통합시켜놧다. war로 할필요없고 그냥 배포하면된다.
dependencies많은데 스타터로 알아서 넣어주겟다. configure알아서 설정해주겟다. 최대한 구조설정을 해주겟다는 것임.
자바프로그램처럼 만들고 알아서 설정하게 하겟다.
스프링 그냥 만들기, 부트를 사용해서 알아서 설정되게 하기 두가지로 개발을 할 수 있게 되엇다.

2. 학습개요


고전적인 방식 1대1로 매핑햇다 -> 어노테이션으로 매핑햇다. -> 버전올라가며 자바 컨피그를 사용햇다. -> 부트에서는 이것도 사용하지 말라는 것이다.

3. sts다운

이미 다운받았으니 넘어가자.

4. Spring Boot Starter 프로젝트 만들기


스프링을 만들면서 필요한 것을 추가할 수있다.라이브러리도 선택하여 추가할 수 있다.
신기한점은 메인함수가 존재하게 된다. 웹하면서 메인을 사용하지 않앗어는데 서블릿에서는 메인이 없엇다. 메인은 톰캣이 가지고 잇엇기때문임. 톰캣도 스프링부트 밑에 들어가게 되는 것이다.
pom.xml을 가도 별로없는데 수많은 라이브러리들이 알아서 들어가잇다.
HomeController를만들어보자.

package com.newlecture.web;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringWebApplication {

public static void main(String[] args) {
    SpringApplication.run(SpringWebApplication.class, args);
}
}

package com.newlecture.web.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController {

@RequestMapping("/index")
public String adf() {
    return "Hello Spring Boot";
}}

SpringWebApplication 메인함수이니 그냥 여기서 실행하면된다.

http://localhost:8080/index
매핑된곳으로 가보면 Hello Spring Boot 잘 출력된다!

5. 수업용 HTML 파일 준비하기

원래는 여기다 햇다. main- webapp 만들어주자. 그럼여기에 옮긴다? 아니다.
스프링MVC에서 정적페이지들을 저장하기 위해서 webapp에 static을 만들엇엇다. 스프링부트는 resources/static이라는 폴더가 이미 잇다.
스프링 부트에서는 정적인 페이지 디렉토리와 동적인 페이지가 나뉘어져잇다.
http://localhost:8080/index.html 페이지를 들어가면 들어가진다.
이제 홈디렉토리가 두가지가 된다. 루트가 두개가 되는 것이다.ngboot

2023.01.19 후기

스프링 MVC를 열심히 배우고 싶었으나 강의가 더이상 올라오지 않는다.
일단 진도를 나가지만 스프링부트의 경우 더 쉽게 알아들을 수 있도록 원리 설명이 없는 것 같다.
JSP를 했던 내용과 겹치지만 잘 생각이 나지 않는다. 역시 한번만 본 것으로는 쓸모가 없다. 복습을 철처하게 해야하는 이유를 다시 깨달았다.
20일은 국비 교육 오티가 있는 날이다. 이제 진짜 드디어 라는 생각이 든다. 여기저기 안좋은 소식뿐이다. 잘 되고 싶다.