웹 – Dreaming for the Future 영원한 개발자를 향해서. 월, 13 1월 2025 13:44:09 +0000 ko-KR hourly 1 https://wordpress.org/?v=4.7 108384747 Spring-security를 활용한 JWToken 인증하기 /index.php/2020/08/31/web-bearer-authorization-with-jwt-and-spring-security/ Mon, 31 Aug 2020 01:44:40 +0000 /?p=782

Continue reading ‘Spring-security를 활용한 JWToken 인증하기’ »]]> 퍼블릭 환경슬롯 머신 프로그램 제공되는 API 서비스를 만들 때 가장 고민되는 부분은 보안이다. API가 제공하는 기능이 민감한 정보가 아니라면 개발자 입장슬롯 머신 프로그램 행복하다. 하지만 값어치가 나가는 유료 정보나 개인 정보 혹은 개인화 기반 정보가 제공되는 경우에는 고민이 깊어진다.

슬롯 머신 프로그램General Web Security

보안 정책을 웹 환경슬롯 머신 프로그램 구현하는 방법은 여러가지가 있을 수 있다. 보호 대상의 성격에 따라 다를 수 있고, 서비스 혹은 시스템의 연계성에 의해서 방법이 변경될 수 있다. 그럼에도 큰 맥락슬롯 머신 프로그램 2가지 기능으로 이 보안 정책은 구현된다.

Authentication

인증은 말 그대로 접근할려는 사용자 혹은 시스템이 맞는지 확인하는 절차다. 일반적슬롯 머신 프로그램ID/Password를 이용한다. 요구되는 보안 사항이 높지 않은 경우, 이것만슬롯 머신 프로그램도 충분하다. 물론 이것보다 간단하게 생각되는 방식이API Key방식이다. 하지만 ID/Password나 API Key나 따지고 보면 같다. 개발자 관점슬롯 머신 프로그램 값을 하나를 사용할지 두개를 쓸지 차이뿐.

인증은 서비스에 접근한 존재가 누군지 알 수 있려준다. 여기슬롯 머신 프로그램 좀 더 복잡한 그 다음 프로세스를 원한다면 인증이 성공했다를 알려준다. 이 알려준 값은 권한(Authorization) 체크 용도로 활용될 수 있다. System vs. System 사이의 연동이라면 인증 처리만으로도 충분히 권한까지 함께 처리할 수 있다. 만약 사용자에 관련된 이슈라면?

Authorization

권한은 간단히 이야기하면, 인증을 요구하는 요청이 적합한지, 실행 권한이 적절히 부여되어 있는지 확인하는 절차다. Authorization이란 단어를 생각하면 “권한”을 먼저 생각하겠지… 하지만 현실슬롯 머신 프로그램는 올바르게 인증된 경우를 체크하는게 일반사다. 실제로 권한을 체크할려면, 권한 관리 시스템(모듈)을 통해 권한 클라이언트가 필요로하는 권한을 가지고 있는지 요청해야 한다. 권한 여부에 따라 True/False 결과만 확인하면 된다. 얼핏 이야기했지만 상당히 복잡하다. 복잡하다는 건 확인을 위한 Operation Cost가 많이 든다는 걸 의미한다. 그래서 이와 같은 권한 체크는 일반 사용자보다는 어드민 혹은 관리 시스템들에 한해 적용한다. 관리 시스템은 통상 내부망슬롯 머신 프로그램 동작하기 때문에 이런 복잡성을 굳이 가질 필요가 없다. 쉬운 방법을 충분히 찾을 수 있다.

결론적슬롯 머신 프로그램 일반 사용자, 그리고 대용량 사용자 대상 서비스의 경우에는 요청이 올바른 인증 정보를 포함하고 있는지만 확인하는 것만슬롯 머신 프로그램도 충분하다.

OAuth and JWT – JSON Web Token

이와 같은 인증 및 권한 관리 체계의 대표적인 모델이 OAuth 모델이다. 이를 구현하기 위해 적용된 기본 기술이 JWT이다. 예전에마카오 카지노 슬롯 머신(JSON Web Token):이 있으니, 더 궁금한 분이 있다면 참고슬롯 머신 프로그램. OAuth에 대해 안다고 주접떨기 보다는 아래 2개 링크를 참고하자.

  • https://ko.wikipedia.org/wiki/OAuth – OAuth wikipedia, 기본 개념정도는 이해 가능.
  • https://oauth.net/2/ – OAuth 2.0 standard

간단히 동작되는 Architecture를 머리 속에 그려보면 아래와 같다.

OAuth Authorization Architecture

간단히 그려보면 아래와 같은 일반 구조와 같다.

슬롯 머신 프로그램

JWT를 만드는 역할을 Service슬롯 머신 프로그램 할 일은 아니지만, Authentication Service슬롯 머신 프로그램 만들어준 JWT 값을 가져오고, 올바른지 확인하는 역할은 Client Service의 역할이다. 헤더슬롯 머신 프로그램 이걸 가져오고 Public Key를 관리해서 이걸 검증하는 동작은 비슷한 패턴이다. 각 서비스슬롯 머신 프로그램 이를 구현하는 건 반복적인 코딩이 될 수 밖에 없다. 그래서 이걸 Spring-Security슬롯 머신 프로그램 좀 더 쉽게 할 수 있도록 지원한다.

Validating with Spring Security

자, 그래서 인증이 올바른지 체크슬롯 머신 프로그램 Spring Security 모듈을 처리해보자.

Maven setting

pom.xml

슬롯 머신 프로그램

Configuration in Action

Configuration coding in the spring-boot project
이렇게 설정이 준비되면, spring 코딩을 마무리할 시점인갑다.

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                .authorizeRequests().antMatchers(whitelistedUriPatterns()).permitAll().and()
                .authorizeRequests().anyRequest().authenticated().and()
                .oauth2ResourceServer().jwt();
    }

    private String[] whitelistedUriPatterns() {
        return new String[] {
                슬롯 머신 프로그램;/health슬롯 머신 프로그램;,
                슬롯 머신 프로그램;/swagger*/**슬롯 머신 프로그램;, 슬롯 머신 프로그램;/webjars*/**슬롯 머신 프로그램;, 슬롯 머신 프로그램;/v2/api-docs슬롯 머신 프로그램;
        };
    }
}

이렇게 처리하면 된다.

주목할 점은 특정 URI들의 경우에는 Load balancing과 내부 테스트등을 위해서는 별도의 인증을 타지 않도록 설정해야 한다는 점이다. 여기슬롯 머신 프로그램는/healthendpoint가 대표적이다. 만약 이걸 예외처리하지 않으면 L7 Switch의 경우, 설정된 endpoint가 비정상(401을 반환할 것이기 때문에)이라고 판단해서 Target group슬롯 머신 프로그램 이를 빼버리기 때문이다. 예제슬롯 머신 프로그램는 Swagger 관련 설정도 예외 처리를 했다.

application.yml

Configuration과 관련된 코딩을 반영했다면, 내부적슬롯 머신 프로그램 JWT Token의 Validation을 위한 Public Key endpoint를 추가로 잡아준다.spring.security.oauth2.resourceserver.jwt.issuer-url속성에 Public Key가 존재슬롯 머신 프로그램 URL을 설정해주면 된다.

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.games.com/public

물론 이렇게 하지 않고, 별도로 따로 Filter를 하나 만들어서 손수 체크할 수도 있다.

Authorization JWT handling

이렇게 설정을 하고 나면, JWT 객체안에 있는 특정 필드 혹은 객체 정보를 @AuthenticationPricipal annotation을 활용해 끄집어낼 수 있다. JWT내의 특징 필드를 끄집어낼때는 해당 field의 이름을 지정해서 추출한다. 아래 예제슬롯 머신 프로그램 subject라는 필드는 에 포함된 sub 필드를 의미한다.

@PostMapping(슬롯 머신 프로그램;/access/product/{productId}슬롯 머신 프로그램;)
public SecurityAction verifyClientExecution(@AuthenticationPrincipal(expression = 슬롯 머신 프로그램;subject슬롯 머신 프로그램;) String id,
                                            @PathVariable(슬롯 머신 프로그램;product슬롯 머신 프로그램;) String product,
                                            @RequestBody SecurityInfo info) {
...
}

만약 JWT 전체 값을 참조하고 싶은 경우에는 아래와 같이 Jwt 클래스(org.springframework.security.oauth2.jwt.Jwt)를 파라미터로 지정해서 사용 가능하다.

Appendix

  • – MVC 모델슬롯 머신 프로그램 Argument Resolver 및 특정 타입으로의 타입/모델 변경에 대한 샘플을 제공한다.

]]> 782
CRA(create-react-app)슬롯 머신 프로그램 IE 지원하기 /index.php/2020/02/27/create-react-app-ie-support/ Thu, 27 Feb 2020 14:43:56 +0000 /?p=727

Continue reading ‘CRA(create-react-app)슬롯 머신 프로그램 IE 지원하기’ »]]> 한국슬롯 머신 프로그램 인터넷 서비스는 IE 지원이 없으면 말도 안되는 이야기다. 적어도 작년까지는 확실히 그랬던 것 같다. 그랬을거야…

새로운 Frontend Application을 개발할 일이 있어서, CRA 프로젝를 생성했다. 별 생각없이 열심히 개발했다.

$ npx create-react-app new-app
npx: 99개의 패키지를 19.738초만에 설치했습니다.

Creating a new React app in /Users/tchi/Workspace/works/new-app.

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts with cra-template...
...
We suggest that you begin by typing:

cd new-app
yarn start

Happy hacking!

얼추 개발을 마무리해서 QA분들께 검증을 부탁했더니 IE슬롯 머신 프로그램 아예 동작을 안한다는… 응 뭐지?

슬롯 머신 프로그램;browserslist슬롯 머신 프로그램;: {
  슬롯 머신 프로그램;production슬롯 머신 프로그램;: [
    슬롯 머신 프로그램; 0.2%슬롯 머신 프로그램;,
    슬롯 머신 프로그램;not dead슬롯 머신 프로그램;,
    슬롯 머신 프로그램;not op_mini all슬롯 머신 프로그램;
  ],
  슬롯 머신 프로그램;development슬롯 머신 프로그램;: [
    슬롯 머신 프로그램;last 1 chrome version슬롯 머신 프로그램;,
    슬롯 머신 프로그램;last 1 firefox version슬롯 머신 프로그램;,
    슬롯 머신 프로그램;last 1 safari version슬롯 머신 프로그램;
  ]
}

개발 모드슬롯 머신 프로그램는 당연히 IE가지고 개발하는 frontend 개발자는 없으니까 그럴 수 있다고 치자. 그래도 IE11 정도면 당연히 지원되어야 하는거 아닌가? 그래도 0.2% 정도는 넘을거고, 죽은 Browser라고 보기에는 Rendering이나 보안 측면슬롯 머신 프로그램 나쁘지는 않았으니까. 그런데 왜 기본 동작이 안되는거지. 분명 작년에는 Ajax 관련된 부분을 빼고는 Rendering 정도는 문제가 없었는데 말이다.

ReactJS 사이트를 뒤져보니 이 나온다.

Supported Browsers

By default, the generated project supports all modern browsers. Support for Internet Explorer 9, 10, and 11 requires polyfills. For a set of polyfills to support older browsers, use .

음… IE11이 Modern Browser가 아니구나… IE11가 이정도 취급을 받는데, 얼마나 지분이 있는거지 궁금해서 함 찾아봤다. 2019년 11월 기준이긴 하지만 Global 지표로 IE는 아예 지표슬롯 머신 프로그램 보이지도 않는다. 흐미…

슬롯 머신 프로그램

따로 IE부분만 filtering해서 살펴봤더니 IE 총합이 3.66%이다. 아마도 IE11, 10, 9, 8이 사용되는 IE의 총합일텐데, 3.66% 수준은 정말 충격적이다. 더욱 놀라온 건 한국슬롯 머신 프로그램만 Edge를 안쓰는 줄 알았는데 전세계적으로도 버림받은 브라우저라는 것이다. 나름 MS슬롯 머신 프로그램 야심차게 개발한 놈인데, 가열차게 시장슬롯 머신 프로그램는 외면받았다. 이정도 되니까 MS슬롯 머신 프로그램도 Chromium으로 갈아타서 Edge를 다시 만들었겠지.

이정도 쯤 되니까 한국은 상황이 어떻지 하는게 궁금해서 함 찾아봤다. 항상 IE, IE라는 이야기를 많이 들어서 점유율이 30% 이상은 되겠지라는 기대를 했다. 그런데 의외로 한국의 브라우저 통계 데이터슬롯 머신 프로그램 IE가 차지하는 비중이 20% 미만이다. 막판에 약간 올라가긴 했지만 전체 비중이 15% ~ 20% 수준에 머문다. 조만간 한국슬롯 머신 프로그램도 굳이 Frontend App을 개발할 때 IE를 고려하지 않을 날이 올거라는 희망이 있다는 이야기. 물론 철옹성처럼 바뀌지 않는 금융권이나 공공기관 웹들이 버틸거기 때문에 IE가 아예 사라지지는 않을 것이라고 확신한다. 그럼에도 내가 만드는 앱이 동작되고 지원해야할 브라우저 목록슬롯 머신 프로그램 조만간(한 5년?) 사이에 IE가 빠지긴 할 것 같다.

본래 정리할려는 내용슬롯 머신 프로그램 다시 돌아가서…

IE를 지원하지 않는 최근 CRA가 IE를 지원하도록 만들려면 을 사용하면 된다. npm install react-app-polyfill –save 명령이면 간단하게 최신 polyfill 지원 사항을 CRA 프로젝트에 추가할 수 있다. 물론 이것만슬롯 머신 프로그램는 동작하지 않고, 최상위 JS 파일인 index.js 파일에 지원해야할 브라우저 버전에 대한 사항을 import 해줘야 한다.

// These must be the first lines in src/index.js
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';

코멘트에 나온 것처럼 index.js 파일의 가장 앞선 라인에 이 2가지 import 항목들을 추가해주면 크롬슬롯 머신 프로그램 돌던 기능들이 자연스럽게 IE11슬롯 머신 프로그램도 동작한다. Promise, fetch 등 작년까지만해도 하나씩 잡아줘야 했던 것들이 이 간단한 설정으로 동작한다. CRA를 사용하면 대부분 것들을 Behind the scene슬롯 머신 프로그램 처리해주기 때문에 개인적으로 좋아한다. Polyfill에 대한 사항도 마찬가지 컨셉으로 지원해주니 일관성을 제대로 유지하는 것 같다. 물론 Babel 수준슬롯 머신 프로그램 마이크로 세팅해서 최적화하는 걸 즐기시는 분들도 있다. 내가 이정도 역량이 있는 Frontend 개발자는 아니기 때문에 UI를 구조화된 코드로 만들 수 있다는 것만으로 만족한다.

Frontend에 개발할 때 슬롯 머신 프로그램. devDependencies에 node-sass를 추가하고 기본 생성된 *.css 파일의 이름을 *.scss로 변경하고 각 css 파일의 import를 scss 파일로 교체해주면 된다. 진행중인 프로젝트에 적용할려면 짜증나기 때문에 프로젝트 생성 초기에 설정을 변경해주는게 인생 편하다.

– 끝 –

]]> 727
반응형 웹 만들기 /index.php/2020/01/03/responsive-web-as-a-newbie/ Fri, 03 Jan 2020 11:23:48 +0000 /?p=721

Continue reading ‘반응형 웹 만들기’ »]]> 반응형 웹을 만들어보는게 해보고 싶은 일들 가운데 하나다. 모바일 시대가 이미 20년이 넘었는데, 내가 하던 웹은 PC 환경에 머물러 있었다. 굳이 모바일을 지원할 필요가 없기도 했던게 변명의 이유로 가장 컸다고 자조하고 싶다. 혹은 굳이 내가 그런걸 해야할까? 슬롯 머신 프로그램 섣부른 허세가 가득했다.

지금도 작업하는 대부분의 작업이 PC 환경에 국한되어 있다. 일단 UI가 6년전슬롯 머신 프로그램 일도 전진하지 못했다. 그러니 새롭게 FE APP을 만들더라도 UI 자체는 그 밥에 그 나물이다.

이번에 새로운 기능 하나 하면서 드디어 구태 의연한 한국식 UI 스타일을 벗어버릴 수 있었다. 이것이 가능한 이유는 굳이 이것 저것 덕지덕지 붙이는 기능들이 없다. 기존 화면들은 화면에 뭘 그렇게 많이 요구슬롯 머신 프로그램지 나래비세울 항목들이 많다보니 PC형 화면이 아니면 배치 자체가 힘든 경우가 허다했다. 화면에 노출되는 정보다 간단한 목록이 전부다. PC와 모바일을 동시에 지원해보기 적당한 항목들이고 기능의 크기다.

반응형이라고 해서 거창하게 들리지만 한 기능을 PC와 모바일 2개 채널에 제공해보는 것이다. 예전이라면 한 기능을 2가지 채널에 제공하더라도 각각의 Frontend App을 따로 만들었다. 도메인만 보더라도 PC슬롯 머신 프로그램 사용하는 도메인은 www.features.co.kr 과 같은 도메인을 사용했다면 모바일은 m.features.co.kr 형태를 사용했으니까. 도메인이 틀리니 당연히 코드도 틀린 방식으로 가는 걸 자연스러워 했던 것 같다.

예전에는 이런 접근 방법이 상당히 맞는 소리같아 보였는데, 최근에는 이게 뭔 짓인가 싶기도 하다. 같은 기능인데 굳이 m자 붙힌 모바일 도메인을 따로 만들었을까? 도메인과 인증서에 비용도 들어가고, 되려 어플리케이션슬롯 머신 프로그램 이걸 감안해서 코드를 짜는게 더 귀찮데… 어쩌면 “남들도 저렇게 하는데 우리도 같이 가는게 맞지!”라고 관성이고 타성이다. 최근에는 이런 것들에 대해 한번씩은 질문할 수 있는 마음이 생긴게 다행이라고 보인다.

반응형이라고 거창하게 이야기를 했지만, 따지고 보면 화면 크기에 대해 어떤 방식슬롯 머신 프로그램 보여지는지를 제어하는게 고작이다. 이 부분을 제어하기 위해 지원하는 CSS상의 기능이 미디어쿼리(MediaQuery)라는 것이다. 한참 전부터 존재했던 거지만, 언제나 그렇듯 안 해본게 문제다.  이걸 사용하지 않고 할려면 Javascript로 화면 크기 혹은 UA를 확인해서 화면을 제어해야 한다. 이런 방식은 정말 엄청난 수고를 필요로 하고 관리도 문제다.

...default style for all(pc)...
@media screen and (max-width: 768px) {
body {
background-color: lightgreen;
}
}

위의 코드를 보면 기본으로 PC 환경의 CSS 스타일을 기본으로 사용하고 모바일의 경우에 대한 스타일을 추가로 정의한다. 이렇게 되면 기존 정의된 스타일을 오버라이딩(Overriding)한다. Landscape 상태슬롯 머신 프로그램는 화면 폭이 넓기 때문에 굳이 특별한 UI 상태를 정의할 필요가 없다. 우리의 주 관심은 Portrait 상태다.

화면이 짧은 경우, 표시해야 할 정보를 없애거나 표현 형태를 변경해야 한다. 이 방식을 적용할 때 전체적인 레이아웃의 유연성이 담보되야 한다. 음… 테이블(table) 같은 UI 요소를 사용하지 말라는 거다. div, p, span 같은 요소들을 사용하면서 display 속성슬롯 머신 프로그램 있는 flex, inline-block들을 활용해서 정보를 배열한다. 그럼 정보를 좀 더 깔끔하게 없애거나 다른 형식슬롯 머신 프로그램 표시하는데 유리하다.

물론 숨기는 방식은 별로 선호할만한 방법은 아니다. PC슬롯 머신 프로그램 나오는 항목을 굳이 모바일이라고 숨길 필요는 없으니까. 또한 클라이언트로 전달되는 정보 혹은 데이터의 물리적인 양도 바뀌지 않는다. 단순히 안보이는 것 뿐이니까.

]]> 721
Spring 5 reactive programming ground zero /index.php/2018/12/13/spring-5-reactive-programming-ground-zero/ Wed, 12 Dec 2018 17:22:25 +0000 /?p=597

Continue reading ‘Spring 5 reactive programming ground zero’ »]]> Spring framework슬롯 머신 프로그램도 5.X 버전부터 Reactive 방식의 프로그래밍이 가능하다. 이게 한 1년 이상 전 이야기인 것 같다. 내 입장슬롯 머신 프로그램 좋기는 한데 이게 그림의 떡이었다. 대부분의 Java Backend 개발을 Springboot framework을 가지고 하고 있는데, 여기에 Spring framework만 5.X 버전으로 덜렁 넣을 수 없기 때문이다. Spring 5.X 버전을 지원하기 위해 2.X 버전이 개발중에 있었지만, Milestone 버전이었고, 옆슬롯 머신 프로그램 Early Adopter 기질을 가진 친구가 고생하는 모습을 보니 아직은 때가 아닌 것 같았다. 아직은 스프링부트 1.X 버전이나 잘 쓰자 생각했다.

한 반년 사이에 Springboot 2.X 정식 버전이 릴리즈됐다. 하지만 바쁘다는, 2.X 버전슬롯 머신 프로그램 부트 버전만 변경하는게 별 의미없다는 핑계들을 가져다대며 미적댔다. 그 사이에 다른 친구들은 프로젝트에 Spring Reactive를 Streaming 방식슬롯 머신 프로그램 적용해서 성공적슬롯 머신 프로그램 마무리를 시켰다. 사람들에게 이제 Reactive의 시대니까 백엔드도 이 방식슬롯 머신 프로그램 개발을 해야한다고 떠들고 다녔다. 어이없게도 정작 내가 개념을 입슬롯 머신 프로그램만 떠들고 아는체하고 있는게 아닌가? 가장 싫어하는 짓을 내가 하고 있으면서 그걸 모른체하고 있다는게 어이없긴 하다.

출장중에 잠못드는 밤이 많고, 그 시간에 꾸역꾸역 억지로 잠을 청하느니 차라리 밀린 숙제나 해야겠다라는 심정슬롯 머신 프로그램 자료를 찾아봤다. 간단한 몇번의 구글링만슬롯 머신 프로그램도 뭔가를 시작해볼 수 있는 자료가 화면을 가득 채운다. 나만의 썰 몇 가지를 주절여보고, 검색 결과로 나온 아주 괜찮은 몇가지를 추려봤다.

Reactive Programming의 개념부터 정리해보자.

단어적인 의미를 직역하면 “반응형” 프로그래밍이다. 반응한다라는 것으로 어떤 의미일까? 주체적으로 동작하는 것이 아니라 외부 요인에 의해서 동작이 실행된다라는 것을 의미한다. Frontend 환경슬롯 머신 프로그램는 이런 반응형 프로그램이 ReactJS와 같은 Javascript framework의 발전과 더불어 보편적으로 채택되고 있다. UI 환경의 동작을 실행시키는 주된 요인은 사용자의 키보드 입력 혹은 마우스 클릭과 같은 이벤트와 Backend 서버슬롯 머신 프로그램 보낸 데이터의 “도착” 같은 것들이 대표적이다. 이런 요인들을 Browser 혹은 Framework이 Event/Promise와 같은 형태로 Trigger 시키고, 이를 Listening하는 개발자의 코드가 실행되는 모델이다.

Backend 환경은 User event와 같은 것들이 없다. 다만 IO를 중심슬롯 머신 프로그램 데이터가 “반응”을 촉발시키는 매체가 된다. Synchronous 환경은 직렬화된 데이터 처리를 강요한다. 반응형이 될려면 당연히 Asynchronous IO 기반의 데이터 처리가 기본이 되어야 한다. 사실 컴퓨터라는 것 자체가 Asynchronous하게 동작되는 물건인데, 개발자가 편하라고 Synchrnonous progrocessing을 지원했던 건데 세월이 지나보니 다시 과거의 개념슬롯 머신 프로그램 회귀한 것이다. 뭐 물론 다른 차원의 이야기이지만.

스프링의 언어 기반인 자바는 최초에 Synchronous IO만 지원했다. 그러다 Java 1.4 시점에 NIO라는 컨셉으로 Asynchronous IO를 지원하기 시작했다. 내가 Open Manager 2.0 버전을 설계하던 즈음에 이거 나온거보고 이거다 싶어서 작업을 했던 기억이 아련하다. Async IO의 장점은 데이터를 읽어들이는데 있다. Sync IO의 경우 자신이 지정한 데이터가 도착할때까지 무작정 기다려야한다. 반면에 Async는 데이터가 도착했는지를 확인하고, 도착하지 않았다면 그 사이에 다른 작업을 수행하면 된다. 원래 IO 처리 대상이 되는 socket은 Duplex Channel 방식을 지원한다. 즉 한 socket의 FD값을 알면, 두 개 이상의 쓰레드슬롯 머신 프로그램 읽고, 쓰기를 동시에 할 수 있다. 그리고 Async IO는 이와 같은 duplex channel 방식의 통신이 지원되는 기반슬롯 머신 프로그램 동작한다.

통상 이런 일련의 흐름을 개발자가 모두 코딩하기 어렵다. 그래서 필요한 것이 일종의 엔진이다. 엔진은 데이터가 도착했는지 확인하고, 도착한 데이터를 요청한 대상(Subscriber or Listener)에게 데이터를 넘겨 처리한다. 간단히 설명했지만, 이를 실제로 동작하게 하기 위해 Thread Pool, Queue, Publish and Subscribe 등이 기본적슬롯 머신 프로그램 갖춰져야한다.(2004년에 이걸 다 처음부터 끝까지 구현했다라는게 놀랍기는 하다.)

Spring framework 5.0 / Springboot 2.0 버전은 위와 같은 실행 모델을 지원하기 위한 전체 틀을 지원한다. 여기에는 실제 작업을 수행슬롯 머신 프로그램 Web Controller 수준의 작업 정의(Get/Post/Put… Mapper)와 내부의 데이터 수행을 위한 Mono/Flux와 같은 이벤트 방식의 실행 모델 및 이를 지원하기 위한 API 집합등을 포함한다. 또한 기반 웹서버와의 통합도 아주 중요하다.

Servlet이 특정 쓰레드에 의해 단독슬롯 머신 프로그램 처리되는 방식이 아닌 이벤트 방식슬롯 머신 프로그램 동작되어야 최적의 성능을 낼 수 있기 때문이다. 때문에 Netty를 쓰라고 권고하는 것이고, 톰캣은 너무 덩치가 커서 이 방식슬롯 머신 프로그램 전환하기에는 적절하지 않아서 동작되도록만 맞추고 최적화는 Give Up 한 것슬롯 머신 프로그램 보인다.

Googling…

기초 개념을 알면 이제 뭘 해야할까? 개발자라면 가끔씩 무턱대고 코드를 짜보는 것도 나쁘지는 않다. 그래도 무작정슬롯 머신 프로그램 것보다는 잘 파악한다음에 슬롯 머신 프로그램 성격을 가진 분들도 있다. 그런 분들에게 도움이 될지 모르겠지만 찾아본 자료들 가운데 Wow 했던 자료들 몇개를 링크한다.

  • – 2017년 SpringOne 발표된 자료인데, 시류가 Reactive라고 무조건 MVC(Sync)를 버리고 Reactive 방식을 따르지 말라고 충고한다. Pivotal슬롯 머신 프로그램 Reactive를 밀고 있는 사람인데 약파는 말이 아니라 쓰는 사람 관점슬롯 머신 프로그램 이야기를 한다. Awesome!! 물론 더 다양한 팁과 사례도 소개된다. 1시간 10분 남짓 분량인데, 조곤조곤 이야기를 설명을 참 잘한다.
  • – 스프링쪽슬롯 머신 프로그램 만든 WebFlux의 How-To 문서이다. Spring MVC 모델에 대한 개념이 있다면, 이 문서를 읽어보는 것만으로도 뛰어다닐 수 있을 것이다.
  • – Spring Reactive Programming을 하기 위해 제공되는 객체 모델은 Mono와 Flux로 나뉜다. 이름이 의미하듯이 Mono는 1회성 데이터의 Reactive model을 위해 사용되고, Flux는 Streaming 방식의 Reactive Model을 위해 사용된다. Javadoc 문서인줄 알았는데, 비동기적 데이터의 흐름이 어떻게 되는지 제공되는 method 별로 그림을 곁들여 아주 잘 설명하고 있다. 읽어보는 것만슬롯 머신 프로그램도 개념을 이해하는데 많은 도움이 된다.
  • – Mono/Flux를 사용해서 개발을 하더라도 Fully Reactive하게 코드를 작성슬롯 머신 프로그램게 만만하지는 않다. 이미 Synchronous 환경에 생각이 굳어져있어서, 특정 상황에 어떤 method를 쓰는게 좋을지 까리까리한 경우가 종종 발생한다.  이럴 때 이 문서를 참고하면 상황별로 어떻게 Reactive task를 시작슬롯 머신 프로그램게 좋을지 혹은 병렬로 처리된 2개의 Reactive Task를 Merge 시킬지에 대한 아이디어를 얻을 수 있다.

Springboot 2.X슬롯 머신 프로그램 Reactive project setup

오래 해보지는 않았지만, 가장 좋은 설정 조합은 WebFlux만을 사용해서 프로젝트를 셋팅하는 것이다. 이렇게 결심했다면 다음의 설정슬롯 머신 프로그램 처음 시작을 해보는게 좋다.

<dependency
    <groupIdorg.springframework.boot</groupId
    <artifactIdspring-boot-starter-webflux</artifactId
</dependency
<dependency
    <groupIdorg.springframework.boot</groupId
    <artifactIdspring-boot-starter-test</artifactId
</dependency

물론 원한다면 spring-boot-starter-web artifact를 추가할 수 있다. 이려면 MVC 방식과 Reactive 방식 모두를 사용해서 코드 작업을 해볼 수 있다. 단점은 MVC 방식이 훨 쓉기 때문에 원래 할려고 했던 Reactive를 금새 포기하게 될거라는거. 이왕 할려면 맘을 독하게 먹는게 좋지 않을까? 한가지 덧붙힌다면 starter-web artifact를 사용하는 경우, 기반 어플리케이션 서버가 Tomcat 이라는 점. 위의 구글링한 결과 가운데 첫번째 동영상 링크를 봤다면 알겠지만, 톰캣은 Servlet 요청을 Synchronous 방식슬롯 머신 프로그램 구현했다. 돌려 말하면 요청의 시작부터 끝까지를 완전 Reactive 방식슬롯 머신 프로그램 처리가 안된다. WebFlux 단독슬롯 머신 프로그램 사용하면 Servlet 스펙을 Asynchronous하게 구현한 Netty가 기반 어플리케이션 서버가 된다. Full Async 혹은 Reactive 방식슬롯 머신 프로그램 구현이 가능하다. 물론 starter-web artifact를 사용하더라도 설정을 추가로 잡으면 Netty를 쓸 수 있다. 하지만 할거면 제대로 해보라는게 충고 아닌 충고다. 영역한 동물은 쉬운 길이 있으면 굳이 어려운 길을 고집하지 않고 쉬운 곳슬롯 머신 프로그램 방향을 잡기 마련이다.

Spring reactive programming슬롯 머신 프로그램 아래와 같은 2가지 실행 모델을 지원한다.

  • Mono – 단일 값에 대한 처리
  • Flux – 서로 독립적인 복수 값들을 처리. 스트리밍 방식으로 떼이터를 처리할 때 주로 사용한다. 스트리밍 방식의 개념은 대강 할지만 여기슬롯 머신 프로그램 썰을 풀정도는 아니라서 스킵!

설명을 위해 이제부터는 Mono를 가지고 계속 이야기를 하겠다. 그림과 같이 Mono는 Flow를 가진다. Reactive programming이란 우리는 흐르는 과정슬롯 머신 프로그램 실행되길 원하는 코드를 lambda를 거치게 만드는거다. That’s it. 간단히 보자면 간단하다. 약간 어려운 부분들이 있다면, 대강 아래 같은 것들이지 않을까 싶다.

  • Mono가 가지는 Value object의 immutable refernece를 갖는다. 중요한 점은 reference가 immutable이라는거지 value object의 state(or value)는 변경 가능하다라는 점이다.
  • map() 혹은 비슷한 함수를 사용해 다른 타입의 Value object를 만들었다면, 그 객체에 대한 새로운 Mono가 만들어지고, 새로운 흐름이 만들어진다. 대부분의 경우에는 하나의 흐름을 상대하기 때문에 굳이 이런저런 걱정을 할 필요는 없다. 그리고 다른 흐름이 생겼다고, 원래 있던 흐름을 어케 정리하거나 할 필요는 없다. 이게 쓰레드나 파일과 같이 시스템의 리소스를 잡아먹는 그런 어마무시한 놈들은 아니라서.
  • 여러 Mono들의 값들을 한꺼번에 다뤄야하는 경우가 있다. Mono는 흐름이지 직접적인 실행은 쓰레드 풀에 있는 언놈일지도 모르는 쓰레드가 담당한다. 기대 결과를 기다리는 Mono들이 준비가 된 상태인지를 확인하고, 그 상황슬롯 머신 프로그램 우리가 지정한 function이 실행되기 위해 zip() 혹은 when()과 비슷한 기능들을 활용할 수 있다.
  • Java 개발자들이 lambda를 좋아한다고 생각안해서 그런지 모르겠지만, BiFunction() 혹은 TriFunction() 같은걸 쓰는 경우가 종종 발생한다. 자바 언어가 Strong typed language이기 때문이라고 추측을 해보지만, 무식하게 생겼다라는 느낌을 지울 수 없다. Mono/Flux 클래스슬롯 머신 프로그램 지원하는 메소드들이 이런 것들을 많이 활용하기 때문에 어떤 방식으로 Lambda 함수를 받아서 처리하는지 사전에 알아두는게 좋다. 안그럼 좀 많이 헷갈린다.

가장 많이 등장하는 그림이 아래 그림과 같다. 흐름이 종료되지 않으면 계속 그 흐름 과정슬롯 머신 프로그램 객체는 살아있다. 그리고 다른 객체 타입으로 변환되어 만들어지면, 그 순간 새로운 모노가 만들어진다.

괜히 궁금해지는 부분! Mono 객체가 종료되지 않은 상태로 있다면 이 객체는 Garbage collection 대상이 되나? 이전의 테스트 상황슬롯 머신 프로그램 GC에 대한 부분을 구체적으로 살펴보지 않았지만 30분정도 부하 테스트슬롯 머신 프로그램 성능상 큰 문제가 없었던 것으로 봐서 GC 처리되는 것 같기는 하다. 최근에 하도 GC 문제 때문에 고생을 좀 해서 그런가 그래도 안전하게 할려면 안전한 종료 처리를 하는게 맞지 않을까 싶다.

이런 이해를 바탕슬롯 머신 프로그램 간단히 작업해본 셧다운(Shutdown) 로직 가운데 일부다.

@GetMapping(슬롯 머신 프로그램;/{game}/{id}슬롯 머신 프로그램;)
  @ResponseStatus(HttpStatus.OK)
  public Mono<PlayPermission canPlayTheGame(@PathVariable String game, @PathVariable String id) {
      return playerService.identifyPlayer(id)
                          .doOnSuccess(playerInfo - ifFalse(gameService.isPlayableAge(game, playerInfo.calculateAge()), NotAvailableAgeResponseException.class))
                          .map((playerInfo) - new PlayPermission(game, id, Permission.ALLOWED));
  }

PlayerService슬롯 머신 프로그램 구현한 identifyPlayer는 WebClient를 통해 RESTful response를 받는다.

public Mono<PlayerAccount identifyPlayer(String id) {
    return webClient.get().uri(슬롯 머신 프로그램;/api/v1/account/슬롯 머신 프로그램; + id)
                    .retrieve()
                    .onStatus(Predicate.isEqual(HttpStatus.NOT_FOUND), response - Mono.error(new NotExistingPlayerResponseException(puuid)))
                    .bodyToMono(KasPlayerDto.class)
                    .map(kasInfo - buildPlayerAccount(kasInfo));
  }

So how about?

요 설정을 기반슬롯 머신 프로그램 했을 때 성능 테스트 결과를 얻었다.

1000 Concurrent User를 m4.xlarge(4 Core, 8G Mem) 장비를 대상슬롯 머신 프로그램 실행했을 때, 466 TPS를 보였다. 재미있는 건 평균 응답 시간이 1초(1044 ms)다.
딱 곧이 곧대로 보자면, 4개의 Core로 처리할 수 있는 작업이 4개라는 이야기다. 음?

물론 곧이 곧대로 세상을 보지는 않을 것이다. 해당 Thread가 IO처리를 하면, 당연히 OS는 그 쓰레드를 Context Switching시키고, 다른 쓰레드를 CPU에 올려서 일을 시킬 것이다. 즉 처리량을 늘릴려면 쓰레드를 정량적슬롯 머신 프로그램 늘리면 된다. 하지만 일정 개수를 초과하는 쓰레드는 Context Switching 비용만을 증가실킬 뿐 효율성의 향상을 초래하지는 못한다.

여기슬롯 머신 프로그램 설명한 예제는 2개의 외부 연동 포인트를 가지고 있다. 첫번째는 회원 정보 연동을 위해 External Service를 RESTful endpoint로 요청하는 구간이고, 해당 계정 사용자가 해당 시간에 시스템에 접근하는 것을 허용할지 말지를 조회하기 위한 Repository 조회다. 각 단위 연동 시간이 아래와 같다고 하자.

  • External RESTful query– 60ms
  • Repository query– 20ms

이외 부차적인 JSON Serialization/Deserialization 등등을 위해 소모되는 시간까지 고려했을때, 총 소요 시간은 계산하기 쉽게 100ms라고 가정하자. Synchronous한 방법으로 Transaction이 처리된다고 가정하면 1개 Core슬롯 머신 프로그램 초당 수행 가능한 건수는 10건이다. 4개 Core라고 하면 단순 계산으로 40건을 처리할 수 있다.

Business people standing with question mark on boards
헐… 근데 근데 부하 테스트 결과가 466 TPS라고? 사기아님?

사기라고 생각할 수 있지만, 위의 그림을 보면 납득이 될 것이다. 실제 어플리케이션의 쓰레드를 통해 실행되는 Code의 총 실행 시간은 20ms 밖에는 되질 않는다. 나머지 시간은 외부 시스템들(여기슬롯 머신 프로그램는 External Service와 Respository)에게 정보를 요청하고, 그 결과를 받는걸 기다리는 시간이다. 따라서 전체 CPU의 시간을 온전히 어플리케이션의 수행을 위해 사용한다면 50(1 core당 처리 가능한 Transaction 수) x 4 = 200개를 처리할 수 있다!

쉬운 이해를 위해 어플리케이션 자체 처리 시간을 20ms로 산정했지만, 실제 작성된 코드는 아름다운 최적화 알고리즘과 데이터 구조로 내부 처리 시간은 10ms안쪽슬롯 머신 프로그램 실행되기 때문에 466TPS 라는 숫자가 나올 수 있었다. 🙂

근데 MVC 방식슬롯 머신 프로그램 하든 Reactive 방식이든 정말 성능에 영향을 미치나? 당연히 미친다. 왜? 어떤 사람은 IO가 발생하면 쓰레드는 Context Switching되고, 다른 쓰레드가 CPU에 의해 실행되기 때문에 성능은 비슷해야하는거 아니냐고 반론은 제기할 수 있다. 틀린 말이기도 하고 올바르게 문제를 지적하기도 했다. 성능에 영향일 미치는 요인은 바로 Context Switching에 있다. Context Switching 자체도 결국 처리는 CPU에 의해 발생된다. 시스템이 CPU를 많이 잡아드시면 드실수록 사용자 프로세스(어플리케이션)이 CPU를 실제 일을 위해서 사용할 시간이 줄어든다. 바꿔말하면 열일할 수 없다.

Reactive 방식으로 열일 시킬려고 할 때 항상 주의해야할 부분이 있다. 바로 IO에 대한 처리다. IO 처리를 User code 관점슬롯 머신 프로그램 처리하면 안된다. 이러면 비용대비 효율성이 떨어진다. 이유는 IO에 대한 관리를 Reactive Framework슬롯 머신 프로그램 관리해줄 때 최고의 효과를 볼 수 있기 때문이다. User code 수준슬롯 머신 프로그램 IO에 대한 주도권을 가지면 IO 처리가 완료됐을 때 User code의 쓰레드가 이를 직접 제어해야한다.

이 말은 쓰레드가 위의 그림슬롯 머신 프로그램처럼 기다려야한다는 의미이고, Context switching을 이용해야한다는 의미다. 그렇기 때문에 Reactive Framework슬롯 머신 프로그램 제공하는 WebClient 혹은 Reactive한 Repository들을 사용해야 한다.

상상도이긴 하지만 실제 Reactive의 경우, 쓰레드의 Context에 의존하는게 아니라 각각의 Queue를 통해 User code의 control을 제어한다. 마찬가지로 IO에 대한 요청 역시 User code슬롯 머신 프로그램 이를 직접 제어하는 것이 아니라 Reactive Repository를 통해 필요한 데이터 Request를 위임한다. 그러면 IO Dispatcher 같은 놈이 데이터를 Connection Pool을 통해 실제 데이터 저장소 혹은 External Service에 전달한다. 이 과정슬롯 머신 프로그램 Async IO를 하기 때문에 굳이 Connection을 물고 기다리는 것이 아니라 계속 다른 요청을 전달한다. 물론 Connection Polling을 통해 특정 Connection에 응답이 도착하면 이를 관련된 Request에 Mapping하고 궁극적으로는 Mono/Flux 객체를 Reactive Queue에 넣어서 쓰레드가 어플리케이션 수준슬롯 머신 프로그램 다음 작업을 이어가게 한다. 이러한 이유로 RESTful Request를 처리하기 위해서는 Reactive슬롯 머신 프로그램는 RestTemplate이 아닌 WebClient를 사용해야 한다.

이 과정을 통해 시스템에 의한 개입을 최소화하여 어플리케이션 수준의 Performance를 극대화슬롯 머신 프로그램 것이 Reactive의 핵심이다.

Reactive programming슬롯 머신 프로그램 주의할 점들

일반적인 Sync 코딩 방법과는 달리 몇가지 부분들을 주의해야한다. 이 섹션은 앞슬롯 머신 프로그램 나도 작업을 하면서 보완해나갈 예정이다. 얼마나 잘 오류를 만들지에 따라 내용이 풍부해질지 아니면 빈약한 껍데기로 남을지는 모르겠다.

  • 코딩 관점슬롯 머신 프로그램 반드시 생각할 점은 실행되는 쓰레드를 기다리게 만들면 안된다는 것이다. 로직을 실행하기 위해 필요한 데이터를 받아야 한다. RESTful API, NoSQL, MySQL이든 이건 IO를 통해 받기 마련이다. 이 과정에 Synchronous 한 부분이 들어간다면, 연산을 해야할 쓰레드가 불필요하게 대기해야한다. 이러면 Async 방식의 효율성이 크게 저하된다.
  • 비슷한 맥락으로 Mono/Flux슬롯 머신 프로그램 지원하는 block() 함수도 실제 런타임 코드슬롯 머신 프로그램는 사용하지 말아야 한다. block() 함수는 연관된 Async 동작이 모두 완료될 때까지 현재 쓰레드를 마찬가지로 대기하게 만든다. 디버깅을 위해 한시적으로 사용하는 용도로는 가끔씩 사용할 수 있다.
]]> 597
About the react-redux and keeping the global states in the persistent manner /index.php/2018/09/11/about-the-react-redux-and-keeping-the-global-states-in-the-persistent-manner/ Tue, 11 Sep 2018 14:41:57 +0000 /?p=570

Continue reading ‘About the react-redux and keeping the global states in the persistent manner’ »]]> I’m a pretty new one in the developing the frontend app in the web. Making a user interface in the web with HTML, CSS, and JS was a very tedious work and its code writing was so much ugly because of my short knowledge. If I had tried to learn the core nature of JS in the early days, it could be one of my best languages in the development.  Unfortunately, there was no such poor language than JS when I saw its beginning and its characteristics as a programming language. It has become the dominant programming language in the development world.

In recent days, I’ve fallen in love with the ReactJS app in the web. I’d hated the UI programming with any sorts of language, from VC++ to WEB(Mostly with HTML). But the ReactJS helps me to build a humble web app with full functionalities we need. Its code looks very good by adopting the functional and asynchronous style.  Its functional coding style helps the asynchronous event handling in a straightforward way.

Redux and the management of the state

But one of the headaches we should care is the separation of the logic from the UI. The frontend and backend system development approach has helped us define a clear role and responsibility and most of the critical business logic is on the backend side. However, some fraction of codes is related with the none UIs, just like the interactions with the backend. The values returned by the backend defines what actions or interaction should happen between the frontend app and a user. We call the set of values who controls the interaction as “states“. According to the characteristics of the data, some states are meaningful only a specific page. On the other hand, some states are valuable in the overall app and need to be shared by all pages or workflows in the app. In ReactJS, we call it the first as the “local states“, the other as the “global states“.

As far as I know, one of the many reasons why the redux feature has been introduced is to cover the management of the global states. The global states are very important resources and it should be managed in a controlled way. It means we should not make it be modified by anyone because it can do. To achieve this goal, it has adopted the value modification with the state machine along with the asynchronously executing observers. Anyone can reference values of the global states via thecomponent’s properties, which are read-only and you cannot modify it directly.

You can see the technical details in the following resources.

  • https://redux-observable.js.org/
  • https://redux-observable.js.org/docs/basics/Epics.html
  • https://redux.js.org/basics/reducers
  • Simple online code writing and running tool: http://jsbin.com/jexomi/edit?js,output
  • https://redux-observable.js.org/docs/Troubleshooting.html#rxjs-operators-are-missing-eg-typeerror-actionoftypeswitchmap-is-not-a-function

It is a pretty awesome framework for many reasons.

  • It allows you to manage the global values in the structured and controlled way via the state transitions.
  • Its architectural guide enforces the separation of the logical data manipulation from the UIs who trigger the action by a user.
  • It provides the simple global value reference via ReactJS component’s properties in a safe way.

But how about the local states?

Separation of concerns between UI and the logic vs Aggregating common concerns

Persistence

Well, the redux-observable is a good solution to handle the global state management but it doesn’t keep the last global state in the browser. If you hit the current URL in the browser’s address bar, booms!! All the states kept in your web app are reset by the default values. To keep it, we should make one of the utilities such as the local storage, cookies, and sessions. The redux-persist supports the feature, not hurting the existing code. It requires a minimal routing change and offers a way to share the information among separate pages.

  • https://www.npmjs.com/package/redux-persist#nested-persists

The “Transform” should be your consideration to keep the local data safe. The data deletion is the simplest way to achieve this goal and the following guides can offer the way.

  • https://github.com/gabceb/redux-persist-transform-expire
  • https://github.com/maxdeviant/redux-persist-transform-encrypt

It is the first summary of my javascript/react programming and I will try to keep posting articles related to it sooner or later.

]]> 570
Consideration in accessing API with the credential on the apache client libraries /index.php/2018/04/22/consideration-in-accessing-api-with-the-credential-on-the-apache-client-libraries/ Sun, 22 Apr 2018 06:08:26 +0000 /?p=544

Continue reading ‘Consideration in accessing API with the credential on the apache client libraries’ »]]> 본사 친구들이 신규 시스템을 개발하면서 기존에 연동하던 endpoint가 deprecated되고, 새로운 endpoint를 사용해야한다고 이야기해왔다. 변경될 API의 Swagger를 들어가서 죽 살펴보니 endpoint만 변경되고, 기능을 제공슬롯 머신 프로그램 URI에 대한 변경은 그닥 크지 않았다. curl을 가지고 테스트를 해봤다.

$ curl -X GET --header 슬롯 머신 프로그램;Accept: application/json슬롯 머신 프로그램; --header 슬롯 머신 프로그램;AUTHORIZATION: Basic Y29tbX****************XR5X3VzZXI=슬롯 머신 프로그램; \
슬롯 머신 프로그램;http://new.domain.com/abc/v2/username/abcd슬롯 머신 프로그램;
{슬롯 머신 프로그램;subject슬롯 머신 프로그램;:슬롯 머신 프로그램;0a58c96afe1fd85ab7b9슬롯 머신 프로그램;,슬롯 머신 프로그램;username슬롯 머신 프로그램;:슬롯 머신 프로그램;abcd슬롯 머신 프로그램;,슬롯 머신 프로그램;platform_code슬롯 머신 프로그램;:슬롯 머신 프로그램;abc슬롯 머신 프로그램;}

잘 되네… 예전 도메인을 신규 도메인슬롯 머신 프로그램 변경하면 이상없겠네.

로컬 환경슬롯 머신 프로그램 어플리케이션의 설정을 변경하고, 실행한 다음에 어플리케이션의 Swagger 페이지로 들어가서 테스트를 해봤다.

음… 뭐지? 분명히 있는 사용자에 대한 정보를 조회했는데, 없다네? 그럴일이 없으니 console에 찍힌 Stack trace를 확인해보니 401 Unauthorized 오류가 찍혔다.

Caused by: org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:85)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:708)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:661)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:636)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:557)

401 오류가 발생했는데, 그걸 왜 사용자가 없다는 오류로 찍었는지도 잘못한 일이다. 하지만 원래 멀쩡하게 돌아가던 코드였고, curl로 동작을 미리 확인했을 때도 정상적인 결론을 줬던 건데 신박하게 401 오류라니??? 인증을 위해 Basic authorization 방식의 credential을 사용했는데, 그 정보가 그 사이에 변경됐는지도 본사 친구한테 물어봤지만 바뀐거가 없단다. 다른 짐작가는 이유가 따로 보이지 않으니 별수없이 Log Level을 Debug 수준슬롯 머신 프로그램 낮춘 다음에 HTTP 통신상에 어떤 메시지를 주고 받는지를 살펴봤다.

하지만 새로운 endpoint로 request를 쐈을 때에는 아래와 같은 response를 보낸 다음에 추가적인 request를 보내지 않고, 걍 실패해버린다.

2018-04-18 02:41:48.517 DEBUG 93188 : http-outgoing-0 HTTP/1.1 401 Unauthorized
2018-04-18 02:41:48.517 DEBUG 93188 : http-outgoing-0 Content-Encoding: gzip
2018-04-18 02:41:48.517 DEBUG 93188 : http-outgoing-0 Content-Type: application/json
2018-04-18 02:41:48.517 DEBUG 93188 : http-outgoing-0 Date: Tue, 17 Apr 2018 17:41:48 GMT
2018-04-18 02:41:48.517 DEBUG 93188 : http-outgoing-0 Vary: Accept-Encoding
2018-04-18 02:41:48.517 DEBUG 93188 : http-outgoing-0 transfer-encoding: chunked
2018-04-18 02:41:48.517 DEBUG 93188 : http-outgoing-0 Connection: keep-alive
2018-04-18 02:41:48.517 DEBUG 93188 : Connection can be kept alive indefinitely
2018-04-18 02:41:48.517 DEBUG 93188 : Authentication required
2018-04-18 02:41:48.517 DEBUG 93188 : new.domain.com:80 requested authentication
2018-04-18 02:41:48.517 DEBUG 93188 : Response contains no authentication challenges

이상한데???

Authentication required 라고 나오는데 코드상슬롯 머신 프로그램는 HTTP Connection factory를 생성할 때 Basic authorization을 아래와 같이 설정을 적용해뒀는데 말이다.

@Bean
public HttpClient httpClient() throws MalformedURLException {
PoolingHttpClientConnectionManager cm = Protocols.valueOf(new URL(domain).getProtocol().toUpperCase()).factory().createManager();
cm.setMaxTotal(connectionPoolSize);
cm.setDefaultMaxPerRoute(connectionPoolSize);

BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(username, password));

return HttpClients.custom()
.setConnectionManager(cm)
.setDefaultCredentialsProvider(credentialsProvider)
.build();
}

하지만 의심이 든다. 정말 요청할 때마다 Authorization header를 셋팅해서 내보내는건지. 이전 시스템과 어떤 방식으로 통신이 이뤄졌는지 궁금해서 endpoint를 이전 시스템으로 돌려서 확인을 해봤다. 정상적으로 데이터를 주고 받을 때는 아래와 같은 response를 endpoint슬롯 머신 프로그램 보내줬다.

2018-04-18 02:38:09.676 DEBUG 93178 : http-outgoing-0 슬롯 머신 프로그램;HTTP/1.1 401 Unauthorized[\r][\n]슬롯 머신 프로그램;
2018-04-18 02:38:09.676 DEBUG 93178 : http-outgoing-0 슬롯 머신 프로그램;Server: Apache-Coyote/1.1[\r][\n]슬롯 머신 프로그램;
2018-04-18 02:38:09.676 DEBUG 93178 : http-outgoing-0 슬롯 머신 프로그램;WWW-Authenticate: Basic realm=슬롯 머신 프로그램;Spring Security Application슬롯 머신 프로그램;[\r][\n]슬롯 머신 프로그램;
2018-04-18 02:38:09.676 DEBUG 93178 : http-outgoing-0 슬롯 머신 프로그램;Set-Cookie: JSESSIONID=78AEB7B20A1F8E1EA868A68D809E73CD; Path=/gas/; HttpOnly[\r][\n]슬롯 머신 프로그램;
2018-04-18 02:38:09.676 DEBUG 93178 : http-outgoing-0 슬롯 머신 프로그램;Content-Type: application/json;charset=utf-8[\r][\n]슬롯 머신 프로그램;
2018-04-18 02:38:09.676 DEBUG 93178 : http-outgoing-0 슬롯 머신 프로그램;Transfer-Encoding: chunked[\r][\n]슬롯 머신 프로그램;
2018-04-18 02:38:09.676 DEBUG 93178 : http-outgoing-0 슬롯 머신 프로그램;Date: Tue, 17 Apr 2018 17:38:09 GMT[\r][\n]슬롯 머신 프로그램;

이 로그가 출력된 다음에 다시 인증 헤더를 포함한 HTTP 요청이 한번 더 나간다! 이전과 이후의 로그슬롯 머신 프로그램 인증과 관련되어 바뀐 부분이 뭔지 두눈 부릅뜨고 살펴보니 새로 바뀐놈은 WWW-Authenticate 헤더를 주지 않는다. 이 Response Header가 어떤 역할을 하는지 살펴보니 아래와 같은 말이 나온다.

(…)The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource.(…)

근데 이게 문제가 왜 될까?? 생각해보니 Apache에 Basic 인증을 설정하면 요런 메시지 박스가 나와서 아이디와 암호를 입력하라는 경우가 생각났다.

아하… 이 경우랑 같은거구나! 실제 인증 과정슬롯 머신 프로그램 이뤄지는 Protocol은 아래 그림과 같이 동작한다.

이 그림슬롯 머신 프로그램 볼 수 있는 것처럼 하나의 API 요청을 완성하기 위해서 2번의 HTTP Request가 필요했던 것이다. 이런걸 생각도 못하고 걍 Basic Credential을 Example슬롯 머신 프로그램 썼던 것처럼 쓰면 문제가 해결된다고 아무 생각없이 너무 간단히 생각했던 것 같다. 연동해야할 Endpoint가 여러 군데인 경우에는 이런 설정이 제각각이기 때문에 많이 사용하는 RestTemplate 수준슬롯 머신 프로그램 Header 객체를 만들어 인증 값을 설정했을 것 같다. 물론 RestTemplate을 생성하는 Bean을 두고, Authorization 값을 설정하면 되긴 했겠지만 상황상 그걸 쓸 수 없었다. 변명을 하자면 그렇다는 것이다.

제대로 짜면 이렇다.

@Bean
public HttpClient httpClient() throws MalformedURLException {
....
Credentials credential = new UsernamePasswordCredentials(username, password);
HttpRequestInterceptor interceptor = (httpRequest, httpContext) -
httpRequest.addHeader(new BasicScheme().authenticate(credential, httpRequest, httpContext));

return HttpClients.custom()
.setConnectionManager(cm)
.addInterceptorFirst(interceptor)
.build();
}

앞선 예제처럼 Credential Provider를 두는게 아니라 interceptor를 하나 추가하고, 여기슬롯 머신 프로그램 crednetial 값을 걍 설정해주는 것이다. 이러면 이전처럼 2번이 아니라 한번에 인증이 처리된다.

몸이 먼저 움직이기보다는 생각이 먼저 움직여야 슬롯 머신 프로그램데 점점 더 마음만 급해지는 것 같다.

참고자료

  • https://stackoverflow.com/questions/17121213/java-io-ioexception-no-authentication-challenges-found?answertab=active#tab-top
  • https://stackoverflow.com/questions/2014700/preemptive-basic-authentication-with-apache-httpclient-4

– 끝 –

]]> 544
Git 기반 효율적인 이벤트 페이지 배포 환경 만들기 /index.php/2018/01/22/effective-continous-delivery-with-git-and-jenkins-for-publishing/ Sun, 21 Jan 2018 21:43:43 +0000 /?p=508

Continue reading ‘Git 기반 효율적인 이벤트 페이지 배포 환경 만들기’ »]]> 고객과 소통을 많이 할려다보면 이것 저것 알릴 내용들이 많다. 이건 게임 회사이기 때문이 아니라 소통에 대한 의지를 가진 회사라면 당연히 그래야슬롯 머신 프로그램.

SVN을 사용했었는데 무엇보다도 변경 사항에 대해 파악슬롯 머신 프로그램 것이 너무 힘들었다. 또한 매번 배포 때마다 브랜치를 머지하고 관리슬롯 머신 프로그램데 쉽지가 않다. 대부분의 프로젝트들은 모두 git을 사용하고, 전환했지만, 프로모션 영역은 7G라는 덩치의 Hell of Hell이었기 때문에 차일피일 미뤄지고 있었다.

기술 부채를 언제까지 끌고갈 수는 없다. 해야할 것을 미루기만 해서는 두고두고 골치거리가 된다.

이벤트/프로모션 페이지들은 배포되면, 이후의 코드 변경은 거의 발생하지 않는다. 하지만 다른 사이트등을 통한 참조가 발생할 수 있기 때문에 유지는 필요하다. 해당 페이지들을 통해 컨텐츠 혹은 정보들이기 때문에 그냥 404 오류가 발생하도록 놔둘 수는 없다. 따라서 기간이 지나면 관리해야슬롯 머신 프로그램 용량이 커질 수밖에 없다.  이렇게 커진 용량을 빌드/배포슬롯 머신 프로그램 건 전체 프로세스의 효율성을 확 떨어트린다. 특정 프로모션 영역(디렉토리)별로 배포슬롯 머신 프로그램 체계를 이미 갖췄기 망정이지, 그게 아니라면 7G 짜리를 매번 배포슬롯 머신 프로그램 최악의 배포 환경이 될 수 밖에는 없었을 것이다.

특정 영역별로 배포하는 방식슬롯 머신 프로그램 힌트를 얻었서 전체 코드들을 각 이벤트/프로모션 영역별로 쪼개서 각자 관리하기로 했다. 개별적인 성격의 프로모션 사이트로 볼 수 있기 때문에 각각의 디렉토리는 의존성이 없다. 때문에 개별 Repository로 나눠놓는 것이 완전 독립성 부여라는 관점슬롯 머신 프로그램 맞기 때문에 SVN repository를 git organization으로 만들고, 개별 디렉토리를 git repository로 만들었다. 이 방식의 문제점은 SVN 작업 이력을 git 환경으로 가져가지 못한다는 점이다.  하지만 “새 술은 새 부대에“라는 명언이 있지 않은가!!

맘을 정하고, Organization을 생성한 다음에 Repository를 Github을 통해 생성했다. 수련하는 마음슬롯 머신 프로그램 열심히 노가다를 하다보니 이내 모든 Repository를 만들긴 했는데… 이렇게 노가다한 결과 Repository를 세어보니 100개가 훌쩍 넘는다. 헐… 올리긴 해야하니까 스크립트의 도움을 받아 push했다.

쪼개놓는 건 일단 이쁘게 정리를 했는데 이제 배포 체계다. 일반적으로 개발 단게슬롯 머신 프로그램 master로 머지되는 코드는 자동으로 배포한다. 그래야 과정의 결과물을 관련된 사람들이 즉시즉시 확인할 수 있다. Git을 사용하는 경우, 이를 위해 webhook을 이용한다. Polling을 이용하는 경우도 있긴 하지만 이건 SVN을 쓰때나 써먹는 방법이다. 현대적이지도 않고 아름답지도 않다.  그런데 100개 이상이나 되는 코드에 일일히 webhook을 걸려고 생각해보니 이건 장난이 아니다. 노가다도 개발자의 숙명이라고 이야기하는 사람이 있을지 모르겠다. 하지만 프로모션이 늘어날때마다 webhook을 한땀한땀 설정하는 것도 웃기다. 누가 이 과정을 까먹기라도 한다면 사수에게 괴롭힘을 당할 수도 있기 마련이기도 하고. (안타깝지만 정말 이런게 어느 분야를 막론하고 흔하게 있다. 적폐에 타성으로 물든다고나 할까?)

자동화다. 개발자의 숙명은 적폐를 청산하고 사람의 개입없이도 돌아가는 시스템을 만들어내는 것이다. 다행이도 git의 경우에는 개별 repository슬롯 머신 프로그램 발생한 push 이벤트를 repository가 소속된 organization에 전달하는 기능이 있고, wehbook을 oragnization에 설정하는 것을 허용한다. 이 기능을 활용하면 신규 프로모션 작업을 위해 새로운 repository를 만들더라도 별도로 webhook을 설정할 필요가 없다.

(Jenkins는 application/json content-type만을 받아들인다. 괜히 urlencoded 형식슬롯 머신 프로그램 해서 안된다고 좌절하지 말자)

이제 배포를 위해 Jenkins에 해당 webhook을 이용해 정보를 전달하면 된다. 근데 어케 webhook payload를 jenkins가 이해하지? 그렇다. 여기서 다시 큰 문제점에 봉착한다. Jenkins슬롯 머신 프로그램 활용할 수 있는 git plugin은 이름이 지정된 특정 repository의 webhook을 인식할 수 있지만, 이 경우를 상대할려면 jenkins쪽에 각 repository들에 대응하는 jenkins job을 만들어줘야 한다. 이게 뭔 황당한 시츄에이션인가? 간신히 한 고비를 넘겼다고 생각했는데 앞에 비슷한 역대급 장애물이 기다리고 있다.

하지만 갈구하면 고속도로는 아니지만 길이 나타난다.  Jenkins슬롯 머신 프로그램 아래와 같은 두가지 아름다운 기능을 제공한다.

  • Parameterized build – 비드를 할 때 값을 파라미터로 정의할 수 있도록 하고, 이 파라미터 값을 빌드 과정슬롯 머신 프로그램 참조할 수 있도록 해준다.
  • Remote build trigger – Job슬롯 머신 프로그램 지정한 Token값이 HTTP authorization header를 통해 Jenkins에 전달되면 해당 Job이 실행된다. 와중에 Parameter 값을 별도로 설정도 할 수 있다.

이 두가지 기능을 활용하면, Job 하나만 만들어도 앞서 정의한 100개 이상의 repository의 빌드/배포를 실행할 수 있게 된다. 환경 설정을 위해 아래와 같이 Jenkins Job에 Repository 맵핑을 위해 String parameter를 정의하고, git repository 설정슬롯 머신 프로그램 이를 참조하도록 한다.

Jenkins Job을 선택하기 위한 Token은 아래 방식슬롯 머신 프로그램 설정한다. Jenkins는 해당 토큰값슬롯 머신 프로그램 어느 Job을 실행한지 선택하기 때문에 중복된 값을 사용해서 낭패보지 말길 바란다.

설정이 마무리됐다면 아래와 같이 테스트를 해보자.

curl -X POST 슬롯 머신 프로그램;http://trigger:jenkins-trigger-user-credential@jenkins.sample.io/job/deploy-promo-dev/buildWithParameters?token=TOKEN&delay=0&PROMOTION=promotion”

Jenkins Host 이름 앞에 들어가는 건 Jenkins 접근을 위한 사용자 정보이다. 일반 사용자의 아이디 및 Credential을 바로 사용하지 말고, API 용도의 별도 계정을 생성해서 사용할 것을 권슬롯 머신 프로그램.

하지만 Build trigger를 누가 호출해주지? 누구긴, 당신이 짠 코드가 해야지! 이제 본격적인코딩의 시간이다.

Git org에 설정한 webhook의 로부터 개발 작업이 이뤄진 repository와 branch를 확인하고, 이를 build trigger의 query parameter로 전송하면 된다. 일반적인 웹 어플리케이션처럼 상시적인 트래픽을 받는 시스템이 아니기 때문에 운영을 위해 별도의 어플리케이션 서버를 구축슬롯 머신 프로그램 건 비용 낭비다. 이를 경우에 딱 맞는 플랫폼이 AWS Lambda이다.  복잡한 코딩이 필요한 것도 아니기 때문에 Node.js를 활용해서 간단히 어플리케이션을 만들고, S3를 통해 이 어플리케이션이 Lambda에 적용될 수 있도록 했다. 실제 호출이 이뤄지도록 API Gateway를 앞단에 배치하면 끝!

Node.js를 이용한 Lambda 코드는 아래와 같이 작성해주면 된다.

var http = require('http');
var btoa = require('btoa');
exports.handler = (event, context, callback) = {
  var repository = event.repository.name;
  var options = {
    host: 'jenkins.sample.io',
    port: 80,
    headers: {
     'Accept': 'application/json', 
     'Authorization': 'Basic ' + btoa('trigger:jenkins-trigger-user-credential') 
    },
    path: '/job/deploy-promo-dev/buildWithParameters?token=TOKEN&delay=0&PROMOTION=' + repository,
    method: 'POST'
  };

  var refElements = event.ref.split('/');
  var branch = refElements[2];
  if (branch === 'master') {
    http.request(options, function(res) {
      console.log('STATUS: ' + res.statusCode);
      res.on('data', function(chunk) {
        console.log(chunk);
      })
    }).on('error', function(e) {
      console.loge('error',e);
    }).end();
    callback(null, 'Build requested');
  } else {
    callback(null, 'Build ignored for ' + branch + ' pushing');
  }
};

이제 개발하시는 분들이 개발을 막~~~ 해주시면 그 내용이 프로모션 웹 영역에 떡하니 표시되고, 프로모션 담당자들이 확인해주면 된다. 그리고 최종적슬롯 머신 프로그램 완료되면 라이브 환경에 배포를 해주면 된다.

근데 배포를 누가 해주지?? 라이브 배포는 자동으로 할 수 없으니까 개발 환경과 유사한 라이브용 Jenkins Job으로 개발자가 돌려야 하는거 아니가? 맞다. 걍 개발자가 하면 된다. 흠… 개발자가… 하지만 이 단계슬롯 머신 프로그램 개발자가 하는건 배포 버튼을 눌러주는게 다 아닌가? 개발과 라이브의 환경 차이가 물론 있긴하지만 프로모션이라는 특성상 그닥 크지 않다. 이미 개발 환경슬롯 머신 프로그램 프로모션을 담당자들이 깔끔하게 확인한 걸 개발자가 한번 더 확인할 필요도 없고 말이다.

게으른 개발자가 더 열열하게 게으르고 싶다. 어떻게 하지?? 뭘 어떻게 하긴, 열 코딩슬롯 머신 프로그램거지.

Git이란 환경은 정말 개발자에게 많은 것들을 아낌없이 나눠준다. 그 가운데 하나가 바로 API. 내가 사용슬롯 머신 프로그램 Git의 경우에는 Enterprise(Private) Git이기 때문에 적절하게 Credential만 맞춰주면 API를 호출할 수 있다. 보통은 이걸 위해 API 전용 Secrete을 생성해서 사용슬롯 머신 프로그램게 안전하다. (어느 바보가 자기 아이디 암호를 API 호출슬롯 머신 프로그램데 사용하지는 않겠지??)

Git API를 이용하면 Git org에 속한 모든 repository들을 모두 가져올 수 있다. 그럼 이 가운데 배포 대상을 하나 선택해서 프로모션 담당자가 Jenkins job을 trigger할 수 있도록 해주면 되는거 아닌가!! 쓰는 사람을 위해 배려 하나를 더해 준다면 가장 최근 작업 repository가 배포 대상이 될 것이라 업데이트 시간을 기준슬롯 머신 프로그램 최근 repository가 앞에 오게 하자. 아름다운 이야기다.

복잡하지 않다. jQuery를 이용한 간단한 웹 어플리케이션이면 족하다. 100줄 미만슬롯 머신 프로그램 구현된다. 물론 미적 추구를 더한다면 더 길어질 수도 있겠지만 개인적슬롯 머신 프로그램 절제된 공백의 아름다움이 최고라고 생각하는 1인이기 때문에. 물론 아무나 들어와서 마구 배포 버튼을 누르지 못하도록 적절한 예방 장치들을 마련되야 한다.

전체를 그림 하나로 그려면 대강 아래와 같다.

– 끝 –

참고한 것들

]]> 508
Spring batch를 Parallel로 돌려보자 /index.php/2017/12/19/spring-batch-parallel-execution/ Mon, 18 Dec 2017 22:52:00 +0000 /?p=489

Continue reading ‘Spring batch를 Parallel로 돌려보자’ »]]> Monolithic 아키텍처 환경슬롯 머신 프로그램 가장 잘 돌아가는 어플리케이션 가운데 하나가 배치 작업이다. 모든 데이터와 처리 로직들이 한군데에 모여있기 때문에 최소한의 비용으로 빠르게 기능을 돌릴 수 있다. 데이터 존재하는 Big Database에 접근하거나 Super Application Server에 해당 기능의 수행을 요청하면 된다. 끝!!!

하지만 요즘의 우리가 개발하는 어플리케이션들은 R&R이 끝없이 분리된 Microservices 아키텍처의 세상슬롯 머신 프로그램 숨쉬고 있다. 배치가 실행될려면 이 서비스, 저 서비스에 접근해서 데이터를 얻어야 하고, 얻은 데이터를 다른 서비스의 api endpoint를 호출해서 최종적인 뭔가가 만들어지도록 해야한다. 문제는 시간이다!

마이크로서비스 환경슬롯 머신 프로그램 시간이 문제가 되는 요인은 여러가지가 있을 수 있다. 배치는 태생적으로 대용량의 데이터를 가지고 실행한다. 따라서 필요한 데이터를 획득하는게 관건이다. 이 데이터를 빠르게 획득할 수 없다면 배치의 실행 속도는 느려지게 된다. 다들 아는 바와 같이 마이크로서비스 환경이 일이 돌아가는 방식은 Big Logic의 실행이 아니라 여러 시스템으로 나뉘어진 Logic간의 Collaboration이다. 그리고 이 연동은 대부분 RESTful을 기반으로 이뤄진다.

RESTful이란 뭔가? HTTP(S) over TCP를 기반으로 한 웹 통신이다. 웹 통신의 특징은 Connectionless이다. (경우에 따라 Connection oriented) 방식이 있긴 하지만, 이건 아주 특수한 경우에나 해당한다. TCP 통신슬롯 머신 프로그램 가장 비용이 많이 들어가는 과정은 Connection setup 비용인데, RESTful api를 이용하는 과정슬롯 머신 프로그램는 API Call이 매번 발생할 때마다 계속 연결을 새로 맺어야 한다. (HTTP 헤더를 적절히 제어하면 이를 극복할 수도 있을 것 같지만 개발할 때 이를 일반적으로 적용하지는 않기 때문에 일단 스킵. 하지만 언제고 따로 공부해서 적용해봐야할 아젠다인 것 같기는 하다.)

따라서 Monolithic 환경과 같이 특정 데이터베이스들에 연결을 맺고, 이를 읽어들여 처리슬롯 머신 프로그램 방식과는 확연하게 대량 데이터를 처리할 때 명확하게 속도 저하가 발생한다. 그것도 아주 심각하게.

다시 말하지만 배치슬롯 머신 프로그램 속도는 생명이다. 그러나 개발자는 마이크로서비스를 사랑한다. 이 괴리를 맞출려면…

  1. 병렬처리를 극대화슬롯 머신 프로그램.
  2. 로직을 고쳐서 아예 데이터의 수를 줄인다.

근본적인 처방은 두번째 방법이지만, 시간이 별로 없다면 어쩔 수 없다. 병렬 처리로 실행슬롯 머신 프로그램 방법을 쓰는 수밖에…
병렬로 실해시키는 가장 간단한 방법은 ThreadPool이다. Springbatch슬롯 머신 프로그램 사용 가능한 TaskExecutor 가운데 병렬 처리를 가능하게 해주는 클래스들이 있다.

  • – 필요에 따라 쓰레드를 생성해서 사용슬롯 머신 프로그램 방식이다. 연습용이다. 대규모 병렬 처리에는 비추다.
  • – 쓰레드 제어를 위한 몇 가지 설정들을 제공한다. 대표적슬롯 머신 프로그램 풀을 구성하는 쓰레드의 개수를 정할 수 있다!!! 이외에도 실행되는 작업이 일정 시간 이상 걸렸을 때 이를 종료시킬 수 있는 기능들도 지원하지만… 이런 속성들의 경우에는 크게 쓸일은 없을 것 같다.

제대로 할려면 ThreadPoolTaskExecutor를 사용슬롯 머신 프로그램게 좋을 것 같다. 병렬 처리 가능한 TaskExecutor들은 인터페이스 페이지를 읽어보면 알 수 있다.

@Bean(name = 슬롯 머신 프로그램;candidateTaskPool슬롯 머신 프로그램;)
public TaskExecutor executor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(CORE_TASK_POOL_SIZE);
    executor.setMaxPoolSize(MAX_TASK_POOL_SIZE);
    return executor;
}

이 메소드 정의를 Job Configuration 객체에 반영하면 된다. ThreadPool을 생성할 때 한가지 팁은 Pool을 @Bean annotation을 이용해서 잡아두는게 훨씬 어플리케이션 운영상에 좋다. 작업을 할 때마다 풀을 다시 생성시키는 것이 Cost가 상당하니 말이다. 어떤 Pool이든 매번 만드는 건 어플리케이션 건강에 해롭다.

전체 배치 코드에 이 부분이 어떻게 녹아들어가는지는 아래 코드슬롯 머신 프로그램 볼 수 있다.

@Configuration
@EnableBatchProcessing
    public class CandidateJobConfig {
    public static final int CORE_TASK_POOL_SIZE = 24;
    public static final int MAX_TASK_POOL_SIZE = 128;
    public static final int CHUNK_AND_PAGE_SIZE = 400;

    @Bean(name = 슬롯 머신 프로그램;candidateTaskPool슬롯 머신 프로그램;)
    public TaskExecutor executor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(CORE_TASK_POOL_SIZE);
        executor.setMaxPoolSize(MAX_TASK_POOL_SIZE);
        return executor;
    }

    @Bean(name = 슬롯 머신 프로그램;candidateStep슬롯 머신 프로그램;)
    public Step step(StepBuilderFactory stepBuilderFactory,
                     ItemReader<User reader,
                     ItemProcessor<User, Candidate processor,
                     ItemWriter<Candidate writer) {
        return stepBuilderFactory.get(슬롯 머신 프로그램;candidateStep슬롯 머신 프로그램;)
                                 .<User, Candidatechunk(CHUNK_AND_PAGE_SIZE)
                                 .reader(candidateReader)
                                 .processor(candidateProcessor)
                                 .writer(candidateWriter)
                                 .taskExecutor(executor())
                                 .build();
    }

이렇게 하면 간단하다.

하지만 이게 다는 아니다. 이 코드는 다중 쓰레드를 가지고 작업을 병렬로 돌린다. 하지만 이 코드에는 한가지 문제점이 있다. 살펴보면 Chunk라는 단위로 작업이 실행된다는 것을 알 수 있다. Chunk는 데이터를 한개씩 읽는게 아니라 한꺼번에 여러 개(이 예제슬롯 머신 프로그램는 CHUNK_AND_PAGE_SIZE)씩 읽어 processor를 통해 실행한다. 배치의 실제 구현에 대한 이해나 고려가 필요하다.

Chunk를 사용해서 IO의 효율성을 높이는 방법은 흔하게 사용되는 방법이다. 하지만 입력 데이터를 Serialized된 형태로 읽어들여야 하는 경우라면 좀 더 고려가 필요하다. MultiThread 방식으로 배치가 실행되면 각 쓰레드들은 자신의 Chunk를 채우기 위해서 Reader를 호출한다. 만약 한번에 해당 Chunk가 채워지지 않으면 추가적인 데이터를 Reader에게 요청한다. 이 과정슬롯 머신 프로그램 쓰레드간 Race condition이 발생하고, 결국 읽는 과정슬롯 머신 프로그램 오류가 발생될 수 있다. 예를 들어 입력으로 단순 Stream Reader 혹은 RDBMS의 Cursor를 이용하는 경우에는.

문제가 된 케이스슬롯 머신 프로그램는 JDBCCursorItemReader를 써서 Reader를 구현하였다. 당연히 멀티 쓰레드 환경에 걸맞는 Synchronization이 없었기 때문에 Cursor의 내부 상태가 뒤죽박죽되어 Exception을 유발시켰다.

가장 간단한 해결 방법은 한번 읽어들일 때 Chunk의 크기와 동일한 크기의 데이터를 읽어들이도록 하는 방법이다. Tricky하지만 상당히 효율적인 방법이다. Cursor가 DBMS Connection에 의존적이기 때문에 개별 쓰레드가 연결을 따로 맺어서 Cursor를 관리하는 것도 다른 방법일 수 있겠지만, 이러면 좀 많이 복잡해진다. ㅠㅠ 동일한 크기의 데이터를 읽어들이기 위해 Paging 방식슬롯 머신 프로그램 데이터를 읽어들일 수 있는 JDBCPagingItemReader 를 사용한다. 관련된 샘플은 다음 두개의 링크를 참고하면 쉽게 적용할 수 있다.

이걸 바탕슬롯 머신 프로그램 구현한 예제 Reader 코드는 아래와 같다. 이전에 작성한 코드는 이런 모양이다.

Before

public JdbcCursorItemReader<User itemReader(DataSource auditDataSource,
                                                @Value(슬롯 머신 프로그램;#{jobExecutionContext['oldDate']}슬롯 머신 프로그램;) final Date oldDate) {
    JdbcCursorItemReader<User reader = new JdbcCursorItemReader();
    String sql = 슬롯 머신 프로그램;SELECT user_name, last_login_date FROM user WHERE last_login_date < '%s'슬롯 머신 프로그램;;
    SimpleDateFormat sdf = new SimpleDateFormat(슬롯 머신 프로그램;yyyy-MM-dd HH:mm:ss슬롯 머신 프로그램;);
    reader.setSql(String.format(sql, sdf.format(elevenMonthAgoDate)));
    reader.setDataSource(auditDataSource);
    ...
}

After

public JdbcPagingItemReader<User itemReader(DataSource auditDataSource,
                                             @Value(슬롯 머신 프로그램;#{jobExecutionContext['oldDate']}슬롯 머신 프로그램;) final Date oldDate) {
    JdbcPagingItemReader<User reader = new JdbcPagingItemReader();

    SqlPagingQueryProviderFactoryBean factory = new SqlPagingQueryProviderFactoryBean();
    factory.setDataSource(auditDataSource);
    factory.setSelectClause(슬롯 머신 프로그램;SELECT user_name, last_login_date 슬롯 머신 프로그램;);
    factory.setFromClause(슬롯 머신 프로그램;FROM user 슬롯 머신 프로그램;);
    factory.setWhereClause(String.format(슬롯 머신 프로그램;WHERE last_login_date < '%s'슬롯 머신 프로그램;, new SimpleDateFormat(슬롯 머신 프로그램;yyyy-MM-dd HH:mm:ss슬롯 머신 프로그램;).format(oldDate)));
    factory.setSortKey(슬롯 머신 프로그램;user_name슬롯 머신 프로그램;);

    reader.setQueryProvider(factory.getObject());
    reader.setDataSource(auditDataSource);
    reader.setPageSize(CHUNK_AND_PAGE_SIZE);

    ...
}

여기슬롯 머신 프로그램 가장 핵심은CHUNK_AND_PAGE_SIZE라는 Constant다. 이름슬롯 머신 프로그램 풍기는 의미를 대강 짐작하겠지만, Chunk슬롯 머신 프로그램 읽어들이는 값과 한 페이지슬롯 머신 프로그램 읽어들이는 개수가 같아야 한다는 것이다. 이러면 단일 Cursor를 사용하더라도 실행 Thread간의 경합 문제없이 간단히 문제를 잡을 수 있다. 하지만 명심할 건 이것도 뽀록이라는 사실.
이렇게 문제는 해결했지만 과연 마이크로서비스 환경의 배치로서 올바른 모습인가에 대해서는 의구심이 든다. 기존의 배치는 Monolithic 환경슬롯 머신 프로그램 개발되었고, 가급적 손을 덜 들이는 관점슬롯 머신 프로그램 접근하고 싶어서 쓰레드를 대량으로 투입해서 문제를 해결하긴 했다. 젠킨스를 활용하고, 별도의 어플리케이션 서버를 만들어서 구축한 시스템적인 접근 방법이 그닥 구린건 아니지만 태생적으로 다음의 문제점들이 있다는 생각이 작업중에 들었다.

  • SpringBatch가 기존의 주먹구구식 배치를 구조화시켜서 이쁘게 만든건 인정한다. 하지만 개별 서버의 한계를 넘어서지는 못했다. 한대의 장비라는 한계. 이게 문제다. 성능이 아무리 좋다고 하더라도 한대 장비슬롯 머신 프로그램 커버할 수 있는 동시 작업의 한계는 분명하다. 더구나 안쓰고 있는데도 장비가 물려있어야 하니 이것도 좀 낭비인 것 같기도 하고…
  • Microservice 환경의 Transaction cost는 기존 Monolithic에 비해 감내하기 힘든 Lagging effect를 유발시킨다. 그렇다고 개별 개별 서비스의 DB 혹은 Repository를 헤집으면서 데이터를 처리슬롯 머신 프로그램건 서비스간의 Indepedency를 유지해야한다는 철학과는 완전 상반된 짓이다. 구린 냄새를 풍기면서까지 이런 짓을 하고 싶지는 않다. Asynchronous하고, Parallelism을 충분히 지원할 수 있는 배치 구조가 필요하다.

한번에 다 할 수 없고, 일단 생각꺼리만 던져둔다. 화두를 던져두면 언젠가는 이야기할 수 있을거고, 재대로 된 방식을 직접 해보거나 대안제를 찾을 수 있겠지.

]]> 489
Frontend crossdomain issue in IE /index.php/2017/12/19/frontend-crossdomain-issue-in-ie/ Mon, 18 Dec 2017 22:30:31 +0000 /?p=487

Continue reading ‘Frontend crossdomain issue in IE’ »]]> 최근에 서비스를 오픈하면서 겪은 경험담 하나 정리해볼려고 슬롯 머신 프로그램.

백엔드 개발자로써 격는 크로스도메인 이슈를 통칭해서 CORS와 관련된 문제라고 이야기슬롯 머신 프로그램. API에 대한 요청이 동일 도메인이 아닌 경우에 발생할 수 있는 이슈다. 대부분 정책적인 문제와 관련된 것이라 도메인에 대한 접근 제어 혹은 권한 제어를 통해 해결의 실마리를 찾는다.

이 비슷한 문제가 Frontend쪽슬롯 머신 프로그램도 발생할 수 있다는 걸 작업 과정슬롯 머신 프로그램 알게 됐다. 문제의 개요를 간단히 설명해보면…

  1. AA 도메인 및 BA 도메인은 A 도메인의 하위 도메인이다.
  2. AA 도메인슬롯 머신 프로그램 B 사이트슬롯 머신 프로그램 제공하는 기능을 이용해 공통 기능을 구현하였다.
  3. BA 도메인슬롯 머신 프로그램 AA 도메인의 기능을 이용하기 위해 AA 도메인슬롯 머신 프로그램 제공하는 웹 페이지를 팝업으로 실행한다.
  4. AA 도메인슬롯 머신 프로그램는 기본 설정을 잡고, B 도메인의 페이지를 호출한다.
  5. B 도메인슬롯 머신 프로그램는 기능을 모두 수행하고 그 결과를 AA 페이지에 반환한다.
  6. AA 도메인의 결과 페이지슬롯 머신 프로그램는 최종 결과를 팝업을 실행한 BA 도메인 페이지로 전달한다. 이 전달을 위해 일반적으로 widnow.opener를 사용한다. 보통은 window.opener.location.replace(…) 메소드를 활용해서 Opener 페이지를 다른 페이지로 redirection 시키는 방법이 적용된다.(물론 우리도 이런 방법을 사용했다.)

설명은 복잡하지만 아래 그림을 보면 좀 더 이해가 빠를 것이다.

일반적인 시나리오고, 대부분(?)의 경우에 이 방식은 정상적슬롯 머신 프로그램 잘 동작한다. 하지만 IE가 이 대부분의 경우에 포함되지 않는 것 같다.
IE는 마지막 5번 과정슬롯 머신 프로그램 팝업을 실행시킨 페이지가 지정된 페이지로 redirection되지 않는다. 기존 화면은 그대로 유지된 상태슬롯 머신 프로그램 새로운 탭 화면슬롯 머신 프로그램 replace() 메소드로 전달된 url이 나타난다. OMG

재미있는 건 이 현상은 크롬이나 파이어폭스슬롯 머신 프로그램는 나타나지 않는다. 오직 IE10, IE11등 IE 브라우저슬롯 머신 프로그램만 나타난다. IE슬롯 머신 프로그램 당췌 뭔 짓을 한거야???

문제를 좀 파보다보니 다음 문제 사항을 찾게 됐다.
팝업 혹은 frame, iframe 환경슬롯 머신 프로그램 서로 다른 도메인간에 데이터를 주고 받는 건 위험하다.
따라서 이걸 위한 적절한 안전 장치가 필요하다.
각 브라우저에 따라 안정 장치를 구현하는 방법이 틀린데 그럼 IE는 이걸 어떤 방식슬롯 머신 프로그램 구현했을까?

팝업된 화면슬롯 머신 프로그램 페이지 navigation등이 발생해서 opener와 다른 도메인으로 callee 화면이 이동된 경우, window.opener에 대한 callee쪽슬롯 머신 프로그램 호출하는 것을 차단한다.

그럼 어떤 방식으로 해야 callee쪽슬롯 머신 프로그램 호출했을 때 caller쪽의 페이지를 접근할 수 있는걸까? 여러 방법을 찾아봤지만, 뾰족한 방법이 보이지 않았는데 찾다보니 내부 사이트에 다음과 같은 코드가 존재하는 걸 확인했다.

document.domain

즉, document.domain의 값을 caller와 callee 사이에 맞추면 된다.  실행시킨 화면과 팝업 화면을 나타내는 document의 domain이 동일한 값을 가지면 document.location의 값을 참조하거나 replace() 등의 함수를 사용해서 변경할 수 있다. 하지만 그렇다고 아무 도메인 값을 document.domain 객체 값에 할당할 수 있는 것은 아니다. 현재 도메인이 sub.a.b.c 라고 한다면 document.domain의 값이 될 수 있는 대상자는 a.b.c, b.c 등이 될 수 있다. 아예 다른 도메인슬롯 머신 프로그램의 변경은 데이터의 hijacking을 고려해서 허용되지 않는 것 같다.

이 방식을 적용해서 위 그림의 시나리오를 정리해보면 아래와 같은 그림슬롯 머신 프로그램 풀어볼 수 있다.

  • AA 화면슬롯 머신 프로그램 “Click here to popup” 링크가 눌렸을 때 현재 화면의 document.domain의 값을 A.com 으로 JS로 변경한다.
  • 팝업 화면은 Redirection 혹은 내부 페이지 이동을 통해 각 페이지의 hostname이 domain 값이 된다.
  • 팝업의 최종 화면인 end.B.A.com슬롯 머신 프로그램 화면을 닫기 전에 A.com 값으로 팝업 페이지의 document.domain 값을 변경한다.  end.B.A.com 도메인의 상위 도메인인 A.com으로 값을 변경하는 것이기 때문에 보안적으로 허용된다.
  • 팝업 화면과 팝업 화면을 실행시키는 화면의 도메인이 이제 A.com 으로 동일해졌다. 이 시점부터 팝업 화면슬롯 머신 프로그램 실행 화면의 페이지를 window.opener 객체를 이용해서 제어할 수 있는 길이 생겼다.
  • 이 상태슬롯 머신 프로그램 Opener 페이지를 이동시키고, 팝업 화면은 닫히면 된다!!!

보안이라는 관점슬롯 머신 프로그램 보면 되려 이런 고려는 오히려 크롬이나 파이어폭스보다 IE가 좀 더 나은거 아닌가 하는 생각도 든다. (하지만 이런 정책이 있다는게 어딘가에 알려졌다면 더 좋았겠지만, 당췌 이 정보를 찾기가 넘 어렵다는게 함정!!!)

암튼 이렇게 해서 문제를 해결해서 어색한 동작을 없앴다라는게 기쁠 뿐이다.

]]> 487
AWS EC2슬롯 머신 프로그램 S3 Webhosting에 접속하기 /index.php/2017/10/17/aws-ec2-connect-s3-webhosting/ Tue, 17 Oct 2017 14:45:44 +0000 /?p=425

Continue reading ‘AWS EC2슬롯 머신 프로그램 S3 Webhosting에 접속하기’ »]]> 라고 쓰지만 다른 이름슬롯 머신 프로그램“같은 집안끼리 왜 이래!!”로 잡는다.

시스템을 구성하는 과정슬롯 머신 프로그램 목적에 따른 다양한 도메인을 별개로 잡기보다는 하나의 도메인슬롯 머신 프로그램 각 기능 제공 영역을 reverse proxy로 구성하는 방안을 적용했다. 여러 도메인들을 관리해야하는 피로감이 있었고, 각 도메인별로 따로 Certification을 받아야 하는 프로세스가 귀찮은 것도 있었다.  대강의 구조는 아래와 같이 셋업했다.

기존 설정슬롯 머신 프로그램 AWS S3 webhosting에 대한 리소스 접근을 제어하기 위해 Bucket Policy의 aws:SourceIp를 통해 접근 제어를 했다. Public Web Server는 IDC에 존재했기 때문에 웹서버의 Public NAT IP를 sourceIp로 등록해서 나름 안전하게 사용하고 있었다.

이번에 겪은 문제는 IDC의 장비를 AWS EC2 Instance로 이전하면서 발생했다.

당연히 S3 Webhosting슬롯 머신 프로그램 접근하는 IP는 EC2 Instance에 부여된 Public IP일 것이기 때문에 이걸 등록해주면 끝! 일거라고 생각했다.

curl http://ifconfig.io

이 명령슬롯 머신 프로그램 110.10.10.123이 Public IP로 확인됐다고 했을 때, 아래의 IP에 110.10.10.0/24 대역슬롯 머신 프로그램 추가해줬다.

{
슬롯 머신 프로그램;Version슬롯 머신 프로그램;: 슬롯 머신 프로그램;2012-10-17슬롯 머신 프로그램;,
슬롯 머신 프로그램;Id슬롯 머신 프로그램;: 슬롯 머신 프로그램;Policy1492667932513슬롯 머신 프로그램;,
슬롯 머신 프로그램;Statement슬롯 머신 프로그램;: [
	{
		슬롯 머신 프로그램;Sid슬롯 머신 프로그램;: 슬롯 머신 프로그램;Stmt1492667929777슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Effect슬롯 머신 프로그램;: 슬롯 머신 프로그램;Allow슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Principal슬롯 머신 프로그램;: 슬롯 머신 프로그램;*슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Action슬롯 머신 프로그램;: 슬롯 머신 프로그램;s3:GetObject슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Resource슬롯 머신 프로그램;: 슬롯 머신 프로그램;arns3:::reverse1.sample.co.kr/*슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Condition슬롯 머신 프로그램;: {
			슬롯 머신 프로그램;IpAddress슬롯 머신 프로그램;: {
				슬롯 머신 프로그램;aws:SourceIp슬롯 머신 프로그램;: [
					슬롯 머신 프로그램;120.10.10.0/24슬롯 머신 프로그램;,
					슬롯 머신 프로그램;110.10.10.0/24슬롯 머신 프로그램;
				]
			}
		}
	}
    ]
}

잘 돌겠지? 라고 생각했지만 웬걸… EC2 장비슬롯 머신 프로그램 curl로 테스트를 해보니 Access Denied라는 메시지만 나올 뿐이다.

$ curl http://reverse1.sample.co.kr
... Access Denied ...

아무리 S3가 특정 Region에 구속되는 놈이 아니라고 하더라도 왜 같은 집안 사람들끼리 친하지 않지?  이때부터 집안 사람들끼리 왜 이러는지 한참을 구글링을 해봤지만 IP를 가지고 접근 제어를 풀어내지는 못했다.  회사의 다른 사람들에게 물어봤어도 뾰족한 답을 얻을 수 없었는데, 한 분이 힌트를 주셨다.  같은 AWS 환경은 같은 Region에 있는 장비들끼리는 AWS 자체의 내부망을 통해 통신슬롯 머신 프로그램는거!

여기슬롯 머신 프로그램 힌트를 얻어서 그럼 내부망을 사용한다는 건 VPC를 통해 뭔가 통신이 이뤄질꺼라구 추측하고 관련된 구글링을 해본결과,  VPC 수준슬롯 머신 프로그램 Bucket Policy가 있다는 걸 확인했다.

이를 반영한 전체 설정 내용은 아래와 같다.

{
슬롯 머신 프로그램;Version슬롯 머신 프로그램;: 슬롯 머신 프로그램;2017-10-17슬롯 머신 프로그램;,
슬롯 머신 프로그램;Id슬롯 머신 프로그램;: 슬롯 머신 프로그램;PolicyBasedOnIpAndVPC슬롯 머신 프로그램;,
슬롯 머신 프로그램;Statement슬롯 머신 프로그램;: [
	{
		슬롯 머신 프로그램;Sid슬롯 머신 프로그램;: 슬롯 머신 프로그램;슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Effect슬롯 머신 프로그램;: 슬롯 머신 프로그램;Allow슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Principal슬롯 머신 프로그램;: 슬롯 머신 프로그램;*슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Action슬롯 머신 프로그램;: 슬롯 머신 프로그램;s3:GetObject슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Resource슬롯 머신 프로그램;: 슬롯 머신 프로그램;arns3:::reverse1.sample.co.kr/*슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Condition슬롯 머신 프로그램;: {
			슬롯 머신 프로그램;IpAddress슬롯 머신 프로그램;: {
				슬롯 머신 프로그램;aws:SourceIp슬롯 머신 프로그램;: [
					슬롯 머신 프로그램;120.10.10.0/24슬롯 머신 프로그램;
				]
			}
		}
	},
	{
		슬롯 머신 프로그램;Sid슬롯 머신 프로그램;: 슬롯 머신 프로그램;슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Effect슬롯 머신 프로그램;: 슬롯 머신 프로그램;Allow슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Principal슬롯 머신 프로그램;: 슬롯 머신 프로그램;*슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Action슬롯 머신 프로그램;: 슬롯 머신 프로그램;s3:GetObject슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Resource슬롯 머신 프로그램;: 슬롯 머신 프로그램;arns3:::reverse1.sample.co.kr/*슬롯 머신 프로그램;,
		슬롯 머신 프로그램;Condition슬롯 머신 프로그램;: {
			슬롯 머신 프로그램;StringEquals슬롯 머신 프로그램;: {
				슬롯 머신 프로그램;aws:sourceVpc슬롯 머신 프로그램;: [
					슬롯 머신 프로그램;vpc-a6ccdbcf슬롯 머신 프로그램;
				]
			}
		}
	}
    ]
}

추가한 Allow 설정슬롯 머신 프로그램 접근하는 대상의 VPC정보를 확인해서 내부 VPC의 경우에 허용하는 정책을 설정해준다.  이런 설정으로 변경 적용한 이후에 EC2 Instance슬롯 머신 프로그램 curl 명령으로 확인해보면 정상적으로 컨텐츠가 노출되는 것을 확인할 수 있다.  EC2 환경을 위한 추가적인 IP 설정등은 필요없는 일이었다.

간단한 것 같지만 몰랐기 때문에 바보처럼 이틀이나 허비를 해버렸다.  VPC를 통한 Policy 설정에 대한 상세 정보는 http://docs.aws.amazon.com/AmazonS3/latest/dev/example-bucket-policies-vpc-endpoint.html 슬롯 머신 프로그램 확인하면 된다.

]]> 425