Controller
[어노테이션 정리]
1. @Controller
2. @RequestMapping("url경로")
배열로 만들시 {"url", "url"} 동시에 반응하는 컨트롤러 생성가능
3. @Data
Model
과거에는 ModelAndView를 사용했지만 스프링이 발전하면서 Model만 사용하게 되었다
Model에 담는 데이터 -> 파라미터가 아니라 다른 곳에서 발생한 데이터를 담기 위한 그릇이다
@ModelAttribute : 어떠한 데이터를 내가 어떤 변수에 넣어서 사용하겠다할 때 사용하는 것
@RedirectAttribute : 화면에 한번만 전달되는 파라미터를 처리하는 용도
Controller의 리턴 타입
(1) String : jsp를 이용하는 경우에는 jsp 파일의 경로와 파일이름을 나타내기 위해서 사용
성공 - jsp, 실패 - ??
(2) void : 호출하는 URL과 동일한 이름의 jsp를 의미
(3) VO,DTO 타입 : JSON 타입의 데이터를 만들어서 반환하는 용도로 사용(추가적인 라이브러리 필요)
(4) ResponseEntity 타입 : response 할 때 Http 헤더 정보와 내용을 가공하는 용도로 사용
(5) Model, ModelAndView : Model로 데이터를 반환하거나 화면까지 같이 지정하는 경우에 사용(최근에는 사용하지 않음)
(6) HttpHeaders : 응답에 내용 없이 Http 헤더 메시지만 전달하는 용도로 사용
3,4번은 Web에서는 잘 사용하지 않음
String 타입
redirect : 강제로 이동시킬 때 사용함
사용법 : return "redirect:";
[예제]
@GetMapping("/re1")
public String re1( ){
log.info("re1......");
return "redirect:/sample/re2";
}
@GetMapping("/re2")
public void re2( ){
log.info("re1......");
}
이렇게 되면 실제로 url에서는 re1을 호출 했지만 redirect로 인해서 re2를 이동하게 됨
파일 업로드
pom.xml에 해당 코드 추가
<!-- commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
multipartResolver 스프링 빈 추가 - servlet-context.xml
<!-- 파일 업로드 관련 - multipartResolver -->
<beans:bean id="multipartResolver"class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<beans:property name="defaultEncoding" value="utf-8"/>
<beans:property name="maxUploadSize" value="104857560" />
<beans:property name="maxUploadSizePerFile" value="2097152"/>
<beans:property name="uploadTempDir" value="file:/C:/upload/tmp"/>
<beans:property name="maxInMemorySize" value="10485756" />
</beans:bean>
실행
위의 이미지에서 주목할 점
(1) 첫번째 사진의 GetMapping, PostMapping
(2) 첫번째 사진의 files라는 변수
(3) form action의 경로
(4) 파일을 보내기위한 form의 enctype
먼저 GetMapping으로 해당 Url을 호출 시 jsp파일의 웹을 띄우게 된다
그 곳에 파일 업로드를 한 뒤 submit 버튼을 통해서 from의 action url을 호출하게 되고
그로 인해 Controller의 PostMapping이 반응하게 되고
파일 업로드를한 input의 이름이 files이기에 files라는 ArrayList에 해당 파일들이 들어있고
그 파일들을 테스트 해보게 되는 시스템이다
@ControllerAdvice : 예외처리와 원래의 컨트롤러가 혼합 된 형태의 클래스가 작성되는 방식
@ExceptionHandler(Exception.class) : 라는 방식으로 예외를 처리함
Controller
스프링 MVC에서 동작하는 Controller 클래스
Service, ServiceImpl
비즈니스 영역을 담당하는 인터페이스는 'xxxService'라는 방식을 사용하고 인터페이스를 구현한 클래스는 'xxxServiceImpl'이라는 이름을 사용
DAO
DAO나 Repository(저장소)라는 이름으로 영역을 따로 구성하는 것이 보편적, 예제에서는 별도의 DAO를 구성하는 대신에 Mybatis의 Mapper 인터페이스를 활용
VO, DTO
VO의 경우 Read Only의 목적이 강한 변수의 집합소 이고, DTO의 주로 데이터 수집의 용도이다
CRUD
게시물 조회, 목록, 수정, 등록, 삭제 등으로 예제를 실습해봄
예제 셋팅
(1) Spring Legacy Project 생성
(2) pom.xml에서 스프링 및 자바 버전 변경
(3) spring-test, spring-jdbc, spring-tx 추가
(4) junit 버전 변경
(5) Servlet 버전 변경
(6) HikariCP, MyBatis, mybatis-spring, Log4jdbc 추가
(7) log4jdbc 프로퍼티파일
(8) JDBC 드라이버 프로젝트 내 추가
(9) 기타 Lombok의 설정 등
전체적인 셋팅 값을 여기에서 확인해볼 수 있다
테이블 셋팅 및 기본키 설정
create sequence seq_board;
create table tbl_board(
bno number(10,0),
title varchar2(200) not null,
content varchar2(2000) not null,
writer varchar2(50) not null,
regdate date default sysdate,
updatedate date default sysdate
);
alter table tbl_board add constraint pk_board primary key(bno);
commit;
BoardVO 클래스
BoardVO 클래스에는 DB에서 가져올 데이터를 저장할 변수들의 집합소로써 private로 보호하는 변수들을 만들어서 @Data로 Setter, Getter를 사용해서 데이터를 가져올 수 있게 셋팅한다
Mapper 클래스
Mapper클래스란 Mybatis로 DB에 접근 할 수 있는 경우 어노테이션을 사용하거나 xml을 사용하여서 해당 메소드에 DB의 데이터를 가져올 수 있게 한다
단, 주의 해야할 점은 xml을 만들 때 Mapper 인터페이스의 패키지와 같은 경로로 폴더를 일일히 나누어서 xml을 생성하는데 반드시 하나씩 폴더를 따로따로 만들어줘야한다. 여기서 오류가 발생할 가능성이 매우 높다
MyBatis에 대한 정보
https://mybatis.org/mybatis-3/ko/sqlmap-xml.html
Insert
주의 해야 할 점은 mybatis에서 #은 Getter를 의미한다
즉 테스트코드와의 순서를 보면 먼저 VO 클래스에 insert할 데이터를 넣어준다
그리고 난 뒤 insert를 실행 시키면 현재 VO에 들어있는 데이터를
Mybatis에서 #{ }으로 인식하여 BoardVO에 있는 title, content, writer의 데이터를 가져와서 DB에 삽입시킨다
라는 의미이다
SelectKey
https://yookeun.github.io/java/2014/07/11/mybatis-selectkey/
BoardMapper.xml
<insert id="insert">
insert into tbl_board(bno, title,content,writer)
values (seq_board.nextval, #{title}, #{content}, #{writer})
</insert>
<select id="read" resultType="org.zerock.domain.BoardVO">
select * from tbl_board where bno = #{bno}
</select>
<delete id="delete">
delete tbl_board where bno = #{bno}
</delete>
<update id="update">
update tbl_board set
title = #{title},
content = #{content},
writer = #{writer},
updatedate = sysdate
where bno = #{bno}
</update>
BoardMapper 인터페이스
List<BoardVO> getList();
void insert(BoardVO vo);
BoardVO read(Long bno);
int delete(Long bno);
int update(BoardVO board);
Test 코드
@Test
public void read() {
BoardVO vo = boardMapper.read(7L);
log.info(vo);
}
@Test
public void delete() {
int count = boardMapper.delete(1L);
log.info("count : " + count);
}
@Test
public void update() {
BoardVO vo = new BoardVO();
vo.setBno(3L);
vo.setTitle("Updated Title");
vo.setContent("Update Content");
vo.setWriter("User00)");
log.info("count : " + boardMapper.update(vo));
}
Service
Service 계층을 나누는 이유는
(1) Repository Layer (저장소 DB)
(2) Service Layer (비즈니스)
(3) Presentation Layer (웹 표면)
이러한 구조로 이뤄지기 때문이다
저장소에서 바로 Presetation 으로 갈 수도 있지만 이런식으로 구현하지 않는다
그렇기에 아까 만들었던 Mapper 클래스를 사용해서 Service 클래스를 만들어서
Web으로 출력시킬수 있도록 한다
Service(인터페이스)
public interface BoardService {
public void register(BoardVO board); || insert
public BoardVO get(Long bno); || read
int modify(BoardVO board); || update
int remove(Long bno); || delete
List<BoardVO> getList(); || getList
}
ServiceImpl(실제 구현 클래스)
public class BoardServiceImpl implements BoardService {
private final BoardMapper mapper;
@Override
public void register(BoardVO board) {
mapper.insert(board);
}
@Override
public BoardVO get(Long bno) {
return mapper.read(bno);
}
@Override
public int modify(BoardVO board) {
return mapper.update(board);
}
@Override
public int remove(Long bno) {
return mapper.delete(bno);
}
@Override
public List<BoardVO> getList() {
return mapper.getList();
}
}
Web 부분의 페이지를 만들기 전에 이러한 도표를 만들 필요성이 있다