'Back-End/Spring'에 해당되는 글 102건

  1. 2019.06.27 인터셉터 (Interceptor)
  2. 2019.06.26 AOP와 트랜잭션 처리 실습 (코드로 테스트)
  3. 2019.06.26 http 응답 코드와 method의 종류
  4. 2019.06.24 AOP와 트랜잭션 처리 실습 (데이터베이스로 테스트)
  5. 2019.06.22 AOP 복습(Spring Framework STS4 동영상 강의)
  6. 2019.06.21 Spring를 사용한 Chart 그리기 (JFree Chart)
  7. 2019.06.20 Spring를 사용한 Chart 그리기 (Google Chart) 1
  8. 2019.06.19 스프링을 사용해서 pdf 생성 2

인터셉터 (Interceptor)

Back-End/Spring 2019. 6. 27. 17:56

- Interceptor (인터셉터) -



  클라이언트의 요청 전,후에 특정 작업을 처리하고자 할 때 사용하는 기능 (간단하게 컨트롤러 같은 계층이라고 생각하면 됨)


  매개변수 - HttpServletRequest, HttpServletResponse

  

  용도 - 로그인 처리, pc웹 / 모바일웹 분기 처리 등

  

  


  Filter : 인코딩 처리할때 사용 (예를 들면 한글 인코딩 필터), 필터는 선처리만 된다.

  

  Interceptor : AOP의 @Around와 비슷한 성격이다, 하지만 AOP는 메소드 단위 (코드)로 설정하고, 인터셉터는 특정한 URL 단위로 설정한다.

  

  html 페이지는 웹서버에서 별도의 처리를 하지 않고, 그대로 클라이언트에게 전달된다.


  jsp 페이지는 JspServlet를 경유하여 html로 변환된 후 클라이언트에게 전달됩니다.

 

  Spring Framework에서는 DispatchereServlet을 경유하여 처리됩니다.


  인터셉터는  Filter (필터) - DispatcherServlet 이전에 실행되며 선처리만 가능합니다.

  

  

  AOP - @Around => ProceedingJoinPoint


@Before, @After => JoinPoint


  실행순서 : 1. Filter > 2. DispatcherServlet > 3. Interceptor > 4. AOP



-예제 1. 특정한 url에 대해서 인터셉터가 실행되는지 확인.-

  

SampleInterceptor.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
package com.example.spring02.interceptor;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
// HandlerInterceptorAdapter 를 상속받음(추상클래스)
public class SampleInterceptor 
    extends HandlerInterceptorAdapter {
    //로깅을 위한 변수
    private static final Logger logger
        = LoggerFactory.getLogger(SampleInterceptor.class);
    
    
    //선처리와  후처리는 필수가 아니므로 원하는곳에서 작업을 하면 된다.
    
    
    
//선처리
    @Override
    //preHandle는 메인 요청 전에 경유하는 메소드
    public boolean preHandle(HttpServletRequest request
            , HttpServletResponse response, Object handler) 
                    throws Exception {
        logger.info("pre handle...");
        return true//true이면 계속 진행, false이면 멈춤
    }
//후처리
    @Override
    //postHandle는 메인 요청이 끝난 다음에 경유하는 메소드 
    public void postHandle(HttpServletRequest request
            , HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        logger.info("post handle..."); 
    }
}
 
 
cs



servlet - context 인터셉터 관련 설정



  먼저 인터셉터를 bean으로 설정한 후


  <beans : bean id = "인터셉터의 아이디" class = "인터셉터의 경로" />




  

  어떤 url을 호출했을 때 인터셉터를 작동시킬 것인지 설정해야 합니다.


  <interceptors>

  <interceptor>

  <mapping path = "/shop/**"/>

  <beans:ref bean = "인터셉터의 아이디"/>

  </interceptor>

  </interceptors>




servlet-context.xml

(인터셉터 관련 빈을 등록하고 shop 하위 모든 url에 인터셉터를 적용)

=> 즉, shop 하위 url이 실행될때 인터셉터 빈에 등록된 클래스가 실행된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
<!-- 인터셉터 빈을 등록 -->
 
<beans:bean id = "sampleInterceptor" class = "com.example.spring02.interceptor.SampleInterceptor" />
    <interceptors>
        <interceptor>
 
        <!-- 인터셉터의 매핑 정보 -->
    
        <!-- shop 하위 url에 인터셉터를 적용 -->
 
            <mapping path="/shop/**" />
            <beans:ref bean="sampleInterceptor" /> //아이디를 참조하는 것이기 때문에 bean id와 ref bean은 같아야 한다.
    </interceptor>
</interceptors>
cs



코드를 추가하고 서버를 돌리고 로그를 확인해보면 pre handle와  post handle 사이에 인터셉터가 실행된 것을 확인할 수 있다.



==============================================================================================



예제 2. 


views / include / session_check.jsp


인터셉터를 사용하지 않고 세션의 존재 여부를 체크하는 코드를 작성할 경우 아래와 같이 처리한다.


  

  shop하위의 페이지로 넘어갈때 서블릿에 인터셉터를 빈으로 등록해놓으면 장바구니를 클릭할때 


  인터셉터에서 로그인 여부를 판단해서 작업을 처리한다.




원래는 컨트롤러에서 처리를 해도 된다

관리자 전용 페이지의 세션 체크

http://localhost:8080/spring02/shop/product/write.do


write.jsp파일에 다음 코드를 추가



  

  <%@ page language = "java" contentType = "text/html; charset=UTF-8" pageEncoding = "UTF-8"%>

  <!--views/include/session_check.jsp -->

  <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>

  <c:if test = "${sessionScope.admin_userid == null}" >   // 세션을 체크했을때 null값과 같으면 (로그인을 안했다면)..

      <script>

           alert("로그인 하신 후 사용하세요.");                  // 다음 메시지를 출력함..

           location.href = "${path}/admin/login.do";          // 로그인 페이지로 이동시켜주는 코드

      </script>

  </c:if>




LoginInterceptor.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
package com.example.spring02.interceptor;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
 
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
 
//HandlerInterceptorAdapter 추상클래스 상속
// preHandle(), postHandler() 오버라이딩
 
public class LoginInterceptor 
    extends HandlerInterceptorAdapter {
    
    
    //로그인을 하기전에 로그인이 되어있는 상태인지 검사하고 로그인이 되어있으면
    //메인 액션으로 이동하고, 로그인이 안되어있으면 로그인 페이지로 이동시킨다.
    //메인 액션이 실행되기 전 실행되는 메소드

    @Override
    public boolean preHandle(HttpServletRequest request
            , HttpServletResponse response, Object handler) 
                    throws Exception {
        //세션 객체 생성
        HttpSession session=request.getSession();
        
        //세션이 없으면(로그인되지 않은 상태)
        if(session.getAttribute("userid"== null) {
            
            //login 페이지로 이동
            response.sendRedirect(request.getContextPath()
                    +"/member/login.do?message=nologin");
            return false//메인 액션으로 가지 않음
        }else {
            return true//메인 액션으로 이동
        }
    }
    
    //메인 액션이 실행된 후 실행되는 메소드
    @Override
    public void postHandle(HttpServletRequest request
            , HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);
    }
}
cs


이 인터셉터를 적용하기 위해 태그를 선언해야 한다.


servlet - context.xml

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
<!-- 인터셉터 빈을 등록 -->
    <beans:bean id="sampleInterceptor" //id에는 클래스이름에다가 첫글자만 소문자로 된 것을 많이 사용한다.
class="com.example.spring02.interceptor.SampleInterceptor"> //클래스에는 정확한 경로를 적어야 한다.
    </beans:bean>
    <beans:bean id="loginInterceptor"
class="com.example.spring02.interceptor.LoginInterceptor">
    </beans:bean>
    <beans:bean id="adminInterceptor"
class="com.example.spring02.interceptor.AdminInterceptor" />
 
<!-- 인터셉터 호출을 위한 url mapping -->
 
    <interceptors>
        <interceptor>
        <!-- 인터셉터의 매핑 정보 -->    
        <!-- shop 하위 url에 인터셉터를 적용 -->
            <mapping path="/shop/**" />
            <beans:ref bean="sampleInterceptor" />
        </interceptor>

        <interceptor>
//그러니까 아래와 같은 url로 (list.do, insert.do) 접속했을때 loginInterceptor를 적용시키라는 의미.
            <mapping path="/shop/cart/list.do"/>
            <mapping path="/shop/cart/insert.do"/>
            <beans:ref bean="loginInterceptor"/>
        </interceptor>

    <!-- 관리자 세션 체크를 위한 인터셉터 설정 -->

        <interceptor>
            <mapping path="/shop/product/write.do"/>
            <mapping path="/shop/product/insert.do"/>
            <beans:ref bean="adminInterceptor" />
        </interceptor>

    </interceptors>
cs



인터셉터를 만들어놓으면 컨트롤러마다 세션을 만들지 않아서 되서 편리하다.



3. 관리자 로그인을 체크할 수 있는 인터셉터 만들기


AdminInterceptor.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
package com.example.spring02.interceptor;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
 
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

// HandlerInterceptorAdapter 추상클래스 상속
// preHandle(), postHandle() 오버라이딩

public class AdminInterceptor 
    extends HandlerInterceptorAdapter {
    
    //로그인 실행전 처리하는 메소드
    //세션에 저장되어있는 id가 null이면 로그인 페이지로 이동하고,
    //세션에 저장되어 있는 id가 있으면 메인 액션으로 이동함

    @Override
    public boolean preHandle(HttpServletRequest request
            , HttpServletResponse response, Object handler) 
                    throws Exception {
        //세션 객체 생성
        HttpSession session=request.getSession();
        //세션변수 admin_userid가 없으면
        if(session.getAttribute("admin_userid")==null) {
            //로그인 페이지로 이동
            response.sendRedirect(request.getContextPath()
                    +"/admin/login.do?message=nologin");
            //메인 액션으로 이동하지 않음
            return false;
        }else {
            //세션 변수가 있으면 메인 액션으로 이동
            return true;
        }
    }

    //후처리
    @Override
    public void postHandle(HttpServletRequest request
            , HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);
    }
}
cs



servlet-context.xml 중 일부 (인터셉터 매핑 url 추가, chart랑 jchart도 관리자만 사용할 수 있게끔)

1
2
3
4
5
6
7
8
9
10
       <!-- 관리자 세션 체크를 위한 인터셉터 설정 -->
 
        <interceptor>
            <mapping path="/pdf/list.do"/>
            <mapping path="/shop/product/write.do"/>
            <mapping path="/shop/product/insert.do"/>
            <mapping path="/chart/**" />
            <mapping path="/jchart/**" />
            <beans:ref bean="adminInterceptor" />
        </interceptor>
cs



프로젝트를 실행하면 관리자 로그인을 한 상태에서만 차트와 j차트를 볼 수 있게끔 하고, 관리자 로그인이 안되어 있으면 관리자 로그인창이 출력된다.

:

AOP와 트랜잭션 처리 실습 (코드로 테스트)

Back-End/Spring 2019. 6. 26. 17:52

-AOP 실습예제-


사용자가 메시지를 남기면 포인트 10 증가


메시지를 읽으면 포인트 5 증가


글쓰기를 하면 포인트 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 용어 정리


아래 용어들은 Spring에서만 사용되는 용어들이 아닌 AOP 프레임워크 전체에서 사용되는 공용어입니다.




 타겟 (Target)


 부가기능을 부여할 대상을 의미한다.


 여기선 핵심기능을 담당하는 getBoards 혹은 getUsers를 하는 Service들을 의미한다.

 

 

 애스팩트 (Aspect)


 객체지향 모듈을 오브젝트라 부르는 것과 비슷하게 부가기능 모듈을 애스펙트라고 부르며, 핵심기능에 부가되어 의미를 갖는 특별한 모듈이라


 생각하면 된다.


 애스팩트는 부가될 기능을 정의한 어드바이스와 어드바이스를 어디에 적용할지를 결정하는 포인트컷을 함께 갖고 있습니다.


 AOP(Aspect Oriented Programiming) 라는 뜻 자체가 어플리케이션의 핵심적인 기능에서 부가적인 기능을 분리해서 애스팩트라는 모듈로 


 만들어서 설계하고 개발하는 방법을 의미한다.

 

 

 어드바이스 (Advice)


 실질적으로 부가기능을 담은 구현체를 의미합니다.


 어드바이스의 경우 타겟 오브젝트에 종속되지 않기 때문에 순수하게 부가기능에만 집중할 수 있습니다.


 어드바이스는 애스펙트가 '무엇' 을 '언제' 할지를 정의하고 있습니다.



 포인트컷 (PointCut)


 부가기능이 적용될 대상 (메소드) 를 선정하는 방법을 얘기합니다.


 즉, 어드바이스를 적용할 조인포인트를 선별하는 기능을 정의한 모듈을 얘기합니다.



 조인포인트 (JoinPoint)


 어드바이스가 적용될 수 있는 위치를 얘기합니다.


 다른 AOP 프레임워크와 달리 Spring에서는 메소드 조인포인트만 제공하고 있습니다.


 따라서 Spring 프레임워크 내에서 조인포인트라 하면 메소드를 가리킨다고 생각해도 된다.


 

 프록시 (Proxy)


 타겟을 감싸서 타겟의 요청을 대신 받아주는 랩핑(Wrapping) 오브젝트 입니다.


 호출자 (클라이언트)에서 타겟을 호출하게 되면 타겟이 아닌 타겟을 감싸고 있는 프록시가 호출되어, 

 

 타겟 메소드 실행전에 선처리, 타겟 메소드 실행 후, 후처리를 실행시키도록 구성되어있습니다.



  

  (AOP에서 프록시는 호출을 가로챈 후, 어드바이스에 등록된 기능을 수행 후 타겟 메소드를 호출합니다.)
















MessageDTO.java (메시지 저장 DTO 클래스)

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
.package com.example.spring02.model.message.dto;
 
import java.util.Date;
 
public class MessageDTO {
    
    //메시지 저장 DTO 클래스
    
    private int mid; 
    private String targetid;
    private String sender;
    private String message;
    private Date opendate; //java.util.Date
    private Date senddate;
    
    //getter,setter,toString()
    
    public int getMid() {
        return mid;
    }
    public void setMid(int mid) {
        this.mid = mid;
    }
    public String getTargetid() {
        return targetid;
    }
    public void setTargetid(String targetid) {
        this.targetid = targetid;
    }
    public String getSender() {
        return sender;
    }
    public void setSender(String sender) {
        this.sender = sender;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public Date getOpendate() {
        return opendate;
    }
    public void setOpendate(Date opendate) {
        this.opendate = opendate;
    }
    public Date getSenddate() {
        return senddate;
    }
    public void setSenddate(Date senddate) {
        this.senddate = senddate;
    }
    @Override
    public String toString() {
        return "MessageDTO [mid=" + mid + ", targetid=" + targetid + ", sender=" + sender + ", message=" + message
                + ", opendate=" + opendate + ", senddate=" + senddate + "]";
    }
    
}
cs




UserDTO (유저 정보 저장 DTO 클래스)

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
.package com.example.spring02.model.message.dto;
 
public class UserDTO {
    
    //사용자 정보 저장 DTO 클래스
    
    private String userid; //아이디
    private String upw;    //비밀번호
    private String uname; //이름
    private int upoint; //포인트
    
    //getter,setter,toString()
    public String getUserid() {
        return userid;
    }
    public void setUserid(String userid) {
        this.userid = userid;
    }
    public String getUpw() {
        return upw;
    }
    public void setUpw(String upw) {
        this.upw = upw;
    }
    public String getUname() {
        return uname;
    }
    public void setUname(String uname) {
        this.uname = uname;
    }
    public int getUpoint() {
        return upoint;
    }
    public void setUpoint(int upoint) {
        this.upoint = upoint;
    }
    @Override
    public String toString() {
        return "UserDTO [userid=" + userid + ", upw=" + upw + ", uname=" + uname + ", upoint=" + upoint + "]";
    }
    
}
 
cs




MessageDAO.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.package com.example.spring02.model.message.dao;
 
import com.example.spring02.model.message.dto.MessageDTO;
 
public interface MessageDAO {
    
    //메시지 쓰기
    public void create(MessageDTO dto);
    
    //메시지 읽기
    public MessageDTO readMessage(int mid);
    
    //상태 변경
    public void updateState(int mid);
}
 
cs




MessageDAOImpl.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
.package com.example.spring02.model.message.dao;
 
import javax.inject.Inject;
 
 
import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;
 
import com.example.spring02.model.message.dto.MessageDTO;
import com.example.spring02.model.shop.dao.CartDAO;
 
@Repository //dao bean으로 등록
 
public class MessageDAOImpl implements MessageDAO {
 
    @Inject //의존관계 주입(Dependency Injection, DI)
    SqlSession sqlSession;
    
    @Override
    public void create(MessageDTO dto) { //메시지를 추가하는 메소드
        sqlSession.insert("message.create", dto);
    }
 
    @Override
    public MessageDTO readMessage(int mid) {
        return null;
    }
 
    @Override
    public void updateState(int mid) {
    }
 
}
 
cs



messageMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<!-- 다른 mapper와 중복되지 않도록 네임스페이스 기재 -->
<mapper namespace="message">
<!--메시지를 만들때 값을 집어넣는 쿼리문 -->
    <insert id="create">
        insert into tbl_message (mid,targetid,sender,message)
        values ( message_seq.nextval
                        , #{targetid}, #{sender}, #{message} )    
    </insert>
        
</mapper>
cs



메시지를 저장한 후에 포인트를 주기위한 Point 관련 인터페이스와 클래스를 만듦


PointDAO.java

1
2
3
4
5
6
.package com.example.spring02.model.message.dao;
 
public interface PointDAO {
    public void updatePoint(String userid, int point);
}
 
cs



PointDAOImpl.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
.package com.example.spring02.model.message.dao;
 
import java.util.HashMap;
import java.util.Map;
 
import javax.inject.Inject;
 
import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;
 
@Repository //스프링에서 관리하게끔 하기 위해 bean 선언
public class PointDAOImpl implements PointDAO {
    
    @Inject //의존성을 주입
    SqlSession sqlSession;
    
    @Override
    public void updatePoint(String userid, int point) {
        Map<String,Object> map=new HashMap<>(); //두가지 타입의 값을 넣을때는 HashMap()사용
        map.put("userid", userid); //map안에 값들을 저장함
        map.put("point", point);
        //2개 이상의 값을 전달할 경우 - dto, map
        sqlSession.update("point.updatePoint", map); //네임스페이스와 id를 맞춰서 mapper 쿼리 실행
        
    }
 
}
 
 
cs



pointMapper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<!-- mappers/message/pointMapper.xml -->
 
<!-- 다른 mapper와 중복되지 않도록 네임스페이스 기재 -->
<mapper namespace="point">
<!-- sqlSession.update("point.updatePoint", map); -->
    <update id="updatePoint">
        update tbl_user
        set upoint=upoint+#{point}
        where userid=#{userid}
    </update>
    <!-- 사용자의 아이디에 맞춰서 포인트를 올려주는 쿼리문 -->
    <!-- 변수 2개가 hashmap<>로 전달되었다. -->
    
</mapper>
cs




MessageSerivce.java

1
2
3
4
5
6
7
8
9
10
package com.example.spring02.service.message;
 
import com.example.spring02.model.message.dto.MessageDTO;
 
public interface MessageService {
    public void addMessage(MessageDTO dto); //메시지를 추가하는 메소드
    public MessageDTO readMessage(String userid, int mid); //메시지를 읽는 메소드
    
}
 
cs




-트랜잭션 처리 (거래 처리 단위)-


트랜잭션이 완료되지 않은 상태에서 에러가 발생할 경우 데이터에 오류가 발생함


트랜잭션의 특징중 일관성에 어긋나기 때문.


예를 들어 메시지는 전달되나 포인트는 안올라가는 문제가 발생할 수 있음.


이러한 문제를 예방하기 위해 @Transactional 어노테이션을 트랜잭션 메소드에 사용한다.


이 어노테이션을 사용하면 메소드 안에 있는 코드가 하나라도 에러가 발생할 시 rollback을 시켜버려서 트랜잭션의 일관성을 유지시켜준다.





-사전 설정-


1. 실습하기 전에 root-context.xml 파일의 네임스페이스의 tx (트랜잭션 처리를 할 수 있는 namespaces) 체크를 확인




2. root-context.xml에 트랜잭션 설정 코드가 있는지 확인


1
2
3
4
5
6
7
    <!-- 트랜잭션 관련 설정 -->
//dataSource에서 트랜잭션 관리자 역할을 할수있는 빈을 만든다.
//<tx:annotation-driven /> 이 태그는 아까 사용한 @Transactional을 인식할 수 있는 태그이다.

    <bean id="transactionManager" //변수명
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> //자료형
        <property name="dataSource" ref="dataSource" />
    </bean> //dataSource는 bean을 참조한다. (xml파일의 위쪽에 있음)

    <!-- 트랜잭션 관련 어노테이션을 자동 인식하는 옵션 -->

    <tx:annotation-driven /> //@Transaction 어노테이션을 인식할 수 있게 하는 태그
cs





MessageSerivceImpl.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
.package com.example.spring02.service.message;
 
import javax.inject.Inject;
 
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import com.example.spring02.model.message.dao.MessageDAO;
import com.example.spring02.model.message.dao.PointDAO;
import com.example.spring02.model.message.dto.MessageDTO;
 
@Service //현재 클래스를 스프링에서 관리하는 bean으로 설정
public class MessageServiceImpl implements MessageService {
    
    //서비스는 dao보다 좀 더 큰 단위의 업무를 수행한다.
    //dao에서는 세부 단위 업무를 수행
    
//Inject는 각각 해야 함
//의존성을 주입해야할 DAO클래스가 2개이기때문에 반드시 각각한다.
    
    @Inject
    MessageDAO messageDao;
    
    @Inject
    PointDAO pointDao;
 
    
    //트랜잭션 처리 대상 method
    //이 어노테이션을 사용하는 메소드는 그 메소드안에 있는 명령이 모두다 실행이 되지
    //않으면 rollback을 시켜버린다.
    //트랜잭션의 일관성을 유지하기 위해.
    
    @Transactional //method 내부의 코드를 트랜잭션 (거래처리 단위)로 묶음
    @Override
    public void addMessage(MessageDTO dto) {
        
        //메시지를 테이블에 저장
        messageDao.create(dto);
        //메시지를 보낸 회원에게 10포인트 추가
        pointDao.updatePoint(dto.getSender(), 10); 
    }
 
    @Override
    public MessageDTO readMessage(String userid, int mid) {
        // TODO Auto-generated method stub
        return null;
    }
 
}
 
cs



이 예제를 진행할때는 jsp 페이지를 만들지 않는다.


원래는 만들어야 정상이지만,  이번에는 툴로 처리를 할 것이기 때문에 만들지 않는다.


@RestController을 사용해서 입력되는 데이터와 되돌려주는 데이터를 모두 json으로 처리한다.



MessageController.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
.package com.example.spring02.controller.message;
 
import javax.inject.Inject;
 
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
import com.example.spring02.model.message.dto.MessageDTO;
import com.example.spring02.service.message.MessageService;
 
@RestController //jsp파일을 사용하지 않고, 입력되는 데이터와 되돌려주는 데이터를 모두 json으로 처리한다.
@RequestMapping("messages/*"//공통적인 url mapping
public class MessageController {
    
    @Inject
    MessageService messageService;
    
    @RequestMapping(value="/", method=RequestMethod.POST)
    //post방식일때만 이 메소드가 실행되게 만들었다.
    
    public ResponseEntity<String> addMessage(
            @RequestBody MessageDTO dto){
        //json형식으로 자료가 입력될때 RequestBody 어노테이션을 사용한다.
        //RequestBody어노테이션을 DTO를 json형식으로 받는다는 뜻
        //즉 json(String)타입으로 들어오면 dto타입으로 변환해서 되돌려준다.
        
        ResponseEntity<String> entity=null;
        try {
            messageService.addMessage(dto);
            entity=new ResponseEntity<>("success",HttpStatus.OK);
            //ResponseEntiry<>는 메시지 (성공 or 실패)와 에러코드를 함께 출력하고 싶을때 사용하는 타입
        } catch (Exception e) { 
            //메시지 처리가 실패하면 실행되는 코드, 메시지와 에러코드를 출력
            e.printStackTrace();
            entity=new ResponseEntity<>(e.getMessage()
                    ,HttpStatus.BAD_REQUEST); //메시지 처리가 실패하면 메시지와 400에러를 출력시킨다.
        }
        return entity; //메시지와 에러코드를 같이 리턴한다. 그리고 되돌려주는 값도 json형식 으로 보낸다.
    }
}
cs



 서비스 쪽에 AOP 설정을 해서 전, 후에 처리가 어떻게 되는지 확인



 

 @Bean과 @Component의 차이


 @Bean의 경우 개발자가 컨트롤이 불가능한 외부 라이브러리들을 Bean으로 등록하고 싶은 경우에 사용된다.


 ( 예를 들면 ObjectMapper의 경우 ObjectMapper Class에 @Component를 선언할 수는 없으니 ObjectMapper의 인스턴스를 생성하는 메소드를


 만들고 해당 메소드에 @Bean을 선언하여 Bean으로 등록한다.)


 반대로 개발자가 직접 컨트롤이 가능한 Class들의 경우엔 @Component를 사용한다.





MessageAdvice.java


@Before 어노테이션으로 MessageService.java 시작 전에는 로그 수집을 하고, 


@Around 어노테이션으로 MessageService.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
package com.example.spring02.aop;
 
import java.util.Arrays;
 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
 
@Component // 기타 bean, 개발자가 직접 컨트롤이 가능한 class의 경우에 사용하는 어노테이션
 
@Aspect // aop bean - 공통 업무를 지원하는 코드
 
public class MessageAdvice {
 
    private static final Logger logger = LoggerFactory.getLogger(MessageAdvice.class);
    //MessageAdvice.class의 로그를 출력
    
    @Before( //핵심업무 수행전에 실행이 되는 어노테이션
            "execution(* "
            
//================================================================            
            
            + " com.example.spring02.service.message"
            + ".MessageService*.*(..))")
    
    // MessageSerivce로 시작하는 모든 클래스의 모든 메소드들, 모든파라미터들에 대해서
    // execution구문이 실행되기 전에 startLog메소드가 먼저 실행된다.
    
//================================================================
    
    //그러니까 메시지 서비스 시작하기 전에는 로그수집을 한다.
    public void startLog(JoinPoint jp) {                            //execution구문이 실행되기 바로 전에 호출됨
                                                                    //joinPoint는 advice를 적용 가능한 지점을 의미한다.
                                                                    //메소드 호출, 필드 값 변경 등이 Joinpoint에 해당한다.
        
        logger.info("핵심 업무 코드의 정보:" +jp.getSignature());             //시그니처의 값을 반환
        
        logger.info("method:" +jp.getSignature().getName());        //시그니처의 이름을 반환
        
        logger.info("매개변수:" +Arrays.toString(jp.getArgs()));        //클라이언트가 메소드를 호출할 때 넘겨준 인자 목록을 Object 배열로 리턴함
    }
    
    @Around( //호출 전 후에 실행 되게끔하는 어노테이션
            "execution(* "
            + " com.example.spring02.service.message"
            + ".MessageService*.*(..) )")
    public Object timeLog(ProceedingJoinPoint pjp) throws Throwable {//MessageService 시작 전후로는 시간체크를 한다.
        
        //호출 전(Before)
        long start=System.currentTimeMillis(); //long타입의 값이며, 1/1000초의 값을 리턴한다. 정확한 시간을 측정할때 사용
        
//=========================================================
        
        Object result=pjp.proceed();
        
        //이 구문을 기준으로 위쪽은 호출 전이고, 아래쪽은 호출 후 이다.
        
//=========================================================
        
        //호출 후(After)
        long end=System.currentTimeMillis();                          //long타입의 값이며, 1/1000초의 값을 리턴한다. 정확한 시간을 측정할때 사용
        logger.info(pjp.getSignature().getName()+":"+(end-start));    //시그니처의 이름과 실제 실행한 시간을 로그로 찍는다.
        logger.info("=================");                             //
        return result;                                                
    }
}
cs



(실습 방법)


이번 실습에서는 뷰 (view)를 별도로 만들지 않고 Advanced REST client 확장 프로그램으로만 테스트를 수행합니다.


method : POST


Request URL : http://localhost/spring02/messages/


Body > Body content type > application/json


     Editor view > Raw input





  

  Advanced REST client 확장 프로그램 설치


  (파라미터를 사용해서 컨트롤러로 값을 보낼 수 있음)


  구글 검색창에 Advanced REST client 검색


  




  




일단 입력 데이터를 json으로 보내기 위해 사용해보기


put : 전체 수정


delete : 삭제


patch : 일부분 수정


Method : POST


Request URL : http://localhost/spring02/message/Body


Body content type : application/json


Editor view : Raw input


다음 처럼 설정하고 json 코드를 작성




json 코드 작성하고 send버튼을 누른다.


1
2
3
4
5
{
"targetid" : "user01", //"변수" : "값" 형식이다, 즉, "targetid" 변수의 값은 "user01" 이다.
"sender" : "user02",
"message" : "Good morning"
} // 해석 : user02가 user01에게 "Good morning" 메시지를 보낸다는 뜻.
cs


하단에 200 OK버튼이 뜨면 정상적으로 처리된 것이다.


MessageController.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
package com.example.spring02.controller.message;
 
import javax.inject.Inject;
 
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
import com.example.spring02.model.message.dto.MessageDTO;
import com.example.spring02.service.message.MessageService;
 
@RestController    //json을 리턴하는 method가 있는 경우에는 RestController을 사용
 
@RequestMapping("messages/*"//공통적인 url mapping
 
 
//메시지를 추가할때 맵핑되는 컨트롤러
 
 
public class MessageController {
    
    
    @Inject
    MessageService messageService;    //서비스를 호출하기위해 의존성을 주입한다.
    
    
    @RequestMapping(value="/", method=RequestMethod.POST) //POST방식의 메소드와 맵핑
    
    public ResponseEntity<String> addMessage(
             
            //    크롬 ARC를 사용해서 데이터를 보냈는데 String타입이기때문에 dto에 저장하려면
            //    변환과정이 필요하다.
            //    그 변환과정을 RequestBody라는 어노테이션을 붙이면서 자동적으로 dto타입으로 변환이 되었다.
            

@RequestBody와 ResponseEntity의 차이점

Spring에서는 HttpEntity란 클래스를 제공하는데 이 클래스의 역할은 Http 프로토콜을 이용하는 통신의 header과

body 관련 정보를 저장할 수 있게끔 합니다. 그리고 이를 상속받은 클래스로 RequestEntity와 ResponseEntity가

존재한다.

즉, 통신 메시지 관련 header과 body의 값들을 하나의 객체로 저장하는 것이 HttpEntity 클래스 객체이고,

Request 부분일 경우 HttpEntity를 상속받은 RequestEntity가, Response 부분일 경우 HttpEntity를 상속받은

ResponseEntity가 하게 됩니다.

@ResponseBody나 ResponseEntity를 return 하는거나 결과적으로는 같은 기능이지만.. 그 구현 방법이 틀리다.

예를 들어 header 값을 변경시켜야 할 경우엔 @ResponseBody의 경우 파라미터로 Response 객체를 받아서

이 객체에서 header를 변경시켜야 하고,.. ResponseEntity에서는 이 클래스 객체를 생성한뒤 객체에서

header 값을 변경시키면 된다.

자세한것은 Spring API 문서를 참고하면 된다.
            @RequestBody MessageDTO dto){
        
        ResponseEntity<String> entity=null;
        
        try {
            
            messageService.addMessage(dto);    
            
            entity=new ResponseEntity<>("success",HttpStatus.OK);
            
            //되돌려줄 메시지는 success, 상태메시지는 상태코드는 200이다.
            
        } catch (Exception e) {
            e.printStackTrace();
            
            entity=new ResponseEntity<>(e.getMessage()
                    ,HttpStatus.BAD_REQUEST);
        }
        return entity;
    }
}
cs


데이터베이스에서 데이터를 삭제해보고 크롬브라우저에서 send를 사용해서 컨트롤러로 자료를 보내서 테스트를 해본다.


로그를 확인해보면 dto에 값들이 쌓여있는것을 확인할 수 있다.

:

http 응답 코드와 method의 종류

Back-End/Spring 2019. 6. 26. 17:11

HTTP 응답 코드의 종류



 응답 코드


 설  명

100

 Continue (클라이언트로부터 일부 요청을 받았으며 나머지 정보를 계속 요청함) 

 101 

 Switching protocols 

 200

 OK (요청이 성공적으로 수행되었음) 

 201

 Created (PUT 메소드에 의해 원격지 서버에 파일이 생성됨)

 202

 Accepted (웹 서버가 명령을 수신함) 

 203

 Non - authoritative information (서버가 클라이언트 요구 중 일부만 전송) 

 204

 No content, (사용자 요구를 처리하였으나 전송할 데이터가 없음) 

 301

 Moved permanently (요구한 데이터를 변경된 타 URL에 요청함) 

 302

 Not temporarily 

 304

 Not modified (컴퓨터 로컬의 캐시 정보를 이용함. 대개 gif 등은 웹 서버에 요청하지 않음) 

 400

 Bad request (사용자의 잘못된 요청을 처리할 수 없음) , 스프링으로 작업할 때 많이 나는 에러

 401

 Unauthorized (인증이 필요한 페이지를 요청한 경우) 

 402

 Payment required (예약됨) 

 403

 Forbidden (접근 금지, 디렉터리 리스팅 요청 및 관리자 페이지 접근 등을 차단)

 404

 Not found. (요청한 페이지가 없음)

 405

 Method not allowed (허용되지 않는 http method 사용함)

 407

 Proxy authentication required (프록시 인증 요구됨)

 408

 Request timeout (요청 시간 초과)

 410

 Gone (영구적으로 사용 금지)

 412

 Precondition failed (전체 조건 실패)

 500

 Internal server error (내부 서버 오류)

 501

 Not implemented (웹 서버가 처리할 수 없음) 

 503

 Service unnailable (서비스 제공 불가)

 504

 Gateway timeout (게이트웨이 시간 초과)

 505

 HTTP version not supported (해당 http 버전이 지원되지 않음)




HTTP 메소드의 종류


 

HTTP Method


 전송 형태 

설명 

 GET

 

 GET [request-uri]?query_string


 HTTP/1.1\r\n


 Host:[Hostname] 혹은 [IP] \r\n


GET 요청 방식은 URI (URL) 이 가진 정보를

 

검색하기 위해 서버측에 요청하는 형태이다. 

 POST

 

 POST [request-uri]?query_string


 HTTP/1.1\r\n


 HOST:[Hostname] 혹은 [IP] \r\n


 Content-Lenght:[Lenght in Bytes] \r\n


 \r\n


 [query-string] 혹은 [데이터]


 POST 요청 방식은 요청 URI (URL)에 폼 입력을


 처리하기 위해 구성한 서버 측 스크립트


 (ASP, PHP, JSP 등) 혹은 CGI 프로그램으로


 구성되고 Form Action과 함께 전송되는데,


 이 때 헤더 정보에 포함되지 않고 데이터 부분에


 요청 정보가 들어가게 된다.

 HEAD

 HEAD                               [request-uri]


 HTTP/1.1\r\n


 Host:[Hostname] 혹은 [IP] \r\n 


 HEAD 요청 방식은 GET과 유사한 방식이나


 웹 서버에서 헤더 정보 이외에는 어떤 데이터도


 보내지 않는다.


 웹 서버의 다운 여부 점검 (Health Check)이나


 웹 서버 정보 (버전 등) 등을 얻기 위해 


 사용될 수 있다. 


 OPTIONS

 

 OPTIONS [request-uri]


 HTTP/1.1\r\n


 Host:[Hostname] 혹은 [IP] \r\n



 해당 메소드를 통해 시스템에서 지원되는


 메소드의 종류를 확인할 수 있다. 

 PUT

 

 PUT [request-uri] HTTP/1.1\r\n


 Host:[Hostname] 혹은 [IP] \r\n


 Content-Lenght:[Length] 


 

 POST와 유사한 전송 구조를 가지기 때문에

 

 헤더 이외에 메시지 (데이터)가 함께 전송된다.


 원격지 서버에 지정한 콘텐츠를 저장하기 위해


 사용되며 홈페이지 변조에 많이 악용되고 있다.


 DELETE

 

 DELETE                               [request-uri]


 HTTP/1.1 \r\n


 Host:[Hostname] 혹은 [IP] \r\n  \r\n


 

 원격지 웹 서버에 파일을 삭제하기 위해

 

 사용되며 PUT과는 반대 개념의 메소드이다.


 TRACE

 

 TRACE                                [request-uri]


 HTTP/1.1 \r\n


 Host:[Hostname] 혹은 [IP] \r\n \r\n


 
 원격지 서버에 Loopback (루프백)
 
 메시지를 호출하기 위해 사용된다. 

 CONNECT

 

 CONNECT                           [request-uri]


 HTTP/1.1 \r\n


 Host:[Hostname] 혹은 [IP] \r\n   \r\n


 웹 서버에 프락시 기능을 요청할 때 사용된다.


:

AOP와 트랜잭션 처리 실습 (데이터베이스로 테스트)

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(50not null,    //id
upw varchar2(50not null,        //비밀번호
uname varchar2(100not null,    //이름
upoint number default 0,        //포인트, 기본값은 0으로 한다
primary key(userid)                //기본키는 id로 한다.
);
 
 
 
create table tbl_message(        //tbl_message (메시지를 저장) 테이블을 생성
mid number not null,            //메시지의 id
targerid varchar2(50not null,    //메시지를 받을 사람의 id
sender varchar2(50not null,    //메시지를 보낸 사람의 id
message varchar2(4000not 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 실시


1
2
3
delete from tbl_message; //메시지 테이블은 지금 필요 없기 때문에 일단 삭제
 
commit; //지금까지 한 작업들을 반영하기 위해 commit를 
cs




:

AOP 복습(Spring Framework STS4 동영상 강의)

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>의 버전을 따라간다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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 : 트랜잭션 처리 관련된 태그

1
2
3
4
5
6
<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 태그를 추가

1
2
<!-- 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



이제 실행을 하면 콘솔창에 각 클래스와 메소드가 실행될때 로그가 뜨게 된다.

:

Spring를 사용한 Chart 그리기 (JFree Chart)

Back-End/Spring 2019. 6. 21. 12:18

- JFreeChart 만들기 -




참고 사이트 : http://www.jfree.org/jfreechart/




  

  JFreeChart의 특징


  다양한 형태의 차트 지원


  Swing, JSP, Servlet 에서 많이 사용되고 있음


  pdf 또는 png, jpg 등의 다양한 이미지 파일 형식으로 export 가능


  오픈 소스 라이브러리




pom.xml에 라이브러리를 추가

1
2
3
4
5
6
.<!-- 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, 900550);
        //차트를 받아와서 가로, 세로 길이를 설정해준다. 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(500500)));
            //차트의 내용을 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

1
2
3
4
5
6
7
8
9
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, //세로로 차트를 만든다는 의미
            truetruefalse);
             
    
//파이 차트
    //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


:

Spring를 사용한 Chart 그리기 (Google Chart)

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에 리소스 매핑을 추가

 

1
<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

1
2
3
4
5
6
7
8
9
10
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

1
2
3
4
    @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어노테이션을 안붙여도 된다.

1
2
3
4
5
6
7
8
9
10
11
    @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

 

아래 책은 제가 공부할때 활용했던 책으로 추천드리는 책이니 한번씩 읽어보시는것을 추천드립니다!! ㅎㅎ

토비의 스프링 3.1 세트:스프링의 이해와 원리 + 스프링의 기술과, 에이콘출판

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

:

스프링을 사용해서 pdf 생성

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

1
2
3
4
5
6
7
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ 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


: