코틀린 코루틴 한판 정리

코틀린의 등장 배경

프로세스는 별도 설정없이는 단일 스레드로 실행됩니다 (Single Thread Process), 실행 중에 File System IO작업, Network작업과 같이 Blocking 작업이 실행되면 대기하게 됩니다.
이를 해결하기 위해 멀티 스레드 프로그래밍이 등장했습니다, 작업간 독립성이 있는 경우 Parallel Processing 이 가능하기 떄문에 단일 스레드보다 더 높은 작업량을 처리할 수 있습니다.

멀티 스레드 프로그래밍도 만능은 아니고, 여러 단점이 존재합니다. Thread 생성 비용과 Thread간 context switching 비용이 존재합니다,
JVM에선 1개의 Thread 당 약 1MB의 Stack 메모리 영역을 차지합니다.
또한 I/O 작업과 같이 thread가 blocking 되는 일도 발생합니다.

thread blocking : thread가 아무것도 하지 않고, 대기하는 상태 (Mutex, Semaphore 로 인해 공유되는 자원에 접근할 수 있는 스레드가 제한되는 경우에도 발생함)]
예를 들어 아래의 상황에서도 blocking 될 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
fun main(args: Array<String>) {
val executorService = Executors.newFixedThreadPool(2)
val future = executorService.submit<String>{
Thread.sleep(1000L)
return@submit "작업완료!";
}

val result = future.get(); // thread blocking 됨!!
println(result)
executorService.shutdown()
}

thread blocking문제를 코루틴이라는 경량 쓰레드(lightweight thread)로 해결할 수 있는데요,
코루틴은 작업이 일시 중단되면 더 이상 쓰레드 사용이 필요없으므로 쓰레드를 다른 코루틴에게 양보합니다.
즉 마치 코루틴을 쓰레드에 땟다가 붙이면서 쓰레드가 쉬는 시간을 갖지 않도록 열심히 채찍질하는거죠..

그래서 코루틴의 정의란 ?

일시 중단이 가능한 ‘작업’입니다. thread에 땟다가 붙일 수가 있기 때문에 경량 쓰레드라고도 불리고 있습니다.

Read more

Flutter 빠르게 파헤치기

Flutter란?

  • 앱 개발하는 방식에는 네이티브 앱 개발 방식과 크로스 플랫폼 앱 개발 방식 이렇게 2가지로 나뉩니다. 플러터는 이 중에서 크로스 플랫폼 앱 개발 방식에 속합니다.

    • 네이티브 앱 개발 방식은 각 운영체제에 종속적으로 개발하는 방식으로, 각 운영체제의 모든 기능에 대해 공식 지원과 문서화가 잘 되어 있습니다.
    • 크로스 플랫폼 앱 개발 방식은 말 그대로 여러 플랫폼에 동시에 사용될 수 있는 방식입니다. 대신 단점으로 느린 속도와 UI 커스터마이징 제한을 가지고 있다는 단점을 가지고 있습니다.
  • 구글에서 개발한 크로스 플랫폼 프레임워크로 Dart언어를 사용합니다.

  • Flutter 3.0버전부터는 윈도우, 맥, 리눅스, iOS, Andrioid, Web 까지 총 6개의 플랫폼을 모두 공식적으로 지원합니다.

즉 플러터는 하나의 언어로 모바일앱, 테스크톱 앱, 웹사이트, 임베디드 시스템까지 개발이 가능합니다! :o

6개의 플랫폼 스톤을 모두 모은 플러터 타노스

Read more

nGrinder

nGrinder란?

nGrinder는 네이버에서 만든 오픈소스 부하 테스트 도구입니다.

nGrinder에서는 아래의 load parameter를 통해 performace test 결과를 보여줍니다.

테스트 결과를 바탕으로 target server가 동시 접속자 수 x명(vUser)일때, 서버가 적절한 response time으로 응답하는지 peak TPS는 어떻게 되는지, MTT는 어떻게 되는지 등을 판단할 수 있습니다.

매개변수 설명
TPS 초당 처리한 요청 수를 나타냅니다.
Peak TPS TPS가 가장 높을 때, 즉 초당 처리 가능한 최대 요청 수를 나타냅니다.
MTT (Mean Test Time) 개별 테스트의 평균 수행 시간으로, API 호출 외의 시간이 포함될 수 있습니다. nGrinder QNA에서 평균 API 응답시간이라고 설명됩니다.
MTTFB (Mean Time to First Byte) 테스트 이후 테스트 타겟 서버로부터 첫 번째 바이트를 응답받기까지의 평균 시간입니다. (평균 API 응답시간과 동일)
Executed Tests 실행한 테스트 수를 나타냅니다.

nGrinder Architecture

nGrinder는 2개의 주요 구성 요소로 구성됩니다:

  • Controller: 성능 테스트를 위한 웹 UI를 제공하며, 테스트 결과를 시각화하고 에이전트 풀을 관리하며, 테스트 스크립트를 생성, 수정, 삭제합니다.
  • Agent: 테스트 대상 서버에 실제 부하를 주는 서버로, API 사용자 역할을 합니다.

nGrinder Architecture

Controller와 Agent는 12001~120XX 포트를 사용하여 통신합니다.

Read more

켄트백의 Tidy First?

켄트백의 소프트웨어 설계 시리즈 책으로, 개발자 스스로 할 수 있는 간단한 수준에서의 소프트웨어 설계를 다루고 있습니다. 본 책을 읽고 이해한 내용을 정리한 글입니다.
책에서는 큰 소프트웨어 설계보다 작은, 적절한 수준의 소프트웨어 설계를 통해 변화를 쉽게 만드는 것을 다루고 있습니다.

1장. 코드 정리법

  • 간단한 수준의 리팩토링을 다룹니다.
  • 반드시 작은 단계를 거쳐 코드를 정리해야 합니다. 큰 설계가 일으킬 장애가 무섭다면, 작은 단계로 작업합니다. 그래도 무섭다면 무서워지지 않을 때까지 작은 단계로 작업합니다.

보호 구문,Guard Clause

중첩된 조건은 가독성을 떨어트립니다.

1
2
if(조건)
if(다른 조건 부정)

위와 같은 코드보다는 다음과 같이 정리된 코드가 마치 코드의 세부사항을 살펴보기전에 전제조건이 있습니다 라고 말하는 것처럼 가독성이 좋습니다.

1
2
if(조건A) return 
if(조건B) return

안 쓰는 코드 제거

Reflection을 사용한 코드는 추적이 어려워, 코드 제거 후 테스트가 필요합니다. 코드를 제거하더라도 형상 관리 도구를 통해 원복할 수 있으니, 지우는 것을 걱정할 필요가 없습니다.

대칭으로 맞추기.

초기화 지연 코드를 예시로 같은 역할을 수행하는 코드라도 다른 방식으로 작성될 수 있습니다.

1
2
3
4
5
6
7
8
9
10
foo(){
return foo if foo not null;
foo := ...;
return foo;
}
foo(){
if foo is null
foo:= ...;
return foo;
}

위 코드들은 모두 foo 가 있으면 반환하고 없으면 만들어서 반환하라는 코드입니다. 어떤 방식이든 한 가지 방식을 선택해서 정합니다.
즉 일관성이 있어야 합니다. 일관성이 없으면 코드를 읽을 때 더 많은 노력을 요구합니다.

새로운 인터페이스로 기존 루틴 부르기

루틴을 호출해야 하는데, 기존 인터페이스 떄문에 어려운 경우는 새로 만든 통로 인터페이스(pass-through interface)를 만들어 기존 인터페이스를 호출합니다.

통로 인터페이스를 이용해 설계를 했다면 변경이 용이합니다.

읽는 순서

당연한 말이지만 읽기 좋은 순서로 코드를 정렬하는게 좋습니다.

응집도를 높이는 배치

변경이 같이 되는 코드는 가까이 위치시킵니다. 즉 두 루틴에 결합도가 높다면 바로 옆에 위치 시킵니다.
파일에 결합도가 높다면 같은 디렉토리에 위치시킵니다.

Read more

헥사고날 아키텍처

헥사고날 아키텍처를 왜 사용해야 할까요?

  • 포트와 어댑터라는 컴포넌트를 두어 비즈니스 코드를 기술 코드로부터 분리 하여 즉 외부 기술이 변경되더라도 비즈니스 코드에 영향을 주지 않고도 기술 코드를 변경할 수 있습니다 (Change-Tolerant)
  • 도메인 규칙이 변할 경우에는 유일하게 도메인 헥사곤만 변경합니다. 만약 새로운 프로토콜을 사용하는 기능이 추가되면 프레임워크 헥사곤에서 사용하는 새로운 어댑터만 추가하면 됩니다. (Maintainability)
  • UI/데이터베이스에 의존하지 않는 도메인 헥사곤이 테스트하기 용이합니다. (Testability)

헥사고날 아키텍처의 구성 요소

hexagonal architecture

도메인 헥사곤 (Domain Hexagon)

  • 소프트웨어가 해결하기를 원하는 핵심 문제를 설명하는 요소를 Entity ObjectValue Object(값 객체)를 사용하여 결합합니다.
  • 비즈니스 규칙을 엔티티와 값 객체로 캡슐화합니다.
  • 소프트웨어가 풀고자 하는 실 세계 문제를 이해하고 모델링하는 영역입니다.
  • Entity는 기술적인 요구사항으로부터 보호되어야 합니다. 즉 특정 기술에 연관되지 않아야 합니다.
  • 도메인 헥사곤에 위치한 도메인 서비스는 외부의 애플리케이션, 프레임워크 헥사곤에는 의존해서는 안됩니다. 반대로 애플리케이션 헥사곤이나, 프레임워크 헥사곤은 도메인 헥사곤에 의존하는 관계입니다.

애플리케이션 헥사곤 (Application Hexagon)

  • 도메인 헥사곤에서 나오는 비즈니스 규칙을 사용, 처리하고 조정하는 역할을 수행합니다. 비즈니스 측면과 기술 측면 사이에 상호 작용하는 역할 수행합니다.
  • 애플리케이션 헥사곤은 유스케이스, 입력포트, 출력포트로 구분됩니다.

유스케이스

  • 유스케이스는 도메인 제약 사항을 지원하기 위해 시스템의 동작을 소프트웨어 영역내 존재하는 애플리케이션 특화 오퍼레이션을 통해 나타냅니다.
  • 도메인 헥사곤의 엔티티와 다른 유스케이스와 직접 상호작용할 수 있습니다.
  • 다음과 같이 인터페이스로 소프트웨어가 할 수 있는 동작을 추상화로 나타냅니다.
1
2
3
4
5
public interface RouterViewUseCase {

List<Router> getRouters(Predicate<Router> filter);
}

입력 포트

  • 유스케이스가 인터페이스라면 유스케이스의 구현체가 입력 포트의 역할입니다.
  • 책에서는 “유스케이스에 서술된 소프트웨어의 의도를 만족시키는 입력 포트를 구현한다”라고 표현합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class RouterViewInputPort implements RouterViewUseCase {

private RouterViewOutputPort routerViewOutputPort;

public RouterViewInputPort(RouterViewOutputPort routerViewOutputPort) {
this.routerViewOutputPort = routerViewOutputPort;
}

@Override
public List<Router> getRouters(Predicate<Router> filter) {
var routers = routerViewOutputPort.fetchRouters();
return Router.retrieveRouter(routers, filter);
}
}

출력 포트

  • 유스케이스는 목표를 달성하기 위해 외부 리소스에서 데이터를 가져와야 하는 상황이 있습니다. 이때 출력 포트가 외부에서 데이터를 제공하는 인터페이스 역할을 수행합니다.
  • 이떄 출력 포트는 특정 기술에 종속되지 않습니다. 즉 데이터가 RDB로부터 오던 API부터로 오던 세분화된 책임은 출력 어댑터에 할당됩니다.
1
2
3
4
5
public interface RouterViewOutputPort {

List<Router> fetchRouters();

}

프레임워크 헥사곤 (Framework Hexagon)

  • 외부 인터페이스를 제공합니다. 즉 애플리케이션 기능의 노출 방법을 결정할 수 있는 곳입니다.
  • 헥사고날에서는 소프트웨어의 통신 방식을 2가지로 봅니다. 첫번째는 Driving 방식이고, 두번째는 Driven 방식입니다.

Driving Operation

  • 프론트 서버에서 우리 소프트웨어에 동작을 요청하는 방식이다.즉 입력 어댑터(Input Adapter)를 사용한다.
  • 드라이빙이라는 용어를 사용하는 이유가 외부에서 우리 소프트웨어의 동작을 유도(driving)하기 때문에 driving operation이라고 부른다고 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class RouterViewCLIAdapter {

RouterViewUseCase routerViewUseCase;

public RouterViewCLIAdapter(RouterViewUseCase routerViewUseCase) {
this.routerViewUseCase = routerViewUseCase;
}

public List<Router> obtainRelatedRouters(String type){
return routerViewUseCase.getRouters(router-> router.filterRouterByType(type));
}

private void setAdapters(){
// 유스케이스 인터페이스를 통해 입력 포트를 사용한다.
this.routerViewUseCase = new RouterViewInputPort(RouterViewFileAdapter.getInstance());
}
}
  • 위와 같이 입력 어댑터를 입력 포트에 연결하여 사용할 수 있다.

Driven Operation

  • Driving Operation과 반대로 우리 소프트웨어에서 외부에 요구사항을 만족시키기 위한 데이터를 가져옵니다.
  • 출력 어댑터(Output Adapter)를 통해서 Driven Operation을 정의합니다.
  • 입력 어댑터가 입력 포트와 매핑되야 하듯이, 출력 어댑터도 출력 포트와 매핑되야 합니다.
  • 구체적인 예시로 Oracle부터 데이터를 가져오는 출력 어댑터, MongoDb로부터 데이터를 가져오는 출력 어댑터 등이 있을 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
public class RouterViewFileAdapter implements RouterViewOutputPort {

@Override
public List<Router> fetchRouters() {
return readFileAsString();
}

private List<Router> readFileAsString() {
// 외부파일을 읽어 List<Router>를 생성해서 반환하는 로직
}

}
Read more

리액티브 프로그래밍이란?

리액티브 설계 원칙 (리액티브 선언문)

  1. Message Driven
  • 비동기 메시지 기반의 통신을 통해 구성요소들간의 느슨한 결합, 격리성을 보장합니다.
  1. Elastic (탄력성)
  • 시스템의 작업량이 변화하더라도 일정한 응답, 즉 시스템의 트래픽이 많든 적든 시스템에서 요구하는 응답성을 일정하게 유지하는 것입니다.
  1. Resilient (회복성)
  • 시스템에 장애가 발생하더라도 응답성을 유지하는 것을 의미한다. 시스템의 구성요소들이 독립적으로 분리되기 떄문에 장얘가 발생하더라도 전체 시스템은 여전히 응답 가능하고, 장애가 난 부분만 복구하면 된다는 의미입니다.
  1. Responsive (응답성)
  • 비동기 메시지 기반 통신을 바탕으로 한 회복성과 탄력성을 기반으로 즉각적으로 응답 가능한 시스템을 의미합니다.

Reactive 설계원칙

리액티브 프로그래밍(Reactive Programming)이란?

  • 비동기 메시지 기반으로 통신하는 높은 응답성을 가지는 Reactive System을 구축하는 데 필요한 프로그래밍 모델입니다.
  • 데이터 소스의 변경이 있을떄 마다 데이터를 전파하는 이벤트 기반 아키텍쳐를 사용합니다.
  • 선언형 프로그래밍 방식을 사용해 실행할 동작을 구체적으로 명시하지 않고, 목표만 선언합니다.
Read more

Database Lock

  • 트랜잭션은 작업의 완전성을 보장해주는 것으로 논리적인 작업을 모두 완벽하게 처리하거나, 처리하지 못할 경우에는 원 상태로 복구해서 작업의 일부만 적용되는 현상 (partial update)를 막아준다.
  • 잠금은 하나의 레코드를 여러 Connection에서 동시에 변경하고자 할때, 동시성을 제어하는 기능이다.

Lock 종류

  1. Global Lock
  • 한 세션에서 Global Lock을 획득하면 다른 세션에서는 Select를 제외한 DDL,DML 문장 실행 불가

  • FLUSH TABLES WITH READ LOCK 명령어로 LOCK을 걸고, UNLOCK TABLES명령어로 LOCK을 해제한다.

  1. Table Lock
  • 개별 테이블 단위로 설정되는 Lock으로 LOCK TABLES 테이블명 READ , LOCK TABLES 테이블명 WRITE 로 특정 테이블의 Lock을 획득할 수 있으며 UNLOCK TABLES명령어로 Lock을 해제한다
  • InnoDB 테이블의 경우 스토리지 엔진 차원에서 레코드 기반 잠금을 제공하기 떄문에 DML로는 묵시적으로 Table Lock이 걸리지 않으나 DDL에서는 묵시적 Table Lock이 걸린다.
  1. Named Lock
  • GET_LOCK() 함수를 이용해 임의의 문자열에 대해 잠금을 설정 할 수 있다.
  • 잠금 대상이 데이터베이스 객체가 아니라 임의의 문자열이며, 주로 여러 Client간 동기화 시켜야할 작업이 있을떄 활용될 수 있다.
1
2
3
SELECT GET_LOCK('lock',5); -- lock 문자열에 대해 5초간 잠금획득
SELECT RELEASE_LOCK('lock'); -- lock 문자열에 대해 잠금해제
SELECT RELEASE_ALL_LOCKS(); -- 모든 lock 해제
  1. MetaData Lock
  • 데이터베이스 객체의 이름이나 구조를 변경하는 경우에 획득하는 Lock
  • 명시적으로 획득이 불가능하며, 자동으로 테이블 이름 변경시 획득된다.

InnoDB Storage 엔진 잠금

  1. Record Lock
  • 다른 상용 DBMS의 Record Lock과 동일한 역할 수행
  • Record 자체에 대해 Lock을 수행하지 않고, Index 의 Record를 잠근다.
  • 인덱스가 하나도 없는 테이블이라도 내부적으로 자동 생성된 클러스터 인덱스를 이용해 잠금을 설정한다.
  • 즉, 변경해야 할 레코드를 찾기 위해 검색한 인덱스의 레코드를 모두 락을 걸어야 한다.

예를 들어 아래의 employees 테이블이 first_name 칼럼에 대해서만 index를 가지고 있다면, innoDB는 first_name이 ‘kim’인 모든 record에 대해서 lock을 수행한다.
테이블에 index가 하나도 없는 경우에는 풀스캔하면서 모든 record에 대해 lock을 건다.

1
UPDATE employees SET hire_date=NOW() WHERE first_name = 'kim' and last_name ='cs'
  1. Gap Lock
  • 인접한 레코드 사이의 간격을 잠그는 것을 의미한다. 즉 레코드와 레코드 사이 새로운 레코드가 생성되는 것을 제어하는 것이다.
  1. Next Key Lock
  • Record Lock과 Gap Lock을 합쳐놓은 형태의 잠금
  1. Auto Increment Lock
  • 자동 증가하는 숫자값이 중복되지 않고, 저장된 순서대로 증가함을 보장하기 위해 테이블 수준의 잠금을 사용
  • Insert 에서만 걸리며, Delete, Update에서는 걸리지 않는다. 2개의 Insert 쿼리가 동시에 실행되는 경우 하나의 Insert 쿼리가 Auto Increment Lock을 반납할떄까지 다른 하나의 쿼리는 대기해야한다.
  • 명시적으로 획득하거나 해제가 불가능하다.
Read more

Spring Batch Step 파헤쳐보기

Step 개념

  • Batch Job 을 구성하는 독립적인 단계로 하나의 Job은 하나 이상의 Step으로 구성됩니다.
  • Spring Batch Job 구현체중 하나인 SimpleJob을 까보면 Job 은 내부 멤버변수로 Step List를 들고 있음을 확인할 수 있습니다.
    1
    2
    3
    4
    public class SimpleJob extends AbstractJob {
    private List<Step> steps = new ArrayList<>();
    //...
    }
    [2] Spring Batch Step

StepExecution 이란?

  • Step에 대한 한번의 시도를 의미하는 객체로서 Step 실행 중에 발생한 정보들을 저장하고 있는 객체입니다.
  • DB의 BATCH_STEP_EXECUTION 테이블과 1:1로 매핑됩니다.
  • Job이 실패해서 재수행하는 경우에는 실패한 Step에 대해서만 재시작하고, 성공한 Step은 생략합니다. ( 하지만 allowStartIfComplete 설정값을 변경하면 성공한 Step도 재시작되게 변경할 수 있습니다 )

예를 들어 Step 1,2,3이 존재하는데 Step2번이 실행중에 실패한다면 Step 1은 성공 , Step 2는 실패처리 됩니다. 그리고 Step 3는 실행되지 않습니다.
이 상태에서 Job을 재시작하면 Step2부터 재시작하여 Step2,Step3 가 수행됩니다.

  • Job을 구성하는 모든 Step의 실행 정보인 StepExecution이 완료 처리되어야만 JobExecution이 완료처리됩니다.

즉 Spring Batch 도메인 용어를 정리하면 하나의 Job은 여러개의 Step으로 구성되고, Job이 JobParameter를 주입받아 실행되는 객체가 JobInstance객체입니다.

JobInstance를 실행한 정보가 JobExecution이고, Job이 실행되면서 Job을 구성하는 Step들의 실행정보가 StepExecution입니다.

[1] Spring Batch Job과 Step에 관련된 도메인 객체들

Step간 데이터 공유하기 - ExecutionContext

  • ExecutionContext를 활용하면 Job내 Step간 데이터를 공유할 수 있습니다. 혹은 실패한 Step에서 Step 재시작시 실패 이전까지 작업했던 상태값들을 가져올 수 있습니다.
  • ExecutionContext는 Spring Batch에서 관리하는 key-value (Map) 컬렉션입니다.
  • StepExecution, JobExecution 객체의 멤버변수로 선언되고 , 각각 DB의 BATCH_JOB_EXECUTION_CONTEXT , BATCH_STEP_EXECUTION_CONTEXT 테이블에 1:1 매핑됩니다.
  • StepExecutionExecutionContext는 Step안에서만 공유됩니다. 즉 특정 Step에서만 접근이 가능합니다. 실패한 Step이 재시작된 경우도 이전까지 작업한 내용을 불러들일수 있습니다.
  • JobExeuctionExecutionContext는 모든 Step안에서 공유됩니다.
ExecutionContext

예시 코드

아래와 같이 ExecutionContextStepContribution 또는 ChunkContext를 통해 접근하고, 값을 넣어줄 수 있습니다.
넣어준 값은 BATCH_JOB_EXECUTION_CONTEXT , BATCH_STEP_EXECUTION_CONTEXT 테이블에 각각 직렬화되어 저장됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ExecutionContextTasklet1 implements Tasklet {

@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {

ExecutionContext jobExecutionContext = contribution.getStepExecution().getJobExecution().getExecutionContext();
ExecutionContext stepExecutionContext = contribution.getStepExecution().getExecutionContext();

jobExecutionContext.put("jobName", "developer"); //1. BATCH_JOB_EXECUTION_CONTEXT 에 저장 (모든 STEP에서 공유)
stepExecutionContext.put("stepName", "software"); // 2. BATCH_STEP_EXECUTION_CONTEXT 에 저장 (특정 STEP에서 공유)

return RepeatStatus.FINISHED;
}
}
BATCH_JOB_EXECUTION_CONTEXT 테이블
BATCH_STEP_EXECUTION_CONTEXT 테이블

Spring Batch에서 제공하는 Step 구현체(5)

  • Step 인터페이스를 AbstractStep이라는 추상 클래스에서 구현하고,AbstractStep 추상클래스를 구현하는 구조입니다.
  • Batch에서 제공하는 Step의 구현체는 아래와 같은 5개의 구현체가 존재합니다.
  1. Tasklet Step
  2. Partition Step
  3. Job Step
  4. Flow Step
  5. Decision Step
Spring Batch에서 제공하는 Step 구현체 종류

TaskletStep

  • RepeatTemplate을 사용해서 Tasklet 코드 block을 트랜잭션 경계 내(성공시 커밋,실패시 롤백)에서 반복해서 실행합니다.
    언제까지 반복해서 실행할것인가에 대한 판단은 Tasklet 객체에서 반환하는 RepeatStatus값에 의해 결정됩니다. RepeatStatus.FINISHED 와 같이 특정 RepeatStatus를 반환할떄까지 계속해서 실행합니다.

  • TaskletStep은 아래와 같은 tasklet 인터페이스를 구현하는 tasklet 구현체를 멤버변수로 가지고 있습니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
    * Strategy for processing in a step.
    */
    public interface Tasklet {

    /**
    * @return an {@link RepeatStatus} indicating whether processing is
    * continuable. Returning {@code null} is interpreted as {@link RepeatStatus#FINISHED}
    *
    * @throws Exception thrown if error occurs during execution.
    */
    @Nullable
    RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception;

    }
  • TaskletStep의 코드를 확인해보면 Tasklet 호출횟수를 조정하기 위한 RepeatTemplate 와 실행되야할 작업 그 자체 (tasklet) 구현체를 멤버변수로 들고있는것을 확인할 수 있습니다.

1
2
3
4
5
6
7
public class TaskletStep extends AbstractStep {

private RepeatOperations stepOperations = new RepeatTemplate();
//...
private Tasklet tasklet;
//...
}
  • 자주 사용되는 tasklet 구현체는 Spring Batch에서 이미 구현해놓았습니다. 이 중 ChunkOrientedTasklet구현체을 활용해 Chunk 단위로 배치 작업을 쪼개서 처리할 수 있습니다.
    Spring Batch에서 제공하는 Step 구현체 종류
Read more

Vue3 Data Method와 Rendering Directives

Vue의 data() method

  • data method가 반환하는 객체는 모두 proxy로 래핑됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
Vue.createApp({
name : "App",
data() {
return {
contracts : [
{'unique_value': 1,'name' : 'cs', 'age' : 29},
{'unique_value': 2,'name' : 'khkim', 'age' : 23},
{'unique_value': 3,'name' : 'jylim', 'age' : 21}
] // ES6 Proxy 객체로 Wrapping됨.
};
},
})
  • 위 contracts 배열은 proxy 로 래핑되서 반환되며, 이때 배열의 데이터를 변경하는 method들(push(),splice(),sort())도 같이 래핑하고 있습니다. 즉 배열의 데이터가 변경되면 Watcher에게 변경을 알려서 다시 렌더링이 일어나도록 합니다.
https://vuejs.org/v2/guide/reactivity.html
  • 떄문에 데이터를 변경하고자 하면 항상 Proxy 객체를 통해서 변경시켜야 합니다. Vue 인스턴스 외부에서 배열을 직접 변경하면 Vue에서 제공하는 반응성이 동작하지 않습니다.

Vue Rendering 관련 디렉티브

  1. v-text 디렉티브 사용 : HTML 태그를 escape 처리하여 문자열 그대로 노출합니다.
  • 내부적으로 innerText 속성에 연결됩니다.
    1
    2
    3
    <div id="app">
    <h2 v-text="message"></h2>
    </div>
  1. v-html 디렉티브 사용 : HTML 태그를 파싱해서 노출합니다.
  • 태그가 치환되어 노출되며, 내부적으로 innerHtml 속성에 연결됩니다.
    1
    2
    3
    <div id="app">
    <h2 v-html="message"></h2>
    </div>
  1. v-bind 디렉티브 사용 : DOM의 특정 속성값을 데이터 바인딩할 수 있습니다.
  • v-bind:value 는 :value와 같이 v-bind 디렉티브를 생략하고 사용가능합니다.
  • v-bind 디렉티브는 단방향 데이터 바인딩입니다. 즉 model값의 변경이 View에 바인딩되지만, View의 변경이 model에 바인딩되지는 않습니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <input type="text" :value="message" />
    <img v-bind:src="imgPath" />
    // ---
    const model = {
    message : "hello Vue3!",
    imgPath : "https://cdn.pixabay.com/photo/2016/03/02/13/59/bird-1232416_1280.png",
    };
    const vm = Vue.createApp({
    name : 'App',
    data(){
    return model;
    }
    })
Read more

Kubernetes 개념 한판 정리

Kubernetes의 정의

  • Container화 된 application을 배포/관리해주는 오픈소스 Container Orchestrator Framework

Kubernetes에서 제공하는 기능

  1. Service Discovery

DNS 이름 또는 자체 IP주소를 사용해서 Client가 사용할 Container를 노출할 수 있음. Traffic을 Load Balancing하고, AutoScaling이 가능하다.

  1. Storage Orchestration

원하는 저장소 시스템을 탑재할 수 있다.

  1. 자동화된 binPacking

container화된 작업을 실행하는데 사용할 수 있는 kubernetes cluster 노드를 제공하며, 각 container가 필요로 하는 CPU , RAM을 설정하면 kubernetes가 container를 resource를 가장 잘 사용할 수 있는 node에 배포한다.

  1. self-healing

실패한 container 재시작, 교체기능을 가지고 있다.

Kubernetes Object 이해하기

object란 k8s cluster에 대해 의도한 상태를 명세해놓은 하나의 “의도를 담은 레코드”이다.
( https://kubernetes.io/ko/docs/concepts/overview/working-with-objects/kubernetes-objects/ )

object에는 대표적으로 pod, service, volume, namespace등 이 있다.

object를 관리하는 controller는 사용자가 바라는 상태와 현재 상태가 일치하도록 object를 생성/삭제한다.

controller에는 대표적으로 ReplicaSet, Deployment, DemonSet, Job등이 있다.

스쿼드별로 하나의 kubernetes cluster를 공유하기

  • namespace는 하나의 k8s cluster를 여러 개의 논리적인 단위로 쪼개서 사용한다.
  • namespace를 통해 하나의 k8s cluster를 공유하되, 논리적으로 구분할 수 있다.
k8s namespace
  • 기본적으로 k8s 를 실행하면 몇 개의 namespace가 자동으로 생성됩니다. 이 중 default namespace는 기본 namespace로 별도로 k8s에 namespace를 지정하지 않으면 항상 이 default namespace에 명령이 적용된다.

Kubernetes Object 기술하기

kubernetes에서는 object를 생성할때, object의 spec을 지정해주어야 한다.
대부분 yaml파일로 kubectl CLI에 제공한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

대표적으로 사용되는 필드값들은 다음의 의미를 나타낸다.

  • apiVersion : object를 생성하기 위한 Kubernetes버전
  • kind : 생성할 object의 종류
  • metadata : object를 유일하게 구분지어 줄 데이터
  • spec : object에 의도하는 상태값들
Read more