2017년 12월 27일 수요일

`익스에서 안되요`라는 말을 들을 때


`크롬 쓰세요`

라고 하고 싶다. 진심.

2017년 11월 24일 금요일

Windows TaskKill @BatchFile

taskkill /fi "WINDOWTITLE eq appName"
start "appName" java -jar appName.jar


2017년 11월 22일 수요일

JetBrains Night Seoul를 다녀와서


intelliJ 짱짱맨.

teamcity 도입해봐야 겠다.

kotlin으로 highorder functional 좋은거 같긴한데 공부할 시간이 없네...


2017년 11월 21일 화요일

JDK9와 android build


JDK9가 깔려있으면 gradle build가 안된다.

JAVA_HOME으로 8을 지정하면 되려나...?

jigsaw 때문에 그런가?

android jdk8도 힘겹게 지원하는데 9는 지원할까?




2017년 10월 31일 화요일

android에서 zeromq 사용해보려다가 - #0.JeroMQ

pure java 구현체인 JeroMQ가 있다.

몇가지 문제가 있는데,
0.4.3-SNAPSHOT 이상 버전에서 해결이 되었다.

해당 홈페이지에는 0.4.3-SNAPSHOT을 사용하는 방법도 친절하게 안내가 되어 있다.

...만 maven을 사용할 때의 경우다.

android는 gradle로 빌드를 하고,
gradle에서는 0.4.3-SNAPSHOT을 찾아서 가져오질 못했다.
몇 시간 버린 후에 끝내

https://oss.sonatype.org/content/repositories/snapshots

로 직접들어가서 jar를 받아 file dependency로 추가했다.

...정식 릴리즈까지는 이렇게 써야 할듯.

zeromq도 socket을 이용하기 때문에
asyncTask 등을 통해 사용해야 한다.

2017년 7월 26일 수요일

excel 함수 몇 가지 정리


=index({원하는 값이 있는 셀 범위}, match({찾을 값}, {찾을 범위}, 0))

=char(65 + match(~))

=indirect(char(~))

indirect에서 다른 시트에 있는 값을 참조하게 하려면 `{시트 이름}!`을 추가하면 된다.


자료관리는 역시 엑셀(?)

2017년 6월 26일 월요일

Spring boot, using of external static resources

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {

    protected final Logger log = getLogger(this.getClass());
    private static final String LOCATION_PATTERN = "file:///%s/%s/";

    @Value("${repository.location}")
    private String externalResource;

    @Override    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
                .addResourceHandler("/ext/**")
                .addResourceLocations(String.format(LOCATION_PATTERN, externalResource, "ext"))
                .setCacheControl(CacheControl.maxAge(3L, TimeUnit.DAYS).cachePublic())
        ;        super.addResourceHandlers(registry);    }

}

1.

`ResourceHandler`는 pathPattern임을 기억하자.

`ResourceLocations`는 prefix `file:///`와, suffix `/`를 반드시 붙여서 사용해야 한다.

`CacheControl`은 적당히 사용하면 된다.

덧. `file:///c:\dev...` 이건 윈도우의 경우. 맥에서는 `file:/Users/Documents...` 뭐 이런 패턴.

2017년 6월 14일 수요일

example of JPA Example Matcher



Parent.java, Child.java는 생략

JPA가 생성하는 Query를 확인해보자.
join과 like를 적절하게 사용해서 생성해준다.


EDIT:
Model Class 설계할때, 초기값을 넣어주도록 했다면
`ExampleMatcher`가 생성하는 Query `where`절에 해당 값이 들어가는 점에
유의하도록 하자.

아무튼 Reflection과 함께 잘 활용하면 간단하게 구현할 수 있는 부분들이 많다.




2017년 5월 19일 금요일

code Convention과 @ResponseBody

`@ResponseBody` 를 사용할때 마다 겪는 불편함이 있었다.
흔히(?) 등장하는 `@ResponseBody`의 사용 예이다.

1. 그런데 `Auto Indent Line`같은 걸 한번 실행하면 보통 다음처럼 된다.

2. 으으... 난 이게 너무 보기 싫었다.
그래서 매번 다시 1. 처럼 일일이 손으로 수정하곤 했었는데,
다음과 같은 사실을 깨달았다.

`@ResponseBody`는 `ElementType.METHOD`를 대상으로(@Target)하고 있어서 메서드 아무데나 갖다놔도 된다는 것을.

3. 즉 3.과 같이 사용하면 된다는 것이다!

기쁘다 흑흑.
이제 더이상 삽질은 없다!

2017년 5월 1일 월요일

Reflection을 이용해서 CSV Writer를 만들어보자 #2

지난 글에서 java의 reflection을 이용해서 CSV Writer를 간단히 만들어 봤다.

문제점을 몇가지 짚어보자.

  1. HttpServletResponse에 의존적이다. 그냥 File로 바로 변환하려면 어떻게?
  2. Test는 어떻게?

첫번째 문제를 해결하기 위해서 interface로 변환하고, HttpServletResponse를 사용하던 것을 OutputStream으로 변경해 보자.


그러면 이런 형태가 될 것이다.

이걸 이쁘게 implements 한 class를 만들면 된다.
구현 순서는 다음과 같을 것이다.

  1. Header 생성(column은 구분해줘야 하니까...)
  2. List을 순회하면서 각 항목을 reflection을 사용해서 String[] 타입으로 변환
  3. CSVWriter에 List<String[]> 데이터 타입을 보내서 outputStream에 쓰기

그런데 위 작업이 이미 충분히 추상화 되어 있기 때문에 굳이 interface를 또 사용할 필요까지는 없는 것 같다.(실컷 다 작업하고 나서야 그 사실을 깨달았다 이런)


아무튼 그래서 2가지 방법으로 구현해봤다.
유연한 쪽은 아무래도 interface를 활용한쪽일 것이다.

1,2,3은 공통으로 사용될 부분이라 java8에 추가된 interface의 default method를 활용했다.

소스는 여기에 있다.

2017년 4월 24일 월요일

Reflection을 이용해서 CSV Writer를 만들어보자

# CSV Writer 만들기

csv로 저장할 model의 field는 얼마든지 바뀔 수 있으므로 reflection을 사용해보도록 하자.
`getClass().getDeclaredFields()`이면 model class에 선언한 field들을 가져올 수 있다.


## BOM(Byte Order Marker)?

해당 문서가 어떤 Charset으로 되어 있는지 판별하기 위해 사용되는 Marker이다.
몇가지 타입이 있는데 자세한건 검색해보도록 하자.

## java BOM 지원

java reader들은 BOM를 지원하지 않기 때문에,
쓰기 할때는 BOM을 추가, 읽을 때는 BOM을 삭제해줘야 한다.
BOM을 반영해주지 않으면 charset 때문에 읽기 곤란한 문서와 만나게 된다.


2017년 3월 28일 화요일

AWS IAM Role 로 Credentials 획득하기

http://stackoverflow.com/questions/43060650/can-i-get-credentials-by-aws-iam-role-published-by-another-aws-account

정리하면,
IAM Role을 제공하는 쪽은 accountId, roleName, externalId를 알려줘야 한다.

accountId는 12자리 숫자열이다.
ARN을 보면 어떤 값인지 알 수 있다.


결국은
roleName이 accessKey
externalId가 secretKey에 대응하는 것 같다.

아무튼 이 정보를 가지고 AssumeRoleRequest를 AWS SecurityTokenService Client를 통해 요청하면 AssumeRoleResult를 던져주는데, 이 녀석이 credential 정보를 가지고 있다.

요컨데 임시 발급되는 accessKey, secretKey, sessionToken을 가지고
일반적인 AWSCredentials을 확장한 AWSSessionCredentials을 만들어서

AWS Service에 접근하게 된다. (헉헉)

2017년 3월 21일 화요일

AWS, PARALLEL 성공적

ec2, rds 정도야 일상적인게 되어 버린 때.

lambda, cloudwatch까지 원없이 건들여 보았다.

java8 스펙에 충실히(?) stream api를 사용했다면,
병렬화 작업은 .parallelStream() 한줄 추가하는 것으로 해결되는 놀라운 경험도 했다.

AWS, PARALLEL, 성공적.

2017년 3월 2일 목요일

2017년 1월 21일 토요일

JPA LazyInitializationException 문제 해결하기

JPA를 사용해서 신나게 개발하다가 처음 겪는 장벽이 있다면
바로 LazyInitializationException 일 것이다.

JPA나 hibernate를 사용하지 않는다면 겪을 일이 없는 예외이기 때문이다.

우선 JPA는 기본적으로 다음과 같이 table과 column을 관리한다.

* table
 @Entity로 정의된 class에 대응

* column
class에 선언된 멤버 변수의 타입을 체크, primitive type의 경우에는 그대로 column을 생성.
one-to-one, one-to-many, many-to-one, many-to-many annotation이 붙은 경우에는 상황에 맞게 처리한다.

자. primitive type의 경우에는 아무런 문제가 없다.
jpaRepository를 이용해서 값을 가져오면 잘 가져오고 잘 저장한다.

LazyInitializationException을 만날 일 자체가 없다.

그런데, @OneToOne 부터는 이야기가 다르다.

JPA가 instance를 관리하는 life-cycle을 이해하지 않으면 LazyInitializationException에서 벗어날 수가 없다.

https://docs.oracle.com/cd/E16439_01/doc.1013/e13981/undejbs003.htm
JPA lifeCycle from https://docs.oracle.com/cd/E16439_01/doc.1013/e13981/undejbs003.htm

새로 생성해서 instance를 저장한 경우 managed 상태가 된다. 이경우에는 lazyLoading이 적용된다. 그러다가 @Tranactional로 선언된 경계를 벗어난 순간 detached 상태가 된다. 이때 lazyLoading 대기 상태이던 필드에 접근하면 LazyInitializationException이 발생한다.

요컨대, JPA 관리 범위안에(@Tranactional) 있는 경우에는 lazyLoading 요청에 대해서 대응할 수 있지만 범위를 벗어나면 exception이 발생하는 것이다.

그렇다면 해결책은 무엇인가?

* OSIV(Open Session In View) : true로 관리범위를 presentation layer까지 확장한다. @Tranactional의 범위가 늘어난다고 생각하자.

* OSIV false인 경우, DTO(viewModel)을 활용

1. repository에서 값을 가져올때 fetch join 해서 가져온다. JPQL등을 이용해서 query로 해당 칼럼을 미리 join해서 lazyLoading이 아닌 eagerLoading이 이루어 지게 한다.

2. @Tranactional 범위 안에서 필요한 필드의 값을 loading 한다. Collections라면 size() 메서드를 호출하는 것으로 충분하다.