Maven build

카테고리 없음 2016. 11. 23. 17:03

clean : 빌드 결과물 삭제
compile : 코드 컴파일
test : 테스트코드 수행
package : JAR WAR 형태로 패키징
install : 로컬리파지토리에 저장
deploy : 원격저장소에 저장

AND


네트워크 프로그램을 개발할 일은 아주 가끔 있지만, 대부분 원격의 서버와 통신하는 경우가 대부분이라서 

테스트하기도 힘들고, 운영 중이라면 언제 어떤 문제가 발생할지 예측할 수가 없다.

그래서, 각 오류별로 발생하는 문제와 이 문제의 원인을 미리 파악하고 있으면 신속한 대응이 가능하다.


네트워크 프로그램

간단하게 구성하자면 원격에서 클라이언트와 서버가 통신하는 것이라고 할 수 있다.

서버는 데몬 형태로 항상 떠 있으면서 클라이언트의 요청이나 데이터를 받고, 그에 대한 응답을 주는 역할이고,

클라이언트는 서버와 통신할 필요가 있을 경우에 객체, 프로세스, 스레드 등을 생성해서 서버에 요청을 보내고 응답을 받거나, 데이터를 보내고 ACK를 받은 후에 종료되는 형태이다.

웹을 예로 들면, 웹브라우저가 클라이언트가 되고 웹서버나 WAS는 서버의 역할을 한다.


네트워크 통신 과정

1. 연결

   클라이언트와 서버간에 커넥션을 생성하는 과정이다.

   클라이언트가 서버에 연결을 요청하고, 서버가 연결을 수락한다는 답을 보내면 커넥션 생성이 완료된다.

2. 데이터 송수신

   커넥션이 생성된 후에는 클라이언트가 서버에 요청이나 데이터를 전송한다.

   서버는 수신한 요청에 대한 응답을 생성해서 클라이언트로 전송하거나, 단순히 ACK를 전송한다.

   클라이언트가 정상적으로 응답이나 ACK를 정상적으로 수신했다고 판단되면 커넥션을 종료하거나, 다음 요청을 보내는 식의 작업이 이어진다.


동기/비동기 방식

(이 포스트와의 큰 관련이 없어 보이지만, 헷갈려 하는 분들이 많아 간단하게 언급하려고 한다)

- 동기

클라이언트가 서버에 요청을 보내고 연결이 유지된 상태에서 응답을 기다리다가, 서버에서 응답을 받은 후에 커넥션을 종료하는 형태이다.

한 개의 연결로 요청 및 응답이 모두 처리되는 형태로, 가장 일반적인 형태이다.

- 비동기

A시스템의 클라이언트가 B시스템의 서버에 요청을 보내면 그 내용을 저장한 후에 잘 받았다는 의미의 ACK만을 클라이언트에 전송하고 커넥션이 종료된다.

그 이후에, B시스템에 저장된 요청에 대한 처리가 끝나면 클라이언트를 생성하여 A시스템의 서버에 접속해 응답을 보내고, A시스템의 서버는 잘 받았다는 ACK를 전송하고 커넥션이 종료된다.

결과적으로 A와 B 시스템에 각각 클라이언트/서버가 모두 존재해야 하고, A시스템이 받은 응답이 어떤 요청에 대한 것인지를 판단하기 위해 추가로 ID값을 요청, 응답, ACK에 달고 다녀야 한다.

비동기는 구현이나 관리가 복잡해지는 단점이 있어, 동기식으로 처리할 경우 응답을 기다리다가 타임아웃이 발생할 정도로 오래 걸리는 경우에 주로 사용한다.


네트워크 프로그램 오류의 종류

- 커넥션 타임아웃

클라이언트에서 서버로 커넥션 요청을 보냈는데 지정된 시간동안 그에 대한 응답이 없을 경우에 발생한다.

(참고로, 보통 API에서 커넥션 타임아웃에 대한 값을 지정할 수 있으니, 프로그램이나 서버 환경에 맞게 적절히 세팅해 사용한다.)

거의 방화벽이 막혀 있어서 발생한다.

-> 보통 2개의 방화벽이 있을 수 있는데, 클라이언트단에 있는 방화벽에 클라이언트에서 서버가 가는 방화벽이 열려 있는지 확인하고, 서버단에 있는 방화벽에 클라이언트로부터 서버로 들어오는 방화벽이 열려 있는지 확인해야 한다.

네트워크 전문가가 아니라서 잘은 모르지만, 대부분 방화벽은 금지된 통로로 오가는 패킷들은 그냥 받아서 없애 버리는 것 같다. 그래서, 커넥션 요청을 보내 놓고 마냥 기다리다가 커넥션 타임아웃이 발생하는 것으로 보인다.

- Read Timeout

이 오류가 발생하는 것은 이미 커넥션은 생성되어 있다는 의미이다.

커넥션이 생성된 후에 클라이언트가 서버로 요청을 보내고 응답을 기다리다가 타임아웃이 발생한 것이다. 즉, 서버가 지정된 시간 안에 응답을 주지 않았다는 것이다.

클라이언트의 문제일 가능성은 적고, 서버에 문제가 생겨 응답을 주지 못 하는 경우가 많으므로, 서버단 담당자에게 확인을 요청해야 한다.

- Too Many Open Files

파일을 열 때 뿐만 아니라, 소켓을 열 때도 서버의 File Descriptor를 사용한다. 

즉, 소켓도 사용 후에 잘 닫아 줘야 이런 오류가 발생하지 않는다.

일단, 현재 열려 있는 파일과 소켓을 보고 싶으면 lsof나 pfiles 명령을 이용해서 확인한다.

가끔씩 잘 닫아 줘도 이런 문제가 발생하는 경우가 있다.

서버는 WAS의 서블릿을 만들어 두고, HttpClient를 사용해 클라이언트를 만들어 통신한 후에 클라이언트를 종료하도록 구성했다.

그런데, HTTP의 특성상 API로 커넥션을 종료시켜도 한동안 TIME_WAIT 상태로 커넥션이 머물러 있다가 잠시 후에 진짜 커넥션이 종료되는 것이었다. 단시간에 많은 통신을 수행한 경우 이런 TIME_WAIT이 미처 풀리지 않고 많이 쌓여 있다가 더 이상 커넥션을 생성하지 못 해 Too Many Open Files가 발생한다.

이런 경우는 Connection Pool을 사용해 일정한 수의 커넥션들을 생성해 놓고 재사용하도록 하면, 커넥션이 늘어나지 않아 Too Many Open Files를 막을 수 있고, 매번 커넥션을 생성하는 데 소요되는 시간을 절약하여 성능을 향상시킬 수 있다.

Apache HttpClient의 Connection Pool 사용법은 여기를 클릭하면 확인할 수 있다.


AND


가끔 대용량 파일을 열어서 내용을 확인할 일이 생긴다. 주로 로그파일을 열 때 그렇다.

vi
파일을 열 때 가장 많이 사용하는 게 vi인데, 이건 대용량 파일을 열면 로딩할 때 아주 오래 걸리거나 아예 못 여는 경우도 가끔 생긴다.
연다고 해도 메모리를 많이 사용하기 때문에, 운영서버에서 사용하는 경우는 신경이 많이 쓰인다.

more
이 명령은 파일의 편집 기능이 없어, 대용량 파일도 가볍게 띄운다.
단, 기능도 너무 가벼워서 검색을 해도 내가 찾는 내용이 어디에 있는지 확인하기가 어렵다.
그리고, 일단 아래로 내려오면 위의 내용으로 올라갈 수가 없다.

less
more의 가벼움과 vi의 찾기 기능의 장점을 모아서 만들어진 것이 less이다.
이름은 less인데 more보다 기능이 많다니, 아이러니하게 지은 작명 센스가 꽤 괜찮다.
기능적으로도 참 센스있게 만들었다.

아래는 less의 기능
(vi의 command 모드에 있다고 생각하고 아래의 단축키를 눌러 주면 된다)

g : 1라인으로 이동
G : 마지막라인으로 이동
/pattern : 특정 패턴(문자열) 찾기
n : 정방향으로 계속 찾기
N : 역방향으로 계속 찾기
f : 다음 페이지로 이동
b : 이전 페이지로 이동
-N[return] : 라인넘버 표시 토글 (속도 아주 많이 느려짐. 꼭 필요할 때만 켰다가, 바로 꺼야 함)
q : 나가기



AND


HttpClient로 빈번히 connection을 맺었다가, 사용이 끝나면 끊고 하다 보면 더 이상 connection을 열 수 없는 경우가 발생할 수 있다.
그 이유는 connection을 닫는다고 호출을 해도, 실제로는 어느 정도 TIME_WAIT 상태에 있다가 끊어지는데 이런 것들이 많이 쌓여 있으면 File Descriptor가 꽉 찼다는 에러(Too Many Open Files)가 나면서 connection을 맺지 못 하게 된다.
이런 현상을 방지하기 위해서는 Connection을 재사용할 수 있도록 HttpClient에서 제공하는 Connection Pool을 사용하는 것이다.


<사용 라이브러리>
httpclient-4.2.3.jar, httpcore-4.2.2.jar

public class ConnectionManager {

private static PoolingClientConnectionManager connectionManager = null;
public static synchronized HttpClient getHttpClient() {
if (connectionManager == null) {
connectionManager = new PoolingClientConnectionManager();
connectionManager.setMaxTotal(50);
connectionManager.setDefaultMaxPerRoute(20);
}

return new DefaultHttpClient(connectionManager);
}

public static void abort(HttpRequestBase httpRequest) {
if (httpRequest != null) {
try {
httpRequest.abort();
} catch (Exception e) {}
}
}

public static void release(HttpResponse response) {
if (response != null && response.getEntity() != null)
EntityUtils.consumeQuietly(response.getEntity());
}
}


요런 식으로, static으로 PoolingClientConnectionManager 를 하나 선언해 두고, 최초로 getHttpClient를 호출할 때 Connection Pool이 지정된 사이즈로 생성된다. 그리고, Connection을 하나 만들어 리턴한다.

- maxTotal : Connection Pool의 수용 가능한 최대 사이즈
- defaultMaxPerRoute : 각 host(IP와 Port의 조합)당 Connection Pool에 생성가능한 Connection 수


protected <T> T get(String url) {
HttpClient httpClient = null;
HttpGet httpGet = null;
HttpResponse response = null;
try {
httpClient = ConnectionManager.getHttpClient();
httpGet = new HttpGet(url);
response = httpClient.execute(httpGet);

StatusLine statusLine = response.getStatusLine();
// 에러 발생
if (statusLine.getStatusCode() < 200 || statusLine.getStatusCode() >= 300) {
throw new Exception(statusLine.getStatusCode(), getReason(response));
}
// 성공
else {
                            // do something
}
} catch (ClientProtocolException e) {
e.printStackTrace();
ConnectionManager.abort(httpGet);
throw e;
} catch (IOException e) {
e.printStackTrace();
ConnectionManager.abort(httpGet);
throw e;
} finally {
ConnectionManager.release(response);
}
}

Connection Pool은 요런 식으로 이용하면 되겠다. 위는 GET 방식 사용시.


Pool을 사용할 때마다 항상 주의할 것은 역시 반환을 꼭 해 줘야 한다는 것!!!
Exception이 발생하면 abort를 호출해서 정리해 주고, finally에선 항상 EntityUtils.consumeQuietly를 호출해서 리소스를 반납하게 해 준다.


AND

IE나 Firefox에서는 보통 쓰던대로 아래와 같이 넘기고 받으면 끝이었다.


Parent.html

    var retVal = window.showModalDialog("popup.html", null,);

    if (retVal) {

        $("#some_id").val(retVal.some_id);

        $("#some_name").val(retVal.some_name);

    }


popup.html

function fn_Click(some_id, some_name) {

    var obj = new Object();

    obj. some_id  =  some_id ;

    obj. some_name = some_name ;

    window.returnValue = obj;

    self.close();

}


그런데, Chrome에서는 popup에서 받은 retVal이라는 변수가 undefined이다.

이럴 때 아래와 같이 Chrome 브라우저에 대한 추가처리코드를 추가해 주면 해결된다.


Parent.html

    var retVal = window.showModalDialog("popup.html", null,);


    if (retVal == undefined)

    retVal = window.returnValue;


    if (retVal) {

        $("#some_id").val(retVal.some_id);

        $("#some_name").val(retVal.some_name);

    }


popup.html

function fn_Click(some_id, some_name) {

    var obj = new Object();

    obj. some_id = some_id ;

    obj. some_name = some_name ;

    if (window.opener)

        window.opener.returnValue = obj; 

    window.returnValue = obj;

    self.close();

}



'Java_Web' 카테고리의 다른 글

대용량 파일 조회 및 문자열 찾기  (0) 2015.03.10
HttpClient Connection Pooling  (0) 2015.03.10
JD Java Decompiler  (0) 2010.04.19
Hibernate에서 CLOB 컬럼 사용하기  (0) 2009.12.16
HQL UNION 제약사항  (0) 2009.08.21
AND


Django에서는 Cache 기능도 제공한다.

memcached, DB, 로컬메모리, 파일(?) 등의 종류를 제공하는데 각각 장단점이 있다.


memcached는 외부 캐쉬프로세스를 띄워 놓고, 거기에 

DB 방식의 경우는 여러 인스턴스가 동일한 DB를 바라보니까 모두 같은 데이터를 사용할 수 있겠지만, 속도는 좀 느릴 것이고,

로컬메모리 방식은 각 인스턴스가 자기 메모리에 각각 cache하니까 다른 데이터를 사용할 수 있는 가능성이 있겠지만, 속도는 빠를 것이고,

파일은 DB와 비슷할 것 같다.


나는 설정할 것이 별로 없고 빠른 로컬메모리 방식을 선택했다.

거의 변경가능성이 희박한 데이터를 cache한다면, 로컬메모리 방식이 가장 좋을 듯하다.


일단 settings.py에 cache를 사용하겠다고 설정을 해 줘야 한다.

CACHES = {

    'default': {

        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',

        'TIMEOUT': 3600,

    }

}


이렇게 하면, Local Memory 방식의 cache를 사용하고, 3600초(1분) 단위로 cache 들어간 데이터가 삭제한다는 뜻이다.


다음은 코드에서 사용하기만 하면 된다.

from django.core.cache import cache


cache.set('key', value)
cached_val = cache.get('key')


아주 간단하다.

cache를 import 하고, cache.set으로 집어 넣고, cache.get으로 꺼낸다.


이것을 응용해서 구현을 한다면,

get해서 나온 값이 None이면 넣을 데이터를 조회하거나 만들어서 set을 하면 되겠다.

cache에는 어떤 값이나 객체도 넣을 수 있는 것 같다.


이외에도 get_many로 한번에 여러 key를 리스트로 던지면, key와 value로 구성된 dict로 리턴하는 함수도 있고,

cache에서 내용을 지우는 delete, 전체를 지우는 clear 함수도 있다.


다시 매뉴얼을 보니 화면단에서도 cache를 사용할 수 있는데, 필요한 분들은 매뉴얼을 보시길...


'Python_Django' 카테고리의 다른 글

decorator 만들기  (0) 2012.07.25
hash(digest) 호출  (0) 2012.07.25
request의 값 처리  (0) 2012.07.25
CSRF token 생성  (0) 2012.07.25
Django의 session 처리  (0) 2012.07.13
AND

decorator 만들기

Python_Django 2012. 7. 25. 10:50


Django에서 기본으로 제공하는 사용자정보나 권한정보를 이용하지 않고 별도의 테이블을 만들어서 사용할 경우,

decorator를 만들어서 login_required, permission_required 를 구현할 수 있다.


test.common.decorators.py

from django.db import connection

from django.http import HttpResponseRedirect


# view_func : @login_required가 붙어 있는 함수

# request : view_func의 첫번째 인자

def login_required(view_func, login_url='/test/user/login/?autherr=login'):

    def decorator(request, *args, **kwargs):

        # 로그인된 상태이므로 원래 view 함수를 실행한다

        if 'userid' in request.session:

            return view_func(request, *args, **kwargs)

        # 로그인을 하지 않았으므로, 로그인페이지로 redirect

        else:

            return HttpResponseRedirect(login_url)

    return decorator


# view_func : @permission_required가 붙어 있는 함수

# request : view_func의 첫번째 인자

def permission_required(view_func, login_url='/test/user/login/?autherr=perm'):

    def decorator(request, *args, **kwargs):

        if has_permission(request):

            return view_func(request, *args, **kwargs)

        else:

            return HttpResponseRedirect(login_url)

    return decorator


views.py

from test.common.decorators import login_required, permission_required


@login_required

@permission_required

def sample_page(request):

    "샘플 페이지"

    return render_to_response('test/sample_page.html',   # Template 페이지 

                              RequestContext(request))   # request를 넘김


먼저 login_required를 보면,(로그인 여부 체크)

파라미터로 받는 view_func는 아래 views.py에서 sample_page 함수이다.

그리고, 그 안에 있는 decorator 함수의 request 파라미터는 views.py의 sample_page 함수의 request 파라미터가 넘어간다.

결국, 세션 안에 userid 값이 있으면 로그인된 상태로 판단하여 원래의 view_func(sample_page함수)가 수행되고,

아니면 login_url 페이지로 리다이렉트된다.

나는 login_url의 autherr이라는 get 방식 파라미터를 넘겼는데, login_url 페이지의 상단에서 이 값이 넘어오면 어떤 권한 에러가 났는지 나타내 주기 위해 추가했다.

여기서는 세션에 있는 userid 값으로 로그인여부를 체크하였기 때문에, 로그아웃시에는 반드시 request.session.clear() 함수를 호출해서 세션에 있는 값들을 모두 삭제해 줘야 한다.


permission_required를 보면,(해당 메뉴에 대한 권한여부 체크)

login_required와 같은 구조인데, boolean값을 리턴하는 has_permission 함수를 이용해서 권한이 있는지 확인한다.

나는 권한에 대한 메뉴리스트를 최초에는 DB에서 조회해서 있으면 True를, 없으면 False를 리턴하도록 구현했는데,

성능 문제가 발생할 것 같아, DB에서 조회한 내용을 Django에서 제공하는 Cache에 넣도록 수정했다.

이런 내용은 업무성격에 따라 처리하면 되겠다.


views.py에서는

로그인여부와 권한여부를 체크해야 하는 함수의 바로 위에 @login_required와 @permission_required를 붙여 주기만 하면 된다. 물론, 필요에 따라 둘 중에 하나만 사용해도 된다.


'Python_Django' 카테고리의 다른 글

Django의 cache 사용  (0) 2012.07.25
hash(digest) 호출  (0) 2012.07.25
request의 값 처리  (0) 2012.07.25
CSRF token 생성  (0) 2012.07.25
Django의 session 처리  (0) 2012.07.13
AND

hash(digest) 호출

Python_Django 2012. 7. 25. 10:23


요즘 패스워드를 저장할 때, 복호화가 불가능하도록 hash 함수를 이용해 패스워드를 저장한다.

이런 함수를 Django에서도 제공한다.

아래는 그 사용 예.


from django.utils.hashcompat import hashlib


def digest_password(password):

    return hashlib.sha256(password).hexdigest() # 64자리 문자열 리턴


sha256 외에도 여러 함수를 제공하는데, 그 아래는 공격이 가능하다고 하니 안전하게 256 이상을 사용하는 것이 좋을 것 같다.

※ 혹시, 이걸로 어떻게 하는지 잘 모르는 분들을 위해 간단한 패스워드 처리 방법.
1. 패스워드를 위의 함수를 이용해서 처리한 후 DB에 저장
2. 사용자가 로그인시 입력한 패스워드를 위의 함수를 이용해 변환한 후, DB에 있는 변환된 값과 비교해서
   같으면 로그인 성공, 다르면 틀린 패스워드

'Python_Django' 카테고리의 다른 글

Django의 cache 사용  (0) 2012.07.25
decorator 만들기  (0) 2012.07.25
request의 값 처리  (0) 2012.07.25
CSRF token 생성  (0) 2012.07.25
Django의 session 처리  (0) 2012.07.13
AND


request로부터 값을 얻을 때는 방식(POST,GET)에 따라 다른 클래스를 통해서 값을 얻어야 한다.


POST방식, (python코드에선 다음와 같이 방식 확인 : request.method == 'POST')


1. python에서

   request.POST['user_id']


2. template에서

   {{ request.POST.user_id }}


request.POST가 dictionary 객체



GET 방식이면, request.GET 이고 나머지는 동일


'Python_Django' 카테고리의 다른 글

Django의 cache 사용  (0) 2012.07.25
decorator 만들기  (0) 2012.07.25
hash(digest) 호출  (0) 2012.07.25
CSRF token 생성  (0) 2012.07.25
Django의 session 처리  (0) 2012.07.13
AND

CSRF token 생성

Python_Django 2012. 7. 25. 10:16


공격방법 중에 CSRF 관련된 것이 있는 모양인데(어떻게 하는지는 잘 모르겠고),

Django에선 이런 걸 막는데 필요한 정보가 넘어 오지 않으면 처리 중에 에러를 발생시킨다.

그러니까, 반드시 아래의 절차를 따라서 submit할 때 csrf token를 서버로 보내 줘야 한다.


POST방식으로 데이터를 전송하려면, 입력하는 화면에서 아래와 같은 코드가 추가되어야 한다.


1. view에서 request에 대해 csrf 정보 추가

from django.core.context_processors import csrf


def show_userinfo3(request):

    c = {}

    c.update(csrf(request))

    return render_to_response('user/input.html',c)


위와 같이 귀찮게 매번 코드에 추가하지 않으려면,

settings.py의 TEMPLATE_CONTEXT_PROCESSORS에 django.core.context_processors.csrf 가 포함되어 있으면 아래와 같이만 써도 자동으로 csrf 태그가 정보가 추가됨

def show_userinfo3(request):

    return render_to_response('user/input.html', RequestContext(request))



2. form 태그 안에 {% csrf_token %} 추가

<form action="/pasta/user/userinfo4/" method="post"> {% csrf_token %}


이렇게 해서 웹브라우저에서 user/input.html 화면의 소스보기를 보면, {% csrf_token %} 부분에 아래와 같은 코드가 생성됨

<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='c497670310117cb5fbcdc6bc0657d455' /></div>


이런 csrf 정보가 없으면, submit했을 때 서버에서 처리하다가 CSRF token missing or incorrect 에러가 발생함.


AJAX로 처리할 때도 물론 이 token 정보를 보내 줘야 한다.
그러므로, form 전체를 보내는 게 아니라면 반드시 csrfmiddlewaretoken을 포함시켜 보내 줘야 한다.

'Python_Django' 카테고리의 다른 글

Django의 cache 사용  (0) 2012.07.25
decorator 만들기  (0) 2012.07.25
hash(digest) 호출  (0) 2012.07.25
request의 값 처리  (0) 2012.07.25
Django의 session 처리  (0) 2012.07.13
AND