소개
- 주력 언어 또는 기술 : Java, Spring Framework, RxJava, Reactor, Spring WebFlux
- (주)코드스테이츠(https://www.codestates.com)에서 Senior Educational Software Engineer(Backend)로 활동
(From 2022.03 To 2024.01.31)
- 프리랜서 개발자 및 강사로 활동(From 2024.02)
- 저서
안녕하세요? Kevin이라고 합니다. ^^
인프런에서 이렇게 강사로서 여러분들을 만나게 되어서 너무 반갑습니다.
어떤 분야든 마찬가지겠지만 특히나 소프트웨어 개발자는 끊임없이 변화하는 트렌드에 뒤처지지 않도록 항상 자기 자신을 갈고 닦는것이 개발자로써 살아남는 유일한 방법이라고 생각하며 항상 배우는 자세로 즐겁게 소프트웨어 개발을 하고 있는 개발자 중 한 명입니다.
제가 가지고 있는 지식과 경험이 다른 분들에게 조금이나마 도움이 되기를 바라면서 인프런에서 강좌를 시작하였습니다.
앞으로 수강생분들에게 현실적으로 도움이 되는 다양한 강좌로 꾸준히 찾아뵙도록 하겠습니다. 감사합니다.
질문이나 의견은 언제든지 환영하니, 이메일(it.village.host@gmail.com)로 편하게 얘기해주세요.
강의
전체5로드맵
전체1수강평
- 좋은 강의 감사합니다. 덕분에 어떤 순서로 공부를 해야할 지 감을 잡게 되었습니다. 항상 건강하세요.
Kei Shimizu
2024.07.02
1
- 좋은 강의였습니다.
rlackdgml97
2024.05.14
1
게시글
질문&답변
2024.07.15
import com.itvillage.utils.Logger;
안녕하세요. com.itvillage 패키지는 샘플코드의 패키지 경로여서 Gradle의 의존성과는 상관없습니다. 샘플 코드의 com.itvillage.utils 패키지를 복사해서 highjune님의 프로젝트에 붙여넣기 해주시면 되겠습니다. 감사합니다.
- 0
- 1
- 35
질문&답변
2024.07.15
Interceptor에서 reactor Context 유지하는 방법
안녕하세요. 제가 해외에 있어서 답변이 조금 늦어져서 죄송합니다. gRPC는 개념은 알고 있지만 사용한 적은 없다는 점을 먼저 말씀 드리고 답변을 드려보면, class CustomHeaderInterceptor() : ClientInterceptor { companion object { const val X_REQUEST_ID_KEY = "X-Request-Id" val X_REQUEST_ID_HEADER_KEY: Metadata.Key = Metadata.Key.of("x-request-id", Metadata.ASCII_STRING_MARSHALLER) } override fun interceptCall( method: MethodDescriptor , callOptions: CallOptions, next: Channel ): ClientCall { return object : ForwardingClientCall.SimpleForwardingClientCall (next.newCall(method, callOptions)) { override fun start(responseListener: Listener , headers: Metadata) { Mono.deferContextual { context -> val requestId: String = context.getOrDefault(X_REQUEST_ID_KEY, fallbackRequestId()) ?: fallbackRequestId() headers.put(X_REQUEST_ID_HEADER_KEY, requestId) delegate().start(responseListener, headers) // (1) gRPC 호출 시작 부분? Mono.just(requestId) }.subscribe() } } } (1)의 gRPC 호출을 시작하는 코드가 Mono.deferContextual(...) 내부가 아니라 subscribe(..) 내부에서 동작해야 되지 않을까 하는 생각이 드네요. Mono.just(requestId) 대신에 Mono.just(headers)로 리턴하고, subscribe() 내부에서 gRPC로 headers를 전달하는게 자연스러워 보여서요. 그렇게 생각한 이유는 Reactor의 Sequence가 비동기로 동작하기 때문에 Mono 내부에서 gRPC가 호출되지 않을 거라고 생각했기 때문입니다. gRPC 호출을 Reactor Sequece 동작이 끝난 후에 일어나도록 하면 정상적으로 동작할 것 같다라는 생각이 듭니다. 오류 해결 되시길 바래볼게요. 감사합니다.
- 0
- 2
- 58
질문&답변
2024.06.24
filterWhen의 차이
안녕하세요? filterWhen() Operator는 사실 처음 Reactor에 입문하는 분들이 학습할 때 혼란스러울 수도 있을 것 같아서 filter() Operator만 설명드리려다가 그래도 간단한 예제로라도 설명을 해보자 라는 생각으로 강의에 추가한 Operator이긴 한데요. 아무튼 filterWhen() Operator를 어떤 경우에 사용할 수 있는지에 대해서만 간단하게 설명드리겠습니다. Flux data = Flux.just("data1", "data2", "data3", "data4"); data .filterWhen(data -> callExternalService(data)) // 외부 서비스 호출 .subscribe(filteredData -> System.out.println("Filtered data: " + filteredData)); Mono callExternalService(String data) { // 외부 서비스 호출을 Non-Blocking 호출로 시뮬레이션하는 코드 return WebClient.create() .get() .uri("https://aaa.com/evaluate?data=" + data) .retrieve() .bodyToMono(Boolean.class); } 위 코드를 보시면, filterWhen() 내부에서 callExternalService() 메서드를 호출하고 있는데요. callExternalService()가 호출되면 WebClient를 이용해 외부 서비스에서 각각의 data의 조건을 평가할 것입니다. 여기서 WebClient로 호출하는 외부 API( aaa.com/evaluate?data=xxx)가 Spring WebFlux 기반이라면 data1부터 data4까지 각각 Non-Blocking 형태로 호출 되어서 동기적으로 조건을 평가하는 것보다 효율성이 향상 될 겁니다. Reactor 만으로 설명하기가 사실상 한계가 있긴한데 그래도 기본 개념은 알려드리고 싶어서 강의에서 filterWhen()에 대한 예제를 추가했었습니다. ^^; 강의에서 설명이 부족했다면 양해 부탁드리겠습니다. 나중에 Spring WebFlux 기반의 애플리케이션을 여러 개 돌려 놓고, 테스트 해보시고 결과를 눈으로 확인해 보시면 좋을 것 같아요. 감사합니다.
- 1
- 1
- 79
질문&답변
2024.06.18
예시코드는 webflux의 이점보단 webclient의 이점 아닌가요?
안녕하세요. 좋은 질문 주셔서 감사드립니다. 말씀하신대로 클라이언트 쪽이 Non-Blocking이고, 서버 쪽이 MVC(Blocking)일 경우, 동일한 결과가 나올겁니다.(논리적으로 생각해 본 것이고, 사정상 제가 직접 돌려보지는 못했으니 양해 부탁드릴게요.) 단, MVC 쪽에서 들어오는 요청에 thread가 1대 1로 매핑 된다는 전제가 깔려있어야 될겁니다. MVC 쪽에서는 톰캣이 들어오는 요청 당 thread 하나를 매핑 시켜 줄텐데 이 경우에는 요청이 동시 다발적으로 들어올 때 각각 다른 thread가 매핑될테니 말씀하신대로 동일한 결과가 나올텐데요. 만약에 톰캣 쪽에 들어오는 요청에 사용되는 thread가 딱 하나만 존재한다면 말씀하신대로의 결과가 나오지 않을겁니다. 물론 Fully Non-Blocking 구조에서는 서버 쪽에서 worker thread가 하나만 있어도 Blocking 되지 않구요.(예제에서 System.setProperty("reactor.netty.ioWorkerCount", "1"); 를 통해서 요청을 처리하는 thread 개수를 1로 지정했습니다.) 강의에 나오는 예시는 Blocking I/O와 Non-Blocking I/O를 쉽게 설명드리기 위한 단순 예제라고 이해해 주시면 감사드릴게요. 다시 한 번 좋은 질문 주셔서 감사드립니다!
- 0
- 1
- 106
질문&답변
2024.06.10
advancedTimeBy와 thenAwait 사용 예시가 궁금합니다
안녕하세요? advancedTimeBy()와 thenAwait()의 차이점에 대해서 질문 주셨는데요. 일단 결론부터 말씀드리면 두 메서드는 정말 거의 동일한 기능을 하기 때문에 실제 시간을 당긴다는 측면에서는 둘 중 어떤걸 사용해도 상관없습니다. 다만, advancedTimeBy()는 능동적으로 시간을 제어한다고 볼 수 있고, thenAwait()은 수동적으로 가상의 시간이 흘러가는 걸 기다린다는 측면에서 수동적입니다. 반드시 어떤 상황에 이걸 사용해야 한다고 딱 꼬집어서 명시되어 있는건 아닌데 일반적으로 advancedTimeBy()는 복잡한 테스트 시나리오에서 직접적으로 시간을 세밀하게 제어하고 싶을 때 사용하고, thenAwait()은 스케쥴링이 되어 있는 반복적인 작업 등에서 상태 변화를 체크하는데 사용됩니다. advancedTimeBy() 같은 경우, 복잡한 테스트 시나리오에 적용하니 코드 가독성도 좀 떨어질 수 있지만 thenAwait() 같은 경우는 메서드 자체도 굉장히 직관적이라서 코드 가독성이 좋을테구요. 예를 들어서 10분에 한번씩 데이터를 가져와서 캐시를 업데이트 하는 로직이 있는데, 테스트 할 때 마다 10분씩 기다리면 비효율적이니까 advancedTimeBy()와 thenAwait() 중에 하나를 이용해서 시간을 당기면 될텐데요. 이때 10분에 한번 씩 데이터를 가져오는 로직이 단순하면 thenAwait()을 사용하는게 더 바람직할 테구요. 만약에 어떤 로직이 복잡한 상황을 처리하는 로직일 경우 이를 테면, Flux .concat( fetchDataSource1(), fetchDataSource2(), fetchDataSource3() ) 이런 식으로 데이터를 여러 곳에서 fetch 해야하는 상황인데, 각 Sequence에서 fetch 하는 시간이 다를 경우 각각의 sequence 별로 데이터를 fetch하는 시간 주기를 세밀하게 당겨서 테스트 하고 싶을 때, advancedTimeBy()를 사용하는게 조금 더 적절하지 않을까 생각합니다. 근데 둘 중 어떤걸 사용해도 둘 다 시간을 당기는 것이기 때문에 테스트를 수행할 수 있을겁니다. 결국 비슷한 기능을 사용자가 선택해서 사용할 수 있도록 여러 선택지를 주었다라고 생각해 볼 수도 있는 문제인거 같네요. 질문에 대한 답변이 충분히 되셨길 바랍니다. 감사합니다.
- 0
- 1
- 62