반응형

주요 단축키

꼭 디버깅이 아니더라도 아래 단축키 정도는 익혀두면 여러모로 쓸 곳이 많다.

  • shift + shift : 전체 검색 (file, action 모두 검색)

  • command + shift + a : action 검색 (설정, Refactor 등)

  • command + shift + o : 파일 찾기

  • command + e : 최근 열었던 파일 리스트 보기

  • command+[ / command+] : 이전/다음 포커스로 이동

단축키 팁

빠른 디버깅을 위해서 단축키를 익혀놓으면 편하다.
하지만 하나하나 찾아서 하기는 귀찮으니, 아래 처럼 key promoter 플러그인을 설치하자.

설치하면 내가 클릭한 기능의 단축키를 바로 보여준다.

단축키를 확인 한 후, 다음부터는 단축키로 명령을 실행하자.

Break Point

습관적으로 run을 실행하지만, 로컬 개발에서는 웬만하면 debug로 실행하자.
어플리케이션 구동 중에 Break Point를 사용하려면 debug여야만 한다.

코드 좌측의 여백을 우클릭하면 Show Line Numbers가 나오는데 이를 체크하면 아래처럼 코드 라인에 숫자가 부여된다.
에러가 발생했을 때, 몇 번째 Line에서 발생했는지 log에 표시되는데, 저 표시가 없으면 개발자가 보기 힘드니 미리 체크해놓자.

라인 넘버와 코드 사이의 여백을 클릭하면 아래처럼 그 Line에 Break Point가 표시 된다.

어플리케이션이 debug 모드일때, 해당 Break Point가 지정된 라인이 실행되는 시점에서 멈추게 된다.
이때 여러 값을 확인하고, 명령어를 실행하면서 디버깅을 시작하면 된다.
추가로 여기서 Break Point를 우클릭 하면 조건으로 break를 걸 수가 있다.
(굉장히 중요함)

(productId가 2L일때만 위 point에서 break가 되도록 지정)

특히나 for, while등의 반복적인 행위속에, 특정값이 들어올때만 break를 하고 싶은 때가 있다.
이때 정말 유용하게 사용할 수 있는 기능이다.

디버깅 버튼

위 내용을 통해 break를 걸게 되면, 아래처럼 여러 버튼을 사용할 수 있다.

(break가 된 상태에서만 사용 가능하다)

resume

  • 단축키 : option+command+r
  • 기능 : 다음 break point로 이동

첫번째 break point에서

다음 break point로 이동한다.

이클립스의 resume(F8)과 동일한 기능이다.

step 버튼들은 왼쪽에서 오른쪽 순으로 하겠다.

step over

  • 단축키 : F8
  • 기능 : 현재 break 된 파일에서 다음 라인 이동

break 걸린 라인을 전부 실행 후, 다음라인으로 이동한다.

이 기능을 몰라서 라인 하나하나에 전부 break를 걸었다면, F8을 사용하자.

step into

  • 단축키 : F7
  • 기능 : 현재 break 된 라인에서 실행하고 있는 라인으로 이동한다.

break 걸린 라인에서 다음 실행될 코드는 PurchaseOrder.createOrder다.
이때 F7을 누르면

이렇게 createOrder로 이동한다.

Force step into

  • 단축키 : option+shift+F7
  • 기능 : 다음 실행되는 라인으로 이동하나, step into와 달리 Stepping을 무시하고 진행한다.

예를 들어, command+shift+a -> stepping으로 설정창을 오픈후,

아래처럼 skip simple getters를 체크 하자.

이렇게 한 뒤, getter 메소드를 호출하는 곳에 break point를 지정한 후,

번갈아가며 step into와 force step into를 입력해보자.

step into(F7)

이렇게 메소드 invoke 영역으로 바로 이동한다.

반면에

force step into(option+shift+F7)

설정과 무관하게 getter 메소드까지 이동한다.
굳이 확인이 불필요한 getter, 생성자 등에 skip 옵션을 설정한 뒤, skip이 필요할때는 step into로, 전부 확인이 필요하다면 force step into로 이동하면서 디버깅 하면 된다.

Step out

  • 단축키 : shift+F8
  • 기능 : 현재 break 된 라인에서 호출한 곳으로 이동한다.

step into 로 createOrder 메소드로 이동 후,

step out의 단축키인 shift+F8을 입력하면,

메소드를 빠져나오게 된다.

보통은 step into로 파고들어간 라인을 빠져나오려할때 많이 사용한다.

Drop Frame

  • 단축키 : 없음
  • 기능 : call stack을 거슬러 올라간다.

이것만 보면 step out과 별차이 없어 보이는데, 큰 차이점은 step out은 해당 라인이 실행된 후 돌아가지만, drop frame은 해당 라인이 실행되기 전에 돌아간다.

Run to Cursor

  • 단축키 : option+F9
  • 기능 : 포커스 되어있는 라인으로 이동

위 처럼 break된 상태에서 다음에 실행될 코드 중, ApplicationTests의 assert에 포커스를 두고,

(51라인에 포커스를 두었다.)

여기서 option+F9를 누르면

이렇게 포커스가 지정된 51라인으로 break가 이동한다.

보통 break point로 지정하지 않고, 단발성으로 break를 걸고 싶을때 사용한다.

Watch와 Call Stack

break point와 디버그 버튼들로 break line을 사용하는 법과 이동하는 법을 배웠다.
이제는 break된 라인에서 어떤 일이 가능한지 확인하겠다.

Evaluate

break 된 상태에서 코드를 실행할 수 있는 방법은 2가지가 있다.
Evaluate Watch인데, 둘 중에 한가지만 사용하기 보다는 둘다 인지하고 그때그때 상황에 따라 사용하면 될것 같다.

디버깅 버튼들 가장 우측(run to cursor 우측)에 계산기처럼 되어 있는 버튼이 Evaluate 버튼이다.

  • 단축키 : option+F8
  • 기능 : break 된 라인에서 사용가능한 모든 코드를 실행할 수 있음

Evaluate를 클릭하면 아래와 같이 팝업이 하나 등장하는데, 여기서 확인하고 싶은 코드를 입력하고 실행시키면 결과를 바로 확인할 수 있다.

모든 코드를 사용할수는 없다.
현재 라인에서 사용 가능한 코드 (메소드 변수, 클래스 필드 등)만 사용할 수 있다.

Watch

바로 위 Evaluate와 거의 동일한 기능이다.
사용법은 간단하다.
먼저 디버그 모드에서 아래처럼 watch를 활성화 시키자.

위 클릭을 따라가면 아래와 같은 형태가 된다.

우측 하단에 있는 watches에서 우리는 마음껏 디버깅 코드를 작성해보면 된다.
예를 들어, purchaseOrderRepository.save 기능이 잘 수행되었는지 확인하기 위해, 아래와 같이 확인해볼 수 있다.

해당 라인에서 가능한 모든 값, 메소드를 사용할 수 있다.
단순한 변수값부터 시작해서 Autowired된 코드까지 전부 사용가능하다.

Evaluate vs Watch

2개의 기능은 동일하다.
단, Evaluate의 경우 코드를 계속 수동 실행해야하지만, Watch의 경우 삭제하지 않는한, break line이 실행될때마다 자동 실행된다.
값을 변경하는 코드를 watch에 등록했다면 의도치 않게 값이 변경되서 다른 로직을 수행하게 되는 경우도 있으니 주의하자.
하지만 Watch는 여러 디버깅 코드의 결과를 동시에 확인이 가능하고, 반복적으로 디버깅 코드를 사용할 필요 없어서 개인적으로 더 선호한다.

evaluate를 선호하는 분들의 경우 단축키 때문에 많이들 사용한다.
watch는 기본 단축키가 없어 마우스를 사용하거나, 단축키를 추가해야하는데 evaluate는 option+F8으로 열고 바로 코드 작성 후 실행하면 되서 사용하기 편하다

Call Stack

디버깅 화면의 좌측 하단에는 해당 break line에 오기까지의 call stack 이 출력된다.
이를 통해 이전에 어떤 값들이 넘어 온것인지, 이전에 다른 연산을 했으면 어떻게 값이 바뀔지를 모두 확인할 수 있다.

예를 들어, order 메소드를 호출한 test 코드의 값을 확인하고 몇가지 연산식을 사용한다고 하자.

좌측 하단의 Call Stack에서 찾고자 하는 코드라인을 클릭하면 해당 라인으로 이동하게 된다.

이동후에는, break line에서 했던것 처럼 Variables와 Watches를 이용해 확인하고자 하는 값과 코드를 확인하면 된다.

특히 Spring과 같은 프레임워크에서 어떻게 코드가 실행되고 값이 변경되는지 확인할때 굉장히 유용하게 사용된다.

 

 

출처 : https://jojoldu.tistory.com/149

반응형
반응형

Oracle / Tibero


 select * from tableName where rownum = 1;


Mssql


 select top 1 * from tableName;


Informix


 select first 1 * from tableName;


Sybase


 select * from tableName where rowid(tableName) between 1 and 1;


Altibase / MySQL


 select * from tableName limit 1;


DB2


 select * from tableName fetch first 1 row only;



출처: http://extremeblue.tistory.com/12 [extremeblue's blues]

반응형
반응형

1. JUnit이란?

JUnit은 단위 테스트 도구입니다. 외부 테스트 프로그램(케이스)을 작성하여 System.out으로 번거롭게 디버깅하지 않아도 됩니다. 프로그램 테스트 시 걸릴 시간도 관리할 수 있게 해주며 오픈 소스이며, 플러그인 형태로 Eclipse에 포함되어 있습니다. 하나의 jar 파일이 전부이며 사용법도 간단합니다. 어느 정도 개발이 진행되면 프로그램에 대한 단위 테스트는 반드시 수행해야 합니다. JUnit은 보이지 않고 숨겨진 단위 테스트를 끌어내어 정형화시켜 단위 테스트를 쉽게 해주는 테스트용 Framework 입니다. JDK 1.4에서 추가된 assertXXX를 사용하여 Test를 진행합니다. JUnit은 테스트 결과를 확인하는 것 이외 최적화된 코드를 유추해내는 기능도 제공합니다. 또한, 테스트 결과를 단순한 텍스트로 남기는 것이 아니라 Test클래스로 남깁니다. 그래서 개발자에게 테스트 방법 및 클래스의 History를 넘겨줄 수도 있습니다.



2. 특징

  • 단위 테스트 Framework 중 하나
  • 문자 혹은 GUI 기반으로 실행됨
  • 단정문으로 테스트 케이스의 수행 결과를 판별함(assertEquals(예상 값, 실제 값))
  • 어노테이션으로 간결하게 지원함
  • 결과는 성공(녹색), 실패(붉은색) 중 하나로 표시


3. Eclipse에 JUnit 설정하기

  • Java Project를 생성
  • Project 이름에서 오른쪽 마우스를 클릭하고 Properties를 선택
  • Java BuildPath를 선택
  • Libraries 탭을 선택하고, Add Library를 선택
  • < 그림 1 >

    JUnit을 선택하고, Next 버튼 선택

    < 그림 2 >

    버전을 선택하고, Finish 버튼 선택

    < 그림 3 >

    JUnit이 추가 된 사항을 확인 할 수 있습니다.

    < 그림 4 >

    그림 4까지 하셨으면 Eclipse에 JUnit이 설정되었습니다. 지금부터는 간단한 Calculator 클래스를 통해 Eclipse에서 어떻게 JUnit을 사용하는지 알아봅니다.


4. JUnit 사용하기

먼저 com.calculator 패키지를 만들고 Calculator 클래스를 만듭니다. 메소드는 int형 두 개의 파라미터를 받아 두 개의 합을 리턴 하는 간단한 sum 메소드 입니다. 소스는 다음과 같습니다.

package com.calculator;

public class Calculator {  
    public int sum(int num1, int num2){
        return num1 + num2;
    }
}

Calculator 클래스를 테스트하기 위한 테스트 클래스를 만들겠습니다. 먼저 com. Calculator.test 패키지를 만듭니다. 그리고 com. Calculator.test 패키지에서 마우스 오른쪽 버튼 -> New -> JUnit Test Case 선택

< 그림 5 >

아래 그림에서

①은 테스트 클래스 이름 입력 부분이고, 테스트 클래스 이름은 테스트할 클래스 이름에 Test를 추가해서 입력합니다.

②는 테스트 클래스가 테스트하게 될 클래스를 지정하는 부분입니다.

Next 버튼 선택,

< 그림 6 >

Calculator 클래스에서 테스트할 메소드를 선택하고 Finish 버튼을 선택,

< 그림 7 >

다음과 같은 테스트 코드가 생성됩니다.

package com.calculator.test;  
import static org.junit.Assert.*;  
import org.junit.Test;  
public class CalculatorTest {  
    @Test
    public void testSum() {
        fail("Not yet implemented");
    }
}

위 testSum 메소드를 보면 어노테이션으로 @Test가 선언된 것을 볼 수 있습니다. 이 의미는 testSum 메소드가 단위 테스트 메소드임을 지정한다는 것입니다. 그리고 이 testSum 메소드 안에서 Calculator 클래스의 Sum메소드를 테스트합니다.

testSum 메소드를 다음과 같이 수정하였습니다.

    @Test
    public void testSum() {
        Calculator calculator = new Calculator();
        assertEquals(30, calculator.sum(10, 20));
    }

위 소스를 보시면 Calculator 클래스 객체를 생성한 후 assertEquals 메소드를 사용하여 sum 메소드의 리턴 값이 제대로 반환되는지 확인합니다. 반환 값이 지정한 결과와 같으면 테스트는 성공입니다. 하지만 반환 값이 생각한 결과와 같지 않으면 실패입니다.

테스트 클래스를 실행하는 방법은 테스트 클래스에서 마우스 오른쪽 버튼 -> RunAs -> JUnit Test를 선택

< 그림 8 >

테스트 메소드가 아무런 문제 없이 성공되면 그림 9와 같습니다.

< 그림 9 >

테스트 메소드에 문제가 있어 실패하면 그림 10과 같습니다.

< 그림 10 >

지금까지 간단한 Calculator 클래스를 통해 JUnit에 대해 알아보았습니다. 예제 소스에서 assertEquals 메소드를 보셨을 겁니다. 이 메소드는 JUnit에서 가장 많이 사용되는 메소드입니다. 이외에도 유용한 메소드가 있으며 이러한 메소드를 단정문 이라고도 합니다. 단정문에 해당하는 메소드는 많이 있으며 대표적인 것에 대해 알아보겠습니다.

5. 대표적인 단정문

assertArrayEquals(a,b) : 배열 a와b가 일치함을 확인 
assertEquals(a,b) : 객체 a와b의 값이 같은지 확인 
assertSame(a,b) : 객체 a와b가 같은 객체임을 확인 
assertTrue(a) : a가 참인지 확인 
assertNotNull(a) : a객체가 null이 아님을 확인 
이외에도 다양한 단정문이 존재합니다. 자세한 내용은 아래 링크를 가시면 확인하실 수 있습니다.http://junit.sourceforge.net/javadoc/org/junit/Assert.html

6. 어노테이션 활용하기

Java 언어 자체가 좀 더 넓게 확장하고 다양하게 발전할 수 있도록 도와준 어노테이션을 JUnit에서 활용할 수 있습니다.

(1) 테스트 메소드 지정하기

@Test가 메소드 위에 선언되면 이 메소드는 테스트 대상 메소드임을 의미합니다.

    @Test
    public void testSum() { 

    }

(2) 테스트 메소드 수행시간 제한하기

@Test(timeout=5000)를 메소드 위에 선언합니다. 시간단위는 밀리 초 입니다. 이 테스트 메소드가 결과를 반환하는데 5,000밀리 초를 넘긴다면 이 테스트는 실패입니다.

    @Test(timeout=5000)
    public void testSum() { 

    }

(3) 테스트 메소드 Exception 지정하기

@Test(expected=RuntimeException.class)가 메소드 위에 선언되면 이 테스트 메소드는 RuntimeException이 발생해야 테스트가 성공, 그렇지 않으면 실패입니다.

    @Test(expected=RuntimeException.class)
    public void testSum() { 

    }

(4) 초기화 및 해제

@BeforeClass, @AfterClass가 메소드 위에 선언되면 해당 테스트 클래스에서 딱 한 번씩만 수행되도록 지정하는 어노테이션 입니다.

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {

    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {

    }

@Before, @After가 메소드 위에 선언되면 해당 테스트 클래스 안에 메소드들이 테스트 되기 전과 후에 각각 실행되게 지정하는 어노테이션 입니다.

    @Before
    public void setUp() throws Exception {

    }

    @After
    public void tearDown() throws Exception {

    }

아래 그림과 같이 표현할 수 있습니다.

< 그림 11 >

@BeforeClass 어노테이션은 테스트 클래스 수행 시 단위 테스트 메소드 보다 먼저 딱 한 번 수행되어야 할 경우 지정합니다. 예를 들어 DB 연결 시 드라이버 로딩 부분을 @BeforeClass로 지정한 메소드에 작성합니다. 반대로 @AfterClass 어노테이션은 단위 테스트 함수들이 다 수행되고 맨 마지막에 수행되어야 할 경우 지정합니다. 예를 들어 DB 연결 후 마지막에 드라이버를 반납하는 부분을 @AfterClass로 지정한 메소드에 작성합니다.

@Before와@After는 @Test로 지정된 단위 테스트 메소드가 실행되기 전 한 번씩 수행하거나 실행되고 난 후 수행되어야 하는 부분을 작성합니다. 예를 들어 공통으로 객체를 생성하는 코드를 @Test로 지정된 단위 테스트 메소드 안에 있다면 그 부분을 @Before에 지정된 메소드에 작성합니다.

7. 마치며...

지금까지 단위 테스트 Framework인 JUnit에 대해 알아보았습니다. JUnit을 사용하여 테스트 클래스를 작성하는 데 있어 깊은 내용은 아니지만, Eclipse에서 설치 방법과 어떻게 사용하는지를 살펴 보았습니다. JUnit을 처음 접하시는 분들께 작은 도움이나마 되었으면 하는 바램입니다.

감사합니다.

참고 도서 및 사이트



반응형
반응형

CryptoJS 를 이용한 암호화


CryptoJS 를 사용하면 웹에서도 손쉽게 AES 암호화가 가능하다.


자바스크립트에서 AES 암호화 예 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
<script    src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
<script    src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/sha256.js"></script>
<script>
    
//메시지 암호화
    var message = "Led Zeppelin- Stairway to Heaven"
    var passphrase = "1234";
    var encrypt = CryptoJS.AES.encrypt(message, passphrase);
    var decrypted = CryptoJS.AES.decrypt(encrypt, passphrase );
 
    // 암호화 이전의 문자열은 toString 함수를 사용하여 추출할 수 있다.
    var text = decrypted.toString(CryptoJS.enc.Utf8);

//파일 암호화
var formData = new FormData();
var image = $("#img_1").attr('src');
var key = "${map.key}"; //암호화 key
var encrypt = CryptoJS.AES.encrypt(image, key+"<%=session.getId()%>",{ //암호화키 + 세션ID mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }).toString(); formData.append("attach1", encrypt);
//AJAX
$.ajax({ .... });
</script>
cs



자바스크립트에서 AES 암호화 예 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script    src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
<script    src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/sha256.js"></script>
<script>
    
    var message = "Led Zeppelin- Stairway to Heaven"
 
    // 생성된 해쉬 값을 사용하면 평문 사용을 피할 수 있다.
    var hashedPassword = CryptoJS.SHA256("1234").toString() ; 
 
    var encrypt = CryptoJS.AES.encrypt(message, hashedPassword);
    var decrypted = CryptoJS.AES.decrypt(encrypt, hashedPassword );
 
    // 암호화 이전의 문자열은 toString 함수를 사용하여 추출할 수 있다.
    var text = decrypted.toString(CryptoJS.enc.Utf8);
</script>
cs


텍스트 형식의 비밀번호를 사용하고 싶지 않는 경우는 SHA-256 같은 단방향 해쉬 알고리즘을 사용 (암호화된 메시지) 다이제스트를 생성하여 사용할 수 도 있다. 대부분의 웹 프로그램들은 보통 텍스트 형식의 비밀번호를 추론하기 어렵게 단방향으로 암호화하여 비밀번호를 보호하고 있다. (원본 메시지를 알면 암호화된 메시지를 구하기는 쉽지만 암호화된 메시지로는 원본 메시지를 구할 수 없어야 하며 이를 '단방향성'이라고 한다.) 


자바에서 원본 메시지를 구하기 위해서는 CryptoJS 내부적으로 키생성을 위하여 내부적으로 사용하는 비표준 OpenSSL KDF 를 사용함을 알고 있어야 한다. (사실 이미 관련 자바 코드가 공개되어 있어 몰라도 상관없다.) 


주의할 것은 자바에서는 미국 이외의 국가에는 암호화 관련 제약(AES에서 128bit(16byte)를 초과하는 길이의 key 사용불가)이 있으며 별도의 확장 패키지(JCE Unlimited Strength Jurisdiction Policy)를 설치를 통하여 해지가 가능하다. 참고로 SHA-256를 사용하지 않고 PBKDF2 키를 사용하면 확장 패키지 설치 없이 암호화 및 복호화가 가능하다. 

Java SE Downloads 



자바에서 복호화


다음은 자바에서 암호화된 메시지를 복호화하는 과정이다. 




복호화 예제

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
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
 
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
 
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
 
public class CryptoTest {
    
    public static void main(String[] args) throws UnsupportedEncodingException, GeneralSecurityException, DecoderException {        
        String ciphertext = "U2FsdGVkX18+s94R2PY8hZ+HLrPiucqI33KNQHmkAvES41hGlh1HLgfTwGbwc9dkSid4RW5xj5yBn9hj0y7y0A==";
        String password = "1234";
        System.out.println ( decrypt(ciphertext, password) );
       }
    
    public static String decrypt(String ciphertext, String passphrase) {
        try {
            final int keySize = 256;
            final int ivSize = 128;
 
            // 텍스트를 BASE64 형식으로 디코드 한다.
            byte[] ctBytes = Base64.decodeBase64(ciphertext.getBytes("UTF-8"));
 
            // 솔트를 구한다. (생략된 8비트는 Salted__ 시작되는 문자열이다.) 
            byte[] saltBytes = Arrays.copyOfRange(ctBytes, 816);
            System.out.println( Hex.encodeHexString(saltBytes) );
            
            // 암호화된 테스트를 구한다.( 솔트값 이후가 암호화된 텍스트 값이다.)
            byte[] ciphertextBytes = Arrays.copyOfRange(ctBytes, 16, ctBytes.length);
                       
            // 비밀번호와 솔트에서 키와 IV값을 가져온다.
            byte[] key = new byte[keySize / 8];
            byte[] iv = new byte[ivSize / 8];
            EvpKDF(passphrase.getBytes("UTF-8"), keySize, ivSize, saltBytes, key, iv);
            
            // 복호화 
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
            byte[] recoveredPlaintextBytes = cipher.doFinal(ciphertextBytes);
 
            return new String(recoveredPlaintextBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
 
        return null;
    }
    
    private static byte[] EvpKDF(byte[] password, int keySize, int ivSize, byte[] salt, byte[] resultKey, byte[] resultIv) throws NoSuchAlgorithmException {
        return EvpKDF(password, keySize, ivSize, salt, 1"MD5", resultKey, resultIv);
    }
 
    private static byte[] EvpKDF(byte[] password, int keySize, int ivSize, byte[] salt, int iterations, String hashAlgorithm, byte[] resultKey, byte[] resultIv) throws NoSuchAlgorithmException {
        keySize = keySize / 32;
        ivSize = ivSize / 32;
        int targetKeySize = keySize + ivSize;
        byte[] derivedBytes = new byte[targetKeySize * 4];
        int numberOfDerivedWords = 0;
        byte[] block = null;
        MessageDigest hasher = MessageDigest.getInstance(hashAlgorithm);
        while (numberOfDerivedWords < targetKeySize) {
            if (block != null) {
                hasher.update(block);
            }
            hasher.update(password);            
            // Salting 
            block = hasher.digest(salt);
            hasher.reset();
            // Iterations : 키 스트레칭(key stretching)  
            for (int i = 1; i < iterations; i++) {
                block = hasher.digest(block);
                hasher.reset();
            }
            System.arraycopy(block, 0, derivedBytes, numberOfDerivedWords * 4, Math.min(block.length, (targetKeySize - numberOfDerivedWords) * 4));
            numberOfDerivedWords += block.length / 4;
        }
        System.arraycopy(derivedBytes, 0, resultKey, 0, keySize * 4);
        System.arraycopy(derivedBytes, keySize * 4, resultIv, 0, ivSize * 4);
        return derivedBytes; // key + iv
    }    
}
cs



AES256 복호화의 경우 java 버전마다 깔린곳에 외부 라이브러리를 덮어쓰기 해야한다. 보통 업체쪽에서는 원하지 않고 꺼려하므로 bouncycastle 라이브러리를 사용하여 복호화 하는 방법이 있다. 추후 소스 올릴 예정이다. ^^ 


출처 및 참고 : http://andang72.blogspot.com/2016/12/blog-post.html

반응형
반응형

요즘 이미지 해상도가 매우 높아져서 이미지사이즈가 매우 크다. 파일업로드를 수행하는 서버에서는 네트워크 트래픽에 부하가 많이 발생하므로 이를 줄이는 방법 중 하나는 클라이언트에서 사이즈를 줄이는 방법이 있다.



HTML5 canvas로 이미지를 받을 경우 javascript에서 처리하는 방법이다.


1. 공통.js 나 해당 javascript 안에 선언


(function () {

    var matched, browser;

 

    // Use of jQuery.browser is frowned upon.

    // More details: http://api.jquery.com/jQuery.browser

    // jQuery.uaMatch maintained for back-compat

    jQuery.uaMatch = function (ua) {

        ua = ua.toLowerCase();

 

        var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||

            /(webkit)[ \/]([\w.]+)/.exec(ua) ||

            /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||

            /(msie) ([\w.]+)/.exec(ua) ||

            ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||

            [];

 

        return {

            browser: match[1] || "",

            version: match[2] || "0"

        };

    };

 

    matched = jQuery.uaMatch(navigator.userAgent);

    browser = {};

 

    if (matched.browser) {

        browser[matched.browser] = true;

        browser.version = matched.version;

    }

 

    // Chrome is Webkit, but Webkit is also Safari.

    if (browser.chrome) {

        browser.webkit = true;

    } else if (browser.webkit) {

        browser.safari = true;

    }

 

    jQuery.browser = browser;

})();



// html5의 Canvas를 사용해서 이미지 리사이징이 가능한지 판별하는 메소드 function isImageResizable() { var isCanvasUsable = !!document.createElement('canvas').getContext; var isLowerIE = false; if($.browser.msie) { if($.browser.version < 10) { // IE9은 Canvas는 쓸 수 있지만 file input에서 file Object를 가져오지 못해 제외 isLowerIE = true; } } return isCanvasUsable && !isLowerIE; }

2. 해당 imageUpload.jsp


function openCamera(id) {

var inputId = '#' + id; $(inputId).click(); }


$('#test1').change(function(e){ $('#img_test1').attr('src', URL.createObjectURL(e.target.files[0])); resizingImage('test1', e); });




// fileInput Object에 값이 변경될 때

function resizingImage(fileId, e){

// 이미지 리사이즈가 브라우저단에서 가능하다면 html5 사용...

    if(isImageResizable()) {

        var file = e.target.files[0]; // file Object get...

        if(file.type.match(/image.*/)) {

            // Load the image

            var reader = new FileReader();

            reader.onload = function (readerEvent) {

                var image = new Image();

                image.onload = function (imageEvent) {

 

                    // Resize the image

                    var canvas = document.createElement('canvas'),

                    max_size = 1280,

                    width = image.width,

                    height = image.height;

 

                    if (width > height) {

                        if (width > max_size) {

                            height *= max_size / width;

                            width = max_size;

                        }

                    } else {

                        if (height > max_size) {

                            width *= max_size / height;

                            height = max_size;

                        }

                    }

                    canvas.width = width;

                    canvas.height = height;

                    canvas.getContext('2d').drawImage(image, 0, 0, width, height);

                    var dataUrl = canvas.toDataURL('image/jpeg');

                    //var dataUrl = canvas.toDataURL('image/jpeg', 0.90);   // 이미지 퀄리티 조절도 가능...

                    //var resizedImage = dataURLToBlob(dataUrl);  // 이미지를 바이너리 형태로 변환?

                     

                // 이미지가 리사이즈 되었으면 파일 업로드하는 메소드 호출 시작

                        //변환된 사진 attribute 변경

$("#img_"+fileId).attr('src', dataUrl);

uploadFile(fileId);

// 이미지가 리사이즈 되었으면 파일 업로드하는 메소드 호출 끝

                    return false;

                }

                image.src = readerEvent.target.result;

            }

            reader.readAsDataURL(file);

        }

    }

    return false;

}


...................

...................

...................



<input type="file" accept="image/*" style="display: none;" id="test1" name="test1" capture> <button id="attach_test1" type="button" class="btn_type2 attach_yn" onClick="javascript:openCamera('test1');">

<div class="btn_wrap" id="imgDiv_test1" style="display: none;">     <span class="txt" align="center"><img id="img_test1" style="width: 97%;" /></span> &nbsp;<br> </div>








대략적으로 쓴내용이라 필자는 테스트 해보지 못함. 참고로 사용하길 바람




반응형

'JQUERY/JAVASCRIPT' 카테고리의 다른 글

History API 주소를 내 마음대로  (0) 2018.01.03
window.history 제어 및 뒤로가기 막기  (0) 2018.01.03
반응형

리눅스 vi 에디터 사용법 정리

리눅스 vi 에디터 사용법 정리 vim 텍스트 에디터 단축키

vi 에디터를 사용하기 위해서는 명령모드와 입력모드의 차이를 알아야합니다. 입력모드는 메모장처럼 텍스트를 자유롭게 편집하는 모드이고, 명령모드는 말 그대로 다양한 명령을 내리는 모드를 말합니다. 처음 vi 에디터를 실행하면 명령모드로 시작을 합니다. vi 에디터 명령모드에서는 커서이동, 복사, 붙여넣기, 문자열 검색 등을 수행할 수 있습니다.


명령모드에서 입력모드로 전환

a : 커서 다음(오른쪽)에 입력

A : 행 마지막 부분에 입력

i : 커서 앞(왼쪽)에 입력

I : 행 처음 부분에 입력

o : 커서 밑에 빈 행을 추가하여 입력

O : 커서 위에 빈 행을 추가하여 입력

s : 커서에 있는 글자를 지우고 입력


입력모드에서 명령모드로 전환

ESC 키를 누른다.


저장, 종료하기

명령모드에서 : (콜론)을 이용하여 다양한 작업을 할 수 있다.

:q

종료한다.

:q! 

저장하지 않고 그냥 강제로 종료한다.

:w

저장한다.

:wq

저장하고 종료한다.

ZZ

저장하고 종료한다. (:wq와 동일)

:wq 파일이름

저장할 때 파일이름을 지정할 수 있다.

(vi 에디터에서는 비정상 종료시 .swp 파일이 생성된다. 필요가 없으면 삭제)


커서 이동

h, j, k, l : 좌, 하, 상, 우 커서 이동 (방향 키가 없는 키보드에서 사용)

w : 다음 단어의 첫 글자로 이동

b : 이전 단어의 첫 글자로 이동

G : 마지막 행으로 가기

:숫자 :  지정한 숫자 행으로 이동. ex) :5


삭제

x : 커서에 있는 글자 삭제

X : 커서 앞에 있는 글자 삭제

dw : 커서를 기준으로 뒤에 있는 단어 글자 삭제 (커서 포함)

db : 커서를 기준으로 앞에 있는 단어 글자 삭제

dd : 커서가 있는 라인(줄) 삭제

dw, db, dd 명령 앞에 삭제할 숫자를 지정할 수 있다. ex) 3dw, 2db, 4dd

삭제 된 내용은 버퍼에 저장되어 붙여넣기가 가능하다.


복사

yw : 커서를 기준으로 뒤에 있는 단어 글자 복사 (커서 포함)

yb : 커서를 기준으로 앞에 있는 단어 글자 복사

yy : 커서가 있는 라인(줄) 복사

yw, yb, yy 명령 앞에 복사할 숫자를 지정할 수 있다. ex) 3yw, 2yb, 4yy


붙여넣기 (복사, 삭제된 내용을 붙여넣는다.)

p : 커서 다음에 붙여넣기

P : 커서 이전에 붙여넣기


찾기

/문자열 : 앞에서 부터 문자열을 찾는다.

?문자열 : 뒤에서 부터 문자열을 찾는다.

n : 뒤로 검색

N : 앞으로 검색


바꾸기

:%s/old/new

각 행의 처음 나오는 old를 찾아 new로 바꾼다.

:%s/old/new/g

모든 old를 찾아 new로 바꾼다.

:%s/old/new/gc

모든 old를 찾아 new로 바꾸기 전에 물어본다.


되돌리기(Undo), 다시실행(Redo)

u : 이전으로  되돌리기 (Undo)

Ctrl + r : 되돌리한 것은 다시 실행하기 (Redo)


자주 사용하는 기능들

:set number 

행번호를 출력한다. (간단하게 :set nu)

:set nonumber 

행번호를 숨긴다. (간단하게 :set nonu)

:cd

현재 디렉토리를 출력한다.

반응형
반응형

JSP 개발서버, 스테이징서버, 상용서버를 만지다 보면 외부 API나 라이센스 등등 URL만 바뀌어서 사용하는 경우가 많다.


한번에 바꾸기 위한 방법이다.


1. deploy_domain.jsp


<%

// 상용 // String deploy_domain = "http://real.penthegom.co.kr"; // 스테이징 // String deploy_domain = "http://stage.penthegom.co.kr"; // 개발 // String deploy_domain = "http://dev.penthegom.co.kr"; // 로컬 // String deploy_domain = "http://localhost:8080"; %>

2. 원하는.jsp 사용방법


<%@ include file="/deploy_domain.jsp"%>

...

...

<%

...

if(deploy_domain.indexOf("real") > -1){

...

}else if(deploy_domain.indexOf("stage") > -1){

...

}else if(deploy_domain.indexOf("dev") > -1){

...

}else{

...

}

...

%>

...

...


반응형
반응형

XSS 란?


Cross-site Scripting. 링크 타고 들어갔는데 교차사이트 스크립팅 방지한다고 안 되면 진짜 짜증난다.

SQL injection과 함께[1] 웹 상에서 가장 기초적인 취약점 공격 방법의 일종으로, 악의적인 사용자가 공격하려는 사이트에 스크립트를 넣는 기법을 말한다. 공격에 성공하면 사이트에 접속한 사용자는 삽입된 코드를 실행하게 되며,[2] 보통 의도치 않은 행동을 수행시키거나 쿠키나 세션 토큰 등의 민감한 정보를 탈취한다.

크로스 사이트 스크립팅이란 이름 답게, 자바스크립트를 사용하여 공격하는 경우가 많다. 공격 방법이 단순하고 가장 기초적이지만, 많은 웹사이트들이 XSS에 대한 방어 조치를 해두지 않아 공격을 받는 경우가 많다. 여러 사용자가 접근 가능한 게시판 등에 코드를 삽입하는 경우도 많으며, 경우에 따라서는 메일과 같은 매체를 통해서도 전파된다. 

물론, HTML을 사용하는 것이기 때문에, Text-Only 게시판이나, BBCode를 이용하는 위키위키 등에서는 XSS가 발생할 일은 없다. 단, 나무위키의 경우 {{{#!html HTML}}} 을 이용해서 HTML 태그를 사용할 수 있으므로 취약점이 있을 수도 있다. 그래서 나무위키 초반에는 스크립트 태그와 이벤트 속성도 막지 않았다. 물론, 이는 후에 대부분 수정되었다.

주로 CSRF를 하기 위해서 사용되기 때문에 종종 CSRF와 혼동되는 경우가 있으나, XSS는 자바스크립트를 실행시키는 것이고, CSRF는 특정한 행동을 시키는 것이므로 다르다.



3.1. 스크립트 태그[편집]

방법

스크립트 태그로 자바스크립트를 실행한다.

예제

<script>alert('XSS');</script>

설명

스크립트 태그의 스크립트를 실행시킨다. 안타깝게도, 매우 정직한 방법이라 대부분의 사이트에서 막는 경우가 많다. 브라우저단에서 필터링 해주는 경우도 있다. 물론 예외도 있다.[5] 하지만 만들어진지 몇십 년 이상 되었거나, 보안 X까! 하면서 막지도 않는 경우도 있다. 애초에 비밀번호를 평문/MD5로 저장하는 경우도 있는데 새삼스럽게...

3.2. 자바스크립트 링크[편집]

방법

링크 태그로 자바스크립트를 실행한다.

예제

<a href="javascript:alert('XSS')">XSS</a>

설명

브라우저에서 about: 링크와 같이, javascript: 로 시작하는 링크는 스크립트를 실행시킨다. 스크립트 태그와 같이, javascript: 를 필터링하는 경우가 많아 많은 사이트에서 막는다.

3.3. 이벤트 속성[편집]

방법

이벤트 속성을 사용한다.

예제

<img src="#" onerror="alert('XSS')">

설명

이벤트 속성으로 스크립트를 실행할 수 있다. 주로 on 으로 시작하는 속성이 이벤트 속성이다. 자주 사용되는 이벤트 속성으로는 onload onerror onclick 등이 있다. 물론, 이 방법 역시 '자바스크립트 링크' 방법만큼 많이 막혔다.

3.4. 블랙리스트 우회[편집]

방법

알려지지 않은 태그와 속성들을 사용한다.

예제

<ruby oncopy="alert('XSS')">XSS</ruby>

설명

블랙 리스트 방식으로 막는 사이트에 사용할 수 있다. 이벤트 속성 목록을 참고하자. 위의 방법들보다는 적게 막혔으나, 여전히 최근 웹사이트들에선 화이트리스트 방식 차단이 대부분이라, 막혔을 가능성이 높다.

3.5. 내용 난독화[편집]

방법

따옴표로 감싸는 문자열 사이에 공백 문자들을 넣고, HTML 인코드를 하여 난독화한다.

예제

<a href="&#x6A;&#x61;&#x76;&#x61;&#x73;&#xA;&#x63;&#x72;&#x69;&#x70;&#x74;&#xA;&#x3A;&#xA;&#x61;&#x6C;&#x65;&#x72;&#x74;&#xA;&#x28;&#x27;&#x58;&#x53;&#x53;&#x27;&#x29;">XSS</a>

설명

일부 브라우저에서 javascript: 링크 사이에 공백 문자가 들어갈 수 있고, HTML 인코드를 해도 디코드된 내용이 출력된다는 점을 이용한다. 여기에서는 '자바스크립트 링크' 방법과 사용하였지만, 당연히 다른 방법과 함께 사용할 수 있다.

-나무 위키 발췌-



위와 같이 공격할 수 있다는 내용이다.

필자는 Spring-Boot를 사용하기 때문에 적용방법을 적어보자 한다.


naver에 lucy-xss-servlet-filter를 적용하고자 한다.

깃헙 주소 : https://github.com/naver/lucy-xss-servlet-filter


먼저 pom.xml에 디펜던시를 추가하거나 jar 파일을 받아 라이브러리 빌드패스에 추가한다.

<dependency>
	<groupId>com.navercorp.lucy</groupId>
	<artifactId>lucy-xss-servlet</artifactId>
	<version>2.0.0</version>
</dependency>



%%% 주의 사항 %%%

프로젝트 중 소켓통신이나 json데이터가 있을경우 json데이터 안에 있는 xss필터링 문자열도 걸러내는 경우가 생긴다. 해당 글은 이것을 해결하고 올린 글이다.




먼저 spring-boot의 핵심인 빈을 추가한다. XssServletFilter 클래스는 아래에 나온다.


1. WebMvcConfig.java


@Configuration

public class WebMvcConfig extends WebMvcConfigurerAdapter 

...

...

import 관련

org.springframework.boot.web.servlet.FilterRegistrationBean

org.springframework.boot.web.servlet.FilterRegistrationBean.FilterRegistrationBean()

 /*

     * lucy-xss-filter

     *  

     * */

    @Bean

    public FilterRegistrationBean getFilterRegistrationBean(){

        FilterRegistrationBean registrationBean = new FilterRegistrationBean();

        registrationBean.setFilter(new XssServletFilter(acceptUrls));

        registrationBean.setOrder(1);

        registrationBean.addUrlPatterns("/*");

        return registrationBean;

    }

...

...



2. XssServletFilter.java


import java.io.IOException;


import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;


import com.navercorp.lucy.security.xss.servletfilter.XssEscapeFilter;

import com.navercorp.lucy.security.xss.servletfilter.XssEscapeServletFilterWrapper;


public class XssServletFilter implements Filter {

private static final Logger logger = LoggerFactory.getLogger(XssServletFilter.class);

private String[] acceptUrls;

public XssServletFilter(String[] acceptUrls){

this.acceptUrls = acceptUrls;

}

private XssEscapeFilter xssEscapeFilter = XssEscapeFilter.getInstance();


@Override

public void init(FilterConfig filterConfig) throws ServletException {

}


@Override

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

if(excludeUrl(request)){

chain.doFilter(request, response); //걸러내는 URI일 경우 요청값 그대로 처리

}else{

chain.doFilter(new XssEscapeServletFilterWrapper(request, xssEscapeFilter), response);

}

}


private boolean excludeUrl(ServletRequest request) {

String uri = ((HttpServletRequest) request).getRequestURI().toString().trim();

logger.info("XssServletFilter Uri : {}", uri);

boolean returnValue = false;

for(String url : this.acceptUrls) {

if(uri.startsWith(url)){

returnValue = true;

}

}

return returnValue;

}


@Override

public void destroy() {

}

}

3. lucy-xss-servlet-filter-rule.xml


src/main/resources 에 넣어야함.


<?xml version="1.0" encoding="UTF-8"?>


<config xmlns="http://www.navercorp.com/lucy-xss-servlet">

    <defenders>

        <!-- XssPreventer 등록 -->

        <defender>

            <name>xssPreventerDefender</name>

            <class>com.navercorp.lucy.security.xss.servletfilter.defender.XssPreventerDefender</class>

        </defender>


        <!-- XssSaxFilter 등록 -->

        <defender>

            <name>xssSaxFilterDefender</name>

            <class>com.navercorp.lucy.security.xss.servletfilter.defender.XssSaxFilterDefender</class>

            <init-param>

                <param-value>lucy-xss-sax.xml</param-value>   <!-- lucy-xss-filter의 sax용 설정파일 -->

                <param-value>false</param-value>        <!-- 필터링된 코멘트를 남길지 여부, 성능 효율상 false 추천 -->

            </init-param>

        </defender>


        <!-- XssFilter 등록 -->

        <defender>

            <name>xssFilterDefender</name>

            <class>com.navercorp.lucy.security.xss.servletfilter.defender.XssFilterDefender</class>

            <init-param>

                <param-value>lucy-xss.xml</param-value>    <!-- lucy-xss-filter의 dom용 설정파일 -->

                <param-value>false</param-value>         <!-- 필터링된 코멘트를 남길지 여부, 성능 효율상 false 추천 -->

            </init-param>

        </defender>

    </defenders>


    <!-- default defender 선언, 필터링 시 지정한 defender가 없으면 여기 정의된 default defender를 사용해 필터링 한다. -->

    <default>

        <defender>xssPreventerDefender</defender>

    </default>


    <!-- global 필터링 룰 선언 -->

    <global>

        <!-- 모든 url에서 들어오는 globalParameter 파라메터는 필터링 되지 않으며

                또한 globalPrefixParameter1로 시작하는 파라메터도 필터링 되지 않는다.

                globalPrefixParameter2는 필터링 되며 globalPrefixParameter3은 필터링 되지 않지만

                더 정확한 표현이 가능하므로 globalPrefixParameter2, globalPrefixParameter3과 같은 불분명한 표현은 사용하지 않는 것이 좋다. -->

        <params>

<!--             <param name="JSONDS1" useDefender="false" /> -->

        </params>

    </global>

    <!-- url 별 필터링 룰 선언 -->

    <url-rule-set>

        <!-- url disable이 true이면 지정한 url 내의 모든 파라메터는 필터링 되지 않는다. -->

<!--         <url-rule> -->

<!--             <url disable="true">/eform/Clip.jsp</url> -->

<!--             <url disable="true">/ajax/eformUserData</url> -->

<!--         </url-rule> -->

    </url-rule-set>

</config>

4. application.properties


xss필터를 타지 않게 할 url 설정 


#xss accept uri 

xss.acceptUrls = /test, /admin


반응형
반응형

http://www.pyrasis.com/docker.html

반응형
반응형

 요즘 Spring Boot를 이용해서 Non-web application을 만드는 작업에 빠져서 Spring에 대해 많은 걸 배우고 공부하는 중이다. 그래서 이번에는 Spring Boot application을 위한 설정파일로 가장 간단하게 이용할 수 있는 방법인 application.properties를 이용하는 방법을 간단히 설명해 보려고 한다.


 Spring Boot를 이용할 때 대부분의 경우 설정 파일로 application.properties를 이용한다. application.properties는 기본적으로 Spring Boot가 읽어들이도록 되어있기 때문에 사실 파일만 만들어서 'src/main/resources' 폴더에 넣어주면 바로 설정파일로 이용이 가능하다. 이렇게 편한 방법이 있는데 따로 만들어서 사용할 이유가 없다.

 간단한 사용법을 보자.

# application.properties
name=Michael
import org.springframework.stereotype.*
import org.springframework.beans.factory.annotation.*

@Component
public class MyBean {

    @Value("${name}")
    private String name;

    // ...

}
 위의 MyBean 클래스의 name 멤버변수는 @Value 어노테이션을 지정한 것만으로 application.properties에 있는 name속성 값 "Michael"로 초기화가 된다. 

 만약에 application.properties 파일에 name 속성이 정의되어있지 않았을 때 default값을 지정하는 방법도 있다. 아래와 같이 @Value 어노테이션에서 속성 이름 옆에 콜론(:)을 찍고 직접 지정해 주면 된다.

    @Value("${name:Michael}")
    private String name;

    @Value("${age:20}")
    private int age;
 위 예에서 지정된 default 값들은 application.properties에 값이 정의되어있지 않으면 각 변수의 타입에 맞게 변환되어 대입된다. 

 사실 Spring Boot의 문서를 보면 property source를 설정하는 다양한 방법이 제공된다.

  1. Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).
  2. @TestPropertySource annotations on your tests.
  3. @SpringBootTest#properties annotation attribute on your tests.
  4. Command line arguments.
  5. Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property)
  6. ServletConfig init parameters.
  7. ServletContext init parameters.
  8. JNDI attributes from java:comp/env.
  9. Java System properties (System.getProperties()).
  10. OS environment variables.
  11. A RandomValuePropertySource that only has properties in random.*.
  12. Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants)
  13. Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants)
  14. Application properties outside of your packaged jar (application.properties and YAML variants).
  15. Application properties packaged inside your jar (application.properties and YAML variants).
  16. @PropertySource annotations on your @Configuration classes.
  17. Default properties (specified using SpringApplication.setDefaultProperties).

 위의 목록은 더 높은 우선순위를 갖는 property source부터 차례대로 나열한 것이다. 여기서 소개하는 application.properties 파일을 'src/main/resources' 폴더에 넣는 방법은 위의 목록 중 15번에 나오는 방법으로 우선순위로 보면 상당히 아래쪽에 나온다. 즉 상당히 우선순위가 낮다는 말이다. 'src/main/resources' 폴더에 application.properties 파일을 넣어 놓는다는 것은 배포 시에 jar 파일에 함께 묶여서 배포가 된다는 것이고 따라서 배포 후에 설정 값을 변경하려는 의도로 사용하기에는 적합하지 않다. 그러한 용도로 사용하려면 14번에서 말하는 jar 외부의 application.properties를 사용하면 된다. 그냥 application.properties 파일을 따로 만들어서 jar를 실행하는 directory에 두면 된다. jar 안에 있는 application.properties 파일보다 우선순위가 높기 때문에 둘 다 있는 경우 jar 외부의 application.properties 파일의 값이 더 높은 우선순위를 갖는다.

 여기서 많은 사람들이 우선순위에 대해서 오해하는 점 하나를 짚고 가야겠다. 예를 들어 jar안에 같이 포함된 application.properties파일과 jar 외부의 application.properties 파일이 둘 다 존재할 때 외부의 application.properties 파일이 더 우선순위가 높기 때문에 내부의 application.properties 파일이 완전히 무시된다고 생각하기 쉽다. 하지만 그렇지 않다. 우선순위가 높은 property source의 값들은 더 낮은 property source의 값들을 덮어쓰는 것이지 낮은 우선순위의 property source를 읽어들이지 않는 다는 뜻이 아니다. 즉 둘 다 name 속성이 정의되어있다면 더 높은 우선순위의 property source에 선언된 name 속성 값이 사용된다. 하지만 age라는 속성이 더 낮은 우선순위의 property source에만 선언되어있다면 낮은 우선순위의 property에 선언되었지만 그 값이 사용된다. (더 높은 우선순위에 값이 정의되지 않았으므로)

 즉 위 목록의 모든 property source들을 아래쪽부터 윗쪽으로 차례대로 다 찾아서 읽어들이면서 이미 정의된 property들은 덮어 쓰는 개념이다. 목록을 보면 테스트 환경을 제외하고 command line argument가 가장 우선순위가 높으므로 임시로 특정 값을 override하고 싶다면 command line을 이용해서 property를 전달하면 무조건 전달된 값이 이용될 것이다.

 command line을 이용하는 방법은 아래와 같은 방법으로 전달하면 된다. ('server.port' 속성값 지정)

$ java -jar myapp.jar --server.port=9000

 위 목록에 나열된 방법은 많지만 흔히 사용하는 방법으로 이 정도만 알고 있으면 대부분의 경우 문제없이 개발을 진행할 수 있다. 개념적으로 잘 이해하고 있다면 다른 나머지 방법을 이용하는 것도 사실 시간문제일 뿐 크게 어려운 부분은 없을 것이다.



출처 : http://progtrend.blogspot.kr/2017/07/spring-boot-applicationproperties.html

반응형

+ Recent posts