객체 지향의 4대 특성

객체 지향은 현실 세계를 인지하는 방식으로 프로그램을 만들기 때문에 직관적이다. 인간의 인지 및 사고 방식까지 프로그래밍에 접목하는 인간 지향을 실천하고 있다.

 

캡 : 캡슐화(Encapsulation). 정보 은닉.

상 : 상속(Inheritance). 재사용.

추 : 추상화(Abstraction). 모델링.

다 : 다형성(Polymorphism). 사용 편의.

 

 

 

클래스와 객체의 차이

클래스와 객체의 차이는 분류(class)와 사물(Object)의 관계라고도 할 수 있다.

사물(객체) : 분류(클래스) = 개념 : 실체 라고도 표현할 수 있다.

 

 

 

추상화

객체지향의 추상화는 모델링이다. 비슷한 개념으로, 객체지향에서 추상화의 결과는 클래스다.

개념을 넓게 본다면 상속을 통한 추상화, 인터페이스를 통한 추상화, 다형성을 통한 추상화. 이 3가지 내용도 포함된다.

 

 

 

상속

객체지향에서의 상속은 상위 클래스의 특성을 하위 클래스에서 상속하고, 거기에 더해 필요한 특성을 추가(확장)해 사용할 수 있다는 의미이다. 상위 클래스 쪽으로 갈수록 추상화, 일반화 되었다고 말하며 하위 클래스 쪽으로 갈수록 구체화, 특수화 되었다고 말한다.

상속한다는 것이 상위 클래스의 특성을 상속한다는 것이지, 부모-자식 관계는 아니다.

상위 클래스의 특성을 재사용하는 것, 상위 클래스의 특성을 확장하는 것, is a kind of 관계(분류 관계)를 만족하는 것이다.

 

c++에서는 다중 상속이 없으나, 자바에서는 인터페이스를 도입해 다중 상속의 득만 취했다.인터페이스는 be able to (할 수 있는)의 표현 형태이다. Serializable(직렬화할 수 있는), Runnable(실행할 수 있는) 등의 인터페이스가 있다.

 

조금 더 깊이 생각해보면, 상위 클래스는 물려줄 특성이 풍부할수록 좋고, 인터페이스는 구현을 강제할 메소드의 개수가 적을수록 좋다. 상위 클래스가 풍성할수록 좋은 이유는 리스코프 치환 원칙에 따른 이유이며 인터페이스 메소드가 적을수록 좋은 이유는 인터페이스 분할 원칙에 따른 이유이다.

 

 

 

다형성

자바에서의 다형성 기본은 오버로딩과 오버라이딩이다.

오버로딩 : 같은 메소드 이름, 같은 인자 목록으로 상위 클래스의 메소드를 재정의

오버라이딩 : 같은 메소드 이름, 같은 인자 목록으로 다수 메소드를 중복 정의

오버라이딩, 오버로딩을 통해 이 다형성이 개발자가 프로그램을 작성할 때 사용 편의성을 준다.

 

 

 

캡슐화

정보를 은닉하기 위해 사용하며, 접근 제어자인 private, default, protected, public을 생각해야 한다.

call by value : 기본 자료형 변수는 저장하고 있는 값을 그 값 자체로 판단

call by reference : 참조 변수는 저장하고 있는 값을 주소로 판단

'BackEnd > Java' 카테고리의 다른 글

객체지향 디자인 패턴1 (퍼사드, 싱글톤)  (0) 2020.11.03
Java :: NULL vs isEmpty() 차이  (0) 2018.01.16
LinkedHashMap  (0) 2017.06.29
RSA 암호화 구현  (0) 2017.03.14
Java :: Spring 파일 업로드 (multipart-form/data)  (2) 2017.03.14

디자인 패턴이란 이미 알려진 설계 문제를 해결할 답안을 제시하면서 다양한 프로그램이 언어 개발자가 서로 같은 용어를 써서 효과적으로 소통할 수 있게 해준다. 문제가 생길 우려가 있는 부분에만 패턴을 사용하면 된다.

 

- 생성 패턴 : 객체 생성, 초기화, 클래스 선택에 관한 패턴(싱글톤 패턴), 팩토리 패턴

- 행동 패턴 : 객체 간 소통, 메시징, 상호 작용에 관한 패턴(옵저버 패턴)

- 구조 패턴 : 클래스와 객체 관계를 조직하는 패턴, 관련된 객체를 서로 묶어 원하는 로직을 구현하는 방법을 안내(장식자 패턴)

 

 

1. 퍼사드 패턴 (Facade Pattern)

하위 시스템의 복잡도를 감추는 동시에 그 전체 기능을 사용하기 쉬운 인터페이스로 제공하는 것이다. 예를 들어, 인터페이스도 일종의 퍼사드라 할 수 있다. (내부의 많은 서브 시스템을 외부의 거대한 클래스로 감싸서 편리한 인터페이스를 제공하는 것)

 

public class WashingMachine {
    public void heavilySoilded() {
        setWaterTemperature(100);
        setWashCycleDuration(90);
        setSpinCycleDuration(10);
        addDetergent();
        addBleach();
        addFabricSoftener();
        heatWater();
        satartWash();
    }
 
    public void lightlySoilded() {
        setWaterTemperature(40);
        setWashCycleDuration(20);
        setSpinCycleDuration(10);
        addDetergent();
        heatWater();
        satartWash();
    }
}
 
new WashingMachine().lightlySolied();
cs

 

메소드 구현체는 클라이언트와 완전히 분뢰되어 있어 구현체를 부꿔도 접근하는 클라이언트에는 아무런 영향이 없다.또 클라이언트는 두 메소드를 어떻게 구현했는지 알 수 없고, 알 필요도 없다.

 

장점으로는 결합도가 낮아지고, 유지보수성이 좋아지며 여러 번 실행해도 호출하는 메소드는 동일하기 때문에 일관된 서비스가 보장된다. 비즈니스 로직이 덜 복잡해지고 보안 및 트렌잭션 관리를 중앙화한다.

 

복잡한 로직을 상위 레벨에서 캡슐화하고, 접근 지점을 명확하게 단일화해 API를 통해서만 접근할 수 있게 유도한다.

퍼사드 패턴 사용시 메소드 호출의 계층 구조를 단순하게 만들 수 있다.

 

퍼사드 패턴은 POJO 또는 상태성/무상태성 세션 빈으로 구현할 수 있으나, 상태성 세션 빈으로 구현시에는 리소스가 바닥나지 않도록 주의해야 한다. 클라이언트가 일부러 대화를 끝내거나 타임아웃이 나기 전에는 서버 리소스를 붙들고 있기 때문이다.

 

 

2. 싱글톤 패턴 (Singleton Pattern)

자신의 인스턴스를 가리키는 레퍼런스가 유일하게 존재하고, getter 메소드 하나로만 생성/접근이 가능한 클래스를 가리킨다.

 

클래스 내 인스턴스가 하나 뿐이므로 프로그램 전역에서 해당 클래스의 인스턴스를 바로 얻을 수 있고 불필요한 메모리 낭비를 최소화한다. 하지만 싱글톤 남용시 쓸데없이 리소스를 캐시하고 가비지 컬렉터가 객체를 회수하지 못해 쓸 수 있는 메모리 리소스가 줄어들 수 있다. (단위 테스트에도 싱글톤은 좋지 않음)

 

public class ExampleSingleton {
    private static ExampleSingleton instance;
 
    private ExampleSingleton() {}
    
    public static ExampleSingleton getInstance() {
        if (instance == null) {
            instance = new ExampleSingleton();
        }
 
        return instance;
    }
}
cs

 

싱글톤은 생성자가 외부에 들어나지 않으면 간단히 해결된다. 인스턴스를 반환하는 메소드도 필요한데, 인스턴스를 생성하는 메소드는 클래스명을 통해 접근할 수 있도록 정적 메소드로 표시한다. (ex. ExampleSingleton.getInstance();)

 

반드시 접근제한자를 이용해 외부의 접근을 막거나 fianl로 레퍼런스를 변경하지 못하도록 설정해야 하는데, 생성자를 다른 곳에서도 사용할 수 있으면 그 곳에서도 인스턴스를 생성할 수 있기 때문이다. 생성자에 접근제한자를 사용하면 다른 인스턴스로 레퍼런스 시키지는 못해도, null값을 넣어 레퍼런스 자체를 지워버릴 수도 있어 그렇다.

 

자바 싱글톤은 자바5부터 도입된 Enum 타입을 사용해 생성하는 방법이 가장 좋다. Enum은 형태 자체가 싱글톤이라, 그 외의 생성 과정은 JVM이 처리하므로 객체 생성 및 동기화, 초기화 관련 문제를 신경 쓰지 않아도 된다.

 

public class ExampleSingleton {
    private enum ExampleSingletonEnum {
        INSTANCE;
        public void doAnythingAmazing() {}
    }
}
cs

 

싱글톤 자체 인스턴스 참조법 : ExampleSingletonEnum ese = ExampleSingletonEnum.INSTANCE;

'BackEnd > Java' 카테고리의 다른 글

자바와 객체 지향  (0) 2020.11.17
Java :: NULL vs isEmpty() 차이  (0) 2018.01.16
LinkedHashMap  (0) 2017.06.29
RSA 암호화 구현  (0) 2017.03.14
Java :: Spring 파일 업로드 (multipart-form/data)  (2) 2017.03.14

1. NULL

인스턴스가 생성되지 않은 상태를 말한다. List 변수가 메모리에 아무런 주소값도 창조하지 않은 상태이다.

그러므로, List에 값이 없을 경우 null로 체크하면 안된다.



2. isEmpty()

size() = 0

자바 SE 1.6 이상에서만 사용 가능하다.

인스턴스는 생성되었으나, List 안에 아무것도 없는 상태를 말한다. (값이 존재하지 않는 상태)

객체에 ""이란 값으로 들어가 있는 상태이다. (공백도 값으로 처리가 되기 때문에)

'BackEnd > Java' 카테고리의 다른 글

자바와 객체 지향  (0) 2020.11.17
객체지향 디자인 패턴1 (퍼사드, 싱글톤)  (0) 2020.11.03
LinkedHashMap  (0) 2017.06.29
RSA 암호화 구현  (0) 2017.03.14
Java :: Spring 파일 업로드 (multipart-form/data)  (2) 2017.03.14

HashMap은 입력된 데이터를 출력할 때 key값을 가지고 출력하기 때문에 출력하는 순서가 정해져 있지 않으나,

LinkedHashMap은 입력한 순서대로 데이터가 쌓여 있는 것을 확인할 수 있는 객체이다. 또한 HashMap의 장점을 그대로 가지고 있다.


Iterator로 출력시에도 HashMap은 순서가 없으나 LinkedHashMap은 입력된 순서에 맞게 값을 출력한다.

동일한 key의 중복을 막아주고, 특정 key의 값을 찾고자 할 떄 for문을 들어 전체 값을 검색할 필요가 없다.


순서대로 입력되어 있기 때문에, 전체 값을 순서대로 operation 해야할 때 유용하다.

HashMap 클래스를 상속 받아 만들어져 있고, Iterator를 사용하여 가져오더라도 입력시의 순서에 맞게 값을 가져올 수 있다.

LinkedList로 저장되며, HashMap과 마찬가지로 다중스레드를 사용시 Synchronized가 되지 않는다.


JDK 1.4 이상부터 사용 가능한 Collection 객체이다.


* removeEldestEntry() : put() 할 때 불리는 메소드. 들어온 순서를 기억하고 가장 오래된 값을 그 자리에 방금 들어온 값으로 대체하는 메소드.



2. 예제

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
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
 
public class LinkedHashMapExample {
    public static void initData(Map<StringString> map) {
        map.put("key1""1");
        map.put("key2""2");
        map.put("key3""3");
        map.put("key4""4");
        map.put("key5""5");
        map.put("key6""6");
        map.put("key7""7");
        map.put("key8""8");
        map.put("key9""9");
        map.put("key10""10");
    }
    
    public static void printResult(Map<StringString> map) {
        Set<String> set = map.keySet();
        Iterator<String> iter = set.iterator();
        
        while (iter.hasNext()) {
            String key = ((String)iter.next());
            String value = map.get(key);
            
            System.out.println("key : " + key + ", value : " + value);
        }
    }
    
    public static void main(String[] args) {
        System.out.println("==========HashMap Test=========");
        
        Map<StringString> hashMap = new HashMap<StringString>();
        
        initData(hashMap);
        
        printResult(hashMap);
        
        System.out.println("==========LinkedHashMap Test=====");
        
        Map<StringString> linkedHashMap = new LinkedHashMap<StringString>();
        
        initData(linkedHashMap);
        
        printResult(linkedHashMap);
    }
}
cs


포트폴리오 중 하나인 그룹웨어 시스템에서 로그인시 보안을 위해 간단하게라도 암호화 알고리즘을 구현해야겠다고 생각했다.

 

알고리즘의 종류에는 대칭 암호화와 비대칭 암호화가 있는데,

대칭 암호화는 빠르다는 장점이 있지만 자주 키를 바꿔야 해 관리가 어렵다는 단점이 있고

비대칭 암호화는 대칭에 비해 다소 느리지만 안전하고 키 관리가 단순하다는 장점이 있다.

 

계정 정보가 해킹당할 시 사내 정보가 유출될 수 있다는 점을 염두해 안전하다는 부분에 초점을 맞췄고,

비대칭 암호화인 RSA 알고리즘을 구현하기로 했다.  

 

 

* RSA 라이브러리 참고 사이트 : http://www-cs-students.stanford.edu/~tjw/jsbn/ 

 

 

1. 로그인 전 공개키 생성


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Controller
public class HomeController {
    final static Logger log = LoggerFactory.getLogger(HomeController.class);
 
    /**
     * @author GREEN GO
     * @since 2017.03.14
     */
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String homeCtrl(Locale locale, HttpServletRequest request, HttpServletResponse response, Model model,
            HttpSession session) throws Exception, NoSuchAlgorithmException {
 
        session = request.getSession();
 
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
 
        generator.initialize(1024);
 
        KeyPair keyPair = generator.genKeyPair();
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
 
        // RSA 개인키
        session.setAttribute("_RSA_WEB_Key_", privateKey);
 
        log.debug("HomeController _RSA_WEB_Key_ : " + request.getAttribute("setAttribute"));
 
        RSAPublicKeySpec publicSpec = (RSAPublicKeySpec) keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class);
 
        String publicKeyModulus = publicSpec.getModulus().toString(16);
        String publicKeyExponent = publicSpec.getPublicExponent().toString(16);
 
        // 로그인 폼 hidden setting
        request.setAttribute("RSAModulus", publicKeyModulus);
        // 로그인 폼 hidden setting
        request.setAttribute("RSAExponent", publicKeyExponent);
 
        log.debug("HomeController RSAModulus getAttribute : " + request.getAttribute("RSAModulus"));
        log.debug("HomeController RSAExponent getAttribute : " + request.getAttribute("RSAExponent"));
 
        return "member/mm_login";
    }
}
cs


로그인 페이지가 열리기 전 컨트롤러에서 공개키를 생성한 후, 로그인 form에 request 범위에 담아 hidden 타입으로 적어준다. 

 

 

2. 로그인


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
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE HTML>
<html>
<head>
    <link href="<c:url value='/resources/css/bootstrap.min.css'/>" rel="stylesheet">
    <link href="<c:url value='/resources/css/landing-page.css'/>" rel="stylesheet">
    <link href="<c:url value='/resources/font-awesome/css/font-awesome.min.css'/>" rel="stylesheet" type="text/css">
    <link href="https://fonts.googleapis.com/css?family=Lato:300,400,700,300italic,400italic,700italic" rel="stylesheet" type="text/css">
    
    <!-- RSA javascript library / cdn 올리는 순서 틀리면 안 됨 -->
    <script src="<c:url value='/resources/js/rsa/jsbn.js'/>"></script>
    <script src="<c:url value='/resources/js/rsa/rsa.js'/>"></script>
    <script src="<c:url value='/resources/js/rsa/prng4.js'/>"></script>
    <script src="<c:url value='/resources/js/rsa/rng.js'/>"></script>
    
    <script src="<c:url value='/resources/js/jquery-3.1.1.min.js'/>"></script>
    <script>
        $(document).on('click','#loginBtn',function() {
        
        // 로그인 버튼 클릭시 서버로 전송되기 전에 계정 정보 암호화 후 서버로 전송
        // $("#loginBtn").click(function() {
            var mmCode = $("#mmCode").val();
            var mmPassword = $("#mmPassword").val();
         
            // RSA 암호키 생성
            var rsa = new RSAKey();
            
            rsa.setPublic($("#RSAModulus").val(), $("#RSAExponent").val());
            
            // 계정 정보 암호화
            var secondMmCode = rsa.encrypt(mmCode);
            var secondMmPassword = rsa.encrypt(mmPassword);
            
            // console.log('secondMmCode : ' + secondMmCode);
            // console.log('secondMmPassword : ' + secondMmPassword);
            
            // Restful 기술을 이용한 컨트롤러를 이용해 ajax로 form 정보를 전송하고, 유효성 검사 후
            // 아이디, 비밀번호 일치하면 메인 페이지로 이동 (실패시 alert 창 띄움)
            $.ajax({ 
                  type: "post",  
                  url: "/smart/mm/loginRSA",
                  dataType: "json",
                  data: {"secondMmCode": secondMmCode, "secondMmPassword": secondMmPassword},
                  success: function(data) {    
                      
//                       alert('data.state : ' + data.state);
                     
                      if(data.state == true) {
                          location.href = "<c:url value='/member/mm_login'/>"
                      } else if(data.state == false) {
                         THIS.oWin.alert("로그인","로그인에 실패했습니다. <br> 아이디와 패스워드를 확인하세요.");
                      } else {
                         THIS.oWin.alert("로그인","잘못된 경로로 접근했습니다. <br>암호화 인증에 실패했습니다."); 
                      } 
                  } 
            });
        });
    </script>
</head>
<body>
    <div class="intro-header">
       <div class="container">
        <div class="row">
               <div class="col-lg-12">
                   <div class="intro-message">
                       <h1>Smart Groupware-System</h1>
                       <hr class="intro-divider">
                    <div class="col-md-4 col-md-offset-4">
                        <div class="panel panel-default">
                            <div class="panel-body">
                                <h5 class="text-center" style="color:black;">SIGN UP</h5>
                                <!-- 컨트롤러에서 생성했던 공개키를 form에 hidden 타입으로 배치-->
                                <form class="form form-signup" role="form" id="mmlogin" action="member/mm_login" method="post">
                                    <div class="form-group">
                                        <input type="hidden" id="RSAModulus" value="${RSAModulus}">
                                        <input type="hidden" id="RSAExponent" value="${RSAExponent}">
                                        <div class="input-group">
                                            <span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
                                            <input type="text" class="form-control" name="mmCode" id="mmCode" value="2"/>
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <div class="input-group">
                                            <span class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></span>
                                            <input type="password" class="form-control" name="mmPassword" id="mmPassword" value="123456"/>
                                        </div>
                                    </div>
                                    <input type="button" id="loginBtn" class="btn btn-sm btn-primary btn-block" value="로그인"/>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>
cs


컨트롤러에서 jsp 페이지로 보낸 hidden 타입들을 form 내에 배치하고, hidden 타입 데이터와 계정 정보를 ajax로 보내

Restful 기술을 이용한 컨트롤러에서 DB 정보와 비교한 후 성공시 메인 페이지로 이동하고 실패시 alert 창을 띄운다.

 

 

3. 계정 정보 복호화


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
package com.cafe24.smart;
 
import javax.crypto.Cipher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.bind.annotation.RestController;
 
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;
 
import com.cafe24.smart.member.domain.Department;
import com.cafe24.smart.member.domain.Member;
import com.cafe24.smart.member.service.MemberService;
 
@RestController
public class HomeRestController {
    private static final Logger log = LoggerFactory.getLogger(HomeRestController.class);
 
    @Autowired
    MemberService memberService;
 
    // 로그인 체크
    @RequestMapping(value = "mm/loginRSA", method = RequestMethod.POST)
    @ResponseBody
    // 암호화된 키들을 다시 복호화 하여 DB 값들과 비교 => 있으면 로그인
    public Map<String, Object> mmLoginRSACtrl(HttpServletRequest request, Map<String, Object> map) {
 
        Map<String, Object> params = new HashMap<String, Object>();
 
        String mmCode = request.getParameter("secondMmCode");
        String mmPassword = request.getParameter("secondMmPassword");
 
        HttpSession session = request.getSession();
 
        // 로그인 전에 세션에 저장했던 개인키를 getAttribute
        PrivateKey privateKey = (PrivateKey) session.getAttribute("_RSA_WEB_Key_");
 
        log.debug("HomeRestController mmLoginRSACtrl privateKey : " + privateKey);
 
        // 개인키(아이디)가 들어오지 않은 경우
        if (privateKey == null) {
            
            params.put("state"false);
            // 개인키(아이디)가 들어온 경우
        } else {
            try {
                // 암호화 처리된 사용자 계정을 복호화 처리
                int _mmCode = Integer.parseInt(decryptRsa(privateKey, mmCode));
                int _mmPassword = Integer.parseInt(decryptRsa(privateKey, mmPassword));
 
                Member member = new Member();
                Department department = new Department();
                member.setMmCode(_mmCode);
 
                // 복호화한 사원코드로 값을 가져와서 DB의 사원 패스워드와 같은지 비교
                params = memberService.mmLoginServ(member);
 
                log.debug("HomeRestController mmLoginRSACtrl _mmCode : " + _mmCode);
                log.debug("HomeRestController mmLoginRSACtrl _mmPassword : " + _mmPassword);
 
                if (params != null) {
                    log.debug("mmLoginRSACtrl params not null");
 
                    session.setAttribute("mmCode", _mmCode);
                    // session.setAttribute("mmName", params.get("mmName"));
 
                    member = memberService.mmContentByMmCodeServ(_mmCode);
                    session.setAttribute("mmName", member.getMmName());
 
                    // 부서정보세팅
                    department = memberService.mmDpDetailServ(member.getDpCode());
                    session.setAttribute("dpName", department.getDpName());
                }
 
                log.debug("mmLoginRSACtrl mmCode session : " + session.getAttribute("mmCode"));
                log.debug("mmLoginRSACtrl mmCode session : " + session.getAttribute("mmName"));
 
                params.put("state"true);
            } catch (Exception e) {
                params.put("state"false);
 
                e.printStackTrace();
            }
        }
 
        log.debug("HomeRestController mmLoginRSACtrl params : " + params);
 
        return params;
    }
 
    public String decryptRsa(PrivateKey privateKey, String securedValue) {
 
        String decryptedValue = "";
 
        log.debug("mmLoginRSACtrl decryptRsa privateKey : " + privateKey);
        log.debug("mmLoginRSACtrl decryptRsa securedValue : " + securedValue);
 
        try {
            Cipher cipher = Cipher.getInstance("RSA");
 
            // 암호화 된 값 : byte 배열
            // 이를 문자열 form으로 전송하기 위해 16진 문자열(hex)로 변경
            // 서버측에서도 값을 받을 때 hex 문자열을 받아 다시 byte 배열로 바꾼 뒤 복호화 과정을 수행
            byte[] encryptedBytes = hexToByteArray(securedValue);
 
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
 
            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
 
            // 문자 인코딩
            decryptedValue = new String(decryptedBytes, "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
 
        log.debug("mmLoginRSACtrl decryptRsa decryptedValue : " + decryptedValue);
 
        return decryptedValue;
    }
 
    // 16진 문자열을 byte 배열로 변환
    public static byte[] hexToByteArray(String hex) {
 
        if (hex == null || hex.length() % 2 != 0) {
            return new byte[] {};
        }
 
        byte[] bytes = new byte[hex.length() / 2];
 
        log.debug("mmLoginRSACtrl hexToByteArray bytes : " + bytes);
 
        for (int i = 0; i < hex.length(); i += 2) {
            byte value = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
 
            bytes[(int) Math.floor(i / 2)] = value;
        }
 
        log.debug("mmLoginRSACtrl hexToByteArray final bytes : " + bytes);
 
        return bytes;
    }
}
cs


계정 정보와 hidden 타입으로 받아온 공개키들을 복호화한다.


'BackEnd > Java' 카테고리의 다른 글

Java :: NULL vs isEmpty() 차이  (0) 2018.01.16
LinkedHashMap  (0) 2017.06.29
Java :: Spring 파일 업로드 (multipart-form/data)  (2) 2017.03.14
가변인수 (Java 5.0 이상)  (0) 2017.02.13
Java :: 열거형 (Enumeration) (Java 5.0 이상)  (0) 2017.02.13

스프링 프레임워크의 내장 객체인 CommonsMultipartResolver 를 이용한 파일 업로드 방식이다.


1. pom.xml 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 
    <!-- MultipartHttpServletRequset -->
    <dependencies>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.0.1</version>
        </dependency>
 
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.2.2</version>
        </dependency>
    </dependencies>
 
</project>
cs

pom.xml 파일에서

ㄱ. commons-io.jar

ㄴ. commons-fileupload.jar

두 가지 .jar를 추가한다.



2. context.xml 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
 
    <!-- MultipartResolver -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="100000000"/>
        <property name="maxInMemorySize" value="100000000"/>
    </bean>
 
</beans>
cs

context.xml에서 CommonsMultipartResolver 객체를 추가한다.


CommonsMultipartResolver는 스프링 프레임워크에 내장되어 있는 MultipartResolver 이다.


* maxUploadSize : 한 번에 최대 올릴 수 있는 파일 사이즈

  maxInMemorySize : 해당 경로에 최대로 저장할 수 있는 파일 사이즈



3. form 파일 업로드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
    <title>title</title>
</head>
<body>
 
    <!-- form enctype="multipart/form-data" 을 꼭 적어줘야 함 -->
    <form class="form-horizontal" method="post" action="<c:url value='/re/add'/>" enctype="multipart/form-data">
        <!-- input type="file" 이라고 꼭 저어줘야 함 -->
        <input type="file" class="form-control1" id="uploadFile" name="uploadFile" style="border:0px solid black;"/>
        
        <button type="submit" class="btn btn-default">등록</button>
        <button type="reset" class="btn btn-default">취소</button>
    </form>
 
</body>
</html>
cs

form enctype="multipart/form-data"라고 꼭 적어줘야 한다. 그렇지 않으면 form이 전송되지 않는다.

마찬가지로 전송할 파일 타입도 file이라고 적어줘야 한다.



4. MultipartResolver 클래스 작성

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
package com.cafe24.smart.util;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.util.Map;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.view.AbstractView;
 
import com.cafe24.smart.approve.domain.Draft;
import com.cafe24.smart.reward.domain.Reward;
 
// @Component > @Service
//              : 스프링 프레임워크가 관리하는 컴포넌트의 일반적 타입 
//              : 개발자가 직접 조작이 가능한 클래스의 경우 해당 어노테이션을 붙임
//              : ( <=> @Bean : 개발자가 조작이 불가능한 외부 라이브러리를 Bean으로 등록시 사용)
@Component
public class UtilFile {
    String fileName = "";
    
//  프로젝트 내 지정된 경로에 파일을 저장하는 메소드
//  DB에는 업로드된 전체 경로명으로만 지정되기 때문에(업로드한 파일 자체는 경로에 저장됨)
//  fileUpload() 메소드에서 전체 경로를 리턴받아 DB에 경로 그대로 저장   
    public String fileUpload(MultipartHttpServletRequest request,
                                        MultipartFile uploadFile, Object obj) {
        String path = "";
        String fileName = "";
        
        OutputStream out = null;
        PrintWriter printWriter = null;
        
        try {
            fileName = uploadFile.getOriginalFilename();
            byte[] bytes = uploadFile.getBytes();
            path = getSaveLocation(request, obj);
            
            System.out.println("UtilFile fileUpload fileName : " + fileName);
            System.out.println("UtilFile fileUpload uploadPath : " + path);
            
            File file = new File(path);
            
//          파일명이 중복으로 존재할 경우
            if (fileName != null && !fileName.equals("")) {
                if (file.exists()) {
//                    파일명 앞에 업로드 시간 초단위로 붙여 파일명 중복을 방지
                    fileName = System.currentTimeMillis() + "_" + fileName;
                    
                    file = new File(path + fileName);
                }
            }
            
            System.out.println("UtilFile fileUpload final fileName : " + fileName);
            System.out.println("UtilFile fileUpload file : " + file);
            
            out = new FileOutputStream(file);
            
            System.out.println("UtilFile fileUpload out : " + out);
            
            out.write(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (printWriter != null) {
                    printWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        return path + fileName;
    }
    
//  업로드 파일 저장 경로 얻는 메소드
//  업로드한 파일의 경로가 도메인 별로 달라야 했기 때문에 도메인의 형을 비교하여 파일 저장 정로를 다르게 지정함
    private String getSaveLocation(MultipartHttpServletRequest request, Object obj) {
        
        String uploadPath = request.getSession().getServletContext().getRealPath("/");
        String attachPath = "resources/files/";
        
//      Reward인 경우
        if (obj instanceof Reward) {
            attachPath += "reward/";
//      Approval인 경우
        } else if(obj instanceof Draft) {
            attachPath += "approval/";
//      Document인 경우            
        } else {
            attachPath += "document/";
        }
        
        System.out.println("UtilFile getSaveLocation path : " + uploadPath + attachPath);
        
        return uploadPath + attachPath;
    }
}
cs

시간이 촉박했기 때문에 파일의 경로를 암호화하는 UUID 객체는 사용하지 않았다.


getSaveLocation() 메소드에서 저장될 경로를 지정할 때 해당 프로젝트의 경로를 변수 uploadPath로 값 복사하였는데

로컬에서는 (본인의 컴퓨터) 에서는 아무리 해당 경로를 찾아봐도 업로드 했던 파일이 나오지 않는다.

그러나 cafe24 같은 서버에 프로젝트를 호스팅 하면 지정 경로로 업로드가 잘 되니 걱정하지 않아도 된다.



5. form에서 보낸 파일 업로드

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
@Controller
public class RewardController {
//  파일을 업로드하는 컨트롤러 클래스 메소드
    @RequestMapping(value = "re/add", method = RequestMethod.POST)
//  인자로 MulfiPartFile 객체, MultipartHttpServletRequest 객체, 업로드 하려는 도메인 클래스를 받는다
    public String reAddProCtrl(@RequestParam("uploadFile") MultipartFile uploadFile,
                                    MultipartHttpServletRequest request, Reward reward) {
        
        System.out.println("RewardController reAddProCtrl uploadFile : " + uploadFile);
        System.out.println("RewardController reAddProCtrl reward : " + reward);
        
//      UtilFile 객체 생성
        UtilFile utilFile = new UtilFile();
        
//      파일 업로드 결과값을 path로 받아온다(이미 fileUpload() 메소드에서 해당 경로에 업로드는 끝났음)
        String uploadPath = utilFile.fileUpload(request, uploadFile, reward);
        
//      해당 경로만 받아 db에 저장
        int n = rewardService.reAddServ(uploadPath, reward);
        
        System.out.println("RewardController reAddProCtrl n : " + n);
        System.out.println("RewardController reAddProCtrl uploadPath : " + uploadPath);
        
        return "redirect:listAll";
    }
}
cs

이로서 업로드가 끝난다.

'BackEnd > Java' 카테고리의 다른 글

LinkedHashMap  (0) 2017.06.29
RSA 암호화 구현  (0) 2017.03.14
가변인수 (Java 5.0 이상)  (0) 2017.02.13
Java :: 열거형 (Enumeration) (Java 5.0 이상)  (0) 2017.02.13
초기화 블록 (Initialization block)  (0) 2017.02.13

1. 정의

필요에 따라 인자(매개변수) 갯수를 가변적으로 조정할 수 있는 기술.

가변인수가 없던 때에는 Collection이나 Array를 이용하여 가변인수를 대체하여 사용했었다.



2. 원리
인자로 ...이라는 표시를 해두면 컴파일러가 배열 형식으로 바꿔 인식한다.

그리고 인자로 주어지는 가변인수들을 모아 배열 객체로 만들어 처리한다.


1
2
3
4
5
// 원본
public static void display (String... str) {
 
// 컴파일러 변환 
public static void display (String as[]) {
cs



3. 예제

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
import java.util.Vector;
 
public class VarArgs {
    public static void display (Vector v) {
        for (Object s : v) {
            System.out.println("컬렉션 형태 : " + s);
        }
    }
    
//  가변인수를 받는 메소드
    public static void display (String... str) {
        for (String s : str) {
            System.out.println("가변 배열 형태 : " + s);
        }
    }
}
 
import java.util.Vector;
 
public class VarArgsMain {
    public static void main(String[] args) {
        VarArgs varArgs = new VarArgs();
        Vector vector = new Vector();
        
        vector.add("Hello");
        vector.add("World");
        vector.add("Korea");
        
        varArgs.display(vector);
//      인자로 여러 개를 입력
        varArgs.display("Hello""World""Korea");
    }
}
cs


'BackEnd > Java' 카테고리의 다른 글

RSA 암호화 구현  (0) 2017.03.14
Java :: Spring 파일 업로드 (multipart-form/data)  (2) 2017.03.14
Java :: 열거형 (Enumeration) (Java 5.0 이상)  (0) 2017.02.13
초기화 블록 (Initialization block)  (0) 2017.02.13
AWT vs 스윙 (Swing)  (0) 2017.02.10

1. 정의

final과 같이 자바에서 상수값을 만드는 방법 중 하나이다.


enum은 관련 있는 상수들의 집합이며, 해당 클래스가 상수만으로 작성되어 있을 경우 반드시 class로 선언할 필요 없이 enum으로 선언하면 된다.


일반적인 데이터 타입의 경우 변수를 만든 뒤 형에 맞는 아무 값이나 넣을 수 있으나, 열거형 타입으로 변수를 선언하는 것은 일반 데이터 타입과는 동일하지만 열거형을 선언할 때 명시한 값을 넣을 수 있다.


열거형을 사용하는 가장 큰 이유는 숫자 상수를 사용하는 것보다 열거형 상수를 사용하는 것이 훨씬 직관적이기 때문이다.

열거형은 상수를 묶어서 관리할 수 있다는 큰 장점도 가지고 있고 상수는 상수 자체가 형이 아니기 때문에 형 안정성을 보장할 수 없었으나 자바 5.0 이후로는 열거형을 통해 상수의 형 안정성을 보장하고 있다.



2. 특징

순번

특징

 1

 코드가 단순하고, 가독성이 높음

 2

 인스턴스를 생성할 수 있고 상속을 방지할 수 있음

 3

 enum이라는 키워드를 사용하기 때문에 의도가 분명하게 나타남



3. 예제

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
import java.util.HashMap;
import java.util.Map;
 
public enum Sample {
    PASSED(1"Passed""The test has passed."),
    FAILED(-1"Failed""The test was executed but failed."),
    DID_NOT_RUN(0"Did not run""The test did not start.");
    
//  변수, Map 생성
    int code;
    String label;
    String description;
    static Map<Integer, Sample> map;
    
//  생성자 메소드로 변수들 초기화
    private Sample (int code, String label, String description) {
        this.code = code;
        this.label = label;
        this.description = description;
        
        System.out.println("console> code : " + code);
        System.out.println("console> label : " + label);
        System.out.println("console> description : " + description);
    }
    
//  정수를 넣으면 해당하는 상수 값을 Sample 클래스에 담아 리턴하는 메소드
    public static Sample getStatus(int i) {
//      main 메소드에서 getStatus() 호출하는 경우 Map에 들어있는게 없기 때문에
//      initMapping() 메소드를 무조건 호출하게 됨
        if (map == null) {
            initMapping();
        }
        
        System.out.println("console> getStatus get(i)) : " + map.get(i));
        
        return map.get(i);
    }
    
    private static void initMapping() {
        map = new HashMap<Integer, Sample>();
        
//      Sample 클래스에 선언되어 있는 상수들을 모두 돌림
        for (Sample sample : values()) {
            map.put(sample.code, sample);
            
            System.out.println("console> map.get = " + sample);
        }
    }
    
    public int getCode() {
        return code;
    }
    
    public String getLabel() {
        return label;
    }
    
    public String getDescription() {
        return description;
    }
    
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        
        sb.append("Status");
        sb.append("{code = ").append(code);
        sb.append(", label = '").append(label).append('\'');
        sb.append(", description = '").append(description).append('\'');
        sb.append('}');
        
        return sb.toString();
    }
    
    public static void main(String[] args) {
        System.out.println(Sample.PASSED);
        System.out.println(Sample.getStatus(-1));
    }
}
cs


1. 정의

클래스의 초기화와 관련된 작업을 수행하고, 생성자보다 먼저 호출되며 클래스 초기화 블록과 인스턴스 초기화 블록으로 구분된다.

 

 

2. 분류

분류 

설명 

 클래스 초기화 블록

 클래스 파일이 JVM에 로드 되는 시점에 실행됨

 인스턴스 초기화 블록

 클래스의 인스턴스가 생성되고, 생성자가 호출되기 전에 실행됨

 

 

3. 예제

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
public class Hello {
    
    //class initialization block
    static {
        System.out.println("클래스 초기화 블록");
    }
    
    //instance initialization block
    {
        System.out.println("인스턴스 초기화 블록");
    }
    
    //default constructor
    Hello () {
        System.out.println("디폴트 생성자");
    }
    
    Hello (int value) {
        System.out.println("생성자 오버로딩");
    }
}
 
public class HelloMain {
    final static boolean LOG_ENABLE = false;
    
    public static void main(String[] args) {
        Hello hello = new Hello();
        Hello hello2 = new Hello(0);
    }
}
cs

 

 


'BackEnd > Java' 카테고리의 다른 글

가변인수 (Java 5.0 이상)  (0) 2017.02.13
Java :: 열거형 (Enumeration) (Java 5.0 이상)  (0) 2017.02.13
AWT vs 스윙 (Swing)  (0) 2017.02.10
람다식과 함수형 프로그래밍 (Java 8.0 이상)  (0) 2017.02.09
자바 기본 정리  (0) 2017.02.09

자바에서 사용 가능한 GUI (Graphic User Interface) 객체의 종류로는 두 가지를 꼽을 수 있는데, AWT와 스윙이다.

GUI는 입출력과 결과값 리턴을 Command 창에서만 할 수 있었으나 하나의 윈도우 창을 띄운 뒤 버튼과 라벨 등으로 꾸밀 수 있는 것을 말한다.

 

 

1. AWT (Abstract Window Toolkit)

해당 시스템에서 실행될 시 해당 컴포넌트를 사용하는 GUI이다.

자바 시스템에서 실행된다고 할 경우에는 자바 컴포넌트를 사용하기 때문에 여러 컴포넌트에서 AWT를 사용한다고 할 경우

실행하는 컴포넌트의 모습이 서로 달라 일관된 화면을 제공하는 데에 어려움이 따른다. 자바 초기 버전에 제공되던 GUI이다.

 

 

2. 스윙 (Swing)

자바에서 컴포넌트를 작성시 화면이 일관되게 나오는 GUI이다.

AWT로 GUI를 사용했던 시절에는 버튼의 롤오버나 풍선말 등을 AWT로 구현하지 못했기 때문에

가장 큰 단계의 컨테이너만을 운영체제(AWT) 로 지원하고, 그 하부의 컴포넌트들은 직접 자바 코드로 그렸었다.

그러한 불편함을 해소하기 위해 등장한 GUI가 스윙이며, 스윙은 운영체제가 대신 해주던 일을

JVM (Java Virtual Machine)이 대신 해줘야 하기 때문에 AWT와는 확연히 다른 구조를 가지며 주로 GUI 구성에서 많이 사용된다.

화면이 일관적이기 때문에 현재는 스윙 사용을 권장하는 편이다.

 

스윙의 컴포넌트는 대부분이 javax.swing.JComponent 클래스로부터의 상속을 받아 구현한다.

(AWT와 구분을 하기 위해 클래스명 맨 앞에 J~가 붙음)

 

* Container > JComponent > Swing


* Swing 특징

순번

 특징

 1

 더블 버퍼링 지원

 2

 각 컴포넌트의 속성값 설정 가능

 3

 컴포턴트에 투명 효과를 줄 수 있음

 4

 Tool tip / border 지원

 

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
import java.awt.Color;
 
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
 
//JFrame을 상속받는 Pizza 클래스
public class Pizza extends JFrame {
    
//  Constructor method
    public Pizza() {
//      frame size settting
        setSize(500150);
//      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); : 메인 프레임 닫을 때,
//                                                프로그램 또한 안정적으로 종료됨
//                                                지정하지 않을 시 메인프로그램은 닫혀도
//                                                JFrame 객체는 JVM에서 계속 살아 있음
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//      frame title setting
        setTitle("Frame Test");
        
//      panel 객체 생성
        JPanel panel = new JPanel();
        JPanel panel1 = new JPanel();
        JPanel panel2 = new JPanel();
        
//      panel 색상 설정
        panel.setBackground(Color.BLUE);
        panel1.setBackground(Color.CYAN);
        panel2.setBackground(Color.GREEN);
        
//      라벨 객체 생성, panel1에 라벨 추가
        JLabel label = new JLabel("피자 종류를 선택하세요.");
        
        panel1.add(label);
        
//      버튼 객체 생성
        JButton button1 = new JButton("치즈");
        JButton button2 = new JButton("불고기");
        JButton button3 = new JButton("포테이토");
        
//      panel2에 button1, 2, 3 추가
        panel2.add(button1);
        panel2.add(button2);
        panel2.add(button3);
        
//      panel에 panel1, panel2 추가
        panel.add(panel1);
        panel.add(panel2);
        
//      프레임에 panel 추가
        add(panel);
        
        setVisible(true);
    }
}
 
 
public class PizzaMain {
    public static void main(String[] args) {
        Pizza pizza = new Pizza();
    }
}
cs

 

 

 

3. AWT vs 스윙 비교

 특징

AWT 

Swing 

 무게

 중량 컴포넌트

 (컴포넌트를 운영체제의 GUI와 연결시키므로,

운영체제에 따라 다른 모양과 배치를 띔)

 경량 컴포넌트

 (직접 컴포넌트를 만들어 구현함)

 패키지

 java.awt.*;

javax.swing.*;

 장점

 각 운영체제에서의 구현이 쉬움

 운영체제간 버그를 자동적으로 해결함


+ Recent posts