|
Back-End/Spring 2019. 6. 24. 23:17
AOP 실습예제
사용자가 메시지를 남기면 포인트 10 증가 메시지를 읽으면 포인트 5 증가
com.examplt.spring02.aop ㄴMessageAdvice.java
com.example.spring02.controller.message ㄴMessageController.java
com.example.spring02.model.message.dto ㄴUserDTO.java ㄴMessageDTO.java
com.example.spring02.model.message.dao ㄴMessageDAO.java ㄴMessageDAOImpl.java ㄴPointDAO.java ㄴPointDAOImpl.java
com.example.spring02.service.message ㄴMessageService.java ㄴMessageServiceImpl.java
|
-AOP 실습용 테이블을 생성-
(데이터베이스를 이용한 실습)
AOP와 트랜잭션 처리를 활용한 실습
글쓰기를 하면 포인트 10을 부여 글읽기를 하면 열람시간을 수정하고 포인트 5를 부여
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | drop table tbl_user cascade constraints //혹시 중복된 테이블이 있을수도 있기 때문에 테이블 삭제 create table tbl_user ( //tbl_user 테이블을 생성 userid varchar2(50) not null, //id upw varchar2(50) not null, //비밀번호 uname varchar2(100) not null, //이름 upoint number default 0, //포인트, 기본값은 0으로 한다 primary key(userid) //기본키는 id로 한다. ); create table tbl_message( //tbl_message (메시지를 저장) 테이블을 생성 mid number not null, //메시지의 id targerid varchar2(50) not null, //메시지를 받을 사람의 id sender varchar2(50) not null, //메시지를 보낸 사람의 id message varchar2(4000) not null,//메시지의 내용 opendate date, //메시지를 열어본 시간 senddate date default sysdate, //메시지를 보낸 시간 (보낼 당시 현재 시간) primary key(mid) //기본키를 메시지의 id로 설정 ); --시퀀스 생성-- create sequence message_seq //message_seq 시퀀스를 생성 (메시지가 하나 생길때마다 숫자가 1씩증가, 초기값은 1로 설정) start with 1, increment by 1; --제약 조건 설정-- alter table tbl_message add constraint fk_usertarger //tbl_message테이블에 제약조건을 설정 foreign key (targetid) references tbl_user(userid); //무결성이 깨질수 있기 때문에 targetid값에 userid값만 들어갈 수 있도록 설정 (아무id나 들어가면 안되기 때문에) alter table tbl_message add constraint fk_usersender //tbl_message테이블에 제약조건을 설정 foreign key (sender) references tbl_user(userid); //위쪽과 마찬가지로 sender값에 userid값만 들어갈 수 있도록 설정함 --사용자 추가-- insert into tbl_user (userid, upw, uname) values ('user00','user00','kim'); insert into tbl_user (userid, upw, uname) values ('user01','user01','park'); insert into tbl_user (userid, upw, uname) values ('user02','user02','hong'); insert into tbl_user(userid, upw, uname) values ('user03','user03','choi'); insert into tbl_user(userid, upw, uname) values ('user04','user04','lee'); select * from tbl_user; --user02가 user00에게 메시지를 전송 insert into tbl_message (mid, targetid, sender, message) values (message_seq.nextval, 'user00','user02','안녕...'); --user02에게 포인트 10 추가 update tbl_user set upoint=upoint+10 where userid='user02'; select * from tbl_user; --user00의 메시지박스 조회 select * from tbl_message where targetid='user00'; update tbl_message set opendate=sysdate where mid=2; //메시지를 읽으면 읽은 시간을 현재시간으로 바꾼다. select * from tbl_message; update tbl_user set upoint=upoint+5 where userid='user00'; //userid가 user00과 같으면 포인트를 5포인트 증가시킨다. select * from tbl_user; delete from tbl_message; update tbl_user set upoint=0; //tbl_message테이블이 삭제될때 tbl_user테이블의 포인트를 0으로 한다. --메시지 일련번호를 관리할 시퀀스 객체 create sequence message_seq start with 1 --1부터 시작 increment by 1; --1씩 증가 --시퀀스.nextval => 다음번호 select message_seq.nextval from dual; //이 구문을 실행시키게 되면 마치 은행에서 번호표를 뽑듯이 번호가 1씩 계속 증가가된다. //또한 이미 내가 뽑은 번호의 앞번호로 돌아갈수는 없고, 내가 뽑은 번호가 기억이 된다. ====================포인트를 얻게하는 테스트========================================================================
--테스트를 위한 사용자 계정을 추가한다. insert into tbl_user (userid, upw, uname) values ('user00','user00','kim'); //아이디, 비밀번호, 이름 insert into tbl_user (userid, upw, uname) values ('user01','user01','park'); insert into tbl_user (userid, upw, uname) values ('user02','user02','hong'); insert into tbl_user (userid, upw, uname) values ('user03','user03','choi'); insert into tbl_user (userid, upw, uname) values ('user04','user04','lee'); select * from tbl_user; --메시지 보내기 테스트-- --user02가 user00에게 메시지 전송 insert into tbl_message (mid, targetid, sender, message) values (message_seq.nextval,'user00','user02','안녕'); //해석 : user02가 user00에게 '안녕'이라는 메시지를 보낸다. select * from tbl_message; --user02에게 포인트 10 추가 update tbl_user set upoint=upoint+10 where userid='user02'; select * from tbl_user; --user00이 메시지 목록 select * from tbl_message where targetid='user00'; --메시지를 읽으면 열람시간을 저장 update tbl_message set opendate=sysdate where mid=18; //18번 메시지를 읽으면 읽은 열람시간을 현재시간으로 바꾸기 select * from tbl_message; --메시지를 읽으면 읽은 사람한테 5포인트를 추가 update tbl_user set upoint=upoint+5 where userid='user00'; select * from tbl_user;
| cs |
테스트 종료 후 테이블 삭제 및 commit 실시
| delete from tbl_message; //메시지 테이블은 지금 필요 없기 때문에 일단 삭제 commit; //지금까지 한 작업들을 반영하기 위해 commit를 | cs |
Back-End/API 2019. 6. 23. 12:48
스프링 AOP JoinPoint
JoinPoint는 Spring AOP 혹은 AspectJ에서 AOP가 적용되는 지점을 뜻한다.
해당 지점을 JoinPoint 라는 인터페이스로 나타낸다.
AOP 를 수행하는 메소드는 이 JoinPoint 인스턴스를 인자로 받는다.
JoinPoint 인스턴스에서 조인 포인트 지점의 정보를 얻어내야 한다.
|
시그니처(signature)란?
객체가 선언하는 모든 연산은 연산의 이름, 매개변수로 받아들이는 객체들을, 연산의 시그니처라고 합니다.
메소드 종류
메소드 명 |
설명 |
JoinPoint.getThis( )
|
AOP 프록시 객체를 반환한다. |
JoinPoint.getTarget( )
|
AOP가 적용된 대상 객체를 반환한다. 프록시가 벗겨진 상태의 객체이다. |
JoinPoint.getArgs( )
|
JointPoint에 전단된 인자를 배열로 반환한다. ( 인자는 AOP 를 사용하는 메소드의 인자를 말함. ) |
JoinPoint.getKind( )
|
어떤 종류의 JoinPoint인지 문자열로 반환한다. 보통은 메소드 호출이므로 "method-execution" |
JoinPoint.getSignature( )
|
Signature 객체 반환 |
getName( )
|
JointPoint의 이름을 반환한다. 메소드 JoinPoint이면 메소드 이름 |
getDeclaringType( )
|
JointPoint를 선언하고 있는 타입을 반환한다. 즉, JoinPoint가 메소드이면, 해당 메소드의 클래스를 반환한다. |
getDeclaringTypeName( ) |
JoinPoint를 선언하고 있는 타입의 이름을 반환한다.
즉, JoinPoint가 메소드이면, 해당 메소드의 클래스 이름을 반환한다.
Signature를 상속받은 MethodSignature 인터페이스의 구현체인 경우, 조인 포인트로 지정된 메소드를 바로 받을 수 있다.
|
MethodSignature.getMethod( ) |
JoinPoint가 메소드일 경우 해당 JoinPoint에 대한 java.lang.reflect.Method 객체 확보하기
|
출 처 http://egloos.zum.com/hyphen/v/1893002
Back-End/Spring 2019. 6. 22. 21:25
- AOP (Aspect Oriented Programming, 관점(관심) 지향적인 프로그래밍 ) -
1. OOP (Object Oriented Programming, 객체지향프로그래밍) 를 보완하는 확장적인 개념
2. Aspect (측면, 관점, 관심) : 핵심적인 비즈니스 로직은 아니지만 반드시 해야 하는 작업들 ex) 버스, 지하철로 출퇴근을 할 때 교통카드를 찍어야함, 목적지에 정시에 도착하는 것이 중요하고, 교통카드를 찍는 행위가 메인은 아님.
3. 관심의 분리 (Separation of Concerns)를 통해 핵심관점 (업무로직) + 횡단관점(트랜잭션, 로그, 보안, 인증 처리 등) 으로 관심의 분리를 실현
장점 : 중복되는 코드 제거, 효율적인 유지 보수, 높은 생산성, 재활용성 극대화, 변화 수용의 용이성
핵심관심과 횡단관심
ex) 은행업무를 볼때 계좌이체, 입출금, 이자계산 등 중요하고 안할수가 없는 업무를 "핵심관심 (종단관심) " 이라고 한다.
또한 이러한 업무들을 할때 필요한 "로깅" , "보안" , "트랜잭션" 등을 "횡단관심" 이라고 한다.
|
-AOP의 주요 용어-
1. Aspect : 공통 관심사 (로깅, 보안, 트랜잭션 등)
2. Join Points : method를 호출하는 시점, 예외가 발생하는 시점 등과 같이 특정 작업이 실행되는 시점을 의미함 (호출전, 호출후, 호출전후 3가지로 지원해줌)
3. Advice : Join Points에서 실행되어야 하는 코드 (실제로 AOP 기능을 구현한 객체) (횡단관심에 있는 것들을 코딩을 덜 할 수 있도록 지원해 주는것)
4. Pointcuts : 실제로 Advice를 적용시킬 대상 method
5. Proxy : Advice가 적용되었을 때 만들어지는 객체
-Advice의 종류-
1. Before : target method 호출 전에 적용
2. After : target method 호출 후에 적용
3. Around : target method 호출 이전과 이후 모두 적용 (가장 광범위하게 사용됨)
-AOP의 설정 방법-
1. pom.xml에 라이브러리를 추가함
AspectJ의 버전은 기본적으로 properties에 있는 <org.aspectj-version>의 버전을 따라간다. | <properties> <java-version>1.8</java-version> <!-- 2019년 1월 현재 최신 버전 5.1.4, 에러가 날 경우 호환성을 위해 버전을 내려야 함 --> <org.springframework-version>5.1.4.RELEASE</org.springframework-version> <org.aspectj-version>1.9.2</org.aspectj-version> <org.slf4j-version>1.7.25</org.slf4j-version> </properties> <!-- AspectJ --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${org.aspectj-version}</version> </dependency> | cs |
2. servlet-context.xml의 Namespace에서 aop와 tx 추가 체크

추가를 하면 servlet-context.xml의 source쪽에 aop와 tx관련 소스가 추가되어 있다. 즉, xml 파일에서 aop와 tx관련 태그를 사용할 수 있다는 뜻이다.
tx : 트랜잭션 처리 관련된 태그 | <beans:beans xmlns="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" | cs |
3. servlet-context.xml에 aop 태그를 추가 | <!-- aop의 설정을 통한 자동적인 Proxy 객체를 생성 --> <aop:aspectj-autoproxy /> | cs |
4. LogAdvice.java 만들기 로그 수집 작업을 하려면 모든 메소드에 해야한다.
로그 수집은 중요한 업무이지만 핵심적인 업무는 아니고 공통적인 업무에 해당됨
공통적인 업무는 모든 class의 모든 method에 작성하지 말고 Advice에 모아서 작성하고
세부적인 코드에서는 핵심업무에 집중하도록 처리
시그니처(signature)란?
객체가 선언하는 모든 연산은 연산의 이름, 매개변수로 받아들이는 객체들을, 연산의 시그니처라고 합니다.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | package com.example.spring02.aop; import java.util.Arrays; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import com.itextpdf.text.log.LoggerFactory; import ch.qos.logback.classic.Logger; //@Controller @Service @Repository @Component //스프링에서 관리하는 bean, aop는 위의 3개에 속하지 않기때문에 컴포넌트 어노테이션을 사용 @Aspect //스프링에서 관리하는 aop bean, aop를 사용하기 위해서는 2개의 어노테이션이 다 있어야 한다. public class LogAdvice { private static final Logger logger = LoggerFactory.getLogger(LogAdvice.class); //=============================중요================================== //포인트컷 - 실행시점, Around - 실행 전후 //Before, After, Around (어떤 요청이 오기 전에 실행될지, 후에 실행될지, 전후에 실행될지...) //컨트롤러, 서비스, dao의 모든 method 실행 전후에 logPring method가 호출됨 //execution (리턴자료형 class.method(매개변수)) //@Around( Around를 포인트 컷이라 한다. (언제 어느곳에서 실행될지 정함), //지금은 요청전에 실행하는걸로 정함 //"execution(* com.example.spring02.controller..*Controller.*(..))" // .이 2개 있는것은 하위패키지가 다 들어갈 수 있다는 의미, . 1개는 직속 디렉토리 //즉, Controller로 끝나는 모든 메소드들을 말한다. // 밑에있는 코드는 Controller의 모든 메소드를 호출하면 실행되는 공통적인 코드이다. //+ " or execution(* com.example.spring02.service..*Impl.*(..))" // 마찬가지로 밑에있는 코드를 service.Impl에 있는 모든 메소드를 호출하면 실행되는 //공통적인 코드로 실행 시키고 싶을때 사용. //+ " or execution(* com.example.spring02.model..dao.*Impl.*(..))") // 마찬가지로 밑에있는 코드를 dao.Impl에 있는 모든 메소드를 호출하면 실행되는 //공통적인 코드로 실행 시키고 싶을때 사용. //=================================================================== //모든 코드에서 로그를 출력하는 메소드 public Object logPrint(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); //시작할때의 현재시간을 start 변수에 저장 //==========호출전=========================================== Object result = joinPoint.proceed(); // joinPoint.proceed(); 메소드를 호출해서 프록시 패턴을 result변수에 저장. // 즉 공통적인부분 (로그를 출력하는 부분)을 묶에서 result에 저장한다는 뜻. //만약 컨트롤러에서 login.check메소드가 호출한다고 가정했을때 //호출되기 전에 먼저 위쪽에 있는 logPrint메소드가 먼저 실행되고, //login.check메소드가 다 실행되고 난 후 (호출후에는) 밑에 있는 부분이 실행이된다. //그러니까 Object result = joinPoint.proceed(); 를 기준으로 //'호출전' 과 '호출후' 로 나뉘어진다. //==========호출후=========================================== //class name (호출한 클래스의 이름) 컨트롤러, 서비스, DAO인지 if문으로 판별해서 실행 String type = joinPoint.getSignature().getDeclaringTypeName(); // JoinPoint를 선언하고 있는 타입의 이름을 반환해서 그 값을 Signature객체를 반환할때 매개값으로 쓴다. // String name = ""; //null값이면 오류가 발생하기 때문에 초기값을 공백으로 지정 if (type.indexOf("Controller") > -1 ) { name = "Controller \t: "; } else if (type.indexOf("Service") > -1) { name = "ServiceImpl \t: "; } else if (type.indexOf("DAO") > -1) { name = "DAOImpl \t: "; } //method name (메소드를 호출할때 로그가 찍히게 된다) logger.info( name + type + "." + joinPoint.getSignature().getName()+"()"); //매개변수 (마찬가지로 매개변수를 사용할때 로그가 찍히게 된다.) logger.info(Arrays.toString(joinPoint.getArgs())); //핵심로직으로 이동 long end = System.currentTimeMillis(); //끝날때의 현재시간을 end에 저장 long time = end-start; //끝난시간에서 시작한시간을 빼면 실행시간을 계산할 수 있다. logger.info("실행시간:"+time); return result; //결과를 반환한다. } } | cs |
이제 실행을 하면 콘솔창에 각 클래스와 메소드가 실행될때 로그가 뜨게 된다.
Back-End/Spring 2019. 6. 21. 12:18
- JFreeChart 만들기 -
참고 사이트 : http://www.jfree.org/jfreechart/
JFreeChart의 특징
다양한 형태의 차트 지원
Swing, JSP, Servlet 에서 많이 사용되고 있음
pdf 또는 png, jpg 등의 다양한 이미지 파일 형식으로 export 가능
오픈 소스 라이브러리
|
pom.xml에 라이브러리를 추가 | .<!-- https://mvnrepository.com/artifact/org.jfree/jfreechart --> <dependency> <groupId>org.jfree</groupId> <artifactId>jfreechart</artifactId> <version>1.5.0</version> </dependency> | cs |
두가지 형식으로 만들기
JFreeChart (png) : 미리 차트를 만들어서 png파일 (이미지파일) 로 출력시킴 (뷰 => 컨트롤러 => 서비스,서비스구현 => 컨트롤러(바로출력))
JFreeChart (pdf) : 미리 차트를 만들어서 pdf 파일로 만들어주고, 그 파일을 출력함 (뷰 => 컨트롤러 =>서비스,서비스구현 => 컨트롤러 => 뷰) (pdf 만드는 법은 itextpdf를 사용하는 방법을 써서 만듦)
컨트롤러, 서비스 , 서비스구현 클래스 만들어서 사용
admin_menu.jsp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | .<!-- 메뉴 링크를 추가하고 PDF리스트로 갈수있는 링크도 추가 --> <a href="${path}/pdf/list.do">PDF</a> ㅣ <a href="${path}/chart/chart1.do">구글차트(json)</a> ㅣ <a href="${path}/chart/chart2.do">구글차트(db)</a> ㅣ <a href="${path}/jchart/chart2.do">JFreeChart(png)</a> ㅣ <a href="${path}/jchart/chart2.do">JFreeChart(pdf)</a> ㅣ <a href="${path}/email/write.do">이메일 발송</a> ㅣ <a href="${path}/shop/product/list.do">상품목록</a> ㅣ <a href="${path}/shop/product/write.do">상품등록</a> ㅣ | cs |
JFreeChartController.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | .package com.example.spring02.controller.chart; import java.io.FileOutputStream; import javax.inject.Inject; import javax.servlet.http.HttpServletResponse; import org.jfree.chart.ChartUtils; import org.jfree.chart.JFreeChart; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import com.example.spring02.service.chart.JFreeChartService; import com.itextpdf.text.Document; import com.itextpdf.text.Image; import com.itextpdf.text.pdf.PdfWriter; @Controller public class JFreeChartController { @Inject JFreeChartService chartService; //서비스를 호출해야하기 때문에 서비스에 의존성 주입 @RequestMapping("chart1.do")//view에서 맵핑되는 메소드 public void createChart1(HttpServletResponse response){ //화면에 바로 출력을 해야하기 때문에 HttpServletResponse를 사용 try { JFreeChart chart = chartService.createChart(); //서비스에서 생성한 차트를 받아와 변수에 저장, 차트를 얻어온다음에 바로 이미지파일로 보냄 ChartUtils.writeChartAsPNG(response.getOutputStream(), chart, 900, 550); //차트를 받아와서 가로, 세로 길이를 설정해준다. view 필요없이 화면에 곧바로 출력이 된다. } catch (Exception e) { e.printStackTrace(); } } @RequestMapping("chart2.do") //view에서 맵핑되는 메소드, pdf를 만들때 사용되는 메소드 public ModelAndView createChart2(HttpServletResponse response) { String message = ""; try { JFreeChart chart = chartService.createChart(); //서비스로부터 차트를 가져옴 Document document = new Document(); //웹 페이지에 접근할수 있는 객체를 생성 PdfWriter.getInstance(document, new FileOutputStream("d:/test.pdf")); //pdf파일 저장될 경로 //pdf 파일로 변환하기 위한 과정 document.open(); //pdf파일을 연다 Image png = Image.getInstance( ChartUtils.encodeAsPNG(chart.createBufferedImage(500, 500))); //차트의 내용을 500 x 500 이미지로 만들기 document.add(png); //만든 이미지를 pdf 파일에 저장 document.close(); //저장을 했으니 파일을 닫기 message = "pdf 파일이 생성되었습니다."; } catch (Exception e) { e.printStackTrace(); message = "pdf 파일 생성 실패..."; } return new ModelAndView("chart/jchart02","message",message); //보낼 페이지와 보낼 값을 지정해서 리턴한다. //jchart02 페이지에 값을 보내서 메시지를 출력한다. (pdf파일이 생성이 되었는지, 안되었는지..) } } | cs |
JFreeChartService.java | package com.example.spring02.service.chart; import org.jfree.chart.JFreeChart; public interface JFreeChartService { public JFreeChart createChart(); //JFreeChart를 만드는 추상메소드 } | cs |
JFreeChartServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | .package com.example.spring02.service.chart; import java.util.List; import javax.inject.Inject; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.StandardChartTheme; import org.jfree.chart.plot.PlotOrientation; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.general.DefaultPieDataset; import org.springframework.stereotype.Service; import com.example.spring02.model.shop.dao.CartDAO; import com.example.spring02.model.shop.dto.CartDTO; import com.itextpdf.text.Font; import com.sun.prism.paint.Color; @Service public class JFreeChartServiceImpl implements JFreeChartService { @Inject CartDAO cartDao; //DAO를 호출하기 위해 의존성을 주입시킨다. @Override public JFreeChart createChart() { List<CartDTO> list = cartDao.cartMoney(); //Dao에 저장된 장바구니에 담은 금액을 리스트에 저장 //파이 차트가 아닌경우 //파이 차트일때와는 클래스가 틀리다. DefaultCategoryDataset dataset = new DefaultCategoryDataset(); for (CartDTO dto : list) { dataset.setValue(dto.getMoney(), "과일", dto.getProduct_name()); //dataset.setValue(value, rowKey, columnKey); 형식으로 들어간다. //금액, 과일, 상품이름이 들어간다. (총 3개의 값이 들어감) } //파이 차트인 경우 //DefaultPieDataset dataset = new DefaultPieDataset(); //for (CartDTO dto : list) { //dataset.setValue(dto.getProduct_name(), dto.getMoney()); //파이차트에는 x,y축이 없기 때문에 값이 2개만 들어간다. //그러니까 rowKey값이 안들어간다. //} //dataset.setValue(value, rowKey, columnKey); 형식은 이렇게 되지만, 값이 2개만 들어감 JFreeChart chart = null; //차트 객체 선언 String title = "장바구니 통계"; //장바구니 타이틀의 제목 try { // 선그래프 // chart = ChartFactory.createLineChart( // title, "상품명" , "금액" , dataset, // PlotOrientation.VERTICAL, true, true, // false); // 라인 (선) 그래프에서는 이런 값들이 들어간다. // title, categoryAxisLabel, valueAxisLabel, dataset //막대 그래프 chart = ChartFactory.createBarChart( //세로형식의 막대그래프를 만듦 title, "상품명", "금액", dataset, PlotOrientation.VERTICAL, //세로로 차트를 만든다는 의미 true, true, false); //파이 차트 //chart = ChartFactory.createPieChart( //title, //chart title //dataset, //data //true, //include legend //true, //false); //제목, 타이틀의 폰트와 글씨크기를 설정 chart.getTitle().setFont( new Font("돋움",Font.BOLD, 15)); //범례, 범례의 폰트와 글씨크기를 설정 chart.getLegend().setItemFont( new Font("돋움",Font.PLAIN, 10)); Font font = new Font("돋움", Font.PLAIN,12); Color color = new Color(0,0,0); StandardChartTheme chartTheme = (StandardChartTheme) StandardChartTheme.createJFreeTheme(); //테마 설정 chartTheme.setExtraLargeFont(font); //폰트 크기에 따라 테마를 다르게 설정 chartTheme.setLargeFont(font); chartTheme.setRegularFont(font); chartTheme.setSmallFont(font); chartTheme.setAxisLabelPaint(color); //축, 범례등의 색상을 변경 chartTheme.setLegendItemPaint(color); chartTheme.setItemLabelPaint(color); chartTheme.apply(chart); } catch (Exception e) { e.printStackTrace(); } return chart; } } | cs |
jchart02.jsp (컨트롤러에서 넘어온 메시지가 출력되는 View페이지, jcart01타입은 그 컨트롤러에서 바로 출력되기때문에 View가 필요없다.) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | .<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <%@ include file = "../include/header.jsp" %> </head> <body> <%@ include file = "../include/admin_menu.jsp" %> <h2>${message}</h2> <!-- JFreeChartController에서 리턴된 메시지가 출력된다--> <!-- pdf가 만들어 졌는지, 안만들어졌는지.. --> </body> </html> | cs |
Back-End/Spring 2019. 6. 20. 11:56
- 구글에서 제공하는 구글 차트 -
https://google-developers.appspot.com/chart/
차트그리기는 구글서버에 차트에 넣을 값만 넘겨주면 그것을 받아서 뿌려주는 형식으로 되어있다.
구글차트에서는 우리가 서버에다가 요청을 하면 제이슨으로 데이터가 넘어오게 된다.
(즉, json을 만들어서 javascript로 넣어주면 차트를 그려주는 방식)
제이슨 방식 (xml 방식보다 더 간결해서 요즘에 많이 사용)
|
xml 방식 |
{"name" : "김철수" , "email" : "kim@gmail.com", "age" : 21 }
|
<person> <name> 김철수 </name> <email> kim@gmail.com </email> <age> 21 </age> </person>
|
1. pom.xml에 라이브러리를 추가
(추가하는 이유 : 제이슨으로 넘어온 값들을 파싱
(ex-이름은 이거고, 이메일을 저거고.... 등등 따로 코딩하지 않고 자동으로 하게끔 하기 위해서) 하기 위해서)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!-- https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple -->
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
|
cs |
2. 자바스크립트를 이용하여 차트 작성에 필요한 JSON 데이터를 리턴받아 화면에 출력시킴
-구글 차트 실습 예제-
admin_menu.jsp에 각 차트에 대한 하이퍼링크를 추가한다.
admin_menu.jsp 중 일부
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
.<a href="${path}/pdf/list.do">PDF</a>
ㅣ
<a href="${path}/chart/chart1.do">구글차트(json)</a>
ㅣ
<a href="${path}/chart/chart2.do">구글차트(db)</a>
ㅣ
<a href="${path}/jchart/chart2.do">JFreeChart(png)</a>
ㅣ
<a href="${path}/jchart/chart2.do">JFreeChart(pdf)</a>
ㅣ
<a href="${path}/email/write.do">이메일 발송</a>
ㅣ
<a href="${path}/shop/product/list.do">상품목록</a>
ㅣ
<a href="${path}/shop/product/write.do">상품등록</a>
ㅣ
|
cs |
구글차트 (json) : json을 사용해서 차트를 그리는 방법
구글차트 (db) : db를 읽어들여서 제이슨을 생성해서 차트를 그리는 방법
JFreeChart (png) : JFreeChart로는 그림으로 이미지 파일 차트를 추적하는 방법
JFreeChart (pdf) : 차트를 pdf로 출력하는 방법
장바구니 기능을 연동해서 쓸것이기 때문에 그 쪽은 서비스나 모델을 안만들고 사용할 예정 (장바구니 기능과 연동된 부분만)
주로 컨트롤러로 들어가고 뷰 (jsp) 쪽, 서비스 쪽을 만들 예정
컨트롤러 생성
GoogleChartController.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
.package com.example.spring02.controller.chart;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
@RestController //json을 리턴하는 method가 있는 경우
@RequestMapping("/chart/*") //공통적인 맵핑 url
//일반적인 controller어노테이션을 jsp <=> controller을 연동할때 사용하지만
//RestController은 그 데이터 자체를 받아서 제이슨 형식으로 바꿔서 출력하고 싶을때 사용
//(지금은 json 형식으로 차트를 그릴것이기 때문에 Rest를 붙여서 컨트롤러를 선언한 것이다)
public class GoogleChartController {
@RequestMapping("chart1.do") //view에서 맵핑되는 url
public ModelAndView chart1() {
return new ModelAndView("chart/chart01");
//새로운 ModelAndView객체를 만들어서 chart/chart01페이지로 이동
}
}
|
cs |
화면이 이동하는게 아니라 화면이 멈춰져 있는 상태에서 백그라운드에서 데이터가 이동하게 할 예정
그렇기 때문에 정적인 제이슨 파일 (미리만들어진) 을 사용할 예정
json파일은 file로 만들면 된다. (따로 형식이 없음)
sampleData2.json (제이슨 파일)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//구글에서 제공하는 구글 차트를 쓰려면 해야되는 형식
//이 제이슨 데이터를 구글서버에 보내면 서버에서 이걸 해석해서 차트를 그려서 보내준다.
{
"cols": [
{"id":"", "label":"Topping","pattern":"", "type":"string"},
{"id":"", "label":"Slices","pattern":"", "type":"number"}
],
"rows":[
{"c":[{"v":"Mushrooms"},{"v":3}]},
{"c":[{"v":"Onions"},{"v":1}]}
{"c":[{"v":"Olives"},{"v":1}]}
{"c":[{"v":"Zucchini"},{"v":1}]}
{"c":[{"v":"Pepperoni"},{"v":2}]}
]
}
|
cs |
sampleData.json (제이슨 파일)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
{
"rows" : [ //rows는 행이라는 뜻
{
"c" : [
{
"v" : "귤" //컬럼의 이름 (상품명)
},
{
"v" :35000 //컬럼에 해당하는 값 (가격)
}
]
},
{
"c" : [
{
"v" : "딸기"
},
{
"v" :88000
}
]
},
"c" : [
{
"v" : "오렌지"
},
{
"v" :20000
}
]
},
"c" : [
{
"v" : "키위"
},
{
"v" :30000
}
]
},
"c" : [
{
"v" : "포도"
},
{
"v" : 15000
}
]
]
},
"cols" : [ //컬럼 정보
{
"label" : "상품명", //컬럼의 이름
"type" : "string" //컬럼의 자료형
},
{
"label" : "금액",
"type" : "number"
}
]
}
|
cs |
servlet-context.xml에 리소스 매핑을 추가
|
<resources location="/WEB-INF/views/json/" mapping="/json/**" />
|
cs |
chart01.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!--views/chart_exam/chart01.jsp -->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<%@ include file="../include/header.jsp"%>
<!-- 구글 차트 호출을 위한 js 파일 -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script>
//구글 차트 라이브러리 로딩
//google객체는 위쪽 google src안에 들어있음
google.load('visualization','1',{
'packages' : ['corechart']
});
//로딩이 완료되면 drawChart 함수를 호출
google.setOnLoadCallback(drawChart); //라이브러리를 불러오는 작업이 완료되었으면 drawChart작업을 실행하라는 뜻.
function drawChart() {
var jsonData = $.ajax({ //비동기적 방식으로 호출한다는 의미이다.
url : "${path}/json/sampleData.json",
//json에 sampleData.json파일을 불러온다.
//확장자가 json이면 url 맵핑을 꼭 해주어야 한다. 안해주면 자바파일인줄 알고 404에러가 발생한다.
//그렇기 때문에 servlet-context파일에서 리소스를 맵핑해준다.
dataType : "json",
async : false
}).responseText; //제이슨파일을 text파일로 읽어들인다는 뜻
console.log(jsonData);
//데이터테이블 생성
var data
= new google.visualization.DataTable(jsonData);
//제이슨 형식을 구글의 테이블 형식으로 바꿔주기 위해서 집어넣음
//차트를 출력할 div
//LineChart, ColumnChart, PieChart에 따라서 차트의 형식이 바뀐다.
var chart = new google.visualization.PieChart(
document.getElementByld('chart_div')); //원형 그래프
//var chart
// = new google.visualization.LineChart(
//document.getElementById('chart_div')); 선 그래프
//var chart
// = new google.visualization.ColumnChart(document.getElementById('chart_div'));
//차트 객체.draw(데이터 테이블, 옵션) //막대그래프
//cuveType : "function" => 곡선처리
//데이터를 가지고 (타이틀, 높이, 너비) 차트를 그린다.
chart.draw(data, {
title : "차트 예제",
//curveType : "function", //curveType는 차트의 모양이 곡선으로 바뀐다는 뜻
width : 600,
height : 400
});
}
</script>
</head>
<body>
<%@ include file="../include/admin_menu.jsp"%>>
<!-- 차트 출력 영역 -->
<div id="chart_div"></div>
<!-- 차트가 그려지는 영역 -->
<!-- 차트 새로고침 버튼 -->
<button id="btn" type="button" onclick="drawChart()">refresh</button>
</body>
</html>
|
cs |
======여기까지는 정적인 json (그러니까 json의 주소로 직접이동해서 사용하는 방식=================================
json을 만드는 방법 (실습)
데이터베이스를 읽어서 구글에서 요구하는 json 형식으로 데이터를 만들면 된다.
GoogleChartService.java
|
package com.example.spring02.service.chart;
import org.json.simple.JSONObject;
//json오브젝트는 pom.xml에 추가한 라이브러리 안에 들어있는 것들이다
public interface GoogleChartService {
public JSONObject getChartData(); //json 타입으로 리턴
}
|
cs |
GoogleChartServiceImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
package com.example.spring02.service.chart;
import javax.inject.Inject;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.springframework.stereotype.Service;
import com.example.spring02.model.shop.dto.CartDTO;
import com.example.spring02.service.shop.CartService;
import com.itextpdf.text.List;
@Service
public class GoogleCharServiceImpl
implements GoogleChartService {
@Inject
CartService cartService;
//장바구니 서비스에 있는 값들을 가져오기 위해서 의존성을 주입
//{"변수명" : [{},{},{}], "변수명" : "값"}
@Override
public JSONObject getChartData() {//제이슨 오브젝트를 리턴하는 것
// getChartData메소드를 호출하면
//db에서 리스트 받아오고, 받아온걸로 json형식으로 만들어서 리턴을 해주게 된다.
List<CartDTO> items = cartService.cartMoney();
//리턴할 json 객체
JSONObject data = new JSONObject(); //{}
//json의 칼럼 객체
JSONObject col1 = new JSONObject();
JSONObject col2 = new JSONObject();
//json 배열 객체, 배열에 저장할때는 JSONArray()를 사용
JSONArray title = new JSONArray();
col1.put("label","상품명"); //col1에 자료를 저장 ("필드이름","자료형")
col1.put("type", "string");
col2.put("label", "금액");
col2.put("type", "number");
//테이블행에 컬럼 추가
title.add(col1);
title.add(col2);
//json 객체에 타이틀행 추가
data.put("cols", title);//제이슨을 넘김
//이런형식으로 추가가된다. {"cols" : [{"label" : "상품명","type":"string"}
//,{"label" : "금액", "type" : "number"}]}
JSONArray body = new JSONArray(); //json 배열을 사용하기 위해 객체를 생성
for (CartDTO dto : items) { //items에 저장된 값을 dto로 반복문을 돌려서 하나씩 저장한다.
JSONObject name = new JSONObject(); //json오브젝트 객체를 생성
name.put("v", dto.getProduct_name()); //name변수에 dto에 저장된 상품의 이름을 v라고 저장한다.
JSONObject money = new JSONObject(); //json오브젝트 객체를 생성
money.put("v", dto.getMoney()); //name변수에 dto에 저장된 금액을 v라고 저장한다.
JSONArray row = new JSONArray(); //json 배열 객체 생성 (위에서 저장한 변수를 칼럼에 저장하기위해)
row.add(name); //name을 row에 저장 (테이블의 행)
row.add(money); //name을 row에 저장 (테이블의 행)
JSONObject cell = new JSONObject();
cell.put("c", row); //cell 2개를 합쳐서 "c"라는 이름으로 추가
body.add(cell); //레코드 1개 추가
}
data.put("rows", body); //data에 body를 저장하고 이름을 rows라고 한다.
return data; //이 데이터가 넘어가면 json형식으로 넘어가게되서 json이 만들어지게 된다.
}
}
|
cs |
CartDAOImpl.java
|
@Override
public List<CartDTO> cartMoney() {
return sqlSession.selectList("cart.cart_money");
}
|
cs |
cartMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<mapper namespace="cart">
<select id = "cart_money"
resultType = "com.example.spring02.model.shop.dto.CartDTO">
select product_name, sum(price * amount) money
from cart c, product p
where c.product_id=p.product_id
group by product_name
order by product_name
<!-- 상품테이블과 장바구니 테이블로부터 상품의 이름과 전체 금액을 검색 -->
<!-- 조건 : 장바구니 테이블의 상품 id와 상품 테이블의 상품 id가 같은 것만 (즉, 내가 장바구니에 담은 상품의 id만) -->
<!-- 그리고 검색한 것들을 product_name란 속성을 만들어서 내림차순으로 정렬시킴 -->
</select>
|
cs |
===========================================여기까지 json파일 생성 과정=============================
아까는 json의 주소를 직접 적었지만
이제는 컨트롤러에 가서 json을 동적으로 생성하게 만들어서 그것을 보내주고 있는것.
GoogleCharController.java
컨트롤러를 선언할때 controller 어노테이션을 사용하면 ResponseBody어노테이션을 붙여야 하고,
컨틀롤러를 선언할때 RestController 어노테이션을 붙이면 ResponseBody어노테이션을 안붙여도 된다.
|
@RequestMapping("chart2.do")
public ModelAndView chart2() {
return new ModelAndView("chart/chart02"); //json데이터를 호출한 곳으로 되돌려준다.
}
//@ResponseBody //화면으로 넘어가는 것이 아닌 데이터를 리턴하는 경우 사용
@RequestMapping("cart_money_list.do")
public JSONObject cart_money_list() {
return GoogleChartService.getChartData();
}
|
cs |
chart02.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
.<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!--views/chart_exam/chart02.jsp -->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<%@ include file="../include/header.jsp"%>
<!-- 구글 차트 호출을 위한 js 파일 -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script>
//구글 차트 라이브러리 로딩
//google객체는 위쪽 google src안에 들어있음
google.load('visualization','1',{
'packages' : ['corechart']
});
//로딩이 완료되면 drawChart 함수를 호출
google.setOnLoadCallback(drawChart); //라이브러리를 불러오는 작업이 완료되었으면 drawChart작업을 실행하라는 뜻.
function drawChart() {
var jsonData = $.ajax({ //비동기적 방식으로 호출한다는 의미이다.
url : "${path}/chart/cart_money_list.do",
//chart01에서는 json의 주소를 직접 적었지만 이 페이지에서는 컨트롤러로 이동해 맵핑해서 제이슨을 동적으로
//직접만들어 그 만든 json을 직접 보낸다.
//chart01에서 쓰던 방식 url : "${path}/json/sampleData.json",
//json에 sampleData.json파일을 불러온다.
//확장자가 json이면 url 맵핑을 꼭 해주어야 한다. 안해주면 자바파일인줄 알고 404에러가 발생한다.
//그렇기 때문에 servlet-context파일에서 리소스를 맵핑해준다.
dataType : "json",
async : false
}).responseText; //제이슨파일을 text파일로 읽어들인다는 뜻
console.log(jsonData);
//데이터테이블 생성
var data
= new google.visualization.DataTable(jsonData);
//제이슨 형식을 구글의 테이블 형식으로 바꿔주기 위해서 집어넣음
//차트를 출력할 div
//LineChart, ColumnChart, PieChart에 따라서 차트의 형식이 바뀐다.
//var chart = new google.visualization.PieChart(
//document.getElementByld('chart_div')); //원형 그래프
var chart
= new google.visualization.LineChart(
document.getElementById('chart_div')); //선 그래프
//var chart
// = new google.visualization.ColumnChart(document.getElementById('chart_div'));
//차트 객체.draw(데이터 테이블, 옵션) //막대그래프
//cuveType : "function" => 곡선처리
//데이터를 가지고 (타이틀, 높이, 너비) 차트를 그린다.
chart.draw(data, {
title : "장바구니 통계",
curveType : "function", //curveType는 차트의 모양이 곡선으로 바뀐다는 뜻
width : 600,
height : 400
});
}
</script>
</head>
<body>
<%@ include file="../include/admin_menu.jsp"%>>
<!-- 차트 출력 영역 -->
<div id="chart_div"></div>
<!-- 차트가 그려지는 영역 -->
<!-- 차트 새로고침 버튼 -->
<button id="btn" type="button" onclick="drawChart()">refresh</button>
</body>
</html>
|
cs |
아래 책은 제가 공부할때 활용했던 책으로 추천드리는 책이니 한번씩 읽어보시는것을 추천드립니다!! ㅎㅎ

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
Back-End/Spring 2019. 6. 19. 23:21
스프링을 사용해서 pdf파일 생성해보기
- itextpdf 라이브러리 -
- 참고할만한 사이트들 -
- http://itextpdf.com (pdf관련 예제 수록)
- API : http://developers.itextpdf.com/examples-itext5 (pdf관련 API)
|
사전 설정
pom.xml에 pdf에 한글처리를 위해 폰트 정보를 메이븐 저장소 홈페이지에서 복사해온다.
기본 구조
뷰 => 컨트롤러 => Service => dto => Service => 컨트롤러 => 뷰
1. admin_menu.jsp 에서 pdf링크를 만들어 list.do ( url )로 컨트롤러와 맵핑
2. pdf파일을 만들기 위해 컨트롤러에서 서비스, 서비스에서 서비스 구현 클래스를 호출
3. PdfServiceImpl.java에서 pdf를 만들기 위해 createpdf메소드를 실행
ㄱ. pdf 문서를 처리하는 객체인 Document 생성
ㄴ. pdf 문서의 저장경로 설정, 한글폰트 처리 및 폰트의 사이즈를 따로 지정 해준다.
ㄷ. pdf 문서에 나타날 셀을 설정하고 테이블에 집어넣는다.
ㄹ. 타이틀을 지정하고, 가운데 정렬하고, 줄바꿈을 해준다. (타이틀이 테이블보다 위쪽에 있기 때문에)
ㅁ. 위에서 만든 셀에 "상품명", "수량", "단가", "금액" 값을 정렬방식과 폰트를 지정해서 넣는다.
ㅂ. 그리고 테이블에 위에서 생성시킨 셀을 넣는다.
ㅅ. 서비스에 저장되어있던 장바구니리스트에 id값을 매개값으로 리스트에 저장
ㅇ. 리스트에 저장한 값들을 반복문을 사용해서 하나씩 출력해서 dto에 저장한다. ㄱ. pdf 문서를 처리하는 객체인 Document 생성
ㄴ. pdf 문서의 저장경로 설정, 한글폰트 처리 및 폰트의 사이즈를 따로 지정 해준다.
ㄷ. pdf 문서에 나타날 셀을 설정하고 테이블에 집어넣는다.
ㄹ. 타이틀을 지정하고, 가운데 정렬하고, 줄바꿈을 해준다. (타이틀이 테이블보다 위쪽에 있기 때문에)
ㅁ. 위에서 만든 셀에 "상품명", "수량", "단가", "금액" 값을 정렬방식과 폰트를 지정해서 넣는다.
ㅂ. 그리고 테이블에 위에서 생성시킨 셀을 넣는다.
ㅅ. 서비스에 저장되어있던 장바구니리스트에 id값을 매개값으로 리스트에 저장
ㅇ. 리스트에 저장한 값들을 반복문을 사용해서 하나씩 출력해서 dto에 저장한 ㅈ. dto에 저장한 값들을 pdfCell 객체를 생성해서 cell안에 넣어준다.
ㅊ. Cell에 저장한 데이터들을 table 안에 저장한다. ㅋ. document 객체에 table를 저장하고, 저장이 끝났으면 document객체를 닫는다.
4. createpdf 메소드를 실행한 후 컨트롤러로 돌아옴
5. 컨트롤러에서 결과값을 result.jsp에 전달해서 pdf파일이 생성되었는지, 안되었는지 확인하고 결과에 대한 메시지가 result.jsp 파일에 출력이 되도록 한다.
|
1. 한글 처리를 위해서는 폰트 정보가 필요함
pom.xml에 라이브러리를 추가. (메이븐 저장소홈페이지에서 복사해온다.) (https://mvnrepository.com/search?q=itextpdf&p=1) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13</version> </dependency> <!-- https://mvnrepository.com/artifact/com.itextpdf/itext-pdfa --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-pdfa</artifactId> <version>5.5.13</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/com.itextpdf/itext-xtra --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-xtra</artifactId> <version>5.5.13</version> </dependency> <!-- https://mvnrepository.com/artifact/com.itextpdf.tool/xmlworker --> <dependency> <groupId>com.itextpdf.tool</groupId> <artifactId>xmlworker</artifactId> <version>5.5.13</version> </dependency> <!-- https://mvnrepository.com/artifact/com.itextpdf/font-asian --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>font-asian</artifactId> <version>7.1.6</version> <scope>test</scope> </dependency> | cs |
2. pdf 기능을 테스트하기 위해서 관리자 메뉴 페이지에 pdf 링크 추가
admin_menu.jsp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!-- core태그를 사용하기 위해 taglib를 사용 --> <!-- 관리자로그인한 상태에서만 보이는 메뉴 --> <!-- 메뉴 링크를 추가하고 PDF리스트로 갈수있는 링크도 추가 --> <a href="${path}/shop/product/list.do">상품목록</a> ㅣ <a href="${path}/shop/product/write.do">상품등록</a> ㅣ <a href="${path}/pdf/list.do">PDF</a> ㅣ <c:choose> <c:when test="${sessionScope.admin_userid == null }"> <a href="${path }/admin/login.do">관리자 로그인</a> <!-- 세션에 관리자 아이디의 값이 NULL일때 (즉 로그인 되어있지 않은 상태일때) --> <!-- 관리자 로그인 링크를 표시 --> </c:when> <c:otherwise> ${sessionScope.admin_name}님이 로그인중입니다. <a href="${path}/admin/logout.do">로그아웃</a> <!-- 관리자가 로그인한 상태일때는 로그아웃 링크를 표시 --> </c:otherwise> </c:choose> <hr> | cs |
3. PdfController.java 로 맵핑된다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package com.example.spring02.controller.pdf; import javax.inject.Inject; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import com.example.spring02.service.pdf.PdfService; @Controller //컨트롤러 표시 어노테이션 @RequestMapping("/pdf/*")//공통주소를 맵핑 public class PdfController { @Inject PdfService pdfService; //서비스 객체를 사용하기 위해 의존성을 주입 @RequestMapping("list.do") //View에서 맵핑 url 주소 public ModelAndView list() throws Exception { String result = pdfService.createPdf(); //createPdf()메소드에서 pdf파일이 생성되었는지 결과가 result에 담긴다. return new ModelAndView("pdf/result","message",result); //그 결과가 message로 pdf/result페이지로 전송된다. } } | cs |
PdfService.java | package com.example.spring02.service.pdf; public interface PdfService { public String createPdf(); //pdf를 만드는 추상 메소드 } | cs |
PdfServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | package com.example.spring02.service.pdf; import java.io.FileOutputStream; import javax.inject.Inject; import org.springframework.stereotype.Service; import com.example.spring02.model.shop.dto.CartDTO; import com.example.spring02.service.shop.CartService; import com.itextpdf.text.Chunk; import com.itextpdf.text.Document; import com.itextpdf.text.Element; import com.itextpdf.text.Font; import com.itextpdf.text.List; import com.itextpdf.text.Paragraph; import com.itextpdf.text.Phrase; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.PdfPCell; import com.itextpdf.text.pdf.PdfPTable; import com.itextpdf.text.pdf.PdfWriter; @Service //서비스에서는 이 어노테이션을 사용해야한다. public class PdfServiceImpl implements PdfService { @Inject CartService cartService; // 장바구니에 있는 내용들을 pdf파일로 만들기 위해 장바구니 서비스객체를 사용하기 위해서 의존성을 주입시킨다. @Override public String createPdf() { String result = ""; // 초기값이 null이 들어가면 오류가 발생될수 있기 때문에 공백을 지정 try { Document document = new Document(); // pdf문서를 처리하는 객체 PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("d:/sample.pdf")); // pdf파일의 저장경로를 d드라이브의 sample.pdf로 한다는 뜻 document.open(); // 웹페이지에 접근하는 객체를 연다 BaseFont baseFont = BaseFont.createFont("c:/windows/fonts/malgun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); // pdf가 기본적으로 한글처리가 안되기 때문에 한글폰트 처리를 따로 해주어야 한다. // createFont메소드에 사용할 폰트의 경로 (malgun.ttf)파일의 경로를 지정해준다. // 만약에 이 경로에 없을 경우엔 java파일로 만들어서 집어넣어야 한다. Font font = new Font(baseFont, 12); // 폰트의 사이즈를 12픽셀로 한다. PdfPTable table = new PdfPTable(4); // 4개의 셀을 가진 테이블 객체를 생성 (pdf파일에 나타날 테이블) Chunk chunk = new Chunk("장바구니", font); // 타이틀 객체를 생성 (타이틀의 이름을 장바구니로 하고 위에 있는 font를 사용) Paragraph ph = new Paragraph(chunk); ph.setAlignment(Element.ALIGN_CENTER); document.add(ph); // 문단을 만들어서 가운데 정렬 (타이틀의 이름을 가운데 정렬한다는 뜻) document.add(Chunk.NEWLINE); document.add(Chunk.NEWLINE); // 줄바꿈 (왜냐하면 타이틀에서 두줄을 내린후에 셀(테이블)이 나오기 때문) PdfPCell cell1 = new PdfPCell(new Phrase("상품명", font)); // 셀의 이름과 폰트를 지정해서 셀을 생성한다. cell1.setHorizontalAlignment(Element.ALIGN_CENTER); // 셀의 정렬방식을 지정한다. (가운데정렬) PdfPCell cell2 = new PdfPCell(new Phrase("단가", font)); cell2.setHorizontalAlignment(Element.ALIGN_CENTER); PdfPCell cell3 = new PdfPCell(new Phrase("수량", font)); cell3.setHorizontalAlignment(Element.ALIGN_CENTER); PdfPCell cell4 = new PdfPCell(new Phrase("금액", font)); cell4.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell1); // 그리고 테이블에 위에서 생성시킨 셀을 넣는다. table.addCell(cell2); table.addCell(cell3); table.addCell(cell4); List<CartDTO> items = cartService.listCart("park"); // 서비스로부터 id값을 매개값으로 주어서 장바구니목록을 가져온다. for (int i = 0; i < items.size(); i++) { CartDTO dto = items.get(i); // 레코드에 값들을 꺼내서 dto에 저장 PdfPCell cellProductName = new PdfPCell(new Phrase(dto.getProduct_name(), font)); // 반복문을 사용해서 상품정보를 하나씩 // 출력해서 셀에 넣고 테이블에 // 저장한다. PdfPCell cellPrice = new PdfPCell(new Phrase("" + dto.getPrice(), font)); // Phrase타입은 숫자형(int형 같은타입)으로 하면 에러가 발생되기 때문에 dto앞에 공백("")주어서 String타입으로 변경한다. PdfPCell cellAmount = new PdfPCell(new Phrase("" + dto.getAmount(), font)); // Phrase타입은 숫자형(int형 같은타입)으로 하면 에러가 발생되기 때문에 dto앞에 공백("")주어서 String타입으로 변경한다. PdfPCell cellMoney = new PdfPCell(new Phrase("" + dto.getMoney(), font)); // Phrase타입은 숫자형(int형 같은타입)으로 하면 에러가 발생되기 때문에 dto앞에 공백("")주어서 String타입으로 변경한다. table.addCell(cellProductName); // 셀의 데이터를 테이블에 저장한다. (장바구니안에 들어있는 갯수만큼 테이블이 만들어진다) table.addCell(cellPrice); table.addCell(cellAmount); table.addCell(cellMoney); } document.add(table); // 웹접근 객체에 table를 저장한다. document.close(); // 저장이 끝났으면 document객체를 닫는다. result = "pdf 파일이 생성되었습니다."; } catch (Exception e) { e.printStackTrace(); result = "pdf 파일 생성 실패..."; } return result; } } | cs |
result.jsp | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <!-- 컨트롤러에서 pdf파일이 생성되었는지 알려주는 페이지 (메시지로 알려준다) --> <title>Insert title here</title> <%@ include file = "../include/header.jsp" %> </head> <body> <%@ include file = "../include/admin_menu.jsp" %> <h2>${message}</h2> </body> </html> | cs |
Back-End/Spring 2019. 6. 18. 18:18
- 관리자용 상품등록 / 수정 / 삭제 기능 구현 - (관리자로 로그인한 상태에서만 가능)
-상품등록-
admin_menu.jsp | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!-- jstl을 사용하기 위한 c태그 --> <a href="${path}/shop/product/list.do">상품목록</a> | <!-- 상품목록에 들어가는 링크를 만듦 --> <a href="${path}/shop/product/write.do">상품등록</a> | <!-- 상품등록에 들어가는 링크를 만듦 -->
<c:choose> <c:when test="${sessionScope.admin_userid == null }"> <a href="${path}/admin/login.do">관리자 로그인</a> <!-- 세션에 들어있는 관리자 id가 null값이면 로그인 페이지로 이동함 --> </c:when>
<c:otherwise> ${sessionScope.admin_name}님이 로그인중입니다. <a href="${path}/admin/logout.do">로그아웃</a> <!-- 로그인 중이면 로그아웃 페이지를 출력함) --> </c:otherwise>
</c:choose> <hr> | cs |
product_write.jsp (상품 등록) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <%@ include file="../include/header.jsp"%> <script> //상품을 추가하기위한 정보를 담아 insert.do로 보내는 자바스크립트 함수 function product_write() { var product_name = document.form1.product_name.value; // document는 웹페이지에 접근하기위한 객체.. form1에 있는 상품이름을 반환해서 name에 저장함 var price = document.form1.price.value; // document는 웹페이지에 접근하기위한 객체.. form1에 있는 상품의 값을 반환해서 price에 저장함 var description = document.form1.description.value; // document는 웹페이지에 접근하기위한 객체.. form1에 있는 상품의 정보를 반환해서 description에 저장함 //document.form.은 폼페이지에 있는 값을 반환한다는 뜻. if (product_name == "") { //상품 이름이 입력되어 있지 않으면 alert("상품명을 입력하세요"); document.form1.product_name.focus(); //form1페이지에 있는 "상품명을 입력하세요" 에 커서를 올려둔다. return; } if (price == "") { //상품가격이 입력되어 있지 않으면 alert("가격을 입력하세요"); document.form1.price.focus(); //form1페이지에 있는 "가격을 입력하세요" 에 커서를 올려둔다. return; } if (description == "") { //상품설명이 입력되어 있지 않으면 alert("상품설명을 입력하세요"); document.form1.description.focus(); //form1페이지에 있는 "상품설명을 입력하세요" 에 커서를 올려둔다. return; } // input 태그를 마우스로 클릭하여 입력상태로 만든것을 포커스를 얻었다고 한다. // 그리고 입력상태를 떠난 것을 포커스가 벗어났다고 한다.
document.form1.action = "${path}/shop/product/insert.do"; //insert.do 페이지로 form1에 저장된 자료를 전송함 document.form1.submit(); } </script> </head> <body> <%@ include file="../include/admin_menu.jsp"%> <!-- 관리자용 메뉴는 일반 회원의 메뉴와 다르기 때문에 일부러 관리자용 메뉴를 만들고 그 메뉴를 출력한다. --> <h2>상품 등록</h2> <form id="form1" name="form1" method="post" enctype="multipart/form-data"> <!-- 파일업로드를 위해 추가하는 타입 --> <table> <tr> <td>상품명</td> <td><input name="product_name"></td> </tr> <tr> <td>가격</td> <td><input name="price"></td> </tr> <tr> <td>상품설명</td> <td><textarea rows="5" cols="60" name="description" id="description"></textarea></td> </tr> <tr> <td>상품이미지</td> <td><input type="file" name="file1"></td> </tr> <tr> <td colspan="2" align="center"><input type="button" value="등록" onclick="product_write()"> <!-- "등록" 버튼을 누르면 위쪽에 있는 스크립트문에서 product_write()함수가 호출되서 실행되 insert.do페이지로 자료를 전송한다. --> <input type="button" value="목록" onclick="location.href='${path}/admin/product/list.do'"> <!-- "목록 버튼을 누르면 list.do페이지로 이동" --> </td> </tr> </table> </form> </body> </html> | cs |
ProductController.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | //상품을 추가하는 메소드 @RequestMapping("write.do") public String write() { return "shop/product_write"; }
==================================================================================================================== //관리자 페이지에서 맵핑된 메소드 //첨부파일을 추가하는 메소드 (그러니까 과일의 사진을 추가) @RequestMapping("insert.do") public String insert(ProductDTO dto) { String filename="-"; if(!dto.getFile1().isEmpty()) { //첨부파일이 존재하면 (isEmpty()는 빈공간이라는 뜻인데 !가 붙었으므로 첨부파일이 존재하면..이라는 뜻) filename=dto.getFile1().getOriginalFilename(); //dto에 저장된 서버상에 업로드된 파일명을 반환해서 filename변수에 저장한다. String path="D:\\work\\.metadata\\.plugins\\org.eclipse.wst.server.core\\tmp1\\wtpwebapps\\spring02\\WEB-INF\\views\\images\\"; //이미지 파일이 저장된 경로를 지정 //개발디렉토리가 아니라 곧바로 배포디렉토리로 설정하였다.
//개발디렉토리에서 앞부분을 자르고(d:\work\spring02 까지), 그 부분에 배포디렉토리를 붙여넣으면 된다. //이렇게 하지 않으면 이미지를 추가했을때 개발 디렉토리와 배포 디렉토리가 다르기 때문에 일일히 새로고침을 해주어야 한다. 이클립스에서는 개발을 할때는 워크스페이스 (개발디렉토리) 에서 개발한 코드를 톰캣에서 카피해서 배포 디렉토리에 배포를 한다. (카피를 할때 새로고침을 해야한다)
개발 디렉토리
d:\work\spring02
배포 디렉토리
D:\work\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebappsdd\spring02\ //파일업로드시에는 예외처리를 해주어야 한다. try { new File(path).mkdir(); //새로운 파일의 경로를 생성하고 해당 경로에 폴더를 만든다. dto.getFile1().transferTo(new File(path+filename)); //생성한 디렉터리에 파일을 저장한다. (dto에 저장한다는 뜻) } catch (Exception e) { e.printStackTrace(); } } dto.setPicture_url(filename); //첨부파일을 dto에 저장 productService.insertProduct(dto); //서비스에 dto에 있는 과일 이미지 사진을 저장함 return "redirect:/shop/product/list.do"; //파일이 첨부되면 list페이지로 되돌아간다. }
| cs |
ProductServiceImpl.java 중 일부 | @Override public void insertProduct(ProductDTO dto) { productDao.insertProduct(dto); } | cs |
ProductDAOImpl.java 중 일부 | @Override public void insertProduct(ProductDTO dto) { sqlSession.insert("product.insert", dto); } | cs |
productMapper.java 중 일부 | <insert id="insert"> insert into product values (seq_product.nextval, #{product_name}, #{price}, #{description}, #{picture_url} ) //seq_product.nextval은 상품코드를 시퀀스를 이용해서 발급하는 것. //그러니까 숫자가 하나씩 올라간다는 뜻. </insert> | cs |
-상품수정-
상품 수정을 할때는 url에 상품id(숫자)를 넘겨서 @PathVariable로 받을 예정
product_list.jsp 중 일부 (편집버튼 추가) | <c:if test = "${sessionScope.admin_userid != null}"> <br> <a href="${path }/shop/product/edit/${row.product_id}">[편집]</a> </c:if> | cs |
ProductController.java | //상품정보 편집 페이지로 이동하고 데이터를 보내주도록하는 메소드 //상품의 아이디를 맵핑해서.. @RequestMapping("edit/{product_id}") public ModelAndView edit(@PathVariable("product_id") int product_id, ModelAndView mav) { //다른페이지에서 pathvariable로 받은 product_id를 product_id에 저장하고, //데이터를 보내고, 페이지를 이동하기위해서 ModelAndView 타입을 사용한다. mav.setViewName("/shop/product_edit"); //이동할 페이지의 이름 //전달할 데이터를 저장 mav.addObject("dto", productService.detailProduct(product_id)); //서비스로부터 상품 id(번호)를 받아와서 mav에 저장 return mav; //페이지 이동 } | cs |
ProductService.java 중 일부 | @Override public ProductDTO detailProduct(int product_id) { return productDao.detailProduct(product_id); // } | cs |
ProductDAOImpl.java 중 일부 | @Override public ProductDTO detailProduct(int product_id) { return sqlSession.selectOne( "product.detail_product", product_id); } | cs |
product_edit.jsp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <%@ include file="../include/header.jsp"%> <script> //상품을 갱신하는 함수 //컨트롤러의 update.do로 맵핑된다. function product_update() { document.form1.action = "${path}/shop/product/update.do"; document.form1.submit(); } //상품을 삭제하는 함수 //컨트롤러의 delete.do로 맵핑된다. function product_delete() { if (confirm("삭제하시겠습니까?")) { document.form1.action = "${path}/shop/product/delete.do"; document.form1.submit(); } } </script> </head> <body> <%@ include file="../include/admin_menu.jsp"%> <h2>상품 정보 편집</h2> <form id="form1" name="form1" method="post" enctype="multipart/form-data"> <!-- 파일 업로드를 하기위한 타입, 필수로 작성해야 한다.--> <table> <!-- 관리자로그인을 한 후에 들어갈 수 있는 상품정보 편집정보 --> <!-- 해당되는 자료들은 dto에서 가져와서 보여준다. --> <tr> <td>상품명</td> <td><input name="product_name" value="${dto.product_name}"></td> </tr> <tr> <td>가격</td> <td><input name="price" type="number" value="${dto.price }"></td> </tr> <tr> <td>상품설명</td> <td><textarea rows="5" cols="60" name="description" id="description">${dto.description }</textarea></td> </tr> </table> </form> </body> </html> | cs |
ProductController.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
//상품의 정보를 수정 (갱신) 할때 View에서 맵핑되는 메소드 @RequestMapping("update.do") public String update(ProductDTO dto) { String filename = "-"; if(!dto.getFile1().isEmpty()) { //첨부파일이 존재하면 filename = dto.getFile1().getOriginalFilename();//dto에 저장된 서버상에 업로드된 파일명을 반환해서 filename변수에 저장한다. String path="D:\\work\\.metadata\\.plugins\\org.eclipse.wst.server.core\\tmp1\\wtpwebapps\\spring02\\WEB-INF\\views\\images\\"; //이미지 파일이 저장된 경로를 지정 try { new File(path).mkdir(); //새로운 파일의 경로를 생성하고 해당 경로에 폴더를 만든다. dto.getFile1().transferTo(new File(path+filename)); //지정된 디렉토리로 카피하는 명령어 } catch (Exception e) { //생성한 디렉터리에 파일을 저장한다. (dto에 저장한다는 뜻) e.printStackTrace(); } dto.setPicture_url(filename); //이미지의 경로 }else { //새로운 첨부파일이 올라가지않고 기존에 있는 정보를 그대로 써야하는 경우 //기존의 정보를 가져와서 dto2변수에 넣고, dto ProductDTO dto2= productService.detailProduct(dto.getProduct_id()); dto.setPicture_url(dto2.getPicture_url()); } productService.updateProduct(dto); return "redirect:/shop/product/list.do"; } | cs |
ProductServiceImpl.java 중 일부 | @Override public void updateProduct(ProductDTO dto) { productDao.updateProduct(dto); } | cs |
ProductDAO.Impl.java 중 일부 | @Override public void updateProduct(ProductDTO dto) { sqlSession.update("Product.update_product",dto); } | cs |
productMapper.xml 중 일부 | <mapper namespace="product"> <update id = "update_product"> update product set product_name = #{product_name}, price=#{price}, description=#{description}, picture_url=#{picture_url} where product_id=#{product_id} <!-- 상품 id와 맞는 상품의 가격, 설명, 사진을 갱신하는 쿼리문 --> </update> | cs |
-상품삭제-
상품을 삭제할때는 안에 상품안에 저장되어 있는 이미지파일도 같이 삭제하여야함.
product_edit.jsp에서 컨트롤러로 맵핑됨
ProductController.java 중 일부 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //상품 정보를 삭제할때 뷰 (product_edit.jsp)페이지와 매핑되는 메소드 //상품 정보를 삭제할때는 그 정보 안에 담긴 이미지도 같이 삭제 해야 한다. @RequestMapping("delete.do") public String delete(@RequestParam int product_id) { //RequestParam 어노테이션을 사용해서 상품의 id를 받아온다. String filename=productService.fileInfo(product_id); //fileaname변수에 파일을 삭제하기위해 서비스에서 상품의 id를 매개변수로 삼아 서비스에 저장한후 filename 변수에 저장 if(filename != null && !filename.equals("-")) { //filename이 null값이 아니거나, filename안에 들어있는것이 아닌 것이 "-"와 같으면, (그러니까 filename에 값이 담겨있을 경우!!!!!) String path="D:\\work\\.metadata\\.plugins\\org.eclipse.wst.server.core\\tmp1\\wtpwebapps\\spring02\\WEB-INF\\views\\images\\"; //상품에 담겨있는 이미지도 같이 삭제해야하기 때문에 이미지 경로를 지정 File f = new File(path+filename); //이미지 파일을 삭제하기 위해 (파일의 객체 (경로와 파일이 담겨있음)을 생성하고 변수 f에 저장) if(f.exists()) { //exists()메소드는 유효성을 검사하는 메소드, 그러니까 값이 존재하는지 안하는지 판단하는 메소드이다. //f안에 이미지 파일과 경로가 존재한다면, delete()메소드로 변수 f안에 있는 이미지파일과 경로를 제거 (즉 파일을 제거) f.delete(); } } productService.deleteProduct(product_id); //이미지를 삭제한 후에 나머지 (가격 등등) 자료들도 삭제하기 위해 서비스에 id를 매개변수로 줘서 저장(삭제함) return "redirect:/shop/product/list.do"; //최신화 한 후에 list.jsp로 redirect한다. } | cs |
먼저 저장되어 있는 이미지를 삭제하기 위해서 서비스 => DAO => Mapper을 거쳐서 다시 컨트롤러로 이동하고 저장된 이미지를 filename변수에 저장
ProductServiceImpl.java 중 일부 | @Override public String fileInfo(int product_id) { return productDao.fileInfo(product_id); } | cs |
ProductDAOImpl.java 중 일부 | @Override public String fileInfo(int product_id) { return sqlSession.selectOne("product.file_info",product_id); } | cs |
productMapper.xml 중 일부 | <mapper namespace="product"> <select id = "file_info" resultType="String"> select picture_url from product where product_id=#{product_id} <!-- product테이블안에 있는 입력된 id에 해당하는 이미지를 검색하는 쿼리문 --> </select> </mapper> | cs |
============== 이미지를 삭제한 후에 나머지 자료들을 삭제하기 위해 서비스를 호출해서 매개값(id)를 줘서 자료를 삭제 ===========
ProductServiceImpl.java 중 일부 | @Override public void deleteProduct(int product_id) { productDao.deleteProduct(product_id); } | cs |
ProductDAOImpl.java 중 일부 | @Override public void deleteProduct(int product_id) { sqlSession.delete("product.product_delete",product_id); } | cs |
productMapper.xml 중 일부 | <mapper namespace="product"> <delete id = "product_delete"> delete from product where product_id=#{product_id} <!-- product테이블안에 있는 상품번호가 ?인 객체를 삭제하는 쿼리문 --> </delete> | cs |
Back-End/Spring 2019. 6. 17. 18:07
파일업로드
스프링 프레임워크의 내장객체
SPRING / 파일업로드 적용방법
- pom.xml 설정 -
maven build를 위해서 pom.xml 파일에 commons-fileupload.jar 과 commons-io.jar 파일을 추가 <!-- MultipartHttpServletRequset -->
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
</dependencies>
- spring-context.xml 설정 (spring bean 설정) -
1. CommonsMultipartResolver 는 스프링 프레임 워크에 내장되어 있음
2. property -> maxUploadSize : 파일 업로드 시 최대로 올릴 수 있는 파일 사이즈 (파일의 용량)
3. property -> maxlnMemorySize : 적용된 경로에 최대로 저장할 수 있는 파일 사이즈 <!-- MultipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000000"/> <!-- 10MB-->
<property name="maxInMemorySize" value="100000000"/> <!-- 10MB-->
</bean>
-jsp 설정 (화면)-
1. form 태그의 enctype을 multipart / form-data로 설정해줘야 파일업로드 기능이 적용된다.
2. input 값에 대한 타입은 file로 적용해야 한다. <!-- form enctype="multipart/form-data"를 적어줘야 한다. -->
<form method="post" action="<c:url value='/regFile'/>" enctype="multipart/form-data">
<!-- input type="file" 이라고 꼭 적어줘야 함 -->
<input type="file" class="form-control" id="uploadFile"
name="uploadFile" />
<button type="submit" class="btn btn-default">저장</button>
<button type="list" class="btn btn-default">목록</button>
</form>
java 설정 (서버) - 준비
* 업로드한 파일의 경로가 도메인 별로 달라지기 때문에 파일 저장 경로를 다르게 지정해야 한다. public class UtilFile {
String fileName = "";
// fileUpload() 메소드에서 전체 경로를 DB에 경로 그대로 저장 한다.
public String fileUpload(MultipartHttpServletRequest request,
MultipartFile uploadFile,Object obj) {
String path = "";
String fileName = "";
OutputStream out = null;
PrintWriter printWriter = null;
try {
fileName = uploadFile.getOriginalFilename();
byte[] bytes = uploadFile.getBytes();
path = getServletContext().getRealPath("/") + files/;
File file = new File(path);
// 파일명을 중복체크
if (fileName != null && !fileName.equals("")) {
if (file.exists()) {
// 파일명 앞에 구분(예)업로드 시간 초 단위)을 주어 파일명 중복을 방지한다.
fileName = System.currentTimeMillis() + "_" + fileName;
file = new File(path + fileName);
}
}
out = new FileOutputStream(file);
out.write(bytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
if (printWriter != null) {
printWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return path + fileName;
}
java 설정 (서버) - 적용
@Controller
public class RewardController {
// 파일을 업로드하는 컨트롤러 클래스 메소드
@RequestMapping(value = "re/add", method = RequestMethod.POST)
// 인자로 MulfiPartFile 객체,
MultipartHttpServletRequest 객체, 업로드 하려는 도메인 클래스를 받는다
public String reAddProCtrl(@RequestParam("uploadFile") MultipartFile uploadFile, MultipartHttpServletRequest request, Reward reward) {
//UtilFile 객체 생성
UtilFile utilFile = new UtilFile();
//파일 업로드 결과값을 path로 받아온다
String uploadPath=utilFile.fileUpload(request,uploadFile,
reward);
// 해당 경로만 받아 db에 저장한다.
int n = rewardService.reAddServ(uploadPath, reward);
return "isg/Boardlist";
}
}
출처 http://www.incodom.kr/spring/%ED%8C%8C%EC%9D%BC%EC%97%85%EB%A1%9C%EB%93%9C#h_c37566d71129dd68628fc71b2b2e2a15
-File 파일 업로드 관련 클래스-
File 클래스 정리
java.io 패키지는 기존의 파일이나 폴더에 대한 제어를 하는 데 사용하는 File 클래스를 제공한다.
이 클래스를 이용해서 파일과 폴더에 대한 다양한 기능을 제공한다.
파일을 나타내는 객체를 생성하려면 다음과 같은 File 클래스의 생성자 함수를 이용한다.
* File 클래스의 생성자
File 클래스의 생성자 | 설명 | File(File parent, String Child) | parent 객체 폴더의 child 라는 파일에 대한 File 객체를 생성한다. | File(String pathname) | pathname에 해당되는 파일의 File 객체를 생성한다. | File(String parent, String, child) | parent 폴더 경로의 child라는 파일에 대한 File 객체를 생성한다. | File(URI uri) | file uri 경로에 대한 파일의 File 객체를 생성한다. |
* File 클래스의 메소드
File 클래스의 메소드 | 설명 | File getAbsoluteFile() | 파일의 절대 경로를 넘겨준다. | String getAbsolutePath() | 파일의 절대 경로를 문자열로 넘겨준다. | File getCanonicalFile() | 파일의 Canonical 경로를 넘겨준다. | String getCanonicalPath() | 파일의 Canonical 경로를 문자열로 넘겨준다. | String getName() | 파일이나 폴더의 이름을 넘겨준다. | String getParent() | 부모 경로에 대한 경로명을 문자열로 넘겨준다. | File getParentFile() | 부모 폴더를 File의 형태로 리턴한다. | String getPath() | 파일의 경로를 문자열의 형태로 리턴한다. | long getTotalSpace() | 하드디스크의 총 용량을 리턴한다. | long getUsableSpace() | 하드디스크의 사용 가능한 용량을 리턴한다. | long getFreeSpace() | 하드디스크의 남은 공간을 리턴한다. | int hashCode() | hash code를 반환한다. | long lastModified() | 해당 경로 파일의 최종 수정 일자를 반환한다. | long length() | 해당 경로 파일의 길이를 반환한다. | Path toPath() | java.nio.file.Path 객체로 반환한다. | URI toURI() | URI 형태로 파일 경로를 반환한다. | File[] listRoots() | 하드디스크의 루트 경로를 반환한다. | String[] list() | 경로의 파일들과 폴더를 문자열 배열로 반환한다. | String[] list(FilenameFilter filter) | filter에 만족되는 파일들과 폴더 이름을 문자열 배열로 반환한다. | File[] listFiles() | 해당 경로의 파일들과 폴더의 파일을 배열로 반환한다. | File[] listFiles(FileFilter filter) | filter에 만족되는 파일들과 폴더를 File 배열로 반환한다. | File[] listFiles(FilenameFilter filter) | filter에 만족되는 파일들과 폴더를 File 배열로 반환한다. |
* File 생성/수정/삭제 메소드
File 생성 수정 삭제 메소드 | 설명 | boolean createNewFile() | 주어진 이름의 파일이 없으면 새로 생성한다. | static File createTempFile(String prefix, String suffix) | default temporary-file 디렉토리에 파일 이름에 prefix와 suffix를 붙여 임시파일을 생성한다. | static File createTempFile(String prefix, String suffix, File directory) | 새로운 임시파일을 파일 이름에 prefix와 suffix를 붙여 directory 폴더에 생성한다. | boolean delete() | 파일이나 폴더를 삭제한다. 단, 폴더가 비어있지 않으면 삭제할 수 없다. | void deleteOnExit() | 자바가상머신이 끝날 때 파일을 삭제한다. | boolean mkdir() | 해당 경로에 폴더를 만든다. | boolean mkdirs() | 존재하지 않는 부모 폴더까지 포함하여 해당 경로에 폴더를 만든다. | boolean renameTo(File dest) | dest 로 File 이름을 변경한다. |
* File 체크 메소드
File 체크 메소드 | 설명 | boolean exists() | 파일의 존재 여부를 리턴한다. | boolean isAbsolute() | 해당 경로가 절대경로인지 여부를 리턴한다. | boolean isDirectory() | 해당 경로가 폴더인지 여부를 리턴한다. | boolean isFile() | 해당 경로가 일반 file 인지 여부를 리턴한다. | boolean isHidden() | 해당 경로가 숨김 file 인지 여부를 리턴한다. |
* File 권한 메소드
File 클래스 권한 관련 메소드 | 설명 | boolean canExecute() | 파일을 실행할 수 있는지 여부를 리턴한다. | boolean canRead() | 파일을 읽을 수 있는지 여부를 리턴한다. | boolean canWrite() | 파일을 쓸 수 있는지 여부를 리턴한다. | boolean setExecutable(boolean executable) | 파일 소유자의 실행 권한을 설정한다. | boolean setExecutable(boolean executable, boolean ownerOnly) | 파일의 실행 권한을 소유자 또는 모두에 대해 설정한다. | boolean setReadable(boolean readable) | 파일의 소유자의 읽기 권한을 설정한다. | boolean setReadable(boolean readable, boolean ownerOnly) | 파일의 읽기 권한을 소유자 또는 모두에 대해 설정한다. | boolean setReadOnly() | 파일을 읽기 전용으로 변경한다. | boolean setWritable(boolean writable) | 파일의 소유자의 쓰기 권한을 설정한다. | boolean setWritable(boolean writable boolean ownerOnly) | 파일의 쓰기 권한을 소유자 또는 모두에 대해 설정한다 |
출처: https://hyeonstorage.tistory.com/233 [개발이 하고 싶어요]
|