public class HelloWorld {
static long start;

public static void main(String[] args) throws InterruptedException, ExecutionException {
HelloWorld helloWorld = new HelloWorld();
Future<String> hello;
System.out.println("[" + System.currentTimeMillis() + "]" + "Start!");
hello = helloWorld.getSayHelloAsync();
System.out.println("[" + System.currentTimeMillis() + "]" + "End!");
System.out.println(hello.get());
}

public String sayHello() throws InterruptedException{
System.out.println("[" + System.currentTimeMillis() + "]calling sayHello()");
Thread.sleep(1000);
System.out.println("[" + System.currentTimeMillis() + "]end sayHello()");
return "[" + System.currentTimeMillis() + "]" + "HelloWorld!";
}

public Future<String> getSayHelloAsync(){
CompletableFuture<String> future = new CompletableFuture<>();
new Thread(()-> {
try {
String result = sayHello();
future.complete(result);
} catch (Exception e) {
future.completeExceptionally(e);
}
}).start();
return future;
}
}
[1552891812467]Start!
[1552891812526]End!
[1552891812526]calling sayHello()
[1552891813526]end sayHello()
[1552891813526]HelloWorld!

여기에서 중요한건 "End!"가 프린트 되는 시점입니다. 동기식은 return을 기다리기 때문에 "End!" 프린트 되기전에 sayHello()함수 안의 내용이 먼저 프린트 되는 반면에, 비동기식은 return을 기다리지 않고 "End!"가 먼저 프린트 되고 후에 sayHello()함수 안의 내용이 먼저 프린트되는 것을 보실 수 있습니다.

return을 기다리는 동기식과 return을 기다리지 않고 다음 작업을 계속 진행하는 비동기식의 차이를 알아 봤습니다.



보통 프로그래밍을 하면 동기프로그램을 짜게 됩니다. HelloWorld와 같은 sample 코드들 말입니다. Main 함수에서 호출되는 다른 함수들을 생각해 보세요.

public class HelloWorld {

public static void main(String[] args) {
HelloWorld helloWorld = new HelloWorld();
System.out.println(helloWorld.sayHello());
}

public String sayHello() {
return "Hello!";
}
}

sayHello()함수가 호출되고 있고 이 함수는 string을 return하고 있습니다. 아주 짧은 순간이긴 하지만 sayHello()가 실행되는 순간에는 main함수는 멈추고 return되기를 기다리고 있는 것입니다. 다음 예를 보시면 이해가 빠르실 겁니다.

동기식

public class HelloWorld {
static long start;

public static void main(String[] args) throws InterruptedException{
HelloWorld helloWorld = new HelloWorld();
System.out.println("[" + System.currentTimeMillis() + "]" + "Start!");
System.out.println(helloWorld.sayHello());
System.out.println("[" + System.currentTimeMillis() + "]" + "End!");
}

public String sayHello() throws InterruptedException{
System.out.println("[" + System.currentTimeMillis() + "]calling sayHello()");
Thread.sleep(1000);
System.out.println("[" + System.currentTimeMillis() + "]end sayHello()");
return "[" + System.currentTimeMillis() + "]" + "HelloWorld!";
}
}
[1552890046994]Start!
[1552890046994]calling sayHello()
[1552890047994]end sayHello()
[1552890047994]HelloWorld!
[1552890047994]End!

비동기식

public class HelloWorld {
static long start;

public static void main(String[] args) throws InterruptedException, ExecutionException {
HelloWorld helloWorld = new HelloWorld();
Future<String> hello;
System.out.println("[" + System.currentTimeMillis() + "]" + "Start!");
hello = helloWorld.getSayHelloAsync();
System.out.println("[" + System.currentTimeMillis() + "]" + "End!");
System.out.println(hello.get());
}

public String sayHello() throws InterruptedException{
System.out.println("[" + System.currentTimeMillis() + "]calling sayHello()");
Thread.sleep(1000);
System.out.println("[" + System.currentTimeMillis() + "]end sayHello()");
return "[" + System.currentTimeMillis() + "]" + "HelloWorld!";
}

public Future<String> getSayHelloAsync(){
CompletableFuture<String> future = new CompletableFuture<>();
new Thread(()-> {
try {
String result = sayHello();
future.complete(result);
} catch (Exception e) {
future.completeExceptionally(e);
}
}).start();
return future;
}
}
[1552891812467]Start!
[1552891812526]End!
[1552891812526]calling sayHello()
[1552891813526]end sayHello()
[1552891813526]HelloWorld!

여기에서 중요한건 sayHello()의 결과를 얻어오는 타이밍입니다. 동기식은 main함수의 3줄 sayHello()가 실행되어야 "End!"가 프린트 되는 반면, 비동기식은 getSayHelloAsync()가 실행되고 바로 "End!"가 출력되고나서 결과를 가져옵니다.

한마디로 Synchronous/Asynchronous는 호출되는 함수의 호출에 따른 결과를 언제 받느냐에 따라 구분이 됩니다. 

함수를 호출했을 때 바로 결과를 주기로 한 방식은 Synchronous

함수를 호출했을 때 바로 결과를 받지않고 나중에 결과를 주기로 한 방식은 Asynchronous

이번에 Async와 Sync의 차이점에 대해서 알아 봤습니다. 다음 포스트에서는 Block과 Non-block에 대해서 알아보겠습니다. 참고로 block/non-block과 sync/async의 개념은 양립할 수 있는 개념이기에 sync/async도 비교 설명 하겠습니다.


이상한모임 글중에 나의 마음에 와닿는 글입니다.

TDD의 효용성에 대해 짧은 글로 하고자 하는 말을 정확히 전달해서 아주 인상적으로 읽었습니다.


구직 과정에 코딩 시험이 있었다. 면접관의 컴퓨터와 응사자의 컴퓨터가 코딩 시험 도구로 연결되어 면접관이 응시자의 코딩을 지켜보거나 개입할 수 있는 환경이었다. 어떤 문제의 답 코드를 쓰다 10줄 이상이 되니 자신감이 떨어져 TDD를 사용하기로 했다. 여기서 나는 흥미로운 경험을 했다.

물론 코딩 시험에 테스팅 도구는 주어지지 않았다. TDD는 테스팅 도구가 있어야만 할 수 있는 것이 아니다.

이 글에서 테스트 케이스는 테스트 코드가 아니라 테스트 데이터를 의미한다.

나는 시험 문제를 반영한 간단한 테스트 함수를 만들고 제시된 테스트 케이스와 몇가지 임의의 테스트 케이스를 추가했다. 답 코드를 쓰는 과정에서 지속적으로 테스트를 실행했고 모든 테스트 케이스가 성공한 후 면접관에게 코딩이 끝났다고 말했다.

즉시 면접관 중 한 명이 버그가 있으니 수정해 달라고 얘기했다. 나는 버그를 찾기 위해 무의식적으로 답 코드가 아니라 시험 문제 지문과 테스트 케이스 목록을 확인했다. 잠깐 살펴봤지만 안타깝게도 나는 버그를 발견하지 못했다. 면접관에게 버그를 찾지 못했다고 말하자 면접관은 버그를 설명하는 대신 테스트 케이스 하나를 추가했다. 나는 추가된 테스트 케이스가 실패하는 것을 확인한 후 답 코드에 논리 하나를 추가했다. 다시 테스트를 실행했고 모든 테스트 케이스에 대해 성공 표시가 출력됐다.

이것이 나에게 흥미로운 이유는 코딩 시험 과정이 소프트웨어를 만드는 한가지 유용한 프로세스 사례의 축소판이 되었기 때문이다. 프로그래머는 구현 코드가 아니라 요구사항과 요구사항의 구체적인 사례에 더 집중한다. 도메인 전문가나 품질 책임자는 발견된 버그를 재현 방법과 함께 프로그래머에 전달한다. 프로그래머는 버그를 반복 재현하며 코드를 고친다. 작업의 완료와 소프트웨어 회귀(software regression)가 없음은 자동화된 테스팅을 통해 확인한다.

항상 코딩 시험의 평가자 역할을 수행하다 정말 오랜만에 응시자가 되었는데 이런 놀라운 경험을 하게 될 것이라고는 전혀 생각하지 못했다. 처음부터 버그 없는 답을 썼어도 좋았겠지만 나는 원래 완벽한 코드를 쓸 수 있는 사람이 아니다. 오히려 이번 경험이 앞으로 나에게 큰 영향을 줄 것으로 생각된다.

-출처 : https://justhackem.wordpress.com/2019/01/05/coding-test-and-tdd/?utm_source=weirdmeetup&utm_medium=original_link_on_post&utm_campaign=%EC%BD%94%EB%94%A9+%EC%8B%9C%ED%97%98%EA%B3%BC+TDD


'Programming > Spring Framework' 카테고리의 다른 글

Spring Boot Auto Configuration 예제  (0) 2020.04.21
2way ssl 인증을 위한 JKS 만들기  (0) 2020.03.10
Annotation-based Controller  (0) 2017.02.01

+ Recent posts