'Back-End'에 해당되는 글 287건

  1. 2019.07.01 Spring 이메일 보내기 1
  2. 2019.07.01 이메일 관련 보내기 관련 API
  3. 2019.07.01 Spring xml bean property 정리
  4. 2019.06.30 jQuery 이벤트 바인딩
  5. 2019.06.30 Spring API (@RequestMapping이 사용하는 속성)
  6. 2019.06.28 드래그앤드롭, ajax 방식의 파일 업로드
  7. 2019.06.28 Spring 자바 코드 난독화 (Java Code Obfuscation)
  8. 2019.06.28 Spring 자바스크립트 난독화

Spring 이메일 보내기

Back-End/Spring 2019. 7. 1. 15:54

-이메일 서비스 구축 방법-

 

이메일 발송을 위해서는 메일 서버가 필요

 

 

 
  * 서버 구축 방법 *


  - 자체 구축


  - 포털사이트에서 제공하는 smtp(Simple Mail Transfer Protocol) server 활용


  - gmail의 경우 : 내 계정 - 로그인 및 보안 - 기기 활동 및 보안관련 활동 - 보안 수준이 낮은 앱 허용 옵션을 사용으로 설정해야 함


  - 여기에서는 gmail을 활용할 예정

 

 

 

-실습 예제-

 

1) pom.xml에 라이브러리 추가

 

빌드가 잘 안될 경우 스프링의 버전을 조정할 필요가 있음

비슷한 이름의 라이브러리가 있으므로 artifactld (버전 정보를 생략한 jar파일의 이름)를 정확히 확인하고 추가해야 함

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- 이메일 발송 관련 라이브러리 -->
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>vax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
cs

 

 

 

2) root-context.xml 에 메일 발송을 위한 bean을 등록

(계정이 등록되어 있어야 하기 때문)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 이메일 발송 bean 설정 -->
    <bean id="mailSender" 
class="org.springframework.mail.javamail.JavaMailSenderImpl">
        <property name="host" value="smtp.gmail.com" /<!-- gmail을 사용 -->
        <property name="port" value="587" />
        <property name="username" value="이메일주소" />
        <property name="password" value="비밀번호" />
        <property name="javaMailProperties">
            <props>
                <prop key="mail.transport.protocol">smtp</prop>
                <prop key="mail.smtp.auth">true</prop>
                <prop key="mail.smtp.starttls.enable">true</prop>
                <prop key="mail.debug">true</prop>
            </props>
        </property>
    </bean>
</beans>
cs

 

 

 

3) EmailDTO.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
package com.example.spring02.model.email;
 
public class EmailDTO {
    private String senderName;    //발신자 이름
    private String senderMail;    //발신자 이메일 주소
    private String receiveMail;    //수신자 이메일 주소
    private String subject;            //제목
    private String message;            //본문
    
    //getter,setter,toString
    public String getSenderName() {
        return senderName;
    }
    public void setSenderName(String senderName) {
        this.senderName = senderName;
    }
    public String getSenderMail() {
        return senderMail;
    }
    public void setSenderMail(String senderMail) {
        this.senderMail = senderMail;
    }
    public String getReceiveMail() {
        return receiveMail;
    }
    public void setReceiveMail(String receiveMail) {
        this.receiveMail = receiveMail;
    }
    public String getSubject() {
        return subject;
    }
    public void setSubject(String subject) {
        this.subject = subject;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    @Override
    public String toString() {
        return "EmailDTO [senderName=" + senderName + ", senderMail=" + senderMail + ", receiveMail=" + receiveMail
                + ", subject=" + subject + ", message=" + message + "]";
    }
    
}
 
cs

 

 

 

4) EmailService.java 만들기

1
2
3
4
5
6
7
package com.example.spring02.service.email;
 
import com.example.spring02.model.email.EmailDTO;
 
public interface EmailService {
    public void sendMail(EmailDTO dto);
}
cs

 

 

 

5)EmailServiceImpl.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
package com.example.spring02.service.email;
 
import javax.inject.Inject;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage.RecipientType;
 
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
 
import com.example.spring02.model.email.EmailDTO;
 
@Service // 서비스 빈으로 등록
public class EmailServiceImpl implements EmailService {
 
    @Inject
    JavaMailSender mailSender; // root-context.xml에 설정한 bean, 의존성을 주입
 
    @Override
    public void sendMail(EmailDTO dto) {
        try {
            // 이메일 객체
            MimeMessage msg = mailSender.createMimeMessage();
 
            // 받는 사람을 설정 (수신자, 받는사람의 이메일 주소 객체를 생성해서 수신자 이메일주소를 담음)
            msg.addRecipient(RecipientType.TO, new InternetAddress(dto.getReceiveMail()));
 
            /*
             * createMimeMessage() : MimeMessage객체를 생성시킴 (이것을 이용해서 메시지를 구성한 뒤 메일 발송)
             * addRecipient() : 메시지의 발신자를 설정 InternetAddress() : 이메일 주소 getReceiveMail() :
             * 수신자 이메일 주소
             */
 
            // 보내는 사람(이메일주소+이름)
            // (발신자, 보내는 사람의 이메일 주소와 이름을 담음)
            // 이메일 발신자
            msg.addFrom(new InternetAddress[] { new InternetAddress(dto.getSenderMail(), dto.getSenderName()) });
 
            // 이메일 제목 (인코딩을 해야 한글이 깨지지 않음)
            msg.setSubject(dto.getSubject(), "utf-8");
            // 이메일 본문 (인코딩을 해야 한글이 깨지지 않음)
            msg.setText(dto.getMessage(), "utf-8");
 
//            html로 보낼 경우            
//            MimeMessage message = mailSender.createMimeMessage();
//            MimeMessageHelper helper 
//            = new MimeMessageHelper(message, true);
//            helper.setTo("test@host.com");
//            helper.setText("<html><body><img src='cid:identifier1234'></body></html>", true);
 
            // 이메일 보내기
            mailSender.send(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
cs

 

 

 

6) admin_menu.jsp 에 이메일 발송 하이퍼링크를 추가

1
<a href="${path}/email/write.do">이메일 발송</a> | 
cs

 

 

 

7) EmailController.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
package com.example.spring02.controller.email;
 
import java.util.List;
 
import javax.inject.Inject;
 
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
 
import com.example.spring02.model.email.EmailDTO;
import com.example.spring02.service.email.EmailService;
 
@Controller // 컨트롤러 어노테이션 선언
@RequestMapping("email/*"// 공통적인 매핑 주소
public class EmailController {
 
    @Inject
    EmailService emailService; // 서비스를 호출하기위한 의존성 주입
 
    @RequestMapping("write.do"// 이메일 쓰기를 누르면 이 메소드로 맵핑되어서
    public String write() {
        return "/email/write"// 다시 write jsp 페이지로 이동하고 jsp페이지에서 내용을 다 채운 뒤에 확인 버튼을 누르면 send.do로 넘어감
    }
 
    @RequestMapping("send.do"// 확인 (메일발송) 버튼을 누르면 맵핑되는 메소드
    public String send(@ModelAttribute EmailDTO dto, Model model) {
        try {
 
            emailService.sendMail(dto); // dto (메일관련 정보)를 sendMail에 저장함
            model.addAttribute("message""이메일이 발송되었습니다."); // 이메일이 발송되었다는 메시지를 출력시킨다.
 
        } catch (Exception e) {
            e.printStackTrace();
            model.addAttribute("message""이메일 발송 실패..."); // 이메일 발송이 실패되었다는 메시지를 출력
        }
        return "/email/write"// 실패했으므로 다시 write jsp 페이지로 이동함
    }
}
cs

 

 

 

8) write.jsp 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!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" %>
</head>
<body>
<%@ include file="../include/admin_menu.jsp" %>
<h2>이메일 보내기</h2>
<form method="post" action="${path}/email/send.do"
<!-- post방식으로 자료를 컨트롤러로 보냄 -->
 
발신자 이름 : <input name="senderName"><br>
발신자 이메일 : <input name="senderMail"><br>
수신자 이메일 : <input name="receiveMail"><br>
제목 : <input name="subject"><br>
내용 : <textarea rows="5" cols="80" name="message"></textarea>
<br>
<input type="submit" value="전송">
</form>
<span style="color:red;">${message}</span>
 
</body>
</html>
cs

 

 

 

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

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

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

:

이메일 관련 보내기 관련 API

Back-End/API 2019. 7. 1. 15:26

javax.mail 패키지의 주요 클래스는

Session, Message, Address, Authenticator, Transport Store, Folder 등이 있다.

이들은 메시지를 만들고 전송하고 또 메일 서버에 있는 메시지를 꺼내오는데 개념적으로 중요한 역할을 한다. 이러한 클래스들의 주요 기능과 역할은 다음과 같다.


Session

메일 처리를 시작하는데 필요한 클래스로서 메일 처리 환경을 설정하고, 처리에 필요한
객체들을 생성해내는 역할을 한다. 메일 작업을 시작하려면 우선 Session 객체가 필요하다.
환경 설정을 위하여 java.util.Properties 를 사용하여 파라미터를 사용하며, 새로 객체를 생성하는 constructor는 private이기 때문에 사용할 수 없고, 다음과 같이 getDefaultInstance 메소드를 통하여 객체를 얻을 수 있다.


Properties props = new Properties(); // props 에 적절한 파라미터를 채움

Session session = Session.getInstance(props, null);


getDefaultInstance의 두번째 파라미터는 Authenticator인데 이는 차 후에 다루도록 하겠다.


Message

메일의 메시지를 의미하는 클래스로서 수신자/송신자/유형 등 헤더 부분과 본문 부분등 을 처리할 수 있도록 만들어진 클래스이다. 메시지의 기초 부분을 의미하는 Part 인터페이스를
implement 하고 있으며 Part는 메시지가 갖추어야한 기본적 요소를 정의하고 있다. Message는 기본적으로 추상 클래스이므로 실제 이를 상속받아 구체적 인터넷 메일 메시지를 구현한 클래스를 사용하기 위해서는 javax.mail.internet.MimeMessage를 주로 사용한다.


다음은 간단한 메시지를 생성해내는 예제이다.

MimeMessage message = new MimeMessage(session);
message.setSubject("테스트 제목");
message.setText("테스트 메일 입니다.");


Address

메시지를 송/수신하는 메일 주소를 추상한 클래스이다. 이 클래스도 추상 클래스이므로
인테넷 메일을 주소 클래스를 사용하려면 javax.mail.internet.InternetAddress를 사용한다.

다음은 "xxx@javanuri.com"이라는 인터넷 주소를 InternetAddress클래스를 사용하한 객체로 만들어 Message의 setFrom 메소드를 이용하여 송신자를 표시하는 예제이다.


Address address = new InternetAddress("xxx@javanuri.com", "홍길동");
message.setFrom(address);


메일 메시지의 수신자를 지정하는데는 Message 클래스의 addRecipient 메소드를 사용하는데
이 메소드 역시 Address 객체를 파라미터로 받아들인다. 송신자의 구분은 Message 객체에
RecipientType 이라는 상수로 정의되어 있다. 다음은 송신자, 참조자, 숨김 참조자를 메시지에 지정하는 예제이다.

Address toAddr = new InternetAddress("xxx@javanuri.com");
Address ccAddr = new InternetAddress("
yyy@javanuri.com");
Address bccAddr = new InternetAddress("
zzz@javanuri.com");

message.addRecipient(Message.RecipientType.TO, toAddr);
message.addRecipient(Message.RecipientType.CC, ccAddr);
message.addRecipient(Message.RecipientType.BCC, bccAddr);


Authenticator

주로 네트웍이나 서버에 연결할 때 사용자 아이디 및 암호 확인에 사용되는 추상 클래스이다. 사용자 인증을 위하여 Authenticator 클래스를 사용하려면 이 클래스를 상속을 받아 자신만의 클래스를 만들어 인증 방법을 구현해야 한다. 주로 사용자 인증에 관한 자신만의 고유 방법이 있거나 사용자 아이디와 암호를 묻는 화면을 만들때 사용한다. 상속하는 클래스를 만들 때는Authoticator에 있는 getXXX() 메소드를 override 하면 된다.

get 메소드 중에서 getPasswordAuthentication()은 사용자 아이디와 암호가 있는
PasswordAuthentication 객체를 리턴하도록 되어 있는데 PasswordAuthentication 객체는 화면 또는 적절한 방법에 의하여 아이디와 암호를 얻은 후에 생성하여 리턴하면 된다.

Session 생성시에 Authenticator를 파라미터로 주면 session이 필요한 시점에 사용자 아이디와
암호를 묻을 수 있도록 응용프로그램을 작성할 수 있다. 다음은 MyAuthenicator라는 새로운
클래스를 Authenticator로부터 상속받아 만들었다고 가정하고 session을 생성하는 예제이다.

Properties props = new Properties();
Authenticator auth = new MyAuthenticator();
Session session = Session.getDefaultInstance(props, auth);


Transport

메세지를 최종적으로 전달하는 클래스이다. 이 클래스도 추상 클래스로서 구체적 구현은 프로토콜 마다 다르게 할 수 있도록 구성되어 있다. 하지만 인터넷 메일을 일반적으로 SMTP 프로토콜에 의하여 전달된다. 메시지를 기본 SMTP 프로토콜을 이용하여 전달하려면 static 메소드인 Transport.send(Message msg)를 이용하면 된다. 프로토콜에 의하여 메시지를 전달하려면 다음 예제와 같이 Session 객체의 getTransport 메소드를 이용한다.

Transport trans = session.getTransport("smtp");
trans.connect("
www.javanuri.com", "xxxx", "yyyy");
trans.sendMessage(message, message.getAllRecipients());
trans.close();


Store

메시지를 저장하고 있는 장소를 추상화한 클래스인데, 메시지 저장은 메일 서버에서 하게된다. Store는 메일 서버와 연결이 되어 있어 저장된 메시지를 Store 객체를 통하여 가져올 수 있다. 다음은 Session 객체에서 POP3 프로토콜을 이용하여 Store 객체를 가져와 서버와 연결하는 예제이다.

Store store = session.getStore("pop3");
store.connect(host, username, password);


Folder

메시지를 분류해 놓은 장소를 추상화한 클래스이다.

서버에 따라 메시지는 수신함 "INBOX", 발신함 "OUTBOX" 등 여러가지 폴더를 갖을 수 있다.

하지만 POP3 프로토콜을 "INBOX" 폴더만을 지원한다.

폴더는 메시지를 저장하고 있는 Store 객체 안에서 찾을 수 있다.

다음은 Store 객체로부터 "INBOX" 폴더를 가져와 그 안에 있는 메시지를 가져오는 예제이다.

Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Message message[] = folder.getMessages();


Part

Message 와 BodyPart등의 메일을 이루는 부분들을 공통적 기반이되는 interface로서 내용과 그 내용에 대한 속성들로 이루어져 있다. 하나의 메일에는 여러 부분의 내용이 서로 형식을 달리하여 있을 수 있고 또 첨부 화일이나 자료가 여러가지가 있을 수 있다. 이러한 하나 하나의 구성요소가 Part라 볼 수 있고, 여러가지 형태가 복합되어 있는 것을 multipart라 한다. 이러한 메일 메시지의 기반을 이루고 있는 것이 바로 Part interface이다.


위에서 다룬 javax.mail 패키지들의 모두 추상 클래스이거나 인터페이스 이다.

이를 상속 받아 인터넷 메일 쪽으로 구현한 패키지는 javax.mail.internet 패키지이다.

한지만 javax.mail.internet 패키지에서도 구체적 MIME 형태의 메시지인 MimeMessage를 주로 다룬다. 즉 메시지 내용을 어떻게 구성하는지에 관한 클래스들이다. 메일에서 중요한 전송과 수신 즉 Transport와 Store에 관한 구현클래스는 없다. 기본적으로 Transport와 Store는 제 3의 제공자 (Provider)가 해당 프로토콜에 맞도록 구현하여 제공하도록 되어 있다. JavaMail 패키지에는 SUN사가 Provider가 되어 Transport와 Store 클래스를 상속받아 만든 com.sun.mail 패키지가 클래스 형태로 포함되어 있고 디폴트로 이 클래스를 사용하도록 설정되어 있다. 어떤 Provider가 제공하는 클래스들을 사용할 것인가는 Session 객체가 설정된 환경 및 properties를 가지고 결정을 한다. Session 객체는 다음 이름의 파일들을 읽어들여 Provider를 결정하고 제공하는 클래스들을 사용한다.

javamail.providers
javamail.default.providers

위의 파일들의 위치를 찾는 순서는 우선 JRE의 홈디렉토리, 다음의 Jar 파일의 META-INF 등의 순서로 다음과 같이 찾는다.

1. java.home/lib/javamail.X
2. META-INF/javamail.X
3. META-INF/javamail.default.X


다음의 JavaMail 패키지의 mail.jar안에 있는 META-INF/javamail.default.providers의 내용이다.
이러한 내용이 기본적으로 설정되어 있기 때문에 Session 객체는 com.sun.mail 패키지의 클래스들을 사용하는 것이다. 만일 제3의 Vendor가 제공하는 클래스를 사용하려면 다음 내용을 바꾸어주면 된다.

javamail.default.providers
====================================================

# JavaMail IMAP provider Sun Microsystems, Inc
protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Sun Microsystems, Inc;
# JavaMail SMTP provider Sun Microsystems, Inc
protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Sun Microsystems, Inc;
# JavaMail POP3 provider Sun Microsystems, Inc
protocol=pop3; type=store; class=com.sun.mail.pop3.POP3Store; vendor=Sun Microsystems, Inc



출처: https://jmoonyoung.tistory.com/entry/펌자바메일javamail-관련 [무녕이 스토리지]

:

Spring xml bean property 정리

Back-End/Spring 2019. 7. 1. 15:16

일반적인 bean 선언


<bean id = "testBean" class = "com.ljw.TestBean" />




id : spring container 에서 유일하게 식별할 수 있는 이름


class : 해당하는 bean의 full path (경로)




id대신 name 속성을 사용할 수 있음


<bean name = "testBean" class = "com.ljw.TestBean" />




facotry 클래스의 getInstace 를 통한 bean을 설정


<bean id = "testFactory" class = "com.ljw.TestFactory"

factory-method = "getInstance" />

factory-method : 해당 클래스의 객체를 반환해주는 메소드 (singleton 에서)




생성자를 통한 bean 설정


<bean id="testService" class="com.ljw.Service">
    <constructor-arg>
        <ref bean="testDao"/>
    </constructor-arg>
</bean>

<bean id="testDao" class="com.ljw.TestDao"/>
  
ref : reference, 즉 testDao id(혹은 name)를 갖는 bean을 생성자의 인자로 넘겨주겠다는 의미    

<bean id="testService" class="com.ljw.Service">
    <constructor-arg ref="testDao"/>
</bean>


이것 역시 위와 같은 의미

생성자에 특정 값을 넣어줄 때
<bean id="testService" class="com.ljw.Service">
    <constructor-arg>
        <value> 10  </value>
     </constructor-arg>
</bean>

혹은 다음과 같이 작성가능
<bean id="testService" class="com.ljw.Service">
    <constructor-arg value="10"/>
</bean>

<value type="long"> 3000 </value> 같이 value 값의 type을 지정해줄수도 있다(기본적으로 string으로 인식)
즉 생성자가 여러가지 유형이 있을경우 위와같이 type을 설정해주지 않으면 기본적으로 string type의 생성자를 먼저 고려하게됨


property bean 설정 방식(setXXX 함수 를 이용하여 bean 설정)

<bean id="testServcie" class="con.ljw.TestService" >
    <property name="testDao">
        <ref bean="implTestDao"/>
    </property>
</bean>

<bean id="implTestDao" class="con.ljw.ImplTestDao" />

property : TestService class의 setTestDao method를 의미

즉 위의 설정은 TestService class의 setTestDao 메소드를 호출하면서 인자 값으로 ImplTestDao 객체를 넘겨준다는 의미


<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/login/login.mw">loginController</prop>
            </props>
        </property>
    </bean>

props : java.util.properties 클래스로 key, value를 string type으로만 갖는다.
즉 위의 예는 SimpleUrlHandlerMapping 클스의  setMappings 메소드를통해 properties 객체를 생성하고
해당 properties 객체는 key="/login/login.mw", value = loginController를 갖고 있게된다.



Bean 객체 범위
<bean name="testDao" class="com.ljw.TestDao"/>

TestDao testDao1 = (TestDao)applicationContext.getBean("testDao");
TestDao testDao2 = (TestDao)applicationContext.getBean("testDao");
위 코드를 통해 생성된 testDao는 동일한 객체이다(스프링 컨테이너 내에서 빈 객체는 싱글턴)

bean scope를 명시하여 서로다른 객체로 생성이 가능한데 다음과 같다.

scope="singleon"  : 기본값이며 스프링 컨테이너당 하나의 빈 객체 생성
scope="prototype" : 빈은 사용할때마다 새로운 객체 생성
scope="request"    :  http 요청마다 새로운 객체 생성(WebApplicationContext에서 사용) 
scope="session"    :  세션마다 새로운 객체 생성(WebApplicationContext에서 사용)


<bean name="testDao" class="com.ljw.TestDao" scope="protytype" />
--> bean의 scope 속성값에 설정 하여 사용


list, map 등의 컬렉션 에대한 xml 태크가 있긴하지만 위에 정리된 부분만 봐도
spring xml에 대해 이해가 될거라 판단됨.



출처

http://jwlee1728.egloos.com/v/1805102

:

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 방식 -


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





: