<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>테크폭스</title>
    <link>https://techfox.tistory.com/</link>
    <description>Software Engineer. Favorite at Pyrhon, C/C++ language. Now Studing at Spring boot with kotlin.
Graduate from master Degree in computer engineering of Kwangwoon university.(Video CODEC)</description>
    <language>ko</language>
    <pubDate>Mon, 13 Apr 2026 22:08:40 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Tech업</managingEditor>
    <image>
      <title>테크폭스</title>
      <url>https://tistory1.daumcdn.net/tistory/5025495/attach/b8ba28feb1e44fffad7acae1d19c9bc5</url>
      <link>https://techfox.tistory.com</link>
    </image>
    <item>
      <title>Querydsl 일반적인 사용 정리</title>
      <link>https://techfox.tistory.com/58</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Querydsl로 프로젝트를 하면서 많은 쿼리문을 작성하는데 필요 시 계속 구글링하면서 하기도 귀잖고 해서 일반적인 사용 방법에 대해 정리하려고 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆기본적인 사용&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;//객체 조회
val foos = query.selectFrom(foo).fetch()
//count 조회
val count = query.selectFrom(foo).fetchCount()
//join
val foos = query.selectFrom(foo)
                                            .leftJoin(food).on(foo.id.eq(food.foo.id)
                      .fetch()
//ordering
val foos = query.selectFrom(foo)
                .orderBy(foo.name.asc(), foo.age.desc()).fetch()
//update
query.update(foo).set(foo.name,&quot;joy&quot;).where(foo.name.eq(&quot;jerrey&quot;)).execte()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆Subqueries&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sub query를 사용하기 위해서는 JPAExpression을 사용해야 한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;var foos = query
            .selectFrom(foo)
            .where(
                foo.id.`in`(
                    JPAExpressions
                        .select(user.foo.id)
                        .from(user)
                        .where(user.age.isNotNull)
                )
            ).fetch()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆Paging 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 제공하는 Paging 객체를 사용하면 order by 문구를 생성 시에 QClass로 생성되는 것을 볼 수 있다. 이 문제는 &lt;code&gt;PathBuilder&lt;/code&gt;와 &lt;code&gt;OrderSpecifier&lt;/code&gt;를 사용하여 쉽게 해결 할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;pageable.sort.forEach {
            val pathBuilder = PathBuilder(foo.type, foo.metadata)
            query = query.orderBy(
                OrderSpecifier(
                    if (it.isAscending) Order.ASC else Order.DESC,
                    pathBuilder.getString(it.property)
                )
            )
        }

query = query.offset(pageable.offset).limit(pageable.pageSize.toLong())

var foos= query.fetch()

return PageImpl&amp;lt;foo&amp;gt;(foos, pageable, query.fetchCount())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막에 Page정보를 업데이트하여 return해야만 정확한 Page정보를 전달할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆LAZY load 문제 해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data Entity에서 LAZY로 설정된 칼럼을 방문함에 있어 &lt;b&gt;&lt;b&gt;LazyInitializationException&lt;/b&gt;&lt;/b&gt; 이 발생할 수 있다. &lt;a href=&quot;https://jsonobject.tistory.com/605&quot;&gt;참고링크&lt;/a&gt;, 이를 해결하기 위해 방법은 여러가지가 있지만 QueyDSL를 이용하여 쉽게 해결하는 방법을 소개 하려고 한다. left join후 강제로 join를 fetch 시키는 방법이다.&lt;/p&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;var query = query
            .selectFrom(user)
            .leftJoin(user.foo)
            .where(user.foo.id.eq(ids))
return query.fetchJoin().fetch()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 실행문에서 fetchJoin를 먼저 해줘야만 LAZY로 설정된 객체를 같이 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆ 2 Depth fetch&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종종 업무를 하다보면 2depth object까지 조회해야하는 경우가 있고 그 과정에서 LAZY 에러가 발생하는 경우가 있다. 이경우 아래와 같이 사용하면 된다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;query
            .select(foo)
            .from(foo)
            .innerJoin(foo.user, user).fetchJoin()
            .innerJoin(user.role, rol).fetchJoin()
            .where(
                foo.user.id.eq(id),                
            ).fetch()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Join과 동시에 fetchJoin을 실행하여 구현 가능 하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆ BooleanExpression 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건문이 많은 경우 query를 더 직관적으로 사용할 수 있는 방법이다&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;fun eqEmail(userEmail: String?): BooleanExpression? {
        userEmail ?: return null

        return user.email.like(&quot;$userEmail%&quot;)
}

fun predicateUserType(type: String): BooleanExpression? {
        if (type == &quot;admin&quot;) {
            return user.role.name.qe(&quot;admin&quot;);
        }

        return user.role.name.notEqualsIgnoreCase(&quot;admin&quot;)

}
fun test(){
        var query = query
            .selectFrom(user)
            .leftJoin(user.foo)
            .where(eqEmail(&quot;abc@abc.com&quot;),
                                predicateUserType(&quot;admin&quot;))
        return query.fetchJoin().fetch()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://vimsky.com/examples/detail/java-class-com.querydsl.jpa.JPAExpressions.html&quot;&gt;https://vimsky.com/examples/detail/java-class-com.querydsl.jpa.JPAExpressions.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://querydsl.com/static/querydsl/latest/reference/html/ch03s02.html&quot;&gt;http://querydsl.com/static/querydsl/latest/reference/html/ch03s02.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ict-nroo.tistory.com/117&quot;&gt;https://ict-nroo.tistory.com/117&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ksabs.tistory.com/186&quot;&gt;https://ksabs.tistory.com/186&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jsonobject.tistory.com/605&quot;&gt;https://jsonobject.tistory.com/605&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hojak99.tistory.com/578&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hojak99.tistory.com/578&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@sonaky47/querydsl-%EB%91%90%EB%B2%88%EC%A7%B8-%EA%B9%8A%EC%9D%B4%EB%A5%BC-fetch-%ED%95%B4%EB%B3%B4%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@sonaky47/querydsl-%EB%91%90%EB%B2%88%EC%A7%B8-%EA%B9%8A%EC%9D%B4%EB%A5%BC-fetch-%ED%95%B4%EB%B3%B4%EC%9E%90&lt;/a&gt;&lt;/p&gt;</description>
      <category>Spring boot</category>
      <category>DATABASE</category>
      <category>Kotlin</category>
      <category>query</category>
      <category>querydsl</category>
      <category>Spring</category>
      <author>Tech업</author>
      <guid isPermaLink="true">https://techfox.tistory.com/58</guid>
      <comments>https://techfox.tistory.com/58#entry58comment</comments>
      <pubDate>Mon, 4 Jul 2022 15:56:54 +0900</pubDate>
    </item>
    <item>
      <title>특정 기능 속도 개선 경험담(MySQL)</title>
      <link>https://techfox.tistory.com/60</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2개월동안 진행했던 속도 개선 작업에 관하여 간단한 정리를 하려고 한다. 추후 진행할 프로젝트에서 참고될수 있도록 정리해 놓는 목적이 크다. 실질적으로 속도 개선은 소량 데이터(만건이하)에서 약 70%개선, 많은 데이터(5만건 이상)에서 10배 정도 개선이 있지만 만약 제일 처음에 개선한 buik insert 변경은 속도 개선 효과는 결과에 반영하지 않았다. 만약 반영한다면 소량 데이터에서도 5배 속도 개선이 이루어 졌다고 봐도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개선 작업을 요약하면 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JPA save를 Buik insert로 수정&lt;/li&gt;
&lt;li&gt;native query를 QueryDSL로 변경 및 projection 방식 채택&lt;/li&gt;
&lt;li&gt;속도 느린 query 속도 개선&lt;/li&gt;
&lt;li&gt;전체 Logic 수정하여 DB 접근을 최소화&lt;/li&gt;
&lt;li&gt;데이터 불일치 문제 해결&lt;/li&gt;
&lt;li&gt;재 배포 없이 API로만 결과 검증 가능하도록 검증 API 추가&lt;/li&gt;
&lt;li&gt;모든 코드를 kotlin로 재 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;JPA save를 Buik insert로 수정&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Buik Insert로 로직 수정. 개선 작업을 진행한 부분은 모든 JPA save를 사용하여 속도가 엄청 느렸다. 자료 조사를 통해 먼저 colloction에 후 saveAll 방식으로 수정 했지만 좀 더 낫은 방법으로 개선 하기 위하여 buik insert 방식을 채용. 기존 save 방식 대비 약 10배 성능 향상이 이루어 짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;native query를 QueryDSL로 변경 및 projection 방식 채택&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 로직에서 데이터를 조회하는 부분이 native query로 작성되여 있고 가져온 object를 class 생성자를 만들어서 강제 형 변환을 하여 사용하였다. 이부분의 query 가 복잡하여 해석도 힘들고 유지보수도 힘들것 같아 queryDSL로 작성하였다. queryDSL로 변환하니 모든 relation data를 가져오는 것을 확인 하여 조회 속도가 느려진 것을 확인 하고 필요한 데이터를 projection하는 방식을 택하여 가져와서 속도 개선을 하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;속도 느린 query 속도 개선&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;속도 느린 query 에 대하여 explain 명령어를 사용하여 왜 느린지를 분석하고 index가 없으면 전체 테이블 관계를 파악하여 index를 만들어 넣고 만약 기존에 index가 존재하면 기존 index를 타게끔 조건문과 join 관계를 수정하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전체 로직을 수정하여 DB 접근을 최소화&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존에는 필요시마다 DB에서 데이터를 계속 읽어와서 처리해야 함으로 빈번한 조회가 발생하여 전체 로직의 속도가 느려졌다. 처리 해야 할 데이터 양이 적으면 큰 문제가 되지 않지만 10만 건 이상 데이터를 처리함에 있어 많은 속도 저하가 발생하였다. 이런 문제를 해결하기 위해 처리에 필요한 데이터를 맨 처음에 읽어 온 후 비즈니스 로직으로 처리하도록 변경하였다. 또한 coroutine으로 작성된 로직에서 병렬로 처리함에 있어 DB 조회 속도가 현저히 떨어지는 것을 발견하고 coroutine 시작 시 약간의 delay을 DB을 접근을 동시에 하는 것을 막아 조회 속도를 일정하게 보장 시켰다. ( 추후 DB 성능을 높이니 delay를 주지 않아도 조회 속도에 문제 없는 것을 확인 하고 delay에 대한 코드는 제거 했다.)&lt;br /&gt;경험한 프로젝트에서는 3만건 정도면 약 70% 속도 차이를 보이지만 10만 데이터는 약 10배 속도 향상을 달성 하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;데이터 불일치 문제 해결&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 로직과 새로 작성한 로직 사이 결과가 불일치 문제 해결&lt;br /&gt;모든 작업을 끝내고 최종 테스트를 진행하였는데 소수점 자리수의 마지막 자리가 계속 상이한 점을 발견하고 어디에 문제가 있을지를 고민해 보았다. 많은 케이스에서 문제 없는데 오직 한 케이스만 문제 있었다. 자세히 들여다 보니 double를 소수점 타입을 사용한 것이 문제 였다. 지단로보트의 &lt;a href=&quot;https://jsonobject.tistory.com/466&quot;&gt;BigDecimal 사용법 정리&lt;/a&gt;를 보고 상이함이 발생하는 부분의 double만 BigDecimal로 수정하니 모든 문제가 해결 되였다. ( 기존 방법은 일정한 단위로 처리 후 DB에 저장 후 다시 필요하면 읽어오는 방식이 여서 저장하지 않는 값과 저장한 값 사이 약간의 차이가 발생 한 것으로 추정 된다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;재배포없이 API로만 결과 검증 가능하도록 검증 API 추가&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 로직과 속도 비교를 위하여 서비스 중인 분산캐시를 사용하여 on/off 할 수 있게 하였다. 실시간으로 새로 개선한 로직이 기존 로직과 결과가 일치함과 서비스를 새로 배포하지 않고 바로바로 테스트 가능 하여 속도 개선 작업에서 많이 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;모든 로직을 kotlin으로 재 작성&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 로직은 JAVA로 되여 있었다. JAVA로 많은 중복 코드로 인해 코드의 가독성이 떨어져 있었다. 모든 코드를 이해하고 kotlin으로 작성하였다. 변환 과정에서 한번 더 느겼지만 코드가 휠씬 간결해지고 특히 코드 작성에서 null에서 체크를 모두 끝내니 NPE가 발생을 원춴적으로 차단 된것 같다는 느낌이 든다. 또한 다른 JAVA class를 사용함에 있어서도 null에 대한 체크를 엄격히 하니 코드 작성에 좀 더 집중할 수 있는 것 같다. 필자는 JAVA로 작성시 여러번 NPE를 발생 시킨 경험이 있다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>mysql</category>
      <category>성능 개선</category>
      <author>Tech업</author>
      <guid isPermaLink="true">https://techfox.tistory.com/60</guid>
      <comments>https://techfox.tistory.com/60#entry60comment</comments>
      <pubDate>Thu, 23 Jun 2022 22:42:02 +0900</pubDate>
    </item>
    <item>
      <title>현재 읽고 있는 책들</title>
      <link>https://techfox.tistory.com/21</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;아래 내용은 현재 테크업이 읽고 있는 책들이다. 아직은 진행 중인 책들이 대부분이여서 완독할 때까지 이 포스트를 업데이트 할 예정이다. 추천해 주실 책들이 있으시면 댓글로 부탁드립니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Kotlin&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/kotlin-in-action/9781617293290/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Kotlin in Action&lt;/span&gt;&lt;/a&gt;&lt;br&gt;제일 처음 코틀린으로 입문하면서 본 책이다.&lt;br&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/hands-on-data-structures/9781788994019/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Hands-On Data Structures and Algorithms with Kotlin&lt;/span&gt;&lt;/a&gt;&lt;br&gt;데이터 구조와 알고리즘 구현을 코틀린으로 재해석한 책이다. 기존적인 array부터 queue, map 까지 kotlin으로 구현한걸 설명과 함께 다루고 있다.&lt;br&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/functional-kotlin/9781788476485/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Functional Kotlin&lt;/span&gt;&lt;/a&gt;&lt;br&gt;Functional 프로그래밍관점으로 kotlin에 대하여 설명한 책이다&lt;br&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/kotlin-standard-library/9781788837668/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Kotlin Standard Library Cookbook&lt;/span&gt;&lt;/a&gt;&lt;br&gt;코틀린의 표준라이브러리 소개와 사용법을 다룬 책이다. 이 책 내용 일부는 블로그에 글로 정리되여 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JAVA&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/java-a-beginners/9781260440225/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Java: A Beginner's Guide, Eighth Edition, 8th Edition&lt;/span&gt;&lt;/a&gt;&lt;br&gt;자바 언어 입문으로 읽는 책이다. 설명이 간단 명료하고 좋다. 자바 입문으로 추천한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Architeture&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/microservices-with-spring/9781801072977/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Microservices with Spring Boot and Spring Cloud - Second Edition&lt;/span&gt;&lt;/a&gt;&lt;br&gt;MSA 서비스에 대하여 다루고 있다. spring boot로 MSA를 구성함에 있어 일반적인 문제점과 그것들에 대한 해결책을 제시하고 있다.&lt;br&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/hands-on-high-performance/9781788838382/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Hands-On High Performance with Spring 5&lt;/span&gt;&lt;/a&gt;&lt;br&gt;Spring 입문으로 읽는 책이다. Spring에대한 기초적인 지식과 최적의 사용법에 대하여 다루고 고민하게 만든다.&lt;br&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/spring-boot-2/9781484239636/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Spring Boot 2 Recipes: A Problem-Solution Approach&lt;/span&gt;&lt;/a&gt;&lt;br&gt;Spring boot를 이용하여 웹서비스를 구성함에 있어 시나리오별로 소개하고 있는 책이다. 입문 서적으로써 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/the-design-of/9781617295102/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;The Design of Web APIs&lt;/span&gt;&lt;/a&gt;&lt;br&gt;전반적인 API 설계에 대하여 복습하기 위해 읽는 책이다. 읽으면서 블로그에 정리할 예정이다.&lt;br&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/clean-architecture-a/9780134494272/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Clean Architecture: A Craftsman’s Guide to Software Structure and Design&lt;/span&gt;&lt;/a&gt;&lt;br&gt;좋은 Architecture를 가지는 software를 만드는데 있어서 기본적인 원칙부터 아키텍쳐 설계까지 망라한 책이다. &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Algorithm&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/40-algorithms-every/9781789801217/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;40 Algorithms Every Programmer Should Know&lt;/span&gt;&lt;/a&gt;&lt;br&gt;알고리즘을 복습하기 좋은 책, 평소에 여유 시간되면 읽으면 좋은 책이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Clean Code&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/clean-code-a/9780136083238/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Clean Code: A Handbook of Agile Software Craftsmanship&lt;/span&gt;&lt;/a&gt;&lt;br&gt;개발자라면 항상 고민해야할 문제 clean code 에 대하여 깊게 다룬 책이다. 이 책은 더 설명이 필요 없을것 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Logging&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/kotlin-standard-library/9781788837668/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Kotlin Standard Library Cookbook&lt;/span&gt;&lt;/a&gt;&lt;br&gt;로깅에 대하여 깊게 다룬 책이다. 기회가 된다면 읽어보길 추천 드린다. 초기 OS레벨의 로깅법부터 어플리케이션 로깅까지 자세히 설명 되여 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DataBase&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/learn-mongodb-4x/9781789619386/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Learn MongoDB 4.x&lt;/span&gt;&lt;/a&gt;&lt;br&gt;MongoDB 입문으로 좋은 책이다. 입문부터 후반 최적화까지 다루고 있다.&lt;br&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/mongodb-in-action/9781617291609/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;MongoDB in Action, Second Edition: Covers MongoDB version 3.0&lt;/span&gt;&lt;/a&gt;&lt;br&gt;입문으로 간이 프로젝트 맛보기로 좋은 책이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/mastering-dynamodb/9781783551958/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Mastering DynamoDB&lt;/span&gt;&lt;/a&gt;&lt;br&gt;Dynamo DB에 대한 전체적 소개와 best practice 내용이 포함되여 입문용으로 AWS 문서와 결합해 보면 좋은 책&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ELK&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learning.oreilly.com/library/view/learning-elastic-stack/9781789954395/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Learning Elastic Stack 7.0 - Second Edition&lt;/span&gt;&lt;/a&gt;&lt;br&gt;ELK에 대해 배우고 싶어 시작한 책인데 아직 초반에 머물러 있다.&lt;/p&gt;</description>
      <category>이모저모</category>
      <author>Tech업</author>
      <guid isPermaLink="true">https://techfox.tistory.com/21</guid>
      <comments>https://techfox.tistory.com/21#entry21comment</comments>
      <pubDate>Thu, 19 May 2022 21:40:02 +0900</pubDate>
    </item>
    <item>
      <title>[python] AWS lambda layer로 필요 라이브러리 추가</title>
      <link>https://techfox.tistory.com/59</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;lambda 로 간단한 프로그램을 작성하여 사용하는 경우가 많은데 라이브러이들을 layer로 저장하고 사용하면 여러 lambda 함수에서 공동으로 사용할 수 있어 개발 속도를 높이는데 도움이 된다. 이 글은 인터넷 상에 있는 여러가지 방법들 중 제일 간단하고 쉽게 할 수 있는 방법에 대해 소개 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;◆ 라이브러리 다운로드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리는 Linux 환경에서 다운받고 그 라이브러리를 사용하여 layer를 만드는 것을 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS lambda에서 사용하는 python 버전도 중요하다. 본 글에서는 python 3.8 기준으로 설명할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 라이브러리를 다운받을 폴더를 생성 후 필요한 라이브러리 들을 다운 받는다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;#폴더 생성
cd $HOME 
mkdir -p lib/python 
cd lib/python
#라이브러리 다운
pip install pymysql -t .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운 받는 목록을 현재 목록으로 &lt;b&gt;지정&lt;/b&gt;해 주어야 한다. 지정해 주지 않으면 python설치 목록 중 Lib 폴더에 설치 된다.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;#만든 라이브러리 폴더를 zip으로 묶어 준다.
cd .. 
zip ../pymysql.zip .&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;◆AWS lambda layer 추가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lambda 서비스로 가서 Layer 탭에 들어가서 아래와 같이 설정해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Create Layer &amp;rarr; 이름, 설명 기입 &amp;rarr; upload a .zip file를 선택 후 금방 만든 zip파일을 업로드 (compatible architecture도 선택해 줘야 한다. 여기서는 x86_64를 선택) &amp;rarr; runtime에서 python 3.8을 선택에서 layer 구성을 끝마친다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;◆함수에 Layer 추가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 편집 화면에서 Layers 항목에서 금방 추가한 layer를 선택하여 추가하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명심해야할 점은 Layer에 크기 제한이 있다는 점이다. 왼만한 라이브러리를 모두 추가 가능 하나 deep learning 관련 큰 라이브러리는 따로 서비스를 이용하거나 몇개 layer로 만들어서 배포하는 것을 추천 한다. 아래 layer를 쉽게 만드는 github링크도 공유 했다. 해당 방법을 이용하면 쉽게 layer zip파일을 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.skbali.com/2018/11/aws-lambda-layer-example-in-python/&quot;&gt;https://blog.skbali.com/2018/11/aws-lambda-layer-example-in-python/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Python</category>
      <category>AWS</category>
      <category>LAMBDA</category>
      <category>Layer</category>
      <category>python</category>
      <author>Tech업</author>
      <guid isPermaLink="true">https://techfox.tistory.com/59</guid>
      <comments>https://techfox.tistory.com/59#entry59comment</comments>
      <pubDate>Sat, 12 Mar 2022 07:33:38 +0900</pubDate>
    </item>
    <item>
      <title>AWS SES 사용하기</title>
      <link>https://techfox.tistory.com/57</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;쉽고 편리하게 E-mail를 고객한테 보내려면 Gmail SMTP를 사용하는 방법은 검색하면 많이 나온다. 이번 글은 spring 프로젝트에서 AWS SES를 설정하고 메일을 보내는 방법을 설명 하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에서 서비스를 신청하고 도메인을 등록하고 권한을 부여하는 부분에 대한 내용은 생략하고 Spring에서 설정하고 메일을 보내는 방법에 대해 적으려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;◆Dependency 추가&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;com.amazonaws&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;aws-java-sdk-ses&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;1.12.167&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;◆Config Class&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;import com.amazonaws.auth.AWSStaticCredentialsProvider
import com.amazonaws.auth.BasicAWSCredentials
import com.amazonaws.services.simpleemail.AmazonSimpleEmailService
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClientBuilder
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration

class AwsSesConfig(

    @Value(&quot;\${aws_accesskey}&quot;)
    val accessKey: String,

    @Value(&quot;\${aws_secret}&quot;)
    val secretKey: String,

    @Value(&quot;\${aws_region}&quot;)
    val region: String,
) {
    @Bean
    fun amazonSimpleEmailService(): AmazonSimpleEmailService {
        return AmazonSimpleEmailServiceClientBuilder.standard()
            .withRegion(region)
            .withCredentials(
                AWSStaticCredentialsProvider(
                    BasicAWSCredentials(accessKey, secretKey)
                )
            )
            .build()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수로 AWS Crtedential 정보를 설정하고 그것을 이용하여 AmazonSimpleEmailService client를 생성하는 Bean을 만들어 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;◆ 간단한 메일 보내기&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;fun sendEmail() {
        val message = Message()
        message.withSubject(Content(&quot;this&quot;))
        message.withBody(Body().withHtml(Content(&quot;This is test mail&quot;)))
        val destination = Destination().withToAddresses(&quot;apple@hotmail.com&quot;)
        awsSimpleEmailServiceClient.sendEmail(
            SendEmailRequest()
                .withSource(&quot;foo@hotmail.com&quot;)
                .withDestination(destination)
                .withMessage(message)
        )
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용, 발신자, 수신자를 설정하고 sendEmail 함수를 호출하여 메일을 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추후 sendBulkTemplatedEmail를 사용하여 다수의 수신자에게 메일을 보내는 방법에 대해 업데이트 할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/BlackthornYugen/1b3e1ff4426294e7054c9a7190e8f2cd&quot;&gt;https://gist.github.com/BlackthornYugen/1b3e1ff4426294e7054c9a7190e8f2cd&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://junho85.pe.kr/1449&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://junho85.pe.kr/1449&lt;/a&gt;&lt;/p&gt;</description>
      <category>Spring boot</category>
      <category>aws ses</category>
      <category>Spring</category>
      <author>Tech업</author>
      <guid isPermaLink="true">https://techfox.tistory.com/57</guid>
      <comments>https://techfox.tistory.com/57#entry57comment</comments>
      <pubDate>Tue, 1 Mar 2022 16:46:06 +0900</pubDate>
    </item>
    <item>
      <title>Hibernate L2 Cache - Region 별 저장 및 통계 정보를 이용하기</title>
      <link>https://techfox.tistory.com/56</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Hibernate L2 Cache는 기본적인 설정으로는 default 영역에 모든 데이터가 저장된다. 이렇게 저장되면 정확히 cache가 동작여부를 알 수 없다. 분산 캐시를 구성하면 더더욱 확인하기 어렵다. 이를 정확히 확인 하기 위해서는 cache를 entity별로 Region를 설정하고 각 Region 의 통계 정보를 이용하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Region 설정&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = &quot;Foo&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Cache 이노테이션에 region 값을 넣으면 &amp;ldquo;Foo&amp;rdquo; region이 생성된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정된 Region에 저장&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@QueryHints(value ={@QueryHint(name = HINT_CACHEABLE, value = &quot;true&quot;),
@QueryHint(name = HINT_CACHE_REGION , value = &quot;Foo&quot;)})
fun existsFooByName(name: String): Boolean;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@QueryHint의 HINT_CACHE_REGION 값을 위에서 설정한 값으로 설정하면 Foo Region으로 데이터가 저장된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;통계정보를 이용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hibernate의 statistics 정보를 이용하여 Hit, Miss, Put count를 Region 별로 얻을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;class HibernateHelper(
private val sessionFactory: SessionFactory
){
    fun getHibernateHitMiss(){
            val cacheRegionNames = sessionFactory.statistics.secondLevelCacheRegionNames
        cacheRegionNames.foreach{
            val cachaStatistics = sessionFactory.statistics.getCacheRegionStatistics(it)
            println(cachaStatistics.regionName)
            println(cachaStatistics.hitCount)
            println(cachaStatistics.missCount)
            println(cachaStatistics.putCount)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통계정보는 SessionFactory부터 얻을 수 있다. 이미 hibernate에서는 Bean으로 설정되여 있기때문에 가져다 쓰면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/hibernate-second-level-cache&quot;&gt;https://www.baeldung.com/hibernate-second-level-cache&lt;/a&gt;&lt;/p&gt;</description>
      <category>DataBase</category>
      <category>cache</category>
      <category>Hibernate</category>
      <category>L2</category>
      <category>r&amp;eacute;gion</category>
      <author>Tech업</author>
      <guid isPermaLink="true">https://techfox.tistory.com/56</guid>
      <comments>https://techfox.tistory.com/56#entry56comment</comments>
      <pubDate>Tue, 25 Jan 2022 22:56:17 +0900</pubDate>
    </item>
    <item>
      <title>[kotlin] Serialization을 이용하여 JSON를 다루어 보자</title>
      <link>https://techfox.tistory.com/55</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Kolin에서 Json를 보통 Gson 라이브러리를 사용하여 다룬다. Gson를 사용해도 원만한 처리는 가능하다. Serialization은 Gson에서 지원이 미흡한 NEP문제에 대해 강력하게 제한함으로써 kotlin의 가치관를 그대로 반영한 라이브러리이다. 이글은 kotlin의 serialization에 대하여 설정부터 간단한 사용법을 정리할 예정이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆ Maven 설정&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;properties&amp;gt;
    &amp;lt;kotlin.version&amp;gt;1.6.10&amp;lt;/kotlin.version&amp;gt;
    &amp;lt;serialization.version&amp;gt;1.3.2&amp;lt;/serialization.version&amp;gt;
&amp;lt;/properties&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전 정보를 속성에 명시한다.&lt;/p&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;build&amp;gt;
    &amp;lt;plugins&amp;gt;
        &amp;lt;plugin&amp;gt;
            &amp;lt;groupId&amp;gt;org.jetbrains.kotlin&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;kotlin-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;${kotlin.version}&amp;lt;/version&amp;gt;
            &amp;lt;executions&amp;gt;
                &amp;lt;execution&amp;gt;
                    &amp;lt;id&amp;gt;compile&amp;lt;/id&amp;gt;
                    &amp;lt;phase&amp;gt;compile&amp;lt;/phase&amp;gt;
                    &amp;lt;goals&amp;gt;
                        &amp;lt;goal&amp;gt;compile&amp;lt;/goal&amp;gt;
                    &amp;lt;/goals&amp;gt;
                &amp;lt;/execution&amp;gt;
            &amp;lt;/executions&amp;gt;
            &amp;lt;configuration&amp;gt;
                &amp;lt;compilerPlugins&amp;gt;
                    &amp;lt;plugin&amp;gt;kotlinx-serialization&amp;lt;/plugin&amp;gt;
                &amp;lt;/compilerPlugins&amp;gt;
            &amp;lt;/configuration&amp;gt;
            &amp;lt;dependencies&amp;gt;
                &amp;lt;dependency&amp;gt;
                    &amp;lt;groupId&amp;gt;org.jetbrains.kotlin&amp;lt;/groupId&amp;gt;
                    &amp;lt;artifactId&amp;gt;kotlin-maven-serialization&amp;lt;/artifactId&amp;gt;
                    &amp;lt;version&amp;gt;${kotlin.version}&amp;lt;/version&amp;gt;
                &amp;lt;/dependency&amp;gt;
            &amp;lt;/dependencies&amp;gt;
        &amp;lt;/plugin&amp;gt;
    &amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Plugin 정보를 추가한다.&lt;/p&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.jetbrains.kotlinx&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;kotlinx-serialization-json&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;${serialization.version}&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 dependency를 추가한다. gradle보다 설정이 좀 복잡하다. dependency 뿐만아니라 plugin를 추가해야만 사용 가능 하다. gradle 설정 참고&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆ JSON object class 작성&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Serializable
data class Foo(
    @SerializedName(&quot;id&quot;)
    val id: Int? = null,
    @SerializedName(&quot;name&quot;)
    val name: String? =null,
    @SerializedName(&quot;age&quot;)
    val age: Int? = null,
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Serializable 어노테이션을 사용하면 해당 class는 Serialization라이브러리를 사용할 수 있는 객체로 된다. @SerializedName은 json에 매핑되는 key 값을 의미한다. Serialization을 사용하면 null 값에 대한 설정 부분이 약간 민감하다. Gson를 사용하면 class에서 null값 허용을 설정해 주지 않아도 알아서 무시한다. Serialization에서는 초기값을 null로 설정해 주지않으면 null 값을 Parsing하지 않고 exception을 발생 시킨다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆사용&lt;/h3&gt;
&lt;pre class=&quot;pony&quot;&gt;&lt;code&gt;//DeSerialize
val fooStr = &quot;&quot;&quot;{&quot;id&quot;:1,&quot;name&quot;:&quot;foo&quot;,&quot;age&quot;:20}&quot;&quot;&quot;&quot;
val fooObj = Json.decodeFromString&amp;lt;Foo&amp;gt;(fooStr)
//Serialize
val jsonList = Json.encodeToString(fooObj )&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆프로젝트에서의 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타 사이트와 연동하거나 타 서비스와 데이터를 주고 받음에 있어 요즘은 json포맷이 보편화 되였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 쉽게 json class을 생성하는 방법을 공유하려고 한다. IntelliJ에서 Json To kotlin class라는 plugin를 설치하고 class 파일을 만든 후 클릭 메뉴에서 generate ...를 선택하고 kotlin data from json를 선택하고 json 을 붙여 넣으면 자동으로 kotlin class를 생성해 준다. Advanced를 클릭하여 &lt;b&gt;어노테이션, null 값에&lt;/b&gt; 대한 처리 대한 옵션을 선택할 수 있다. 자동으로 생성해준 class는 완벽할 수 없으므로 한번 검토해 보고 사용하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TiP:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gson vs serialization&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. not null 변수에 null이 들어 갈 수 있음, kotlin 언어의 null safe 장점이 사라진다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. default value 적용 불가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ref:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kotlinlang.org/docs/serialization.html#example-json-serialization&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kotlinlang.org/docs/serialization.html#example-json-serialization&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Kotlin/kotlinx.serialization&quot;&gt;https://github.com/Kotlin/kotlinx.serialization&lt;/a&gt;&lt;/p&gt;</description>
      <category>Kotlin</category>
      <category>JSON</category>
      <category>Kotlin</category>
      <category>Serialization</category>
      <author>Tech업</author>
      <guid isPermaLink="true">https://techfox.tistory.com/55</guid>
      <comments>https://techfox.tistory.com/55#entry55comment</comments>
      <pubDate>Thu, 20 Jan 2022 22:03:40 +0900</pubDate>
    </item>
    <item>
      <title>[JobRunr] Pro 버전에서 Server Tag 설정 방법 및 실전 노하우</title>
      <link>https://techfox.tistory.com/54</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이글에서 JobRunr에 대하여 간단한 사용법에 대하여 알아 보았다. 이번 글은 JobRunr Pro 버전에서 Service node에 대하여 각기 다른 &lt;b&gt;tag&lt;/b&gt;를 설정하는 방법 및 &lt;b&gt;Recurring Job&lt;/b&gt;를 설정 시 ProgressBar를 이용하는 방법에 대해 정리한 곳이 없어 정리하려고 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆Server Tag 기능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 production 환경에는 여러개의 node가 존재하고 특정 업무를 수행하는 node도 존재하기 마련이다. Tag 기능은 특정 Job를 특정 node에서만 수행 하도록 하는 기능이다. &lt;b&gt;이 기능은 pro버전에만 제공한다.&lt;/b&gt; JobRunr 설정에 아래 내용을 추가 해야 Tag 기능을 사용 가능하다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Configuration
class JobRunrConfig(
    private val mongoClient: MongoClient
) {
    @Bean
    fun storageProvider(jobMapper: JobMapper): StorageProvider {

        val storageProvider = MongoDBStorageProvider(mongoClient)
        storageProvider.setJobMapper(jobMapper)
        return storageProvider
    }

    @Bean
    fun backgroundJobServer(
        storageProvider: StorageProvider,
        @Qualifier(&quot;jobRunrJsonMapper&quot;) jsonMapper: JsonMapper,
        jobActivator: JobActivator
    ): BackgroundJobServer {

        val backgroundJobServer = BackgroundJobServer(
            storageProvider, jsonMapper, jobActivator,
            BackgroundJobServerConfiguration
                .usingStandardBackgroundJobServerConfiguration()
                .andTags(&quot;Windows&quot;),
            JobNotFoundConfiguration.usingStandardJobNotFoundConfiguration()
        )
        backgroundJobServer.start()

        return backgroundJobServer
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;backgroundJobServer에 대한 설정을 해 줘야 된다. 여기서는 모든 parameter 값은 기본 값을 사용했고 tag만 별도로 설정해 주었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆ Tag 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JobRunr 함수 정의 시 Tag 정보도 같이 적어 준다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Recurring(id = &quot;Myjob-window&quot;, cron = &quot;8 8 12 * * *&quot;)
@Job(name = &quot;MyJob&quot;, runOnServerWithTag = &quot;Windows&quot;, retries = 2)
public void myJob() throws Exception {
     log.info(&quot;My job start.&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆ Recurring Job에서 Progress Bar 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Progress Bar 기능은 JobRunr에서 기본적으로 제공하는 DashBoard 기능 중 하나이다. Progress Bar를 사용하려면 JobContext를 인자로 받아야 사용가능하다. 그런데 Recurring Job를 정의하면 인자로 아무것도 받지 못한다. 이를 해결 하기 위해서 Recrring Job 정의서의 Background Job를 enqueue하는 방식으로 우회하여 해결이 가능 하다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Recurring(id = &quot;Myjob-window&quot;, cron = &quot;8 8 12 * * *&quot;)
@Job(name = &quot;MyJob&quot;, runOnServerWithTag = &quot;Windows&quot;, retries = 2)
public void myJob() throws Exception {
     log.info(&quot;My job start.&quot;);
}

@Job(name = &quot;MyJob-enqueue&quot;, runOnServerWithTag = &quot;Windows&quot;, retries = 2)
public Boolean MyRealJob(JobContext jobContext) throws Exception {
        JobDashboardProgressBar progressBar = jobContext.progressBar(1000);
        for(int i = 0; i &amp;lt; 1000; i++) {
            progressBar.increaseByOne();
     }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆DashBoard에서 log 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 제공하는 Dashboard에서는 log 정보도 담을 수 있다. 설정은 아래와 같이 하고 사용하면 된다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;//정의
private Logger log =new JobRunrDashboardLogger(LoggerFactory.getLogger(MyService.class))

//사용
log.info(&quot;My Job&quot;);&lt;/code&gt;&lt;/pre&gt;</description>
      <category>JAVA</category>
      <category>Java</category>
      <category>job manager</category>
      <category>Jrunr</category>
      <author>Tech업</author>
      <guid isPermaLink="true">https://techfox.tistory.com/54</guid>
      <comments>https://techfox.tistory.com/54#entry54comment</comments>
      <pubDate>Fri, 14 Jan 2022 18:50:02 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin] foreach 와 map 알고 쓰자</title>
      <link>https://techfox.tistory.com/53</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Kotlin으로 구현하다보면 foreach와 map은 실제로 같은 기능을 하는 것 같지만 깊게 이해하고 사용하면 상황에 따라 유용하여 사용할 수 있다. 이글에서는 두 문법에 관하여 정리해 보려고 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆foreach&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문법은 단순히 모든 Collection 원소에 대하여 한번 순회하면서 처리하는 문법이다. 중간에 중단도 가능하고 결과 값을 만들어 내기도 하고 아닐 수도 있다. 즉 모든 처리가 가능 하다.&lt;/p&gt;
&lt;pre class=&quot;golo&quot;&gt;&lt;code&gt;val list = listOf(
     Data(&quot;John&quot;, &quot;Ali&quot;), 
     Data(&quot;Naidu&quot;, &quot;Tan&quot;), 
     Data(&quot;Evgenii&quot;, &quot;Dmitry&quot;))
list.foreach{
    if(it.firstName==&quot;Naidu&quot;){
        println(&quot;This is Naidu&quot;)
        break
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆map&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;map은 모든 원소을 순회하면서 결과값을 반환한다. 즉 모든 원소를 순환하는 목적이 다른 무언가를 하기 위함이다. 원래 Collection이 아닌 다른 Collection으를 만든다. 이 떄문에 map을 보통 두 Collection를 transform 하기위해 사용한다.&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;val list = listOf(
        Data(&quot;John&quot;, &quot;Ali&quot;,30),
        Data(&quot;Naidu&quot;, &quot;Tan&quot;,32),
        Data(&quot;Evgenii&quot;, &quot;Dmitry&quot;,32))
val newList =list.map{ it.age +=1}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 모든 원소에 대하여 어떤 처리를 하거나 특정 조건에서 loop를 중단하고 결과 값이 필요 없으면 foreach를 선택, loop의 결과를 다른 Collection으로 반환 받고 싶으면 map을 선택하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/354909/is-there-a-difference-between-foreach-and-map&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/354909/is-there-a-difference-between-foreach-and-map&lt;/a&gt;&lt;/p&gt;</description>
      <category>Kotlin</category>
      <category>foreach</category>
      <category>Kotlin</category>
      <category>map</category>
      <author>Tech업</author>
      <guid isPermaLink="true">https://techfox.tistory.com/53</guid>
      <comments>https://techfox.tistory.com/53#entry53comment</comments>
      <pubDate>Wed, 12 Jan 2022 22:48:21 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 코드에서 Active Profile 가져오기</title>
      <link>https://techfox.tistory.com/52</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;실행프로파일 별로 다르게 실행해야할 업무 로직을 구현이 필요할 경우 아래와 같이 진행하면 쉽게 구현 할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆Enviroment 빈 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring 환경 변수는 Enviroment class로부터 가져 올 수 있다. 아래 코드로 bean을 주입한다.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Autowired
private final Environment environment;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;◆사용&lt;/h3&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;if(Arrays.stream(environment.getActiveProfiles()).anyMatch(
                env -&amp;gt; (env.equalsIgnoreCase(&quot;dev&quot;) ))){
	//비즈니스 로직 구현
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Spring boot</category>
      <category>enviroment</category>
      <category>Profile</category>
      <category>Spring</category>
      <author>Tech업</author>
      <guid isPermaLink="true">https://techfox.tistory.com/52</guid>
      <comments>https://techfox.tistory.com/52#entry52comment</comments>
      <pubDate>Sun, 9 Jan 2022 12:15:29 +0900</pubDate>
    </item>
  </channel>
</rss>