jQuery 이벤트 바인딩

Back-End/Spring 2019. 6. 30. 21:49

바인딩(Binding)이란?


서로 묶어서 연결해 준다는 의미이다. 예를 들어, 버튼이 하나 있는데 이 버튼을 클릭했을 때 click 이벤트가 발생한다고 가정했을때,


click이벤트를 함수 객체와 바인딩 (연결) 하면 이 click 이벤트가 발생했을 때 등록한 함수 객체가 실행된다.


즉, 사용자가 버튼을 클릭하면 이 함수가 실행된다.




특정 요소에서 발생하는 이벤트를 처리하기 위해서는 이벤트 핸들러(event handler) 함수를 작성해야만 한다.
이렇게 작성된 이벤트 핸들러를 특정 요소에 연결하는 것을 이벤트 바인딩(event binding)이라고 한다.

jQuery는 이벤트 바인딩을 위한 다양한 방법을 제공한다.

다음 예제는 id가 "btn"인 요소에 클릭(click) 이벤트 핸들러를 바인딩하는 다양한 방법이다.

$("#btn").click(function(event) { // 실행하고자 하는 jQuery 코드 });
$("#btn").bind("click", function(event) { // 실행하고자 하는 jQuery 코드 });
$("#btn").on("click", function(event) { // 실행하고자 하는 jQuery 코드 });
$("body").on({click: function(event) { // 실행하고자 하는 jQuery 코드 }}, "#btn");
$("body").on("click", "#btn", function(event) { // 실행하고자 하는 jQuery 코드 });

jQuery 1.7부터는 .bind()나 .click() 메소드를 사용해도, 내부적으로 .on() 메소드를 이용하여 이벤트 핸들러와 바인딩한다.

이벤트 처리의 발전

jQuery의 발전에 따라 이벤트를 처리하는 방법 역시 변해 왔다.
jQuery 1.0에서는 .bind() 메소드를 사용하여 이벤트 핸들러를 등록했다.
그 다음에는 .live() 메소드와 .delegate() 메소드를 거쳐, 현재는 .on() 메소드를 이용하여 이벤트를 처리하고 있다.

.on() 메소드

jQuery는 특정 요소에 이벤트 바인딩(event binding)하기 위해 .on() 메소드를 사용한다. jQuery 1.7부터 소개된 .on() 메소드는 다음과 같은 특징을 가진다.

  1. 선택한 요소에 어떤 타입의 이벤트라도 연결할 수 있다.
  2. 하나의 이벤트 핸들러에 여러 개의 이벤트를 동시에 연결할 수 있다.
  3. 선택한 요소에 여러 개의 이벤트 핸들러와 여러 개의 이벤트를 같이 연결할 수 있다.
  4. 사용자 지정 이벤트(custom event)를 위해 이벤트 핸들러로 데이터를 넘길 수 있다.
  5. 차후에 다루게 될 요소를 이벤트에 바인딩할 수 있다.

기본형

아래 예제는 요소를 클릭했을 때 알람창이 켜지는 예제이다.

$("p").on("click", function(){
 alert("문장이 클릭되었습니다.");
});


이벤트 핸들러 하나에 이벤트를 여러개 설정

.on() 메소드를 사용하면 하나의 이벤트 핸들러에 여러 개의 이벤트를 동시에 바인딩할 수 있다.

아래 예제는 <p> 요소에 mouseenter와 mouseleave 이벤트를 설정하였다.

$("p").on("mouseenter mouseleave", function() {
  $("div").append("마우스 커서가 문장 위로 들어오거나 빠져 나갔습니다.<br>");
});


또한, 하나의 요소에 여러 개의 이벤트 핸들러를 사용하여 여러 개의 이벤트를 같이 바인딩할 수도 있다.

$("p").on({ 
  click: function() {
    $("div").append("마우스가 문장을 클릭했습니다.<br>");
  },
  mouseenter: function() {
    $("div").append("마우스가 커서가 문장 위로 들어왔습니다.<br>");
  },
  mouseleave: function() {
    $("div").append("마우스가 커서가 문장을 빠져 나갔습니다.<br>");
  }
});


.off() 메소드

.off() 메소드는 더 이상 사용하지 않는 이벤트와의 바인딩(binding)을 제거한다.

$("#btn").on("click", function() {
  alert("버튼을 클릭했습니다.");
});
$("#btnBind").on("click", function() {
  $("#btn").on("click").text("버튼 클릭 가능");
});
$("#btnUnbind").on("click", function() {
  $("#btn").off("click").text("버튼 클릭 불가능");
});


.one() 메소드

.one() 메소드는 바인딩(binding)된 이벤트 핸들러가 한번만 실행되고 나서는, 더는 실행되지 않는다.

$("button").one("click", function() {
  $("div").append("이제 클릭이 되지 않습니다.<br>");
});


.one() 메소드는 on() 메소드와 같은 인수를 전달받는다.
따라서 여러 개의 이벤트 핸들러와 여러 개의 이벤트를 가질 수 있는 등 .on() 메소드와 같은 특징을 갖는다.


출처

http://www.devkuma.com/books/pages/232







:

Spring API (@RequestMapping이 사용하는 속성)

Back-End/API 2019. 6. 30. 10:35

@RequestMapping 어노테이션이란?


URL을 컨트롤러의 메소드와 맵핑할 때 사용하는 스프링 프레임워크의 어노테이션

(View <=> Controller)




@RequestMapping가 사용하는 속성


이름 

타입 

설명 

value

String[ ]

 URL 값으로 매핑 조건을 부여함 (default)

method

RequestMethod[ ]

 HTTP Request 메소드 값을 매핑 조건으로 부여

 사용 가능한 메소드는 GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE

params

String[ ] 

HTTP Request 파라미터를 매핑 조건으로 부여 

consumes 

String[ ] 

설정과 Content-Type request 헤더가 일치할 경우에만 URL이 호출됨 

produces

String[ ] 

설정과 Accept request 헤더가 일치할 경우에만 URL이 호출됨


:

드래그앤드롭, ajax 방식의 파일 업로드

Back-End/Spring 2019. 6. 28. 17:22

ajax (비동기적 방식)을 이용해서 드래그앤드롭을 이용해 파일업로드


이번에는 드래그해서 파일을 갖다놓으면 파일이 업로드되는 방식으로 할 예정



-실습 예제-


menu.jsp에 업로드 링크 코드를 추가

1
<a href="${path}/upload/uploadAjax">업로드(Ajax)</a> | 
cs


util 패키지를 추가하고, MediaUtils.java 파일을 만듦




MediaUtils.java


이미지와 이미지가 아닌 파일을 구분해서 업로드하고 섬네일을 만들예정


나머지 파일들은 업로드만 할 예정


그럼 구분을 해주어야 하는데 getMediaType( ) 메소드를 호출하게 되면 get( )안에 들어있는 이미지 형식이면 그림파일이고,


안들어있는 형식의 파일이면 일반파일로 생각해 구분할 예정.


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
package com.example.spring02.util;
 
import java.util.HashMap;
import java.util.Map;
 
import org.springframework.http.MediaType;
 
public class MediaUtils {
    private static Map<String,MediaType> mediaMap;
    
    //클래스를 로딩할 때 제일 먼저 실행되는 코드
    
    //파일을 업로드할때 해당되는 형식을 리턴하는 클래스
    
    //static로 만들어놓았기 때문에 이 부분은 처음부터 메모리에 올라가있는 상태이다.
    
    static {
        mediaMap = new HashMap<>(); //키와 값을 저장해야 하기 때문에 HashMap<>를 사용
        mediaMap.put("JPG",MediaType.IMAGE_JPEG); //JPG 값을 저장
        mediaMap.put("GIF",MediaType.IMAGE_GIF); //GIF 값을 저장
        mediaMap.put("PNG",MediaType.IMAGE_PNG); //PNG 값을 저장
    }
    
    public static MediaType getMediaType(String type) {
        // toUpperCase() 대문자로 변경
        return mediaMap.get(type.toUpperCase()); //mediaMap안에 저장되어 있는 형식 (jpg,gif,png)이면 그림파일, 아니면 일반파일로 구분함
    }    //toUpperCase() 메소드는 대문자로 바꿔주는 메소드
}
cs



util.UploadFileUtils.java


 - 파일 업로드를 할 때 파일의 이름이 중복되지 않도록 하기 위해 uuid를 사용했음


 - uuid는 16바이트 숫자로 구성되며 중복가능성이 없다고 보장할 수는 없으나 거의 중복이 없다고 본다.


 - uuid는 밀리세컨드 + db시퀀스로 구성되며 16바이트 (128비트), 32개의 십육진수로 표현, 총 36개 문자 (32개 문자와 4개의 하이픈)


   8 - 4 - 4 - 4 - 12


예) 550e8400-e29b-41d4-a716-446655440000

     340,282,366,920,938,463,463,374,607,431,768,211,456개의 사용 가능한 UUID를 만들 수 있다.



UploadFileUtils.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package com.example.spring02.util;
 
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.UUID;
import javax.imageio.ImageIO;
import org.imgscalr.Scalr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.FileCopyUtils;
 
public class UploadFileUtils {
    //로깅을 위한 변수
    private static final Logger logger 
        = LoggerFactory.getLogger(UploadFileUtils.class);
 
    
    //업로드를 하면 이 메소드가 호출된다.
    //File name를 똑같이 쓰면 덮어쓰기가 될 수 있으므로
    //uuid를 생성해서 파일이름 앞쪽에 붙여주어서 파일이름이 서로 중복되지 않게끔 한다.
    
    public static String uploadFile(String uploadPath
            , String originalName, byte[] fileData) 
                    throws Exception {
        // uuid 발급, 랜덤한uuid를 만들어 uid에 저장
        UUID uid = UUID.randomUUID();
        
        //uuid를 추가한 파일 이름
        String savedName = uid.toString() + "_" + originalName;
        
        // 업로드할 디렉토리 생성 (월, 일 날짜별로 디렉토리를 만든다.)
        // calcPath는 년도, 월, 일이 출력되게하는 메소드이고, 밑에서 static으로 선언되었으므로 메모리에 제일 처음 올려져 있다.
        // calcpath에 업로드 경로를 매개값으로 줘서, 업로드한 날짜를 savedPath에 저장하고, target변수에 File
        
        String savedPath = calcPath(uploadPath);
        File target = new File(uploadPath 
                + savedPath, savedName);    //업로드경로와 저장경로에 저장한파일의 이름에 대한 File 객체를 생성한다.
        
        // 임시 디렉토리에 업로드된 파일을 지정된 디렉토리로 복사
        FileCopyUtils.copy(fileData, target); //target에 저장된 파일경로와 이름, 그리고 fileData(파일용량)을 복사
        
        // 파일의 확장자 검사
        // a.jpg / aaa.bbb.ccc.jpg
        String formatName = originalName.substring(
                originalName.lastIndexOf("."+ 1);
        //lastIndexOf() 메소드는 String오브젝트에서 "." 문자열부터 끝쪽 방향으로 문자열을 찾는다.
        //"."이 여러개 있을 수도 있으므로 마지막 "." 뒤쪽부터 확장자이다.
        //그러니까 파일의 확장자가 뭔지 찾기 위해서 "." 뒷부분의 문자를 검색한다는 뜻이다.
        
        String uploadedFileName = null//uploadedFileName의 초기값을 지정
        
        // 이미지 파일은 썸네일 사용
        // 타입을 집어넣으면 이미지인지 아닌지 확인이 가능하다.
        if (MediaUtils.getMediaType(formatName) != null) {
            // 만약 이미지 이면
            // 썸네일 생성 (해당 그림이 드래그하면 작게보임)
            uploadedFileName = makeThumbnail(uploadPath
                    , savedPath, savedName);
            
        } else {    //이미지 파일이 아니면 아이콘을 생성한다.
            uploadedFileName = makeIcon(uploadPath, savedPath
                    , savedName);
        }
        return uploadedFileName;
    }
    
    //아이콘 생성
    private static String makeIcon(String uploadPath
            , String path, String fileName) throws Exception {
        // 아이콘의 이름
        String iconName = uploadPath + path + File.separator 
                + fileName;
        // 아이콘 이름을 리턴
        // File.separatorChar : 디렉토리 구분자
        // 윈도우 \ , 유닉스(리눅스) /
        return iconName.substring(uploadPath.length())
                .replace(File.separatorChar, '/');
        //파일 이름이라고 생각하면 된다.
    }
    
    
    private static String makeThumbnail(String uploadPath
            , String path, String fileName) throws Exception {
        // 이미지를 읽기 위한 버퍼
        BufferedImage sourceImg = ImageIO.read(
                new File(uploadPath + path, fileName));
        // 100픽셀 단위의 썸네일 생성
        // 작은 썸네일이기 때문에 이미지의 크기를 줄여야 한다.
        // 높이가 100픽셀이면 가로 사이즈는 자동으로 지정된다.
        BufferedImage destImg = Scalr.resize(
                sourceImg, Scalr.Method.AUTOMATIC
                , Scalr.Mode.FIT_TO_HEIGHT, 100);
        
        
        // 썸네일의 이름
        // 썸네일에는 "s_" 를 붙인다.
        
        String thumbnailName = uploadPath + path 
        + File.separator + "s_" + fileName;
        
        File newFile = new File(thumbnailName); //섬네일의 경로를 newFile변수에 저장
        
        String formatName = fileName.substring
                fileName.lastIndexOf("."+ 1);
                //lastIndexOf() 메소드는 String오브젝트에서 "." 문자열부터 끝쪽 방향으로 문자열을 찾는다.
                //"."이 여러개 있을 수도 있으므로 마지막 "." 뒤쪽부터 확장자이다.
                //그러니까 파일의 확장자가 뭔지 찾기 위해서 "." 뒷부분의 문자를 검색한다는 뜻이다.
        
        
        // 썸네일 생성
        // 아까 사이즈를 조정한 이미지파일형식, 파일이름(대문자로바꿔서), 아까 섬네일의 경로를 저장한 newFile변수를 넣어 
        // write 메소드를 사용해 썸네일을 생성
        ImageIO.write(
                destImg, formatName.toUpperCase(), newFile);
        
        
        // 썸네일의 이름을 리턴함
        return thumbnailName.substring(
        uploadPath.length()).replace(File.separatorChar, '/');
        }
    
    
    private static String calcPath(String uploadPath) {
        Calendar cal = Calendar.getInstance();
        String yearPath = File.separator 
                + cal.get(Calendar.YEAR);
        
        // "월"이랑 "일"은 10보다 작을때가 있으므로 (1월,2월....은 01월, 02월 이런식으로 붙이기 위해)
        // 그러니까 자릿수를 맞춰주기 위해서 DecimalFormat를 사용
        
        String monthPath = yearPath + File.separator 
                + new DecimalFormat("00").format(
                        cal.get(Calendar.MONTH) + 1);
        String datePath = monthPath + File.separator 
                + new DecimalFormat("00").format(cal.get(
                        Calendar.DATE));
        
        //디렉토리를 생성
        makeDir(uploadPath, yearPath, monthPath, datePath);
        logger.info(datePath);
        return datePath;
    }
    
 
    private static void makeDir(
            
            // 위에서 makeDir을 호출할때는 매개변수가 4개 이지만, String뒤에 있는 ...이 가변사이즈 매개변수이기 
            // 때문에 호출시에 매개변수의 숫자가 많아도 호출시에 매개변수가 배열로 만들어져 paths로 다 들어가기 때문에 쌓일수 있다.
            // ex) paths 0번이 yearPath, 1번이 monthPath가 되고, 2번이 datePath가 된다.
            
            String uploadPath, String... paths) {
        //디렉토리가 존재하면 skip, (디렉토리가 기존에 존재하면 만들지 않는다는 뜻)
        if (new File(paths[paths.length - 1]).exists()) {
            return;
        }
        //디렉토리가 없을시에 디렉토리 생성 코드
        //paths 배열에 저장된 값들을 하나씩 path에 저장하고,
        //업로드
        for (String path : paths) {
            File dirPath = new File(uploadPath + path);
            if (!dirPath.exists()) {//디렉토리가 존재하지 않으면
                dirPath.mkdir(); // 디렉토리 생성, mkdir()메소드는 패스명이 나타내는 디렉토리를 생성하는 메소드.
            }
        }
    }
}
cs



pom.xml


이미지 썸네일을 만들어주는 라이브러리를 추가함

1
2
3
4
5
6
<!-- 이미지 썸네일을 만들어주는 라이브러리 -->
        <dependency>
            <groupId>org.imgscalr</groupId>
            <artifactId>imgscalr-lib</artifactId>
            <version>4.2</version>
        </dependency>
cs



menu.jsp (view)

1
<a href="${path}/upload/uploadAjax">업로드(Ajax)</a> | 
cs



AjaxUploadController.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
121
122
123
124
125
126
127
128
129
130
131
package com.example.spring02.controller.upload;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
 
import javax.annotation.Resource;
import javax.inject.Inject;
 
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
 
import com.example.spring02.service.board.BoardService;
import com.example.spring02.util.MediaUtils;
import com.example.spring02.util.UploadFileUtils;
 
@Controller
public class AjaxUploadController {
    // 로깅을 위한 변수
    private static final Logger logger = LoggerFactory.getLogger(AjaxUploadController.class);
    
    @Inject
    BoardService boardService;
 
    // 업로드 디렉토리 servlet-context.xml에 설정되어 있음
    @Resource(name = "uploadPath")
    String uploadPath;
 
    // 파일첨부 페이지로 이동
    @RequestMapping(value = "/upload/uploadAjax"
            method = RequestMethod.GET)
    public String uploadAjax() {
        return "/upload/uploadAjax";
    }
 
    // 업로드한 파일은 MultipartFile 변수에 저장됨
    @ResponseBody // json 형식으로 리턴
    @RequestMapping(value = "/upload/uploadAjax"
    method = RequestMethod.POST, produces = "text/plain;charset=utf-8")
    public ResponseEntity<String> uploadAjax(MultipartFile file) throws Exception {
        // 업로드한 파일 정보와 Http 상태 코드를 함께 리턴
        return new ResponseEntity<String>(
                UploadFileUtils.uploadFile(uploadPath, file.getOriginalFilename(), file.getBytes()), HttpStatus.OK);
    }
 
    // 이미지 표시 기능
    @ResponseBody // view가 아닌 data 리턴
    @RequestMapping("/upload/displayFile")
    public ResponseEntity<byte[]> displayFile(String fileName) 
            throws Exception {
        // 서버의 파일을 다운로드하기 위한 스트림
        InputStream in = null// java.io
        ResponseEntity<byte[]> entity = null;
        try {
            // 확장자 검사
            String formatName = fileName.substring(
                    fileName.lastIndexOf("."+ 1);
            MediaType mType = MediaUtils.getMediaType(formatName);
            // 헤더 구성 객체
            HttpHeaders headers = new HttpHeaders();
            // InputStream 생성
            in = new FileInputStream(uploadPath + fileName);
//            if (mType != null) { // 이미지 파일이면
//                headers.setContentType(mType);
//            } else { // 이미지가 아니면
                fileName = fileName.substring(
                        fileName.indexOf("_"+ 1);
                // 다운로드용 컨텐트 타입
                headers.setContentType(
                        MediaType.APPLICATION_OCTET_STREAM);
                // 큰 따옴표 내부에 " \" "
                // 바이트배열을 스트링으로
                // iso-8859-1 서유럽언어
                // new String(fileName.getBytes("utf-8"),"iso-8859-1")
                headers.add("Content-Disposition",
                        "attachment; filename=\"" 
                                + new String(
fileName.getBytes("utf-8"), "iso-8859-1"+ "\"");
                // headers.add("Content-Disposition"
                // ,"attachment; filename='"+fileName+"'");
//            }
            // 바이트배열, 헤더
            entity = new ResponseEntity<byte[]>(
                    IOUtils.toByteArray(in), headers, HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
            entity = new ResponseEntity<byte[]>(
                    HttpStatus.BAD_REQUEST);
        } finally {
            if (in != null)
                in.close(); // 스트림 닫기
        }
        return entity;
    }
    
    @ResponseBody //뷰가 아닌 데이터를 리턴
    @RequestMapping(value="/upload/deleteFile"
        ,method=RequestMethod.POST)
    public ResponseEntity<String> deleteFile(String fileName){
        logger.info("fileName:"+fileName); 
        //확장자 검사
        String formatName=fileName.substring(
                fileName.lastIndexOf(".")+1);
        MediaType mType=MediaUtils.getMediaType(formatName);
        if(mType != null) { //이미지 파일이면 원본이미지 삭제
            String front=fileName.substring(012);
            String end=fileName.substring(14);
//         File.separatorChar : 유닉스 / 윈도우즈\    
            new File(uploadPath+(front+end).replace(
                    '/',File.separatorChar)).delete();
        }
        //원본 파일 삭제(이미지이면 썸네일 삭제)
        new File(uploadPath+fileName.replace(
                '/',File.separatorChar)).delete();
        //레코드 삭제
        boardService.deleteFile(fileName); 
        
        return new ResponseEntity<String>("deleted"
                ,HttpStatus.OK);
    }
}
cs



servlet-context.xml

1
2
3
4
5
    <!-- 파일업로드를 위한 디렉토리 설정 -->
    <!-- String uploadPath=new String("d:/upload"); -->
    <beans:bean id="uploadPath" class="java.lang.String">
        <beans:constructor-arg value="d:/upload" />
    </beans:bean>
cs

 


uploadAjax.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
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!-- views/upload/uploadAjax.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" %>
<!-- ----------------------------------css파일 부분 ----------------------------------------------------------->
<style>
.fileDrop {
    width: 100%;
    height: 200px;
    border: 1px dotted blue;
}
small {
    margin-left:3px;
    font-weight: bold;
    color: gray;
}
</style>
<!-- ----------------------------------css파일 부분 ----------------------------------------------------------->
<script>
$(function(){
    //드래그 기본 효과를 막음
    $(".fileDrop").on("dragenter dragover"function(event){
        event.preventDefault(); //기본효과를 막는다.
    });
    //기본효과를 막지 않으면 파일 업로드를 할 시에 사진이 실행되어버리기 때문에 기본효과를 막아야 한다.
 
// event : jquery의 이벤트
// originalEvent : javascript의 이벤트
    $(".fileDrop").on("drop",function(event){
        //drop이 될 때 기본 효과를 막음
        event.preventDefault();
        //드래그된 파일의 정보
        
        //첨부파일 배열
        var files=event.originalEvent.dataTransfer.files; //드래그된 파일정보를 files변수에 저장
        var file=files[0]; //첫번째 첨부파일 (컨트롤을 누르고 파일을 여러개를 올릴수도 있기 때문에, 첫번째 첨부파일이라고 지정한다.)
        
        
    /*     -ajax란??-
        Asynchronous JavaScript and XML의 약자 입니다.
        javascript와 xml을 이용하여 비동기 통신을 하는 기능입니다.
        쉽게 말해서 새로고침 없이 새로 고침 효과를 볼 수 있는 기능 (실시간으로 변화하는 페이지..)
        ajax는 데이터를 갱신하는 곳에서 사용한다면 페이지 전환 없이 동적으로 변화하는 모습을 자연스럽게 구현할 수 있습니다.
        
        -동기식과 비동기식-
        
        동기식 방식 : 클라이언트가 서버에게 요청하고 응답이 올 때까지 기다리는 것.
        비동기식 방식 : 클라이언트가 서버에게 요청을 하고 응답이 오기 전까지 다른일을 할 수 있다. */
 
        //ajax로 전달할 폼 객체
        var formData=new FormData(); //폼 객체
        formData.append("file",file); //만들어진 폼 객체에 위에서 저장한 file를 ifle란 이름의 변수로 저장한다
        //서버에 파일 업로드(백그라운드에서 실행됨)
        
        //    processData : false => post 방식
        //    contentType : false => multipart/form-data로 처리됨
        
        
        //비동기적 방식으로 보낸다.
        $.ajax({
            type: "post"//post 방식으로 보냄
            url: "${path}/upload/uploadAjax"//보낼 url
            data: formData, //formData를 보냄
            dataType: "text",
            processData: false,
            contentType: false,
            success: function(data,status,req){
                //alert(data);
                console.log("data:"+data); //업로드된 파일 이름
                console.log("status:"+status); //성공,실패 여부
                console.log("req:"+req.status);//요청코드값
                var str="";
                if(checkImageType(data)){ //이미지 파일일 경우 밑에있는 checkImageType메소드에서 리턴한값으로 참, 거짓 판단
                    //이미지파일이 맞을 경우에 실행되는 구문
                    //파일이름으로 이미지링크를 거는 하이퍼링크를 건다.
 
str="<div><a href='${path}/upload/displayFile?fileName="
        +getImageLink(data)+"'>";
                            
str+="<img src='${path}/upload/displayFile?fileName="
        +data+"'></a>"//이미지 파일일때는 이미지 자체를 보여준다.
 
    }else//이미지가 아닌 경우 텍스트만 보여준다 (text파일이나 기타 응용파일같은 것들....)
    str="<div>";
    str+="<a href='${path}/upload/displayFile?fileName="
        +data+"'>"+getOriginalName(data)+"</a>";
        }
                str+="<span data-src="+data+">[삭제]</span></div>"//삭제링크를 클릭하면 파일이름을 보내게 된다.
                $(".uploadedList").append(str);
            }
        });
    });
    // 태그.on("이벤트","자손태그",이벤트핸들러)
    // data : "fileName="+$(this).attr("data-src")
    // 태그.attr("속성")
    // $("#userid").attr("type")
    
    
    //fileDrop 함수
    //첨부파일 삭제 함수
    $(".uploadedList").on("click","span",function(event){
        //현재 클릭한 태그
        //span태그를 click하면 deleteFile로 이동
        var that=$(this); //this는 클릭한 태그가 된다 (즉, span태그를 클릭핤 )
//data: "fileName="+$(this).attr("data-src"),        
        $.ajax({
            url: "${path}/upload/deleteFile",
            type: "post",
            data: {fileName: $(this).attr("data-src")},
            //deleteFile로 이동하면 data-src속성을 뽑아서
            //보낸다.
            dataType: "text",
            success: function(result){
                if(result=="deleted"){
            //클릭한 span 태그가 속한 div를 제거
                    that.parent("div").remove();
            //dete
                }
            }
        });
    });
    
    function getOriginalName(fileName){
        if(checkImageType(fileName)){ //이미지 파일이면 skip
            return;
        }
        
        var idx=fileName.indexOf("_")+1//uuid를 제외한 파일이름을 리턴함
        return fileName.substr(idx);
    }
    function getImageLink(fileName){
        if(!checkImageType(fileName)){//이미지 파일이 아니면 skip
            return//함수를 종료시킨다.
        }
        
        //    이미지 파일일 경우 연월일 경로와 s_를 제거해서 리턴시킨다.
        var front=fileName.substr(0,12);//연월일 경로
        var end=fileName.substr(14);// s_ 제거
        return front+end;
    }
    
    //이미지인지, 아닌지 체크해주는 메소드
    function checkImageType(fileName){
        // i : ignore case (대소문자 무관)
        var pattern = /jpg|png|jpeg/i; //정규표현식(i는 대소문자 무시하기 때문에 넣은것.) 
        return fileName.match(pattern); //규칙에 맞으면 true
        //그러니까 파일의 확장명을 검사해서 jpg,png,jpeg형식이 있으면 fileName과 매칭해서 true를 리턴한다.
    }
});
</script>
</head>
<body>
<%@ include file="../include/menu.jsp" %>
<h2>Ajax File Upload</h2>
<!-- 파일을 업로드할 영역 -->
<div class="fileDrop"></div>
<!-- 업로드된 파일 목록을 출력할 영역 -->
<div class="uploadedList"></div>
 
</body>
</html>
cs




'Back-End > Spring' 카테고리의 다른 글

Spring xml bean property 정리  (0) 2019.07.01
jQuery 이벤트 바인딩  (0) 2019.06.30
Spring 자바 코드 난독화 (Java Code Obfuscation)  (0) 2019.06.28
Spring 자바스크립트 난독화  (0) 2019.06.28
인터셉터 (Interceptor)  (0) 2019.06.27
:

Spring 자바 코드 난독화 (Java Code Obfuscation)

Back-End/Spring 2019. 6. 28. 14:25

-자바 코드 난독화 (Java Code Obfuscation)-

 

 



 
 - 자바 프로그램은 디컴파일 (decompile), 디스어셈블 (disassemble)이 가능하므로 코드 난독화가 필요함


 - 프로가드 (proguard) : 대표적인 바자 난독화 라이브러리


 - 변수명, method name을 읽기 어렵게 변환함


 - 난독화를 해도 근본적으로 디컴파일은 막기 어려움


 - Spring 프로젝트보다는 자바 프로젝트에 적합
   (war 파일보다는 jar 파일로 export 할 경우에 적합함, 어노테이션, xml 설정 등에는 난독화 적용이 어려움)


 - java 코드를 읽기 어렵게 변경하는 것.


 - 상용프로그램 같은 경우는 배포하기 전에 난독화를 한번 하고 배포를한다. (보안때문에)

 

 

자바 디컴파일러를 다운로드

http://mannaedu.com/bbs/board.php?bo_table=pds&wr_id=41&sfl=wr_subject&stx=%EC%9E%90%EB%B0%94&sop=and

 

 

jd-gui-0.3.6.windows.zip
다운로드

 

 

압축을 풀기 => jd-gui.exe 클릭 (클래스 파일을 주고 자바 소스파일을 얻어내는 프로그램)

 

 

프로그램을 실행하고 File => Open File를 클릭

 

 

 

디컴파일할 파일을 누른 후 열기 버튼을 누름 (마찬가지로 jar 파일도 확인가능)

 

 

 

 

class파일을 열면 그 class가 속해있는 프로젝트가 통째로 열리게 된다.

 

 

 

 

자바 코드 난독화 실습

 

-jad (자바 디컴파일러), 자바 실습소스

 

-자바 실습소스를 이클립스로 import 한 후 jar 파일로 export

 

-jad에서 export한 jar 파일을 열어서 디컴파일된 소스 코드 확인

 

-프로가드 사이트 : http://proguard.sourceforge.net

 

 

proguard 6.0.3 다운로드 주소 : https://sourceforge.net/projects/proguard/files/

 

 

jar 파일 준비 ( jsp 프로젝트보다는 java 프로젝트로 테스트하는 것이 더 좋음), jar 파일을 찾기 쉬운 경로에 미리 복사함

 

 

- 난독화 테스트할 java project 생성

 

- 프로젝트 이름 : java_test

 

 

-proguard 다운로드-

 

 

 

 

 

 

 

 

 

다운로드 받은 압축파일의 압축을 풀고, 압축을 푼 파일을 D드라이브로 이동시킨다.

 

 

 

 

-실습하기-

 

자바 프로젝트 생성 => Jumin.java 클래스 생성하고 아래 코드를 작성

 

Jumin.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
package java_test2;
 
import java.util.Scanner;
 
public class Jumin {
 
    private String jumin; // 주민등록번호
    private int age; // 나이
    private char gender; // 성별
    private String strGender; // 성별저장
    private String nation; // 국가
 
    public Jumin() {
    }
 
    public void input() {
 
        Scanner scan = new Scanner(System.in); // 주민등록번호를 입력받기위해 Scanner객체 선언
        System.out.print("주민등록번호 : ");
        jumin = scan.nextLine(); // 입력받은 주민등록번호를 jumin변수에 저장
    }
 
    public void setAge() { // 나이를 저장하는 메소드
        int base = 0// 초기값을 0으로 선언
        gender = jumin.charAt(7); // index값은 항상 0부터 시작하므로 주민등록번호의 8번재값이 jumin에 저장
        switch (gender) {
 
        // gender에 저장된 숫자에 따라 다른 switch문이 실행됨
        case '1':
        case '2':
        case '5':
        case '6':
            base = 1900;
            break;
 
        case '3':
        case '4':
        case '7':
        case '8':
            base = 2000;
            break;
        }
 
        age = 2019 - (base + Integer.parseInt(jumin.substring(02)));
        // 나이를 계산하기 위해서 index의 0번째부터 4번째값까지를 리턴하고 base값과 더하고
        // 2019에서 그 값을 빼주면 현재 나이가 계산이된다.
        // ex) 나이가 28일때 2019-(1900+92)를 하면 27이된다. (만나이 이기 때문)
    }
 
    public void setNation() {
        // 주민등록 번호의 8번째 자리의 숫자의 값에따라 내국인과 내국인을 판별함.
 
        if (gender >= '1' && gender <= '4') {
            nation = "내국인";
        } else if (gender >= '5' && gender <= '8') {
            nation = "외국인";
        }
    }
 
    public void setStrGender() {
        // 주민등록 번호의 8번째 자리의 숫자의 값에따라 성별을 판별함.
        switch (gender) {
        case '1':
        case '3':
        case '5':
        case '7':
            strGender = "남";
            break;
        case '2':
        case '4':
        case '6':
        case '8':
            strGender = "여";
            break;
        }
    }
 
    public String toString() {
        // 입력한 주민등록번호로 판별된 데이터들을 취합해서 result에 저장하고,
        // result를 리턴.
        String result = "국적 :" + nation + "\n" + "나이:" + age + "\n" + "성별:" + strGender;
        return result;
    }
 
    public static void main(String arg[]) {
        Jumin ex = new Jumin(); // Jumin클래스를 사용하기 위해 ex변수에 Jumin 객체를 생성
        ex.input(); // 주민등록번호 입력 메소드 실행
        ex.setAge(); // 나이 저장 메소드 실행
        ex.setNation(); // 내, 외국 판별 메소드를 실행
        System.out.println(ex); // ex변수에 저장된 result를 출력함.
                                // 나머지 메소드들은 void이기 때문에 리턴값이 없음.
    }
}
 
cs

 

 

 

cmd창을 열고 d: => cd proguard6.0.3\lib => java -jar proguardgui.jar 을 입력

 

 

STS4로 돌아가서 아까 만든 프로젝트를 우클릭 => Export => Java => JAR file 실행

 

아래 그림과 같이 만듦

 

 

 

 

 

 

 

 

 

 

 

저장후 next => finsh 클릭,

 

 

 

 

바탕화면에 jar파일이 생성된것을 확인하고 아까 cmd로 실행한 ProGuard를 연다.

 

Input/Output => Add input... => jar파일 클릭 하고 ok를 클릭

 

 

 

 

input파일 추가를 완료했으므로 이 파일을 난독화 한 후에 출력할 output파일을 지정해야 한다.

 

Add output => 이름 output.jar로 한다. => OK 클릭

 

 

 

output.jar 파일이 지정되었으면 Next 버튼을 클릭하고 마지막에 Process! 버튼을 클릭함

 

클릭하면 난독화가 끝나고 패키징까지 되서 output.jar 파일이 만들어진다.

 

만들어진 output.jar 파일을 디컴파일러로 열어보면 변수이름과 메소드이름이 난독화가 되서 변수 이름이 a,b,c,d,e로 바뀐것을 확인할 수 있다.

 

실행은 정상적으로 된다.

 

Jumin.class (output.jar)

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
package java_test2;
 
import java.io.PrintStream;
import java.util.Scanner;
 
public class Jumin
{
  private String a; //변수 이름이 난독화되어서 a,b,c,d,e로 되었다.
  private int b;
  private char c;
  private String d;
  private String e;
  
  public String toString()
  {
    String str;
    return str = "국적 :" + this.e + "\n나이:" + this.b + "\n성별:" + this.d;
  }
  
  public static void main(String[] paramArrayOfString)
  {
    Object localObject = paramArrayOfString = new Jumin();
    Scanner localScanner = new Scanner(System.in);
    System.out.print("주민등록번호 : ");
    ((Jumin)localObject).a = localScanner.nextLine();
    localObject = paramArrayOfString;
    int i = 0;
    Object tmp42_41 = localObject;
    tmp42_41.c = tmp42_41.a.charAt(7);
    switch (((Jumin)localObject).c)
    {
    case '1'
    case '2'
    case '5'
    case '6'
      i = 1900;
      break;
    case '3'
    case '4'
    case '7'
    case '8'
      i = 2000;
    }
    ((Jumin)localObject).b = (2019 - (i + Integer.parseInt(((Jumin)localObject).a.substring(02))));
    localObject = paramArrayOfString;
    if ((paramArrayOfString.c >= '1'&& (((Jumin)localObject).c <= '4')) {
      ((Jumin)localObject).e = "내국인";
    } else if ((((Jumin)localObject).c >= '5'&& (((Jumin)localObject).c <= '8')) {
      ((Jumin)localObject).e = "외국인";
    }
    localObject = paramArrayOfString;
    switch (paramArrayOfString.c)
    {
    case '1'
    case '3'
    case '5'
    case '7'
      ((Jumin)localObject).d = "남";
      break;
    case '2'
    case '4'
    case '6'
    case '8'
      ((Jumin)localObject).d = "여";
    }
    System.out.println(paramArrayOfString);
  }
}
 
cs

 

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

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

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

:

Spring 자바스크립트 난독화

Back-End/Spring 2019. 6. 28. 10:22

자바스크립트 난독화(Javascript Obfuscation)란?


자바스크립트는 클라이언트에서 실행되도록하는 언어인데, 사용자가 웹사이트에 접근하게 되면

해당 스크립트를 그대로 볼 수 있게 되는데, 이를 방지하고자 적용하는 기법.

변수명과 함수명 같은것들을 읽기 어렵게 바꾸는 기법. 암호화랑은 다르다. 읽기 어렵게만함

하지만 궂이 자바스크립트까지 난독화할 필요는 없다.


(그러니까 사용자가 자바스크립트 코드를 그대로 볼 수 없도록 하는 기법, 보안..문제 때문에 그런거 같음)



  자바스크립트 난독화 사이트


  http://dean.edwards.name/packer/





- 자바스크립트 난독화 방법 -


(이 난독화된 코드는 자바스크립트 코드이므로 실제 Spring에서 그대로 복사해서 사용할 수 있다.)


주의할점 : ${path} 처럼 경로가 설정된 것은 난독화사이트에서 어떤 경로가 설정된 것이지 제대로 알 수가 없기 때문에 제대로 실행이 안될 수도 있다.

              그렇기 때문에 ${path} 같은 것은 사용하지 않고, 원래 경로를 그대로 써준다.

              (또한, $코드는 EL 코드이지, 자바스크립트 코드가 아니기 때문에 에러가 발생한다.)


  - Base62 encode 방식 -


  Binary Data를 Text로 바꾸는 Encoding의 하나로써 Binary Data를 Character set에 영향을 받지 않는


  공통 ASCII 영역의 문자로만 이루어진 문자열로 바꾸는 Encoding이다.


  Base62를 글자 그대로 직역하면 62진법 이라는 뜻.





  - Shrink variables 방식 -


  변수를 알파벳처럼 단순 문자로 바꾸는 방식.





:

Spring API ( HttpServletRequest, HttpServletResponse )

Back-End/API 2019. 6. 27. 18:19

-HttpServletRequest-


HttpServletRequest를 사용하면, 그 안에 있는 값을 받아올 수 있다.


예를 들어, 아이디, 비밀번호 등의 데이터를 컨트롤러로 보냈을 때,


HttpServletRequest 객체 안에 모든 데이터들이 들어가게 된다.



원하는 데이터를 꺼낼 때는 HttpServletRequest 객체 안의 메소드를 이용하면 된다.


getParameter( )



getParameter( ) 메소드는 반환타입이 String 타입이다.



예를 들어,


String id = httpServletRequest.getParameter("id");


httpServletRequest안에 있는 id값을 String타입으로 가져온다.




출처

https://hongku.tistory.com/118



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



-HttpServletResponse-


클라이언트에 데이터를 전송하기 위해서 사용함, 


이를 위하여 Response 객체의 setContentType( ) 와 getWriter( ) 메소드를 이용한다.


그 다음에 html을 작성하거나 다른 컨텐츠를 기록하거나 하는 I/O 작업을 하면 된다.


이 외에도 헤더 정보를 설정하거나, 오류를 발생시키거나, 쿠키를 추가할 때도 Response 객체를 사용한다.




 > jar파일을 전송한다면..

 public class CodeReturn extends HttpServlet{

 public void doGet(HttpServletRequest request, HttpServletResponse reponse) throws IOException{

response.setContentType("application/jar");    //MIME타입. 외우지말자

ServletContext ctx = getServletContext();

InputStream is = ctx.getResourceAsStream("/book.jar");  // 이 코드를 풀어보면 입력스트림으로  book.jar을 주세요.

                                                                                                // 인자값은 반드시 /로 시작.

OutputStream os = response.getOutputStream();

byte[] bytes = new byte[1024];

while(true){

int count = is.read(bytes);

out.write(bytes,0,count);

if(bytes == -1){

break;

}

}

os.flush();

os.close();

}




출처

https://mkkbest.tistory.com/entry/HttpServletRequest-%EA%B0%9D%EC%B2%B4-HttpServletResponse-%EA%B0%9D%EC%B2%B4

:

인터셉터 (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차트를 볼 수 있게끔 하고, 관리자 로그인이 안되어 있으면 관리자 로그인창이 출력된다.

:

기본 용어 정리

Back-End/Data Base 2019. 6. 27. 12:20

 

 용어


 설명

 Table

 데이터 값들의 집합을 뜻한다. 

 Row

 Table 내에서 한 줄에 해당하는 데이터 값들. (=Tuple 와 동일한 단어) 

 Column

 

 Table에서 데이터가 갖는 속성값. (=attribute와 동일한 단어)


 예시) 상단의 "설명" 이 column에 해당한다.


 Domain

 

 도메인은 하나의 Attirbute가 취할 수 있는 같은 타입의 원자 (Atomic)


 값들의 집합


 Attribute

 

 파일 구조상의 데이터 항목 또는 데이터 필드에 해당된다.


 속성 (Attribute)는 개체의 특성을 기술한다.


 Tuple

 Table (Relation) 을 구성하는 각각의 행을 뜻한다.

 Cardinality

 도메인에 있는 모든 값들의 갯수 



출처

https://jhkang-tech.tistory.com/6

'Back-End > Data Base' 카테고리의 다른 글

오라클 반복문 (for loop, while loop)  (0) 2019.07.03
오라클 변수 선언  (0) 2019.07.03
무결성이란?  (0) 2019.06.27
오라클 SQL Develope 다운로드 및 설치  (0) 2019.05.28
19.05.05 데이터베이스 성능  (0) 2019.05.05
: