2013년 10월 28일 월요일

jquery 2중 submit방지

$("form").submit(function() {
    $(":submit",this).attr("disabled", "disabled");
});

이는 W3C가 공식적으로 권고하는 어트리뷰트 설정방식이다

2013년 10월 27일 일요일

당신의 API가 Restful 하지 않은 5가지 증거

restful-api
최근에 “Lorna Mitchell” 라는
Five Clues That Your API isn’t RESTful (당신의 API가 RESTful 하지 않는 5가지 증거)
의 내용을 기본으로 저의 견해를 덧붙여서 재구성 해봤습니다.
참고로 “Lorna Mitchell” 여성 PHP 개발자 이면서, 특히 API 디자인에 대해서
좋은 아이디어 와 글을 포스팅 하고 있습니다.
그녀의 블로그 :http://www.lornajane.net/
개인적으로 일을 하고 있는 도메인 특정상 많은 시스템  과 다양한 업체의
사람들 과 API 연동을 합니다.
API 정의서“를 보면 서두에 “본 API 정의서는 RESTful 기반에…부랴부랴“로 시작을
합니다.
예전에는 “TCP/IP 기반 소켓 통신” 과 “RESTful“이 방식이 아닌
각자 회사 또는 팀에서 개별적으로 정의한 “HTTP/XML “를 사용을 했습니다.
하지만 최근에는 “RESTful” 아키텍쳐를 거의 대부분 사용을 하고 있습니다.
(물론 RESTful  방식이 기술이 아니고 API 디자인 아키텍쳐 입니다.  ^^)
하지만 막상 정의서의 연동 방식을 보면 “도대체 뭐가 RESTful”하게 설계를 했다는 거지?
라는 의문점들이 생깁니다.
그래서 “100% RESTful 아키텍쳐링“을 다 지킬수는 없지만
최소한 아래에 “언급하는 5가지 디자인“에 대해서는 인식할 필요가 있습니다.

1. API의Endpoint가 오직 한개인가?


실제 경험한 사례로써 한번은 어떤 시스템의 연동 문서를 보니
오직 한개의 URL로 fix” 되어 있었습니다.
예를 들어서 URL은 “http://example.com/rest” 이며, 모든 request는
해당 URL로 통합니다.
실제 자원에 대한 정보, 호출 서비스 정보, 파라미터 정보 등은 “XML 형태의 Body 데이터“로
전송 하도록 되어 있었습니다.
REST의 매우 중요한 구성 요소중 하나는 “Resource” 입니다.
“Resource”의 의미는 말 그대로 서비스를 제공 하는 시스템의 자원을 말합니다.
예를 들어서 “영화 예매 시스템“일 경우 “고객“, “예약번호“, “좌석번호“, “영화정보” 같은 것이며
대부분 명사(noun) 형태의 특성을 갖습니다.
또한 각 “Resource“는 “URL로 표시가 되며, 유일한 식별자“로써 표현 됩니다.

2. 모든 요청을 POST 방식으로만 요청 하는가?


RESTful“에서 위에서 언급한 “Resource“를 핸드링하기 위해서 “HTTP의 전송 메서드
를 사용합니다.
자원의 생성은 “POST“, 수정은 “PUT“, 조회는 “GET“, 삭제는 “DELETE” 메서드를 사용합니다.
예를 들면
예약 생성 : POST /reservation/2013012500001
예약 수정 : PUT /reservation/2013012500001
예약 조회 : GET /reservation/2013012500001
예약취소 : DELETE /reservation/2013012500001
하지만 실제 실무에서 이런 부분에 대해서 오해를 잘못해서
아래와 비슷한 방식으로 표현을 했었습니다.
예약 생성 : POST /reservation/2013012500001?cmd=insert
예약 수정 : POST /reservation/2013012500001?cmd=update
예약 조회 : POST /reservation/2013012500001?cmd=select
예약취소 : POST /reservation/2013012500001?cmd=delete
이와 같은 표현은 “RESTful“한 설계라고 할수 없습니다.

3. 응답에 대한 메타데이터를 Body에 포함 하는가?


RESTful” 한 설계에서 “Request/Response“에 대한 메타 데이터는 최대한
HTTP 프로토콜의 방식을 준수 합니다.
클라이언트에서 요청 후 “처리 결과 값이 성공“일 경우 해당 시스템은
처리 결과를 “Body”에 포함하는 것이 아니고, HTTP Status의 값으로써
표현을 합니다.
저 같은 경우는 최대한 “Pure한 HTTP Status” 코드를 사용하고, 추가되는 부분은
별도로 “사용자 정의” 코드로 관리 합니다.
사용자 정보가 없을 경우는 : 404 (Not Found)
요청 정보가 정확하지 않을 경우 : 400 (Bad Request)
인증 실패 : 401 (UNAUTHORIZED)
즉, 이러한 전송에 대한 메타 데이터(결과 값, 세션 키)는 최대한 HTTP 헤더로
선언하고 실제 “Body 데이터“는 위에서 언급한 “Resource의 순수한 데이터
만을 전송 해야 합니다.

4. URL에 동사(Verb)가 포함 되어 있는가?


위에서 언급 했듯이 “RESTful“한 API 설계에서 시스템에서 제공하는 것들은 “Resource“라고
말하며 “URL“로 표기 한다라고 말씀을 드렸습니다.
또한 이러한 “Resource“들은 “명사(noun)“적 특성을 같습니다.
만약 “URL“로 표기할때 동사(Verb)가 포함이 되면 혼돈이 올수 있습니다.
행위적 표현이기때문에 RPC(메서드)를 의미하는지 “Resource“를 하는지
구분이 모호해 집니다.
예를 들어서 예약 상태 정보를 조회를 하기 위해서 “/reservation/001/activate
라는 표현 보다 “/reservation/001/status” 라고 표현을 해야 합니다.

5. URL에 RPC 호출 메서드 명이 있는가?


다양한 시스템 연동 스펙을 보면 아래 와 같이 실제 비지니스 컴포넌트
메서드를 URL에 포함되서 호출하는 경우가 있습니다.

?action=createReservation
?action=modifyReservation
?action=findReservation
?action=removeReservation
또한 URL에 노출되지는 않지만 “Body“안에 “XML” 형태로 선언 하는 경우도 있습니다.
하지만 “RESTful” 설계의 컨셉은 “RPC (Remote Procedure Call)” 목적이 아니고
시스템에서 제공하는 “Resource“를 “bucket“화 하는 것입니다.

My Thought


실제 실무에서 외부연동을 하면 “RESTful” 방식이라고 하지만 실제로는
위에서 언급한 5가지중 몇개를 지키지 않은 경우도 있고, 심지어 5개 모두
적용이 안되는 경우가 많습니다. 또한 HTTP/JSON/XML 통신만 하면
RESTful” 하다고 생각들을 하는 것 같습니다.
개인적으로 아쉬운 부분은 약간의 시간을 내서 검토만 했었으면 어떨까 생각 듭니다.
그런데 필자도 말했지만 모든 연동 시스템이 반드시 “RESTful” 해야 하는 것인가?
에 대해서는 저는 약간 다른 생각을 합니다.
너무 유행이 되어 버려서 요새는 반드시 연동은 “RESTful“로 하려고 해서
오히려 시스템을 복잡하게 만들거나,  심지어 시스템 아키텍쳐가 흔들리는 경우도
있습니다.
2년전 제가 “일본 결제 시스템” 과 연동 한적이 있었습니다. 통신 방식은
HTTP/XML” 이었습니다. 문서가 일본어로 되어 있어서 “번역기” 돌리고
추후에는 한글 번역본을 받아서 작업을 했는데..
연동 항목에 “Data Type, Length, Header 선언부, Null 유무” 등 깔끔하고
심플하면서, 쉽게 구성이 되었습니다.
구현은 어렵지 않았고 “결제 GW 테스트” 서버로 연동 테스트를 했는데
이해하고, 연동 테스트 하는데 3일정도 소요가 되었던것 같습니다.
반면 국내 다른 업체 연동을 할때 “RESTful” 방식으로 한다고 했는데
전혀 “RESTful” 하지도 않고 항목도 불명확해서 완전하게 테스트하는데
무려 한달이란 시간이 소요 되었고, 그 한달이란 기간이 정말 스트레스
엄청 받았었습니다.
또한 모든걸 “JSON“포맷으로 통신하는 것도 문제가 있습니다.
단말 <-> 서버는 “JSON” 포맷이 가볍고 좋을 수 있지만,
실제 “Server to Server” 연동은 가볍고, 빠른게 중요한게 아니라.
데이터의 정합성이 키포인트 입니다.
그럴 경우는 패캣이 다소 무거울수 있더라도 “XML” 포맷으로
해서 보내야 서로 비교 문석하기가 수월 합니다.
대량의 요청을 처리하는 서버는 하나의 바이트도 빼야 한다면
A,B,C” 같은 토큰 방식으로 정의해서 보내야 하는 경우도 있습니다.
이렇듯 “연동 아키텍쳐링“을 할때
명분 없이 단지 “대세”라는 모호한 논리로 맞지도 않은 것을 도입하면
안된다고 생각 합니다.
저희는 개발자이고 아키텍트이기 때문에 “현 Usecase”에 맞는 것인지
냉정한 판단이 필요합니다.
RESTful” 아키텍쳐는 “외부 OpenAPI“에 매우 적합한
연동 아키텍쳐인것 같습니다.
마지막으로 “차니 블로그“님께서 “API Strategy & Strata 2013 Conference” 참관기에서
했던 말을 남기며 본 글을 마무리 하겠습니다.
API 333 목표 : 3초만에 API를 이해하고, 30초만에 API 키를 발급 받아서, 
3분안에 첫번째 요청이 오도록 해라

Spring Namespace 선언 Tip

Spring에서 XML로 빈 설정을 할때 반드시 “네임스페이스“를 선언을 해야 합니다.
이 부분은 스프링을 조금이라도 사용하신 분들이라면 당연한 내용 입니다.
하지만 이러한 당연한 내용속에서도 아키텍쳐적인 선택이 필요 합니다.
흔히 우리가 알고 있는 “스프링 네임스페이스“의 “스키마” 위치를 선언 할때, 현재 사용중인
버전으로 명시적으로 설정 합니다.
<<Spring 2.5.x>>
<<Spring 3.0.x>>
<<Spring 3.1.x>>

물론 위와 같이 명시적으로 현재 사용중인 스프링 버전을 설정하면 에러 없이
구동이 되며, 대부분 스프링 예제에서는 위와 같이 되어 있을 것입니다.
하지만 명시적으로 선언을 하면 문제점이 있습니다. 그건 바로 “스프링 버전 업그레이드
입니다.
예를 들어서 “2.5.x“를 사용하다가 “3.1.x“로 업그레이드시 전부 수정을 해야 할것 입니다.
이렇게 버전 문제 없이 선언 하는 방법이 있습니다.
바로 “버전을 명시하지 않는 것입니다.
스프링은 loading시 버전을 명시 하지 않으면, 현재 loading된 스프링 버전을
인식해서 자동으로 맵핑을 합니다.
즉 아래와 같이 선언을 하면 됩니다.
앞으로 “네임스페이스 로케이션” 설정시 참고 하시기 바랍니다. ^^

자바 스크립트 와 jQuery 디자인 패턴 (Essential JavaScript And jQuery Design Patterns – A Free New Book) [BeyondJ2ee 포스팅]

디자인 패턴“은 어떤 특정 개발언어에만 해당 하는 것이 아니라, 모든 언어에 해당이 됩니다.
자바 스크립트“도 예외는 아닙니다.
디자인 패턴“을 쓰는 이유에 대해서는 쉽게 찾아볼수 있는데요. 아마도
오랫동안 “검증된 로직 과 유지보수“에 대한 관리차원이 아닐까 합니다.
※물론 anti-pattern를 옹호하시는 분들도 계시지만 본 포스팅은 주제는
“디자인 패턴”에 대한 찬양의 글은 아닙니다.
특히 “자바“, “.Net“같은 경우는 성숙된 기간동안 엔터프라이즈 어플리케이션
영역에 있다 보니 참고할 “소스, 온라인 자료 그리고 책“들이 많이 있습니다.
하지만 “자바 스크립트“에 대한 부분은 그다지 자료 찾기가 쉽지가 않더군요.
마침 검색을 하다가 유용하고, 좋은 자료가 있어서 간략하게 알려드리고자 합니다.
관련 자료를 찾다가 “Essential JavaScript And jQuery Design Patterns – A Free New Book” 라는
온라인 무료 자료를 발견 했습니다.
제목에도 알수 있듯 “꼭 알아야할 자바스크립 와 jQuery 디자인 패턴“이라는 책입니다.

※이미지를 클릭하면 해당 사이트로 이동 합니다.
딱 한가지 단점이 있습니다.
코드 와 텍스트 설명이 많기 때문에 직관적으로 이해가 다소 어렵습니다.
.
그래서 “디자인 패턴 다이어그램“과 같이 봐야 더 효과적입니다. 즉, 먼저
다이어그램으로 이해를 하고, 내용과 코드를 보는 것을 추천 드립니다.
마침 “http://www.mcdonaldland.info/” 와 “http://www.dzone.com/“에서
무료로 “PDF 2장” 으로 정리된 문서가 있습니다.
※이미지를 클릭하면 다운로드 받을수 있습니다.
<<MCDONALDLAND>>
<<DZONE>>

.
마지막으로 “jQuery Application Architecture Diagram“로 마무리 하겠습니다.
좋은 작품 많이 만드시기 바랍니다.

2013년 10월 24일 목요일

멀티 쓰레드 환경에서 스프링빈 주의사항

spring-framework
얼마전 “CBT Live” 테스트(실제 서비스 장비에서 성능/기능 통합 테스트)를 하면서
발생한 문제를 공유하고자 합니다.
(1) 증상
외부 시스템에 “A“라는 사용자 정보를 요청 했는데, 확인을 해보니 “B” 사용자
정보가 등록이 되었습니다.
Local” 개발 환경에서는 전혀 그런 증상이 없었고, 실제로 서비스를 하려는
CBT” 개발 환경에서 “성능/기능 테스트“를 하면서 해당 이슈가 발생을 하였습니다.
그래서 “log“를 확인했습니다.
그런데 이상한 점이 “요청을 위임하는 모듈“에서는 정확히 “A” 사용자 정보
가 있었고, “실제 서버와 통신하는 모듈“에서 “B” 사용자 정보를 호출 했습니다.
확인 결과 “Spring Bean“에 대한 개발자의 이해 부족으로 발생된 에러 였습니다.
해결 방법을 말씀 드리기전에 “Spring Application Context“에 대해서
간략하게 설명을 드리겠습니다.
(2) Spring Big Picture
아래의 그림은 “멀티 쓰레드 환경에서 스프링 빈” 관계를 직접 그려봤습니다.
이미지 12
SpringFramework” 기반으로 만든 “어플리케이션”은 기동시 “ApplicationContext“라는
Static Sharing Pool“를 생성 합니다.
좀더 쉽게 설명을 하면 하나의 “싱글톤” 패턴 방식의 구현된 오브젝트가 생성 됩니다.
(정확히는 “싱글톤 패턴”은 아닙니다. 이해를 돕기 위해서)
이렇게 생성된 “ApplicationContext” 영역에 “POJO(Plain Old Java Object) 클래스
들의 오브젝트들이 등록이 됩니다.
“POJO” 클래스는 그냥 “new 하면 스스로 생성”이 가능한 클래스의 형태를 말합니다.
이렇게 등록된 오브젝트를 “Spring Bean“이라고 합니다.
비록 “POJO” 클래스에 “static“으로 선언을 하지 않더라도, “ApplicationContext
가 이미 “Sharing“이 되어 있기때문에 “당연히 등록된 Bean”로
멀티 Thread 환경“에서 서로 공유를 하게 됩니다.
(물론 prototype일 경우는 매번 생성을 합니다.)
위에 그림을 보면 “JVM”에서 하나의 공유 “ApplicationContext”가 생성이 되며
“Spring Bean #1″, “Spring Bean #2″, “Spring Bean #3″ 등
3개의 “Bean”이 등록이 되어 있고, “1번부터 10번 Thread 모두 
동일한 Bean 오브젝트“를 사용을 합니다.
여기까지는 “SpringFramework“를 개발하신 분들은 알고 계실 겁니다.
문제는 개발자들이 Spring Bean의 멤버변수 또한 멀티쓰레드 환경에서
공유가 된다는 것을 간과한다는 것입니다.

이러한 실수는 “Thread Safe” 한 프레임웍(Servlet, Netty Handler, Camel Router, Spring Controller)들에 대한 오해 때문입니다.
문서를 보면 당연히 “Thread Safe“하다고 명시된 부분만을 확인하지, 실제 어디까지
“Thread Safe“하지 않는지 확인을 잘 안한다는 것입니다.
대부분 public 메서드”안에서 선언된 로컬 변수가 “Thread Safe”한거지 “멤버변수”
까지 “Thread Safe” 하다라는 것입니다. 즉 정확한 확인이 필요 합니다.
(3) 해결 방법 및 코딩 가이드
아래는 개발자가 문제를 일으켰단 소스 입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class MemberController{
    @Autowired
    private UserRepository userRepository;
    private Member member = new Member();
    public void createUser(String id, String name) throws Exception {
        member.setID(id);
        member.setName(name);
        userRepository.insertUser(member);
    }
}
7 Line : 멤버변수 “Member” 오브젝트를 생성 합니다.
11 Line : 파라미터 id를 member 오브젝트의 id 속성에 값 설정을 합니다
12 Line : 파라미터 name를 member 오브젝트의 name 속성에 값 설정을 합니다
소스를 보면 위에서 언급한것 처럼 “멤버변수“를 “Bean” 최초 생성시 초기화를 합니다.
그 다음 부터는 “createUser” 메서드에서 계속 재사용을 합니다.
당연히 “createUser” 메서드는 “Thread Safe” 합니다. 하지만 문제는 “멤버변수” 입니다.
해당 코드를 아래와 같이 “리팩토링” 해야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class MemberController{
    @Autowired
    private UserRepository userRepository;
    public void createUser(String id, String name) throws Exception {
        Member member = new Member();
        member.setID(id);
        member.setName(name);
        userRepository.insertUser(member);
    }
}
8 Line :  멤버변수 부분을 삭제하고, 대신 “createUser” 메서드에서 생성을 합니다.
코드는 상당히 간단 합니다. 간혹 물어 보시는 분들이 “저렇게 매번 생성하면” 메모리에
부담이 있는것 아니냐 하시는 분들이 있습니다.
createUser” 메서드가 종료되면 자연스럽게 “member” 오브젝트는 “GC
대상이 됩니다.
(4) Conclusion
멀티 쓰레드 환경에서 스프링빈 주의사항“에 대해서 말씀 드렸습니다.
사실 별거 아닐수 있지만 상당히 주의를 요하며, 개발시에 팀원들에게 숙지를
해야 하며, 팀장 또는 선임 개발자들은 “코드 Inspection“시 반드시
이 부분에 대한 점검이 필요 합니다.
왠만하면 “스프링”에서 “멤버변수”는 “Injection“에 사용하는 “bean“일 경우만
사용하도록 권고 드립니다.
제가 왜 이렇게 강조를 하는 이유는
디버깅이 하기가 너무 어렵다는 이유 입니다. 대부분 개발시에는
개발자 환경은 멀티 쓰레드 환경이 아닙니다. 그렇기 때문에

발견이 거의 되지를 않습니다.  대부분 개발 후반에 성능 테스트
할때 발견이 됩니다.

특정 서버가 CPU를 많이 차지 하거나, 메모리 사용률을 많이 점유하면
thread dump를 떠서 확인을 하지만 이렇게 thread간 race condition
깨진 경우는 dump떠서 확인이 힘들고, 특히나 여러 클래스에 그렇게
사용을 하면 더 찾기가 어렵습니다.
최악의 경우는 프로젝트 막바지는 “멘붕” 상태이기 때문에 “선무당”이 사람 잡는다고
전체 서버에 JDK를 다른 버전을 설치 하거나, WAS를 다시 설치 하거나, 심지어 
OS 커널 또는 버전을 바꿔야 한다든지 더 큰 상황으로 치닫을수 있습니다.
또한 수십, 수백명이 이런 코드를 양산될 경우는 더더욱 트러블 슈팅이 어렵습니다.
이점 유념하시고 개발 하셨으면 합니다.